Modified: subversion/branches/authzperf/subversion/svnfsfs/stats-cmd.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/svnfsfs/stats-cmd.c?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/svnfsfs/stats-cmd.c (original) +++ subversion/branches/authzperf/subversion/svnfsfs/stats-cmd.c Sat Jan 3 14:00:41 2015 @@ -20,1629 +20,51 @@ * ==================================================================== */ -#include "svn_dirent_uri.h" #include "svn_fs.h" #include "svn_pools.h" #include "svn_sorts.h" -#include "private/svn_cache.h" #include "private/svn_sorts_private.h" #include "private/svn_string_private.h" - -#include "../libsvn_fs_fs/index.h" -#include "../libsvn_fs_fs/pack.h" -#include "../libsvn_fs_fs/rev_file.h" -#include "../libsvn_fs_fs/util.h" -#include "../libsvn_fs/fs-loader.h" +#include "private/svn_fs_fs_private.h" #include "svn_private_config.h" #include "svnfsfs.h" -/* We group representations into 2x2 different kinds plus one default: - * [dir / file] x [text / prop]. The assignment is done by the first node - * that references the respective representation. +/* Return the string, allocated in RESULT_POOL, describing the value 2**I. */ -typedef enum rep_kind_t -{ - /* The representation is _directly_ unused, i.e. not referenced by any - * noderev. However, some other representation may use it as delta base. - * null value. Should not occur in real-word repositories. */ - unused_rep, - - /* a properties on directory rep */ - dir_property_rep, - - /* a properties on file rep */ - file_property_rep, - - /* a directory rep */ - dir_rep, - - /* a file rep */ - file_rep -} rep_kind_t; - -/* A representation fragment. - */ -typedef struct rep_stats_t -{ - /* absolute offset in the file */ - apr_size_t offset; - - /* item length in bytes */ - apr_size_t size; - - /* item length after de-deltification */ - apr_size_t expanded_size; - - /* deltification base, or NULL if there is none */ - struct rep_stats_t *delta_base; - - /* revision that contains this representation - * (may be referenced by other revisions, though) */ - svn_revnum_t revision; - - /* number of nodes that reference this representation */ - apr_uint32_t ref_count; - - /* length of the PLAIN / DELTA line in the source file in bytes */ - apr_uint16_t header_size; - - /* classification of the representation. values of rep_kind_t */ - char kind; - - /* the source content has a PLAIN header, so we may simply copy the - * source content into the target */ - char is_plain; - -} rep_stats_t; - -/* Represents a single revision. - * There will be only one instance per revision. */ -typedef struct revision_info_t +static const char * +print_two_power(int i, + apr_pool_t *result_pool) { - /* number of this revision */ - svn_revnum_t revision; - - /* pack file offset (manifest value), 0 for non-packed files */ - apr_off_t offset; + /* These are the SI prefixes for base-1000, the binary ones with base-1024 + are too clumsy and require appending B for "byte" to be intelligible, + e.g. "MiB". - /* offset of the changes list relative to OFFSET */ - apr_size_t changes; + Therefore, we ignore the official standard and revert to the traditional + contextual use were the base-1000 prefixes are understood as base-1024 + when it came to data sizes. + */ + const char *si_prefixes = " kMGTPEZY"; - /* length of the changes list on bytes */ - apr_size_t changes_len; + int number = (1 << (i % 10)); + int thousands = i / 10; - /* offset of the changes list relative to OFFSET */ - apr_size_t change_count; + char si_prefix = ((thousands >= 0) && (thousands < strlen(si_prefixes))) + ? si_prefixes[thousands] + : '?'; - /* first offset behind the revision data in the pack file (file length - * for non-packed revs) */ - apr_off_t end; + if (si_prefix == ' ') + return apr_psprintf(result_pool, "%d", number); - /* number of directory noderevs in this revision */ - apr_size_t dir_noderev_count; - - /* number of file noderevs in this revision */ - apr_size_t file_noderev_count; - - /* total size of directory noderevs (i.e. the structs - not the rep) */ - apr_size_t dir_noderev_size; - - /* total size of file noderevs (i.e. the structs - not the rep) */ - apr_size_t file_noderev_size; - - /* all rep_stats_t of this revision (in no particular order), - * i.e. those that point back to this struct */ - apr_array_header_t *representations; -} revision_info_t; - -/* Data type to identify a representation. It will be used to address - * cached combined (un-deltified) windows. - */ -typedef struct cache_key_t -{ - /* revision of the representation */ - svn_revnum_t revision; - - /* its offset */ - apr_size_t offset; -} cache_key_t; - -/* Description of one large representation. It's content will be reused / - * overwritten when it gets replaced by an even larger representation. - */ -typedef struct large_change_info_t -{ - /* size of the (deltified) representation */ - apr_size_t size; - - /* revision of the representation */ - svn_revnum_t revision; - - /* node path. "" for unused instances */ - svn_stringbuf_t *path; -} large_change_info_t; - -/* Container for the largest representations found so far. The capacity - * is fixed and entries will be inserted by reusing the last one and - * reshuffling the entry pointers. - */ -typedef struct largest_changes_t -{ - /* number of entries allocated in CHANGES */ - apr_size_t count; - - /* size of the smallest change */ - apr_size_t min_size; - - /* changes kept in this struct */ - large_change_info_t **changes; -} largest_changes_t; - -/* Information we gather per size bracket. - */ -typedef struct histogram_line_t -{ - /* number of item that fall into this bracket */ - apr_int64_t count; - - /* sum of values in this bracket */ - apr_int64_t sum; -} histogram_line_t; - -/* A histogram of 64 bit integer values. - */ -typedef struct histogram_t -{ - /* total sum over all brackets */ - histogram_line_t total; - - /* one bracket per binary step. - * line[i] is the 2^(i-1) <= x < 2^i bracket */ - histogram_line_t lines[64]; -} histogram_t; - -/* Information we collect per file ending. - */ -typedef struct extension_info_t -{ - /* file extension, including leading "." - * "(none)" in the container for files w/o extension. */ - const char *extension; - - /* histogram of representation sizes */ - histogram_t rep_histogram; - - /* histogram of sizes of changed files */ - histogram_t node_histogram; -} extension_info_t; - -/* Root data structure containing all information about a given repository. - */ -typedef struct fs_t -{ - /* FS API object*/ - svn_fs_t *fs; - - /* all revisions */ - apr_array_header_t *revisions; - - /* empty representation. - * Used as a dummy base for DELTA reps without base. */ - rep_stats_t *null_base; - - /* undeltified txdelta window cache */ - svn_cache__t *window_cache; - - /* track the biggest contributors to repo size */ - largest_changes_t *largest_changes; - - /* history of representation sizes */ - histogram_t rep_size_histogram; - - /* history of sizes of changed nodes */ - histogram_t node_size_histogram; - - /* history of representation sizes */ - histogram_t added_rep_size_histogram; - - /* history of sizes of changed nodes */ - histogram_t added_node_size_histogram; - - /* history of unused representations */ - histogram_t unused_rep_histogram; - - /* history of sizes of changed files */ - histogram_t file_histogram; - - /* history of sizes of file representations */ - histogram_t file_rep_histogram; - - /* history of sizes of changed file property sets */ - histogram_t file_prop_histogram; - - /* history of sizes of file property representations */ - histogram_t file_prop_rep_histogram; - - /* history of sizes of changed directories (in bytes) */ - histogram_t dir_histogram; - - /* history of sizes of directories representations */ - histogram_t dir_rep_histogram; - - /* history of sizes of changed directories property sets */ - histogram_t dir_prop_histogram; - - /* history of sizes of directories property representations */ - histogram_t dir_prop_rep_histogram; - - /* extension -> extension_info_t* map */ - apr_hash_t *by_extension; -} fs_t; - -/* Open the file containing revision REV in FS and return it in *FILE. - */ -static svn_error_t * -open_rev_or_pack_file(apr_file_t **file, - fs_t *fs, - svn_revnum_t rev, - apr_pool_t *pool) -{ - svn_fs_fs__revision_file_t *rev_file; - SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs->fs, rev, - pool, pool)); - - *file = rev_file->file; - return SVN_NO_ERROR; -} - -/* Return the length of FILE in *FILE_SIZE. Use POOL for allocations. -*/ -static svn_error_t * -get_file_size(apr_off_t *file_size, - apr_file_t *file, - apr_pool_t *pool) -{ - apr_finfo_t finfo; - - SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, file, pool)); - - *file_size = finfo.size; - return SVN_NO_ERROR; -} - -/* Get the file content of revision REVISION in FS and return it in *CONTENT. - * Read the LEN bytes starting at file OFFSET. When provided, use FILE as - * packed or plain rev file. - * Use POOL for temporary allocations. - */ -static svn_error_t * -get_content(svn_stringbuf_t **content, - apr_file_t *file, - fs_t *fs, - svn_revnum_t revision, - apr_off_t offset, - apr_size_t len, - apr_pool_t *pool) -{ - apr_pool_t * file_pool = svn_pool_create(pool); - apr_size_t large_buffer_size = 0x10000; - - if (file == NULL) - SVN_ERR(open_rev_or_pack_file(&file, fs, revision, file_pool)); - - *content = svn_stringbuf_create_ensure(len, pool); - (*content)->len = len; - - /* for better efficiency use larger buffers on large reads */ - if ( (len >= large_buffer_size) - && (apr_file_buffer_size_get(file) < large_buffer_size)) - apr_file_buffer_set(file, - apr_palloc(apr_file_pool_get(file), - large_buffer_size), - large_buffer_size); - - SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool)); - SVN_ERR(svn_io_file_read_full2(file, (*content)->data, len, - NULL, NULL, pool)); - svn_pool_destroy(file_pool); - - return SVN_NO_ERROR; -} - -/* In *RESULT, return the cached txdelta window stored in REPRESENTATION - * within FS. If that has not been found in cache, return NULL. - * Allocate the result in POOL. - */ -static svn_error_t * -get_cached_window(svn_stringbuf_t **result, - fs_t *fs, - rep_stats_t *representation, - apr_pool_t *pool) -{ - svn_boolean_t found = FALSE; - cache_key_t key; - key.revision = representation->revision; - key.offset = representation->offset; - - *result = NULL; - return svn_error_trace(svn_cache__get((void**)result, &found, - fs->window_cache, - &key, pool)); -} - -/* Cache the undeltified txdelta WINDOW for REPRESENTATION within FS. - * Use POOL for temporaries. - */ -static svn_error_t * -set_cached_window(fs_t *fs, - rep_stats_t *representation, - svn_stringbuf_t *window, - apr_pool_t *pool) -{ - /* select entry */ - cache_key_t key; - key.revision = representation->revision; - key.offset = representation->offset; - - return svn_error_trace(svn_cache__set(fs->window_cache, &key, window, - pool)); -} - -/* Initialize the LARGEST_CHANGES member in FS with a capacity of COUNT - * entries. Use POOL for allocations. - */ -static void -initialize_largest_changes(fs_t *fs, - apr_size_t count, - apr_pool_t *pool) -{ - apr_size_t i; - - fs->largest_changes = apr_pcalloc(pool, sizeof(*fs->largest_changes)); - fs->largest_changes->count = count; - fs->largest_changes->min_size = 1; - fs->largest_changes->changes - = apr_palloc(pool, count * sizeof(*fs->largest_changes->changes)); - - /* allocate *all* entries before the path stringbufs. This increases - * cache locality and enhances performance significantly. */ - for (i = 0; i < count; ++i) - fs->largest_changes->changes[i] - = apr_palloc(pool, sizeof(**fs->largest_changes->changes)); - - /* now initialize them and allocate the stringbufs */ - for (i = 0; i < count; ++i) - { - fs->largest_changes->changes[i]->size = 0; - fs->largest_changes->changes[i]->revision = SVN_INVALID_REVNUM; - fs->largest_changes->changes[i]->path - = svn_stringbuf_create_ensure(1024, pool); - } -} - -/* Add entry for SIZE to HISTOGRAM. - */ -static void -add_to_histogram(histogram_t *histogram, - apr_int64_t size) -{ - apr_int64_t shift = 0; - - while (((apr_int64_t)(1) << shift) <= size) - shift++; - - histogram->total.count++; - histogram->total.sum += size; - histogram->lines[(apr_size_t)shift].count++; - histogram->lines[(apr_size_t)shift].sum += size; -} - -/* Update data aggregators in FS with this representation of type KIND, on- - * disk REP_SIZE and expanded node size EXPANDED_SIZE for PATH in REVSION. - * PLAIN_ADDED indicates whether the node has a deltification predecessor. - */ -static void -add_change(fs_t *fs, - apr_int64_t rep_size, - apr_int64_t expanded_size, - svn_revnum_t revision, - const char *path, - rep_kind_t kind, - svn_boolean_t plain_added) -{ - /* identify largest reps */ - if (rep_size >= fs->largest_changes->min_size) - { - apr_size_t i; - large_change_info_t *info - = fs->largest_changes->changes[fs->largest_changes->count - 1]; - info->size = rep_size; - info->revision = revision; - svn_stringbuf_set(info->path, path); - - /* linear insertion but not too bad since count is low and insertions - * near the end are more likely than close to front */ - for (i = fs->largest_changes->count - 1; i > 0; --i) - if (fs->largest_changes->changes[i-1]->size >= rep_size) - break; - else - fs->largest_changes->changes[i] = fs->largest_changes->changes[i-1]; - - fs->largest_changes->changes[i] = info; - fs->largest_changes->min_size - = fs->largest_changes->changes[fs->largest_changes->count-1]->size; - } - - /* global histograms */ - add_to_histogram(&fs->rep_size_histogram, rep_size); - add_to_histogram(&fs->node_size_histogram, expanded_size); - - if (plain_added) - { - add_to_histogram(&fs->added_rep_size_histogram, rep_size); - add_to_histogram(&fs->added_node_size_histogram, expanded_size); - } - - /* specific histograms by type */ - switch (kind) - { - case unused_rep: add_to_histogram(&fs->unused_rep_histogram, - rep_size); - break; - case dir_property_rep: add_to_histogram(&fs->dir_prop_rep_histogram, - rep_size); - add_to_histogram(&fs->dir_prop_histogram, - expanded_size); - break; - case file_property_rep: add_to_histogram(&fs->file_prop_rep_histogram, - rep_size); - add_to_histogram(&fs->file_prop_histogram, - expanded_size); - break; - case dir_rep: add_to_histogram(&fs->dir_rep_histogram, - rep_size); - add_to_histogram(&fs->dir_histogram, - expanded_size); - break; - case file_rep: add_to_histogram(&fs->file_rep_histogram, - rep_size); - add_to_histogram(&fs->file_histogram, - expanded_size); - break; - } - - /* by extension */ - if (kind == file_rep) - { - /* determine extension */ - extension_info_t *info; - const char * file_name = strrchr(path, '/'); - const char * extension = file_name ? strrchr(file_name, '.') : NULL; - - if (extension == NULL || extension == file_name + 1) - extension = "(none)"; - - /* get / auto-insert entry for this extension */ - info = apr_hash_get(fs->by_extension, extension, APR_HASH_KEY_STRING); - if (info == NULL) - { - apr_pool_t *pool = apr_hash_pool_get(fs->by_extension); - info = apr_pcalloc(pool, sizeof(*info)); - info->extension = apr_pstrdup(pool, extension); - - apr_hash_set(fs->by_extension, info->extension, - APR_HASH_KEY_STRING, info); - } - - /* update per-extension histogram */ - add_to_histogram(&info->node_histogram, expanded_size); - add_to_histogram(&info->rep_histogram, rep_size); - } -} - -/* Read header information for the revision stored in FILE_CONTENT (one - * whole revision). Return the offsets within FILE_CONTENT for the - * *ROOT_NODEREV, the list of *CHANGES and its len in *CHANGES_LEN. - * Use POOL for temporary allocations. */ -static svn_error_t * -read_revision_header(apr_size_t *changes, - apr_size_t *changes_len, - apr_size_t *root_noderev, - svn_stringbuf_t *file_content, - apr_pool_t *pool) -{ - char buf[64]; - const char *line; - char *space; - apr_uint64_t val; - apr_size_t len; - - /* Read in this last block, from which we will identify the last line. */ - len = sizeof(buf); - if (len > file_content->len) - len = file_content->len; - - memcpy(buf, file_content->data + file_content->len - len, len); - - /* The last byte should be a newline. */ - if (buf[(apr_ssize_t)len - 1] != '\n') - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Revision lacks trailing newline")); - - /* Look for the next previous newline. */ - buf[len - 1] = 0; - line = strrchr(buf, '\n'); - if (line == NULL) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Final line in revision file longer " - "than 64 characters")); - - space = strchr(line, ' '); - if (space == NULL) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Final line in revision file missing space")); - - /* terminate the header line */ - *space = 0; - - /* extract information */ - SVN_ERR(svn_cstring_strtoui64(&val, line+1, 0, APR_SIZE_MAX, 10)); - *root_noderev = (apr_size_t)val; - SVN_ERR(svn_cstring_strtoui64(&val, space+1, 0, APR_SIZE_MAX, 10)); - *changes = (apr_size_t)val; - *changes_len = file_content->len - *changes - (buf + len - line) + 1; - - return SVN_NO_ERROR; -} - -/* Create *FS for the repository at PATH and read the format and size info. - * Use POOL for temporary allocations. - */ -static svn_error_t * -fs_open(fs_t **fs, const char *path, apr_pool_t *pool) -{ - fs_fs_data_t *ffd; - - *fs = apr_pcalloc(pool, sizeof(**fs)); - - /* Check repository type and open it. */ - SVN_ERR(open_fs(&(*fs)->fs, path, pool)); - - /* Check the FS format number. */ - ffd = (*fs)->fs->fsap_data; - if ((ffd->format != 4) && (ffd->format != 6) && (ffd->format != 7)) - return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, NULL); - - return SVN_NO_ERROR; -} - -/* Utility function that returns true if STRING->DATA matches KEY. - */ -static svn_boolean_t -key_matches(svn_string_t *string, const char *key) -{ - return strcmp(string->data, key) == 0; -} - -/* Comparator used for binary search comparing the absolute file offset - * of a representation to some other offset. DATA is a *rep_stats_t, - * KEY is a pointer to an apr_size_t. - */ -static int -compare_representation_offsets(const void *data, const void *key) -{ - apr_ssize_t diff = (*(const rep_stats_t *const *)data)->offset - - *(const apr_size_t *)key; - - /* sizeof(int) may be < sizeof(ssize_t) */ - if (diff < 0) - return -1; - return diff > 0 ? 1 : 0; -} - -/* Find the revision_info_t object to the given REVISION in FS and return - * it in *REVISION_INFO. For performance reasons, we skip the lookup if - * the info is already provided. - * - * In that revision, look for the rep_stats_t object for offset OFFSET. - * If it already exists, set *IDX to its index in *REVISION_INFO's - * representations list and return the representation object. Otherwise, - * set the index to where it must be inserted and return NULL. - */ -static rep_stats_t * -find_representation(int *idx, - fs_t *fs, - revision_info_t **revision_info, - svn_revnum_t revision, - apr_size_t offset) -{ - revision_info_t *info; - *idx = -1; - - /* first let's find the revision */ - info = revision_info ? *revision_info : NULL; - if (info == NULL || info->revision != revision) - { - info = APR_ARRAY_IDX(fs->revisions, revision, revision_info_t*); - if (revision_info) - *revision_info = info; - } - - /* not found -> no result */ - if (info == NULL) - return NULL; - - /* look for the representation */ - *idx = svn_sort__bsearch_lower_bound(info->representations, - &offset, - compare_representation_offsets); - if (*idx < info->representations->nelts) - { - /* return the representation, if this is the one we were looking for */ - rep_stats_t *result - = APR_ARRAY_IDX(info->representations, *idx, rep_stats_t *); - if (result->offset == offset) - return result; - } - - /* not parsed, yet */ - return NULL; -} - -/* Read the representation header in FILE_CONTENT at OFFSET. Return its - * size in *HEADER_SIZE, set *IS_PLAIN if no deltification was used and - * return the deltification base representation in *REPRESENTATION. If - * there is none, set it to NULL. Use FS to it look up. - * - * Use POOL for allocations and SCRATCH_POOL for temporaries. - */ -static svn_error_t * -read_rep_base(rep_stats_t **representation, - apr_size_t *header_size, - svn_boolean_t *is_plain, - fs_t *fs, - svn_stringbuf_t *file_content, - apr_size_t offset, - apr_pool_t *pool, - apr_pool_t *scratch_pool) -{ - char *str, *last_str; - int idx; - svn_revnum_t revision; - apr_uint64_t temp; - - /* identify representation header (1 line) */ - const char *buffer = file_content->data + offset; - const char *line_end = strchr(buffer, '\n'); - *header_size = line_end - buffer + 1; - - /* check for PLAIN rep */ - if (strncmp(buffer, "PLAIN\n", *header_size) == 0) - { - *is_plain = TRUE; - *representation = NULL; - return SVN_NO_ERROR; - } - - /* check for DELTA against empty rep */ - *is_plain = FALSE; - if (strncmp(buffer, "DELTA\n", *header_size) == 0) - { - /* This is a delta against the empty stream. */ - *representation = fs->null_base; - return SVN_NO_ERROR; - } - - str = apr_pstrndup(scratch_pool, buffer, line_end - buffer); - last_str = str; - - /* parse it. */ - str = svn_cstring_tokenize(" ", &last_str); - str = svn_cstring_tokenize(" ", &last_str); - SVN_ERR(svn_revnum_parse(&revision, str, NULL)); - - str = svn_cstring_tokenize(" ", &last_str); - SVN_ERR(svn_cstring_strtoui64(&temp, str, 0, APR_SIZE_MAX, 10)); - - /* it should refer to a rep in an earlier revision. Look it up */ - *representation = find_representation(&idx, fs, NULL, revision, (apr_size_t)temp); - return SVN_NO_ERROR; -} - -/* Parse the representation reference (text: or props:) in VALUE, look - * it up in FS and return it in *REPRESENTATION. To be able to parse the - * base rep, we pass the FILE_CONTENT as well. - * - * If necessary, allocate the result in POOL; use SCRATCH_POOL for temp. - * allocations. - */ -static svn_error_t * -parse_representation(rep_stats_t **representation, - fs_t *fs, - svn_stringbuf_t *file_content, - svn_string_t *value, - revision_info_t *revision_info, - apr_pool_t *pool, - apr_pool_t *scratch_pool) -{ - rep_stats_t *result; - svn_revnum_t revision; - - apr_uint64_t offset; - apr_uint64_t size; - apr_uint64_t expanded_size; - int idx; - - /* read location (revision, offset) and size */ - char *c = (char *)value->data; - SVN_ERR(svn_revnum_parse(&revision, svn_cstring_tokenize(" ", &c), NULL)); - SVN_ERR(svn_cstring_strtoui64(&offset, svn_cstring_tokenize(" ", &c), 0, APR_SIZE_MAX, 10)); - SVN_ERR(svn_cstring_strtoui64(&size, svn_cstring_tokenize(" ", &c), 0, APR_SIZE_MAX, 10)); - SVN_ERR(svn_cstring_strtoui64(&expanded_size, svn_cstring_tokenize(" ", &c), 0, APR_SIZE_MAX, 10)); - - /* look it up */ - result = find_representation(&idx, fs, &revision_info, revision, (apr_size_t)offset); - if (!result) - { - /* not parsed, yet (probably a rep in the same revision). - * Create a new rep object and determine its base rep as well. - */ - apr_size_t header_size; - svn_boolean_t is_plain; - - result = apr_pcalloc(pool, sizeof(*result)); - result->revision = revision; - result->expanded_size = (apr_size_t)(expanded_size ? expanded_size : size); - result->offset = (apr_size_t)offset; - result->size = (apr_size_t)size; - - if (!svn_fs_fs__use_log_addressing(fs->fs, revision)) - { - SVN_ERR(read_rep_base(&result->delta_base, &header_size, - &is_plain, fs, file_content, - (apr_size_t)offset, - pool, scratch_pool)); - - result->header_size = header_size; - result->is_plain = is_plain; - } - - svn_sort__array_insert(revision_info->representations, &result, idx); - } - - *representation = result; - - return SVN_NO_ERROR; -} - -/* Get the unprocessed (i.e. still deltified) content of REPRESENTATION in - * FS and return it in *CONTENT. If no NULL, FILE_CONTENT must contain - * the contents of the revision that also contains the representation. - * Use POOL for allocations. - */ -static svn_error_t * -get_rep_content(svn_stringbuf_t **content, - fs_t *fs, - rep_stats_t *representation, - svn_stringbuf_t *file_content, - apr_pool_t *pool) -{ - apr_off_t offset; - svn_revnum_t revision = representation->revision; - revision_info_t *revision_info = APR_ARRAY_IDX(fs->revisions, - revision, - revision_info_t*); - - /* not in cache. Is the revision valid at all? */ - if (revision > fs->revisions->nelts) - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Unknown revision %ld"), revision); - - if (file_content) - { - offset = representation->offset - + representation->header_size; - *content = svn_stringbuf_ncreate(file_content->data + offset, - representation->size, pool); - } - else - { - offset = revision_info->offset - + representation->offset - + representation->header_size; - SVN_ERR(get_content(content, NULL, fs, revision, offset, - representation->size, pool)); - } - - return SVN_NO_ERROR; -} - - -/* Read the delta window contents of all windows in REPRESENTATION in FS. - * If no NULL, FILE_CONTENT must contain the contents of the revision that - * also contains the representation. - * Return the data as svn_txdelta_window_t* instances in *WINDOWS. - * Use POOL for allocations. - */ -static svn_error_t * -read_windows(apr_array_header_t **windows, - fs_t *fs, - rep_stats_t *representation, - svn_stringbuf_t *file_content, - apr_pool_t *pool) -{ - svn_stringbuf_t *content; - svn_stream_t *stream; - char version; - apr_size_t len = sizeof(version); - - *windows = apr_array_make(pool, 0, sizeof(svn_txdelta_window_t *)); - - /* get the whole revision content */ - SVN_ERR(get_rep_content(&content, fs, representation, file_content, pool)); - - /* create a read stream and position it directly after the rep header */ - content->data += 3; - content->len -= 3; - stream = svn_stream_from_stringbuf(content, pool); - SVN_ERR(svn_stream_read_full(stream, &version, &len)); - - /* read the windows from that stream */ - while (TRUE) - { - svn_txdelta_window_t *window; - svn_stream_mark_t *mark; - char dummy; - - len = sizeof(dummy); - SVN_ERR(svn_stream_mark(stream, &mark, pool)); - SVN_ERR(svn_stream_read_full(stream, &dummy, &len)); - if (len == 0) - break; - - SVN_ERR(svn_stream_seek(stream, mark)); - SVN_ERR(svn_txdelta_read_svndiff_window(&window, stream, version, pool)); - APR_ARRAY_PUSH(*windows, svn_txdelta_window_t *) = window; - } - - return SVN_NO_ERROR; -} - -/* Get the undeltified representation that is a result of combining all - * deltas from the current desired REPRESENTATION in FS with its base - * representation. If no NULL, FILE_CONTENT must contain the contents of - * the revision that also contains the representation. Store the result - * in *CONTENT. Use POOL for allocations. - */ -static svn_error_t * -get_combined_window(svn_stringbuf_t **content, - fs_t *fs, - rep_stats_t *representation, - svn_stringbuf_t *file_content, - apr_pool_t *pool) -{ - int i; - apr_array_header_t *windows; - svn_stringbuf_t *base_content, *result; - const char *source; - apr_pool_t *subpool; - apr_pool_t *iterpool; - - /* special case: no un-deltification necessary */ - if (representation->is_plain) - { - SVN_ERR(get_rep_content(content, fs, representation, file_content, - pool)); - SVN_ERR(set_cached_window(fs, representation, *content, pool)); - return SVN_NO_ERROR; - } - - /* special case: data already in cache */ - SVN_ERR(get_cached_window(content, fs, representation, pool)); - if (*content) - return SVN_NO_ERROR; - - /* read the delta windows for this representation */ - subpool = svn_pool_create(pool); - iterpool = svn_pool_create(pool); - SVN_ERR(read_windows(&windows, fs, representation, file_content, subpool)); - - /* fetch the / create a base content */ - if (representation->delta_base && representation->delta_base->revision) - SVN_ERR(get_combined_window(&base_content, fs, - representation->delta_base, NULL, subpool)); - else - base_content = svn_stringbuf_create_empty(subpool); - - /* apply deltas */ - result = svn_stringbuf_create_empty(pool); - source = base_content->data; - - for (i = 0; i < windows->nelts; ++i) - { - svn_txdelta_window_t *window - = APR_ARRAY_IDX(windows, i, svn_txdelta_window_t *); - svn_stringbuf_t *buf - = svn_stringbuf_create_ensure(window->tview_len, iterpool); - - buf->len = window->tview_len; - svn_txdelta_apply_instructions(window, window->src_ops ? source : NULL, - buf->data, &buf->len); - - svn_stringbuf_appendbytes(result, buf->data, buf->len); - source += window->sview_len; - - svn_pool_clear(iterpool); - } - - /* cache result and return it */ - SVN_ERR(set_cached_window(fs, representation, result, subpool)); - *content = result; - - svn_pool_destroy(iterpool); - svn_pool_destroy(subpool); - - return SVN_NO_ERROR; -} - -/* forward declaration */ -static svn_error_t * -read_noderev(fs_t *fs, - svn_stringbuf_t *file_content, - apr_size_t offset, - revision_info_t *revision_info, - apr_pool_t *pool, - apr_pool_t *scratch_pool); - -/* Starting at the directory in REPRESENTATION in FILE_CONTENT, read all - * DAG nodes, directories and representations linked in that tree structure. - * Store them in FS and REVISION_INFO. Also, read them only once. - * - * Use POOL for persistent allocations and SCRATCH_POOL for temporaries. - */ -static svn_error_t * -parse_dir(fs_t *fs, - svn_stringbuf_t *file_content, - rep_stats_t *representation, - revision_info_t *revision_info, - apr_pool_t *pool, - apr_pool_t *scratch_pool) -{ - svn_stringbuf_t *text; - apr_pool_t *iterpool; - apr_pool_t *text_pool; - const char *current; - const char *revision_key; - apr_size_t key_len; - - /* special case: empty dir rep */ - if (representation == NULL) - return SVN_NO_ERROR; - - /* get the directory as unparsed string */ - iterpool = svn_pool_create(scratch_pool); - text_pool = svn_pool_create(scratch_pool); - - SVN_ERR(get_combined_window(&text, fs, representation, file_content, - text_pool)); - current = text->data; - - /* calculate some invariants */ - revision_key = apr_psprintf(text_pool, "r%ld/", representation->revision); - key_len = strlen(revision_key); - - /* Parse and process all directory entries. */ - while (*current != 'E') - { - char *next; - - /* skip "K ???\n<name>\nV ???\n" lines*/ - current = strchr(current, '\n'); - if (current) - current = strchr(current+1, '\n'); - if (current) - current = strchr(current+1, '\n'); - next = current ? strchr(++current, '\n') : NULL; - if (next == NULL) - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Corrupt directory representation in r%ld at offset %ld"), - representation->revision, - (long)representation->offset); - - /* iff this entry refers to a node in the same revision as this dir, - * recurse into that node */ - *next = 0; - current = strstr(current, revision_key); - if (current) - { - /* recurse */ - apr_uint64_t offset; - - SVN_ERR(svn_cstring_strtoui64(&offset, current + key_len, 0, - APR_SIZE_MAX, 10)); - SVN_ERR(read_noderev(fs, file_content, (apr_size_t)offset, - revision_info, pool, iterpool)); - - svn_pool_clear(iterpool); - } - current = next+1; - } - - svn_pool_destroy(iterpool); - svn_pool_destroy(text_pool); - return SVN_NO_ERROR; -} - -/* Starting at the noderev at OFFSET in FILE_CONTENT, read all DAG nodes, - * directories and representations linked in that tree structure. Store - * them in FS and REVISION_INFO. Also, read them only once. Return the - * result in *NODEREV. - * - * Use POOL for persistent allocations and SCRATCH_POOL for temporaries. - */ -static svn_error_t * -read_noderev(fs_t *fs, - svn_stringbuf_t *file_content, - apr_size_t offset, - revision_info_t *revision_info, - apr_pool_t *pool, - apr_pool_t *scratch_pool) -{ - svn_string_t *line; - rep_stats_t *text = NULL; - rep_stats_t *props = NULL; - apr_size_t start_offset = offset; - svn_boolean_t is_dir = FALSE; - svn_boolean_t has_predecessor = FALSE; - const char *path = "???"; - - scratch_pool = svn_pool_create(scratch_pool); - - /* parse the noderev line-by-line until we find an empty line */ - while (1) - { - /* for this line, extract key and value. Ignore invalid values */ - svn_string_t key; - svn_string_t value; - char *sep; - const char *start = file_content->data + offset; - const char *end = strchr(start, '\n'); - - line = svn_string_ncreate(start, end - start, scratch_pool); - offset += end - start + 1; - - /* empty line -> end of noderev data */ - if (line->len == 0) - break; - - sep = strchr(line->data, ':'); - if (sep == NULL) - continue; - - key.data = line->data; - key.len = sep - key.data; - *sep = 0; - - if (key.len + 2 > line->len) - continue; - - value.data = sep + 2; - value.len = line->len - (key.len + 2); - - /* translate (key, value) into noderev elements */ - if (key_matches(&key, "type")) - is_dir = strcmp(value.data, "dir") == 0; - else if (key_matches(&key, "text")) - { - SVN_ERR(parse_representation(&text, fs, file_content, - &value, revision_info, - pool, scratch_pool)); - - /* if we are the first to use this rep, mark it as "text rep" */ - if (++text->ref_count == 1) - text->kind = is_dir ? dir_rep : file_rep; - } - else if (key_matches(&key, "props")) - { - SVN_ERR(parse_representation(&props, fs, file_content, - &value, revision_info, - pool, scratch_pool)); - - /* if we are the first to use this rep, mark it as "prop rep" */ - if (++props->ref_count == 1) - props->kind = is_dir ? dir_property_rep : file_property_rep; - } - else if (key_matches(&key, "cpath")) - path = value.data; - else if (key_matches(&key, "pred")) - has_predecessor = TRUE; - } - - /* record largest changes */ - if (text && text->ref_count == 1) - add_change(fs, (apr_int64_t)text->size, (apr_int64_t)text->expanded_size, - text->revision, path, text->kind, !has_predecessor); - if (props && props->ref_count == 1) - add_change(fs, (apr_int64_t)props->size, (apr_int64_t)props->expanded_size, - props->revision, path, props->kind, !has_predecessor); - - /* if this is a directory and has not been processed, yet, read and - * process it recursively */ - if ( is_dir && text && text->ref_count == 1 - && !svn_fs_fs__use_log_addressing(fs->fs, revision_info->revision)) - SVN_ERR(parse_dir(fs, file_content, text, revision_info, - pool, scratch_pool)); - - /* update stats */ - if (is_dir) - { - revision_info->dir_noderev_size += offset - start_offset; - revision_info->dir_noderev_count++; - } - else - { - revision_info->file_noderev_size += offset - start_offset; - revision_info->file_noderev_count++; - } - svn_pool_destroy(scratch_pool); - - return SVN_NO_ERROR; -} - -/* Given the unparsed changes list in CHANGES with LEN chars, return the - * number of changed paths encoded in it. - */ -static apr_size_t -get_change_count(const char *changes, - apr_size_t len) -{ - apr_size_t lines = 0; - const char *end = changes + len; - - /* line count */ - for (; changes < end; ++changes) - if (*changes == '\n') - ++lines; - - /* two lines per change */ - return lines / 2; -} - -/* Simple utility to print a REVISION number and make it appear immediately. - */ -static void -print_progress(svn_revnum_t revision) -{ - printf("%8ld", revision); - fflush(stdout); -} - -/* Read the content of the pack file staring at revision BASE physical - * addressing mode and store it in FS. Use POOL for allocations. - */ -static svn_error_t * -read_phys_pack_file(fs_t *fs, - svn_revnum_t base, - apr_pool_t *pool) -{ - apr_pool_t *local_pool = svn_pool_create(pool); - apr_pool_t *iter_pool = svn_pool_create(local_pool); - int i; - apr_off_t file_size = 0; - apr_file_t *file; - fs_fs_data_t *ffd = fs->fs->fsap_data; - - SVN_ERR(open_rev_or_pack_file(&file, fs, base, local_pool)); - SVN_ERR(get_file_size(&file_size, file, local_pool)); - - /* process each revision in the pack file */ - for (i = 0; i < ffd->max_files_per_dir; ++i) - { - apr_size_t root_node_offset; - svn_stringbuf_t *rev_content; - - /* create the revision info for the current rev */ - revision_info_t *info = apr_pcalloc(pool, sizeof(*info)); - info->representations = apr_array_make(iter_pool, 4, sizeof(rep_stats_t*)); - - info->revision = base + i; - SVN_ERR(svn_fs_fs__get_packed_offset(&info->offset, fs->fs, base + i, - iter_pool)); - if (i + 1 == ffd->max_files_per_dir) - SVN_ERR(svn_io_file_seek(file, APR_END, &info->end, iter_pool)); - else - SVN_ERR(svn_fs_fs__get_packed_offset(&info->end, fs->fs, - base + i + 1, iter_pool)); - - SVN_ERR(get_content(&rev_content, file, fs, info->revision, - info->offset, - info->end - info->offset, - iter_pool)); - - SVN_ERR(read_revision_header(&info->changes, - &info->changes_len, - &root_node_offset, - rev_content, - iter_pool)); - - info->change_count - = get_change_count(rev_content->data + info->changes, - info->changes_len); - SVN_ERR(read_noderev(fs, rev_content, - root_node_offset, info, pool, iter_pool)); - - info->representations = apr_array_copy(pool, info->representations); - APR_ARRAY_PUSH(fs->revisions, revision_info_t*) = info; - - /* destroy temps */ - svn_pool_clear(iter_pool); - } - - /* one more pack file processed */ - print_progress(base); - svn_pool_destroy(local_pool); - - return SVN_NO_ERROR; -} - -/* Read the content of the file for REVISION in physical addressing mode - * and store its contents in FS. Use POOL for allocations. - */ -static svn_error_t * -read_phys_revision_file(fs_t *fs, - svn_revnum_t revision, - apr_pool_t *pool) -{ - apr_size_t root_node_offset; - apr_pool_t *local_pool = svn_pool_create(pool); - svn_stringbuf_t *rev_content; - revision_info_t *info = apr_pcalloc(pool, sizeof(*info)); - apr_off_t file_size = 0; - apr_file_t *file; - fs_fs_data_t *ffd = fs->fs->fsap_data; - - /* read the whole pack file into memory */ - SVN_ERR(open_rev_or_pack_file(&file, fs, revision, local_pool)); - SVN_ERR(get_file_size(&file_size, file, local_pool)); - - /* create the revision info for the current rev */ - info->representations = apr_array_make(pool, 4, sizeof(rep_stats_t*)); - - info->revision = revision; - info->offset = 0; - info->end = file_size; - - SVN_ERR(get_content(&rev_content, file, fs, revision, 0, file_size, - local_pool)); - - SVN_ERR(read_revision_header(&info->changes, - &info->changes_len, - &root_node_offset, - rev_content, - local_pool)); - - /* put it into our containers */ - APR_ARRAY_PUSH(fs->revisions, revision_info_t*) = info; - - info->change_count - = get_change_count(rev_content->data + info->changes, - info->changes_len); - - /* parse the revision content recursively. */ - SVN_ERR(read_noderev(fs, rev_content, - root_node_offset, info, - pool, local_pool)); - - /* show progress every 1000 revs or so */ - if (revision % ffd->max_files_per_dir == 0) - print_progress(revision); - - svn_pool_destroy(local_pool); - - return SVN_NO_ERROR; -} - -/* Read the item described by ENTRY from the REV_FILE in FS and return - * the respective byte sequence in *CONTENTS allocated in POOL. - */ -static svn_error_t * -read_item(svn_stringbuf_t **contents, - fs_t *fs, - svn_fs_fs__revision_file_t *rev_file, - svn_fs_fs__p2l_entry_t *entry, - apr_pool_t *pool) -{ - fs_fs_data_t *ffd = fs->fs->fsap_data; - - svn_stringbuf_t *item = svn_stringbuf_create_ensure(entry->size, pool); - item->len = entry->size; - item->data[item->len] = 0; - - SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size, NULL, - entry->offset, pool)); - SVN_ERR(svn_io_file_read_full2(rev_file->file, item->data, item->len, - NULL, NULL, pool)); - - *contents = item; - - return SVN_NO_ERROR; -} - -/* Process the logically addressed revision contents of revisions BASE to - * BASE + COUNT - 1 in FS. Use POOL for allocations. - */ -static svn_error_t * -read_log_rev_or_packfile(fs_t *fs, - svn_revnum_t base, - int count, - apr_pool_t *pool) -{ - apr_pool_t *iterpool = svn_pool_create(pool); - apr_pool_t *localpool = svn_pool_create(pool); - apr_off_t max_offset; - apr_off_t offset = 0; - int i; - svn_fs_fs__revision_file_t *rev_file; - fs_fs_data_t *ffd = fs->fs->fsap_data; - - /* we will process every revision in the rev / pack file */ - for (i = 0; i < count; ++i) - { - /* create the revision info for the current rev */ - revision_info_t *info = apr_pcalloc(pool, sizeof(*info)); - info->representations = apr_array_make(pool, 4, sizeof(rep_stats_t*)); - info->revision = base + i; - - APR_ARRAY_PUSH(fs->revisions, revision_info_t*) = info; - } - - /* open the pack / rev file that is covered by the p2l index */ - SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs->fs, base, - localpool, iterpool)); - SVN_ERR(svn_fs_fs__p2l_get_max_offset(&max_offset, fs->fs, rev_file, - base, localpool)); - - /* record the whole pack size in the first rev so the total sum will - still be correct */ - APR_ARRAY_IDX(fs->revisions, base, revision_info_t*)->end = max_offset; - - /* for all offsets in the file, get the P2L index entries and process - the interesting items (change lists, noderevs) */ - for (offset = 0; offset < max_offset; ) - { - apr_array_header_t *entries; - - svn_pool_clear(iterpool); - - /* get all entries for the current block */ - SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs->fs, rev_file, base, - offset, ffd->p2l_page_size, - iterpool)); - - /* process all entries (and later continue with the next block) */ - for (i = 0; i < entries->nelts; ++i) - { - svn_fs_fs__p2l_entry_t *entry - = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t); - - /* skip bits we previously processed */ - if (i == 0 && entry->offset < offset) - continue; - - /* skip zero-sized entries */ - if (entry->size == 0) - continue; - - /* read and process interesting items */ - if (entry->type == SVN_FS_FS__ITEM_TYPE_NODEREV) - { - svn_stringbuf_t *item; - revision_info_t *info = APR_ARRAY_IDX(fs->revisions, - entry->item.revision, - revision_info_t*); - SVN_ERR(read_item(&item, fs, rev_file, entry, iterpool)); - SVN_ERR(read_noderev(fs, item, 0, info, pool, iterpool)); - } - else if (entry->type == SVN_FS_FS__ITEM_TYPE_CHANGES) - { - svn_stringbuf_t *item; - revision_info_t *info = APR_ARRAY_IDX(fs->revisions, - entry->item.revision, - revision_info_t*); - SVN_ERR(read_item(&item, fs, rev_file, entry, iterpool)); - info->change_count - = get_change_count(item->data + 0, item->len); - info->changes_len += entry->size; - } - - /* advance offset */ - offset += entry->size; - } - } - - /* clean up and close file handles */ - svn_pool_destroy(iterpool); - svn_pool_destroy(localpool); - - return SVN_NO_ERROR; -} - -/* Read the content of the pack file staring at revision BASE logical - * addressing mode and store it in FS. Use POOL for allocations. - */ -static svn_error_t * -read_log_pack_file(fs_t *fs, - svn_revnum_t base, - apr_pool_t *pool) -{ - fs_fs_data_t *ffd = fs->fs->fsap_data; - SVN_ERR(read_log_rev_or_packfile(fs, base, ffd->max_files_per_dir, pool)); - - /* one more pack file processed */ - print_progress(base); - - return SVN_NO_ERROR; -} - -/* Read the content of the file for REVISION in logical addressing mode - * and store its contents in FS. Use POOL for allocations. - */ -static svn_error_t * -read_log_revision_file(fs_t *fs, - svn_revnum_t revision, - apr_pool_t *pool) -{ - fs_fs_data_t *ffd = fs->fs->fsap_data; - SVN_ERR(read_log_rev_or_packfile(fs, revision, 1, pool)); - - /* show progress every 1000 revs or so */ - if (revision % ffd->max_files_per_dir == 0) - print_progress(revision); - - return SVN_NO_ERROR; -} - -/* Read the repository at PATH and return the result in *FS. - * Use POOL for allocations. - */ -static svn_error_t * -read_revisions(fs_t **fs, - const char *path, - apr_pool_t *pool) -{ - svn_revnum_t revision; - fs_fs_data_t *ffd; - - /* determine cache sizes */ - SVN_ERR(fs_open(fs, path, pool)); - ffd = (*fs)->fs->fsap_data; - - /* create data containers and caches - * Note: this assumes that int is at least 32-bits and that we only support - * 32-bit wide revision numbers (actually 31-bits due to the signedness - * of both the nelts field of the array and our revision numbers). This - * means this code will fail on platforms where int is less than 32-bits - * and the repository has more revisions than int can hold. */ - (*fs)->revisions = apr_array_make(pool, - (int) ffd->youngest_rev_cache + 1, - sizeof(revision_info_t *)); - (*fs)->null_base = apr_pcalloc(pool, sizeof(*(*fs)->null_base)); - initialize_largest_changes(*fs, 64, pool); - (*fs)->by_extension = apr_hash_make(pool); - - SVN_ERR(svn_cache__create_membuffer_cache(&(*fs)->window_cache, - svn_cache__get_global_membuffer_cache(), - NULL, NULL, - sizeof(cache_key_t), - "", - SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY, - FALSE, pool, pool)); - - /* read all packed revs */ - for ( revision = 0 - ; revision < ffd->min_unpacked_rev - ; revision += ffd->max_files_per_dir) - if (svn_fs_fs__use_log_addressing((*fs)->fs, revision)) - SVN_ERR(read_log_pack_file(*fs, revision, pool)); - else - SVN_ERR(read_phys_pack_file(*fs, revision, pool)); - - /* read non-packed revs */ - for ( ; revision <= ffd->youngest_rev_cache; ++revision) - if (svn_fs_fs__use_log_addressing((*fs)->fs, revision)) - SVN_ERR(read_log_revision_file(*fs, revision, pool)); - else - SVN_ERR(read_phys_revision_file(*fs, revision, pool)); - - return SVN_NO_ERROR; -} - -/* Compression statistics we collect over a given set of representations. - */ -typedef struct rep_pack_stats_t -{ - /* number of representations */ - apr_int64_t count; - - /* total size after deltification (i.e. on disk size) */ - apr_int64_t packed_size; - - /* total size after de-deltification (i.e. plain text size) */ - apr_int64_t expanded_size; - - /* total on-disk header size */ - apr_int64_t overhead_size; -} rep_pack_stats_t; - -/* Statistics we collect over a given set of representations. - * We group them into shared and non-shared ("unique") reps. - */ -typedef struct representation_stats_t -{ - /* stats over all representations */ - rep_pack_stats_t total; - - /* stats over those representations with ref_count == 1 */ - rep_pack_stats_t uniques; - - /* stats over those representations with ref_count > 1 */ - rep_pack_stats_t shared; - - /* sum of all ref_counts */ - apr_int64_t references; - - /* sum of ref_count * expanded_size, - * i.e. total plaintext content if there was no rep sharing */ - apr_int64_t expanded_size; -} representation_stats_t; - -/* Basic statistics we collect over a given set of noderevs. - */ -typedef struct node_stats_t -{ - /* number of noderev structs */ - apr_int64_t count; - - /* their total size on disk (structs only) */ - apr_int64_t size; -} node_stats_t; - -/* Accumulate stats of REP in STATS. - */ -static void -add_rep_pack_stats(rep_pack_stats_t *stats, - rep_stats_t *rep) -{ - stats->count++; - - stats->packed_size += rep->size; - stats->expanded_size += rep->expanded_size; - stats->overhead_size += rep->header_size + 7 /* ENDREP\n */; -} - -/* Accumulate stats of REP in STATS. - */ -static void -add_rep_stats(representation_stats_t *stats, - rep_stats_t *rep) -{ - add_rep_pack_stats(&stats->total, rep); - if (rep->ref_count == 1) - add_rep_pack_stats(&stats->uniques, rep); - else - add_rep_pack_stats(&stats->shared, rep); - - stats->references += rep->ref_count; - stats->expanded_size += rep->ref_count * rep->expanded_size; + return apr_psprintf(result_pool, "%d%c", number, si_prefix); } /* Print statistics for the given group of representations to console. * Use POOL for allocations. */ static void -print_rep_stats(representation_stats_t *stats, +print_rep_stats(svn_fs_fs__representation_stats_t *stats, apr_pool_t *pool) { printf(_("%20s bytes in %12s reps\n" @@ -1651,26 +73,26 @@ print_rep_stats(representation_stats_t * "%20s bytes expanded shared size\n" "%20s bytes with rep-sharing off\n" "%20s shared references\n"), - svn__i64toa_sep(stats->total.packed_size, ',', pool), - svn__i64toa_sep(stats->total.count, ',', pool), - svn__i64toa_sep(stats->shared.packed_size, ',', pool), - svn__i64toa_sep(stats->shared.count, ',', pool), - svn__i64toa_sep(stats->total.expanded_size, ',', pool), - svn__i64toa_sep(stats->shared.expanded_size, ',', pool), - svn__i64toa_sep(stats->expanded_size, ',', pool), - svn__i64toa_sep(stats->references - stats->total.count, ',', pool)); + svn__ui64toa_sep(stats->total.packed_size, ',', pool), + svn__ui64toa_sep(stats->total.count, ',', pool), + svn__ui64toa_sep(stats->shared.packed_size, ',', pool), + svn__ui64toa_sep(stats->shared.count, ',', pool), + svn__ui64toa_sep(stats->total.expanded_size, ',', pool), + svn__ui64toa_sep(stats->shared.expanded_size, ',', pool), + svn__ui64toa_sep(stats->expanded_size, ',', pool), + svn__ui64toa_sep(stats->references - stats->total.count, ',', pool)); } /* Print the (used) contents of CHANGES. Use POOL for allocations. */ static void -print_largest_reps(largest_changes_t *changes, +print_largest_reps(svn_fs_fs__largest_changes_t *changes, apr_pool_t *pool) { apr_size_t i; for (i = 0; i < changes->count && changes->changes[i]->size; ++i) printf(_("%12s r%-8ld %s\n"), - svn__i64toa_sep(changes->changes[i]->size, ',', pool), + svn__ui64toa_sep(changes->changes[i]->size, ',', pool), changes->changes[i]->revision, changes->changes[i]->path->data); } @@ -1679,7 +101,7 @@ print_largest_reps(largest_changes_t *ch * Use POOL for allocations. */ static void -print_histogram(histogram_t *histogram, +print_histogram(svn_fs_fs__histogram_t *histogram, apr_pool_t *pool) { int first = 0; @@ -1695,11 +117,11 @@ print_histogram(histogram_t *histogram, /* display histogram lines */ for (i = last; i >= first; --i) - printf(_(" [2^%2d, 2^%2d) %15s (%2d%%) bytes in %12s (%2d%%) items\n"), - i-1, i, - svn__i64toa_sep(histogram->lines[i].sum, ',', pool), + printf(_(" %4s .. < %-4s %19s (%2d%%) bytes in %12s (%2d%%) items\n"), + print_two_power(i-1, pool), print_two_power(i, pool), + svn__ui64toa_sep(histogram->lines[i].sum, ',', pool), (int)(histogram->lines[i].sum * 100 / histogram->total.sum), - svn__i64toa_sep(histogram->lines[i].count, ',', pool), + svn__ui64toa_sep(histogram->lines[i].count, ',', pool), (int)(histogram->lines[i].count * 100 / histogram->total.count)); } @@ -1710,8 +132,8 @@ static int compare_count(const svn_sort__item_t *a, const svn_sort__item_t *b) { - const extension_info_t *lhs = a->value; - const extension_info_t *rhs = b->value; + const svn_fs_fs__extension_info_t *lhs = a->value; + const svn_fs_fs__extension_info_t *rhs = b->value; apr_int64_t diff = lhs->node_histogram.total.count - rhs->node_histogram.total.count; @@ -1725,8 +147,8 @@ static int compare_node_size(const svn_sort__item_t *a, const svn_sort__item_t *b) { - const extension_info_t *lhs = a->value; - const extension_info_t *rhs = b->value; + const svn_fs_fs__extension_info_t *lhs = a->value; + const svn_fs_fs__extension_info_t *rhs = b->value; apr_int64_t diff = lhs->node_histogram.total.sum - rhs->node_histogram.total.sum; @@ -1740,8 +162,8 @@ static int compare_rep_size(const svn_sort__item_t *a, const svn_sort__item_t *b) { - const extension_info_t *lhs = a->value; - const extension_info_t *rhs = b->value; + const svn_fs_fs__extension_info_t *lhs = a->value; + const svn_fs_fs__extension_info_t *rhs = b->value; apr_int64_t diff = lhs->rep_histogram.total.sum - rhs->rep_histogram.total.sum; @@ -1749,27 +171,27 @@ compare_rep_size(const svn_sort__item_t } /* Return an array of extension_info_t* for the (up to) 16 most prominent - * extensions in FS according to the sort criterion COMPARISON_FUNC. + * extensions in STATS according to the sort criterion COMPARISON_FUNC. * Allocate results in POOL. */ static apr_array_header_t * -get_by_extensions(fs_t *fs, +get_by_extensions(svn_fs_fs__stats_t *stats, int (*comparison_func)(const svn_sort__item_t *, const svn_sort__item_t *), apr_pool_t *pool) { /* sort all data by extension */ apr_array_header_t *sorted - = svn_sort__hash(fs->by_extension, comparison_func, pool); + = svn_sort__hash(stats->by_extension, comparison_func, pool); /* select the top (first) 16 entries */ int count = MIN(sorted->nelts, 16); apr_array_header_t *result - = apr_array_make(pool, count, sizeof(extension_info_t*)); + = apr_array_make(pool, count, sizeof(svn_fs_fs__extension_info_t*)); int i; for (i = 0; i < count; ++i) - APR_ARRAY_PUSH(result, extension_info_t*) + APR_ARRAY_PUSH(result, svn_fs_fs__extension_info_t*) = APR_ARRAY_IDX(sorted, i, svn_sort__item_t).value; return result; @@ -1787,118 +209,124 @@ merge_by_extension(apr_array_header_t *t count = target->nelts; for (i = 0; i < to_add->nelts; ++i) { - extension_info_t *info = APR_ARRAY_IDX(to_add, i, extension_info_t *); + svn_fs_fs__extension_info_t *info + = APR_ARRAY_IDX(to_add, i, svn_fs_fs__extension_info_t *); for (k = 0; k < count; ++k) - if (info == APR_ARRAY_IDX(target, k, extension_info_t *)) + if (info == APR_ARRAY_IDX(target, k, svn_fs_fs__extension_info_t *)) break; if (k == count) - APR_ARRAY_PUSH(target, extension_info_t*) = info; + APR_ARRAY_PUSH(target, svn_fs_fs__extension_info_t*) = info; } } -/* Print the (up to) 16 extensions in FS with the most changes. +/* Print the (up to) 16 extensions in STATS with the most changes. * Use POOL for allocations. */ static void -print_extensions_by_changes(fs_t *fs, +print_extensions_by_changes(svn_fs_fs__stats_t *stats, apr_pool_t *pool) { - apr_array_header_t *data = get_by_extensions(fs, compare_count, pool); + apr_array_header_t *data = get_by_extensions(stats, compare_count, pool); apr_int64_t sum = 0; int i; for (i = 0; i < data->nelts; ++i) { - extension_info_t *info = APR_ARRAY_IDX(data, i, extension_info_t *); + svn_fs_fs__extension_info_t *info + = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *); sum += info->node_histogram.total.count; printf(_("%11s %20s (%2d%%) representations\n"), info->extension, - svn__i64toa_sep(info->node_histogram.total.count, ',', pool), + svn__ui64toa_sep(info->node_histogram.total.count, ',', pool), (int)(info->node_histogram.total.count * 100 / - fs->file_histogram.total.count)); + stats->file_histogram.total.count)); } printf(_("%11s %20s (%2d%%) representations\n"), "(others)", - svn__i64toa_sep(fs->file_histogram.total.count - sum, ',', pool), - (int)((fs->file_histogram.total.count - sum) * 100 / - fs->file_histogram.total.count)); + svn__ui64toa_sep(stats->file_histogram.total.count - sum, ',', pool), + (int)((stats->file_histogram.total.count - sum) * 100 / + stats->file_histogram.total.count)); } -/* Print the (up to) 16 extensions in FS with the largest total size of +/* Print the (up to) 16 extensions in STATS with the largest total size of * changed file content. Use POOL for allocations. */ static void -print_extensions_by_nodes(fs_t *fs, +print_extensions_by_nodes(svn_fs_fs__stats_t *stats, apr_pool_t *pool) { - apr_array_header_t *data = get_by_extensions(fs, compare_node_size, pool); + apr_array_header_t *data = get_by_extensions(stats, compare_node_size, pool); apr_int64_t sum = 0; int i; for (i = 0; i < data->nelts; ++i) { - extension_info_t *info = APR_ARRAY_IDX(data, i, extension_info_t *); + svn_fs_fs__extension_info_t *info + = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *); sum += info->node_histogram.total.sum; printf(_("%11s %20s (%2d%%) bytes\n"), info->extension, - svn__i64toa_sep(info->node_histogram.total.sum, ',', pool), + svn__ui64toa_sep(info->node_histogram.total.sum, ',', pool), (int)(info->node_histogram.total.sum * 100 / - fs->file_histogram.total.sum)); + stats->file_histogram.total.sum)); } printf(_("%11s %20s (%2d%%) bytes\n"), "(others)", - svn__i64toa_sep(fs->file_histogram.total.sum - sum, ',', pool), - (int)((fs->file_histogram.total.sum - sum) * 100 / - fs->file_histogram.total.sum)); + svn__ui64toa_sep(stats->file_histogram.total.sum - sum, ',', pool), + (int)((stats->file_histogram.total.sum - sum) * 100 / + stats->file_histogram.total.sum)); } -/* Print the (up to) 16 extensions in FS with the largest total size of +/* Print the (up to) 16 extensions in STATS with the largest total size of * changed file content. Use POOL for allocations. */ static void -print_extensions_by_reps(fs_t *fs, +print_extensions_by_reps(svn_fs_fs__stats_t *stats, apr_pool_t *pool) { - apr_array_header_t *data = get_by_extensions(fs, compare_rep_size, pool); + apr_array_header_t *data = get_by_extensions(stats, compare_rep_size, pool); apr_int64_t sum = 0; int i; for (i = 0; i < data->nelts; ++i) { - extension_info_t *info = APR_ARRAY_IDX(data, i, extension_info_t *); + svn_fs_fs__extension_info_t *info + = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *); sum += info->rep_histogram.total.sum; printf(_("%11s %20s (%2d%%) bytes\n"), info->extension, - svn__i64toa_sep(info->rep_histogram.total.sum, ',', pool), + svn__ui64toa_sep(info->rep_histogram.total.sum, ',', pool), (int)(info->rep_histogram.total.sum * 100 / - fs->rep_size_histogram.total.sum)); + stats->rep_size_histogram.total.sum)); } printf(_("%11s %20s (%2d%%) bytes\n"), "(others)", - svn__i64toa_sep(fs->rep_size_histogram.total.sum - sum, ',', pool), - (int)((fs->rep_size_histogram.total.sum - sum) * 100 / - fs->rep_size_histogram.total.sum)); + svn__ui64toa_sep(stats->rep_size_histogram.total.sum - sum, ',', + pool), + (int)((stats->rep_size_histogram.total.sum - sum) * 100 / + stats->rep_size_histogram.total.sum)); } -/* Print per-extension histograms for the most frequent extensions in FS. +/* Print per-extension histograms for the most frequent extensions in STATS. * Use POOL for allocations. */ static void -print_histograms_by_extension(fs_t *fs, +print_histograms_by_extension(svn_fs_fs__stats_t *stats, apr_pool_t *pool) { - apr_array_header_t *data = get_by_extensions(fs, compare_count, pool); + apr_array_header_t *data = get_by_extensions(stats, compare_count, pool); int i; - merge_by_extension(data, get_by_extensions(fs, compare_node_size, pool)); - merge_by_extension(data, get_by_extensions(fs, compare_rep_size, pool)); + merge_by_extension(data, get_by_extensions(stats, compare_node_size, pool)); + merge_by_extension(data, get_by_extensions(stats, compare_rep_size, pool)); for (i = 0; i < data->nelts; ++i) { - extension_info_t *info = APR_ARRAY_IDX(data, i, extension_info_t *); + svn_fs_fs__extension_info_t *info + = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *); printf("\nHistogram of '%s' file sizes:\n", info->extension); print_histogram(&info->node_histogram, pool); printf("\nHistogram of '%s' file representation sizes:\n", @@ -1907,79 +335,13 @@ print_histograms_by_extension(fs_t *fs, } } -/* Post-process stats for FS and print them to the console. +/* Print the contents of STATS to the console. * Use POOL for allocations. */ static void -print_stats(fs_t *fs, +print_stats(svn_fs_fs__stats_t *stats, apr_pool_t *pool) { - int i, k; - - /* initialize stats to collect */ - representation_stats_t file_rep_stats = { { 0 } }; - representation_stats_t dir_rep_stats = { { 0 } }; - representation_stats_t file_prop_rep_stats = { { 0 } }; - representation_stats_t dir_prop_rep_stats = { { 0 } }; - representation_stats_t total_rep_stats = { { 0 } }; - - node_stats_t dir_node_stats = { 0 }; - node_stats_t file_node_stats = { 0 }; - node_stats_t total_node_stats = { 0 }; - - apr_int64_t total_size = 0; - apr_int64_t change_count = 0; - apr_int64_t change_len = 0; - - /* aggregate info from all revisions */ - for (i = 0; i < fs->revisions->nelts; ++i) - { - revision_info_t *revision = APR_ARRAY_IDX(fs->revisions, i, - revision_info_t *); - - /* data gathered on a revision level */ - change_count += revision->change_count; - change_len += revision->changes_len; - total_size += revision->end - revision->offset; - - dir_node_stats.count += revision->dir_noderev_count; - dir_node_stats.size += revision->dir_noderev_size; - file_node_stats.count += revision->file_noderev_count; - file_node_stats.size += revision->file_noderev_size; - total_node_stats.count += revision->dir_noderev_count - + revision->file_noderev_count; - total_node_stats.size += revision->dir_noderev_size - + revision->file_noderev_size; - - /* process representations */ - for (k = 0; k < revision->representations->nelts; ++k) - { - rep_stats_t *rep = APR_ARRAY_IDX(revision->representations, - k, rep_stats_t *); - - /* accumulate in the right bucket */ - switch(rep->kind) - { - case file_rep: - add_rep_stats(&file_rep_stats, rep); - break; - case dir_rep: - add_rep_stats(&dir_rep_stats, rep); - break; - case file_property_rep: - add_rep_stats(&file_prop_rep_stats, rep); - break; - case dir_property_rep: - add_rep_stats(&dir_prop_rep_stats, rep); - break; - default: - break; - } - - add_rep_stats(&total_rep_stats, rep); - } - } - /* print results */ printf("\nGlobal statistics:\n"); printf(_("%20s bytes in %12s revisions\n" @@ -1988,27 +350,29 @@ print_stats(fs_t *fs, "%20s bytes in %12s representations\n" "%20s bytes expanded representation size\n" "%20s bytes with rep-sharing off\n"), - svn__i64toa_sep(total_size, ',', pool), - svn__i64toa_sep(fs->revisions->nelts, ',', pool), - svn__i64toa_sep(change_len, ',', pool), - svn__i64toa_sep(change_count, ',', pool), - svn__i64toa_sep(total_node_stats.size, ',', pool), - svn__i64toa_sep(total_node_stats.count, ',', pool), - svn__i64toa_sep(total_rep_stats.total.packed_size, ',', pool), - svn__i64toa_sep(total_rep_stats.total.count, ',', pool), - svn__i64toa_sep(total_rep_stats.total.expanded_size, ',', pool), - svn__i64toa_sep(total_rep_stats.expanded_size, ',', pool)); + svn__ui64toa_sep(stats->total_size, ',', pool), + svn__ui64toa_sep(stats->revision_count, ',', pool), + svn__ui64toa_sep(stats->change_len, ',', pool), + svn__ui64toa_sep(stats->change_count, ',', pool), + svn__ui64toa_sep(stats->total_node_stats.size, ',', pool), + svn__ui64toa_sep(stats->total_node_stats.count, ',', pool), + svn__ui64toa_sep(stats->total_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->total_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->total_rep_stats.total.expanded_size, ',', + pool), + svn__ui64toa_sep(stats->total_rep_stats.expanded_size, ',', pool)); printf("\nNoderev statistics:\n"); printf(_("%20s bytes in %12s nodes total\n" "%20s bytes in %12s directory noderevs\n" "%20s bytes in %12s file noderevs\n"), - svn__i64toa_sep(total_node_stats.size, ',', pool), - svn__i64toa_sep(total_node_stats.count, ',', pool), - svn__i64toa_sep(dir_node_stats.size, ',', pool), - svn__i64toa_sep(dir_node_stats.count, ',', pool), - svn__i64toa_sep(file_node_stats.size, ',', pool), - svn__i64toa_sep(file_node_stats.count, ',', pool)); + svn__ui64toa_sep(stats->total_node_stats.size, ',', pool), + svn__ui64toa_sep(stats->total_node_stats.count, ',', pool), + svn__ui64toa_sep(stats->dir_node_stats.size, ',', pool), + svn__ui64toa_sep(stats->dir_node_stats.count, ',', pool), + svn__ui64toa_sep(stats->file_node_stats.size, ',', pool), + svn__ui64toa_sep(stats->file_node_stats.count, ',', pool)); printf("\nRepresentation statistics:\n"); printf(_("%20s bytes in %12s representations total\n" @@ -2018,60 +382,80 @@ print_stats(fs_t *fs, "%20s bytes in %12s directory property representations\n" "%20s bytes in %12s file property representations\n" "%20s bytes in header & footer overhead\n"), - svn__i64toa_sep(total_rep_stats.total.packed_size, ',', pool), - svn__i64toa_sep(total_rep_stats.total.count, ',', pool), - svn__i64toa_sep(dir_rep_stats.total.packed_size, ',', pool), - svn__i64toa_sep(dir_rep_stats.total.count, ',', pool), - svn__i64toa_sep(file_rep_stats.total.packed_size, ',', pool), - svn__i64toa_sep(file_rep_stats.total.count, ',', pool), - svn__i64toa_sep(fs->added_rep_size_histogram.total.sum, ',', pool), - svn__i64toa_sep(fs->added_rep_size_histogram.total.count, ',', pool), - svn__i64toa_sep(dir_prop_rep_stats.total.packed_size, ',', pool), - svn__i64toa_sep(dir_prop_rep_stats.total.count, ',', pool), - svn__i64toa_sep(file_prop_rep_stats.total.packed_size, ',', pool), - svn__i64toa_sep(file_prop_rep_stats.total.count, ',', pool), - svn__i64toa_sep(total_rep_stats.total.overhead_size, ',', pool)); + svn__ui64toa_sep(stats->total_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->total_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->dir_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->dir_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->file_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->file_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->added_rep_size_histogram.total.sum, ',', + pool), + svn__ui64toa_sep(stats->added_rep_size_histogram.total.count, ',', + pool), + svn__ui64toa_sep(stats->dir_prop_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->dir_prop_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->file_prop_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->file_prop_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->total_rep_stats.total.overhead_size, ',', + pool)); printf("\nDirectory representation statistics:\n"); - print_rep_stats(&dir_rep_stats, pool); + print_rep_stats(&stats->dir_rep_stats, pool); printf("\nFile representation statistics:\n"); - print_rep_stats(&file_rep_stats, pool); + print_rep_stats(&stats->file_rep_stats, pool); printf("\nDirectory property representation statistics:\n"); - print_rep_stats(&dir_prop_rep_stats, pool); + print_rep_stats(&stats->dir_prop_rep_stats, pool); printf("\nFile property representation statistics:\n"); - print_rep_stats(&file_prop_rep_stats, pool); + print_rep_stats(&stats->file_prop_rep_stats, pool); printf("\nLargest representations:\n"); - print_largest_reps(fs->largest_changes, pool); + print_largest_reps(stats->largest_changes, pool); printf("\nExtensions by number of representations:\n"); - print_extensions_by_changes(fs, pool); + print_extensions_by_changes(stats, pool); printf("\nExtensions by size of changed files:\n"); - print_extensions_by_nodes(fs, pool); + print_extensions_by_nodes(stats, pool); printf("\nExtensions by size of representations:\n"); - print_extensions_by_reps(fs, pool); + print_extensions_by_reps(stats, pool); printf("\nHistogram of expanded node sizes:\n"); - print_histogram(&fs->node_size_histogram, pool); + print_histogram(&stats->node_size_histogram, pool); printf("\nHistogram of representation sizes:\n"); - print_histogram(&fs->rep_size_histogram, pool); + print_histogram(&stats->rep_size_histogram, pool); printf("\nHistogram of file sizes:\n"); - print_histogram(&fs->file_histogram, pool); + print_histogram(&stats->file_histogram, pool); printf("\nHistogram of file representation sizes:\n"); - print_histogram(&fs->file_rep_histogram, pool); + print_histogram(&stats->file_rep_histogram, pool); printf("\nHistogram of file property sizes:\n"); - print_histogram(&fs->file_prop_histogram, pool); + print_histogram(&stats->file_prop_histogram, pool); printf("\nHistogram of file property representation sizes:\n"); - print_histogram(&fs->file_prop_rep_histogram, pool); + print_histogram(&stats->file_prop_rep_histogram, pool); printf("\nHistogram of directory sizes:\n"); - print_histogram(&fs->dir_histogram, pool); + print_histogram(&stats->dir_histogram, pool); printf("\nHistogram of directory representation sizes:\n"); - print_histogram(&fs->dir_rep_histogram, pool); + print_histogram(&stats->dir_rep_histogram, pool); printf("\nHistogram of directory property sizes:\n"); - print_histogram(&fs->dir_prop_histogram, pool); + print_histogram(&stats->dir_prop_histogram, pool); printf("\nHistogram of directory property representation sizes:\n"); - print_histogram(&fs->dir_prop_rep_histogram, pool); + print_histogram(&stats->dir_prop_rep_histogram, pool); - print_histograms_by_extension(fs, pool); + print_histograms_by_extension(stats, pool); +} + +/* Our progress function simply prints the REVISION number and makes it + * appear immediately. + */ +static void +print_progress(svn_revnum_t revision, + void *baton, + apr_pool_t *pool) +{ + printf("%8ld", revision); + fflush(stdout); } /* This implements `svn_opt_subcommand_t'. */ @@ -2079,12 +463,15 @@ svn_error_t * subcommand__stats(apr_getopt_t *os, void *baton, apr_pool_t *pool) { svnfsfs__opt_state *opt_state = baton; - fs_t *fs; + svn_fs_fs__stats_t *stats; + svn_fs_t *fs; printf("Reading revisions\n"); - SVN_ERR(read_revisions(&fs, opt_state->repository_path, pool)); + SVN_ERR(open_fs(&fs, opt_state->repository_path, pool)); + SVN_ERR(svn_fs_fs__get_stats(&stats, fs, print_progress, NULL, + check_cancel, NULL, pool, pool)); - print_stats(fs, pool); + print_stats(stats, pool); return SVN_NO_ERROR; }
Modified: subversion/branches/authzperf/subversion/svnfsfs/svnfsfs.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/svnfsfs/svnfsfs.c?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/svnfsfs/svnfsfs.c (original) +++ subversion/branches/authzperf/subversion/svnfsfs/svnfsfs.c Sat Jan 3 14:00:41 2015 @@ -72,6 +72,16 @@ setup_cancellation_signals(void (*handle } +svn_error_t * +check_cancel(void *baton) +{ + if (cancelled) + return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal")); + else + return SVN_NO_ERROR; +} + + /* Custom filesystem warning function. */ static void warning_func(void *baton, @@ -176,9 +186,9 @@ static const svn_opt_subcommand_desc2_t {"load-index", subcommand__load_index, {0}, N_ ("usage: svnfsfs load-index REPOS_PATH\n\n" - "Read the index contents to console. The format is the same as produced by the\n" - "dump command, except that checksum as well as header are optional and will be\n" - "ignored. The data must cover the full revision / pack file; the revision\n" + "Read index contents from console. The format is the same as produced by the\n" + "dump-index command, except that checksum as well as header are optional and will\n" + "be ignored. The data must cover the full revision / pack file; the revision\n" "number is automatically extracted from input stream. No ordering is required.\n"), {'M'} }, @@ -469,7 +479,7 @@ sub_main(int *exit_code, int argc, const apr_signal(SIGXFSZ, SIG_IGN); #endif - /* Configure FSFS caches for maximum efficiency with svnadmin. + /* Configure FSFS caches for maximum efficiency with svnfsfs. * Also, apply the respective command line parameters, if given. */ { svn_cache_config_t settings = *svn_cache_config_get(); @@ -490,7 +500,7 @@ sub_main(int *exit_code, int argc, const || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR) { err = svn_error_quick_wrap(err, - _("Try 'svnadmin help' for more info")); + _("Try 'svnfsfs help' for more info")); } return err; } @@ -506,7 +516,7 @@ main(int argc, const char *argv[]) svn_error_t *err; /* Initialize the app. */ - if (svn_cmdline_init("svnadmin", stderr) != EXIT_SUCCESS) + if (svn_cmdline_init("svnfsfs", stderr) != EXIT_SUCCESS) return EXIT_FAILURE; /* Create our top-level pool. Use a separate mutexless allocator, Modified: subversion/branches/authzperf/subversion/svnfsfs/svnfsfs.h URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/svnfsfs/svnfsfs.h?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/svnfsfs/svnfsfs.h (original) +++ subversion/branches/authzperf/subversion/svnfsfs/svnfsfs.h Sat Jan 3 14:00:41 2015 @@ -54,6 +54,7 @@ svn_opt_subcommand_t subcommand__load_index, subcommand__stats; + /* Check that the filesystem at PATH is an FSFS repository and then open it. * Return the filesystem in *FS, allocated in POOL. */ svn_error_t * @@ -61,6 +62,10 @@ open_fs(svn_fs_t **fs, const char *path, apr_pool_t *pool); +/* Our cancellation callback. */ +svn_error_t * +check_cancel(void *baton); + #ifdef __cplusplus } #endif /* __cplusplus */ Modified: subversion/branches/authzperf/subversion/svnlook/svnlook.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/svnlook/svnlook.c?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/svnlook/svnlook.c (original) +++ subversion/branches/authzperf/subversion/svnlook/svnlook.c Sat Jan 3 14:00:41 2015 @@ -1706,9 +1706,14 @@ do_pget(svnlook_ctxt_t *c, if (path == NULL) { /* We're operating on a revprop (e.g. c->is_revision). */ - err_msg = apr_psprintf(pool, - _("Property '%s' not found on revision %ld"), - propname, c->rev_id); + if (SVN_IS_VALID_REVNUM(c->rev_id)) + err_msg = apr_psprintf(pool, + _("Property '%s' not found on revision %ld"), + propname, c->rev_id); + else + err_msg = apr_psprintf(pool, + _("Property '%s' not found on transaction %s"), + propname, c->txn_name); } else { @@ -2013,10 +2018,11 @@ do_plist(svnlook_ctxt_t *c, /* "</properties>" */ svn_xml_make_close_tag(&sb, pool, "properties"); + errno = 0; if (fputs(sb->data, stdout) == EOF) { - if (errno) - return svn_error_wrap_apr(errno, _("Write error")); + if (apr_get_os_error()) /* is errno on POSIX */ + return svn_error_wrap_apr(apr_get_os_error(), _("Write error")); else return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL); }
