summaryrefslogtreecommitdiff
path: root/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'process.c')
-rw-r--r--process.c171
1 files changed, 134 insertions, 37 deletions
diff --git a/process.c b/process.c
index 2baab9c..11a2074 100644
--- a/process.c
+++ b/process.c
@@ -3,7 +3,7 @@
* Copyright 2002, Kristian Rietveld
*
* Sysprof -- Sampling, systemwide CPU profiler
- * Copyright 2004-2005 Soeren Sandmann
+ * Copyright 2004-2007 Soeren Sandmann
*
* 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
@@ -285,7 +285,7 @@ static int
page_size (void)
{
static int page_size;
- static gboolean has_page_size;
+ static gboolean has_page_size = FALSE;
if (!has_page_size)
{
@@ -489,9 +489,15 @@ find_kernel_binary (void)
return binary;
}
+typedef struct
+{
+ gulong address;
+ char *name;
+} KernelSymbol;
+
static void
parse_kallsym_line (const char *line,
- GHashTable *table)
+ GArray *table)
{
char **tokens = g_strsplit_set (line, " \t", -1);
@@ -502,19 +508,25 @@ parse_kallsym_line (const char *line,
address = strtoul (tokens[0], &endptr, 16);
- if (*endptr == '\0')
+ if (*endptr == '\0' &&
+ (strcmp (tokens[1], "T") == 0 ||
+ strcmp (tokens[1], "t") == 0))
{
- g_hash_table_insert (
- table, GUINT_TO_POINTER (address), g_strdup (tokens[2]));
+ KernelSymbol sym;
+
+ sym.address = address;
+ sym.name = g_strdup (tokens[2]);
+
+ g_array_append_val (table, sym);
}
}
g_strfreev (tokens);
}
-static void
+static gboolean
parse_kallsyms (const char *kallsyms,
- GHashTable *table)
+ GArray *table)
{
const char *sol;
const char *eol;
@@ -532,47 +544,133 @@ parse_kallsyms (const char *kallsyms,
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 GHashTable *
+static GArray *
get_kernel_symbols (void)
{
- static gboolean read_symbols = FALSE;
- static GHashTable *kernel_syms;
+ static GArray *kernel_syms;
+ static gboolean initialized = FALSE;
- if (!read_symbols)
+ if (!initialized)
{
char *kallsyms;
- g_file_get_contents ("/proc/kallsyms", &kallsyms, NULL, NULL);
-
- if (kallsyms)
+ if (g_file_get_contents ("/proc/kallsyms", &kallsyms, NULL, NULL))
{
- kernel_syms = g_hash_table_new_full (g_direct_hash, g_direct_equal,
- NULL, g_free);
-
- parse_kallsyms (kallsyms, kernel_syms);
-
- g_free (kallsyms);
- g_hash_table_destroy (kernel_syms);
+ 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;
+ }
+ }
}
- read_symbols = TRUE;
+ if (!kernel_syms)
+ g_print ("Warning: /proc/kallsyms could not be "
+ "read. Kernel symbols will not be available\n");
+
+ initialized = TRUE;
}
- return NULL;
+ 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);
+ }
}
-static const char *
-lookup_kernel_symbol (gulong address)
+const char *
+process_lookup_kernel_symbol (gulong address,
+ gulong *offset)
{
- static const char *const kernel = "In kernel";
+ GArray *ksyms = get_kernel_symbols ();
+ KernelSymbol *result;
- return kernel;
+ 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)
{
+ static const char *const kernel = "kernel";
const BinSymbol *result;
Map *map = process_locate_map (process, address);
@@ -580,12 +678,16 @@ process_lookup_symbol (Process *process, gulong address)
if (address == 0x1)
{
- get_kernel_symbols ();
-
- return lookup_kernel_symbol (address);
+ return kernel;
}
else if (!map)
{
+ gulong offset;
+ const char *res = process_lookup_kernel_symbol (address, &offset);
+
+ if (res && offset != 0)
+ return res;
+
if (!process->undefined)
{
process->undefined =
@@ -615,11 +717,6 @@ process_lookup_symbol (Process *process, gulong address)
address -= map->start;
address += map->offset;
-
-#if 0
- address -= map->start;
- address += map->offset;
-#endif
#if 0
if (strcmp (map->filename, "[vdso]") == 0)
@@ -637,7 +734,7 @@ process_lookup_symbol (Process *process, gulong address)
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 started. Just return
+ * file has changed since the process was started. Just return
* the undefined symbol in that case.
*/
address = 0x0;