btrfs-progs: mkfs: delete un-used parameter fd
[platform/upstream/btrfs-progs.git] / cmds-inspect-tree-stats.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
29 #include "kerncompat.h"
30 #include "ctree.h"
31 #include "disk-io.h"
32 #include "print-tree.h"
33 #include "transaction.h"
34 #include "list.h"
35 #include "volumes.h"
36 #include "utils.h"
37 #include "commands.h"
38 #include "cmds-inspect-tree-stats.h"
39 #include "help.h"
40
41 static int verbose = 0;
42 static int no_pretty = 0;
43
44 struct seek {
45         u64 distance;
46         u64 count;
47         struct rb_node n;
48 };
49
50 struct root_stats {
51         u64 total_nodes;
52         u64 total_leaves;
53         u64 total_bytes;
54         u64 total_inline;
55         u64 total_seeks;
56         u64 forward_seeks;
57         u64 backward_seeks;
58         u64 total_seek_len;
59         u64 max_seek_len;
60         u64 total_clusters;
61         u64 total_cluster_size;
62         u64 min_cluster_size;
63         u64 max_cluster_size;
64         u64 lowest_bytenr;
65         u64 highest_bytenr;
66         struct rb_root seek_root;
67         int total_levels;
68 };
69
70 static int add_seek(struct rb_root *root, u64 dist)
71 {
72         struct rb_node **p = &root->rb_node;
73         struct rb_node *parent = NULL;
74         struct seek *seek = NULL;
75
76         while (*p) {
77                 parent = *p;
78                 seek = rb_entry(parent, struct seek, n);
79
80                 if (dist < seek->distance) {
81                         p = &(*p)->rb_left;
82                 } else if (dist > seek->distance) {
83                         p = &(*p)->rb_right;
84                 } else {
85                         seek->count++;
86                         return 0;
87                 }
88         }
89
90         seek = malloc(sizeof(struct seek));
91         if (!seek)
92                 return -ENOMEM;
93         seek->distance = dist;
94         seek->count = 1;
95         rb_link_node(&seek->n, parent, p);
96         rb_insert_color(&seek->n, root);
97         return 0;
98 }
99
100 static int walk_leaf(struct btrfs_root *root, struct btrfs_path *path,
101                      struct root_stats *stat, int find_inline)
102 {
103         struct extent_buffer *b = path->nodes[0];
104         struct btrfs_file_extent_item *fi;
105         struct btrfs_key found_key;
106         int i;
107
108         stat->total_bytes += root->fs_info->nodesize;
109         stat->total_leaves++;
110
111         if (!find_inline)
112                 return 0;
113
114         for (i = 0; i < btrfs_header_nritems(b); i++) {
115                 btrfs_item_key_to_cpu(b, &found_key, i);
116                 if (found_key.type != BTRFS_EXTENT_DATA_KEY)
117                         continue;
118
119                 fi = btrfs_item_ptr(b, i, struct btrfs_file_extent_item);
120                 if (btrfs_file_extent_type(b, fi) == BTRFS_FILE_EXTENT_INLINE)
121                         stat->total_inline +=
122                                 btrfs_file_extent_inline_item_len(b,
123                                                         btrfs_item_nr(i));
124         }
125
126         return 0;
127 }
128
129 static u64 calc_distance(u64 block1, u64 block2)
130 {
131         if (block1 < block2)
132                 return block2 - block1;
133         return block1 - block2;
134 }
135
136 static int walk_nodes(struct btrfs_root *root, struct btrfs_path *path,
137                       struct root_stats *stat, int level, int find_inline)
138 {
139         struct extent_buffer *b = path->nodes[level];
140         u32 nodesize = root->fs_info->nodesize;
141         u64 last_block;
142         u64 cluster_size = nodesize;
143         int i;
144         int ret = 0;
145
146         stat->total_bytes += nodesize;
147         stat->total_nodes++;
148
149         last_block = btrfs_header_bytenr(b);
150         for (i = 0; i < btrfs_header_nritems(b); i++) {
151                 struct extent_buffer *tmp = NULL;
152                 u64 cur_blocknr = btrfs_node_blockptr(b, i);
153
154                 path->slots[level] = i;
155                 if ((level - 1) > 0 || find_inline) {
156                         tmp = read_tree_block(root->fs_info, cur_blocknr,
157                                               nodesize,
158                                               btrfs_node_ptr_generation(b, i));
159                         if (!extent_buffer_uptodate(tmp)) {
160                                 error("failed to read blocknr %llu",
161                                         btrfs_node_blockptr(b, i));
162                                 continue;
163                         }
164                         path->nodes[level - 1] = tmp;
165                 }
166                 if (level - 1)
167                         ret = walk_nodes(root, path, stat, level - 1,
168                                          find_inline);
169                 else
170                         ret = walk_leaf(root, path, stat, find_inline);
171                 if (last_block + nodesize != cur_blocknr) {
172                         u64 distance = calc_distance(last_block +
173                                                      nodesize,
174                                                      cur_blocknr);
175                         stat->total_seeks++;
176                         stat->total_seek_len += distance;
177                         if (stat->max_seek_len < distance)
178                                 stat->max_seek_len = distance;
179                         if (add_seek(&stat->seek_root, distance)) {
180                                 error("cannot add new seek at distance %llu",
181                                                 (unsigned long long)distance);
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 != nodesize) {
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 = nodesize;
199                 } else {
200                         cluster_size += nodesize;
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                         error("walking down path failed: %d",  ret);
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 = 0;
223         u64 group_count = 0;
224         u64 group_end = 0;
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                 error("failed to read root %llu", key->objectid);
329                 return 1;
330         }
331
332         btrfs_init_path(&path);
333         memset(&stat, 0, sizeof(stat));
334         level = btrfs_header_level(root->node);
335         stat.lowest_bytenr = btrfs_header_bytenr(root->node);
336         stat.highest_bytenr = stat.lowest_bytenr;
337         stat.min_cluster_size = (u64)-1;
338         stat.max_cluster_size = root->fs_info->nodesize;
339         path.nodes[level] = root->node;
340         if (gettimeofday(&start, NULL)) {
341                 error("cannot get time: %s", strerror(errno));
342                 goto out;
343         }
344         if (!level) {
345                 ret = walk_leaf(root, &path, &stat, find_inline);
346                 if (ret)
347                         goto out;
348                 goto out_print;
349         }
350
351         ret = walk_nodes(root, &path, &stat, level, find_inline);
352         if (ret)
353                 goto out;
354         if (gettimeofday(&end, NULL)) {
355                 error("cannot get time: %s", strerror(errno));
356                 goto out;
357         }
358         timeval_subtract(&diff, &end, &start);
359 out_print:
360         if (stat.min_cluster_size == (u64)-1) {
361                 stat.min_cluster_size = 0;
362                 stat.total_clusters = 1;
363         }
364
365         if (no_pretty || size_fail) {
366                 printf("\tTotal size: %llu\n", stat.total_bytes);
367                 printf("\t\tInline data: %llu\n", stat.total_inline);
368                 printf("\tTotal seeks: %llu\n", stat.total_seeks);
369                 printf("\t\tForward seeks: %llu\n", stat.forward_seeks);
370                 printf("\t\tBackward seeks: %llu\n", stat.backward_seeks);
371                 printf("\t\tAvg seek len: %llu\n", stat.total_seeks ?
372                         stat.total_seek_len / stat.total_seeks : 0);
373                 print_seek_histogram(&stat);
374                 printf("\tTotal clusters: %llu\n", stat.total_clusters);
375                 printf("\t\tAvg cluster size: %llu\n", stat.total_cluster_size /
376                        stat.total_clusters);
377                 printf("\t\tMin cluster size: %llu\n", stat.min_cluster_size);
378                 printf("\t\tMax cluster size: %llu\n", stat.max_cluster_size);
379                 printf("\tTotal disk spread: %llu\n", stat.highest_bytenr -
380                        stat.lowest_bytenr);
381                 printf("\tTotal read time: %d s %d us\n", (int)diff.tv_sec,
382                        (int)diff.tv_usec);
383                 printf("\tLevels: %d\n", level + 1);
384         } else {
385                 printf("\tTotal size: %s\n", pretty_size(stat.total_bytes));
386                 printf("\t\tInline data: %s\n", pretty_size(stat.total_inline));
387                 printf("\tTotal seeks: %llu\n", stat.total_seeks);
388                 printf("\t\tForward seeks: %llu\n", stat.forward_seeks);
389                 printf("\t\tBackward seeks: %llu\n", stat.backward_seeks);
390                 printf("\t\tAvg seek len: %s\n", stat.total_seeks ?
391                         pretty_size(stat.total_seek_len / stat.total_seeks) :
392                         pretty_size(0));
393                 print_seek_histogram(&stat);
394                 printf("\tTotal clusters: %llu\n", stat.total_clusters);
395                 printf("\t\tAvg cluster size: %s\n",
396                                 pretty_size((stat.total_cluster_size /
397                                                 stat.total_clusters)));
398                 printf("\t\tMin cluster size: %s\n",
399                                 pretty_size(stat.min_cluster_size));
400                 printf("\t\tMax cluster size: %s\n",
401                                 pretty_size(stat.max_cluster_size));
402                 printf("\tTotal disk spread: %s\n",
403                                 pretty_size(stat.highest_bytenr -
404                                         stat.lowest_bytenr));
405                 printf("\tTotal read time: %d s %d us\n", (int)diff.tv_sec,
406                        (int)diff.tv_usec);
407                 printf("\tLevels: %d\n", level + 1);
408         }
409 out:
410         while ((n = rb_first(&stat.seek_root)) != NULL) {
411                 struct seek *seek = rb_entry(n, struct seek, n);
412                 rb_erase(n, &stat.seek_root);
413                 free(seek);
414         }
415
416         /*
417          * We only use path to save node data in iterating, without holding
418          * eb's ref_cnt in path.  Don't use btrfs_release_path() here, it will
419          * free these eb again, and cause many problems, as negative ref_cnt or
420          * invalid memory access.
421          */
422         return ret;
423 }
424
425 const char * const cmd_inspect_tree_stats_usage[] = {
426         "btrfs inspect-internal tree-stats [options] <device>",
427         "Print various stats for trees",
428         "-b             raw numbers in bytes",
429         NULL
430 };
431
432 int cmd_inspect_tree_stats(int argc, char **argv)
433 {
434         struct btrfs_key key;
435         struct btrfs_root *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(cmd_inspect_tree_stats_usage);
449                 }
450         }
451
452         if (check_argc_exact(argc - optind, 1)) {
453                 usage(cmd_inspect_tree_stats_usage);
454         }
455
456         ret = check_mounted(argv[optind]);
457         if (ret < 0) {
458                 warning("unable to check mount status of: %s",
459                                 strerror(-ret));
460         } else if (ret) {
461                 warning("%s already mounted, results may be inaccurate",
462                                 argv[optind]);
463         }
464
465         root = open_ctree(argv[optind], 0, 0);
466         if (!root) {
467                 error("cannot open ctree");
468                 exit(1);
469         }
470
471         printf("Calculating size of root tree\n");
472         key.objectid = BTRFS_ROOT_TREE_OBJECTID;
473         ret = calc_root_size(root, &key, 0);
474         if (ret)
475                 goto out;
476
477         printf("Calculating size of extent tree\n");
478         key.objectid = BTRFS_EXTENT_TREE_OBJECTID;
479         ret = calc_root_size(root, &key, 0);
480         if (ret)
481                 goto out;
482
483         printf("Calculating size of csum tree\n");
484         key.objectid = BTRFS_CSUM_TREE_OBJECTID;
485         ret = calc_root_size(root, &key, 0);
486         if (ret)
487                 goto out;
488
489         key.objectid = BTRFS_FS_TREE_OBJECTID;
490         key.offset = (u64)-1;
491         printf("Calculating size of fs tree\n");
492         ret = calc_root_size(root, &key, 1);
493         if (ret)
494                 goto out;
495 out:
496         close_ctree(root);
497         return ret;
498 }