btrfs-progs: import ulist
[btrfs-progs-unstable/devel.git] / btrfs-fragments.c
blob160429ad360cd5b5ee0f12249eb311441c45e912
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public
4 * License v2 as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 * General Public License for more details.
11 * You should have received a copy of the GNU General Public
12 * License along with this program; if not, write to the
13 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14 * Boston, MA 021110-1307, USA.
17 #define _GNU_SOURCE
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
23 #include <dirent.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <libgen.h>
28 #include <limits.h>
29 #include <uuid/uuid.h>
30 #include <ctype.h>
32 #include <gd.h>
34 #undef ULONG_MAX
36 #include "kerncompat.h"
37 #include "ctree.h"
38 #include "ioctl.h"
39 #include "utils.h"
41 static int use_color;
42 static void
43 push_im(gdImagePtr im, char *name, char *dir)
45 char fullname[2000];
46 FILE *pngout;
48 if (!im)
49 return;
51 snprintf(fullname, sizeof(fullname), "%s/%s", dir, name);
52 pngout = fopen(fullname, "w");
53 if (!pngout) {
54 printf("unable to create file %s\n", fullname);
55 exit(1);
58 gdImagePng(im, pngout);
60 fclose(pngout);
61 gdImageDestroy(im);
64 static char *
65 chunk_type(u64 flags)
67 switch (flags & (BTRFS_BLOCK_GROUP_SYSTEM | BTRFS_BLOCK_GROUP_DATA |
68 BTRFS_BLOCK_GROUP_METADATA)) {
69 case BTRFS_BLOCK_GROUP_SYSTEM:
70 return "system";
71 case BTRFS_BLOCK_GROUP_DATA:
72 return "data";
73 case BTRFS_BLOCK_GROUP_METADATA:
74 return "metadata";
75 case BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA:
76 return "mixed";
77 default:
78 return "invalid";
82 static void
83 print_bg(FILE *html, char *name, u64 start, u64 len, u64 used, u64 flags,
84 u64 areas)
86 double frag = (double)areas / (len / 4096) * 2;
88 fprintf(html, "<p>%s chunk starts at %lld, size is %s, %.2f%% used, "
89 "%.2f%% fragmented</p>\n", chunk_type(flags), start,
90 pretty_size(len), 100.0 * used / len, 100.0 * frag);
91 fprintf(html, "<img src=\"%s\" border=\"1\" />\n", name);
94 enum tree_colors {
95 COLOR_ROOT = 0,
96 COLOR_EXTENT,
97 COLOR_CHUNK,
98 COLOR_DEV,
99 COLOR_FS,
100 COLOR_CSUM,
101 COLOR_RELOC,
102 COLOR_DATA,
103 COLOR_UNKNOWN,
104 COLOR_MAX
107 static int
108 get_color(struct btrfs_extent_item *item, int len)
110 u64 refs;
111 u64 flags;
112 u8 type;
113 u64 offset;
114 struct btrfs_extent_inline_ref *ref;
116 refs = btrfs_stack_extent_refs(item);
117 flags = btrfs_stack_extent_flags(item);
119 if (flags & BTRFS_EXTENT_FLAG_DATA)
120 return COLOR_DATA;
121 if (refs > 1) {
122 /* this must be an fs tree */
123 return COLOR_FS;
126 ref = (void *)item + sizeof(struct btrfs_extent_item) +
127 sizeof(struct btrfs_tree_block_info);
128 type = btrfs_stack_extent_inline_ref_type(ref);
129 offset = btrfs_stack_extent_inline_ref_offset(ref);
131 switch (type) {
132 case BTRFS_EXTENT_DATA_REF_KEY:
133 return COLOR_DATA;
134 case BTRFS_SHARED_BLOCK_REF_KEY:
135 case BTRFS_SHARED_DATA_REF_KEY:
136 return COLOR_FS;
137 case BTRFS_TREE_BLOCK_REF_KEY:
138 break;
139 default:
140 return COLOR_UNKNOWN;
143 switch (offset) {
144 case BTRFS_ROOT_TREE_OBJECTID:
145 return COLOR_ROOT;
146 case BTRFS_EXTENT_TREE_OBJECTID:
147 return COLOR_EXTENT;
148 case BTRFS_CHUNK_TREE_OBJECTID:
149 return COLOR_CHUNK;
150 case BTRFS_DEV_TREE_OBJECTID:
151 return COLOR_DEV;
152 case BTRFS_FS_TREE_OBJECTID:
153 return COLOR_FS;
154 case BTRFS_CSUM_TREE_OBJECTID:
155 return COLOR_CSUM;
156 case BTRFS_DATA_RELOC_TREE_OBJECTID:
157 return COLOR_RELOC;
160 return COLOR_UNKNOWN;
163 static void
164 init_colors(gdImagePtr im, int *colors)
166 colors[COLOR_ROOT] = gdImageColorAllocate(im, 255, 0, 0);
167 colors[COLOR_EXTENT] = gdImageColorAllocate(im, 0, 255, 0);
168 colors[COLOR_CHUNK] = gdImageColorAllocate(im, 255, 0, 0);
169 colors[COLOR_DEV] = gdImageColorAllocate(im, 255, 0, 0);
170 colors[COLOR_FS] = gdImageColorAllocate(im, 0, 0, 0);
171 colors[COLOR_CSUM] = gdImageColorAllocate(im, 0, 0, 255);
172 colors[COLOR_RELOC] = gdImageColorAllocate(im, 128, 128, 128);
173 colors[COLOR_DATA] = gdImageColorAllocate(im, 100, 0, 0);
174 colors[COLOR_UNKNOWN] = gdImageColorAllocate(im, 50, 50, 50);
178 list_fragments(int fd, u64 flags, char *dir)
180 int ret;
181 struct btrfs_ioctl_search_args args;
182 struct btrfs_ioctl_search_key *sk = &args.key;
183 int i;
184 struct btrfs_ioctl_search_header *sh;
185 unsigned long off = 0;
186 int bgnum = 0;
187 u64 bgstart = 0;
188 u64 bglen = 0;
189 u64 bgend = 0;
190 u64 bgflags = 0;
191 u64 bgused = 0;
192 u64 saved_extent = 0;
193 u64 saved_len = 0;
194 int saved_color = 0;
195 u64 last_end = 0;
196 u64 areas = 0;
197 long px;
198 char name[1000];
199 FILE *html;
200 int colors[COLOR_MAX];
202 gdImagePtr im = NULL;
203 int black = 0;
204 int width = 800;
206 snprintf(name, sizeof(name), "%s/index.html", dir);
207 html = fopen(name, "w");
208 if (!html) {
209 printf("unable to create %s\n", name);
210 exit(1);
213 fprintf(html, "<html><header>\n");
214 fprintf(html, "<title>Btrfs Block Group Allocation Map</title>\n");
215 fprintf(html, "<style type=\"text/css\">\n");
216 fprintf(html, "img {margin-left: 1em; margin-bottom: 2em;}\n");
217 fprintf(html, "</style>\n");
218 fprintf(html, "</header><body>\n");
220 memset(&args, 0, sizeof(args));
222 sk->tree_id = 2;
223 sk->max_type = -1;
224 sk->min_type = 0;
225 sk->max_objectid = (u64)-1;
226 sk->max_offset = (u64)-1;
227 sk->max_transid = (u64)-1;
229 /* just a big number, doesn't matter much */
230 sk->nr_items = 4096;
232 while(1) {
233 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
234 if (ret < 0) {
235 fprintf(stderr, "ERROR: can't perform the search\n");
236 return ret;
238 /* the ioctl returns the number of item it found in nr_items */
239 if (sk->nr_items == 0)
240 break;
242 off = 0;
243 for (i = 0; i < sk->nr_items; i++) {
244 int j;
246 sh = (struct btrfs_ioctl_search_header *)(args.buf +
247 off);
248 off += sizeof(*sh);
249 if (sh->type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
250 struct btrfs_block_group_item *bg;
252 if (im) {
253 push_im(im, name, dir);
254 im = NULL;
256 print_bg(html, name, bgstart, bglen,
257 bgused, bgflags, areas);
260 ++bgnum;
262 bg = (struct btrfs_block_group_item *)
263 (args.buf + off);
264 bgflags = btrfs_block_group_flags(bg);
265 bgused = btrfs_block_group_used(bg);
267 printf("found block group %lld len %lld "
268 "flags %lld\n", sh->objectid,
269 sh->offset, bgflags);
270 if (!(bgflags & flags)) {
271 /* skip this block group */
272 sk->min_objectid = sh->objectid +
273 sh->offset;
274 sk->min_type = 0;
275 sk->min_offset = 0;
276 break;
278 im = gdImageCreate(width,
279 (sh->offset / 4096 + 799) / width);
281 black = gdImageColorAllocate(im, 0, 0, 0);
283 for (j = 0; j < ARRAY_SIZE(colors); ++j)
284 colors[j] = black;
286 init_colors(im, colors);
287 bgstart = sh->objectid;
288 bglen = sh->offset;
289 bgend = bgstart + bglen;
291 snprintf(name, sizeof(name), "bg%d.png", bgnum);
293 last_end = bgstart;
294 if (saved_len) {
295 px = (saved_extent - bgstart) / 4096;
296 for (j = 0; j < saved_len / 4096; ++j) {
297 int x = (px + j) % width;
298 int y = (px + j) / width;
299 gdImageSetPixel(im, x, y,
300 saved_color);
302 last_end += saved_len;
304 areas = 0;
305 saved_len = 0;
307 if (im && sh->type == BTRFS_EXTENT_ITEM_KEY) {
308 int c;
309 struct btrfs_extent_item *item;
311 item = (struct btrfs_extent_item *)
312 (args.buf + off);
314 if (use_color)
315 c = colors[get_color(item, sh->len)];
316 else
317 c = black;
318 if (sh->objectid > bgend) {
319 printf("WARN: extent %lld is without "
320 "block group\n", sh->objectid);
321 goto skip;
323 if (sh->objectid == bgend) {
324 saved_extent = sh->objectid;
325 saved_len = sh->offset;
326 saved_color = c;
327 goto skip;
329 px = (sh->objectid - bgstart) / 4096;
330 for (j = 0; j < sh->offset / 4096; ++j) {
331 int x = (px + j) % width;
332 int y = (px + j) / width;
333 gdImageSetPixel(im, x, y, c);
335 if (sh->objectid != last_end)
336 ++areas;
337 last_end = sh->objectid + sh->offset;
338 skip:;
340 off += sh->len;
343 * record the mins in sk so we can make sure the
344 * next search doesn't repeat this root
346 sk->min_objectid = sh->objectid;
347 sk->min_type = sh->type;
348 sk->min_offset = sh->offset;
350 sk->nr_items = 4096;
352 /* increment by one */
353 if (++sk->min_offset == 0)
354 if (++sk->min_type == 0)
355 if (++sk->min_objectid == 0)
356 break;
359 if (im) {
360 push_im(im, name, dir);
361 print_bg(html, name, bgstart, bglen, bgused, bgflags, areas);
364 if (use_color) {
365 fprintf(html, "<p>");
366 fprintf(html, "data - dark red, ");
367 fprintf(html, "fs tree - black, ");
368 fprintf(html, "extent tree - green, ");
369 fprintf(html, "csum tree - blue, ");
370 fprintf(html, "reloc tree - grey, ");
371 fprintf(html, "other trees - red, ");
372 fprintf(html, "unknown tree - dark grey");
373 fprintf(html, "</p>");
375 fprintf(html, "</body></html>\n");
377 return ret;
380 void
381 usage(void)
383 printf("usage: btrfs-fragments [options] <path>\n");
384 printf(" -c use color\n");
385 printf(" -d print data chunks\n");
386 printf(" -m print metadata chunks\n");
387 printf(" -s print system chunks\n");
388 printf(" (default is data+metadata)\n");
389 printf(" -o <dir> output directory, default is html\n");
390 exit(1);
393 int main(int argc, char **argv)
395 char *path;
396 int fd;
397 int ret;
398 u64 flags = 0;
399 char *dir = "html";
400 DIR *dirstream = NULL;
402 while (1) {
403 int c = getopt(argc, argv, "cmso:h");
404 if (c < 0)
405 break;
406 switch (c) {
407 case 'c':
408 use_color = 1;
409 break;
410 case 'd':
411 flags |= BTRFS_BLOCK_GROUP_DATA;
412 break;
413 case 'm':
414 flags |= BTRFS_BLOCK_GROUP_METADATA;
415 break;
416 case 's':
417 flags |= BTRFS_BLOCK_GROUP_SYSTEM;
418 break;
419 case 'o':
420 dir = optarg;
421 break;
422 case 'h':
423 default:
424 usage();
428 if (optind < argc) {
429 path = argv[optind++];
430 } else {
431 usage();
432 exit(1);
435 fd = open_file_or_dir(path, &dirstream);
436 if (fd < 0) {
437 fprintf(stderr, "ERROR: can't access '%s'\n", path);
438 exit(1);
441 if (flags == 0)
442 flags = BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA;
444 ret = list_fragments(fd, flags, dir);
445 close_file_or_dir(fd, dirstream);
446 if (ret)
447 exit(1);
449 exit(0);