diff options
Diffstat (limited to 'tools/dev/fsfs-access-map.c')
-rw-r--r-- | tools/dev/fsfs-access-map.c | 180 |
1 files changed, 148 insertions, 32 deletions
diff --git a/tools/dev/fsfs-access-map.c b/tools/dev/fsfs-access-map.c index 5fbd221..ac65182 100644 --- a/tools/dev/fsfs-access-map.c +++ b/tools/dev/fsfs-access-map.c @@ -52,9 +52,15 @@ typedef struct file_stats_t /* number of lseek calls to clusters not previously read */ apr_int64_t uncached_seek_count; + /* number of lseek counts not followed by a read */ + apr_int64_t unnecessary_seeks; + /* number of read() calls */ apr_int64_t read_count; + /* number of read() calls that returned 0 bytes */ + apr_int64_t empty_reads; + /* total number of bytes returned by those reads */ apr_int64_t read_size; @@ -86,12 +92,17 @@ typedef struct handle_info_t /* bytes read so far in the current series of reads started (default: 0) */ apr_int64_t last_read_size; + /* number of read() calls in this series */ + apr_int64_t read_count; } handle_info_t; /* useful typedef */ typedef unsigned char byte; typedef unsigned short word; +/* an RGB color */ +typedef byte color_t[3]; + /* global const char * file name -> *file_info_t map */ static apr_hash_t *files = NULL; @@ -136,6 +147,11 @@ store_read_info(handle_info_t *handle_info) ++*count; } } + else if (handle_info->read_count == 0) + { + /* two consecutive seeks */ + handle_info->file->unnecessary_seeks++; + } } /* Handle a open() call. Ensures that a file_info_t for the given NAME @@ -152,18 +168,18 @@ open_file(const char *name, int handle) if (!file) { apr_pool_t *pool = apr_hash_pool_get(files); - apr_pool_t *sub_pool = svn_pool_create(pool); + apr_pool_t *subpool = svn_pool_create(pool); apr_file_t *apr_file = NULL; apr_finfo_t finfo = { 0 }; - apr_size_t cluster_count = 0; + int cluster_count = 0; /* determine file size (if file still exists) */ apr_file_open(&apr_file, name, - APR_READ | APR_BUFFERED, APR_OS_DEFAULT, sub_pool); + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, subpool); if (apr_file) apr_file_info_get(&finfo, APR_FINFO_SIZE, apr_file); - svn_pool_destroy(sub_pool); + svn_pool_destroy(subpool); file = apr_pcalloc(pool, sizeof(*file)); file->name = apr_pstrdup(pool, name); @@ -171,7 +187,7 @@ open_file(const char *name, int handle) /* pre-allocate cluster map accordingly * (will be auto-expanded later if necessary) */ - cluster_count = (apr_size_t)(1 + (file->size - 1) / cluster_size); + cluster_count = (int)(1 + (file->size - 1) / cluster_size); file->read_map = apr_array_make(pool, file->size ? cluster_count : 1, sizeof(word)); @@ -188,6 +204,14 @@ open_file(const char *name, int handle) else file->rev_num = -1; + /* filter out log/phys index files */ + if (file->rev_num >= 0) + { + const char *suffix = name + strlen(name) - 4; + if (strcmp(suffix, ".l2p") == 0 || strcmp(suffix, ".p2l") == 0) + file->rev_num = -1; + } + apr_hash_set(files, file->name, APR_HASH_KEY_STRING, file); } @@ -220,9 +244,13 @@ read_file(int handle, apr_int64_t count) { /* known file handle -> expand current read sequence */ + handle_info->read_count++; handle_info->last_read_size += count; handle_info->file->read_count++; handle_info->file->read_size += count; + + if (count == 0) + handle_info->file->empty_reads++; } } @@ -242,6 +270,7 @@ seek_file(int handle, apr_int64_t location) handle_info->last_read_size = 0; handle_info->last_read_start = location; + handle_info->read_count = 0; handle_info->file->seek_count++; /* if we seek to a location that had not been read from before, @@ -275,10 +304,17 @@ parse_line(svn_stringbuf_t *line) char *return_value = strrchr(line->data, ' '); char *first_param_end; apr_int64_t func_return = 0; + char *func_start = strchr(line->data, ' '); if (func_end == NULL || return_value == NULL) return; + if (func_start == NULL || func_start > func_end) + func_start = line->data; + else + while(*func_start == ' ') + func_start++; + first_param_end = strchr(func_end, ','); if (first_param_end == NULL) first_param_end = strchr(func_end, ')'); @@ -295,7 +331,7 @@ parse_line(svn_stringbuf_t *line) svn_error_clear(svn_cstring_atoi64(&func_return, return_value)); /* process those operations that we care about */ - if (strcmp(line->data, "open") == 0) + if (strcmp(func_start, "open") == 0) { /* remove double quotes from file name parameter */ *func_end++ = 0; @@ -303,11 +339,11 @@ parse_line(svn_stringbuf_t *line) open_file(func_end, (int)func_return); } - else if (strcmp(line->data, "read") == 0) + else if (strcmp(func_start, "read") == 0) read_file(atoi(func_end), func_return); - else if (strcmp(line->data, "lseek") == 0) + else if (strcmp(func_start, "lseek") == 0) seek_file(atoi(func_end), func_return); - else if (strcmp(line->data, "close") == 0) + else if (strcmp(func_start, "close") == 0) close_file(atoi(func_end)); } @@ -317,7 +353,7 @@ static void parse_file(apr_file_t *file) { apr_pool_t *pool = svn_pool_create(NULL); - apr_pool_t *iter_pool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(pool); /* limit lines to 4k (usually, we need less than 200 bytes) */ svn_stringbuf_t *line = svn_stringbuf_create_ensure(4096, pool); @@ -327,13 +363,13 @@ parse_file(apr_file_t *file) svn_error_t *err = NULL; line->len = line->blocksize-1; - err = svn_io_read_length_line(file, line->data, &line->len, iter_pool); + err = svn_io_read_length_line(file, line->data, &line->len, iterpool); svn_error_clear(err); if (err) break; parse_line(line); - svn_pool_clear(iter_pool); + svn_pool_clear(iterpool); } while (line->len > 0); } @@ -494,17 +530,82 @@ write_bitmap_header(apr_file_t *file, int xsize, int ysize) apr_file_write(file, header, &written); } -/* write the cluster read map for all files in INFO as BMP image to FILE. +/* To COLOR, add the fractional value of SOURCE from fractional indexes + * SOURCE_START to SOURCE_END and apply the SCALING_FACTOR. + */ +static void +add_sample(color_t color, + color_t *source, + double source_start, + double source_end, + double scaling_factor) +{ + double factor = (source_end - source_start) / scaling_factor; + + apr_size_t i; + for (i = 0; i < sizeof(color_t) / sizeof(*color); ++i) + color[i] += (source_end - source_start < 0.5) && source_start > 1.0 + ? factor * source[(apr_size_t)source_start - 1][i] + : factor * source[(apr_size_t)source_start][i]; +} + +/* Scale the IN_LEN RGB values from IN to OUT_LEN RGB values in OUT. + */ +static void +scale_line(color_t* out, + int out_len, + color_t *in, + int in_len) +{ + double scaling_factor = (double)(in_len) / (double)(out_len); + + apr_size_t i; + memset(out, 0, out_len * sizeof(color_t)); + for (i = 0; i < out_len; ++i) + { + color_t color = { 0 }; + + double source_start = i * scaling_factor; + double source_end = (i + 1) * scaling_factor; + + if ((apr_size_t)source_start == (apr_size_t)source_end) + { + add_sample(color, in, source_start, source_end, scaling_factor); + } + else + { + apr_size_t k; + apr_size_t first_sample_end = (apr_size_t)source_start + 1; + apr_size_t last_sample_start = (apr_size_t)source_end; + + add_sample(color, in, source_start, first_sample_end, scaling_factor); + for (k = first_sample_end; k < last_sample_start; ++k) + add_sample(color, in, k, k + 1, scaling_factor); + + add_sample(color, in, last_sample_start, source_end, scaling_factor); + } + + memcpy(out[i], color, sizeof(color)); + } +} + +/* Write the cluster read map for all files in INFO as BMP image to FILE. + * If MAX_X is not 0, scale all lines to MAX_X pixels. Use POOL for + * allocations. */ static void -write_bitmap(apr_array_header_t *info, apr_file_t *file) +write_bitmap(apr_array_header_t *info, + int max_x, + apr_file_t *file, + apr_pool_t *pool) { int ysize = info->nelts; int xsize = 0; int x, y; - int row_size; - int padding; + apr_size_t row_size; apr_size_t written; + color_t *line, *scaled_line; + svn_boolean_t do_scale = max_x > 0; /* xsize = max cluster number */ for (y = 0; y < ysize; ++y) @@ -516,37 +617,40 @@ write_bitmap(apr_array_header_t *info, apr_file_t *file) xsize = 0x3fff; if (ysize >= 0x4000) ysize = 0x3fff; + if (max_x == 0) + max_x = xsize; /* rows in BMP files must be aligned to 4 bytes */ - row_size = APR_ALIGN(xsize * 3, 4); - padding = row_size - xsize * 3; + row_size = APR_ALIGN(max_x * sizeof(color_t), 4); + + /**/ + line = apr_pcalloc(pool, xsize * sizeof(color_t)); + scaled_line = apr_pcalloc(pool, row_size); /* write header to file */ - write_bitmap_header(file, xsize, ysize); + write_bitmap_header(file, max_x, ysize); /* write all rows */ for (y = 0; y < ysize; ++y) { file_stats_t *file_info = APR_ARRAY_IDX(info, y, file_stats_t *); + int block_count = file_info->read_map->nelts; for (x = 0; x < xsize; ++x) { - byte color[3] = { 128, 128, 128 }; - if (x < file_info->read_map->nelts) + color_t color = { 128, 128, 128 }; + if (x < block_count) { word count = APR_ARRAY_IDX(file_info->read_map, x, word); select_color(color, count); } - written = sizeof(color); - apr_file_write(file, color, &written); + memcpy(line[x], color, sizeof(color)); } - if (padding) - { - char pad[3] = { 0 }; - written = padding; - apr_file_write(file, pad, &written); - } + scale_line(scaled_line, max_x, line, block_count ? block_count : 1); + + written = row_size; + apr_file_write(file, do_scale ? scaled_line : line, &written); } } @@ -592,6 +696,8 @@ print_stats(apr_pool_t *pool) apr_int64_t clusters_read = 0; apr_int64_t unique_clusters_read = 0; apr_int64_t uncached_seek_count = 0; + apr_int64_t unnecessary_seek_count = 0; + apr_int64_t empty_read_count = 0; apr_hash_index_t *hi; for (hi = apr_hash_first(pool, files); hi; hi = apr_hash_next(hi)) @@ -609,13 +715,17 @@ print_stats(apr_pool_t *pool) clusters_read += file->clusters_read; unique_clusters_read += file->unique_clusters_read; uncached_seek_count += file->uncached_seek_count; + unnecessary_seek_count += file->unnecessary_seeks; + empty_read_count += file->empty_reads; } printf("%20s files\n", svn__i64toa_sep(apr_hash_count(files), ',', pool)); printf("%20s files opened\n", svn__i64toa_sep(open_count, ',', pool)); printf("%20s seeks\n", svn__i64toa_sep(seek_count, ',', pool)); + printf("%20s unnecessary seeks\n", svn__i64toa_sep(unnecessary_seek_count, ',', pool)); printf("%20s uncached seeks\n", svn__i64toa_sep(uncached_seek_count, ',', pool)); printf("%20s reads\n", svn__i64toa_sep(read_count, ',', pool)); + printf("%20s empty reads\n", svn__i64toa_sep(empty_read_count, ',', pool)); printf("%20s unique clusters read\n", svn__i64toa_sep(unique_clusters_read, ',', pool)); printf("%20s clusters read\n", svn__i64toa_sep(clusters_read, ',', pool)); printf("%20s bytes read\n", svn__i64toa_sep(read_size, ',', pool)); @@ -629,7 +739,7 @@ print_usage(void) printf("Reads strace of some FSFS-based tool from <file>, prints some stats\n"); printf("and writes a cluster access map to 'access.bmp' the current folder.\n"); printf("Each pixel corresponds to one 64kB cluster and every line to a rev\n"); - printf("or packed rev file in the repository. Turquoise and greed indicate\n"); + printf("or packed rev file in the repository. Turquoise and green indicate\n"); printf("1 and 2 hits, yellow to read-ish colors for up to 20, shares of\n"); printf("for up to 100 and black for > 200 hits.\n\n"); printf("A typical strace invocation looks like this:\n"); @@ -665,7 +775,13 @@ int main(int argc, const char *argv[]) apr_file_open(&file, "access.bmp", APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BUFFERED, APR_OS_DEFAULT, pool); - write_bitmap(get_rev_files(pool), file); + write_bitmap(get_rev_files(pool), 0, file, pool); + apr_file_close(file); + + apr_file_open(&file, "access_scaled.bmp", + APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BUFFERED, + APR_OS_DEFAULT, pool); + write_bitmap(get_rev_files(pool), 1024, file, pool); apr_file_close(file); apr_file_open(&file, "scale.bmp", @@ -675,4 +791,4 @@ int main(int argc, const char *argv[]) apr_file_close(file); return 0; -}
\ No newline at end of file +} |