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.
20 #include <sys/ioctl.h>
21 #include <sys/types.h>
28 #include <uuid/uuid.h>
35 #include "kerncompat.h"
42 push_im(gdImagePtr im, char *name, char *dir)
50 snprintf(fullname, sizeof(fullname), "%s/%s", dir, name);
51 pngout = fopen(fullname, "w");
53 printf("unable to create file %s\n", fullname);
57 gdImagePng(im, pngout);
66 switch (flags & (BTRFS_BLOCK_GROUP_SYSTEM | BTRFS_BLOCK_GROUP_DATA |
67 BTRFS_BLOCK_GROUP_METADATA)) {
68 case BTRFS_BLOCK_GROUP_SYSTEM:
70 case BTRFS_BLOCK_GROUP_DATA:
72 case BTRFS_BLOCK_GROUP_METADATA:
74 case BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA:
82 print_bg(FILE *html, char *name, u64 start, u64 len, u64 used, u64 flags,
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);
107 get_color(struct btrfs_extent_item *item, int len)
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)
121 /* this must be an fs tree */
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);
131 case BTRFS_EXTENT_DATA_REF_KEY:
133 case BTRFS_SHARED_BLOCK_REF_KEY:
134 case BTRFS_SHARED_DATA_REF_KEY:
136 case BTRFS_TREE_BLOCK_REF_KEY:
139 return COLOR_UNKNOWN;
143 case BTRFS_ROOT_TREE_OBJECTID:
145 case BTRFS_EXTENT_TREE_OBJECTID:
147 case BTRFS_CHUNK_TREE_OBJECTID:
149 case BTRFS_DEV_TREE_OBJECTID:
151 case BTRFS_FS_TREE_OBJECTID:
153 case BTRFS_CSUM_TREE_OBJECTID:
155 case BTRFS_DATA_RELOC_TREE_OBJECTID:
159 return COLOR_UNKNOWN;
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)
180 struct btrfs_ioctl_search_args args;
181 struct btrfs_ioctl_search_key *sk = &args.key;
183 struct btrfs_ioctl_search_header *sh;
184 unsigned long off = 0;
191 u64 saved_extent = 0;
199 int colors[COLOR_MAX];
201 gdImagePtr im = NULL;
205 snprintf(name, sizeof(name), "%s/index.html", dir);
206 html = fopen(name, "w");
208 printf("unable to create %s\n", name);
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));
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 */
232 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
234 fprintf(stderr, "ERROR: can't perform the search\n");
237 /* the ioctl returns the number of item it found in nr_items */
238 if (sk->nr_items == 0)
242 for (i = 0; i < sk->nr_items; i++) {
245 sh = (struct btrfs_ioctl_search_header *)(args.buf +
248 if (sh->type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
249 struct btrfs_block_group_item *bg;
252 push_im(im, name, dir);
255 print_bg(html, name, bgstart, bglen,
256 bgused, bgflags, areas);
261 bg = (struct btrfs_block_group_item *)
263 bgflags = btrfs_block_group_flags(bg);
264 bgused = btrfs_block_group_used(bg);
266 printf("found block group %lld len %lld "
267 "flags %lld\n", sh->objectid,
268 sh->offset, bgflags);
269 if (!(bgflags & flags)) {
270 /* skip this block group */
271 sk->min_objectid = sh->objectid +
277 im = gdImageCreate(width,
278 (sh->offset / 4096 + 799) / width);
280 black = gdImageColorAllocate(im, 0, 0, 0);
282 for (j = 0; j < ARRAY_SIZE(colors); ++j)
285 init_colors(im, colors);
286 bgstart = sh->objectid;
288 bgend = bgstart + bglen;
290 snprintf(name, sizeof(name), "bg%d.png", bgnum);
294 px = (saved_extent - bgstart) / 4096;
295 for (j = 0; j < saved_len / 4096; ++j) {
296 int x = (px + j) % width;
297 int y = (px + j) / width;
298 gdImageSetPixel(im, x, y,
301 last_end += saved_len;
306 if (im && sh->type == BTRFS_EXTENT_ITEM_KEY) {
308 struct btrfs_extent_item *item;
310 item = (struct btrfs_extent_item *)
314 c = colors[get_color(item, sh->len)];
317 if (sh->objectid > bgend) {
318 printf("WARN: extent %lld is without "
319 "block group\n", sh->objectid);
322 if (sh->objectid == bgend) {
323 saved_extent = sh->objectid;
324 saved_len = sh->offset;
328 px = (sh->objectid - bgstart) / 4096;
329 for (j = 0; j < sh->offset / 4096; ++j) {
330 int x = (px + j) % width;
331 int y = (px + j) / width;
332 gdImageSetPixel(im, x, y, c);
334 if (sh->objectid != last_end)
336 last_end = sh->objectid + sh->offset;
342 * record the mins in sk so we can make sure the
343 * next search doesn't repeat this root
345 sk->min_objectid = sh->objectid;
346 sk->min_type = sh->type;
347 sk->min_offset = sh->offset;
351 /* increment by one */
352 if (++sk->min_offset == 0)
353 if (++sk->min_type == 0)
354 if (++sk->min_objectid == 0)
359 push_im(im, name, dir);
360 print_bg(html, name, bgstart, bglen, bgused, bgflags, areas);
364 fprintf(html, "<p>");
365 fprintf(html, "data - dark red, ");
366 fprintf(html, "fs tree - black, ");
367 fprintf(html, "extent tree - green, ");
368 fprintf(html, "csum tree - blue, ");
369 fprintf(html, "reloc tree - grey, ");
370 fprintf(html, "other trees - red, ");
371 fprintf(html, "unknown tree - dark grey");
372 fprintf(html, "</p>");
374 fprintf(html, "</body></html>\n");
385 printf("usage: btrfs-fragments [options] <path>\n");
386 printf(" -c use color\n");
387 printf(" -d print data chunks\n");
388 printf(" -m print metadata chunks\n");
389 printf(" -s print system chunks\n");
390 printf(" (default is data+metadata)\n");
391 printf(" -o <dir> output directory, default is html\n");
395 int main(int argc, char **argv)
402 DIR *dirstream = NULL;
405 int c = getopt(argc, argv, "cmso:h");
413 flags |= BTRFS_BLOCK_GROUP_DATA;
416 flags |= BTRFS_BLOCK_GROUP_METADATA;
419 flags |= BTRFS_BLOCK_GROUP_SYSTEM;
431 if (check_argc_min(argc - optind, 1)) {
436 path = argv[optind++];
438 fd = btrfs_open_dir(path, &dirstream, 1);
443 flags = BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA;
445 ret = list_fragments(fd, flags, dir);
446 close_file_or_dir(fd, dirstream);