diff options
Diffstat (limited to 'tools/server-side/svn-rep-sharing-stats.c')
-rw-r--r-- | tools/server-side/svn-rep-sharing-stats.c | 530 |
1 files changed, 0 insertions, 530 deletions
diff --git a/tools/server-side/svn-rep-sharing-stats.c b/tools/server-side/svn-rep-sharing-stats.c deleted file mode 100644 index f610409..0000000 --- a/tools/server-side/svn-rep-sharing-stats.c +++ /dev/null @@ -1,530 +0,0 @@ -/* - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * ==================================================================== - */ - -#include <apr_signal.h> - -#include "svn_cmdline.h" -#include "svn_dirent_uri.h" -#include "svn_pools.h" -#include "svn_repos.h" -#include "svn_opt.h" -#include "svn_utf.h" -#include "svn_version.h" - -#include "../../subversion/libsvn_fs_fs/fs.h" -#include "../../subversion/libsvn_fs_fs/fs_fs.h" -/* for svn_fs_fs__id_* (used in assertions only) */ -#include "../../subversion/libsvn_fs_fs/id.h" - -#include "private/svn_cmdline_private.h" - -#include "svn_private_config.h" - - -/** Help messages and version checking. **/ - -static svn_error_t * -version(apr_pool_t *pool) -{ - return svn_opt_print_help4(NULL, "svn-rep-sharing-stats", TRUE, FALSE, FALSE, - NULL, NULL, NULL, NULL, NULL, NULL, pool); -} - -static void -usage(apr_pool_t *pool) -{ - svn_error_clear(svn_cmdline_fprintf - (stderr, pool, - _("Type 'svn-rep-sharing-stats --help' for usage.\n"))); -} - - -static void -help(const apr_getopt_option_t *options, apr_pool_t *pool) -{ - svn_error_clear - (svn_cmdline_fprintf - (stdout, pool, - _("usage: svn-rep-sharing-stats [OPTIONS] REPOS_PATH\n\n" - " Prints the reference count statistics for representations\n" - " in an FSFS repository.\n" - "\n" - " At least one of the options --data/--prop/--both must be specified.\n" - "\n" - "Valid options:\n"))); - while (options->description) - { - const char *optstr; - svn_opt_format_option(&optstr, options, TRUE, pool); - svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr)); - ++options; - } - svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n")); - exit(0); -} - - -/* Version compatibility check */ -static svn_error_t * -check_lib_versions(void) -{ - static const svn_version_checklist_t checklist[] = - { - /* ### check FSFS version */ - { "svn_subr", svn_subr_version }, - { "svn_fs", svn_fs_version }, - { NULL, NULL } - }; - SVN_VERSION_DEFINE(my_version); - - return svn_error_trace(svn_ver_check_list(&my_version, checklist)); -} - - - -/** Cancellation stuff, ### copied from subversion/svn/main.c */ - -/* A flag to see if we've been cancelled by the client or not. */ -static volatile sig_atomic_t cancelled = FALSE; - -/* A signal handler to support cancellation. */ -static void -signal_handler(int signum) -{ - apr_signal(signum, SIG_IGN); - cancelled = TRUE; -} - -/* Our cancellation callback. */ -static svn_error_t * -svn_cl__check_cancel(void *baton) -{ - if (cancelled) - return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal")); - else - return SVN_NO_ERROR; -} - -static svn_cancel_func_t cancel_func = svn_cl__check_cancel; - -static void set_up_cancellation(void) -{ - /* Set up our cancellation support. */ - apr_signal(SIGINT, signal_handler); -#ifdef SIGBREAK - /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */ - apr_signal(SIGBREAK, signal_handler); -#endif -#ifdef SIGHUP - apr_signal(SIGHUP, signal_handler); -#endif -#ifdef SIGTERM - apr_signal(SIGTERM, signal_handler); -#endif - -#ifdef SIGPIPE - /* Disable SIGPIPE generation for the platforms that have it. */ - apr_signal(SIGPIPE, SIG_IGN); -#endif - -#ifdef SIGXFSZ - /* Disable SIGXFSZ generation for the platforms that have it, otherwise - * working with large files when compiled against an APR that doesn't have - * large file support will crash the program, which is uncool. */ - apr_signal(SIGXFSZ, SIG_IGN); -#endif -} - - -/** Program-specific code. **/ -enum { - OPT_VERSION = SVN_OPT_FIRST_LONGOPT_ID, - OPT_DATA, - OPT_PROP, - OPT_BOTH -}; - -static svn_error_t *check_experimental(void) -{ - if (getenv("SVN_REP_SHARING_STATS_IS_EXPERIMENTAL")) - return SVN_NO_ERROR; - - return svn_error_create(APR_EGENERAL, NULL, - "This code is experimental and should not " - "be used on live data."); -} - -/* The parts of a rep that determine whether it's being shared. */ -struct key_t -{ - svn_revnum_t revision; - apr_off_t offset; -}; - -/* What we need to know about a rep. */ -struct value_t -{ - svn_checksum_t *sha1_checksum; - apr_uint64_t refcount; -}; - -/* Increment records[rep] if both are non-NULL and REP contains a sha1. - * Allocate keys and values in RESULT_POOL. - */ -static svn_error_t *record(apr_hash_t *records, - representation_t *rep, - apr_pool_t *result_pool) -{ - struct key_t *key; - struct value_t *value; - - /* Skip if we ignore this particular kind of reps, or if the rep doesn't - * exist or doesn't have the checksum we are after. (The latter case - * often corresponds to node_rev->kind == svn_node_dir.) - */ - if (records == NULL || rep == NULL || rep->sha1_checksum == NULL) - return SVN_NO_ERROR; - - /* Construct the key. - * - * Must use calloc() because apr_hash_* pay attention to padding bytes too. - */ - key = apr_pcalloc(result_pool, sizeof(*key)); - key->revision = rep->revision; - key->offset = rep->offset; - - /* Update or create the value. */ - if ((value = apr_hash_get(records, key, sizeof(*key)))) - { - /* Paranoia. */ - SVN_ERR_ASSERT(value->sha1_checksum != NULL); - SVN_ERR_ASSERT(svn_checksum_match(value->sha1_checksum, - rep->sha1_checksum)); - /* Real work. */ - value->refcount++; - } - else - { - value = apr_palloc(result_pool, sizeof(*value)); - value->sha1_checksum = svn_checksum_dup(rep->sha1_checksum, result_pool); - value->refcount = 1; - } - - /* Store them. */ - apr_hash_set(records, key, sizeof(*key), value); - - return SVN_NO_ERROR; -} - -/* Inspect the data and/or prop reps of revision REVNUM in FS. Store - * reference count tallies in passed hashes (allocated in RESULT_POOL). - * - * If PROP_REPS or DATA_REPS is NULL, the respective kind of reps are not - * tallied. - * - * Print progress report to STDERR unless QUIET is true. - * - * Use SCRATCH_POOL for temporary allocations. - */ -static svn_error_t * -process_one_revision(svn_fs_t *fs, - svn_revnum_t revnum, - svn_boolean_t quiet, - apr_hash_t *prop_reps, - apr_hash_t *data_reps, - apr_hash_t *both_reps, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_fs_root_t *rev_root; - apr_hash_t *paths_changed; - apr_hash_index_t *hi; - - if (! quiet) - SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, - "processing r%ld\n", revnum)); - - /* Get the changed paths. */ - SVN_ERR(svn_fs_revision_root(&rev_root, fs, revnum, scratch_pool)); - SVN_ERR(svn_fs_paths_changed2(&paths_changed, rev_root, scratch_pool)); - - /* Iterate them. */ - /* ### use iterpool? */ - for (hi = apr_hash_first(scratch_pool, paths_changed); - hi; hi = apr_hash_next(hi)) - { - const char *path; - const svn_fs_path_change2_t *change; - const svn_fs_id_t *node_rev_id1, *node_rev_id2; - const svn_fs_id_t *the_id; - - node_revision_t *node_rev; - - path = svn__apr_hash_index_key(hi); - change = svn__apr_hash_index_val(hi); - if (! quiet) - SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, - "processing r%ld:%s\n", revnum, path)); - - if (change->change_kind == svn_fs_path_change_delete) - /* Can't ask for reps of PATH at REVNUM if the path no longer exists - * at that revision! */ - continue; - - /* Okay, we have two node_rev id's for this change: the txn one and - * the revision one. We'll use the latter. */ - node_rev_id1 = change->node_rev_id; - SVN_ERR(svn_fs_node_id(&node_rev_id2, rev_root, path, scratch_pool)); - - SVN_ERR_ASSERT(svn_fs_fs__id_txn_id(node_rev_id1) != NULL); - SVN_ERR_ASSERT(svn_fs_fs__id_rev(node_rev_id2) != SVN_INVALID_REVNUM); - - the_id = node_rev_id2; - - /* Get the node_rev using the chosen node_rev_id. */ - SVN_ERR(svn_fs_fs__get_node_revision(&node_rev, fs, the_id, scratch_pool)); - - /* Maybe record the sha1's. */ - SVN_ERR(record(prop_reps, node_rev->prop_rep, result_pool)); - SVN_ERR(record(data_reps, node_rev->data_rep, result_pool)); - SVN_ERR(record(both_reps, node_rev->prop_rep, result_pool)); - SVN_ERR(record(both_reps, node_rev->data_rep, result_pool)); - } - - return SVN_NO_ERROR; -} - -/* Print REPS_REF_COUNT (a hash as for process_one_revision()) - * to stdout in "refcount => sha1" format. A sha1 may appear - * more than once if not all its instances are shared. Prepend - * each line by NAME. - * - * Use SCRATCH_POOL for temporary allocations. - */ -static svn_error_t * -pretty_print(const char *name, - apr_hash_t *reps_ref_counts, - apr_pool_t *scratch_pool) -{ - apr_hash_index_t *hi; - - if (reps_ref_counts == NULL) - return SVN_NO_ERROR; - - for (hi = apr_hash_first(scratch_pool, reps_ref_counts); - hi; hi = apr_hash_next(hi)) - { - struct value_t *value; - - SVN_ERR(cancel_func(NULL)); - - value = svn__apr_hash_index_val(hi); - SVN_ERR(svn_cmdline_printf(scratch_pool, "%s %" APR_UINT64_T_FMT " %s\n", - name, value->refcount, - svn_checksum_to_cstring_display( - value->sha1_checksum, - scratch_pool))); - } - - return SVN_NO_ERROR; -} - -/* Return an error unless FS is an fsfs fs. */ -static svn_error_t *is_fs_fsfs(svn_fs_t *fs, apr_pool_t *scratch_pool) -{ - const char *actual, *expected, *path; - - path = svn_fs_path(fs, scratch_pool); - - expected = SVN_FS_TYPE_FSFS; - SVN_ERR(svn_fs_type(&actual, path, scratch_pool)); - - if (strcmp(actual, expected) != 0) - return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL, - "Filesystem '%s' is not of type '%s'", - svn_dirent_local_style(path, scratch_pool), - actual); - - return SVN_NO_ERROR; -} - -/* The core logic. This function iterates the repository REPOS_PATH - * and sends all the (DATA and/or PROP) reps in each revision for counting - * by process_one_revision(). QUIET is passed to process_one_revision(). - */ -static svn_error_t *process(const char *repos_path, - svn_boolean_t prop, - svn_boolean_t data, - svn_boolean_t quiet, - apr_pool_t *scratch_pool) -{ - apr_hash_t *prop_reps = NULL; - apr_hash_t *data_reps = NULL; - apr_hash_t *both_reps = NULL; - svn_revnum_t rev, youngest; - apr_pool_t *iterpool; - svn_repos_t *repos; - svn_fs_t *fs; - - if (prop) - prop_reps = apr_hash_make(scratch_pool); - if (data) - data_reps = apr_hash_make(scratch_pool); - if (prop && data) - both_reps = apr_hash_make(scratch_pool); - - /* Open the FS. */ - SVN_ERR(svn_repos_open2(&repos, repos_path, NULL, scratch_pool)); - fs = svn_repos_fs(repos); - - SVN_ERR(is_fs_fsfs(fs, scratch_pool)); - - SVN_ERR(svn_fs_youngest_rev(&youngest, fs, scratch_pool)); - - /* Iterate the revisions. */ - iterpool = svn_pool_create(scratch_pool); - for (rev = 0; rev <= youngest; rev++) - { - svn_pool_clear(iterpool); - SVN_ERR(cancel_func(NULL)); - SVN_ERR(process_one_revision(fs, rev, quiet, - prop_reps, data_reps, both_reps, - scratch_pool, iterpool)); - } - svn_pool_destroy(iterpool); - - /* Print stats. */ - SVN_ERR(pretty_print("prop", prop_reps, scratch_pool)); - SVN_ERR(pretty_print("data", data_reps, scratch_pool)); - SVN_ERR(pretty_print("both", both_reps, scratch_pool)); - - return SVN_NO_ERROR; -} - -int -main(int argc, const char *argv[]) -{ - const char *repos_path; - apr_pool_t *pool; - svn_boolean_t prop = FALSE, data = FALSE; - svn_boolean_t quiet = FALSE; - svn_error_t *err; - apr_getopt_t *os; - const apr_getopt_option_t options[] = - { - {"data", OPT_DATA, 0, N_("display data reps stats")}, - {"prop", OPT_PROP, 0, N_("display prop reps stats")}, - {"both", OPT_BOTH, 0, N_("display combined (data+prop) reps stats")}, - {"quiet", 'q', 0, N_("no progress (only errors) to stderr")}, - {"help", 'h', 0, N_("display this help")}, - {"version", OPT_VERSION, 0, - N_("show program version information")}, - {0, 0, 0, 0} - }; - - /* Initialize the app. */ - if (svn_cmdline_init("svn-rep-sharing-stats", stderr) != EXIT_SUCCESS) - return EXIT_FAILURE; - - /* Create our top-level pool. Use a separate mutexless allocator, - * given this application is single threaded. - */ - pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); - - /* Check library versions */ - err = check_lib_versions(); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svn-rep-sharing-stats: "); - - err = svn_cmdline__getopt_init(&os, argc, argv, pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svn-rep-sharing-stats: "); - - SVN_INT_ERR(check_experimental()); - - os->interleave = 1; - while (1) - { - int opt; - const char *arg; - apr_status_t status = apr_getopt_long(os, options, &opt, &arg); - if (APR_STATUS_IS_EOF(status)) - break; - if (status != APR_SUCCESS) - { - usage(pool); - return EXIT_FAILURE; - } - switch (opt) - { - case OPT_DATA: - data = TRUE; - break; - /* It seems we don't actually rep-share props yet. */ - case OPT_PROP: - prop = TRUE; - break; - case OPT_BOTH: - data = TRUE; - prop = TRUE; - break; - case 'q': - quiet = TRUE; - break; - case 'h': - help(options, pool); - break; - case OPT_VERSION: - SVN_INT_ERR(version(pool)); - exit(0); - break; - default: - usage(pool); - return EXIT_FAILURE; - } - } - - /* Exactly 1 non-option argument, - * and at least one of "--data"/"--prop"/"--both". - */ - if (os->ind + 1 != argc || (!data && !prop)) - { - usage(pool); - return EXIT_FAILURE; - } - - /* Grab REPOS_PATH from argv. */ - SVN_INT_ERR(svn_utf_cstring_to_utf8(&repos_path, os->argv[os->ind], pool)); - repos_path = svn_dirent_internal_style(repos_path, pool); - - set_up_cancellation(); - - /* Do something. */ - SVN_INT_ERR(process(repos_path, prop, data, quiet, pool)); - - /* We're done. */ - - svn_pool_destroy(pool); - /* Flush stdout to make sure that the user will see any printing errors. */ - SVN_INT_ERR(svn_cmdline_fflush(stdout)); - - return EXIT_SUCCESS; -} |