mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2026-02-12 16:10:54 +00:00
Add symbol resolution to tracker
This commit is contained in:
@ -513,7 +513,6 @@ collector_start (Collector *collector,
|
|||||||
* starting collection, so the parsing doesn't interfere
|
* starting collection, so the parsing doesn't interfere
|
||||||
* with the profiling.
|
* with the profiling.
|
||||||
*/
|
*/
|
||||||
process_is_kernel_address (0);
|
|
||||||
|
|
||||||
for (list = collector->counters; list != NULL; list = list->next)
|
for (list = collector->counters; list != NULL; list = list->next)
|
||||||
counter_enable (list->data);
|
counter_enable (list->data);
|
||||||
@ -710,6 +709,8 @@ resolve_symbols (StackLink *trace, gint size, gpointer data)
|
|||||||
Profile *
|
Profile *
|
||||||
collector_create_profile (Collector *collector)
|
collector_create_profile (Collector *collector)
|
||||||
{
|
{
|
||||||
|
return tracker_create_profile (collector->tracker);
|
||||||
|
|
||||||
ResolveInfo info;
|
ResolveInfo info;
|
||||||
Profile *profile;
|
Profile *profile;
|
||||||
|
|
||||||
|
|||||||
289
process.c
289
process.c
@ -485,295 +485,6 @@ find_kernel_binary (void)
|
|||||||
return binary;
|
return binary;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
gulong address;
|
|
||||||
char *name;
|
|
||||||
} KernelSymbol;
|
|
||||||
|
|
||||||
static void
|
|
||||||
parse_kallsym_line (const char *line,
|
|
||||||
GArray *table)
|
|
||||||
{
|
|
||||||
char **tokens = g_strsplit_set (line, " \t", -1);
|
|
||||||
|
|
||||||
if (tokens[0] && tokens[1] && tokens[2])
|
|
||||||
{
|
|
||||||
glong address;
|
|
||||||
char *endptr;
|
|
||||||
|
|
||||||
address = strtoul (tokens[0], &endptr, 16);
|
|
||||||
|
|
||||||
if (*endptr == '\0' &&
|
|
||||||
(strcmp (tokens[1], "T") == 0 ||
|
|
||||||
strcmp (tokens[1], "t") == 0))
|
|
||||||
{
|
|
||||||
KernelSymbol sym;
|
|
||||||
|
|
||||||
sym.address = address;
|
|
||||||
sym.name = g_strdup (tokens[2]);
|
|
||||||
|
|
||||||
g_array_append_val (table, sym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_strfreev (tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
parse_kallsyms (const char *kallsyms,
|
|
||||||
GArray *table)
|
|
||||||
{
|
|
||||||
const char *sol;
|
|
||||||
const char *eol;
|
|
||||||
|
|
||||||
sol = kallsyms;
|
|
||||||
eol = strchr (sol, '\n');
|
|
||||||
while (eol)
|
|
||||||
{
|
|
||||||
char *line = g_strndup (sol, eol - sol);
|
|
||||||
|
|
||||||
parse_kallsym_line (line, table);
|
|
||||||
|
|
||||||
g_free (line);
|
|
||||||
|
|
||||||
sol = eol + 1;
|
|
||||||
eol = strchr (sol, '\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (table->len <= 1)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
compare_syms (gconstpointer a, gconstpointer b)
|
|
||||||
{
|
|
||||||
const KernelSymbol *sym_a = a;
|
|
||||||
const KernelSymbol *sym_b = b;
|
|
||||||
|
|
||||||
if (sym_a->address > sym_b->address)
|
|
||||||
return 1;
|
|
||||||
else if (sym_a->address == sym_b->address)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GArray *
|
|
||||||
get_kernel_symbols (void)
|
|
||||||
{
|
|
||||||
static GArray *kernel_syms;
|
|
||||||
static gboolean initialized = FALSE;
|
|
||||||
|
|
||||||
find_kernel_binary();
|
|
||||||
|
|
||||||
if (!initialized)
|
|
||||||
{
|
|
||||||
char *kallsyms;
|
|
||||||
if (g_file_get_contents ("/proc/kallsyms", &kallsyms, NULL, NULL))
|
|
||||||
{
|
|
||||||
if (kallsyms)
|
|
||||||
{
|
|
||||||
kernel_syms = g_array_new (TRUE, TRUE, sizeof (KernelSymbol));
|
|
||||||
|
|
||||||
if (parse_kallsyms (kallsyms, kernel_syms))
|
|
||||||
{
|
|
||||||
g_array_sort (kernel_syms, compare_syms);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_array_free (kernel_syms, TRUE);
|
|
||||||
kernel_syms = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (kallsyms);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!kernel_syms)
|
|
||||||
g_print ("Warning: /proc/kallsyms could not be "
|
|
||||||
"read. Kernel symbols will not be available\n");
|
|
||||||
|
|
||||||
initialized = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return kernel_syms;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
process_is_kernel_address (gulong address)
|
|
||||||
{
|
|
||||||
GArray *ksyms = get_kernel_symbols ();
|
|
||||||
|
|
||||||
if (ksyms &&
|
|
||||||
address >= g_array_index (ksyms, KernelSymbol, 0).address &&
|
|
||||||
address < g_array_index (ksyms, KernelSymbol, ksyms->len - 1).address)
|
|
||||||
{
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static KernelSymbol *
|
|
||||||
do_lookup (KernelSymbol *symbols,
|
|
||||||
gulong address,
|
|
||||||
int first,
|
|
||||||
int last)
|
|
||||||
{
|
|
||||||
if (address >= symbols[last].address)
|
|
||||||
{
|
|
||||||
return &(symbols[last]);
|
|
||||||
}
|
|
||||||
else if (last - first < 3)
|
|
||||||
{
|
|
||||||
while (last >= first)
|
|
||||||
{
|
|
||||||
if (address >= symbols[last].address)
|
|
||||||
return &(symbols[last]);
|
|
||||||
|
|
||||||
last--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int mid = (first + last) / 2;
|
|
||||||
|
|
||||||
if (symbols[mid].address > address)
|
|
||||||
return do_lookup (symbols, address, first, mid);
|
|
||||||
else
|
|
||||||
return do_lookup (symbols, address, mid, last);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
|
||||||
process_lookup_kernel_symbol (gulong address,
|
|
||||||
gulong *offset)
|
|
||||||
{
|
|
||||||
GArray *ksyms = get_kernel_symbols ();
|
|
||||||
KernelSymbol *result;
|
|
||||||
|
|
||||||
if (offset)
|
|
||||||
{
|
|
||||||
/* If we don't have any offset, just return 1, so it doesn't
|
|
||||||
* look like a callback
|
|
||||||
*/
|
|
||||||
*offset = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ksyms->len == 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
result = do_lookup ((KernelSymbol *)ksyms->data, address, 0, ksyms->len - 1);
|
|
||||||
if (result && offset)
|
|
||||||
*offset = address - result->address;
|
|
||||||
|
|
||||||
return result? result->name : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
|
||||||
process_lookup_symbol (Process *process, gulong address, gulong *offset)
|
|
||||||
{
|
|
||||||
static const char *const kernel = "[kernel]";
|
|
||||||
const BinSymbol *result;
|
|
||||||
Map *map = process_locate_map (process, address);
|
|
||||||
|
|
||||||
/* g_print ("addr: %x\n", address); */
|
|
||||||
|
|
||||||
if (offset)
|
|
||||||
{
|
|
||||||
/* If we don't have any offset, just return 1, so it doesn't
|
|
||||||
* look like a callback
|
|
||||||
*/
|
|
||||||
*offset = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address == 0x1)
|
|
||||||
{
|
|
||||||
return kernel;
|
|
||||||
}
|
|
||||||
else if (!map)
|
|
||||||
{
|
|
||||||
if (!process->undefined)
|
|
||||||
{
|
|
||||||
process->undefined =
|
|
||||||
g_strdup_printf ("No map (%s)", process->cmdline);
|
|
||||||
}
|
|
||||||
|
|
||||||
return process->undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (strcmp (map->filename, "/home/ssp/sysprof/sysprof") == 0)
|
|
||||||
{
|
|
||||||
g_print ("YES\n");
|
|
||||||
|
|
||||||
g_print ("map address: %lx\n", map->start);
|
|
||||||
g_print ("map offset: %lx\n", map->offset);
|
|
||||||
g_print ("address before: %lx (%s)\n", address, map->filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_print ("address before: \n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_print ("%s is mapped at %lx + %lx\n", map->filename, map->start, map->offset);
|
|
||||||
g_print ("incoming address: %lx\n", address);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
address -= map->start;
|
|
||||||
address += map->offset;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (strcmp (map->filename, "[vdso]") == 0)
|
|
||||||
{
|
|
||||||
g_print ("address after: %lx\n", address);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!map->bin_file)
|
|
||||||
map->bin_file = bin_file_new (map->filename);
|
|
||||||
|
|
||||||
/* g_print ("%s: start: %p, load: %p\n", */
|
|
||||||
/* map->filename, map->start, bin_file_get_load_address (map->bin_file)); */
|
|
||||||
|
|
||||||
if (!bin_file_check_inode (map->bin_file, map->inode))
|
|
||||||
{
|
|
||||||
/* If the inodes don't match, it's probably because the
|
|
||||||
* file has changed since the process was started. Just return
|
|
||||||
* the undefined symbol in that case.
|
|
||||||
*/
|
|
||||||
address = 0x0;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = bin_file_lookup_symbol (map->bin_file, address);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_print (" ---> %s\n", result->name);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* g_print ("(%x) %x %x name; %s\n", address, map->start,
|
|
||||||
* map->offset, result->name);
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_print ("name: %s (in %s)\n", bin_symbol_get_name (map->bin_file, result), map->filename);
|
|
||||||
g_print (" in addr: %lx\n", address);
|
|
||||||
g_print (" out addr: %lx\n", bin_symbol_get_address (map->bin_file, result));
|
|
||||||
g_print (" map start: %lx\n", map->start);
|
|
||||||
g_print (" map offset: %lx\n", map->offset);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (offset)
|
|
||||||
*offset = address - bin_symbol_get_address (map->bin_file, result);
|
|
||||||
|
|
||||||
return bin_symbol_get_name (map->bin_file, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
process_get_cmdline (Process *process)
|
process_get_cmdline (Process *process)
|
||||||
{
|
{
|
||||||
|
|||||||
478
tracker.c
478
tracker.c
@ -6,6 +6,8 @@
|
|||||||
#include "tracker.h"
|
#include "tracker.h"
|
||||||
#include "stackstash.h"
|
#include "stackstash.h"
|
||||||
#include "binfile.h"
|
#include "binfile.h"
|
||||||
|
#include "elfparser.h"
|
||||||
|
#include "perf_counter.h"
|
||||||
|
|
||||||
typedef struct new_process_t new_process_t;
|
typedef struct new_process_t new_process_t;
|
||||||
typedef struct new_map_t new_map_t;
|
typedef struct new_map_t new_map_t;
|
||||||
@ -54,25 +56,6 @@ struct sample_t
|
|||||||
|
|
||||||
#define DEFAULT_SIZE (1024 * 1024 * 4)
|
#define DEFAULT_SIZE (1024 * 1024 * 4)
|
||||||
|
|
||||||
static void
|
|
||||||
tracker_append (tracker_t *tracker,
|
|
||||||
void *event,
|
|
||||||
int n_bytes)
|
|
||||||
{
|
|
||||||
if (tracker->n_allocated_bytes - tracker->n_event_bytes < n_bytes)
|
|
||||||
{
|
|
||||||
size_t new_size = tracker->n_allocated_bytes * 2;
|
|
||||||
|
|
||||||
tracker->events = g_realloc (tracker->events, new_size);
|
|
||||||
|
|
||||||
tracker->n_allocated_bytes = new_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_assert (tracker->n_allocated_bytes - tracker->n_event_bytes >= n_bytes);
|
|
||||||
|
|
||||||
memcpy (tracker->events + tracker->n_event_bytes, event, n_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char **
|
static char **
|
||||||
get_lines (const char *format, pid_t pid)
|
get_lines (const char *format, pid_t pid)
|
||||||
{
|
{
|
||||||
@ -244,6 +227,27 @@ tracker_free (tracker_t *tracker)
|
|||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
tracker_append (tracker_t *tracker,
|
||||||
|
void *event,
|
||||||
|
int n_bytes)
|
||||||
|
{
|
||||||
|
if (tracker->n_allocated_bytes - tracker->n_event_bytes < n_bytes)
|
||||||
|
{
|
||||||
|
size_t new_size = tracker->n_allocated_bytes * 2;
|
||||||
|
|
||||||
|
tracker->events = g_realloc (tracker->events, new_size);
|
||||||
|
|
||||||
|
tracker->n_allocated_bytes = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert (tracker->n_allocated_bytes - tracker->n_event_bytes >= n_bytes);
|
||||||
|
|
||||||
|
memcpy (tracker->events + tracker->n_event_bytes, event, n_bytes);
|
||||||
|
|
||||||
|
tracker->n_event_bytes += n_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tracker_add_process (tracker_t * tracker,
|
tracker_add_process (tracker_t * tracker,
|
||||||
pid_t pid,
|
pid_t pid,
|
||||||
@ -332,6 +336,8 @@ struct map_t
|
|||||||
struct state_t
|
struct state_t
|
||||||
{
|
{
|
||||||
GHashTable *processes_by_pid;
|
GHashTable *processes_by_pid;
|
||||||
|
GHashTable *unique_comms;
|
||||||
|
GHashTable *unique_symbols;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -427,14 +433,436 @@ state_new (void)
|
|||||||
state_t *state = g_new0 (state_t, 1);
|
state_t *state = g_new0 (state_t, 1);
|
||||||
|
|
||||||
state->processes_by_pid =
|
state->processes_by_pid =
|
||||||
g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, free_process);
|
g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
||||||
|
NULL, free_process);
|
||||||
|
|
||||||
|
state->unique_symbols = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||||
|
state->unique_comms = g_hash_table_new (g_str_hash, g_str_equal);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
typedef struct
|
||||||
process_sample (state_t *tracker, sample_t *sample)
|
|
||||||
{
|
{
|
||||||
|
gulong address;
|
||||||
|
char *name;
|
||||||
|
} kernel_symbol_t;
|
||||||
|
|
||||||
|
static void
|
||||||
|
parse_kallsym_line (const char *line,
|
||||||
|
GArray *table)
|
||||||
|
{
|
||||||
|
char **tokens = g_strsplit_set (line, " \t", -1);
|
||||||
|
|
||||||
|
if (tokens[0] && tokens[1] && tokens[2])
|
||||||
|
{
|
||||||
|
glong address;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
address = strtoul (tokens[0], &endptr, 16);
|
||||||
|
|
||||||
|
if (*endptr == '\0' &&
|
||||||
|
(strcmp (tokens[1], "T") == 0 ||
|
||||||
|
strcmp (tokens[1], "t") == 0))
|
||||||
|
{
|
||||||
|
kernel_symbol_t sym;
|
||||||
|
|
||||||
|
sym.address = address;
|
||||||
|
sym.name = g_strdup (tokens[2]);
|
||||||
|
|
||||||
|
g_array_append_val (table, sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev (tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_kallsyms (const char *kallsyms,
|
||||||
|
GArray *table)
|
||||||
|
{
|
||||||
|
const char *sol;
|
||||||
|
const char *eol;
|
||||||
|
|
||||||
|
sol = kallsyms;
|
||||||
|
eol = strchr (sol, '\n');
|
||||||
|
while (eol)
|
||||||
|
{
|
||||||
|
char *line = g_strndup (sol, eol - sol);
|
||||||
|
|
||||||
|
parse_kallsym_line (line, table);
|
||||||
|
|
||||||
|
g_free (line);
|
||||||
|
|
||||||
|
sol = eol + 1;
|
||||||
|
eol = strchr (sol, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (table->len <= 1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
compare_syms (gconstpointer a, gconstpointer b)
|
||||||
|
{
|
||||||
|
const kernel_symbol_t *sym_a = a;
|
||||||
|
const kernel_symbol_t *sym_b = b;
|
||||||
|
|
||||||
|
if (sym_a->address > sym_b->address)
|
||||||
|
return 1;
|
||||||
|
else if (sym_a->address == sym_b->address)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static kernel_symbol_t *
|
||||||
|
do_lookup (kernel_symbol_t *symbols,
|
||||||
|
gulong address,
|
||||||
|
int first,
|
||||||
|
int last)
|
||||||
|
{
|
||||||
|
if (address >= symbols[last].address)
|
||||||
|
{
|
||||||
|
return &(symbols[last]);
|
||||||
|
}
|
||||||
|
else if (last - first < 3)
|
||||||
|
{
|
||||||
|
while (last >= first)
|
||||||
|
{
|
||||||
|
if (address >= symbols[last].address)
|
||||||
|
return &(symbols[last]);
|
||||||
|
|
||||||
|
last--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int mid = (first + last) / 2;
|
||||||
|
|
||||||
|
if (symbols[mid].address > address)
|
||||||
|
return do_lookup (symbols, address, first, mid);
|
||||||
|
else
|
||||||
|
return do_lookup (symbols, address, mid, last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GArray *
|
||||||
|
get_kernel_symbols (void)
|
||||||
|
{
|
||||||
|
static GArray *kernel_syms;
|
||||||
|
static gboolean initialized = FALSE;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
find_kernel_binary();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
char *kallsyms;
|
||||||
|
if (g_file_get_contents ("/proc/kallsyms", &kallsyms, NULL, NULL))
|
||||||
|
{
|
||||||
|
if (kallsyms)
|
||||||
|
{
|
||||||
|
kernel_syms = g_array_new (TRUE, TRUE, sizeof (kernel_symbol_t));
|
||||||
|
|
||||||
|
if (parse_kallsyms (kallsyms, kernel_syms))
|
||||||
|
{
|
||||||
|
g_array_sort (kernel_syms, compare_syms);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_array_free (kernel_syms, TRUE);
|
||||||
|
kernel_syms = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (kallsyms);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kernel_syms)
|
||||||
|
g_print ("Warning: /proc/kallsyms could not be "
|
||||||
|
"read. Kernel symbols will not be available\n");
|
||||||
|
|
||||||
|
initialized = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kernel_syms;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
lookup_kernel_symbol (gulong address)
|
||||||
|
{
|
||||||
|
kernel_symbol_t *result;
|
||||||
|
GArray *ksyms = get_kernel_symbols ();
|
||||||
|
|
||||||
|
if (ksyms->len == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
result = do_lookup ((kernel_symbol_t *)ksyms->data, address, 0, ksyms->len - 1);
|
||||||
|
|
||||||
|
return result? result->name : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note that 'unique_symbols' is a direct_hash table. Ie., we
|
||||||
|
* rely on the address of symbol strings being different for different
|
||||||
|
* symbols.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
unique_dup (GHashTable *unique_symbols, const char *sym)
|
||||||
|
{
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
result = g_hash_table_lookup (unique_symbols, sym);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
result = elf_demangle (sym);
|
||||||
|
g_hash_table_insert (unique_symbols, (char *)sym, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static map_t *
|
||||||
|
process_locate_map (process_t *process, gulong addr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < process->maps->len; ++i)
|
||||||
|
{
|
||||||
|
map_t *map = process->maps->pdata[i];
|
||||||
|
|
||||||
|
if (addr >= map->start && addr < map->end)
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
lookup_user_symbol (process_t *process, gulong address)
|
||||||
|
{
|
||||||
|
static const char *const kernel = "[kernel]";
|
||||||
|
const BinSymbol *result;
|
||||||
|
map_t *map = process_locate_map (process, address);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
g_print ("addr: %x\n", address);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (address == 0x1)
|
||||||
|
{
|
||||||
|
return kernel;
|
||||||
|
}
|
||||||
|
else if (!map)
|
||||||
|
{
|
||||||
|
if (!process->undefined)
|
||||||
|
{
|
||||||
|
process->undefined =
|
||||||
|
g_strdup_printf ("No map (%s)", process->comm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return process->undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (strcmp (map->filename, "/home/ssp/sysprof/sysprof") == 0)
|
||||||
|
{
|
||||||
|
g_print ("YES\n");
|
||||||
|
|
||||||
|
g_print ("map address: %lx\n", map->start);
|
||||||
|
g_print ("map offset: %lx\n", map->offset);
|
||||||
|
g_print ("address before: %lx (%s)\n", address, map->filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_print ("address before: \n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
g_print ("%s is mapped at %lx + %lx\n", map->filename, map->start, map->offset);
|
||||||
|
g_print ("incoming address: %lx\n", address);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
address -= map->start;
|
||||||
|
address += map->offset;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (strcmp (map->filename, "[vdso]") == 0)
|
||||||
|
{
|
||||||
|
g_print ("address after: %lx\n", address);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!map->bin_file)
|
||||||
|
map->bin_file = bin_file_new (map->filename);
|
||||||
|
|
||||||
|
/* g_print ("%s: start: %p, load: %p\n", */
|
||||||
|
/* map->filename, map->start, bin_file_get_load_address (map->bin_file)); */
|
||||||
|
|
||||||
|
if (!bin_file_check_inode (map->bin_file, map->inode))
|
||||||
|
{
|
||||||
|
/* If the inodes don't match, it's probably because the
|
||||||
|
* file has changed since the process was started. Just return
|
||||||
|
* the undefined symbol in that case.
|
||||||
|
*/
|
||||||
|
address = 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = bin_file_lookup_symbol (map->bin_file, address);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
g_print (" ---> %s\n", result->name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* g_print ("(%x) %x %x name; %s\n", address, map->start,
|
||||||
|
* map->offset, result->name);
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
g_print ("name: %s (in %s)\n", bin_symbol_get_name (map->bin_file, result), map->filename);
|
||||||
|
g_print (" in addr: %lx\n", address);
|
||||||
|
g_print (" out addr: %lx\n", bin_symbol_get_address (map->bin_file, result));
|
||||||
|
g_print (" map start: %lx\n", map->start);
|
||||||
|
g_print (" map offset: %lx\n", map->offset);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return bin_symbol_get_name (map->bin_file, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
lookup_symbol (state_t *state,
|
||||||
|
process_t *process,
|
||||||
|
uint64_t address,
|
||||||
|
gboolean kernel)
|
||||||
|
{
|
||||||
|
const char *sym;
|
||||||
|
|
||||||
|
g_assert (process);
|
||||||
|
|
||||||
|
if (kernel)
|
||||||
|
{
|
||||||
|
sym = lookup_kernel_symbol (address);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sym = lookup_user_symbol (process, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sym)
|
||||||
|
return unique_dup (state->unique_symbols, sym);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct context_info_t context_info_t;
|
||||||
|
struct context_info_t
|
||||||
|
{
|
||||||
|
enum perf_callchain_context context;
|
||||||
|
char name[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
static const context_info_t context_info[] =
|
||||||
|
{
|
||||||
|
{ PERF_CONTEXT_HV, "- - hypervisor - -" },
|
||||||
|
{ PERF_CONTEXT_KERNEL, "- - kernel - -" },
|
||||||
|
{ PERF_CONTEXT_USER, "- - user - - " },
|
||||||
|
{ PERF_CONTEXT_GUEST, "- - guest - - " },
|
||||||
|
{ PERF_CONTEXT_GUEST_KERNEL, "- - guest kernel - -" },
|
||||||
|
{ PERF_CONTEXT_GUEST_USER, "- - guest user - -" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const everything = "[Everything]";
|
||||||
|
|
||||||
|
static const context_info_t *
|
||||||
|
get_context_info (enum perf_callchain_context context)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof (context_info) / sizeof (context_info[0]); ++i)
|
||||||
|
{
|
||||||
|
const context_info_t *info = &context_info[i];
|
||||||
|
|
||||||
|
if (info->context == context)
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
process_sample (state_t *state, StackStash *resolved, sample_t *sample)
|
||||||
|
{
|
||||||
|
const context_info_t *context = NULL;
|
||||||
|
uint64_t *resolved_traces;
|
||||||
|
process_t *process;
|
||||||
|
StackNode *n;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
process = g_hash_table_lookup (
|
||||||
|
state->processes_by_pid, GINT_TO_POINTER (sample->pid));
|
||||||
|
|
||||||
|
if (!process)
|
||||||
|
{
|
||||||
|
g_warning ("sample for unknown process %d\n", sample->pid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = 4;
|
||||||
|
for (n = sample->trace; n != NULL; n = n->parent)
|
||||||
|
len++;
|
||||||
|
|
||||||
|
resolved_traces = g_new (uint64_t, len);
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
for (n = sample->trace; n != NULL; n = n->parent)
|
||||||
|
{
|
||||||
|
uint64_t address = n->data;
|
||||||
|
const context_info_t *new_context;
|
||||||
|
const char *symbol;
|
||||||
|
|
||||||
|
new_context = get_context_info (address);
|
||||||
|
if (new_context)
|
||||||
|
{
|
||||||
|
if (context)
|
||||||
|
symbol = context->name;
|
||||||
|
else
|
||||||
|
symbol = NULL;
|
||||||
|
|
||||||
|
context = new_context;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gboolean kernel = context && context->context == PERF_CONTEXT_KERNEL;
|
||||||
|
|
||||||
|
symbol = lookup_symbol (state, process, address, kernel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol)
|
||||||
|
resolved_traces[len++] = POINTER_TO_U64 (symbol);
|
||||||
|
}
|
||||||
|
char *cmdline;
|
||||||
|
|
||||||
|
cmdline = g_hash_table_lookup (
|
||||||
|
state->unique_comms, process->comm);
|
||||||
|
|
||||||
|
if (!cmdline)
|
||||||
|
{
|
||||||
|
cmdline = g_strdup (process->comm);
|
||||||
|
|
||||||
|
g_hash_table_insert (state->unique_comms, cmdline, cmdline);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved_traces[len++] = POINTER_TO_U64 (cmdline);
|
||||||
|
resolved_traces[len++] = POINTER_TO_U64 (
|
||||||
|
unique_dup (state->unique_symbols, everything));
|
||||||
|
|
||||||
|
stack_stash_add_trace (resolved, resolved_traces, len, 1);
|
||||||
|
|
||||||
|
g_free (resolved_traces);
|
||||||
}
|
}
|
||||||
|
|
||||||
Profile *
|
Profile *
|
||||||
@ -458,17 +886,19 @@ tracker_create_profile (tracker_t *tracker)
|
|||||||
{
|
{
|
||||||
case NEW_PROCESS:
|
case NEW_PROCESS:
|
||||||
create_process (state, (new_process_t *)event);
|
create_process (state, (new_process_t *)event);
|
||||||
|
event += sizeof (new_process_t);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NEW_MAP:
|
case NEW_MAP:
|
||||||
create_map (state, (new_map_t *)event);
|
create_map (state, (new_map_t *)event);
|
||||||
|
event += sizeof (new_map_t);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SAMPLE:
|
case SAMPLE:
|
||||||
process_sample (state, (sample_t *)event);
|
process_sample (state, resolved_stash, (sample_t *)event);
|
||||||
|
event += sizeof (sample_t);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
profile = profile_new (resolved_stash);
|
profile = profile_new (resolved_stash);
|
||||||
|
|||||||
Reference in New Issue
Block a user