Btrfs-progs: enhance btrfs qgroup to print the result as a table
[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 #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>
31
32 #include <gd.h>
33
34 #undef ULONG_MAX
35
36 #include "kerncompat.h"
37 #include "ctree.h"
38 #include "ioctl.h"
39 #include "utils.h"
40
41 static int use_color;
42 static void
43 push_im(gdImagePtr im, char *name, char *dir)
44 {
45         char fullname[2000];
46         FILE *pngout;
47
48         if (!im)
49                 return;
50
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);
56         }
57
58         gdImagePng(im, pngout);
59
60         fclose(pngout);
61         gdImageDestroy(im);
62 }
63
64 static char *
65 chunk_type(u64 flags)
66 {
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";
79         }
80 }
81
82 static void
83 print_bg(FILE *html, char *name, u64 start, u64 len, u64 used, u64 flags,
84          u64 areas)
85 {
86         double frag = (double)areas / (len / 4096) * 2;
87
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);
92 }
93
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
105 };
106
107 static int
108 get_color(struct btrfs_extent_item *item, int len)
109 {
110         u64 refs;
111         u64 flags;
112         u8 type;
113         u64 offset;
114         struct btrfs_extent_inline_ref *ref;
115
116         refs = btrfs_stack_extent_refs(item);
117         flags = btrfs_stack_extent_flags(item);
118
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;
124         }
125
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);
130
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;
141         }
142
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;
158         }
159
160         return COLOR_UNKNOWN;
161 }
162
163 static void
164 init_colors(gdImagePtr im, int *colors)
165 {
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);
175 }
176
177 int
178 list_fragments(int fd, u64 flags, char *dir)
179 {
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         u64 saved_flags = 0;
195         int saved_color = 0;
196         u64 last_end = 0;
197         u64 areas = 0;
198         long px;
199         char name[1000];
200         FILE *html;
201         int colors[COLOR_MAX];
202
203         gdImagePtr im = NULL;
204         int black = 0;
205         int white = 0;
206         int width = 800;
207
208         snprintf(name, sizeof(name), "%s/index.html", dir);
209         html = fopen(name, "w");
210         if (!html) {
211                 printf("unable to create %s\n", name);
212                 exit(1);
213         }
214
215         fprintf(html, "<html><header>\n");
216         fprintf(html, "<title>Btrfs Block Group Allocation Map</title>\n");
217         fprintf(html, "<style type=\"text/css\">\n");
218         fprintf(html, "img {margin-left: 1em; margin-bottom: 2em;}\n");
219         fprintf(html, "</style>\n");
220         fprintf(html, "</header><body>\n");
221         
222         memset(&args, 0, sizeof(args));
223
224         sk->tree_id = 2;
225         sk->max_type = -1;
226         sk->min_type = 0;
227         sk->max_objectid = (u64)-1;
228         sk->max_offset = (u64)-1;
229         sk->max_transid = (u64)-1;
230
231         /* just a big number, doesn't matter much */
232         sk->nr_items = 4096;
233
234         while(1) {
235                 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
236                 if (ret < 0) {
237                         fprintf(stderr, "ERROR: can't perform the search\n");
238                         return ret;
239                 }
240                 /* the ioctl returns the number of item it found in nr_items */
241                 if (sk->nr_items == 0)
242                         break;
243
244                 off = 0;
245                 for (i = 0; i < sk->nr_items; i++) {
246                         int j;
247
248                         sh = (struct btrfs_ioctl_search_header *)(args.buf +
249                                                                   off);
250                         off += sizeof(*sh);
251                         if (sh->type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
252                                 struct btrfs_block_group_item *bg;
253
254                                 if (im) {
255                                         push_im(im, name, dir);
256                                         im = NULL;
257
258                                         print_bg(html, name, bgstart, bglen,
259                                                 bgused, bgflags, areas);
260                                 }
261
262                                 ++bgnum;
263
264                                 bg = (struct btrfs_block_group_item *)
265                                                 (args.buf + off);
266                                 bgflags = btrfs_block_group_flags(bg);
267                                 bgused = btrfs_block_group_used(bg);
268                                 
269                                 printf("found block group %lld len %lld "
270                                         "flags %lld\n", sh->objectid,
271                                         sh->offset, bgflags);
272                                 if (!(bgflags & flags)) {
273                                         /* skip this block group */
274                                         sk->min_objectid = sh->objectid +
275                                                            sh->offset;
276                                         sk->min_type = 0;
277                                         sk->min_offset = 0;
278                                         break;
279                                 }
280                                 im = gdImageCreate(width,
281                                         (sh->offset / 4096 + 799) / width);
282
283                                 white = gdImageColorAllocate(im, 255, 255, 255);
284                                 black = gdImageColorAllocate(im, 0, 0, 0);  
285
286                                 for (j = 0; j < 10; ++j)
287                                         colors[j] = black;
288
289                                 init_colors(im, colors);
290                                 bgstart = sh->objectid;
291                                 bglen = sh->offset;
292                                 bgend = bgstart + bglen;
293
294                                 snprintf(name, sizeof(name), "bg%d.png", bgnum);
295
296                                 last_end = bgstart;
297                                 if (saved_len) {
298                                         px = (saved_extent - bgstart) / 4096;
299                                         for (j = 0; j < saved_len / 4096; ++j) {
300                                                 int x = (px + j) % width;
301                                                 int y = (px + j) / width;
302                                                 gdImageSetPixel(im, x, y,
303                                                                 saved_color);
304                                         }
305                                         last_end += saved_len;
306                                 }
307                                 areas = 0;
308                                 saved_len = 0;
309                         }
310                         if (im && sh->type == BTRFS_EXTENT_ITEM_KEY) {
311                                 u64 e_flags;
312                                 int c;
313                                 struct btrfs_extent_item *item;
314
315                                 item = (struct btrfs_extent_item *)
316                                                 (args.buf + off);
317                                 e_flags = btrfs_stack_extent_flags(item);
318
319                                 if (use_color)
320                                         c = colors[get_color(item, sh->len)];
321                                 else
322                                         c = black;
323                                 if (sh->objectid > bgend) {
324                                         printf("WARN: extent %lld is without "
325                                                 "block group\n", sh->objectid);
326                                         goto skip;
327                                 }
328                                 if (sh->objectid == bgend) {
329                                         saved_extent = sh->objectid;
330                                         saved_len = sh->offset;
331                                         saved_flags = e_flags;
332                                         saved_color = c;
333                                         goto skip;
334                                 }
335                                 px = (sh->objectid - bgstart) / 4096;
336                                 for (j = 0; j < sh->offset / 4096; ++j) {
337                                         int x = (px + j) % width;
338                                         int y = (px + j) / width;
339                                         gdImageSetPixel(im, x, y, c);
340                                 }
341                                 if (sh->objectid != last_end)
342                                         ++areas;
343                                 last_end = sh->objectid + sh->offset;
344 skip:;
345                         }
346                         off += sh->len;
347
348                         /*
349                          * record the mins in sk so we can make sure the
350                          * next search doesn't repeat this root
351                          */
352                         sk->min_objectid = sh->objectid;
353                         sk->min_type = sh->type;
354                         sk->min_offset = sh->offset;
355                 }
356                 sk->nr_items = 4096;
357
358                 /* increment by one */
359                 if (++sk->min_offset == 0)
360                         if (++sk->min_type == 0)
361                                 if (++sk->min_objectid == 0)
362                                         break;
363         }
364
365         if (im) {
366                 push_im(im, name, dir);
367                 print_bg(html, name, bgstart, bglen, bgused, bgflags, areas);
368         }
369
370         if (use_color) {
371                 fprintf(html, "<p>");
372                 fprintf(html, "data - dark red, ");
373                 fprintf(html, "fs tree - black, ");
374                 fprintf(html, "extent tree - green, ");
375                 fprintf(html, "csum tree - blue, ");
376                 fprintf(html, "reloc tree - grey, ");
377                 fprintf(html, "other trees - red, ");
378                 fprintf(html, "unknown tree - dark grey");
379                 fprintf(html, "</p>");
380         }
381         fprintf(html, "</body></html>\n");
382         
383         return ret;
384 }
385
386 void
387 usage(void)
388 {
389         printf("usage: btrfs-fragments [options] <path>\n");
390         printf("         -c               use color\n");
391         printf("         -d               print data chunks\n");
392         printf("         -m               print metadata chunks\n");
393         printf("         -s               print system chunks\n");
394         printf("                          (default is data+metadata)\n");
395         printf("         -o <dir>         output directory, default is html\n");
396         exit(1);
397 }
398
399 int main(int argc, char **argv)
400 {
401         char *path;
402         int fd;
403         int ret;
404         u64 flags = 0;
405         char *dir = "html";
406         DIR *dirstream = NULL;
407
408         while (1) {
409                 int c = getopt(argc, argv, "cmso:h");
410                 if (c < 0)
411                         break;
412                 switch (c) {
413                 case 'c':
414                         use_color = 1;
415                         break;
416                 case 'd':
417                         flags |= BTRFS_BLOCK_GROUP_DATA;
418                         break;
419                 case 'm':
420                         flags |= BTRFS_BLOCK_GROUP_METADATA;
421                         break;
422                 case 's':
423                         flags |= BTRFS_BLOCK_GROUP_SYSTEM;
424                         break;
425                 case 'o':
426                         dir = optarg;
427                         break;
428                 case 'h':
429                 default:
430                         usage();
431                 }
432         }
433
434         if (optind < argc) {
435                 path = argv[optind++];
436         } else {
437                 usage();
438                 exit(1);
439         }
440
441         fd = open_file_or_dir(path, &dirstream);
442         if (fd < 0) {
443                 fprintf(stderr, "ERROR: can't access '%s'\n", path);
444                 exit(1);
445         }
446
447         if (flags == 0)
448                 flags = BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA;
449
450         ret = list_fragments(fd, flags, dir);
451         close_file_or_dir(fd, dirstream);
452         if (ret)
453                 exit(1);
454
455         exit(0);
456 }