btrfs-progs: drop feature defines from C files, in favour of CFLAGS defines
[platform/upstream/btrfs-progs.git] / btrfs-calc-size.c
1 /*
2  * Copyright (C) 2011 Red Hat.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License v2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public
14  * License along with this program; if not, write to the
15  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16  * Boston, MA 021110-1307, USA.
17  */
18
19 #include <ctype.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <zlib.h>
28 #include "kerncompat.h"
29 #include "ctree.h"
30 #include "disk-io.h"
31 #include "print-tree.h"
32 #include "transaction.h"
33 #include "list.h"
34 #include "version.h"
35 #include "volumes.h"
36 #include "utils.h"
37
38 static int verbose = 0;
39 static int no_pretty = 0;
40
41 struct seek {
42         u64 distance;
43         u64 count;
44         struct rb_node n;
45 };
46
47 struct root_stats {
48         u64 total_nodes;
49         u64 total_leaves;
50         u64 total_bytes;
51         u64 total_inline;
52         u64 total_seeks;
53         u64 forward_seeks;
54         u64 backward_seeks;
55         u64 total_seek_len;
56         u64 max_seek_len;
57         u64 total_clusters;
58         u64 total_cluster_size;
59         u64 min_cluster_size;
60         u64 max_cluster_size;
61         u64 lowest_bytenr;
62         u64 highest_bytenr;
63         struct rb_root seek_root;
64         int total_levels;
65 };
66
67 struct fs_root {
68         struct btrfs_key key;
69         struct btrfs_key *snaps;
70 };
71
72 static int add_seek(struct rb_root *root, u64 dist)
73 {
74         struct rb_node **p = &root->rb_node;
75         struct rb_node *parent = NULL;
76         struct seek *seek = NULL;
77
78         while (*p) {
79                 parent = *p;
80                 seek = rb_entry(parent, struct seek, n);
81
82                 if (dist < seek->distance) {
83                         p = &(*p)->rb_left;
84                 } else if (dist > seek->distance) {
85                         p = &(*p)->rb_right;
86                 } else {
87                         seek->count++;
88                         return 0;
89                 }
90         }
91
92         seek = malloc(sizeof(struct seek));
93         if (!seek)
94                 return -ENOMEM;
95         seek->distance = dist;
96         seek->count = 1;
97         rb_link_node(&seek->n, parent, p);
98         rb_insert_color(&seek->n, root);
99         return 0;
100 }
101
102 static int walk_leaf(struct btrfs_root *root, struct btrfs_path *path,
103                      struct root_stats *stat, int find_inline)
104 {
105         struct extent_buffer *b = path->nodes[0];
106         struct btrfs_file_extent_item *fi;
107         struct btrfs_key found_key;
108         int i;
109
110         stat->total_bytes += root->leafsize;
111         stat->total_leaves++;
112
113         if (!find_inline)
114                 return 0;
115
116         for (i = 0; i < btrfs_header_nritems(b); i++) {
117                 btrfs_item_key_to_cpu(b, &found_key, i);
118                 if (found_key.type != BTRFS_EXTENT_DATA_KEY)
119                         continue;
120
121                 fi = btrfs_item_ptr(b, i, struct btrfs_file_extent_item);
122                 if (btrfs_file_extent_type(b, fi) == BTRFS_FILE_EXTENT_INLINE)
123                         stat->total_inline +=
124                                 btrfs_file_extent_inline_item_len(b,
125                                                         btrfs_item_nr(i));
126         }
127
128         return 0;
129 }
130
131 static u64 calc_distance(u64 block1, u64 block2)
132 {
133         if (block1 < block2)
134                 return block2 - block1;
135         return block1 - block2;
136 }
137
138 static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path,
139                       struct root_stats *stat, int level, int find_inline)
140 {
141         struct extent_buffer *b = path->nodes[level];
142         u64 last_block;
143         u64 cluster_size = root->leafsize;
144         int i;
145         int ret = 0;
146
147         stat->total_bytes += root->nodesize;
148         stat->total_nodes++;
149
150         last_block = btrfs_header_bytenr(b);
151         for (i = 0; i < btrfs_header_nritems(b); i++) {
152                 struct extent_buffer *tmp = NULL;
153                 u64 cur_blocknr = btrfs_node_blockptr(b, i);
154
155                 path->slots[level] = i;
156                 if ((level - 1) > 0 || find_inline) {
157                         tmp = read_tree_block(root, cur_blocknr,
158                                               btrfs_level_size(root, level - 1),
159                                               btrfs_node_ptr_generation(b, i));
160                         if (!tmp) {
161                                 fprintf(stderr, "Failed to read blocknr %Lu\n",
162                                         btrfs_node_blockptr(b, i));
163                                 continue;
164                         }
165                         path->nodes[level - 1] = tmp;
166                 }
167                 if (level - 1)
168                         ret = walk_nodes(root, path, stat, level - 1,
169                                          find_inline);
170                 else
171                         ret = walk_leaf(root, path, stat, find_inline);
172                 if (last_block + root->leafsize != cur_blocknr) {
173                         u64 distance = calc_distance(last_block +
174                                                      root->leafsize,
175                                                      cur_blocknr);
176                         stat->total_seeks++;
177                         stat->total_seek_len += distance;
178                         if (stat->max_seek_len < distance)
179                                 stat->max_seek_len = distance;
180                         if (add_seek(&stat->seek_root, distance)) {
181                                 fprintf(stderr, "Error adding new seek\n");
182                                 ret = -ENOMEM;
183                                 break;
184                         }
185
186                         if (last_block < cur_blocknr)
187                                 stat->forward_seeks++;
188                         else
189                                 stat->backward_seeks++;
190                         if (cluster_size != root->leafsize) {
191                                 stat->total_cluster_size += cluster_size;
192                                 stat->total_clusters++;
193                                 if (cluster_size < stat->min_cluster_size)
194                                         stat->min_cluster_size = cluster_size;
195                                 if (cluster_size > stat->max_cluster_size)
196                                         stat->max_cluster_size = cluster_size;
197                         }
198                         cluster_size = root->leafsize;
199                 } else {
200                         cluster_size += root->leafsize;
201                 }
202                 last_block = cur_blocknr;
203                 if (cur_blocknr < stat->lowest_bytenr)
204                         stat->lowest_bytenr = cur_blocknr;
205                 if (cur_blocknr > stat->highest_bytenr)
206                         stat->highest_bytenr = cur_blocknr;
207                 free_extent_buffer(tmp);
208                 if (ret) {
209                         fprintf(stderr, "Error walking down path\n");
210                         break;
211                 }
212         }
213
214         return ret;
215 }
216
217 static void print_seek_histogram(struct root_stats *stat)
218 {
219         struct rb_node *n = rb_first(&stat->seek_root);
220         struct seek *seek;
221         u64 tick_interval;
222         u64 group_start;
223         u64 group_count = 0;
224         u64 group_end;
225         u64 i;
226         u64 max_seek = stat->max_seek_len;
227         int digits = 1;
228
229         if (stat->total_seeks < 20)
230                 return;
231
232         while ((max_seek /= 10))
233                 digits++;
234
235         /* Make a tick count as 5% of the total seeks */
236         tick_interval = stat->total_seeks / 20;
237         printf("\tSeek histogram\n");
238         for (; n; n = rb_next(n)) {
239                 u64 ticks, gticks = 0;
240
241                 seek = rb_entry(n, struct seek, n);
242                 ticks = seek->count / tick_interval;
243                 if (group_count)
244                         gticks = group_count / tick_interval;
245
246                 if (ticks <= 2 && gticks <= 2) {
247                         if (group_count == 0)
248                                 group_start = seek->distance;
249                         group_end = seek->distance;
250                         group_count += seek->count;
251                         continue;
252                 }
253
254                 if (group_count) {
255
256                         gticks = group_count / tick_interval;
257                         printf("\t\t%*Lu - %*Lu: %*Lu ", digits, group_start,
258                                digits, group_end, digits, group_count);
259                         if (gticks) {
260                                 for (i = 0; i < gticks; i++)
261                                         printf("#");
262                                 printf("\n");
263                         } else {
264                                 printf("|\n");
265                         }
266                         group_count = 0;
267                 }
268
269                 if (ticks <= 2)
270                         continue;
271
272                 printf("\t\t%*Lu - %*Lu: %*Lu ", digits, seek->distance,
273                        digits, seek->distance, digits, seek->count);
274                 for (i = 0; i < ticks; i++)
275                         printf("#");
276                 printf("\n");
277         }
278         if (group_count) {
279                 u64 gticks;
280
281                 gticks = group_count / tick_interval;
282                 printf("\t\t%*Lu - %*Lu: %*Lu ", digits, group_start,
283                        digits, group_end, digits, group_count);
284                 if (gticks) {
285                         for (i = 0; i < gticks; i++)
286                                 printf("#");
287                         printf("\n");
288                 } else {
289                         printf("|\n");
290                 }
291                 group_count = 0;
292         }
293 }
294
295 static void timeval_subtract(struct timeval *result,struct timeval *x,
296                              struct timeval *y)
297 {
298         if (x->tv_usec < y->tv_usec) {
299                 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
300                 y->tv_usec -= 1000000 * nsec;
301                 y->tv_sec += nsec;
302         }
303
304         if (x->tv_usec - y->tv_usec > 1000000) {
305                 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
306                 y->tv_usec += 1000000 * nsec;
307                 y->tv_sec -= nsec;
308         }
309
310         result->tv_sec = x->tv_sec - y->tv_sec;
311         result->tv_usec = x->tv_usec - y->tv_usec;
312 }
313
314 static int calc_root_size(struct btrfs_root *tree_root, struct btrfs_key *key,
315                           int find_inline)
316 {
317         struct btrfs_root *root;
318         struct btrfs_path *path;
319         struct rb_node *n;
320         struct timeval start, end, diff = {0};
321         struct root_stats stat;
322         int level;
323         int ret = 0;
324         int size_fail = 0;
325
326         root = btrfs_read_fs_root(tree_root->fs_info, key);
327         if (IS_ERR(root)) {
328                 fprintf(stderr, "Failed to read root %Lu\n", key->objectid);
329                 return 1;
330         }
331
332         path = btrfs_alloc_path();
333         if (!path) {
334                 fprintf(stderr, "Could not allocate path\n");
335                 return 1;
336         }
337
338         memset(&stat, 0, sizeof(stat));
339         level = btrfs_header_level(root->node);
340         stat.lowest_bytenr = btrfs_header_bytenr(root->node);
341         stat.highest_bytenr = stat.lowest_bytenr;
342         stat.min_cluster_size = (u64)-1;
343         stat.max_cluster_size = root->leafsize;
344         path->nodes[level] = root->node;
345         if (gettimeofday(&start, NULL)) {
346                 fprintf(stderr, "Error getting time: %d\n", errno);
347                 goto out;
348         }
349         if (!level) {
350                 ret = walk_leaf(root, path, &stat, find_inline);
351                 if (ret)
352                         goto out;
353                 goto out_print;
354         }
355
356         ret = walk_nodes(root, path, &stat, level, find_inline);
357         if (ret)
358                 goto out;
359         if (gettimeofday(&end, NULL)) {
360                 fprintf(stderr, "Error getting time: %d\n", errno);
361                 goto out;
362         }
363         timeval_subtract(&diff, &end, &start);
364 out_print:
365         if (stat.min_cluster_size == (u64)-1) {
366                 stat.min_cluster_size = 0;
367                 stat.total_clusters = 1;
368         }
369
370         if (no_pretty || size_fail) {
371                 printf("\tTotal size: %Lu\n", stat.total_bytes);
372                 printf("\t\tInline data: %Lu\n", stat.total_inline);
373                 printf("\tTotal seeks: %Lu\n", stat.total_seeks);
374                 printf("\t\tForward seeks: %Lu\n", stat.forward_seeks);
375                 printf("\t\tBackward seeks: %Lu\n", stat.backward_seeks);
376                 printf("\t\tAvg seek len: %Lu\n", stat.total_seek_len /
377                        stat.total_seeks);
378                 print_seek_histogram(&stat);
379                 printf("\tTotal clusters: %Lu\n", stat.total_clusters);
380                 printf("\t\tAvg cluster size: %Lu\n", stat.total_cluster_size /
381                        stat.total_clusters);
382                 printf("\t\tMin cluster size: %Lu\n", stat.min_cluster_size);
383                 printf("\t\tMax cluster size: %Lu\n", stat.max_cluster_size);
384                 printf("\tTotal disk spread: %Lu\n", stat.highest_bytenr -
385                        stat.lowest_bytenr);
386                 printf("\tTotal read time: %d s %d us\n", (int)diff.tv_sec,
387                        (int)diff.tv_usec);
388                 printf("\tLevels: %d\n", level + 1);
389         } else {
390                 printf("\tTotal size: %s\n", pretty_size(stat.total_bytes));
391                 printf("\t\tInline data: %s\n", pretty_size(stat.total_inline));
392                 printf("\tTotal seeks: %Lu\n", stat.total_seeks);
393                 printf("\t\tForward seeks: %Lu\n", stat.forward_seeks);
394                 printf("\t\tBackward seeks: %Lu\n", stat.backward_seeks);
395                 printf("\t\tAvg seek len: %s\n", stat.total_seeks ?
396                         pretty_size(stat.total_seek_len / stat.total_seeks) :
397                         pretty_size(0));
398                 print_seek_histogram(&stat);
399                 printf("\tTotal clusters: %Lu\n", stat.total_clusters);
400                 printf("\t\tAvg cluster size: %s\n",
401                                 pretty_size((stat.total_cluster_size /
402                                                 stat.total_clusters)));
403                 printf("\t\tMin cluster size: %s\n",
404                                 pretty_size(stat.min_cluster_size));
405                 printf("\t\tMax cluster size: %s\n",
406                                 pretty_size(stat.max_cluster_size));
407                 printf("\tTotal disk spread: %s\n",
408                                 pretty_size(stat.highest_bytenr -
409                                         stat.lowest_bytenr));
410                 printf("\tTotal read time: %d s %d us\n", (int)diff.tv_sec,
411                        (int)diff.tv_usec);
412                 printf("\tLevels: %d\n", level + 1);
413         }
414 out:
415         while ((n = rb_first(&stat.seek_root)) != NULL) {
416                 struct seek *seek = rb_entry(n, struct seek, n);
417                 rb_erase(n, &stat.seek_root);
418                 free(seek);
419         }
420
421         btrfs_free_path(path);
422         return ret;
423 }
424
425 static void usage()
426 {
427         fprintf(stderr, "Usage: calc-size [-v] [-b] <device>\n");
428 }
429
430 int main(int argc, char **argv)
431 {
432         struct btrfs_key key;
433         struct fs_root *roots;
434         struct btrfs_root *root;
435         size_t fs_roots_size = sizeof(struct fs_root);
436         int opt;
437         int ret = 0;
438
439         while ((opt = getopt(argc, argv, "vb")) != -1) {
440                 switch (opt) {
441                         case 'v':
442                                 verbose++;
443                                 break;
444                         case 'b':
445                                 no_pretty = 1;
446                                 break;
447                         default:
448                                 usage();
449                                 exit(1);
450                 }
451         }
452
453         set_argv0(argv);
454         argc = argc - optind;
455         if (check_argc_min(argc, 1)) {
456                 usage();
457                 exit(1);
458         }
459
460         /*
461         if ((ret = check_mounted(argv[optind])) < 0) {
462                 fprintf(stderr, "Could not check mount status: %d\n", ret);
463                 if (ret == -EACCES)
464                         fprintf(stderr, "Maybe you need to run as root?\n");
465                 return ret;
466         } else if (ret) {
467                 fprintf(stderr, "%s is currently mounted.  Aborting.\n",
468                         argv[optind]);
469                 return -EBUSY;
470         }
471         */
472
473         root = open_ctree(argv[optind], 0, 0);
474         if (!root) {
475                 fprintf(stderr, "Couldn't open ctree\n");
476                 exit(1);
477         }
478
479         roots = malloc(fs_roots_size);
480         if (!roots) {
481                 fprintf(stderr, "No memory\n");
482                 goto out;
483         }
484
485         printf("Calculating size of root tree\n");
486         key.objectid = BTRFS_ROOT_TREE_OBJECTID;
487         ret = calc_root_size(root, &key, 0);
488         if (ret)
489                 goto out;
490
491         printf("Calculating size of extent tree\n");
492         key.objectid = BTRFS_EXTENT_TREE_OBJECTID;
493         ret = calc_root_size(root, &key, 0);
494         if (ret)
495                 goto out;
496
497         printf("Calculating size of csum tree\n");
498         key.objectid = BTRFS_CSUM_TREE_OBJECTID;
499         ret = calc_root_size(root, &key, 0);
500         if (ret)
501                 goto out;
502
503         roots[0].key.objectid = BTRFS_FS_TREE_OBJECTID;
504         roots[0].key.offset = (u64)-1;
505         printf("Calculatin' size of fs tree\n");
506         ret = calc_root_size(root, &roots[0].key, 1);
507         if (ret)
508                 goto out;
509 out:
510         close_ctree(root);
511         free(roots);
512         return ret;
513 }