| /* |
| * This file is part of ltrace. |
| * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. |
| * Copyright (C) 2003,2008,2009 Juan Cespedes |
| * Copyright (C) 2006 Ian Wienand |
| * |
| * This program 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 2 of the |
| * License, or (at your option) any later version. |
| * |
| * This program 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 this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
| * 02110-1301 USA |
| */ |
| |
| #include "config.h" |
| |
| #include <sys/time.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "summary.h" |
| #include "dict.h" |
| #include "library.h" |
| #include "options.h" |
| |
| struct entry_st { |
| const char *name; |
| unsigned count; |
| struct timeval tv; |
| }; |
| |
| struct fill_struct_data { |
| struct vect entries; |
| unsigned tot_count; |
| unsigned long tot_usecs; |
| }; |
| |
| struct opt_c_struct { |
| int count; |
| struct timeval tv; |
| }; |
| |
| static struct dict *dict_opt_c; |
| |
| struct timedelta |
| calc_time_spent(struct timeval start) |
| { |
| struct timeval tv; |
| gettimeofday(&tv, NULL); |
| |
| struct timeval diff; |
| diff.tv_sec = tv.tv_sec - start.tv_sec; |
| if (tv.tv_usec >= start.tv_usec) { |
| diff.tv_usec = tv.tv_usec - start.tv_usec; |
| } else { |
| diff.tv_sec--; |
| diff.tv_usec = 1000000 + tv.tv_usec - start.tv_usec; |
| } |
| |
| struct timedelta ret = { diff }; |
| return ret; |
| } |
| |
| static enum callback_status |
| fill_struct(const char **namep, struct opt_c_struct *st, void *u) |
| { |
| struct fill_struct_data *data = u; |
| struct entry_st entry = { *namep, st->count, st->tv }; |
| if (VECT_PUSHBACK(&data->entries, &entry) < 0) |
| return CBS_STOP; |
| |
| data->tot_count += st->count; |
| data->tot_usecs += 1000000 * st->tv.tv_sec; |
| data->tot_usecs += st->tv.tv_usec; |
| return CBS_CONT; |
| } |
| |
| static int |
| compar(const struct entry_st *en1, const struct entry_st *en2) |
| { |
| if (en2->tv.tv_sec - en1->tv.tv_sec) |
| return en2->tv.tv_sec - en1->tv.tv_sec; |
| else |
| return en2->tv.tv_usec - en1->tv.tv_usec; |
| } |
| |
| static enum callback_status |
| dump_one(struct entry_st *entry, void *u) |
| { |
| struct fill_struct_data *data = u; |
| unsigned long long int c; |
| unsigned long long int p; |
| c = 1000000 * (int)entry->tv.tv_sec + |
| (int)entry->tv.tv_usec; |
| p = 100000 * c / data->tot_usecs + 5; |
| fprintf(options.output, "%3lu.%02lu %4d.%06d %11lu %9d %s\n", |
| (unsigned long int)(p / 1000), |
| (unsigned long int)((p / 10) % 100), |
| (int)entry->tv.tv_sec, (int)entry->tv.tv_usec, |
| (unsigned long int)(c / entry->count), |
| entry->count, |
| #ifdef USE_DEMANGLE |
| options.demangle ? my_demangle(entry->name) : |
| #endif |
| entry->name); |
| |
| return CBS_CONT; |
| } |
| |
| void |
| show_summary(void) |
| { |
| struct fill_struct_data cdata = {}; |
| VECT_INIT(&cdata.entries, struct entry_st); |
| |
| if (dict_opt_c != NULL) { |
| DICT_EACH(dict_opt_c, const char *, struct opt_c_struct, NULL, |
| fill_struct, &cdata); |
| |
| VECT_QSORT(&cdata.entries, struct entry_st, &compar); |
| } |
| |
| fprintf(options.output, |
| "%% time seconds usecs/call calls function\n"); |
| fprintf(options.output, |
| "------ ----------- ----------- --------- --------------------\n"); |
| |
| VECT_EACH(&cdata.entries, struct entry_st, NULL, dump_one, &cdata); |
| |
| fprintf(options.output, |
| "------ ----------- ----------- --------- --------------------\n"); |
| fprintf(options.output, "100.00 %4lu.%06lu %9d total\n", |
| cdata.tot_usecs / 1000000, |
| cdata.tot_usecs % 1000000, cdata.tot_count); |
| |
| vect_destroy(&cdata.entries, NULL, NULL); |
| } |
| |
| static void |
| free_stringp_cb(const char **stringp, void *data) |
| { |
| free((char *)*stringp); |
| } |
| |
| void |
| summary_account_call(struct library_symbol *libsym, struct timedelta spent) |
| { |
| assert(options.summary); |
| |
| if (dict_opt_c == NULL) { |
| dict_opt_c = malloc(sizeof(*dict_opt_c)); |
| if (dict_opt_c == NULL) { |
| oom: |
| fprintf(stderr, |
| "Can't allocate memory for " |
| "keeping track of -c.\n"); |
| free(dict_opt_c); |
| options.summary = 0; |
| return; |
| } |
| DICT_INIT(dict_opt_c, char *, struct opt_c_struct, |
| dict_hash_string, dict_eq_string, NULL); |
| } |
| |
| struct opt_c_struct *st = DICT_FIND_REF(dict_opt_c, &libsym->name, |
| struct opt_c_struct); |
| if (st == NULL) { |
| const char *na = strdup(libsym->name); |
| struct opt_c_struct new_st = {.count = 0, .tv = {0, 0}}; |
| if (na == NULL |
| || DICT_INSERT(dict_opt_c, &na, &new_st) < 0) { |
| free((char *) na); |
| DICT_DESTROY(dict_opt_c, const char *, |
| struct opt_c_struct, |
| free_stringp_cb, NULL, NULL); |
| goto oom; |
| } |
| st = DICT_FIND_REF(dict_opt_c, &libsym->name, |
| struct opt_c_struct); |
| assert(st != NULL); |
| } |
| |
| if (st->tv.tv_usec + spent.tm.tv_usec > 1000000) { |
| st->tv.tv_usec += spent.tm.tv_usec - 1000000; |
| st->tv.tv_sec++; |
| } else { |
| st->tv.tv_usec += spent.tm.tv_usec; |
| } |
| st->count++; |
| st->tv.tv_sec += spent.tm.tv_sec; |
| return; |
| } |