btrfs-progs: check: add support to clear v1 free space cache
[platform/upstream/btrfs-progs.git] / btrfs-fragments.c
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.
5  *
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.
10  *
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.
15  */
16
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>
30
31 #include <gd.h>
32
33 #undef ULONG_MAX
34
35 #include "kerncompat.h"
36 #include "ctree.h"
37 #include "ioctl.h"
38 #include "utils.h"
39
40 static int use_color;
41 static void
42 push_im(gdImagePtr im, char *name, char *dir)
43 {
44         char fullname[2000];
45         FILE *pngout;
46
47         if (!im)
48                 return;
49
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);
55         }
56
57         gdImagePng(im, pngout);
58
59         fclose(pngout);
60         gdImageDestroy(im);
61 }
62
63 static char *
64 chunk_type(u64 flags)
65 {
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";
78         }
79 }
80
81 static void
82 print_bg(FILE *html, char *name, u64 start, u64 len, u64 used, u64 flags,
83          u64 areas)
84 {
85         double frag = (double)areas / (len / 4096) * 2;
86
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);
91 }
92
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
104 };
105
106 static int
107 get_color(struct btrfs_extent_item *item, int len)
108 {
109         u64 refs;
110         u64 flags;
111         u8 type;
112         u64 offset;
113         struct btrfs_extent_inline_ref *ref;
114
115         refs = btrfs_stack_extent_refs(item);
116         flags = btrfs_stack_extent_flags(item);
117
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;
123         }
124
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);
129
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;
140         }
141
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;
157         }
158
159         return COLOR_UNKNOWN;
160 }
161
162 static void
163 init_colors(gdImagePtr im, int *colors)
164 {
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);
174 }
175
176 int
177 list_fragments(int fd, u64 flags, char *dir)
178 {
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];
200
201         gdImagePtr im = NULL;
202         int black = 0;
203         int width = 800;
204
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);
210         }
211
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");
218         
219         memset(&args, 0, sizeof(args));
220
221         sk->tree_id = 2;
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;
227
228         /* just a big number, doesn't matter much */
229         sk->nr_items = 4096;
230
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;
236                 }
237                 /* the ioctl returns the number of item it found in nr_items */
238                 if (sk->nr_items == 0)
239                         break;
240
241                 off = 0;
242                 for (i = 0; i < sk->nr_items; i++) {
243                         int j;
244
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;
251
252                                 if (im) {
253                                         push_im(im, name, dir);
254                                         im = NULL;
255
256                                         print_bg(html, name, bgstart, bglen,
257                                                 bgused, bgflags, areas);
258                                 }
259
260                                 ++bgnum;
261
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);
266
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;
280                                 }
281                                 im = gdImageCreate(width,
282                                         (btrfs_search_header_offset(sh)
283                                          / 4096 + 799) / width);
284
285                                 black = gdImageColorAllocate(im, 0, 0, 0);  
286
287                                 for (j = 0; j < ARRAY_SIZE(colors); ++j)
288                                         colors[j] = black;
289
290                                 init_colors(im, colors);
291                                 bgstart = btrfs_search_header_objectid(sh);
292                                 bglen = btrfs_search_header_offset(sh);
293                                 bgend = bgstart + bglen;
294
295                                 snprintf(name, sizeof(name), "bg%d.png", bgnum);
296
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);
305                                         }
306                                         last_end += saved_len;
307                                 }
308                                 areas = 0;
309                                 saved_len = 0;
310                         }
311                         if (im && btrfs_search_header_type(sh)
312                                         == BTRFS_EXTENT_ITEM_KEY) {
313                                 int c;
314                                 struct btrfs_extent_item *item;
315
316                                 item = (struct btrfs_extent_item *)
317                                                 (args.buf + off);
318
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;
329                                 }
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;
337                                 }
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);
346                                 }
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:;
352                         }
353                         off += btrfs_search_header_len(sh);
354
355                         /*
356                          * record the mins in sk so we can make sure the
357                          * next search doesn't repeat this root
358                          */
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);
362                 }
363                 sk->nr_items = 4096;
364
365                 /* increment by one */
366                 if (++sk->min_offset == 0)
367                         if (++sk->min_type == 0)
368                                 if (++sk->min_objectid == 0)
369                                         break;
370         }
371
372         if (im) {
373                 push_im(im, name, dir);
374                 print_bg(html, name, bgstart, bglen, bgused, bgflags, areas);
375         }
376
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>");
387         }
388         fprintf(html, "</body></html>\n");
389
390 out_close:
391         fclose(html);
392
393         return ret;
394 }
395
396 void fragments_usage(void)
397 {
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);
406 }
407
408 int main(int argc, char **argv)
409 {
410         char *path;
411         int fd;
412         int ret;
413         u64 flags = 0;
414         char *dir = "html";
415         DIR *dirstream = NULL;
416
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();
440                 }
441         }
442
443         set_argv0(argv);
444         if (check_argc_min(argc - optind, 1))
445                 fragments_usage();
446
447         path = argv[optind++];
448
449         fd = btrfs_open_dir(path, &dirstream, 1);
450         if (fd < 0)
451                 exit(1);
452
453         if (flags == 0)
454                 flags = BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA;
455
456         ret = list_fragments(fd, flags, dir);
457         close_file_or_dir(fd, dirstream);
458         if (ret)
459                 exit(1);
460
461         exit(0);
462 }