]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/svnfsfs/stats-cmd.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / svnfsfs / stats-cmd.c
1 /* stats-cmd.c -- implements the size stats sub-command.
2  *
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  */
22
23 #include <assert.h>
24
25 #include "svn_fs.h"
26 #include "svn_pools.h"
27 #include "svn_sorts.h"
28
29 #include "private/svn_sorts_private.h"
30 #include "private/svn_string_private.h"
31 #include "private/svn_fs_fs_private.h"
32
33 #include "svn_private_config.h"
34 #include "svnfsfs.h"
35
36 /* Return the string, allocated in RESULT_POOL, describing the value 2**I.
37  */
38 static const char *
39 print_two_power(int i,
40                 apr_pool_t *result_pool)
41 {
42   /* These are the SI prefixes for base-1000, the binary ones with base-1024
43      are too clumsy and require appending B for "byte" to be intelligible,
44      e.g. "MiB".
45
46      Therefore, we ignore the official standard and revert to the traditional
47      contextual use were the base-1000 prefixes are understood as base-1024
48      when it came to data sizes.
49    */
50   const char *si_prefixes = " kMGTPEZY";
51
52   int number = (i >= 0) ? (1 << (i % 10)) : 0;
53   int thousands = (i >= 0) ? (i / 10) : 0;
54
55   char si_prefix = (thousands < strlen(si_prefixes))
56                  ? si_prefixes[thousands]
57                  : '?';
58
59   if (si_prefix == ' ')
60     return apr_psprintf(result_pool, "%d", number);
61
62   return apr_psprintf(result_pool, "%d%c", number, si_prefix);
63 }
64
65 /* Print statistics for the given group of representations to console.
66  * Use POOL for allocations.
67  */
68 static void
69 print_rep_stats(svn_fs_fs__representation_stats_t *stats,
70                 apr_pool_t *pool)
71 {
72   printf(_("%20s bytes in %12s reps\n"
73            "%20s bytes in %12s shared reps\n"
74            "%20s bytes expanded size\n"
75            "%20s bytes expanded shared size\n"
76            "%20s bytes with rep-sharing off\n"
77            "%20s shared references\n"),
78          svn__ui64toa_sep(stats->total.packed_size, ',', pool),
79          svn__ui64toa_sep(stats->total.count, ',', pool),
80          svn__ui64toa_sep(stats->shared.packed_size, ',', pool),
81          svn__ui64toa_sep(stats->shared.count, ',', pool),
82          svn__ui64toa_sep(stats->total.expanded_size, ',', pool),
83          svn__ui64toa_sep(stats->shared.expanded_size, ',', pool),
84          svn__ui64toa_sep(stats->expanded_size, ',', pool),
85          svn__ui64toa_sep(stats->references - stats->total.count, ',', pool));
86 }
87
88 /* Print the (used) contents of CHANGES.  Use POOL for allocations.
89  */
90 static void
91 print_largest_reps(svn_fs_fs__largest_changes_t *changes,
92                    apr_pool_t *pool)
93 {
94   apr_size_t i;
95   for (i = 0; i < changes->count && changes->changes[i]->size; ++i)
96     printf(_("%12s r%-8ld %s\n"),
97            svn__ui64toa_sep(changes->changes[i]->size, ',', pool),
98            changes->changes[i]->revision,
99            changes->changes[i]->path->data);
100 }
101
102 /* Print the non-zero section of HISTOGRAM to console.
103  * Use POOL for allocations.
104  */
105 static void
106 print_histogram(svn_fs_fs__histogram_t *histogram,
107                 apr_pool_t *pool)
108 {
109   int first = 0;
110   int last = 63;
111   int i;
112
113   /* identify non-zero range */
114   while (last > 0 && histogram->lines[last].count == 0)
115     --last;
116
117   while (first <= last && histogram->lines[first].count == 0)
118     ++first;
119
120   /* display histogram lines */
121   for (i = last; i >= first; --i)
122     printf(_("  %4s .. < %-4s %19s (%2d%%) bytes in %12s (%2d%%) items\n"),
123            print_two_power(i-1, pool), print_two_power(i, pool),
124            svn__ui64toa_sep(histogram->lines[i].sum, ',', pool),
125            (int)(histogram->lines[i].sum * 100 / histogram->total.sum),
126            svn__ui64toa_sep(histogram->lines[i].count, ',', pool),
127            (int)(histogram->lines[i].count * 100 / histogram->total.count));
128 }
129
130 /* COMPARISON_FUNC for svn_sort__hash.
131  * Sort extension_info_t values by total count in descending order.
132  */
133 static int
134 compare_count(const svn_sort__item_t *a,
135               const svn_sort__item_t *b)
136 {
137   const svn_fs_fs__extension_info_t *lhs = a->value;
138   const svn_fs_fs__extension_info_t *rhs = b->value;
139   apr_int64_t diff = lhs->node_histogram.total.count
140                    - rhs->node_histogram.total.count;
141
142   return diff > 0 ? -1 : (diff < 0 ? 1 : 0);
143 }
144
145 /* COMPARISON_FUNC for svn_sort__hash.
146  * Sort extension_info_t values by total uncompressed size in descending order.
147  */
148 static int
149 compare_node_size(const svn_sort__item_t *a,
150                   const svn_sort__item_t *b)
151 {
152   const svn_fs_fs__extension_info_t *lhs = a->value;
153   const svn_fs_fs__extension_info_t *rhs = b->value;
154   apr_int64_t diff = lhs->node_histogram.total.sum
155                    - rhs->node_histogram.total.sum;
156
157   return diff > 0 ? -1 : (diff < 0 ? 1 : 0);
158 }
159
160 /* COMPARISON_FUNC for svn_sort__hash.
161  * Sort extension_info_t values by total prep count in descending order.
162  */
163 static int
164 compare_rep_size(const svn_sort__item_t *a,
165                  const svn_sort__item_t *b)
166 {
167   const svn_fs_fs__extension_info_t *lhs = a->value;
168   const svn_fs_fs__extension_info_t *rhs = b->value;
169   apr_int64_t diff = lhs->rep_histogram.total.sum
170                    - rhs->rep_histogram.total.sum;
171
172   return diff > 0 ? -1 : (diff < 0 ? 1 : 0);
173 }
174
175 /* Return an array of extension_info_t* for the (up to) 16 most prominent
176  * extensions in STATS according to the sort criterion COMPARISON_FUNC.
177  * Allocate results in POOL.
178  */
179 static apr_array_header_t *
180 get_by_extensions(svn_fs_fs__stats_t *stats,
181                   int (*comparison_func)(const svn_sort__item_t *,
182                                          const svn_sort__item_t *),
183                   apr_pool_t *pool)
184 {
185   /* sort all data by extension */
186   apr_array_header_t *sorted
187     = svn_sort__hash(stats->by_extension, comparison_func, pool);
188
189   /* select the top (first) 16 entries */
190   int count = MIN(sorted->nelts, 16);
191   apr_array_header_t *result
192     = apr_array_make(pool, count, sizeof(svn_fs_fs__extension_info_t*));
193   int i;
194
195   for (i = 0; i < count; ++i)
196     APR_ARRAY_PUSH(result, svn_fs_fs__extension_info_t*)
197      = APR_ARRAY_IDX(sorted, i, svn_sort__item_t).value;
198
199   return result;
200 }
201
202 /* Add all extension_info_t* entries of TO_ADD not already in TARGET to
203  * TARGET.
204  */
205 static void
206 merge_by_extension(apr_array_header_t *target,
207                    apr_array_header_t *to_add)
208 {
209   int i, k, count;
210
211   count = target->nelts;
212   for (i = 0; i < to_add->nelts; ++i)
213     {
214       svn_fs_fs__extension_info_t *info
215         = APR_ARRAY_IDX(to_add, i, svn_fs_fs__extension_info_t *);
216       for (k = 0; k < count; ++k)
217         if (info == APR_ARRAY_IDX(target, k, svn_fs_fs__extension_info_t *))
218           break;
219
220       if (k == count)
221         APR_ARRAY_PUSH(target, svn_fs_fs__extension_info_t*) = info;
222     }
223 }
224
225 /* Print the (up to) 16 extensions in STATS with the most changes.
226  * Use POOL for allocations.
227  */
228 static void
229 print_extensions_by_changes(svn_fs_fs__stats_t *stats,
230                             apr_pool_t *pool)
231 {
232   apr_array_header_t *data = get_by_extensions(stats, compare_count, pool);
233   apr_int64_t sum = 0;
234   int i;
235
236   for (i = 0; i < data->nelts; ++i)
237     {
238       svn_fs_fs__extension_info_t *info
239         = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *);
240
241       /* If there are elements, then their count cannot be 0. */
242       assert(stats->file_histogram.total.count);
243
244       sum += info->node_histogram.total.count;
245       printf(_("%11s %20s (%2d%%) representations\n"),
246              info->extension,
247              svn__ui64toa_sep(info->node_histogram.total.count, ',', pool),
248              (int)(info->node_histogram.total.count * 100 /
249                    stats->file_histogram.total.count));
250     }
251
252   if (stats->file_histogram.total.count)
253     {
254       printf(_("%11s %20s (%2d%%) representations\n"),
255              "(others)",
256              svn__ui64toa_sep(stats->file_histogram.total.count - sum, ',',
257                               pool),
258              (int)((stats->file_histogram.total.count - sum) * 100 /
259                    stats->file_histogram.total.count));
260     }
261 }
262
263 /* Calculate a percentage, handling edge cases. */
264 static int
265 get_percentage(apr_uint64_t part,
266                apr_uint64_t total)
267 {
268   /* This include total == 0. */
269   if (part >= total)
270     return 100;
271
272   /* Standard case. */
273   return (int)(part * 100.0 / total);
274 }
275
276 /* Print the (up to) 16 extensions in STATS with the largest total size of
277  * changed file content.  Use POOL for allocations.
278  */
279 static void
280 print_extensions_by_nodes(svn_fs_fs__stats_t *stats,
281                           apr_pool_t *pool)
282 {
283   apr_array_header_t *data = get_by_extensions(stats, compare_node_size, pool);
284   apr_int64_t sum = 0;
285   int i;
286
287   for (i = 0; i < data->nelts; ++i)
288     {
289       svn_fs_fs__extension_info_t *info
290         = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *);
291       sum += info->node_histogram.total.sum;
292       printf(_("%11s %20s (%2d%%) bytes\n"),
293              info->extension,
294              svn__ui64toa_sep(info->node_histogram.total.sum, ',', pool),
295              get_percentage(info->node_histogram.total.sum,
296                             stats->file_histogram.total.sum));
297     }
298
299   if (stats->file_histogram.total.sum > sum)
300     {
301       /* Total sum can't be zero here. */
302       printf(_("%11s %20s (%2d%%) bytes\n"),
303              "(others)",
304              svn__ui64toa_sep(stats->file_histogram.total.sum - sum, ',',
305                               pool),
306              get_percentage(stats->file_histogram.total.sum - sum,
307                             stats->file_histogram.total.sum));
308     }
309 }
310
311 /* Print the (up to) 16 extensions in STATS with the largest total size of
312  * changed file content.  Use POOL for allocations.
313  */
314 static void
315 print_extensions_by_reps(svn_fs_fs__stats_t *stats,
316                          apr_pool_t *pool)
317 {
318   apr_array_header_t *data = get_by_extensions(stats, compare_rep_size, pool);
319   apr_int64_t sum = 0;
320   int i;
321
322   for (i = 0; i < data->nelts; ++i)
323     {
324       svn_fs_fs__extension_info_t *info
325         = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *);
326       sum += info->rep_histogram.total.sum;
327       printf(_("%11s %20s (%2d%%) bytes\n"),
328              info->extension,
329              svn__ui64toa_sep(info->rep_histogram.total.sum, ',', pool),
330              get_percentage(info->rep_histogram.total.sum,
331                             stats->rep_size_histogram.total.sum));
332     }
333
334   if (stats->rep_size_histogram.total.sum > sum)
335     {
336       /* Total sum can't be zero here. */
337       printf(_("%11s %20s (%2d%%) bytes\n"),
338              "(others)",
339              svn__ui64toa_sep(stats->rep_size_histogram.total.sum - sum, ',',
340                               pool),
341              get_percentage(stats->rep_size_histogram.total.sum - sum,
342                             stats->rep_size_histogram.total.sum));
343     }
344 }
345
346 /* Print per-extension histograms for the most frequent extensions in STATS.
347  * Use POOL for allocations. */
348 static void
349 print_histograms_by_extension(svn_fs_fs__stats_t *stats,
350                               apr_pool_t *pool)
351 {
352   apr_array_header_t *data = get_by_extensions(stats, compare_count, pool);
353   int i;
354
355   merge_by_extension(data, get_by_extensions(stats, compare_node_size, pool));
356   merge_by_extension(data, get_by_extensions(stats, compare_rep_size, pool));
357
358   for (i = 0; i < data->nelts; ++i)
359     {
360       svn_fs_fs__extension_info_t *info
361         = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *);
362       printf("\nHistogram of '%s' file sizes:\n", info->extension);
363       print_histogram(&info->node_histogram, pool);
364       printf("\nHistogram of '%s' file representation sizes:\n",
365              info->extension);
366       print_histogram(&info->rep_histogram, pool);
367     }
368 }
369
370 /* Print the contents of STATS to the console.
371  * Use POOL for allocations.
372  */
373 static void
374 print_stats(svn_fs_fs__stats_t *stats,
375             apr_pool_t *pool)
376 {
377   /* print results */
378   printf("\nGlobal statistics:\n");
379   printf(_("%20s bytes in %12s revisions\n"
380            "%20s bytes in %12s changes\n"
381            "%20s bytes in %12s node revision records\n"
382            "%20s bytes in %12s representations\n"
383            "%20s bytes expanded representation size\n"
384            "%20s bytes with rep-sharing off\n"),
385          svn__ui64toa_sep(stats->total_size, ',', pool),
386          svn__ui64toa_sep(stats->revision_count, ',', pool),
387          svn__ui64toa_sep(stats->change_len, ',', pool),
388          svn__ui64toa_sep(stats->change_count, ',', pool),
389          svn__ui64toa_sep(stats->total_node_stats.size, ',', pool),
390          svn__ui64toa_sep(stats->total_node_stats.count, ',', pool),
391          svn__ui64toa_sep(stats->total_rep_stats.total.packed_size, ',',
392                          pool),
393          svn__ui64toa_sep(stats->total_rep_stats.total.count, ',', pool),
394          svn__ui64toa_sep(stats->total_rep_stats.total.expanded_size, ',',
395                          pool),
396          svn__ui64toa_sep(stats->total_rep_stats.expanded_size, ',', pool));
397
398   printf("\nNoderev statistics:\n");
399   printf(_("%20s bytes in %12s nodes total\n"
400            "%20s bytes in %12s directory noderevs\n"
401            "%20s bytes in %12s file noderevs\n"),
402          svn__ui64toa_sep(stats->total_node_stats.size, ',', pool),
403          svn__ui64toa_sep(stats->total_node_stats.count, ',', pool),
404          svn__ui64toa_sep(stats->dir_node_stats.size, ',', pool),
405          svn__ui64toa_sep(stats->dir_node_stats.count, ',', pool),
406          svn__ui64toa_sep(stats->file_node_stats.size, ',', pool),
407          svn__ui64toa_sep(stats->file_node_stats.count, ',', pool));
408
409   printf("\nRepresentation statistics:\n");
410   printf(_("%20s bytes in %12s representations total\n"
411            "%20s bytes in %12s directory representations\n"
412            "%20s bytes in %12s file representations\n"
413            "%20s bytes in %12s representations of added file nodes\n"
414            "%20s bytes in %12s directory property representations\n"
415            "%20s bytes in %12s file property representations\n"
416            "%20s bytes in header & footer overhead\n"),
417          svn__ui64toa_sep(stats->total_rep_stats.total.packed_size, ',',
418                          pool),
419          svn__ui64toa_sep(stats->total_rep_stats.total.count, ',', pool),
420          svn__ui64toa_sep(stats->dir_rep_stats.total.packed_size, ',',
421                          pool),
422          svn__ui64toa_sep(stats->dir_rep_stats.total.count, ',', pool),
423          svn__ui64toa_sep(stats->file_rep_stats.total.packed_size, ',',
424                          pool),
425          svn__ui64toa_sep(stats->file_rep_stats.total.count, ',', pool),
426          svn__ui64toa_sep(stats->added_rep_size_histogram.total.sum, ',',
427                          pool),
428          svn__ui64toa_sep(stats->added_rep_size_histogram.total.count, ',',
429                          pool),
430          svn__ui64toa_sep(stats->dir_prop_rep_stats.total.packed_size, ',',
431                          pool),
432          svn__ui64toa_sep(stats->dir_prop_rep_stats.total.count, ',', pool),
433          svn__ui64toa_sep(stats->file_prop_rep_stats.total.packed_size, ',',
434                          pool),
435          svn__ui64toa_sep(stats->file_prop_rep_stats.total.count, ',', pool),
436          svn__ui64toa_sep(stats->total_rep_stats.total.overhead_size, ',',
437                         pool));
438
439   printf("\nDirectory representation statistics:\n");
440   print_rep_stats(&stats->dir_rep_stats, pool);
441   printf("\nFile representation statistics:\n");
442   print_rep_stats(&stats->file_rep_stats, pool);
443   printf("\nDirectory property representation statistics:\n");
444   print_rep_stats(&stats->dir_prop_rep_stats, pool);
445   printf("\nFile property representation statistics:\n");
446   print_rep_stats(&stats->file_prop_rep_stats, pool);
447
448   printf("\nLargest representations:\n");
449   print_largest_reps(stats->largest_changes, pool);
450   printf("\nExtensions by number of representations:\n");
451   print_extensions_by_changes(stats, pool);
452   printf("\nExtensions by size of changed files:\n");
453   print_extensions_by_nodes(stats, pool);
454   printf("\nExtensions by size of representations:\n");
455   print_extensions_by_reps(stats, pool);
456
457   printf("\nHistogram of expanded node sizes:\n");
458   print_histogram(&stats->node_size_histogram, pool);
459   printf("\nHistogram of representation sizes:\n");
460   print_histogram(&stats->rep_size_histogram, pool);
461   printf("\nHistogram of file sizes:\n");
462   print_histogram(&stats->file_histogram, pool);
463   printf("\nHistogram of file representation sizes:\n");
464   print_histogram(&stats->file_rep_histogram, pool);
465   printf("\nHistogram of file property sizes:\n");
466   print_histogram(&stats->file_prop_histogram, pool);
467   printf("\nHistogram of file property representation sizes:\n");
468   print_histogram(&stats->file_prop_rep_histogram, pool);
469   printf("\nHistogram of directory sizes:\n");
470   print_histogram(&stats->dir_histogram, pool);
471   printf("\nHistogram of directory representation sizes:\n");
472   print_histogram(&stats->dir_rep_histogram, pool);
473   printf("\nHistogram of directory property sizes:\n");
474   print_histogram(&stats->dir_prop_histogram, pool);
475   printf("\nHistogram of directory property representation sizes:\n");
476   print_histogram(&stats->dir_prop_rep_histogram, pool);
477
478   print_histograms_by_extension(stats, pool);
479 }
480
481 /* Our progress function simply prints the REVISION number and makes it
482  * appear immediately.
483  */
484 static void
485 print_progress(svn_revnum_t revision,
486                void *baton,
487                apr_pool_t *pool)
488 {
489   printf("%8ld", revision);
490   fflush(stdout);
491 }
492
493 /* This implements `svn_opt_subcommand_t'. */
494 svn_error_t *
495 subcommand__stats(apr_getopt_t *os, void *baton, apr_pool_t *pool)
496 {
497   svnfsfs__opt_state *opt_state = baton;
498   svn_fs_fs__stats_t *stats;
499   svn_fs_t *fs;
500
501   printf("Reading revisions\n");
502   SVN_ERR(open_fs(&fs, opt_state->repository_path, pool));
503   SVN_ERR(svn_fs_fs__get_stats(&stats, fs, print_progress, NULL,
504                                check_cancel, NULL, pool, pool));
505
506   print_stats(stats, pool);
507
508   return SVN_NO_ERROR;
509 }