1 /* 2 * This file is part of ltrace. 3 * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. 4 * Copyright (C) 2003,2008,2009 Juan Cespedes 5 * Copyright (C) 2006 Ian Wienand 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of the 10 * License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 * 02110-1301 USA 21 */ 22 23 #include "config.h" 24 25 #include <sys/time.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 30 #include "summary.h" 31 #include "dict.h" 32 #include "library.h" 33 #include "options.h" 34 35 struct entry_st { 36 const char *name; 37 unsigned count; 38 struct timeval tv; 39 }; 40 41 struct fill_struct_data { 42 struct vect entries; 43 unsigned tot_count; 44 unsigned long tot_usecs; 45 }; 46 47 struct opt_c_struct { 48 int count; 49 struct timeval tv; 50 }; 51 52 static struct dict *dict_opt_c; 53 54 struct timedelta 55 calc_time_spent(struct timeval start) 56 { 57 struct timeval tv; 58 gettimeofday(&tv, NULL); 59 60 struct timeval diff; 61 diff.tv_sec = tv.tv_sec - start.tv_sec; 62 if (tv.tv_usec >= start.tv_usec) { 63 diff.tv_usec = tv.tv_usec - start.tv_usec; 64 } else { 65 diff.tv_sec--; 66 diff.tv_usec = 1000000 + tv.tv_usec - start.tv_usec; 67 } 68 69 struct timedelta ret = { diff }; 70 return ret; 71 } 72 73 static enum callback_status 74 fill_struct(const char **namep, struct opt_c_struct *st, void *u) 75 { 76 struct fill_struct_data *data = u; 77 struct entry_st entry = { *namep, st->count, st->tv }; 78 if (VECT_PUSHBACK(&data->entries, &entry) < 0) 79 return CBS_STOP; 80 81 data->tot_count += st->count; 82 data->tot_usecs += 1000000 * st->tv.tv_sec; 83 data->tot_usecs += st->tv.tv_usec; 84 return CBS_CONT; 85 } 86 87 static int 88 compar(const struct entry_st *en1, const struct entry_st *en2) 89 { 90 if (en2->tv.tv_sec - en1->tv.tv_sec) 91 return en2->tv.tv_sec - en1->tv.tv_sec; 92 else 93 return en2->tv.tv_usec - en1->tv.tv_usec; 94 } 95 96 static enum callback_status 97 dump_one(struct entry_st *entry, void *u) 98 { 99 struct fill_struct_data *data = u; 100 unsigned long long int c; 101 unsigned long long int p; 102 c = 1000000 * (int)entry->tv.tv_sec + 103 (int)entry->tv.tv_usec; 104 p = 100000 * c / data->tot_usecs + 5; 105 fprintf(options.output, "%3lu.%02lu %4d.%06d %11lu %9d %s\n", 106 (unsigned long int)(p / 1000), 107 (unsigned long int)((p / 10) % 100), 108 (int)entry->tv.tv_sec, (int)entry->tv.tv_usec, 109 (unsigned long int)(c / entry->count), 110 entry->count, 111 #ifdef USE_DEMANGLE 112 options.demangle ? my_demangle(entry->name) : 113 #endif 114 entry->name); 115 116 return CBS_CONT; 117 } 118 119 void 120 show_summary(void) 121 { 122 struct fill_struct_data cdata = {}; 123 VECT_INIT(&cdata.entries, struct entry_st); 124 125 if (dict_opt_c != NULL) { 126 DICT_EACH(dict_opt_c, const char *, struct opt_c_struct, NULL, 127 fill_struct, &cdata); 128 129 VECT_QSORT(&cdata.entries, struct entry_st, &compar); 130 } 131 132 fprintf(options.output, 133 "%% time seconds usecs/call calls function\n"); 134 fprintf(options.output, 135 "------ ----------- ----------- --------- --------------------\n"); 136 137 VECT_EACH(&cdata.entries, struct entry_st, NULL, dump_one, &cdata); 138 139 fprintf(options.output, 140 "------ ----------- ----------- --------- --------------------\n"); 141 fprintf(options.output, "100.00 %4lu.%06lu %9d total\n", 142 cdata.tot_usecs / 1000000, 143 cdata.tot_usecs % 1000000, cdata.tot_count); 144 145 vect_destroy(&cdata.entries, NULL, NULL); 146 } 147 148 static void 149 free_stringp_cb(const char **stringp, void *data) 150 { 151 free((char *)*stringp); 152 } 153 154 void 155 summary_account_call(struct library_symbol *libsym, struct timedelta spent) 156 { 157 assert(options.summary); 158 159 if (dict_opt_c == NULL) { 160 dict_opt_c = malloc(sizeof(*dict_opt_c)); 161 if (dict_opt_c == NULL) { 162 oom: 163 fprintf(stderr, 164 "Can't allocate memory for " 165 "keeping track of -c.\n"); 166 free(dict_opt_c); 167 options.summary = 0; 168 return; 169 } 170 DICT_INIT(dict_opt_c, char *, struct opt_c_struct, 171 dict_hash_string, dict_eq_string, NULL); 172 } 173 174 struct opt_c_struct *st = DICT_FIND_REF(dict_opt_c, &libsym->name, 175 struct opt_c_struct); 176 if (st == NULL) { 177 const char *na = strdup(libsym->name); 178 struct opt_c_struct new_st = {.count = 0, .tv = {0, 0}}; 179 if (na == NULL 180 || DICT_INSERT(dict_opt_c, &na, &new_st) < 0) { 181 free((char *) na); 182 DICT_DESTROY(dict_opt_c, const char *, 183 struct opt_c_struct, 184 free_stringp_cb, NULL, NULL); 185 goto oom; 186 } 187 st = DICT_FIND_REF(dict_opt_c, &libsym->name, 188 struct opt_c_struct); 189 assert(st != NULL); 190 } 191 192 if (st->tv.tv_usec + spent.tm.tv_usec > 1000000) { 193 st->tv.tv_usec += spent.tm.tv_usec - 1000000; 194 st->tv.tv_sec++; 195 } else { 196 st->tv.tv_usec += spent.tm.tv_usec; 197 } 198 st->count++; 199 st->tv.tv_sec += spent.tm.tv_sec; 200 return; 201 } 202