2 * Copyright (C) 2011 Red Hat. All rights reserved.
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.
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.
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.
26 #include <sys/types.h>
29 #include "kerncompat.h"
32 #include "print-tree.h"
33 #include "transaction.h"
38 #include "cmds-inspect-tree-stats.h"
40 static int verbose
= 0;
41 static int no_pretty
= 0;
60 u64 total_cluster_size
;
65 struct rb_root seek_root
;
69 static int add_seek(struct rb_root
*root
, u64 dist
)
71 struct rb_node
**p
= &root
->rb_node
;
72 struct rb_node
*parent
= NULL
;
73 struct seek
*seek
= NULL
;
77 seek
= rb_entry(parent
, struct seek
, n
);
79 if (dist
< seek
->distance
) {
81 } else if (dist
> seek
->distance
) {
89 seek
= malloc(sizeof(struct seek
));
92 seek
->distance
= dist
;
94 rb_link_node(&seek
->n
, parent
, p
);
95 rb_insert_color(&seek
->n
, root
);
99 static int walk_leaf(struct btrfs_root
*root
, struct btrfs_path
*path
,
100 struct root_stats
*stat
, int find_inline
)
102 struct extent_buffer
*b
= path
->nodes
[0];
103 struct btrfs_file_extent_item
*fi
;
104 struct btrfs_key found_key
;
107 stat
->total_bytes
+= root
->nodesize
;
108 stat
->total_leaves
++;
113 for (i
= 0; i
< btrfs_header_nritems(b
); i
++) {
114 btrfs_item_key_to_cpu(b
, &found_key
, i
);
115 if (found_key
.type
!= BTRFS_EXTENT_DATA_KEY
)
118 fi
= btrfs_item_ptr(b
, i
, struct btrfs_file_extent_item
);
119 if (btrfs_file_extent_type(b
, fi
) == BTRFS_FILE_EXTENT_INLINE
)
120 stat
->total_inline
+=
121 btrfs_file_extent_inline_item_len(b
,
128 static u64
calc_distance(u64 block1
, u64 block2
)
131 return block2
- block1
;
132 return block1
- block2
;
135 static int walk_nodes(struct btrfs_root
*root
, struct btrfs_path
*path
,
136 struct root_stats
*stat
, int level
, int find_inline
)
138 struct extent_buffer
*b
= path
->nodes
[level
];
140 u64 cluster_size
= root
->nodesize
;
144 stat
->total_bytes
+= root
->nodesize
;
147 last_block
= btrfs_header_bytenr(b
);
148 for (i
= 0; i
< btrfs_header_nritems(b
); i
++) {
149 struct extent_buffer
*tmp
= NULL
;
150 u64 cur_blocknr
= btrfs_node_blockptr(b
, i
);
152 path
->slots
[level
] = i
;
153 if ((level
- 1) > 0 || find_inline
) {
154 tmp
= read_tree_block(root
, cur_blocknr
,
156 btrfs_node_ptr_generation(b
, i
));
157 if (!extent_buffer_uptodate(tmp
)) {
158 fprintf(stderr
, "Failed to read blocknr %llu\n",
159 btrfs_node_blockptr(b
, i
));
162 path
->nodes
[level
- 1] = tmp
;
165 ret
= walk_nodes(root
, path
, stat
, level
- 1,
168 ret
= walk_leaf(root
, path
, stat
, find_inline
);
169 if (last_block
+ root
->nodesize
!= cur_blocknr
) {
170 u64 distance
= calc_distance(last_block
+
174 stat
->total_seek_len
+= distance
;
175 if (stat
->max_seek_len
< distance
)
176 stat
->max_seek_len
= distance
;
177 if (add_seek(&stat
->seek_root
, distance
)) {
178 fprintf(stderr
, "Error adding new seek\n");
183 if (last_block
< cur_blocknr
)
184 stat
->forward_seeks
++;
186 stat
->backward_seeks
++;
187 if (cluster_size
!= root
->nodesize
) {
188 stat
->total_cluster_size
+= cluster_size
;
189 stat
->total_clusters
++;
190 if (cluster_size
< stat
->min_cluster_size
)
191 stat
->min_cluster_size
= cluster_size
;
192 if (cluster_size
> stat
->max_cluster_size
)
193 stat
->max_cluster_size
= cluster_size
;
195 cluster_size
= root
->nodesize
;
197 cluster_size
+= root
->nodesize
;
199 last_block
= cur_blocknr
;
200 if (cur_blocknr
< stat
->lowest_bytenr
)
201 stat
->lowest_bytenr
= cur_blocknr
;
202 if (cur_blocknr
> stat
->highest_bytenr
)
203 stat
->highest_bytenr
= cur_blocknr
;
204 free_extent_buffer(tmp
);
206 fprintf(stderr
, "Error walking down path\n");
214 static void print_seek_histogram(struct root_stats
*stat
)
216 struct rb_node
*n
= rb_first(&stat
->seek_root
);
223 u64 max_seek
= stat
->max_seek_len
;
226 if (stat
->total_seeks
< 20)
229 while ((max_seek
/= 10))
232 /* Make a tick count as 5% of the total seeks */
233 tick_interval
= stat
->total_seeks
/ 20;
234 printf("\tSeek histogram\n");
235 for (; n
; n
= rb_next(n
)) {
236 u64 ticks
, gticks
= 0;
238 seek
= rb_entry(n
, struct seek
, n
);
239 ticks
= seek
->count
/ tick_interval
;
241 gticks
= group_count
/ tick_interval
;
243 if (ticks
<= 2 && gticks
<= 2) {
244 if (group_count
== 0)
245 group_start
= seek
->distance
;
246 group_end
= seek
->distance
;
247 group_count
+= seek
->count
;
253 gticks
= group_count
/ tick_interval
;
254 printf("\t\t%*Lu - %*Lu: %*Lu ", digits
, group_start
,
255 digits
, group_end
, digits
, group_count
);
257 for (i
= 0; i
< gticks
; i
++)
269 printf("\t\t%*Lu - %*Lu: %*Lu ", digits
, seek
->distance
,
270 digits
, seek
->distance
, digits
, seek
->count
);
271 for (i
= 0; i
< ticks
; i
++)
278 gticks
= group_count
/ tick_interval
;
279 printf("\t\t%*Lu - %*Lu: %*Lu ", digits
, group_start
,
280 digits
, group_end
, digits
, group_count
);
282 for (i
= 0; i
< gticks
; i
++)
292 static void timeval_subtract(struct timeval
*result
, struct timeval
*x
,
295 if (x
->tv_usec
< y
->tv_usec
) {
296 int nsec
= (y
->tv_usec
- x
->tv_usec
) / 1000000 + 1;
297 y
->tv_usec
-= 1000000 * nsec
;
301 if (x
->tv_usec
- y
->tv_usec
> 1000000) {
302 int nsec
= (x
->tv_usec
- y
->tv_usec
) / 1000000;
303 y
->tv_usec
+= 1000000 * nsec
;
307 result
->tv_sec
= x
->tv_sec
- y
->tv_sec
;
308 result
->tv_usec
= x
->tv_usec
- y
->tv_usec
;
311 static int calc_root_size(struct btrfs_root
*tree_root
, struct btrfs_key
*key
,
314 struct btrfs_root
*root
;
315 struct btrfs_path
*path
;
317 struct timeval start
, end
, diff
= {0};
318 struct root_stats stat
;
323 root
= btrfs_read_fs_root(tree_root
->fs_info
, key
);
325 fprintf(stderr
, "Failed to read root %llu\n", key
->objectid
);
329 path
= btrfs_alloc_path();
331 fprintf(stderr
, "Could not allocate path\n");
335 memset(&stat
, 0, sizeof(stat
));
336 level
= btrfs_header_level(root
->node
);
337 stat
.lowest_bytenr
= btrfs_header_bytenr(root
->node
);
338 stat
.highest_bytenr
= stat
.lowest_bytenr
;
339 stat
.min_cluster_size
= (u64
)-1;
340 stat
.max_cluster_size
= root
->nodesize
;
341 path
->nodes
[level
] = root
->node
;
342 if (gettimeofday(&start
, NULL
)) {
343 fprintf(stderr
, "Error getting time: %d\n", errno
);
347 ret
= walk_leaf(root
, path
, &stat
, find_inline
);
353 ret
= walk_nodes(root
, path
, &stat
, level
, find_inline
);
356 if (gettimeofday(&end
, NULL
)) {
357 fprintf(stderr
, "Error getting time: %d\n", errno
);
360 timeval_subtract(&diff
, &end
, &start
);
362 if (stat
.min_cluster_size
== (u64
)-1) {
363 stat
.min_cluster_size
= 0;
364 stat
.total_clusters
= 1;
367 if (no_pretty
|| size_fail
) {
368 printf("\tTotal size: %llu\n", stat
.total_bytes
);
369 printf("\t\tInline data: %llu\n", stat
.total_inline
);
370 printf("\tTotal seeks: %llu\n", stat
.total_seeks
);
371 printf("\t\tForward seeks: %llu\n", stat
.forward_seeks
);
372 printf("\t\tBackward seeks: %llu\n", stat
.backward_seeks
);
373 printf("\t\tAvg seek len: %llu\n", stat
.total_seeks
?
374 stat
.total_seek_len
/ stat
.total_seeks
: 0);
375 print_seek_histogram(&stat
);
376 printf("\tTotal clusters: %llu\n", stat
.total_clusters
);
377 printf("\t\tAvg cluster size: %llu\n", stat
.total_cluster_size
/
378 stat
.total_clusters
);
379 printf("\t\tMin cluster size: %llu\n", stat
.min_cluster_size
);
380 printf("\t\tMax cluster size: %llu\n", stat
.max_cluster_size
);
381 printf("\tTotal disk spread: %llu\n", stat
.highest_bytenr
-
383 printf("\tTotal read time: %d s %d us\n", (int)diff
.tv_sec
,
385 printf("\tLevels: %d\n", level
+ 1);
387 printf("\tTotal size: %s\n", pretty_size(stat
.total_bytes
));
388 printf("\t\tInline data: %s\n", pretty_size(stat
.total_inline
));
389 printf("\tTotal seeks: %llu\n", stat
.total_seeks
);
390 printf("\t\tForward seeks: %llu\n", stat
.forward_seeks
);
391 printf("\t\tBackward seeks: %llu\n", stat
.backward_seeks
);
392 printf("\t\tAvg seek len: %s\n", stat
.total_seeks
?
393 pretty_size(stat
.total_seek_len
/ stat
.total_seeks
) :
395 print_seek_histogram(&stat
);
396 printf("\tTotal clusters: %llu\n", stat
.total_clusters
);
397 printf("\t\tAvg cluster size: %s\n",
398 pretty_size((stat
.total_cluster_size
/
399 stat
.total_clusters
)));
400 printf("\t\tMin cluster size: %s\n",
401 pretty_size(stat
.min_cluster_size
));
402 printf("\t\tMax cluster size: %s\n",
403 pretty_size(stat
.max_cluster_size
));
404 printf("\tTotal disk spread: %s\n",
405 pretty_size(stat
.highest_bytenr
-
406 stat
.lowest_bytenr
));
407 printf("\tTotal read time: %d s %d us\n", (int)diff
.tv_sec
,
409 printf("\tLevels: %d\n", level
+ 1);
412 while ((n
= rb_first(&stat
.seek_root
)) != NULL
) {
413 struct seek
*seek
= rb_entry(n
, struct seek
, n
);
414 rb_erase(n
, &stat
.seek_root
);
419 * We only use path to save node data in iterating,
420 * without holding eb's ref_cnt in path.
421 * Don't use btrfs_free_path() here, it will free these
422 * eb again, and cause many problems, as negative ref_cnt
423 * or invalid memory access.
429 const char * const cmd_inspect_tree_stats_usage
[] = {
430 "btrfs inspect-internal tree-stats [options] <device>",
431 "Print various stats for trees",
432 "-b raw numbers in bytes",
436 int cmd_inspect_tree_stats(int argc
, char **argv
)
438 struct btrfs_key key
;
439 struct btrfs_root
*root
;
443 while ((opt
= getopt(argc
, argv
, "vb")) != -1) {
452 usage(cmd_inspect_tree_stats_usage
);
456 if (check_argc_exact(argc
- optind
, 1)) {
457 usage(cmd_inspect_tree_stats_usage
);
461 if ((ret = check_mounted(argv[optind])) < 0) {
462 fprintf(stderr, "Could not check mount status: %d\n", ret);
464 fprintf(stderr, "Maybe you need to run as root?\n");
467 fprintf(stderr, "%s is currently mounted. Aborting.\n",
473 root
= open_ctree(argv
[optind
], 0, 0);
475 fprintf(stderr
, "Couldn't open ctree\n");
479 printf("Calculating size of root tree\n");
480 key
.objectid
= BTRFS_ROOT_TREE_OBJECTID
;
481 ret
= calc_root_size(root
, &key
, 0);
485 printf("Calculating size of extent tree\n");
486 key
.objectid
= BTRFS_EXTENT_TREE_OBJECTID
;
487 ret
= calc_root_size(root
, &key
, 0);
491 printf("Calculating size of csum tree\n");
492 key
.objectid
= BTRFS_CSUM_TREE_OBJECTID
;
493 ret
= calc_root_size(root
, &key
, 0);
497 key
.objectid
= BTRFS_FS_TREE_OBJECTID
;
498 key
.offset
= (u64
)-1;
499 printf("Calculating size of fs tree\n");
500 ret
= calc_root_size(root
, &key
, 1);