diff options
author | Jiri Olsa <jolsa@shell.devel.redhat.com> | 2009-09-04 02:29:08 -0400 |
---|---|---|
committer | Jiri Olsa <jolsa@shell.devel.redhat.com> | 2009-09-04 02:29:08 -0400 |
commit | 04f3fbbfdb3a5dd197dbc25ca18ad244f1fbf6a5 (patch) | |
tree | 8e53039a4f5d1a4571000bc06214053261aebf8d /src/stats.c | |
download | latrace-04f3fbbfdb3a5dd197dbc25ca18ad244f1fbf6a5.tar.gz latrace-04f3fbbfdb3a5dd197dbc25ca18ad244f1fbf6a5.tar.xz latrace-04f3fbbfdb3a5dd197dbc25ca18ad244f1fbf6a5.zip |
initial commit - 0.5.7
Diffstat (limited to 'src/stats.c')
-rw-r--r-- | src/stats.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/src/stats.c b/src/stats.c new file mode 100644 index 0000000..59e0d9c --- /dev/null +++ b/src/stats.c @@ -0,0 +1,328 @@ +/* + Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com> + + This file is part of the latrace. + + The latrace is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The latrace is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the latrace (file COPYING). If not, see + <http://www.gnu.org/licenses/>. +*/ + + +#include <search.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> + +#include "config.h" + + +struct timeval tv_program_start; +struct timeval tv_program_stop; + +/* res = a - b*/ +static int tv_sub(struct timeval *res, struct timeval *a, struct timeval *b) +{ + res->tv_sec = a->tv_sec - b->tv_sec; + + if (a->tv_usec > b->tv_usec) + res->tv_usec = a->tv_usec - b->tv_usec; + else { + res->tv_usec = a->tv_usec + (1000000 - b->tv_usec); + if (res->tv_sec) + res->tv_sec--; + } + + return 0; +} + +/* res = a + b*/ +static int tv_add(struct timeval *res, struct timeval *a, struct timeval *b) +{ + struct timeval tv; + tv.tv_sec = a->tv_sec + b->tv_sec; + tv.tv_usec = a->tv_usec + b->tv_usec; + if (tv.tv_usec > 1000000) { + tv.tv_usec -= 1000000; + tv.tv_sec++; + } + + *res = tv; + return 0; +} + +int lt_stats_alloc(struct lt_config_app *cfg, struct lt_thread *t) +{ + int i; + + if (!t->sym_array) { + if (!hcreate_r(t->sym_max = LT_SYM_HMAX, &t->sym_htab)) { + perror("hcreate_r failed"); + return -1; + } + + t->sym_array = (struct lt_stats_sym**) malloc( + t->sym_max * sizeof(struct lt_stats_sym*)); + + if (!t->sym_array) { + perror("malloc failed"); + return -1; + } + + return 0; + } + + + PRINT_VERBOSE(cfg->sh.verbose, 1, + "too bad, out of symbols, reallocation... %p - %d\n", + t->sym_array, t->sym_cnt); + + /* hash table reallocation */ + t->sym_max += LT_SYM_HMAX; + hdestroy_r(&t->sym_htab); + + PRINT_VERBOSE(cfg->sh.verbose, 1, "creating new hash table\n"); + + if (!hcreate_r(t->sym_max, &t->sym_htab)) { + perror("hcreate_r failed"); + return -1; + } + + t->sym_array = (struct lt_stats_sym**) realloc(t->sym_array, + t->sym_max * sizeof(struct lt_stats_sym*)); + + if (!t->sym_array) { + perror("realloc failed"); + return -1; + } + + PRINT_VERBOSE(cfg->sh.verbose, 1, + "adding symbols to new hash table %p\n", + t->sym_array); + + for(i = 0; i < t->sym_cnt; i++) { + ENTRY e, *ep; + struct lt_stats_sym *sym; + + sym = t->sym_array[i]; + e.key = sym->name; + e.data = sym; + + PRINT_VERBOSE(cfg->sh.verbose, 1, + "adding symbol %s\n", sym->name); + + if (0 == hsearch_r(e, ENTER, &ep, &t->sym_htab)) { + printf("failed to add hash item during reallocation\n"); + return -1; + } + } + + PRINT_VERBOSE(cfg->sh.verbose, 1, "reallocation ok\n"); + return 0; +} + +int lt_stats_sym(struct lt_config_app *cfg, struct lt_thread *t, + struct lt_fifo_msym* m) +{ + ENTRY e, *ep; + char buf[1000]; + struct lt_stats_sym *sym; + jmp_buf env; + int realloc = 0; + + sprintf(buf, "%s%s", m->data + m->sym, m->data + m->lib); + e.key = strdup(buf); + e.data = 0; + + /* array got out of space */ + if ((t->sym_cnt == t->sym_max) && (-1 == lt_stats_alloc(cfg, t))) + return -1; + + /* hash table got out of space */ + if (setjmp(env)) + realloc = 1; + + if (0 == hsearch_r(e, ENTER, &ep, &t->sym_htab)) { + if (!realloc) { + if (-1 == lt_stats_alloc(cfg, t)) + return -1; + longjmp(env, 1); + } + return -1; + } + + if (!ep->data) { + sym = malloc(sizeof(struct lt_stats_sym)); + memset(sym, 0, sizeof(struct lt_stats_sym)); + + ep->data = sym; + + sym->name = e.key; + sym->sym = strdup(m->data + m->sym); + sym->lib = strdup(m->data + m->lib); + + t->sym_array[t->sym_cnt] = sym; + t->sym_cnt++; + + PRINT_VERBOSE(cfg->sh.verbose, 1, + "adding symbol %d %s\n", t->sym_cnt, sym->name); + } else + free(e.key); + + sym = ep->data; + if (FIFO_MSG_TYPE_ENTRY == m->h.type) { + sym->call++; + sym->tv_cur = m->h.tv; + } + + if (FIFO_MSG_TYPE_EXIT == m->h.type) { + struct timeval tv; + tv_sub(&tv, &m->h.tv, &sym->tv_cur); + tv_add(&sym->tv_all, &sym->tv_all, &tv); + } + + return 0; +} + +static int csort; + +static int qsort_compar(const void *a, const void *b) +{ + struct lt_stats_sym *data_a = *((struct lt_stats_sym**) a); + struct lt_stats_sym *data_b = *((struct lt_stats_sym**) b); + + if (csort == LT_CSORT_TIME) { + if (data_a->tv_all.tv_sec != data_b->tv_all.tv_sec) + return data_b->tv_all.tv_sec - data_a->tv_all.tv_sec; + return data_b->tv_all.tv_usec - data_a->tv_all.tv_usec; + } + + if (csort == LT_CSORT_PERCENT) { + int percent_a = data_a->percent * 100000; + int percent_b = data_b->percent * 100000; + return percent_b - percent_a; + } + + if (csort == LT_CSORT_CALL) { + return data_b->call - data_a->call; + } + + if (csort == LT_CSORT_UCALL) { + return data_b->usec_call - data_a->usec_call; + } + + if (csort == LT_CSORT_LIB) { + int ret = strcmp(data_a->lib, data_b->lib); + if (ret) + return ret; + return strcmp(data_a->sym, data_b->sym); + } + + if (csort == LT_CSORT_SYM) { + int ret = strcmp(data_a->sym, data_b->sym); + if (ret) + return ret; + return strcmp(data_a->lib, data_b->lib); + } + + return 0; +} + +static int lt_stats_show_thread(struct lt_config_app *cfg, struct lt_thread *t) +{ + int i; + struct timeval tv_thread_real; + struct timeval tv_thread_accu = { 0, 0}; + float time_global; + + PRINT_VERBOSE(cfg->sh.verbose, 1, "counting total time\n"); + for(i = 0; i < t->sym_cnt; i++) { + struct lt_stats_sym *sym = t->sym_array[i]; + tv_add(&tv_thread_accu, &tv_thread_accu, &sym->tv_all); + } + + time_global = tv_thread_accu.tv_sec * 1000000 + tv_thread_accu.tv_usec; + + PRINT_VERBOSE(cfg->sh.verbose, 1, "counting post mortem statistics\n"); + + for(i = 0; i < t->sym_cnt; i++) { + struct lt_stats_sym *sym = t->sym_array[i]; + u_int time_sym = sym->tv_all.tv_sec*1000000 + sym->tv_all.tv_usec; + + sym->percent = time_sym / (time_global/100); + sym->usec_call = time_sym/sym->call; + } + + PRINT_VERBOSE(cfg->sh.verbose, 1, "sorting\n"); + + csort = cfg->csort; + qsort(t->sym_array, t->sym_cnt, sizeof(struct lt_stats_sym*), qsort_compar); + + PRINT_VERBOSE(cfg->sh.verbose, 1, "printing\n"); + + tv_sub(&tv_thread_real, &t->tv_stop, &t->tv_start); + printf("\nThread %d (runtime %u.%06u sec)\n", + t->tid, (u_int) tv_thread_real.tv_sec, (u_int) tv_thread_real.tv_usec); + printf("%3s %-6s %10s %10s %10s %-30s\n", + "sec", "usec", "%", "usec/call", "calls", "symbol"); + printf("%3s %-6s %10s %10s %10s %-30s\n", + "---", "------", "----------", "----------", "----------", "------------------------------"); + + for(i = 0; i < t->sym_cnt; i++) { + struct lt_stats_sym *sym = t->sym_array[i]; + + /* sec:usec */ + printf("%3u:%06u ", (u_int) sym->tv_all.tv_sec, (u_int) sym->tv_all.tv_usec); + + /* % */ + printf("%10f ", sym->percent); + + /* usec/call */ + printf("%10u ", sym->usec_call); + + /* call */ + printf("%10u ", sym->call); + + if (LT_CSORT_LIB == cfg->csort) { + /* calls symbol */ + printf("%s ", sym->lib); + printf("%s\n", sym->sym); + } else { + printf("%s@", sym->sym); + printf("%s\n", sym->lib); + } + } + + return 0; +} + +int lt_stats_show(struct lt_config_app *cfg) +{ + struct lt_thread *t; + + struct timeval tv_program_real; + tv_sub(&tv_program_real, &tv_program_stop, &tv_program_start); + + printf("\n--------------------------------------------------------------------------\n"); + printf("Statistics for [%s] total runtime: %u.%06u sec\n", + cfg->prog, + (u_int) tv_program_real.tv_sec, + (u_int) tv_program_real.tv_usec); + + for(t = lt_thread_first(cfg); t; t = lt_thread_next(cfg)) + lt_stats_show_thread(cfg, t); + + return 0; +} + |