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"
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
->fs_info
->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
];
139 u32 nodesize
= root
->fs_info
->nodesize
;
141 u64 cluster_size
= nodesize
;
145 stat
->total_bytes
+= nodesize
;
148 last_block
= btrfs_header_bytenr(b
);
149 for (i
= 0; i
< btrfs_header_nritems(b
); i
++) {
150 struct extent_buffer
*tmp
= NULL
;
151 u64 cur_blocknr
= btrfs_node_blockptr(b
, i
);
153 path
->slots
[level
] = i
;
154 if ((level
- 1) > 0 || find_inline
) {
155 tmp
= read_tree_block(root
->fs_info
, cur_blocknr
,
157 btrfs_node_ptr_generation(b
, i
));
158 if (!extent_buffer_uptodate(tmp
)) {
159 error("failed to read blocknr %llu",
160 btrfs_node_blockptr(b
, i
));
163 path
->nodes
[level
- 1] = tmp
;
166 ret
= walk_nodes(root
, path
, stat
, level
- 1,
169 ret
= walk_leaf(root
, path
, stat
, find_inline
);
170 if (last_block
+ nodesize
!= cur_blocknr
) {
171 u64 distance
= calc_distance(last_block
+
175 stat
->total_seek_len
+= distance
;
176 if (stat
->max_seek_len
< distance
)
177 stat
->max_seek_len
= distance
;
178 if (add_seek(&stat
->seek_root
, distance
)) {
179 error("cannot add new seek at distance %llu",
180 (unsigned long long)distance
);
185 if (last_block
< cur_blocknr
)
186 stat
->forward_seeks
++;
188 stat
->backward_seeks
++;
189 if (cluster_size
!= nodesize
) {
190 stat
->total_cluster_size
+= cluster_size
;
191 stat
->total_clusters
++;
192 if (cluster_size
< stat
->min_cluster_size
)
193 stat
->min_cluster_size
= cluster_size
;
194 if (cluster_size
> stat
->max_cluster_size
)
195 stat
->max_cluster_size
= cluster_size
;
197 cluster_size
= nodesize
;
199 cluster_size
+= nodesize
;
201 last_block
= cur_blocknr
;
202 if (cur_blocknr
< stat
->lowest_bytenr
)
203 stat
->lowest_bytenr
= cur_blocknr
;
204 if (cur_blocknr
> stat
->highest_bytenr
)
205 stat
->highest_bytenr
= cur_blocknr
;
206 free_extent_buffer(tmp
);
208 error("walking down path failed: %d", ret
);
216 static void print_seek_histogram(struct root_stats
*stat
)
218 struct rb_node
*n
= rb_first(&stat
->seek_root
);
225 u64 max_seek
= stat
->max_seek_len
;
228 if (stat
->total_seeks
< 20)
231 while ((max_seek
/= 10))
234 /* Make a tick count as 5% of the total seeks */
235 tick_interval
= stat
->total_seeks
/ 20;
236 printf("\tSeek histogram\n");
237 for (; n
; n
= rb_next(n
)) {
238 u64 ticks
, gticks
= 0;
240 seek
= rb_entry(n
, struct seek
, n
);
241 ticks
= seek
->count
/ tick_interval
;
243 gticks
= group_count
/ tick_interval
;
245 if (ticks
<= 2 && gticks
<= 2) {
246 if (group_count
== 0)
247 group_start
= seek
->distance
;
248 group_end
= seek
->distance
;
249 group_count
+= seek
->count
;
255 gticks
= group_count
/ tick_interval
;
256 printf("\t\t%*Lu - %*Lu: %*Lu ", digits
, group_start
,
257 digits
, group_end
, digits
, group_count
);
259 for (i
= 0; i
< gticks
; i
++)
271 printf("\t\t%*Lu - %*Lu: %*Lu ", digits
, seek
->distance
,
272 digits
, seek
->distance
, digits
, seek
->count
);
273 for (i
= 0; i
< ticks
; i
++)
280 gticks
= group_count
/ tick_interval
;
281 printf("\t\t%*Lu - %*Lu: %*Lu ", digits
, group_start
,
282 digits
, group_end
, digits
, group_count
);
284 for (i
= 0; i
< gticks
; i
++)
294 static void timeval_subtract(struct timeval
*result
, struct timeval
*x
,
297 if (x
->tv_usec
< y
->tv_usec
) {
298 int nsec
= (y
->tv_usec
- x
->tv_usec
) / 1000000 + 1;
299 y
->tv_usec
-= 1000000 * nsec
;
303 if (x
->tv_usec
- y
->tv_usec
> 1000000) {
304 int nsec
= (x
->tv_usec
- y
->tv_usec
) / 1000000;
305 y
->tv_usec
+= 1000000 * nsec
;
309 result
->tv_sec
= x
->tv_sec
- y
->tv_sec
;
310 result
->tv_usec
= x
->tv_usec
- y
->tv_usec
;
313 static int calc_root_size(struct btrfs_root
*tree_root
, struct btrfs_key
*key
,
316 struct btrfs_root
*root
;
317 struct btrfs_path path
;
319 struct timeval start
, end
, diff
= {0};
320 struct root_stats stat
;
325 root
= btrfs_read_fs_root(tree_root
->fs_info
, key
);
327 error("failed to read root %llu", key
->objectid
);
331 btrfs_init_path(&path
);
332 memset(&stat
, 0, sizeof(stat
));
333 level
= btrfs_header_level(root
->node
);
334 stat
.lowest_bytenr
= btrfs_header_bytenr(root
->node
);
335 stat
.highest_bytenr
= stat
.lowest_bytenr
;
336 stat
.min_cluster_size
= (u64
)-1;
337 stat
.max_cluster_size
= root
->fs_info
->nodesize
;
338 path
.nodes
[level
] = root
->node
;
339 if (gettimeofday(&start
, NULL
)) {
340 error("cannot get time: %s", strerror(errno
));
344 ret
= walk_leaf(root
, &path
, &stat
, find_inline
);
350 ret
= walk_nodes(root
, &path
, &stat
, level
, find_inline
);
353 if (gettimeofday(&end
, NULL
)) {
354 error("cannot get time: %s", strerror(errno
));
357 timeval_subtract(&diff
, &end
, &start
);
359 if (stat
.min_cluster_size
== (u64
)-1) {
360 stat
.min_cluster_size
= 0;
361 stat
.total_clusters
= 1;
364 if (no_pretty
|| size_fail
) {
365 printf("\tTotal size: %llu\n", stat
.total_bytes
);
366 printf("\t\tInline data: %llu\n", stat
.total_inline
);
367 printf("\tTotal seeks: %llu\n", stat
.total_seeks
);
368 printf("\t\tForward seeks: %llu\n", stat
.forward_seeks
);
369 printf("\t\tBackward seeks: %llu\n", stat
.backward_seeks
);
370 printf("\t\tAvg seek len: %llu\n", stat
.total_seeks
?
371 stat
.total_seek_len
/ stat
.total_seeks
: 0);
372 print_seek_histogram(&stat
);
373 printf("\tTotal clusters: %llu\n", stat
.total_clusters
);
374 printf("\t\tAvg cluster size: %llu\n", stat
.total_cluster_size
/
375 stat
.total_clusters
);
376 printf("\t\tMin cluster size: %llu\n", stat
.min_cluster_size
);
377 printf("\t\tMax cluster size: %llu\n", stat
.max_cluster_size
);
378 printf("\tTotal disk spread: %llu\n", stat
.highest_bytenr
-
380 printf("\tTotal read time: %d s %d us\n", (int)diff
.tv_sec
,
382 printf("\tLevels: %d\n", level
+ 1);
384 printf("\tTotal size: %s\n", pretty_size(stat
.total_bytes
));
385 printf("\t\tInline data: %s\n", pretty_size(stat
.total_inline
));
386 printf("\tTotal seeks: %llu\n", stat
.total_seeks
);
387 printf("\t\tForward seeks: %llu\n", stat
.forward_seeks
);
388 printf("\t\tBackward seeks: %llu\n", stat
.backward_seeks
);
389 printf("\t\tAvg seek len: %s\n", stat
.total_seeks
?
390 pretty_size(stat
.total_seek_len
/ stat
.total_seeks
) :
392 print_seek_histogram(&stat
);
393 printf("\tTotal clusters: %llu\n", stat
.total_clusters
);
394 printf("\t\tAvg cluster size: %s\n",
395 pretty_size((stat
.total_cluster_size
/
396 stat
.total_clusters
)));
397 printf("\t\tMin cluster size: %s\n",
398 pretty_size(stat
.min_cluster_size
));
399 printf("\t\tMax cluster size: %s\n",
400 pretty_size(stat
.max_cluster_size
));
401 printf("\tTotal disk spread: %s\n",
402 pretty_size(stat
.highest_bytenr
-
403 stat
.lowest_bytenr
));
404 printf("\tTotal read time: %d s %d us\n", (int)diff
.tv_sec
,
406 printf("\tLevels: %d\n", level
+ 1);
409 while ((n
= rb_first(&stat
.seek_root
)) != NULL
) {
410 struct seek
*seek
= rb_entry(n
, struct seek
, n
);
411 rb_erase(n
, &stat
.seek_root
);
416 * We only use path to save node data in iterating, without holding
417 * eb's ref_cnt in path. Don't use btrfs_release_path() here, it will
418 * free these eb again, and cause many problems, as negative ref_cnt or
419 * invalid memory access.
424 const char * const cmd_inspect_tree_stats_usage
[] = {
425 "btrfs inspect-internal tree-stats [options] <device>",
426 "Print various stats for trees",
427 "-b raw numbers in bytes",
431 int cmd_inspect_tree_stats(int argc
, char **argv
)
433 struct btrfs_key key
;
434 struct btrfs_root
*root
;
438 while ((opt
= getopt(argc
, argv
, "vb")) != -1) {
447 usage(cmd_inspect_tree_stats_usage
);
451 if (check_argc_exact(argc
- optind
, 1)) {
452 usage(cmd_inspect_tree_stats_usage
);
455 ret
= check_mounted(argv
[optind
]);
457 warning("unable to check mount status of: %s",
460 warning("%s already mounted, results may be inaccurate",
464 root
= open_ctree(argv
[optind
], 0, 0);
466 error("cannot open ctree");
470 printf("Calculating size of root tree\n");
471 key
.objectid
= BTRFS_ROOT_TREE_OBJECTID
;
472 ret
= calc_root_size(root
, &key
, 0);
476 printf("Calculating size of extent tree\n");
477 key
.objectid
= BTRFS_EXTENT_TREE_OBJECTID
;
478 ret
= calc_root_size(root
, &key
, 0);
482 printf("Calculating size of csum tree\n");
483 key
.objectid
= BTRFS_CSUM_TREE_OBJECTID
;
484 ret
= calc_root_size(root
, &key
, 0);
488 key
.objectid
= BTRFS_FS_TREE_OBJECTID
;
489 key
.offset
= (u64
)-1;
490 printf("Calculating size of fs tree\n");
491 ret
= calc_root_size(root
, &key
, 1);