/* Copyright (C) 2008, 2009, 2010 Jiri Olsa 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 . */ #include #include #include #include #include #include #include "config.h" struct timeval tv_program_start; struct timeval tv_program_stop; 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, 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, 1, "%s\n", "creating new hash table"); 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, 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, 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, 1, "%s\n", "reallocation ok"); 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, 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; timersub(&m->h.tv, &sym->tv_cur, &tv); timeradd(&sym->tv_all, &tv, &sym->tv_all); } 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, 1, "%s\n", "counting total time"); for(i = 0; i < t->sym_cnt; i++) { struct lt_stats_sym *sym = t->sym_array[i]; timeradd(&sym->tv_all, &tv_thread_accu, &tv_thread_accu); } time_global = tv_thread_accu.tv_sec * 1000000 + tv_thread_accu.tv_usec; PRINT_VERBOSE(cfg, 1, "%s\n", "counting post mortem statistics"); 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); if (isnan(sym->percent)) sym->percent = 0.0f; sym->usec_call = time_sym/sym->call; } PRINT_VERBOSE(cfg, 1, "%s\n", "sorting"); csort = cfg->csort; qsort(t->sym_array, t->sym_cnt, sizeof(struct lt_stats_sym*), qsort_compar); PRINT_VERBOSE(cfg, 1, "%s\n", "printing"); timersub(&t->tv_stop, &t->tv_start, &tv_thread_real); 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; timersub(&tv_program_stop, &tv_program_start, &tv_program_real); 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; }