202 lines
5.0 KiB
C
202 lines
5.0 KiB
C
/*
|
|
* 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;
|
|
}
|