btrfs-progs: check: enhanced progress indicator
[btrfs-progs-unstable/devel.git] / btrfs-fragments.c
blob46c78d2b44df80d822dc368c9b19a19760686330
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 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/ioctl.h>
21 #include <sys/types.h>
22 #include <dirent.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <libgen.h>
27 #include <limits.h>
28 #include <uuid/uuid.h>
29 #include <ctype.h>
31 #include <gd.h>
33 #undef ULONG_MAX
35 #include "kerncompat.h"
36 #include "ctree.h"
37 #include "ioctl.h"
38 #include "utils.h"
40 static int use_color;
41 static void
42 push_im(gdImagePtr im, char *name, char *dir)
44 char fullname[2000];
45 FILE *pngout;
47 if (!im)
48 return;
50 snprintf(fullname, sizeof(fullname), "%s/%s", dir, name);
51 pngout = fopen(fullname, "w");
52 if (!pngout) {
53 printf("unable to create file %s\n", fullname);
54 exit(1);
57 gdImagePng(im, pngout);
59 fclose(pngout);
60 gdImageDestroy(im);
63 static char *
64 chunk_type(u64 flags)
66 switch (flags & (BTRFS_BLOCK_GROUP_SYSTEM | BTRFS_BLOCK_GROUP_DATA |
67 BTRFS_BLOCK_GROUP_METADATA)) {
68 case BTRFS_BLOCK_GROUP_SYSTEM:
69 return "system";
70 case BTRFS_BLOCK_GROUP_DATA:
71 return "data";
72 case BTRFS_BLOCK_GROUP_METADATA:
73 return "metadata";
74 case BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA:
75 return "mixed";
76 default:
77 return "invalid";
81 static void
82 print_bg(FILE *html, char *name, u64 start, u64 len, u64 used, u64 flags,
83 u64 areas)
85 double frag = (double)areas / (len / 4096) * 2;
87 fprintf(html, "<p>%s chunk starts at %lld, size is %s, %.2f%% used, "
88 "%.2f%% fragmented</p>\n", chunk_type(flags), start,
89 pretty_size(len), 100.0 * used / len, 100.0 * frag);
90 fprintf(html, "<img src=\"%s\" border=\"1\" />\n", name);
93 enum tree_colors {
94 COLOR_ROOT = 0,
95 COLOR_EXTENT,
96 COLOR_CHUNK,
97 COLOR_DEV,
98 COLOR_FS,
99 COLOR_CSUM,
100 COLOR_RELOC,
101 COLOR_DATA,
102 COLOR_UNKNOWN,
103 COLOR_MAX
106 static int
107 get_color(struct btrfs_extent_item *item, int len)
109 u64 refs;
110 u64 flags;
111 u8 type;
112 u64 offset;
113 struct btrfs_extent_inline_ref *ref;
115 refs = btrfs_stack_extent_refs(item);
116 flags = btrfs_stack_extent_flags(item);
118 if (flags & BTRFS_EXTENT_FLAG_DATA)
119 return COLOR_DATA;
120 if (refs > 1) {
121 /* this must be an fs tree */
122 return COLOR_FS;
125 ref = (void *)item + sizeof(struct btrfs_extent_item) +
126 sizeof(struct btrfs_tree_block_info);
127 type = btrfs_stack_extent_inline_ref_type(ref);
128 offset = btrfs_stack_extent_inline_ref_offset(ref);
130 switch (type) {
131 case BTRFS_EXTENT_DATA_REF_KEY:
132 return COLOR_DATA;
133 case BTRFS_SHARED_BLOCK_REF_KEY:
134 case BTRFS_SHARED_DATA_REF_KEY:
135 return COLOR_FS;
136 case BTRFS_TREE_BLOCK_REF_KEY:
137 break;
138 default:
139 return COLOR_UNKNOWN;
142 switch (offset) {
143 case BTRFS_ROOT_TREE_OBJECTID:
144 return COLOR_ROOT;
145 case BTRFS_EXTENT_TREE_OBJECTID:
146 return COLOR_EXTENT;
147 case BTRFS_CHUNK_TREE_OBJECTID:
148 return COLOR_CHUNK;
149 case BTRFS_DEV_TREE_OBJECTID:
150 return COLOR_DEV;
151 case BTRFS_FS_TREE_OBJECTID:
152 return COLOR_FS;
153 case BTRFS_CSUM_TREE_OBJECTID:
154 return COLOR_CSUM;
155 case BTRFS_DATA_RELOC_TREE_OBJECTID:
156 return COLOR_RELOC;
159 return COLOR_UNKNOWN;
162 static void
163 init_colors(gdImagePtr im, int *colors)
165 colors[COLOR_ROOT] = gdImageColorAllocate(im, 255, 0, 0);
166 colors[COLOR_EXTENT] = gdImageColorAllocate(im, 0, 255, 0);
167 colors[COLOR_CHUNK] = gdImageColorAllocate(im, 255, 0, 0);
168 colors[COLOR_DEV] = gdImageColorAllocate(im, 255, 0, 0);
169 colors[COLOR_FS] = gdImageColorAllocate(im, 0, 0, 0);
170 colors[COLOR_CSUM] = gdImageColorAllocate(im, 0, 0, 255);
171 colors[COLOR_RELOC] = gdImageColorAllocate(im, 128, 128, 128);
172 colors[COLOR_DATA] = gdImageColorAllocate(im, 100, 0, 0);
173 colors[COLOR_UNKNOWN] = gdImageColorAllocate(im, 50, 50, 50);
177 list_fragments(int fd, u64 flags, char *dir)
179 int ret;
180 struct btrfs_ioctl_search_args args;
181 struct btrfs_ioctl_search_key *sk = &args.key;
182 int i;
183 struct btrfs_ioctl_search_header *sh;
184 unsigned long off = 0;
185 int bgnum = 0;
186 u64 bgstart = 0;
187 u64 bglen = 0;
188 u64 bgend = 0;
189 u64 bgflags = 0;
190 u64 bgused = 0;
191 u64 saved_extent = 0;
192 u64 saved_len = 0;
193 int saved_color = 0;
194 u64 last_end = 0;
195 u64 areas = 0;
196 long px;
197 char name[1000];
198 FILE *html;
199 int colors[COLOR_MAX];
201 gdImagePtr im = NULL;
202 int black = 0;
203 int width = 800;
205 snprintf(name, sizeof(name), "%s/index.html", dir);
206 html = fopen(name, "w");
207 if (!html) {
208 printf("unable to create %s\n", name);
209 exit(1);
212 fprintf(html, "<html><header>\n");
213 fprintf(html, "<title>Btrfs Block Group Allocation Map</title>\n");
214 fprintf(html, "<style type=\"text/css\">\n");
215 fprintf(html, "img {margin-left: 1em; margin-bottom: 2em;}\n");
216 fprintf(html, "</style>\n");
217 fprintf(html, "</header><body>\n");
219 memset(&args, 0, sizeof(args));
221 sk->tree_id = BTRFS_EXTENT_TREE_OBJECTID;
222 sk->max_type = -1;
223 sk->min_type = 0;
224 sk->max_objectid = (u64)-1;
225 sk->max_offset = (u64)-1;
226 sk->max_transid = (u64)-1;
228 /* just a big number, doesn't matter much */
229 sk->nr_items = 4096;
231 while(1) {
232 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
233 if (ret < 0) {
234 fprintf(stderr, "ERROR: can't perform the search\n");
235 goto out_close;
237 /* the ioctl returns the number of item it found in nr_items */
238 if (sk->nr_items == 0)
239 break;
241 off = 0;
242 for (i = 0; i < sk->nr_items; i++) {
243 int j;
245 sh = (struct btrfs_ioctl_search_header *)(args.buf +
246 off);
247 off += sizeof(*sh);
248 if (btrfs_search_header_type(sh)
249 == 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",
269 btrfs_search_header_objectid(sh),
270 btrfs_search_header_offset(sh),
271 bgflags);
272 if (!(bgflags & flags)) {
273 /* skip this block group */
274 sk->min_objectid =
275 btrfs_search_header_objectid(sh) +
276 btrfs_search_header_offset(sh);
277 sk->min_type = 0;
278 sk->min_offset = 0;
279 break;
281 im = gdImageCreate(width,
282 (btrfs_search_header_offset(sh)
283 / 4096 + 799) / width);
285 black = gdImageColorAllocate(im, 0, 0, 0);
287 for (j = 0; j < ARRAY_SIZE(colors); ++j)
288 colors[j] = black;
290 init_colors(im, colors);
291 bgstart = btrfs_search_header_objectid(sh);
292 bglen = btrfs_search_header_offset(sh);
293 bgend = bgstart + bglen;
295 snprintf(name, sizeof(name), "bg%d.png", bgnum);
297 last_end = bgstart;
298 if (saved_len) {
299 px = (saved_extent - bgstart) / 4096;
300 for (j = 0; j < saved_len / 4096; ++j) {
301 int x = (px + j) % width;
302 int y = (px + j) / width;
303 gdImageSetPixel(im, x, y,
304 saved_color);
306 last_end += saved_len;
308 areas = 0;
309 saved_len = 0;
311 if (im && btrfs_search_header_type(sh)
312 == BTRFS_EXTENT_ITEM_KEY) {
313 int c;
314 struct btrfs_extent_item *item;
316 item = (struct btrfs_extent_item *)
317 (args.buf + off);
319 if (use_color)
320 c = colors[get_color(item,
321 btrfs_search_header_len(sh))];
322 else
323 c = black;
324 if (btrfs_search_header_objectid(sh) > bgend) {
325 printf("WARN: extent %lld is without "
326 "block group\n",
327 btrfs_search_header_objectid(sh));
328 goto skip;
330 if (btrfs_search_header_objectid(sh) == bgend) {
331 saved_extent =
332 btrfs_search_header_objectid(sh);
333 saved_len =
334 btrfs_search_header_offset(sh);
335 saved_color = c;
336 goto skip;
338 px = (btrfs_search_header_objectid(sh)
339 - bgstart) / 4096;
340 for (j = 0;
341 j < btrfs_search_header_offset(sh) / 4096;
342 ++j) {
343 int x = (px + j) % width;
344 int y = (px + j) / width;
345 gdImageSetPixel(im, x, y, c);
347 if (btrfs_search_header_objectid(sh) != last_end)
348 ++areas;
349 last_end = btrfs_search_header_objectid(sh)
350 + btrfs_search_header_offset(sh);
351 skip:;
353 off += btrfs_search_header_len(sh);
356 * record the mins in sk so we can make sure the
357 * next search doesn't repeat this root
359 sk->min_objectid = btrfs_search_header_objectid(sh);
360 sk->min_type = btrfs_search_header_type(sh);
361 sk->min_offset = btrfs_search_header_offset(sh);
363 sk->nr_items = 4096;
365 /* increment by one */
366 if (++sk->min_offset == 0)
367 if (++sk->min_type == 0)
368 if (++sk->min_objectid == 0)
369 break;
372 if (im) {
373 push_im(im, name, dir);
374 print_bg(html, name, bgstart, bglen, bgused, bgflags, areas);
377 if (use_color) {
378 fprintf(html, "<p>");
379 fprintf(html, "data - dark red, ");
380 fprintf(html, "fs tree - black, ");
381 fprintf(html, "extent tree - green, ");
382 fprintf(html, "csum tree - blue, ");
383 fprintf(html, "reloc tree - grey, ");
384 fprintf(html, "other trees - red, ");
385 fprintf(html, "unknown tree - dark grey");
386 fprintf(html, "</p>");
388 fprintf(html, "</body></html>\n");
390 out_close:
391 fclose(html);
393 return ret;
396 void fragments_usage(void)
398 printf("usage: btrfs-fragments [options] <path>\n");
399 printf(" -c use color\n");
400 printf(" -d print data chunks\n");
401 printf(" -m print metadata chunks\n");
402 printf(" -s print system chunks\n");
403 printf(" (default is data+metadata)\n");
404 printf(" -o <dir> output directory, default is html\n");
405 exit(1);
408 int main(int argc, char **argv)
410 char *path;
411 int fd;
412 int ret;
413 u64 flags = 0;
414 char *dir = "html";
415 DIR *dirstream = NULL;
417 while (1) {
418 int c = getopt(argc, argv, "cmso:h");
419 if (c < 0)
420 break;
421 switch (c) {
422 case 'c':
423 use_color = 1;
424 break;
425 case 'd':
426 flags |= BTRFS_BLOCK_GROUP_DATA;
427 break;
428 case 'm':
429 flags |= BTRFS_BLOCK_GROUP_METADATA;
430 break;
431 case 's':
432 flags |= BTRFS_BLOCK_GROUP_SYSTEM;
433 break;
434 case 'o':
435 dir = optarg;
436 break;
437 case 'h':
438 default:
439 fragments_usage();
443 set_argv0(argv);
444 if (check_argc_min(argc - optind, 1))
445 fragments_usage();
447 path = argv[optind++];
449 fd = btrfs_open_dir(path, &dirstream, 1);
450 if (fd < 0)
451 exit(1);
453 if (flags == 0)
454 flags = BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA;
456 ret = list_fragments(fd, flags, dir);
457 close_file_or_dir(fd, dirstream);
458 if (ret)
459 exit(1);
461 exit(0);