From bf0720afa8e5f5e2a2bb87abc0b015f35beadef3 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 26 Sep 2005 14:26:24 +0200 Subject: [PATCH] [PATCH] blkparse: Add option to hash process by name For some traces, you really don't want to see thousands of cc1 processes in the stats, you want to collect the stats by name instead. This adds the -n option to blkparse, enabling that. Switched to Jenkins hash at the same time, as it allows for easy hash-by-u32 or hash-by-name as we see fit. --- README | 1 + blkparse.c | 106 +++++++++++++++++++++++++++++---------- blktrace.h | 3 ++ jhash.h | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 227 insertions(+), 26 deletions(-) create mode 100644 jhash.h diff --git a/README b/README index 058306f..c96d20a 100644 --- a/README +++ b/README @@ -63,6 +63,7 @@ $ blkparse -i [ -o ] [ -b rb_batch ] [ -s ] [ -t ] [ -q ] -o Output file. If not given, output is stdout. -b stdin read batching. -s Show per-program io statistics. + -n Hash processes by name, not pid. -t Track individual ios. Will tell you the time a request took to get queued, to get dispatched, and to get completed. -q Quiet. Don't display any stats at the end of the trace. diff --git a/blkparse.c b/blkparse.c index b9f4908..de5c9a6 100644 --- a/blkparse.c +++ b/blkparse.c @@ -33,6 +33,7 @@ #include "blktrace.h" #include "rbtree.h" +#include "jhash.h" static char blkparse_version[] = "0.90"; @@ -68,11 +69,13 @@ struct per_process_info { }; #define PPI_HASH_SHIFT (8) -static struct per_process_info *ppi_hash[1 << PPI_HASH_SHIFT]; +#define PPI_HASH_SIZE (1 << PPI_HASH_SHIFT) +#define PPI_HASH_MASK (PPI_HASH_SIZE - 1) +static struct per_process_info *ppi_hash_table[PPI_HASH_SIZE]; static struct per_process_info *ppi_list; static int ppi_list_entries; -#define S_OPTS "i:o:b:stqw:f:F:v" +#define S_OPTS "i:o:b:stqw:f:F:vn" static struct option l_opts[] = { { .name = "input", @@ -128,6 +131,12 @@ static struct option l_opts[] = { .flag = NULL, .val = 'F' }, + { + .name = "hash by name", + .has_arg = no_argument, + .flag = NULL, + .val = 'n' + }, { .name = "version", .has_arg = no_argument, @@ -168,6 +177,7 @@ struct io_track { dev_t device; __u64 sector; __u32 pid; + char comm[16]; unsigned long long allocation_time; unsigned long long queue_time; unsigned long long dispatch_time; @@ -188,6 +198,7 @@ static unsigned long long stopwatch_end = ULONG_LONG_MAX; /* "infinity" */ static int per_process_stats; static int track_ios; +static int ppi_hash_by_pid = 1; #define RB_BATCH_DEFAULT (512) static int rb_batch = RB_BATCH_DEFAULT; @@ -197,25 +208,35 @@ static int pipeline; #define is_done() (*(volatile int *)(&done)) static volatile int done; -static inline unsigned long hash_long(unsigned long val) +#define JHASH_RANDOM (0x3af5f2ee) + +static inline int ppi_hash_pid(__u32 pid) +{ + return jhash_1word(pid, JHASH_RANDOM) & PPI_HASH_MASK; +} + +static inline int ppi_hash_name(const char *name) { -#if __WORDSIZE == 32 - val *= 0x9e370001UL; -#elif __WORDSIZE == 64 - val *= 0x9e37fffffffc0001UL; -#else -#error unknown word size -#endif + return jhash(name, 16, JHASH_RANDOM) & PPI_HASH_MASK; +} + +static inline int ppi_hash(struct per_process_info *ppi) +{ + if (ppi_hash_by_pid) + return ppi_hash_pid(ppi->pid); + + if (ppi->name[0] == 0) + fprintf(stderr, "bad\n"); - return val >> (__WORDSIZE - PPI_HASH_SHIFT); + return ppi_hash_name(ppi->name); } static inline void add_process_to_hash(struct per_process_info *ppi) { - const int hash_idx = hash_long(ppi->pid); + const int hash_idx = ppi_hash(ppi); - ppi->hash_next = ppi_hash[hash_idx]; - ppi_hash[hash_idx] = ppi; + ppi->hash_next = ppi_hash_table[hash_idx]; + ppi_hash_table[hash_idx] = ppi; } static inline void add_process_to_list(struct per_process_info *ppi) @@ -225,12 +246,28 @@ static inline void add_process_to_list(struct per_process_info *ppi) ppi_list_entries++; } +static struct per_process_info *find_process_by_name(char *name) +{ + const int hash_idx = ppi_hash_name(name); + struct per_process_info *ppi; + + ppi = ppi_hash_table[hash_idx]; + while (ppi) { + if (!strcmp(ppi->name, name)) + return ppi; + + ppi = ppi->hash_next; + } + + return NULL; +} + static struct per_process_info *find_process_by_pid(__u32 pid) { - const int hash_idx = hash_long(pid); + const int hash_idx = ppi_hash_pid(pid); struct per_process_info *ppi; - ppi = ppi_hash[hash_idx]; + ppi = ppi_hash_table[hash_idx]; while (ppi) { if (ppi->pid == pid) return ppi; @@ -241,6 +278,14 @@ static struct per_process_info *find_process_by_pid(__u32 pid) return NULL; } +static struct per_process_info *find_process(__u32 pid, char *name) +{ + if (ppi_hash_by_pid) + return find_process_by_pid(pid); + + return find_process_by_name(name); +} + static inline int trace_rb_insert(struct trace *t) { struct rb_node **p = &rb_sort_root.rb_node; @@ -362,7 +407,8 @@ static struct io_track *__find_track(dev_t device, __u64 sector) return NULL; } -static struct io_track *find_track(__u32 pid, dev_t device, __u64 sector) +static struct io_track *find_track(__u32 pid, char *comm, dev_t device, + __u64 sector) { struct io_track *iot; @@ -370,6 +416,7 @@ static struct io_track *find_track(__u32 pid, dev_t device, __u64 sector) if (!iot) { iot = malloc(sizeof(*iot)); iot->pid = pid; + memcpy(iot->comm, comm, sizeof(iot->comm)); iot->device = device; iot->sector = sector; track_rb_insert(iot); @@ -403,7 +450,7 @@ static void log_track_getrq(struct blk_io_trace *t) if (!track_ios) return; - iot = find_track(t->pid, t->device, t->sector); + iot = find_track(t->pid, t->comm, t->device, t->sector); iot->allocation_time = t->time; } @@ -419,12 +466,12 @@ static unsigned long long log_track_insert(struct blk_io_trace *t) if (!track_ios) return -1; - iot = find_track(t->pid, t->device, t->sector); + iot = find_track(t->pid, t->comm, t->device, t->sector); iot->queue_time = t->time; elapsed = iot->queue_time - iot->allocation_time; if (per_process_stats) { - struct per_process_info *ppi = find_process_by_pid(iot->pid); + struct per_process_info *ppi = find_process(iot->pid,iot->comm); int w = (t->action & BLK_TC_ACT(BLK_TC_WRITE)) != 0; if (ppi && elapsed > ppi->longest_allocation_wait[w]) @@ -457,7 +504,7 @@ static unsigned long long log_track_issue(struct blk_io_trace *t) elapsed = iot->dispatch_time - iot->queue_time; if (per_process_stats) { - struct per_process_info *ppi = find_process_by_pid(iot->pid); + struct per_process_info *ppi = find_process(iot->pid,iot->comm); int w = (t->action & BLK_TC_ACT(BLK_TC_WRITE)) != 0; if (ppi && elapsed > ppi->longest_dispatch_wait[w]) @@ -490,7 +537,7 @@ static unsigned long long log_track_complete(struct blk_io_trace *t) elapsed = iot->completion_time - iot->dispatch_time; if (per_process_stats) { - struct per_process_info *ppi = find_process_by_pid(iot->pid); + struct per_process_info *ppi = find_process(iot->pid,iot->comm); int w = (t->action & BLK_TC_ACT(BLK_TC_WRITE)) != 0; if (ppi && elapsed > ppi->longest_completion_wait[w]) @@ -509,12 +556,12 @@ static unsigned long long log_track_complete(struct blk_io_trace *t) static struct io_stats *find_process_io_stats(__u32 pid, char *name) { - struct per_process_info *ppi = find_process_by_pid(pid); + struct per_process_info *ppi = find_process(pid, name); if (!ppi) { ppi = malloc(sizeof(*ppi)); memset(ppi, 0, sizeof(*ppi)); - strncpy(ppi->name, name, sizeof(ppi->name)); + memcpy(ppi->name, name, 16); ppi->pid = pid; add_process_to_hash(ppi); add_process_to_list(ppi); @@ -523,7 +570,6 @@ static struct io_stats *find_process_io_stats(__u32 pid, char *name) return &ppi->io_stats; } - static void resize_cpu_info(struct per_dev_info *pdi, int cpu) { struct per_cpu_info *cpus = pdi->cpus; @@ -988,7 +1034,11 @@ static void show_process_stats(void) while (ppi) { char name[64]; - snprintf(name, sizeof(name)-1, "%s (%u)", ppi->name, ppi->pid); + if (ppi_hash_by_pid) + sprintf(name, "%s (%u)", ppi->name, ppi->pid); + else + sprintf(name, "%s (%u, ...)", ppi->name, ppi->pid); + dump_io_stats(&ppi->io_stats, name); dump_wait_stats(ppi); ppi = ppi->list_next; @@ -1466,6 +1516,7 @@ static char usage_str[] = \ "\t-o Output file. If not given, output is stdout\n" \ "\t-b stdin read batching\n" \ "\t-s Show per-program io statistics\n" \ + "\t-n Hash processes by name, not pid\n" \ "\t-t Track individual ios. Will tell you the time a request took\n" \ "\t to get queued, to get dispatched, and to get completed\n" \ "\t-q Quiet. Don't display any stats at the end of the trace\n" \ @@ -1523,6 +1574,9 @@ int main(int argc, char *argv[]) if (add_format_spec(optarg) != 0) return 1; break; + case 'n': + ppi_hash_by_pid = 1; + break; case 'v': printf("%s version %s\n", argv[0], blkparse_version); return 0; diff --git a/blktrace.h b/blktrace.h index 6ef5ab0..bd704ba 100644 --- a/blktrace.h +++ b/blktrace.h @@ -17,6 +17,9 @@ #define min(a, b) ((a) < (b) ? (a) : (b)) +typedef __u32 u32; +typedef __u8 u8; + struct io_stats { unsigned long qreads, qwrites, creads, cwrites, mreads, mwrites; unsigned long ireads, iwrites; diff --git a/jhash.h b/jhash.h new file mode 100644 index 0000000..82c7ae4 --- /dev/null +++ b/jhash.h @@ -0,0 +1,143 @@ +#ifndef _LINUX_JHASH_H +#define _LINUX_JHASH_H + +/* jhash.h: Jenkins hash support. + * + * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * http://burtleburtle.net/bob/hash/ + * + * These are the credits from Bob's sources: + * + * lookup2.c, by Bob Jenkins, December 1996, Public Domain. + * hash(), hash2(), hash3, and mix() are externally useful functions. + * Routines to test the hash are included if SELF_TEST is defined. + * You can use this free for any purpose. It has no warranty. + * + * Copyright (C) 2003 David S. Miller (davem@redhat.com) + * + * I've modified Bob's hash to be useful in the Linux kernel, and + * any bugs present are surely my fault. -DaveM + */ + +/* NOTE: Arguments are modified. */ +#define __jhash_mix(a, b, c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* The golden ration: an arbitrary value */ +#define JHASH_GOLDEN_RATIO 0x9e3779b9 + +/* The most generic version, hashes an arbitrary sequence + * of bytes. No alignment or length assumptions are made about + * the input key. + */ +static inline u32 jhash(const void *key, u32 length, u32 initval) +{ + u32 a, b, c, len; + const u8 *k = key; + + len = length; + a = b = JHASH_GOLDEN_RATIO; + c = initval; + + while (len >= 12) { + a += (k[0] +((u32)k[1]<<8) +((u32)k[2]<<16) +((u32)k[3]<<24)); + b += (k[4] +((u32)k[5]<<8) +((u32)k[6]<<16) +((u32)k[7]<<24)); + c += (k[8] +((u32)k[9]<<8) +((u32)k[10]<<16)+((u32)k[11]<<24)); + + __jhash_mix(a,b,c); + + k += 12; + len -= 12; + } + + c += length; + switch (len) { + case 11: c += ((u32)k[10]<<24); + case 10: c += ((u32)k[9]<<16); + case 9 : c += ((u32)k[8]<<8); + case 8 : b += ((u32)k[7]<<24); + case 7 : b += ((u32)k[6]<<16); + case 6 : b += ((u32)k[5]<<8); + case 5 : b += k[4]; + case 4 : a += ((u32)k[3]<<24); + case 3 : a += ((u32)k[2]<<16); + case 2 : a += ((u32)k[1]<<8); + case 1 : a += k[0]; + }; + + __jhash_mix(a,b,c); + + return c; +} + +/* A special optimized version that handles 1 or more of u32s. + * The length parameter here is the number of u32s in the key. + */ +static inline u32 jhash2(u32 *k, u32 length, u32 initval) +{ + u32 a, b, c, len; + + a = b = JHASH_GOLDEN_RATIO; + c = initval; + len = length; + + while (len >= 3) { + a += k[0]; + b += k[1]; + c += k[2]; + __jhash_mix(a, b, c); + k += 3; len -= 3; + } + + c += length * 4; + + switch (len) { + case 2 : b += k[1]; + case 1 : a += k[0]; + }; + + __jhash_mix(a,b,c); + + return c; +} + + +/* A special ultra-optimized versions that knows they are hashing exactly + * 3, 2 or 1 word(s). + * + * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally + * done at the end is not done here. + */ +static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval) +{ + a += JHASH_GOLDEN_RATIO; + b += JHASH_GOLDEN_RATIO; + c += initval; + + __jhash_mix(a, b, c); + + return c; +} + +static inline u32 jhash_2words(u32 a, u32 b, u32 initval) +{ + return jhash_3words(a, b, 0, initval); +} + +static inline u32 jhash_1word(u32 a, u32 initval) +{ + return jhash_3words(a, 0, 0, initval); +} + +#endif /* _LINUX_JHASH_H */ -- 2.25.1