blob: 14eaae3ddf1b5db9c326f815468af7dab61a63f5 [file] [log] [blame]
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include "common.h"
#include "proc.h"
#include "library.h"
/* TODO FIXME XXX: include in common.h: */
extern struct timeval current_time_spent;
Dict *dict_opt_c = NULL;
static Process *current_proc = 0;
static int current_depth = 0;
static int current_column = 0;
static void
output_indent(Process *proc) {
current_column +=
fprintf(options.output, "%*s", options.indent * proc->callstack_depth, "");
}
static void
begin_of_line(enum tof type, Process *proc) {
current_column = 0;
if (!proc) {
return;
}
if ((options.output != stderr) && (opt_p || options.follow)) {
current_column += fprintf(options.output, "%u ", proc->pid);
} else if (options.follow) {
current_column += fprintf(options.output, "[pid %u] ", proc->pid);
}
if (opt_r) {
struct timeval tv;
struct timezone tz;
static struct timeval old_tv = { 0, 0 };
struct timeval diff;
gettimeofday(&tv, &tz);
if (old_tv.tv_sec == 0 && old_tv.tv_usec == 0) {
old_tv.tv_sec = tv.tv_sec;
old_tv.tv_usec = tv.tv_usec;
}
diff.tv_sec = tv.tv_sec - old_tv.tv_sec;
if (tv.tv_usec >= old_tv.tv_usec) {
diff.tv_usec = tv.tv_usec - old_tv.tv_usec;
} else {
diff.tv_sec--;
diff.tv_usec = 1000000 + tv.tv_usec - old_tv.tv_usec;
}
old_tv.tv_sec = tv.tv_sec;
old_tv.tv_usec = tv.tv_usec;
current_column += fprintf(options.output, "%3lu.%06d ",
diff.tv_sec, (int)diff.tv_usec);
}
if (opt_t) {
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
if (opt_t > 2) {
current_column += fprintf(options.output, "%lu.%06d ",
tv.tv_sec, (int)tv.tv_usec);
} else if (opt_t > 1) {
struct tm *tmp = localtime(&tv.tv_sec);
current_column +=
fprintf(options.output, "%02d:%02d:%02d.%06d ",
tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
(int)tv.tv_usec);
} else {
struct tm *tmp = localtime(&tv.tv_sec);
current_column += fprintf(options.output, "%02d:%02d:%02d ",
tmp->tm_hour, tmp->tm_min,
tmp->tm_sec);
}
}
if (opt_i) {
if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
current_column += fprintf(options.output, "[%p] ",
proc->return_addr);
} else {
current_column += fprintf(options.output, "[%p] ",
proc->instruction_pointer);
}
}
if (options.indent > 0 && type != LT_TOF_NONE) {
output_indent(proc);
}
}
static Function *
name2func(char const *name) {
Function *tmp;
const char *str1, *str2;
tmp = list_of_functions;
while (tmp) {
#ifdef USE_DEMANGLE
str1 = options.demangle ? my_demangle(tmp->name) : tmp->name;
str2 = options.demangle ? my_demangle(name) : name;
#else
str1 = tmp->name;
str2 = name;
#endif
if (!strcmp(str1, str2)) {
return tmp;
}
tmp = tmp->next;
}
return NULL;
}
void
output_line(Process *proc, char *fmt, ...) {
va_list args;
if (options.summary) {
return;
}
if (current_proc) {
if (current_proc->callstack[current_depth].return_addr) {
fprintf(options.output, " <unfinished ...>\n");
} else {
fprintf(options.output, " <no return ...>\n");
}
}
current_proc = 0;
if (!fmt) {
return;
}
begin_of_line(LT_TOF_NONE, proc);
va_start(args, fmt);
vfprintf(options.output, fmt, args);
fprintf(options.output, "\n");
va_end(args);
current_column = 0;
}
static void
tabto(int col) {
if (current_column < col) {
fprintf(options.output, "%*s", col - current_column, "");
}
}
void
output_left(enum tof type, struct Process *proc,
struct library_symbol *libsym)
{
const char *function_name = libsym->name;
Function *func;
static arg_type_info *arg_unknown = NULL;
if (arg_unknown == NULL)
arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN);
if (options.summary) {
return;
}
if (current_proc) {
fprintf(options.output, " <unfinished ...>\n");
current_column = 0;
}
current_proc = proc;
current_depth = proc->callstack_depth;
begin_of_line(type, proc);
if (!options.hide_caller)
current_column += fprintf(options.output, "%s->",
libsym->lib->soname);
#ifdef USE_DEMANGLE
current_column +=
fprintf(options.output, "%s(",
(options.demangle
? my_demangle(function_name) : function_name));
#else
current_column += fprintf("%s(", function_name);
#endif
func = name2func(function_name);
if (!func) {
int i;
for (i = 0; i < 4; i++) {
current_column +=
display_arg(type, proc, i, arg_unknown);
current_column += fprintf(options.output, ", ");
}
current_column += display_arg(type, proc, 4, arg_unknown);
return;
} else {
int i;
for (i = 0; i < func->num_params - func->params_right - 1; i++) {
current_column +=
display_arg(type, proc, i, func->arg_info[i]);
current_column += fprintf(options.output, ", ");
}
if (func->num_params > func->params_right) {
current_column +=
display_arg(type, proc, i, func->arg_info[i]);
if (func->params_right) {
current_column += fprintf(options.output, ", ");
}
}
if (func->params_right
|| func->return_info->type == ARGTYPE_STRING_N
|| func->return_info->type == ARGTYPE_ARRAY) {
save_register_args(type, proc);
}
}
}
void
output_right(enum tof type, struct Process *proc, struct library_symbol *libsym)
{
const char *function_name = libsym->name;
Function *func = name2func(function_name);
static arg_type_info *arg_unknown = NULL;
if (arg_unknown == NULL)
arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN);
if (options.summary) {
struct opt_c_struct *st;
if (!dict_opt_c) {
dict_opt_c =
dict_init(dict_key2hash_string,
dict_key_cmp_string);
}
st = dict_find_entry(dict_opt_c, function_name);
if (!st) {
char *na;
st = malloc(sizeof(struct opt_c_struct));
na = strdup(function_name);
if (!st || !na) {
perror("malloc()");
exit(1);
}
st->count = 0;
st->tv.tv_sec = st->tv.tv_usec = 0;
dict_enter(dict_opt_c, na, st);
}
if (st->tv.tv_usec + current_time_spent.tv_usec > 1000000) {
st->tv.tv_usec += current_time_spent.tv_usec - 1000000;
st->tv.tv_sec++;
} else {
st->tv.tv_usec += current_time_spent.tv_usec;
}
st->count++;
st->tv.tv_sec += current_time_spent.tv_sec;
// fprintf(options.output, "%s <%lu.%06d>\n", function_name,
// current_time_spent.tv_sec, (int)current_time_spent.tv_usec);
return;
}
if (current_proc && (current_proc != proc ||
current_depth != proc->callstack_depth)) {
fprintf(options.output, " <unfinished ...>\n");
current_proc = 0;
}
if (current_proc != proc) {
begin_of_line(type, proc);
#ifdef USE_DEMANGLE
current_column +=
fprintf(options.output, "<... %s resumed> ",
options.demangle ? my_demangle(function_name) : function_name);
#else
current_column +=
fprintf(options.output, "<... %s resumed> ", function_name);
#endif
}
if (!func) {
current_column += fprintf(options.output, ") ");
tabto(options.align - 1);
fprintf(options.output, "= ");
display_arg(type, proc, -1, arg_unknown);
} else {
int i;
for (i = func->num_params - func->params_right;
i < func->num_params - 1; i++) {
current_column +=
display_arg(type, proc, i, func->arg_info[i]);
current_column += fprintf(options.output, ", ");
}
if (func->params_right) {
current_column +=
display_arg(type, proc, i, func->arg_info[i]);
}
current_column += fprintf(options.output, ") ");
tabto(options.align - 1);
fprintf(options.output, "= ");
if (func->return_info->type == ARGTYPE_VOID) {
fprintf(options.output, "<void>");
} else {
display_arg(type, proc, -1, func->return_info);
}
}
if (opt_T) {
fprintf(options.output, " <%lu.%06d>",
current_time_spent.tv_sec,
(int)current_time_spent.tv_usec);
}
fprintf(options.output, "\n");
#if defined(HAVE_LIBUNWIND)
if (options.bt_depth > 0) {
unw_cursor_t cursor;
unw_word_t ip, sp;
int unwind_depth = options.bt_depth;
char fn_name[100];
unw_init_remote(&cursor, proc->unwind_as, proc->unwind_priv);
while (unwind_depth) {
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
unw_get_proc_name(&cursor, fn_name, 100, NULL);
fprintf(options.output, "\t\t\t%s (ip = 0x%lx)\n", fn_name, (long) ip);
if (unw_step(&cursor) <= 0)
break;
unwind_depth--;
}
fprintf(options.output, "\n");
}
#endif /* defined(HAVE_LIBUNWIND) */
current_proc = 0;
current_column = 0;
}