summaryrefslogtreecommitdiff
path: root/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'process.c')
-rw-r--r--process.c444
1 files changed, 444 insertions, 0 deletions
diff --git a/process.c b/process.c
new file mode 100644
index 0000000..e0a6fa2
--- /dev/null
+++ b/process.c
@@ -0,0 +1,444 @@
+/* MemProf -- memory profiler and leak detector
+ * Copyright 1999, 2000, 2001, Red Hat, Inc.
+ * Copyright 2002, Kristian Rietveld
+ *
+ * Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright 2004-2005 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
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "process.h"
+#include "binfile.h"
+
+#define PAGE_SIZE (getpagesize())
+
+static GHashTable *processes_by_cmdline;
+static GHashTable *processes_by_pid;
+
+typedef struct Map Map;
+struct Map
+{
+ char * filename;
+ gulong start;
+ gulong end;
+ gulong offset;
+#if 0
+ gboolean do_offset;
+#endif
+
+ BinFile * bin_file;
+};
+
+struct Process
+{
+ char *cmdline;
+
+ GList *maps;
+ GList *bad_pages;
+
+ int pid;
+};
+
+static void
+initialize (void)
+{
+ if (!processes_by_cmdline)
+ {
+ processes_by_cmdline = g_hash_table_new (g_str_hash, g_str_equal);
+ processes_by_pid = g_hash_table_new (g_direct_hash, g_direct_equal);
+ }
+}
+
+static GList *
+read_maps (int pid)
+{
+ char *name = g_strdup_printf ("/proc/%d/maps", pid);
+ char buffer[1024];
+ FILE *in;
+ GList *result = NULL;
+
+ in = fopen (name, "r");
+ if (!in)
+ {
+#if 0
+ g_print ("could not open %d: %s\n", pid, g_strerror (errno));
+#endif
+ g_free (name);
+ return NULL;
+ }
+
+ while (fgets (buffer, sizeof (buffer) - 1, in))
+ {
+ char file[256];
+ int count;
+ gulong start;
+ gulong end;
+ gulong offset;
+
+#if 0
+ g_print ("buffer: %s\n", buffer);
+#endif
+
+ count = sscanf (
+ buffer, "%lx-%lx %*15s %lx %*x:%*x %*u %255s",
+ &start, &end, &offset, file);
+ if (count == 4)
+ {
+ Map *map;
+
+ map = g_new (Map, 1);
+
+ map->filename = g_strdup (file);
+ map->start = start;
+ map->end = end;
+
+ map->offset = offset;
+
+ map->bin_file = NULL;
+
+ result = g_list_prepend (result, map);
+ }
+#if 0
+ else
+ {
+ g_print ("scanf\n");
+ }
+#endif
+ }
+
+ g_free (name);
+ fclose (in);
+ return result;
+}
+
+
+static Process *
+create_process (const char *cmdline, int pid)
+{
+ Process *p;
+
+ p = g_new (Process, 1);
+
+ if (*cmdline != '\0')
+ p->cmdline = g_strdup_printf ("[%s]", cmdline);
+ else
+ p->cmdline = g_strdup_printf ("[pid %d]", pid);
+
+ p->bad_pages = NULL;
+ p->maps = NULL;
+ p->pid = pid;
+
+ g_assert (!g_hash_table_lookup (processes_by_pid, GINT_TO_POINTER (pid)));
+ g_assert (!g_hash_table_lookup (processes_by_cmdline, cmdline));
+
+ g_hash_table_insert (processes_by_pid, GINT_TO_POINTER (pid), p);
+ g_hash_table_insert (processes_by_cmdline, g_strdup (cmdline), p);
+
+ return p;
+}
+
+static Map *
+process_locate_map (Process *process, gulong addr)
+{
+ GList *list;
+
+ for (list = process->maps; list != NULL; list = list->next)
+ {
+ Map *map = list->data;
+
+ if ((addr >= map->start) &&
+ (addr < map->end))
+ {
+ return map;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+process_free_maps (Process *process)
+{
+ GList *list;
+
+ for (list = process->maps; list != NULL; list = list->next)
+ {
+ Map *map = list->data;
+
+ if (map->filename)
+ g_free (map->filename);
+
+ if (map->bin_file)
+ bin_file_free (map->bin_file);
+
+ g_free (map);
+ }
+
+ g_list_free (process->maps);
+}
+
+static gboolean
+process_has_page (Process *process, gulong addr)
+{
+ if (process_locate_map (process, addr))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+void
+process_ensure_map (Process *process, int pid, gulong addr)
+{
+ /* Round down to closest page */
+
+ addr = (addr - addr % PAGE_SIZE);
+
+ if (process_has_page (process, addr))
+ return;
+
+ if (g_list_find (process->bad_pages, (gpointer)addr))
+ return;
+
+ /* a map containing addr was not found */
+ if (process->maps)
+ process_free_maps (process);
+
+ process->maps = read_maps (pid);
+
+ if (!process_has_page (process, addr))
+ {
+#if 0
+ g_print ("Bad page: %p\n", addr);
+#endif
+ process->bad_pages = g_list_prepend (process->bad_pages, (gpointer)addr);
+ }
+}
+
+static gboolean
+do_idle_free (gpointer d)
+{
+ g_free (d);
+ return FALSE;
+}
+
+static char *
+idle_free (char *d)
+{
+ g_idle_add (do_idle_free, d);
+ return d;
+}
+
+static char *
+get_cmdline (int pid)
+{
+ char *cmdline;
+ char *filename = idle_free (g_strdup_printf ("/proc/%d/cmdline", pid));
+
+ if (g_file_get_contents (filename, &cmdline, NULL, NULL))
+ {
+ if (*cmdline == '\0')
+ {
+ g_free (cmdline);
+ return NULL;
+ }
+ return cmdline;
+ }
+
+ return NULL;
+}
+
+static char *
+get_statname (int pid)
+{
+ char *stat;
+ char *filename = idle_free (g_strdup_printf ("/proc/%d/stat", pid));
+
+#if 0
+ g_print ("stat %d\n", pid);
+#endif
+
+ if (g_file_get_contents (filename, &stat, NULL, NULL))
+ {
+ char result[200];
+
+ idle_free (stat);
+
+ if (sscanf (stat, "%*d %200s %*s", result) == 1)
+ return g_strndup (result, 200);
+ }
+#if 0
+ g_print ("return null\n");
+#endif
+
+ return NULL;
+}
+
+static char *
+get_pidname (int pid)
+{
+ if (pid == -1)
+ return g_strdup_printf ("kernel");
+ else
+ return g_strdup_printf ("pid %d", pid);
+}
+
+static char *
+get_name (int pid)
+{
+ char *cmdline = NULL;
+
+ if ((cmdline = get_cmdline (pid)))
+ return cmdline;
+
+ if ((cmdline = get_statname (pid)))
+ return cmdline;
+
+ return get_pidname (pid);
+}
+
+Process *
+process_get_from_pid (int pid)
+{
+ Process *p;
+ gchar *cmdline = NULL;
+
+ initialize();
+
+ p = g_hash_table_lookup (processes_by_pid, GINT_TO_POINTER (pid));
+
+ if (p)
+ return p;
+
+ cmdline = get_name (pid);
+
+ p = g_hash_table_lookup (processes_by_cmdline, cmdline);
+ if (p)
+ return p;
+ else
+ return create_process (idle_free (cmdline), pid);
+}
+
+const Symbol *
+process_lookup_symbol (Process *process, gulong address)
+{
+ static Symbol undefined;
+ const Symbol *result;
+ static Symbol kernel;
+ Map *map = process_locate_map (process, address);
+
+/* g_print ("addr: %x\n", address); */
+
+ if (address == 0x1)
+ {
+ kernel.name = "in kernel";
+ kernel.address = 0x0001337;
+ return &kernel;
+ }
+ else if (!map)
+ {
+ if (undefined.name)
+ g_free (undefined.name);
+ undefined.name = g_strdup_printf ("??? %s", process->cmdline);
+ undefined.address = 0xBABE0001;
+
+#if 0
+ g_print ("no map for %p (%s)\n", address, process->cmdline);
+#endif
+ return &undefined;
+ }
+
+#if 0
+ g_print ("has map: %s\n", process->cmdline);
+#endif
+
+#if 0
+ g_print ("has map: %s\n", process->cmdline);
+#endif
+
+/* if (map->do_offset) */
+/* address -= map->start; */
+
+#if 0
+ /* convert address to file coordinates */
+ g_print ("looking up %p ", address);
+#endif
+
+ address -= map->start;
+ address += map->offset;
+
+ 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)); */
+
+ 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); */
+
+ return result;
+}
+
+const char *
+process_get_cmdline (Process *process)
+{
+ return process->cmdline;
+}
+
+static void
+free_process (gpointer key, gpointer value, gpointer data)
+{
+ char *cmdline = key;
+ Process *process = value;
+
+#if 0
+ g_print ("freeing: %p\n", process);
+ memset (process, '\0', sizeof (Process));
+#endif
+ g_free (process->cmdline);
+#if 0
+ process->cmdline = "You are using free()'d memory";
+#endif
+ process_free_maps (process);
+ g_list_free (process->bad_pages);
+ g_free (cmdline);
+
+ g_free (process);
+}
+
+void
+process_flush_caches (void)
+{
+ if (!processes_by_cmdline)
+ return;
+ g_hash_table_foreach (processes_by_cmdline, free_process, NULL);
+
+ g_hash_table_destroy (processes_by_cmdline);
+ g_hash_table_destroy (processes_by_pid);
+
+ processes_by_cmdline = NULL;
+ processes_by_pid = NULL;
+}