move source around in preparation to land sysprof2

This commit is contained in:
Christian Hergert
2016-04-13 05:10:31 -07:00
parent 46f087522d
commit 2ea0287f89
38 changed files with 0 additions and 0 deletions

530
lib/binfile.c Normal file
View File

@ -0,0 +1,530 @@
/* 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, 2006, 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
* 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.
*/
/* Most interesting code in this file is lifted from bfdutils.c
* and process.c from Memprof,
*/
#include "config.h"
#include <glib.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include "binfile.h"
#include "elfparser.h"
#include "util.h"
struct bin_file_t
{
int ref_count;
GList * elf_files;
char * filename;
char * undefined_name;
gulong text_offset;
gboolean inode_check;
ino_t inode;
};
static ino_t
read_inode (const char *filename)
{
struct stat statbuf;
if (strcmp (filename, "[vdso]") == 0)
return (ino_t)0;
if (stat (filename, &statbuf) < 0)
return (ino_t)-1;
return statbuf.st_ino;
}
static gboolean
already_warned (const char *name)
{
static GPtrArray *warnings;
int i;
if (!warnings)
warnings = g_ptr_array_new ();
for (i = 0; i < warnings->len; ++i)
{
if (strcmp (warnings->pdata[i], name) == 0)
return TRUE;
}
g_ptr_array_add (warnings, g_strdup (name));
return FALSE;
}
static const char *const debug_file_directory = DEBUGDIR;
static ElfParser *
get_build_id_file (ElfParser *elf)
{
const char *build_id;
GList *tries = NULL, *list;
char *init, *rest;
ElfParser *result = NULL;
char *tmp;
build_id = elf_parser_get_build_id (elf);
if (!build_id)
return NULL;
if (strlen (build_id) < 4)
return NULL;
init = g_strndup (build_id, 2);
rest = g_strdup_printf ("%s%s", build_id + 2, ".debug");
tmp = g_build_filename (
"/usr", "lib", "debug", ".build-id", init, rest, NULL);
tries = g_list_append (tries, tmp);
tmp = g_build_filename (
debug_file_directory, ".build-id", init, rest, NULL);
tries = g_list_append (tries, tmp);
for (list = tries; list != NULL; list = list->next)
{
char *name = list->data;
ElfParser *parser = elf_parser_new (name, NULL);
if (parser)
{
const char *file_id = elf_parser_get_build_id (parser);
if (file_id && strcmp (build_id, file_id) == 0)
{
result = parser;
break;
}
elf_parser_free (parser);
}
}
g_list_foreach (tries, (GFunc)g_free, NULL);
g_list_free (tries);
g_free (init);
g_free (rest);
return result;
}
static ElfParser *
get_debuglink_file (ElfParser *elf,
const char *filename,
char **new_name)
{
#define N_TRIES 4
const char *basename;
char *dir;
guint32 crc32;
GList *tries = NULL, *list;
ElfParser *result = NULL;
const char *build_id;
if (!elf)
return NULL;
basename = elf_parser_get_debug_link (elf, &crc32);
build_id = elf_parser_get_build_id (elf);
#if 0
g_print (" debug link for %s is %s\n", filename, basename);
#endif
if (!basename)
return NULL;
dir = g_path_get_dirname (filename);
tries = g_list_append (tries, g_build_filename (dir, basename, NULL));
tries = g_list_append (tries, g_build_filename (dir, ".debug", basename, NULL));
tries = g_list_append (tries, g_build_filename ("/usr", "lib", "debug", dir, basename, NULL));
tries = g_list_append (tries, g_build_filename (debug_file_directory, dir, basename, NULL));
for (list = tries; list != NULL; list = list->next)
{
const char *name = list->data;
ElfParser *parser = elf_parser_new (name, NULL);
guint32 file_crc;
const char *file_build_id;
if (parser)
{
/* If both files have build ids, and they don't match,
* there is no point computing a CRC32 that we know
* will fail
*/
file_build_id = elf_parser_get_build_id (parser);
if (build_id && file_build_id && strcmp (build_id, file_build_id) != 0)
goto skip;
file_crc = elf_parser_get_crc32 (parser);
if (file_crc == crc32)
{
result = parser;
*new_name = g_strdup (name);
break;
}
else
{
if (!already_warned (name))
{
g_print ("warning: %s has wrong crc %x, %s has crc %x)\n",
name, file_crc, filename, crc32);
}
}
skip:
elf_parser_free (parser);
}
}
g_free (dir);
g_list_foreach (tries, (GFunc)g_free, NULL);
g_list_free (tries);
return result;
}
static GList *
get_debug_binaries (GList *files,
ElfParser *elf,
const char *filename)
{
ElfParser *build_id_file;
GHashTable *seen_names;
GList *free_us = NULL;
build_id_file = get_build_id_file (elf);
if (build_id_file)
return g_list_prepend (files, build_id_file);
/* .gnu_debuglink is actually a chain of debuglinks, and
* there have been real-world cases where following it was
* necessary to get useful debug information.
*/
seen_names = g_hash_table_new (g_str_hash, g_str_equal);
while (elf)
{
char *debug_name;
if (g_hash_table_lookup (seen_names, filename))
break;
g_hash_table_insert (seen_names, (char *)filename, (char *)filename);
elf = get_debuglink_file (elf, filename, &debug_name);
if (elf)
{
files = g_list_prepend (files, elf);
free_us = g_list_prepend (free_us, debug_name);
filename = debug_name;
}
}
g_list_foreach (free_us, (GFunc)g_free, NULL);
g_list_free (free_us);
g_hash_table_destroy (seen_names);
return files;
}
static char **
get_lines (const char *format, pid_t pid)
{
char *filename = g_strdup_printf (format, pid);
char **result = NULL;
char *contents;
if (g_file_get_contents (filename, &contents, NULL, NULL))
{
result = g_strsplit (contents, "\n", -1);
g_free (contents);
}
g_free (filename);
return result;
}
static const uint8_t *
get_vdso_bytes (size_t *length)
{
static const uint8_t *bytes = NULL;
static size_t n_bytes = 0;
static gboolean has_data;
if (!has_data)
{
char **lines = get_lines ("/proc/%d/maps", getpid());
int i;
for (i = 0; lines[i] != NULL; ++i)
{
char file[256];
gulong start;
gulong end;
int count = sscanf (
lines[i], "%lx-%lx %*15s %*x %*x:%*x %*u %255s",
&start, &end, file);
if (count == 3 && strcmp (file, "[vdso]") == 0)
{
n_bytes = end - start;
/* Dup the memory here so that valgrind will only
* report one 1 byte invalid read instead of
* a ton when the elf parser scans the vdso
*
* The reason we get a spurious invalid read from
* valgrind is that we are getting the address directly
* from /proc/maps, and valgrind knows that its mmap()
* wrapper never returned that address. But since it
* is a legal mapping, it is legal to read it.
*/
bytes = g_memdup ((uint8_t *)start, n_bytes);
has_data = TRUE;
}
}
}
if (length)
*length = n_bytes;
return bytes;
}
bin_file_t *
bin_file_new (const char *filename)
{
ElfParser *elf = NULL;
bin_file_t *bf;
bf = g_new0 (bin_file_t, 1);
bf->inode_check = FALSE;
bf->filename = g_strdup (filename);
bf->undefined_name = g_strdup_printf ("In file %s", filename);
bf->ref_count = 1;
bf->elf_files = NULL;
if (strcmp (filename, "[vdso]") == 0)
{
const guint8 *vdso_bytes;
gsize length;
vdso_bytes = get_vdso_bytes (&length);
if (vdso_bytes)
elf = elf_parser_new_from_data (vdso_bytes, length);
}
else
{
elf = elf_parser_new (filename, NULL);
}
if (elf)
{
/* We need the text offset of the actual binary, not the
* (potential) debug binaries
*/
bf->text_offset = elf_parser_get_text_offset (elf);
bf->elf_files = get_debug_binaries (bf->elf_files, elf, filename);
bf->elf_files = g_list_append (bf->elf_files, elf);
bf->inode = read_inode (filename);
}
return bf;
}
void
bin_file_free (bin_file_t *bin_file)
{
if (--bin_file->ref_count == 0)
{
g_list_foreach (bin_file->elf_files, (GFunc)elf_parser_free, NULL);
g_list_free (bin_file->elf_files);
g_free (bin_file->filename);
g_free (bin_file->undefined_name);
g_free (bin_file);
}
}
const bin_symbol_t *
bin_file_lookup_symbol (bin_file_t *bin_file,
gulong address)
{
GList *list;
#if 0
g_print ("-=-=-=- \n");
g_print ("bin file lookup lookup %d\n", address);
#endif
address -= bin_file->text_offset;
#if 0
g_print ("lookup %d in %s\n", address, bin_file->filename);
#endif
for (list = bin_file->elf_files; list != NULL; list = list->next)
{
ElfParser *elf = list->data;
const ElfSym *sym = elf_parser_lookup_symbol (elf, address);
if (sym)
{
#if 0
g_print ("found %lx => %s\n", address,
bin_symbol_get_name (bin_file, sym));
#endif
return (const bin_symbol_t *)sym;
}
}
#if 0
g_print ("%lx undefined in %s (textoffset %x)\n",
address + bin_file->text_offset,
bin_file->filename,
bin_file->text_offset);
#endif
return (const bin_symbol_t *)bin_file->undefined_name;
}
gboolean
bin_file_check_inode (bin_file_t *bin_file,
ino_t inode)
{
if (bin_file->inode == inode)
return TRUE;
if (!bin_file->elf_files)
return FALSE;
if (!bin_file->inode_check)
{
g_print ("warning: Inode mismatch for %s (disk: "FMT64", memory: "FMT64")\n",
bin_file->filename,
(guint64)bin_file->inode, (guint64)inode);
bin_file->inode_check = TRUE;
}
return FALSE;
}
static const ElfSym *
get_elf_sym (bin_file_t *file,
const bin_symbol_t *symbol,
ElfParser **elf_ret)
{
GList *list;
for (list = file->elf_files; list != NULL; list = list->next)
{
const ElfSym *sym = (const ElfSym *)symbol;
ElfParser *elf = list->data;
if (elf_parser_owns_symbol (elf, sym))
{
*elf_ret = elf;
return sym;
}
}
g_critical ("Internal error: unrecognized symbol pointer");
*elf_ret = NULL;
return NULL;
}
const char *
bin_symbol_get_name (bin_file_t *file,
const bin_symbol_t *symbol)
{
if (file->undefined_name == (char *)symbol)
{
return file->undefined_name;
}
else
{
ElfParser *elf;
const ElfSym *sym;
sym = get_elf_sym (file, symbol, &elf);
return elf_parser_get_sym_name (elf, sym);
}
}
gulong
bin_symbol_get_address (bin_file_t *file,
const bin_symbol_t *symbol)
{
if (file->undefined_name == (char *)symbol)
{
return 0x0;
}
else
{
ElfParser *elf;
const ElfSym *sym;
sym = get_elf_sym (file, symbol, &elf);
return elf_parser_get_sym_address (elf, sym);
}
}

46
lib/binfile.h Normal file
View File

@ -0,0 +1,46 @@
/* MemProf -- memory profiler and leak detector
* Copyright 1999, 2000, 2001, Red Hat, Inc.
* Copyright 2002, Kristian Rietveld
*
* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* 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.
*/
#ifndef BIN_FILE_H
#define BIN_FILE_H
#include <glib.h>
#include <sys/types.h>
typedef struct bin_file_t bin_file_t;
typedef struct bin_symbol_t bin_symbol_t;
/* Binary File */
bin_file_t * bin_file_new (const char *filename);
void bin_file_free (bin_file_t *bin_file);
const bin_symbol_t *bin_file_lookup_symbol (bin_file_t *bin_file,
gulong address);
gboolean bin_file_check_inode (bin_file_t *bin_file,
ino_t inode);
const char * bin_symbol_get_name (bin_file_t *bin_file,
const bin_symbol_t *symbol);
gulong bin_symbol_get_address (bin_file_t *bin_file,
const bin_symbol_t *symbol);
#endif

806
lib/collector.c Normal file
View File

@ -0,0 +1,806 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* 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 <stdint.h>
#include <glib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <string.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <errno.h>
#include "stackstash.h"
#include "collector.h"
#include "watch.h"
#include "elfparser.h"
#include "tracker.h"
#include <linux/perf_event.h>
#include "util.h"
#define d_print(...)
#define N_PAGES 32 /* Number of pages in the ringbuffer */
#define N_WAKEUP_EVENTS 149
typedef struct counter_t counter_t;
typedef struct sample_event_t sample_event_t;
typedef struct mmap_event_t mmap_event_t;
typedef struct comm_event_t comm_event_t;
typedef struct exit_event_t exit_event_t;
typedef struct fork_event_t fork_event_t;
typedef union counter_event_t counter_event_t;
static void process_event (Collector *collector,
counter_t *counter,
counter_event_t *event);
struct counter_t
{
Collector * collector;
int fd;
struct perf_event_mmap_page * mmap_page;
uint8_t * data;
uint64_t tail;
int cpu;
};
struct sample_event_t
{
struct perf_event_header header;
uint64_t ip;
uint32_t pid, tid;
uint64_t n_ips;
uint64_t ips[1];
};
struct comm_event_t
{
struct perf_event_header header;
uint32_t pid, tid;
char comm[1];
};
struct mmap_event_t
{
struct perf_event_header header;
uint32_t pid, tid;
uint64_t addr;
uint64_t len;
uint64_t pgoff;
char filename[1];
};
struct fork_event_t
{
struct perf_event_header header;
uint32_t pid, ppid;
uint32_t tid, ptid;
};
struct exit_event_t
{
struct perf_event_header header;
uint32_t pid, ppid;
uint32_t tid, ptid;
};
union counter_event_t
{
struct perf_event_header header;
mmap_event_t mmap;
comm_event_t comm;
sample_event_t sample;
fork_event_t fork;
exit_event_t exit;
};
struct Collector
{
CollectorFunc callback;
gpointer data;
tracker_t * tracker;
GTimeVal latest_reset;
int prev_samples;
int n_samples;
GList * counters;
gboolean use_hw_counters;
};
static int
get_n_cpus (void)
{
return sysconf (_SC_NPROCESSORS_ONLN);
}
static int
sysprof_perf_counter_open (struct perf_event_attr *attr,
pid_t pid,
int cpu,
int group_fd,
unsigned long flags)
{
#ifndef __NR_perf_counter_open
#if defined(__i386__)
#define __NR_perf_counter_open 336
#elif defined(__x86_64__)
#define __NR_perf_counter_open 298
#elif defined(__arm__)
#define __NR_perf_counter_open 364
#elif defined(__bfin__)
#define __NR_perf_counter_open 369
#elif defined(__frv__)
#define __NR_perf_counter_open 336
#elif defined(__m68k__)
#define __NR_perf_counter_open 332
#elif defined(__MICROBLAZE__)
#define __NR_perf_counter_open 366
#elif defined(__mips__) && defined(_ABIO32)
#define __NR_perf_counter_open 4333
#elif defined(__mips__) && defined(_ABIN32)
#define __NR_perf_counter_open 6296
#elif defined(__mips__) && defined(_ABI64)
#define __NR_perf_counter_open 5292
#elif defined(__mn10300__)
#define __NR_perf_counter_open 337
#elif defined(__hppa__)
#define __NR_perf_counter_open 318
#elif defined(__powerpc__) || defined(__powerpc64__)
#define __NR_perf_counter_open 319
#elif defined(__s390__)
#define __NR_perf_counter_open 331
#elif defined(__sh__) && (!defined(__SH5__) || __SH5__ == 32)
#define __NR_perf_counter_open 336
#elif defined(__sh__) && defined(__SH5__) && __SH5__ == 64
#define __NR_perf_counter_open 364
#elif defined(__sparc__) || defined(__sparc64__)
#define __NR_perf_counter_open 327
#endif
#endif
attr->size = sizeof(*attr);
return syscall (__NR_perf_counter_open, attr, pid, cpu, group_fd, flags);
}
static double
timeval_to_ms (const GTimeVal *timeval)
{
return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0;
}
static double
time_diff (const GTimeVal *first,
const GTimeVal *second)
{
double first_ms = timeval_to_ms (first);
double second_ms = timeval_to_ms (second);
return first_ms - second_ms;
}
#define RESET_DEAD_PERIOD 250
static gboolean
in_dead_period (Collector *collector)
{
GTimeVal now;
double diff;
g_get_current_time (&now);
diff = time_diff (&now, &collector->latest_reset);
if (diff >= 0.0 && diff < RESET_DEAD_PERIOD)
return TRUE;
return FALSE;
}
static int
get_page_size (void)
{
static int page_size;
static gboolean has_page_size = FALSE;
if (!has_page_size)
{
page_size = getpagesize();
has_page_size = TRUE;
}
return page_size;
}
static void
on_read (gpointer data)
{
counter_t *counter = data;
int mask = (N_PAGES * get_page_size() - 1);
int n_bytes = mask + 1;
gboolean skip_samples;
Collector *collector;
uint64_t head, tail;
collector = counter->collector;
tail = counter->tail;
head = counter->mmap_page->data_head;
rmb();
if (head < tail)
{
g_warning ("sysprof fails at ring buffers (head "FMT64", tail "FMT64"\n", head, tail);
tail = head;
}
#if 0
/* Verify that the double mapping works */
x = g_random_int() & mask;
g_assert (*(counter->data + x) == *(counter->data + x + n_bytes));
#endif
skip_samples = in_dead_period (collector);
#if 0
g_print ("n bytes %d\n", head - tail);
#endif
while (head - tail >= sizeof (struct perf_event_header))
{
struct perf_event_header *header;
guint8 buffer[4096];
guint8 *free_me;
free_me = NULL;
/* Note that:
*
* - perf events are a multiple of 64 bits
* - the perf event header is 64 bits
* - the data area is a multiple of 64 bits
*
* which means there will always be space for one header, which means we
* can safely dereference the size field.
*/
header = (struct perf_event_header *)(counter->data + (tail & mask));
if (header->size > head - tail)
{
/* The kernel did not generate a complete event.
* I don't think that can happen, but we may as well
* be paranoid.
*/
break;
}
if (counter->data + (tail & mask) + header->size > counter->data + n_bytes)
{
int n_before, n_after;
guint8 *b;
if (header->size > sizeof (buffer))
free_me = b = g_malloc (header->size);
else
b = buffer;
n_after = (tail & mask) + header->size - n_bytes;
n_before = header->size - n_after;
memcpy (b, counter->data + (tail & mask), n_before);
memcpy (b + n_before, counter->data, n_after);
header = (struct perf_event_header *)b;
}
if (!skip_samples || header->type != PERF_RECORD_SAMPLE)
{
if (header->type == PERF_RECORD_SAMPLE)
collector->n_samples++;
process_event (collector, counter, (counter_event_t *)header);
}
if (free_me)
g_free (free_me);
tail += header->size;
}
counter->tail = tail;
counter->mmap_page->data_tail = tail;
if (collector->callback)
{
if (collector->n_samples - collector->prev_samples >= N_WAKEUP_EVENTS)
{
gboolean first_sample = collector->prev_samples == 0;
collector->callback (first_sample, collector->data);
collector->prev_samples = collector->n_samples;
}
}
}
static void *
fail (GError **err, const char *what)
{
g_set_error (err, COLLECTOR_ERROR, COLLECTOR_ERROR_FAILED,
"%s: %s", what, g_strerror (errno));
return NULL;
}
static void *
map_buffer (counter_t *counter, GError **err)
{
int n_bytes = N_PAGES * get_page_size();
void *address;
address = mmap (NULL, n_bytes + get_page_size(), PROT_READ | PROT_WRITE, MAP_SHARED, counter->fd, 0);
if (address == MAP_FAILED)
return fail (err, "mmap");
return address;
}
static gboolean
counter_set_output (counter_t *counter, int output)
{
return ioctl (counter->fd, PERF_EVENT_IOC_SET_OUTPUT, output) == 0;
}
static void
counter_enable (counter_t *counter)
{
ioctl (counter->fd, PERF_EVENT_IOC_ENABLE);
}
static void
counter_disable (counter_t *counter)
{
d_print ("disable\n");
ioctl (counter->fd, PERF_EVENT_IOC_DISABLE);
}
static counter_t *
counter_new (Collector *collector,
pid_t pid,
int cpu,
counter_t *output,
GError **err)
{
struct perf_event_attr attr;
counter_t *counter;
int fd;
counter = g_new (counter_t, 1);
memset (&attr, 0, sizeof (attr));
attr.type = PERF_TYPE_HARDWARE;
attr.config = PERF_COUNT_HW_CPU_CYCLES;
attr.sample_period = 1200000 ; /* In number of clock cycles -
* FIXME: consider using frequency instead
*/
attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_CALLCHAIN;
attr.wakeup_events = N_WAKEUP_EVENTS;
attr.disabled = TRUE;
attr.mmap = 1;
attr.comm = 1;
attr.task = 1;
attr.exclude_idle = 1;
if (!collector->use_hw_counters || (fd = sysprof_perf_counter_open (&attr, pid, cpu, -1, 0)) < 0)
{
attr.type = PERF_TYPE_SOFTWARE;
attr.config = PERF_COUNT_SW_CPU_CLOCK;
attr.sample_period = 1000000;
fd = sysprof_perf_counter_open (&attr, pid, cpu, -1, 0);
}
if (fd < 0)
return fail (err, "Could not open performance counter");
counter->collector = collector;
counter->fd = fd;
counter->cpu = cpu;
if (output && counter_set_output (counter, output->fd))
{
counter->mmap_page = NULL;
counter->data = NULL;
counter->tail = 0;
}
else
{
counter->mmap_page = map_buffer (counter, err);
if (!counter->mmap_page || counter->mmap_page == MAP_FAILED)
return NULL;
counter->data = (uint8_t *)counter->mmap_page + get_page_size ();
counter->tail = 0;
fd_add_watch (fd, counter);
fd_set_read_callback (fd, on_read);
}
return counter;
}
static void
counter_free (counter_t *counter)
{
d_print ("munmap\n");
munmap (counter->mmap_page, (N_PAGES + 1) * get_page_size());
fd_remove_watch (counter->fd);
close (counter->fd);
g_free (counter);
}
/*
* Collector
*/
static void
enable_counters (Collector *collector)
{
GList *list;
d_print ("enable\n");
for (list = collector->counters; list != NULL; list = list->next)
{
counter_t *counter = list->data;
counter_enable (counter);
}
}
static void
disable_counters (Collector *collector)
{
GList *list;
d_print ("disable\n");
for (list = collector->counters; list != NULL; list = list->next)
{
counter_t *counter = list->data;
counter_disable (counter);
}
}
void
collector_reset (Collector *collector)
{
/* Disable the counters so that we won't track
* the activity of tracker_free()/tracker_new()
*
* They will still record fork/mmap/etc. so
* we can keep an accurate log of process creation
*/
if (collector->counters)
{
d_print ("disable counters\n");
disable_counters (collector);
}
if (collector->tracker)
{
tracker_free (collector->tracker);
collector->tracker = tracker_new ();
}
collector->n_samples = 0;
collector->prev_samples = 0;
g_get_current_time (&collector->latest_reset);
if (collector->counters)
{
d_print ("enable counters\n");
enable_counters (collector);
}
}
/* callback is called whenever a new sample arrives */
Collector *
collector_new (gboolean use_hw_counters,
CollectorFunc callback,
gpointer data)
{
Collector *collector = g_new0 (Collector, 1);
collector->callback = callback;
collector->data = data;
collector->tracker = NULL;
collector->use_hw_counters = use_hw_counters;
collector_reset (collector);
return collector;
}
static void
process_mmap (Collector *collector, mmap_event_t *mmap)
{
tracker_add_map (collector->tracker,
mmap->pid,
mmap->addr,
mmap->addr + mmap->len,
mmap->pgoff,
0, /* inode */
mmap->filename);
}
static void
process_comm (Collector *collector, comm_event_t *comm)
{
d_print ("pid, tid: %d %d", comm->pid, comm->tid);
tracker_add_process (collector->tracker,
comm->pid,
comm->comm);
}
static void
process_fork (Collector *collector, fork_event_t *fork)
{
d_print ("ppid: %d pid: %d ptid: %d tid %d\n",
fork->ppid, fork->pid, fork->ptid, fork->tid);
tracker_add_fork (collector->tracker, fork->ppid, fork->pid);
}
static void
process_exit (Collector *collector, exit_event_t *exit)
{
d_print ("for %d %d", exit->pid, exit->tid);
tracker_add_exit (collector->tracker, exit->pid);
}
static void
process_sample (Collector *collector,
sample_event_t *sample)
{
uint64_t *ips;
int n_ips;
d_print ("pid, tid: %d %d", sample->pid, sample->tid);
if (sample->n_ips == 0)
{
uint64_t trace[3];
if (sample->header.misc & PERF_RECORD_MISC_KERNEL)
{
trace[0] = PERF_CONTEXT_KERNEL;
trace[1] = sample->ip;
trace[2] = PERF_CONTEXT_USER;
ips = trace;
n_ips = 3;
}
else
{
trace[0] = PERF_CONTEXT_USER;
trace[1] = sample->ip;
ips = trace;
n_ips = 2;
}
}
else
{
ips = sample->ips;
n_ips = sample->n_ips;
}
tracker_add_sample (collector->tracker,
sample->pid, ips, n_ips);
}
static void
process_event (Collector *collector,
counter_t *counter,
counter_event_t *event)
{
char *name;
switch (event->header.type)
{
case PERF_RECORD_MMAP: name = "mmap"; break;
case PERF_RECORD_LOST: name = "lost"; break;
case PERF_RECORD_COMM: name = "comm"; break;
case PERF_RECORD_EXIT: name = "exit"; break;
case PERF_RECORD_THROTTLE: name = "throttle"; break;
case PERF_RECORD_UNTHROTTLE: name = "unthrottle"; break;
case PERF_RECORD_FORK: name = "fork"; break;
case PERF_RECORD_READ: name = "read"; break;
case PERF_RECORD_SAMPLE: name = "samp"; break;
default: name = "unknown"; break;
}
d_print ("cpu %d :: %s :: ", counter->cpu, name);
switch (event->header.type)
{
case PERF_RECORD_MMAP:
process_mmap (collector, &event->mmap);
break;
case PERF_RECORD_LOST:
g_print ("lost event\n");
break;
case PERF_RECORD_COMM:
process_comm (collector, &event->comm);
break;
case PERF_RECORD_EXIT:
process_exit (collector, &event->exit);
break;
case PERF_RECORD_THROTTLE:
g_print ("throttle\n");
break;
case PERF_RECORD_UNTHROTTLE:
g_print ("unthrottle\n");
break;
case PERF_RECORD_FORK:
process_fork (collector, &event->fork);
break;
case PERF_RECORD_READ:
break;
case PERF_RECORD_SAMPLE:
process_sample (collector, &event->sample);
break;
default:
g_warning ("unknown event: %d (%d)\n",
event->header.type, event->header.size);
break;
}
d_print ("\n");
}
gboolean
collector_start (Collector *collector,
pid_t pid,
GError **err)
{
int n_cpus = get_n_cpus ();
int i;
counter_t *output;
if (!collector->tracker)
collector->tracker = tracker_new ();
output = NULL;
for (i = 0; i < n_cpus; ++i)
{
counter_t *counter = counter_new (collector, pid, i, output, err);
if (!counter)
{
GList *list;
for (list = collector->counters; list != NULL; list = list->next)
counter_free (list->data);
collector->tracker = NULL;
return FALSE;
}
collector->counters = g_list_append (collector->counters, counter);
if (!output)
output = counter;
}
enable_counters (collector);
return TRUE;
}
void
collector_stop (Collector *collector)
{
GList *list;
if (!collector->counters)
return;
/* Read any remaining data */
for (list = collector->counters; list != NULL; list = list->next)
{
counter_t *counter = list->data;
if (counter->data)
on_read (counter);
counter_free (counter);
}
g_list_free (collector->counters);
collector->counters = NULL;
}
int
collector_get_n_samples (Collector *collector)
{
return collector->n_samples;
}
Profile *
collector_create_profile (Collector *collector)
{
/* The collector must be stopped when you create a profile */
g_assert (!collector->counters);
return tracker_create_profile (collector->tracker);
}
GQuark
collector_error_quark (void)
{
static GQuark q = 0;
if (q == 0)
q = g_quark_from_static_string ("collector-error-quark");
return q;
}

46
lib/collector.h Normal file
View File

@ -0,0 +1,46 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* 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 "profile.h"
typedef struct Collector Collector;
typedef void (* CollectorFunc) (gboolean first_sample,
gpointer data);
#define COLLECTOR_ERROR collector_error_quark ()
GQuark collector_error_quark (void);
typedef enum
{
COLLECTOR_ERROR_FAILED
} CollectorError;
/* callback is called whenever a new sample arrives */
Collector *collector_new (gboolean use_hw_counters,
CollectorFunc callback,
gpointer data);
gboolean collector_start (Collector *collector,
pid_t pid,
GError **err);
void collector_stop (Collector *collector);
void collector_reset (Collector *collector);
int collector_get_n_samples (Collector *collector);
Profile * collector_create_profile (Collector *collector);

9595
lib/demangle.c Normal file

File diff suppressed because it is too large Load Diff

814
lib/elfparser.c Normal file
View File

@ -0,0 +1,814 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2006, 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
* 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 <stdlib.h>
#include <string.h>
#include <elf.h>
#include <sys/mman.h>
#include "elfparser.h"
typedef struct Section Section;
struct ElfSym
{
gulong table;
gulong offset;
gulong address;
};
struct Section
{
const gchar * name;
gsize offset;
gsize size;
gboolean allocated;
gulong load_address;
guint type;
};
struct ElfParser
{
gboolean is_64;
const guchar * data;
gsize length;
int n_sections;
Section ** sections;
int n_symbols;
ElfSym * symbols;
gsize sym_strings;
GMappedFile * file;
char * filename;
gboolean checked_build_id;
char * build_id;
const Section * text_section;
};
/* FIXME: All of these should in principle do endian swapping,
* but sysprof never has to deal with binaries of a different
* endianness than sysprof itself
*/
#define GET_FIELD(parser, offset, struct_name, idx, field_name) \
(((parser))->is_64? \
((Elf64_ ## struct_name *)(((parser)->data + offset)) + (idx))->field_name : \
((Elf32_ ## struct_name *)(((parser)->data + offset)) + (idx))->field_name)
#define GET_UINT32(parser, offset) \
*((uint32_t *)(parser->data + offset)) \
#define GET_SIZE(parser, struct_name) \
(((parser)->is_64? \
sizeof (Elf64_ ## struct_name) : \
sizeof (Elf32_ ## struct_name)))
#define MAKE_ELF_UINT_ACCESSOR(field_name) \
static uint64_t field_name (ElfParser *parser) \
{ \
return GET_FIELD (parser, 0, Ehdr, 0, field_name); \
}
MAKE_ELF_UINT_ACCESSOR (e_shoff)
MAKE_ELF_UINT_ACCESSOR (e_shnum)
MAKE_ELF_UINT_ACCESSOR (e_shstrndx)
#define MAKE_SECTION_HEADER_ACCESSOR(field_name) \
static uint64_t field_name (ElfParser *parser, int nth_section) \
{ \
gsize offset = e_shoff (parser); \
\
return GET_FIELD (parser, offset, Shdr, nth_section, field_name); \
}
MAKE_SECTION_HEADER_ACCESSOR (sh_name);
MAKE_SECTION_HEADER_ACCESSOR (sh_type);
MAKE_SECTION_HEADER_ACCESSOR (sh_flags);
MAKE_SECTION_HEADER_ACCESSOR (sh_addr);
MAKE_SECTION_HEADER_ACCESSOR (sh_offset);
MAKE_SECTION_HEADER_ACCESSOR (sh_size);
#define MAKE_SYMBOL_ACCESSOR(field_name) \
static uint64_t field_name (ElfParser *parser, gulong offset, gulong nth) \
{ \
return GET_FIELD (parser, offset, Sym, nth, field_name); \
}
MAKE_SYMBOL_ACCESSOR(st_name);
MAKE_SYMBOL_ACCESSOR(st_info);
MAKE_SYMBOL_ACCESSOR(st_value);
MAKE_SYMBOL_ACCESSOR(st_size);
MAKE_SYMBOL_ACCESSOR(st_shndx);
static void
section_free (Section *section)
{
g_free (section);
}
static const Section *
find_section (ElfParser *parser,
const char *name,
guint type)
{
int i;
for (i = 0; i < parser->n_sections; ++i)
{
Section *section = parser->sections[i];
if (strcmp (section->name, name) == 0 && section->type == type)
return section;
}
return NULL;
}
static gboolean
parse_elf_signature (const guchar *data,
gsize length,
gboolean *is_64,
gboolean *is_be)
{
/* FIXME: this function should be able to return an error */
if (length < EI_NIDENT)
{
/* FIXME set error */
return FALSE;
}
if (data[EI_CLASS] != ELFCLASS32 &&
data[EI_CLASS] != ELFCLASS64)
{
/* FIXME set error */
return FALSE;
}
if (data[EI_DATA] != ELFDATA2LSB &&
data[EI_DATA] != ELFDATA2MSB)
{
/* FIXME set error */
return FALSE;
}
if (is_64)
*is_64 = (data[EI_CLASS] == ELFCLASS64);
if (is_be)
*is_be = (data[EI_DATA] == ELFDATA2MSB);
return TRUE;
}
ElfParser *
elf_parser_new_from_data (const guchar *data,
gsize length)
{
ElfParser *parser;
gboolean is_64, is_big_endian;
int section_names_idx;
const guchar *section_names;
gsize section_headers;
int i;
if (!parse_elf_signature (data, length, &is_64, &is_big_endian))
{
/* FIXME: set error */
return NULL;
}
parser = g_new0 (ElfParser, 1);
parser->is_64 = is_64;
parser->data = data;
parser->length = length;
#if 0
g_print (" new parser : %p\n", parser);
#endif
/* Read ELF header */
parser->n_sections = e_shnum (parser);
section_names_idx = e_shstrndx (parser);
section_headers = e_shoff (parser);
/* Read section headers */
parser->sections = g_new0 (Section *, parser->n_sections);
section_names = parser->data + sh_offset (parser, section_names_idx);
for (i = 0; i < parser->n_sections; ++i)
{
Section *section = g_new (Section, 1);
section->name = (char *)(section_names + sh_name (parser, i));
section->size = sh_size (parser, i);
section->offset = sh_offset (parser, i);
section->allocated = !!(sh_flags (parser, i) & SHF_ALLOC);
if (section->allocated)
section->load_address = sh_addr (parser, i);
else
section->load_address = 0;
section->type = sh_type (parser, i);
parser->sections[i] = section;
}
/* Cache the text section */
parser->text_section = find_section (parser, ".text", SHT_PROGBITS);
if (!parser->text_section)
parser->text_section = find_section (parser, ".text", SHT_NOBITS);
parser->filename = NULL;
parser->build_id = NULL;
return parser;
}
ElfParser *
elf_parser_new (const char *filename,
GError **err)
{
const guchar *data;
gsize length;
ElfParser *parser;
GMappedFile *file = g_mapped_file_new (filename, FALSE, NULL);
if (!file)
return NULL;
#if 0
g_print ("elf parser new : %s\n", filename);
#endif
data = (guchar *)g_mapped_file_get_contents (file);
length = g_mapped_file_get_length (file);
#if 0
g_print ("data %p: for %s\n", data, filename);
#endif
parser = elf_parser_new_from_data (data, length);
#if 0
g_print ("Parser for %s: %p\n", filename, parser);
#endif
if (!parser)
{
g_mapped_file_free (file);
return NULL;
}
parser->filename = g_strdup (filename);
parser->file = file;
#if 0
g_print ("Elf file: %s (debug: %s)\n",
filename, elf_parser_get_debug_link (parser, NULL));
if (!parser->symbols)
g_print ("at this point %s has no symbols\n", filename);
#endif
return parser;
}
guint32
elf_parser_get_crc32 (ElfParser *parser)
{
static const unsigned long crc32_table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
const guchar *data;
gsize length;
gulong crc;
gsize i;
data = parser->data;
length = parser->length;
crc = 0xffffffff;
madvise ((char *)data, length, MADV_SEQUENTIAL);
for (i = 0; i < length; ++i)
crc = crc32_table[(crc ^ data[i]) & 0xff] ^ (crc >> 8);
/* We just read the entire file into memory, but we only really
* need the symbol table, so swap the whole thing out.
*
* We could be more exact here, but it's only a few minor
* pagefaults.
*/
if (parser->file)
madvise ((char *)data, length, MADV_DONTNEED);
return ~crc & 0xffffffff;
}
void
elf_parser_free (ElfParser *parser)
{
int i;
for (i = 0; i < parser->n_sections; ++i)
section_free (parser->sections[i]);
g_free (parser->sections);
if (parser->file)
g_mapped_file_free (parser->file);
g_free (parser->symbols);
if (parser->filename)
g_free (parser->filename);
if (parser->build_id)
g_free (parser->build_id);
g_free (parser);
}
extern char *sysprof_cplus_demangle (const char *name, int options);
char *
elf_demangle (const char *name)
{
#define DMGL_PARAMS (1 << 0) /* Include function args */
#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
char *demangled = sysprof_cplus_demangle (name, DMGL_PARAMS | DMGL_ANSI);
if (demangled)
return demangled;
else
return g_strdup (name);
}
/*
* Looking up symbols
*/
static int
compare_sym (const void *a, const void *b)
{
const ElfSym *sym_a = a;
const ElfSym *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;
}
#if 0
static void
dump_symbols (ElfParser *parser, ElfSym *syms, guint n_syms)
{
int i;
for (i = 0; i < n_syms; ++i)
{
ElfSym *s = &(syms[i]);
g_print (" %s: %lx\n", elf_parser_get_sym_name (parser, s), s->address);
}
}
#endif
static void
read_table (ElfParser *parser,
const Section *sym_table,
const Section *str_table)
{
int sym_size = GET_SIZE (parser, Sym);
int i, n_symbols;
#if 0
g_print ("elf: Reading table for %s\n", parser->filename? parser->filename : "<unknown>");
#endif
parser->n_symbols = sym_table->size / sym_size;
parser->symbols = g_new (ElfSym, parser->n_symbols);
#if 0
g_print ("sym table offset: %d\n", sym_table->offset);
#endif
n_symbols = 0;
#if 0
g_print ("n syms: %d\n", parser->n_symbols);
#endif
for (i = 0; i < parser->n_symbols; ++i)
{
guint info;
gulong addr;
gulong shndx;
info = st_info (parser, sym_table->offset, i);
addr = st_value (parser, sym_table->offset, i);
shndx = st_shndx (parser, sym_table->offset, i);
#if 0
g_print ("read symbol: %s (section: %d)\n", get_string_indirct (parser->parser,
parser->sym_format, "st_name",
str_table->offset),
shndx);
#endif
if (addr != 0 &&
shndx < parser->n_sections &&
parser->sections[shndx] == parser->text_section &&
(info & 0xf) == STT_FUNC &&
((info >> 4) == STB_GLOBAL ||
(info >> 4) == STB_LOCAL ||
(info >> 4) == STB_WEAK))
{
parser->symbols[n_symbols].address = addr;
parser->symbols[n_symbols].table = sym_table->offset;
parser->symbols[n_symbols].offset = i;
n_symbols++;
#if 0
g_print (" symbol: %s: %lx\n",
get_string_indirect (parser->parser,
parser->sym_format, "st_name",
str_table->offset),
addr - parser->text_section->load_address);
g_print (" sym %d in %p (info: %d:%d) (func:global %d:%d)\n",
addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL);
#endif
}
else if (addr != 0)
{
#if 0
g_print (" rejecting %d in %p (info: %d:%d) (func:global %d:%d)\n",
addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL);
#endif
}
}
parser->sym_strings = str_table->offset;
parser->n_symbols = n_symbols;
/* Allocate space for at least one symbol, so that parser->symbols will be
* non-NULL. If it ends up being NULL, we will be parsing the file over and
* over.
*/
parser->symbols = g_renew (ElfSym, parser->symbols, parser->n_symbols + 1);
qsort (parser->symbols, parser->n_symbols, sizeof (ElfSym), compare_sym);
}
static void
read_symbols (ElfParser *parser)
{
const Section *symtab = find_section (parser, ".symtab", SHT_SYMTAB);
const Section *strtab = find_section (parser, ".strtab", SHT_STRTAB);
const Section *dynsym = find_section (parser, ".dynsym", SHT_DYNSYM);
const Section *dynstr = find_section (parser, ".dynstr", SHT_STRTAB);
if (symtab && strtab)
{
#if 0
g_print ("reading symbol table of %s\n", parser->filename);
#endif
read_table (parser, symtab, strtab);
}
else if (dynsym && dynstr)
{
#if 0
g_print ("reading dynamic symbol table of %s\n", parser->filename);
#endif
read_table (parser, dynsym, dynstr);
}
else
{
/* To make sure parser->symbols is non-NULL */
parser->n_symbols = 0;
parser->symbols = g_new (ElfSym, 1);
}
}
static ElfSym *
do_lookup (ElfSym *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);
}
}
/* Address should be given in 'offset into text segment' */
const ElfSym *
elf_parser_lookup_symbol (ElfParser *parser,
gulong address)
{
const ElfSym *result;
if (!parser->symbols)
{
#if 0
g_print ("reading symbols at %p\n", parser);
#endif
read_symbols (parser);
}
if (parser->n_symbols == 0)
return NULL;
if (!parser->text_section)
return NULL;
address += parser->text_section->load_address;
#if 0
g_print ("elf: the address we are looking up is %p\n", address);
#endif
result = do_lookup (parser->symbols, address, 0, parser->n_symbols - 1);
#if 0
if (result)
{
g_print (" elf: found %s at %lx\n", elf_parser_get_sym_name (parser, result), result->address);
}
else
{
g_print ("elf: not found\n");
}
#endif
if (result)
{
gulong size = st_size (parser, result->table, result->offset);
if (size > 0 && result->address + size <= address)
{
#if 0
g_print (" elf: ends at %lx, so rejecting\n",
result->address + size);
#endif
result = NULL;
}
}
if (result)
{
/* Reject the symbols if the address is outside the text section */
if (address > parser->text_section->load_address + parser->text_section->size)
result = NULL;
}
return result;
}
gulong
elf_parser_get_text_offset (ElfParser *parser)
{
g_return_val_if_fail (parser != NULL, (gulong)-1);
if (!parser->text_section)
return (gulong)-1;
return parser->text_section->offset;
}
static gchar *
make_hex_string (const guchar *data, int n_bytes)
{
static const char hex_digits[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
GString *string = g_string_new (NULL);
int i;
for (i = 0; i < n_bytes; ++i)
{
char c = data[i];
g_string_append_c (string, hex_digits[(c & 0xf0) >> 4]);
g_string_append_c (string, hex_digits[(c & 0x0f)]);
}
return g_string_free (string, FALSE);
}
const gchar *
elf_parser_get_build_id (ElfParser *parser)
{
if (!parser->checked_build_id)
{
const Section *build_id =
find_section (parser, ".note.gnu.build-id", SHT_NOTE);
guint64 name_size;
guint64 desc_size;
guint64 type;
const char *name;
guint64 offset;
parser->checked_build_id = TRUE;
if (!build_id)
return NULL;
offset = build_id->offset;
name_size = GET_FIELD (parser, offset, Nhdr, 0, n_namesz);
desc_size = GET_FIELD (parser, offset, Nhdr, 0, n_descsz);
type = GET_FIELD (parser, offset, Nhdr, 0, n_type);
offset += GET_SIZE (parser, Nhdr);
name = (char *)(parser->data + offset);
if (strncmp (name, ELF_NOTE_GNU, name_size) != 0 || type != NT_GNU_BUILD_ID)
return NULL;
offset += strlen (name);
offset = (offset + 3) & (~0x3);
parser->build_id = make_hex_string (parser->data + offset, desc_size);
}
return parser->build_id;
}
const char *
elf_parser_get_debug_link (ElfParser *parser, guint32 *crc32)
{
guint64 offset;
const Section *debug_link = find_section (parser, ".gnu_debuglink",
SHT_PROGBITS);
const gchar *result;
if (!debug_link)
return NULL;
offset = debug_link->offset;
result = (char *)(parser->data + offset);
if (crc32)
{
int len = strlen (result) + 1;
offset = (offset + len + 3) & ~0x3;
*crc32 = GET_UINT32 (parser, offset);
}
return result;
}
const guchar *
get_section (ElfParser *parser,
const char *name)
{
const Section *section = find_section (parser, name, SHT_PROGBITS);
if (section)
return parser->data + section->offset;
else
return NULL;
}
const guchar *
elf_parser_get_eh_frame (ElfParser *parser)
{
return get_section (parser, ".eh_frame");
}
const guchar *
elf_parser_get_debug_frame (ElfParser *parser)
{
return get_section (parser, ".debug_frame");
}
const char *
elf_parser_get_sym_name (ElfParser *parser,
const ElfSym *sym)
{
g_return_val_if_fail (parser != NULL, NULL);
return (char *)(parser->data + parser->sym_strings +
st_name (parser, sym->table, sym->offset));
}
gboolean
elf_parser_owns_symbol (ElfParser *parser,
const ElfSym *sym)
{
ElfSym *first, *last;
if (!parser->n_symbols)
return FALSE;
first = parser->symbols;
last = parser->symbols + parser->n_symbols - 1;
return first <= sym && sym <= last;
}
gulong
elf_parser_get_sym_address (ElfParser *parser,
const ElfSym *sym)
{
return sym->address - parser->text_section->load_address;
}
/*
* Utility functions
*/

57
lib/elfparser.h Normal file
View File

@ -0,0 +1,57 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2006, 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
* 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 <glib.h>
typedef struct ElfSym ElfSym;
typedef struct ElfParser ElfParser;
ElfParser * elf_parser_new_from_data (const guchar *data,
gsize length);
ElfParser * elf_parser_new (const char *filename,
GError **err);
void elf_parser_free (ElfParser *parser);
const char * elf_parser_get_debug_link (ElfParser *parser,
guint32 *crc32);
const gchar *elf_parser_get_build_id (ElfParser *parser);
const guchar *elf_parser_get_eh_frame (ElfParser *parser);
const guchar *elf_parser_get_debug_frame (ElfParser *parser);
gulong elf_parser_get_text_offset (ElfParser *parser);
/* Lookup a symbol in the file.
*
* The symbol returned is const, so don't free it. It is valid until
* elf_parser_free() is called on the parser.
*
* The address should be given in "file coordinates". This means that
* if the file is mapped at address m and offset o, then an address a
* should be looked up as "a - (m - o)". (m - o) is where the start
* of the file would have been mapped, so a - (m - o) is the position
* in the file of a.
*/
const ElfSym *elf_parser_lookup_symbol (ElfParser *parser,
gulong address);
guint32 elf_parser_get_crc32 (ElfParser *parser);
const char * elf_parser_get_sym_name (ElfParser *parser,
const ElfSym *sym);
gulong elf_parser_get_sym_address (ElfParser *parser,
const ElfSym *sym);
gboolean elf_parser_owns_symbol (ElfParser *parser,
const ElfSym *sym);
char * elf_demangle (const char *name);

577
lib/footreedatalist.c Normal file
View File

@ -0,0 +1,577 @@
/* gtktreedatalist.c
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* This file contains code shared between GtkTreeStore and GtkListStore. Please
* do not use it.
*/
#include "footreedatalist.h"
#include <string.h>
static FooTreeDataList *cache;
/* node allocation
*/
#define N_DATA_LISTS (64)
FooTreeDataList *
_foo_tree_data_list_alloc (void)
{
FooTreeDataList *list;
if (!cache)
{
int i;
list = g_malloc (N_DATA_LISTS * sizeof (FooTreeDataList));
for (i = 0; i < N_DATA_LISTS; ++i)
{
list[i].next = cache;
cache = &(list[i]);
}
}
list = cache;
cache = cache->next;
memset (list, 0, sizeof (FooTreeDataList));
return list;
}
void
_foo_tree_data_list_free (FooTreeDataList *list,
GType *column_headers)
{
FooTreeDataList *tmp, *next;
gint i = 0;
tmp = list;
while (tmp)
{
next = tmp->next;
if (g_type_is_a (column_headers [i], G_TYPE_STRING))
g_free ((gchar *) tmp->data.v_pointer);
else if (g_type_is_a (column_headers [i], G_TYPE_OBJECT) && tmp->data.v_pointer != NULL)
g_object_unref (tmp->data.v_pointer);
else if (g_type_is_a (column_headers [i], G_TYPE_BOXED) && tmp->data.v_pointer != NULL)
g_boxed_free (column_headers [i], (gpointer) tmp->data.v_pointer);
tmp->next = cache;
cache = tmp;
i++;
tmp = next;
}
}
gboolean
_foo_tree_data_list_check_type (GType type)
{
gint i = 0;
static const GType type_list[] =
{
G_TYPE_BOOLEAN,
G_TYPE_CHAR,
G_TYPE_UCHAR,
G_TYPE_INT,
G_TYPE_UINT,
G_TYPE_LONG,
G_TYPE_ULONG,
G_TYPE_INT64,
G_TYPE_UINT64,
G_TYPE_ENUM,
G_TYPE_FLAGS,
G_TYPE_FLOAT,
G_TYPE_DOUBLE,
G_TYPE_STRING,
G_TYPE_POINTER,
G_TYPE_BOXED,
G_TYPE_OBJECT,
G_TYPE_INVALID
};
if (! G_TYPE_IS_VALUE_TYPE (type))
return FALSE;
while (type_list[i] != G_TYPE_INVALID)
{
if (g_type_is_a (type, type_list[i]))
return TRUE;
i++;
}
return FALSE;
}
static inline GType
get_fundamental_type (GType type)
{
GType result;
result = G_TYPE_FUNDAMENTAL (type);
if (result == G_TYPE_INTERFACE)
{
if (g_type_is_a (type, G_TYPE_OBJECT))
result = G_TYPE_OBJECT;
}
return result;
}
void
_foo_tree_data_list_node_to_value (FooTreeDataList *list,
GType type,
GValue *value)
{
g_value_init (value, type);
switch (get_fundamental_type (type))
{
case G_TYPE_BOOLEAN:
g_value_set_boolean (value, (gboolean) list->data.v_int);
break;
case G_TYPE_CHAR:
g_value_set_char (value, (gchar) list->data.v_char);
break;
case G_TYPE_UCHAR:
g_value_set_uchar (value, (guchar) list->data.v_uchar);
break;
case G_TYPE_INT:
g_value_set_int (value, (gint) list->data.v_int);
break;
case G_TYPE_UINT:
g_value_set_uint (value, (guint) list->data.v_uint);
break;
case G_TYPE_LONG:
g_value_set_long (value, list->data.v_long);
break;
case G_TYPE_ULONG:
g_value_set_ulong (value, list->data.v_ulong);
break;
case G_TYPE_INT64:
g_value_set_int64 (value, list->data.v_int64);
break;
case G_TYPE_UINT64:
g_value_set_uint64 (value, list->data.v_uint64);
break;
case G_TYPE_ENUM:
g_value_set_enum (value, list->data.v_int);
break;
case G_TYPE_FLAGS:
g_value_set_flags (value, list->data.v_uint);
break;
case G_TYPE_FLOAT:
g_value_set_float (value, (gfloat) list->data.v_float);
break;
case G_TYPE_DOUBLE:
g_value_set_double (value, (gdouble) list->data.v_double);
break;
case G_TYPE_STRING:
g_value_set_string (value, (gchar *) list->data.v_pointer);
break;
case G_TYPE_POINTER:
g_value_set_pointer (value, (gpointer) list->data.v_pointer);
break;
case G_TYPE_BOXED:
g_value_set_boxed (value, (gpointer) list->data.v_pointer);
break;
case G_TYPE_OBJECT:
g_value_set_object (value, (GObject *) list->data.v_pointer);
break;
default:
g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
break;
}
}
void
_foo_tree_data_list_value_to_node (FooTreeDataList *list,
GValue *value)
{
switch (get_fundamental_type (G_VALUE_TYPE (value)))
{
case G_TYPE_BOOLEAN:
list->data.v_int = g_value_get_boolean (value);
break;
case G_TYPE_CHAR:
list->data.v_char = g_value_get_char (value);
break;
case G_TYPE_UCHAR:
list->data.v_uchar = g_value_get_uchar (value);
break;
case G_TYPE_INT:
list->data.v_int = g_value_get_int (value);
break;
case G_TYPE_UINT:
list->data.v_uint = g_value_get_uint (value);
break;
case G_TYPE_LONG:
list->data.v_long = g_value_get_long (value);
break;
case G_TYPE_ULONG:
list->data.v_ulong = g_value_get_ulong (value);
break;
case G_TYPE_INT64:
list->data.v_int64 = g_value_get_int64 (value);
break;
case G_TYPE_UINT64:
list->data.v_uint64 = g_value_get_uint64 (value);
break;
case G_TYPE_ENUM:
list->data.v_int = g_value_get_enum (value);
break;
case G_TYPE_FLAGS:
list->data.v_uint = g_value_get_flags (value);
break;
case G_TYPE_POINTER:
list->data.v_pointer = g_value_get_pointer (value);
break;
case G_TYPE_FLOAT:
list->data.v_float = g_value_get_float (value);
break;
case G_TYPE_DOUBLE:
list->data.v_double = g_value_get_double (value);
break;
case G_TYPE_STRING:
g_free (list->data.v_pointer);
list->data.v_pointer = g_value_dup_string (value);
break;
case G_TYPE_OBJECT:
if (list->data.v_pointer)
g_object_unref (list->data.v_pointer);
list->data.v_pointer = g_value_dup_object (value);
break;
case G_TYPE_BOXED:
if (list->data.v_pointer)
g_boxed_free (G_VALUE_TYPE (value), list->data.v_pointer);
list->data.v_pointer = g_value_dup_boxed (value);
break;
default:
g_warning ("%s: Unsupported type (%s) stored.", G_STRLOC, g_type_name (G_VALUE_TYPE (value)));
break;
}
}
FooTreeDataList *
_foo_tree_data_list_node_copy (FooTreeDataList *list,
GType type)
{
FooTreeDataList *new_list;
g_return_val_if_fail (list != NULL, NULL);
new_list = _foo_tree_data_list_alloc ();
new_list->next = NULL;
switch (get_fundamental_type (type))
{
case G_TYPE_BOOLEAN:
case G_TYPE_CHAR:
case G_TYPE_UCHAR:
case G_TYPE_INT:
case G_TYPE_UINT:
case G_TYPE_LONG:
case G_TYPE_ULONG:
case G_TYPE_INT64:
case G_TYPE_UINT64:
case G_TYPE_ENUM:
case G_TYPE_FLAGS:
case G_TYPE_POINTER:
case G_TYPE_FLOAT:
case G_TYPE_DOUBLE:
new_list->data = list->data;
break;
case G_TYPE_STRING:
new_list->data.v_pointer = g_strdup (list->data.v_pointer);
break;
case G_TYPE_OBJECT:
case G_TYPE_INTERFACE:
new_list->data.v_pointer = list->data.v_pointer;
if (new_list->data.v_pointer)
g_object_ref (new_list->data.v_pointer);
break;
case G_TYPE_BOXED:
if (list->data.v_pointer)
new_list->data.v_pointer = g_boxed_copy (type, list->data.v_pointer);
else
new_list->data.v_pointer = NULL;
break;
default:
g_warning ("Unsupported node type (%s) copied.", g_type_name (type));
break;
}
return new_list;
}
gint
_foo_tree_data_list_compare_func (GtkTreeModel *model,
GtkTreeIter *a,
GtkTreeIter *b,
gpointer user_data)
{
gint column = GPOINTER_TO_INT (user_data);
GType type = gtk_tree_model_get_column_type (model, column);
GValue a_value = {0, };
GValue b_value = {0, };
gint retval;
const gchar *stra, *strb;
gtk_tree_model_get_value (model, a, column, &a_value);
gtk_tree_model_get_value (model, b, column, &b_value);
switch (get_fundamental_type (type))
{
case G_TYPE_BOOLEAN:
if (g_value_get_boolean (&a_value) < g_value_get_boolean (&b_value))
retval = -1;
else if (g_value_get_boolean (&a_value) == g_value_get_boolean (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_CHAR:
if (g_value_get_char (&a_value) < g_value_get_char (&b_value))
retval = -1;
else if (g_value_get_char (&a_value) == g_value_get_char (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_UCHAR:
if (g_value_get_uchar (&a_value) < g_value_get_uchar (&b_value))
retval = -1;
else if (g_value_get_uchar (&a_value) == g_value_get_uchar (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_INT:
if (g_value_get_int (&a_value) < g_value_get_int (&b_value))
retval = -1;
else if (g_value_get_int (&a_value) == g_value_get_int (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_UINT:
if (g_value_get_uint (&a_value) < g_value_get_uint (&b_value))
retval = -1;
else if (g_value_get_uint (&a_value) == g_value_get_uint (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_LONG:
if (g_value_get_long (&a_value) < g_value_get_long (&b_value))
retval = -1;
else if (g_value_get_long (&a_value) == g_value_get_long (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_ULONG:
if (g_value_get_ulong (&a_value) < g_value_get_ulong (&b_value))
retval = -1;
else if (g_value_get_ulong (&a_value) == g_value_get_ulong (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_INT64:
if (g_value_get_int64 (&a_value) < g_value_get_int64 (&b_value))
retval = -1;
else if (g_value_get_int64 (&a_value) == g_value_get_int64 (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_UINT64:
if (g_value_get_uint64 (&a_value) < g_value_get_uint64 (&b_value))
retval = -1;
else if (g_value_get_uint64 (&a_value) == g_value_get_uint64 (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_ENUM:
/* this is somewhat bogus. */
if (g_value_get_enum (&a_value) < g_value_get_enum (&b_value))
retval = -1;
else if (g_value_get_enum (&a_value) == g_value_get_enum (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_FLAGS:
/* this is even more bogus. */
if (g_value_get_flags (&a_value) < g_value_get_flags (&b_value))
retval = -1;
else if (g_value_get_flags (&a_value) == g_value_get_flags (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_FLOAT:
if (g_value_get_float (&a_value) < g_value_get_float (&b_value))
retval = -1;
else if (g_value_get_float (&a_value) == g_value_get_float (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_DOUBLE:
if (g_value_get_double (&a_value) < g_value_get_double (&b_value))
retval = -1;
else if (g_value_get_double (&a_value) == g_value_get_double (&b_value))
retval = 0;
else
retval = 1;
break;
case G_TYPE_STRING:
stra = g_value_get_string (&a_value);
strb = g_value_get_string (&b_value);
if (stra == NULL) stra = "";
if (strb == NULL) strb = "";
retval = g_utf8_collate (stra, strb);
break;
case G_TYPE_POINTER:
case G_TYPE_BOXED:
case G_TYPE_OBJECT:
default:
g_warning ("Attempting to sort on invalid type %s\n", g_type_name (type));
retval = FALSE;
break;
}
g_value_unset (&a_value);
g_value_unset (&b_value);
return retval;
}
GList *
_foo_tree_data_list_header_new (gint n_columns,
GType *types)
{
GList *retval = NULL;
gint i;
for (i = 0; i < n_columns; i ++)
{
GtkTreeDataSortHeader *header;
header = g_slice_new (GtkTreeDataSortHeader);
retval = g_list_prepend (retval, header);
header->sort_column_id = i;
header->func = _foo_tree_data_list_compare_func;
header->destroy = NULL;
header->data = GINT_TO_POINTER (i);
}
return g_list_reverse (retval);
}
void
_foo_tree_data_list_header_free (GList *list)
{
GList *tmp;
for (tmp = list; tmp; tmp = tmp->next)
{
GtkTreeDataSortHeader *header = (GtkTreeDataSortHeader *) tmp->data;
if (header->destroy)
{
GDestroyNotify d = header->destroy;
header->destroy = NULL;
d (header->data);
}
g_slice_free (GtkTreeDataSortHeader, header);
}
g_list_free (list);
}
GtkTreeDataSortHeader *
_foo_tree_data_list_get_header (GList *header_list,
gint sort_column_id)
{
GtkTreeDataSortHeader *header = NULL;
for (; header_list; header_list = header_list->next)
{
header = (GtkTreeDataSortHeader*) header_list->data;
if (header->sort_column_id == sort_column_id)
return header;
}
return NULL;
}
GList *
_foo_tree_data_list_set_header (GList *header_list,
gint sort_column_id,
GtkTreeIterCompareFunc func,
gpointer data,
GDestroyNotify destroy)
{
GList *list = header_list;
GtkTreeDataSortHeader *header = NULL;
for (; list; list = list->next)
{
header = (GtkTreeDataSortHeader*) list->data;
if (header->sort_column_id == sort_column_id)
break;
header = NULL;
if (list->next == NULL)
break;
}
if (header == NULL)
{
header = g_slice_new0 (GtkTreeDataSortHeader);
header->sort_column_id = sort_column_id;
if (list)
list = g_list_append (list, header);
else
header_list = g_list_append (header_list, header);
}
if (header->destroy)
{
GDestroyNotify d = header->destroy;
header->destroy = NULL;
d (header->data);
}
header->func = func;
header->data = data;
header->destroy = destroy;
return header_list;
}

82
lib/footreedatalist.h Normal file
View File

@ -0,0 +1,82 @@
/* gtktreedatalist.h
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GTK_TREE_DATA_LIST_H__
#define __GTK_TREE_DATA_LIST_H__
#include <gtk/gtk.h>
typedef struct _FooTreeDataList FooTreeDataList;
struct _FooTreeDataList
{
FooTreeDataList *next;
union {
gint v_int;
gint8 v_char;
guint8 v_uchar;
guint v_uint;
glong v_long;
gulong v_ulong;
gint64 v_int64;
guint64 v_uint64;
gfloat v_float;
gdouble v_double;
gpointer v_pointer;
} data;
};
typedef struct _GtkTreeDataSortHeader
{
gint sort_column_id;
GtkTreeIterCompareFunc func;
gpointer data;
GDestroyNotify destroy;
} GtkTreeDataSortHeader;
FooTreeDataList *_foo_tree_data_list_alloc (void);
void _foo_tree_data_list_free (FooTreeDataList *list,
GType *column_headers);
gboolean _foo_tree_data_list_check_type (GType type);
void _foo_tree_data_list_node_to_value (FooTreeDataList *list,
GType type,
GValue *value);
void _foo_tree_data_list_value_to_node (FooTreeDataList *list,
GValue *value);
FooTreeDataList *_foo_tree_data_list_node_copy (FooTreeDataList *list,
GType type);
/* Header code */
gint _foo_tree_data_list_compare_func (GtkTreeModel *model,
GtkTreeIter *a,
GtkTreeIter *b,
gpointer user_data);
GList * _foo_tree_data_list_header_new (gint n_columns,
GType *types);
void _foo_tree_data_list_header_free (GList *header_list);
GtkTreeDataSortHeader *_foo_tree_data_list_get_header (GList *header_list,
gint sort_column_id);
GList *_foo_tree_data_list_set_header (GList *header_list,
gint sort_column_id,
GtkTreeIterCompareFunc func,
gpointer data,
GDestroyNotify destroy);
#endif /* __GTK_TREE_DATA_LIST_H__ */

3363
lib/footreestore.c Normal file

File diff suppressed because it is too large Load Diff

158
lib/footreestore.h Normal file
View File

@ -0,0 +1,158 @@
/* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __FOO_TREE_STORE_H__
#define __FOO_TREE_STORE_H__
#include <gtk/gtk.h>
#include <stdarg.h>
G_BEGIN_DECLS
#define FOO_TYPE_TREE_STORE (foo_tree_store_get_type ())
#define FOO_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_TREE_STORE, FooTreeStore))
#define FOO_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_TREE_STORE, FooTreeStoreClass))
#define FOO_IS_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_TREE_STORE))
#define FOO_IS_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_TREE_STORE))
#define FOO_TREE_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_TREE_STORE, FooTreeStoreClass))
typedef struct _FooTreeStore FooTreeStore;
typedef struct _FooTreeStoreClass FooTreeStoreClass;
struct _FooTreeStore
{
GObject parent;
gint stamp;
gpointer root;
gpointer last;
gint n_columns;
gint sort_column_id;
GList *sort_list;
GtkSortType order;
GType *column_headers;
GtkTreeIterCompareFunc default_sort_func;
gpointer default_sort_data;
GDestroyNotify default_sort_destroy;
guint row_changed_id;
guint row_inserted_id;
guint row_has_child_toggled_id;
guint rows_reordered_id;
guint (columns_dirty) : 1;
};
struct _FooTreeStoreClass
{
GObjectClass parent_class;
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
void (*_gtk_reserved4) (void);
};
GType foo_tree_store_get_type (void) G_GNUC_CONST;
FooTreeStore *foo_tree_store_new (gint n_columns,
...);
FooTreeStore *foo_tree_store_newv (gint n_columns,
GType *types);
void foo_tree_store_set_column_types (FooTreeStore *tree_store,
gint n_columns,
GType *types);
/* NOTE: use gtk_tree_model_get to get values from a FooTreeStore */
void foo_tree_store_set_value (FooTreeStore *tree_store,
GtkTreeIter *iter,
gint column,
GValue *value);
void foo_tree_store_set (FooTreeStore *tree_store,
GtkTreeIter *iter,
...);
void foo_tree_store_set_valuesv (FooTreeStore *tree_store,
GtkTreeIter *iter,
gint *columns,
GValue *values,
gint n_values);
void foo_tree_store_set_valist (FooTreeStore *tree_store,
GtkTreeIter *iter,
va_list var_args);
gboolean foo_tree_store_remove (FooTreeStore *tree_store,
GtkTreeIter *iter);
void foo_tree_store_insert (FooTreeStore *tree_store,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint position);
void foo_tree_store_insert_before (FooTreeStore *tree_store,
GtkTreeIter *iter,
GtkTreeIter *parent,
GtkTreeIter *sibling);
void foo_tree_store_insert_after (FooTreeStore *tree_store,
GtkTreeIter *iter,
GtkTreeIter *parent,
GtkTreeIter *sibling);
void foo_tree_store_insert_with_values (FooTreeStore *tree_store,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint position,
...);
void foo_tree_store_insert_with_valuesv (FooTreeStore *tree_store,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint position,
gint *columns,
GValue *values,
gint n_values);
void foo_tree_store_prepend (FooTreeStore *tree_store,
GtkTreeIter *iter,
GtkTreeIter *parent);
void foo_tree_store_append (FooTreeStore *tree_store,
GtkTreeIter *iter,
GtkTreeIter *parent);
gboolean foo_tree_store_is_ancestor (FooTreeStore *tree_store,
GtkTreeIter *iter,
GtkTreeIter *descendant);
gint foo_tree_store_iter_depth (FooTreeStore *tree_store,
GtkTreeIter *iter);
void foo_tree_store_clear (FooTreeStore *tree_store);
gboolean foo_tree_store_iter_is_valid (FooTreeStore *tree_store,
GtkTreeIter *iter);
void foo_tree_store_reorder (FooTreeStore *tree_store,
GtkTreeIter *parent,
gint *new_order);
void foo_tree_store_swap (FooTreeStore *tree_store,
GtkTreeIter *a,
GtkTreeIter *b);
void foo_tree_store_move_before (FooTreeStore *tree_store,
GtkTreeIter *iter,
GtkTreeIter *position);
void foo_tree_store_move_after (FooTreeStore *tree_store,
GtkTreeIter *iter,
GtkTreeIter *position);
G_END_DECLS
#endif /* __FOO_TREE_STORE_H__ */

605
lib/profile.c Normal file
View File

@ -0,0 +1,605 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* Copyright 2004, 2005, 2006, 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 <glib.h>
#include <stdlib.h>
#include <string.h>
#include "binfile.h"
#include "stackstash.h"
#include "profile.h"
#include "sfile.h"
struct Profile
{
StackStash *stash;
};
static SFormat *
create_format (void)
{
SFormat *format;
SForward *object_forward;
SForward *node_forward;
format = sformat_new();
object_forward = sformat_declare_forward (format);
node_forward = sformat_declare_forward (format);
sformat_set_type (
format,
sformat_make_record (
format, "profile", NULL,
sformat_make_integer (format, "size"),
sformat_make_pointer (format, "call_tree", node_forward),
sformat_make_list (
format, "objects", NULL,
sformat_make_record (
format, "object", object_forward,
sformat_make_string (format, "name"),
sformat_make_integer (format, "total"),
sformat_make_integer (format, "self"),
NULL)),
sformat_make_list (
format, "nodes", NULL,
sformat_make_record (
format, "node", node_forward,
sformat_make_pointer (format, "object", object_forward),
sformat_make_pointer (format, "siblings", node_forward),
sformat_make_pointer (format, "children", node_forward),
sformat_make_pointer (format, "parent", node_forward),
sformat_make_integer (format, "total"),
sformat_make_integer (format, "self"),
sformat_make_integer (format, "toplevel"),
NULL)),
NULL));
return format;
}
static void
serialize_call_tree (StackNode *node,
SFileOutput *output)
{
if (!node)
return;
sfile_begin_add_record (output, "node");
sfile_add_pointer (output, "object", U64_TO_POINTER (node->data));
sfile_add_pointer (output, "siblings", node->siblings);
sfile_add_pointer (output, "children", node->children);
sfile_add_pointer (output, "parent", node->parent);
sfile_add_integer (output, "total", node->total);
sfile_add_integer (output, "self", node->size);
sfile_add_integer (output, "toplevel", node->toplevel);
sfile_end_add (output, "node", node);
serialize_call_tree (node->siblings, output);
serialize_call_tree (node->children, output);
}
gboolean
profile_save (Profile *profile,
const char *file_name,
GError **err)
{
gboolean result;
GList *profile_objects;
GList *list;
SFormat *format = create_format ();
SFileOutput *output = sfile_output_new (format);
sfile_begin_add_record (output, "profile");
sfile_add_integer (output, "size", profile_get_size (profile));
sfile_add_pointer (output, "call_tree",
stack_stash_get_root (profile->stash));
profile_objects = profile_get_objects (profile);
sfile_begin_add_list (output, "objects");
for (list = profile_objects; list != NULL; list = list->next)
{
ProfileObject *object = list->data;
sfile_begin_add_record (output, "object");
sfile_add_string (output, "name", object->name);
sfile_add_integer (output, "total", object->total);
sfile_add_integer (output, "self", object->self);
sfile_end_add (output, "object", object->name);
}
g_list_foreach (profile_objects, (GFunc)g_free, NULL);
g_list_free (profile_objects);
sfile_end_add (output, "objects", NULL);
sfile_begin_add_list (output, "nodes");
serialize_call_tree (stack_stash_get_root (profile->stash), output);
sfile_end_add (output, "nodes", NULL);
sfile_end_add (output, "profile", NULL);
result = sfile_output_save (output, file_name, err);
sfile_output_free (output);
sformat_free (format);
return result;
}
Profile *
profile_load (const char *filename, GError **err)
{
SFormat *format;
SFileInput *input;
Profile *profile;
int n, i;
StackNode *root;
format = create_format ();
input = sfile_load (filename, format, err);
if (!input)
return NULL;
profile = g_new (Profile, 1);
sfile_begin_get_record (input, "profile");
sfile_get_integer (input, "size", NULL);
sfile_get_pointer (input, "call_tree", (gpointer *)&root);
n = sfile_begin_get_list (input, "objects");
for (i = 0; i < n; ++i)
{
char *string;
sfile_begin_get_record (input, "object");
sfile_get_string (input, "name", &string);
sfile_get_integer (input, "total", NULL);
sfile_get_integer (input, "self", NULL);
sfile_end_get (input, "object", string);
}
sfile_end_get (input, "objects", NULL);
profile->stash = stack_stash_new ((GDestroyNotify)g_free);
n = sfile_begin_get_list (input, "nodes");
for (i = 0; i < n; ++i)
{
StackNode *node = stack_node_new (profile->stash);
gint32 size;
gint32 total;
sfile_begin_get_record (input, "node");
sfile_get_pointer (input, "object", (gpointer *)&node->data);
sfile_get_pointer (input, "siblings", (gpointer *)&node->siblings);
sfile_get_pointer (input, "children", (gpointer *)&node->children);
sfile_get_pointer (input, "parent", (gpointer *)&node->parent);
sfile_get_integer (input, "total", &total);
sfile_get_integer (input, "self", (gint32 *)&size);
sfile_get_integer (input, "toplevel", NULL);
node->total = total;
node->size = size;
sfile_end_get (input, "node", node);
g_assert (node->siblings != (void *)0x11);
}
sfile_end_get (input, "nodes", NULL);
sfile_end_get (input, "profile", NULL);
sfile_input_free (input);
sformat_free (format);
stack_stash_set_root (profile->stash, root);
return profile;
}
Profile *
profile_new (StackStash *stash)
{
Profile *profile = g_new (Profile, 1);
profile->stash = stack_stash_ref (stash);
return profile;
}
static void
add_trace_to_tree (StackLink *trace, gint size, gpointer data)
{
StackLink *link;
ProfileDescendant *parent = NULL;
ProfileDescendant **tree = data;
link = trace;
while (link->next)
link = link->next;
for (; link != NULL; link = link->prev)
{
gpointer address = U64_TO_POINTER (link->data);
ProfileDescendant *prev = NULL;
ProfileDescendant *match = NULL;
for (match = *tree; match != NULL; prev = match, match = match->siblings)
{
if (match->name == address)
{
if (prev)
{
/* Move to front */
prev->siblings = match->siblings;
match->siblings = *tree;
*tree = match;
}
break;
}
}
if (!match)
{
/* Have we seen this object further up the tree? */
for (match = parent; match != NULL; match = match->parent)
{
if (match->name == address)
break;
}
}
if (!match)
{
match = g_new (ProfileDescendant, 1);
match->name = address;
match->cumulative = 0;
match->self = 0;
match->children = NULL;
match->parent = parent;
match->siblings = *tree;
*tree = match;
}
tree = &(match->children);
parent = match;
}
parent->self += size;
while (parent)
{
parent->cumulative += size;
parent = parent->parent;
}
}
ProfileDescendant *
profile_create_descendants (Profile *profile,
char *object_name)
{
ProfileDescendant *tree = NULL;
StackNode *node = stack_stash_find_node (profile->stash, object_name);
while (node)
{
if (node->toplevel)
stack_node_foreach_trace (node, add_trace_to_tree, &tree);
node = node->next;
}
return tree;
}
static ProfileCaller *
profile_caller_new (void)
{
ProfileCaller *caller = g_new (ProfileCaller, 1);
caller->next = NULL;
caller->self = 0;
caller->total = 0;
return caller;
}
ProfileCaller *
profile_list_callers (Profile *profile,
char *callee_name)
{
StackNode *node;
StackNode *callees;
GHashTable *callers_by_name;
GHashTable *processed_callers;
ProfileCaller *result = NULL;
callers_by_name = g_hash_table_new (g_direct_hash, g_direct_equal);
processed_callers = g_hash_table_new (g_direct_hash, g_direct_equal);
callees = stack_stash_find_node (profile->stash, callee_name);
for (node = callees; node != NULL; node = node->next)
{
ProfileCaller *caller;
if (!node->parent)
continue;
caller = g_hash_table_lookup (
callers_by_name, U64_TO_POINTER (node->parent->data));
if (!caller)
{
caller = profile_caller_new ();
caller->name = U64_TO_POINTER (node->parent->data);
caller->total = 0;
caller->self = 0;
caller->next = result;
result = caller;
g_hash_table_insert (
callers_by_name, U64_TO_POINTER (node->parent->data), caller);
}
}
for (node = callees; node != NULL; node = node->next)
{
StackNode *top_caller = node->parent;
StackNode *top_callee = node;
StackNode *n;
ProfileCaller *caller;
if (!node->parent)
continue;
for (n = node; n && n->parent; n = n->parent)
{
if (n->data == node->data &&
n->parent->data == node->parent->data)
{
top_caller = n->parent;
top_callee = n;
}
}
caller = g_hash_table_lookup (
callers_by_name, U64_TO_POINTER (node->parent->data));
if (!g_hash_table_lookup (processed_callers, top_caller))
{
caller->total += top_callee->total;
g_hash_table_insert (processed_callers, top_caller, top_caller);
}
caller->self += node->size;
}
g_hash_table_destroy (processed_callers);
g_hash_table_destroy (callers_by_name);
return result;
}
#if 0
/* This code generates a list of all ancestors, rather than
* all callers. It turned out to not work well in practice,
* but on the other hand the single list of callers is not
* all that great either, so we'll keep it around commented
* out for now
*/
static void
add_to_list (gpointer key,
gpointer value,
gpointer user_data)
{
GList **list = user_data;
*list = g_list_prepend (*list, value);
}
static GList *
listify_hash_table (GHashTable *hash_table)
{
GList *result = NULL;
g_hash_table_foreach (hash_table, add_to_list, &result);
return result;
}
ProfileCaller *
profile_list_ancestors (Profile *profile,
char *callee_name)
{
StackNode *callees;
StackNode *node;
GHashTable *callers_by_name;
ProfileCaller *result = NULL;
callers_by_name = g_hash_table_new (g_direct_hash, g_direct_equal);
callees = stack_stash_find_node (profile->stash, callee_name);
for (node = callees; node != NULL; node = node->next)
{
StackNode *n;
gboolean seen_recursive_call;
GHashTable *total_ancestors;
GHashTable *all_ancestors;
GList *all, *list;
/* Build a list of those ancestors that should get assigned
* totals. If this callee does not have any recursive calls
* higher up, that means all of it's ancestors. If it does
* have a recursive call, only the one between this node
* and the recursive call should get assigned total
*/
seen_recursive_call = FALSE;
all_ancestors = g_hash_table_new (g_direct_hash, g_direct_equal);
total_ancestors = g_hash_table_new (g_direct_hash, g_direct_equal);
for (n = node->parent; n; n = n->parent)
{
if (!seen_recursive_call)
{
g_hash_table_insert (total_ancestors, n->address, n);
}
else
{
g_hash_table_remove (total_ancestors, n->address);
}
g_hash_table_insert (all_ancestors, n->address, n);
if (n->address == node->address)
seen_recursive_call = TRUE;
}
all = listify_hash_table (all_ancestors);
for (list = all; list; list = list->next)
{
ProfileCaller *caller;
StackNode *ancestor = list->data;
caller = g_hash_table_lookup (callers_by_name, ancestor->address);
if (!caller)
{
caller = profile_caller_new ();
g_hash_table_insert (
callers_by_name, ancestor->address, caller);
caller->name = ancestor->address;
caller->next = result;
result = caller;
}
caller->self += node->size;
if (g_hash_table_lookup (total_ancestors, ancestor->address))
caller->total += node->total;
}
g_list_free (all);
g_hash_table_destroy (all_ancestors);
g_hash_table_destroy (total_ancestors);
}
return result;
}
#endif
void
profile_free (Profile *profile)
{
stack_stash_unref (profile->stash);
g_free (profile);
}
void
profile_descendant_free (ProfileDescendant *descendant)
{
if (!descendant)
return;
profile_descendant_free (descendant->siblings);
profile_descendant_free (descendant->children);
g_free (descendant);
}
void
profile_caller_free (ProfileCaller *caller)
{
if (!caller)
return;
profile_caller_free (caller->next);
g_free (caller);
}
static int
compute_total (StackNode *node)
{
StackNode *n;
int total = 0;
for (n = node; n != NULL; n = n->next)
{
if (n->toplevel)
total += n->total;
}
return total;
}
static void
build_object_list (StackNode *node, gpointer data)
{
GList **objects = data;
ProfileObject *obj;
StackNode *n;
obj = g_new (ProfileObject, 1);
obj->name = U64_TO_POINTER (node->data);
obj->total = compute_total (node);
obj->self = 0;
for (n = node; n != NULL; n = n->next)
obj->self += n->size;
*objects = g_list_prepend (*objects, obj);
}
GList *
profile_get_objects (Profile *profile)
{
GList *objects = NULL;
stack_stash_foreach_by_address (
profile->stash, build_object_list, &objects);
return objects;
}
gint
profile_get_size (Profile *profile)
{
StackNode *n;
gint size = 0;
for (n = stack_stash_get_root (profile->stash); n != NULL; n = n->siblings)
size += n->total;
return size;
}

78
lib/profile.h Normal file
View File

@ -0,0 +1,78 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* 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.
*/
#ifndef PROFILE_H
#define PROFILE_H
#include <glib.h>
#include "stackstash.h"
typedef struct Profile Profile;
typedef struct ProfileObject ProfileObject;
typedef struct ProfileDescendant ProfileDescendant;
typedef struct ProfileCaller ProfileCaller;
struct ProfileObject
{
char * name; /* identifies this object uniquely
* (the pointer itself, not the
* string)
*/
guint total; /* sum of all toplevel totals */
guint self; /* sum of all selfs */
};
struct ProfileDescendant
{
char * name;
guint self;
guint cumulative;
ProfileDescendant * parent;
ProfileDescendant * siblings;
ProfileDescendant * children;
};
struct ProfileCaller
{
char * name;
guint total;
guint self;
ProfileCaller * next;
};
Profile * profile_new (StackStash *stash);
void profile_free (Profile *profile);
gint profile_get_size (Profile *profile);
GList * profile_get_objects (Profile *profile);
ProfileDescendant *profile_create_descendants (Profile *prf,
char *object);
ProfileCaller * profile_list_callers (Profile *profile,
char *object);
void profile_caller_free (ProfileCaller *caller);
void profile_descendant_free (ProfileDescendant *descendant);
gboolean profile_save (Profile *profile,
const char *file_name,
GError **err);
Profile * profile_load (const char *filename,
GError **err);
#endif /* PROFILE_H */

1180
lib/sfile.c Normal file

File diff suppressed because it is too large Load Diff

169
lib/sfile.h Normal file
View File

@ -0,0 +1,169 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* 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 "sformat.h"
typedef struct SFileInput SFileInput;
typedef struct SFileOutput SFileOutput;
#if 0
Serializer *serializer_new (const char *version);
void serializer_set_format (Serializer *serializer,
SerializerFormat *format);
SerializerFormat *serializer_make_list (Serializer *serializer,
const char *name,
SerializerFormat *contents);
SerializerFormat *serializer_make_record (Serializer *serializer,
const char *name,
SerializerFormat *contents1,
...);
SerializerFormat *serializer_make_integer (Serializer *serialiser,
const char *name);
SerializerFormat *serializer_make_pointer (Serializer *serialiser,
const char *name,
SerializerFormat *target_type);
#endif
/* A possibly better API/naming scheme
*
* Serializer *serializer_new (SerializerFormat *format);
*
* SerializerReadContext *serializer_begin_read_filename (serializer *serialize,
* const char *filename,
* GError *err);
* serializer_get_blah (SerializerReadContext *);
* void serialzier_end_read (...);
*
* SerializerWritecontext *...;
* serializer_begin_write (context);
* serializer_write_int ();
* serializer_end_write (..., GError **err);
*
*
* For formats consider:
*
* Format *format_new (void);
* void format_free (void);
* Content *format_create_record (Format *format, Content *c1, ...);
* Content *format_create_list (Format *format, Content *t);
* Content *format_create_pointer (Format *format, Content *pointer_type);
*
* void format_set_record (Format *f, Content *r);
* Content *new_record (Content *c1, ...);
*
* List *format_new_list (Format *f
*
*
* Consider adding optional elements:
*
* sformat_new_optional (gpointer content)
*
* enums, optionals, selections, empties
*
*
* Other things:
*
* "selections" - when several different types are possible - would need lambda transitions in and out
*
* ability to allow 'ignored' elements that are simply skipped at parse time. This could become important
* for future-proofing files.
*
* unions maybe?
*
*
*
*
*==============================================
* Also think about versioning - apps will want to be able to read and write
* different versions of the format, and they want to be able to sniff the
* format + version
*
* The version should be part of the format. There should be a
* const char *sfile_sniff (const filename);
* that will return NULL (+ error) if the file can't be parsed
*
*/
/* - Describing Types - */
/* - Reading - */
SFileInput * sfile_load (const char *filename,
SFormat *format,
GError **err);
void sfile_begin_get_record (SFileInput *file,
const char *name);
int sfile_begin_get_list (SFileInput *file,
const char *name);
void sfile_get_pointer (SFileInput *file,
const char *name,
gpointer *pointer);
void sfile_get_integer (SFileInput *file,
const char *name,
gint32 *integer);
void sfile_get_string (SFileInput *file,
const char *name,
char **string);
void sfile_end_get (SFileInput *file,
const char *name,
gpointer object);
void sfile_input_free (SFileInput *file);
#if 0
/* incremental loading (worth considering at least) */
SFileLoader *sfile_loader_new (SFormat *format);
void sfile_loader_add_text (SFileLoader *loader,
const char *text,
int len);
SFile * sfile_loader_finish (SFileLoader *loader,
GError **err);
void sfile_loader_free (SFileLoader *loader);
#endif
/* - Writing - */
/* FIXME - not10: see if we can't get rid of the names. It
* should be possible to pass NULL to state_transition_check()
* and have it interprete that as "whatever". We would need
* a way to get the name back then, though.
*/
SFileOutput * sfile_output_new (SFormat *format);
void sfile_begin_add_record (SFileOutput *file,
const char *name);
void sfile_begin_add_list (SFileOutput *file,
const char *name);
void sfile_end_add (SFileOutput *file,
const char *name,
gpointer object);
void sfile_add_string (SFileOutput *file,
const char *name,
const char *string);
void sfile_add_integer (SFileOutput *file,
const char *name,
int integer);
void sfile_add_pointer (SFileOutput *file,
const char *name,
gpointer pointer);
gboolean sfile_output_save (SFileOutput *sfile,
const char *filename,
GError **err);
void sfile_output_free (SFileOutput *sfile);

646
lib/sformat.c Normal file
View File

@ -0,0 +1,646 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* Copyright 2004, 2005, 2006, 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 <glib.h>
#include <string.h>
#include "sformat.h"
typedef struct State State;
typedef struct Transition Transition;
typedef struct Fragment Fragment;
/*
* Format
*/
struct SFormat
{
State * begin;
State * end;
GQueue * types;
GQueue * transitions;
GQueue * states;
};
/*
* Type
*/
typedef enum
{
TYPE_POINTER,
TYPE_STRING,
TYPE_INTEGER,
TYPE_RECORD,
TYPE_LIST,
TYPE_FORWARD
} TypeKind;
struct SType
{
TypeKind kind;
char *name;
Transition *enter, *exit;
SType *target; /* If kind is TYPE_POINTER */
};
static void type_free (SType *type);
/*
* Transition
*/
typedef enum
{
BEGIN,
VALUE,
END
} TransitionKind;
struct Transition
{
SType * type;
TransitionKind kind;
State * to;
};
static Transition *transition_new (SFormat *format,
TransitionKind kind,
SType *type,
State *from,
State *to);
static void transition_free (Transition *transition);
static void transition_set_to_state (Transition *transition,
State *to_state);
/*
* State
*/
struct State
{
GQueue *transitions;
};
static State *state_new (SFormat *format);
static void state_add_transition (State *state,
Transition *transition);
static void state_free (State *state);
/*
* Format
*/
SFormat *
sformat_new (void)
{
/* FIXME: should probably be refcounted, and an SContext
* should have a ref on the format
*/
SFormat *format = g_new0 (SFormat, 1);
format->begin = NULL;
format->end = NULL;
format->types = g_queue_new ();
format->states = g_queue_new ();
format->transitions = g_queue_new ();
return format;
}
void
sformat_free (SFormat *format)
{
GList *list;
for (list = format->types->head; list; list = list->next)
{
SType *type = list->data;
type_free (type);
}
g_queue_free (format->types);
for (list = format->states->head; list; list = list->next)
{
State *state = list->data;
state_free (state);
}
g_queue_free (format->states);
for (list = format->transitions->head; list; list = list->next)
{
Transition *transition = list->data;
transition_free (transition);
}
g_queue_free (format->transitions);
g_free (format);
}
void
sformat_set_type (SFormat *format,
SType *type)
{
format->begin = state_new (format);
format->end = state_new (format);
state_add_transition (format->begin, type->enter);
transition_set_to_state (type->exit, format->end);
}
/*
* Type
*/
static SType *
type_new (SFormat *format,
TypeKind kind,
const char *name)
{
SType *type = g_new0 (SType, 1);
type->kind = kind;
type->name = name? g_strdup (name) : NULL;
type->enter = NULL;
type->exit = NULL;
type->target = NULL;
g_queue_push_tail (format->types, type);
return type;
}
static SType *
type_new_from_forward (SFormat *format,
TypeKind kind,
const char *name,
SForward *forward)
{
SType *type;
if (forward)
{
type = (SType *)forward;
type->kind = kind;
type->name = g_strdup (name);
}
else
{
type = type_new (format, kind, name);
}
return type;
}
static SType *
type_new_value (SFormat *format, TypeKind kind, const char *name)
{
SType *type = type_new (format, kind, name);
State *before, *after;
Transition *value;
before = state_new (format);
after = state_new (format);
type->enter = transition_new (format, BEGIN, type, NULL, before);
type->exit = transition_new (format, END, type, after, NULL);
value = transition_new (format, VALUE, type, before, after);
return type;
}
static void
type_free (SType *type)
{
g_free (type->name);
g_free (type);
}
SForward *
sformat_declare_forward (SFormat *format)
{
SType *type = type_new (format, TYPE_FORWARD, NULL);
return (SForward *)type;
}
static GQueue *
expand_varargs (SType *content1,
va_list args)
{
GQueue *types = g_queue_new ();
SType *type;
g_queue_push_tail (types, content1);
type = va_arg (args, SType *);
while (type)
{
g_queue_push_tail (types, type);
type = va_arg (args, SType *);
}
return types;
}
SType *
sformat_make_record (SFormat *format,
const char *name,
SForward *forward,
SType *content,
...)
{
SType *type;
va_list args;
GQueue *types;
GList *list;
State *begin, *state;
/* Build queue of child types */
va_start (args, content);
types = expand_varargs (content, args);
va_end (args);
/* chain types together */
state = begin = state_new (format);
for (list = types->head; list != NULL; list = list->next)
{
SType *child_type = list->data;
state_add_transition (state, child_type->enter);
state = state_new (format);
transition_set_to_state (child_type->exit, state);
}
g_queue_free (types);
/* create and return the new type */
type = type_new_from_forward (format, TYPE_RECORD, name, forward);
type->enter = transition_new (format, BEGIN, type, NULL, begin);
type->exit = transition_new (format, END, type, state, NULL);
return type;
}
SType *
sformat_make_list (SFormat *format,
const char *name,
SForward *forward,
SType *child_type)
{
SType *type;
State *list_state;
type = type_new_from_forward (format, TYPE_LIST, name, forward);
list_state = state_new (format);
type->enter = transition_new (format, BEGIN, type, NULL, list_state);
type->exit = transition_new (format, END, type, list_state, NULL);
state_add_transition (list_state, child_type->enter);
transition_set_to_state (child_type->exit, list_state);
return type;
}
SType *
sformat_make_pointer (SFormat *format,
const char *name,
SForward *forward)
{
SType *type = type_new_value (format, TYPE_POINTER, name);
type->target = (SType *)forward;
return type;
}
SType *
sformat_make_integer (SFormat *format,
const char *name)
{
return type_new_value (format, TYPE_INTEGER, name);
}
SType *
sformat_make_string (SFormat *format,
const char *name)
{
return type_new_value (format, TYPE_STRING, name);
}
gboolean
stype_is_record (SType *type)
{
return type->kind == TYPE_RECORD;
}
gboolean
stype_is_list (SType *type)
{
return type->kind == TYPE_LIST;
}
gboolean
stype_is_pointer (SType *type)
{
return type->kind == TYPE_POINTER;
}
gboolean
stype_is_integer (SType *type)
{
return type->kind == TYPE_INTEGER;
}
gboolean
stype_is_string (SType *type)
{
return type->kind == TYPE_STRING;
}
SType *
stype_get_target_type (SType *type)
{
g_return_val_if_fail (stype_is_pointer (type), NULL);
return type->target;
}
const char *
stype_get_name (SType *type)
{
return type->name;
}
/* Consider adding unions at some point
*
* To be useful they should probably be anonymous, so that
* the union itself doesn't have a representation in the
* xml file.
*
* API:
* sformat_new_union (gpointer content1, ...);
*
* char *content = begin_get_union ();
* if (strcmp (content, ...) == 0)
* get_pointer ();
* else if (strcmp (content, ...) == 0)
*
* ;
*
* Annoying though, that we then won't have the nice one-to-one
* correspondence between begin()/end() calls and <element></element>s
* Actually, we will probably have to have <union>asdlfkj</union>
* elements. That will make things a lot easier, and unions are
* still pretty useful if you put big things like lists in them.
*
* Or maybe just give them a name ...
*
* We may also consider adding anonymous records. These will
* not be able to have pointers associated with them though
* (because there wouldn't be a natural place
*
*
* Also consider adding the following data types:
*
* - Binary blobs of data, stored as base64 perhaps
*
* - floating point values. How do we store those portably
* without losing precision? Gnumeric may know.
*
* - enums, stored as strings
*
* - booleans.
*/
/*
* State
*/
static State *
state_new (SFormat *format)
{
State *state = g_new0 (State, 1);
state->transitions = g_queue_new ();
g_queue_push_tail (format->states, state);
return state;
}
static void
state_add_transition (State *state,
Transition *transition)
{
g_queue_push_tail (state->transitions, transition);
}
static void
state_free (State *state)
{
g_queue_free (state->transitions);
g_free (state);
}
/*
* Transition
*/
static Transition *
transition_new (SFormat *format,
TransitionKind kind,
SType *type,
State *from,
State *to)
{
Transition *transition = g_new0 (Transition, 1);
transition->type = type;
transition->kind = kind;
transition->to = to;
if (from)
state_add_transition (from, transition);
g_queue_push_tail (format->transitions, transition);
return transition;
}
static void
transition_free (Transition *transition)
{
g_free (transition);
}
static void
transition_set_to_state (Transition *transition,
State *to_state)
{
transition->to = to_state;
}
/* Context */
struct SContext
{
SFormat *format;
State *state;
};
SContext *
scontext_new (SFormat *format)
{
SContext *context = g_new0 (SContext, 1);
context->format = format;
context->state = format->begin;
return context;
}
static SType *
do_transition (SContext *context,
TransitionKind kind,
const char *element)
{
GList *list;
for (list = context->state->transitions->head; list; list = list->next)
{
Transition *transition = list->data;
if (transition->kind == kind)
{
if (kind == VALUE || strcmp (transition->type->name, element) == 0)
{
context->state = transition->to;
return transition->type;
}
}
}
return NULL;
}
SType *
scontext_begin (SContext *context,
const char *element)
{
return do_transition (context, BEGIN, element);
}
SType *
scontext_text (SContext *context)
{
return do_transition (context, VALUE, NULL);
}
SType *
scontext_end (SContext *context,
const char *element)
{
return do_transition (context, END, element);
}
gboolean
scontext_is_finished (SContext *context)
{
return context->state == context->format->end;
}
void
scontext_free (SContext *context)
{
g_free (context);
}
/* assorted stuff */
#if 0
static const State *
state_transition_check (const State *state,
const char *element,
TransitionKind kind,
SType *type)
{
GList *list;
for (list = state->transitions->head; list; list = list->next)
{
Transition *transition = list->data;
if (transition->kind == kind &&
strcmp (element, transition->element) == 0)
{
*type = transition->type;
return transition->to;
}
}
return NULL;
}
static const State *
state_transition_begin (const State *state, const char *element, SType *type)
{
return state_transition_check (state, element, BEGIN, type);
}
static const State *
state_transition_end (const State *state, const char *element, SType *type)
{
return state_transition_check (state, element, END, type);
}
static const State *
state_transition_text (const State *state, SType *type, SType *target_type)
{
GList *list;
for (list = state->transitions->head; list; list = list->next)
{
Transition *transition = list->data;
if (transition->kind == VALUE)
{
*type = transition->type;
if (*type == TYPE_POINTER && target_type)
*target_type = transition->target_type;
/* There will never be more than one allowed value transition for
* a given state
*/
return transition->to;
}
}
return NULL;
}
#endif

67
lib/sformat.h Normal file
View File

@ -0,0 +1,67 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* Copyright 2004, 2005, 2006, 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.
*/
#ifndef SFORMAT_H
#define SFORMAT_H
typedef struct SFormat SFormat;
typedef struct SType SType;
typedef struct SForward SForward;
typedef struct SContext SContext;
SFormat *sformat_new (void);
void sformat_free (SFormat *format);
void sformat_set_type (SFormat *format,
SType *type);
SForward *sformat_declare_forward (SFormat *format);
SType *sformat_make_record (SFormat *format,
const char *name,
SForward *forward,
SType *content,
...);
SType *sformat_make_list (SFormat *format,
const char *name,
SForward *forward,
SType *type);
SType *sformat_make_pointer (SFormat *format,
const char *name,
SForward *type);
SType *sformat_make_integer (SFormat *format,
const char *name);
SType *sformat_make_string (SFormat *format,
const char *name);
gboolean stype_is_record (SType *type);
gboolean stype_is_list (SType *type);
gboolean stype_is_pointer (SType *type);
gboolean stype_is_integer (SType *type);
gboolean stype_is_string (SType *type);
SType *stype_get_target_type (SType *type);
const char *stype_get_name (SType *type);
SContext *scontext_new (SFormat *format);
SType *scontext_begin (SContext *context,
const char *element);
SType *scontext_text (SContext *context);
SType *scontext_end (SContext *context,
const char *element);
gboolean scontext_is_finished (SContext *context);
void scontext_free (SContext *context);
#endif

215
lib/signal-handler.c Normal file
View File

@ -0,0 +1,215 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright (C) 2005 Søren Sandmann (sandmann@daimi.au.dk)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include "watch.h"
#include "signal-handler.h"
typedef struct SignalWatch SignalWatch;
struct SignalWatch
{
int signo;
SignalFunc handler;
gpointer user_data;
struct sigaction old_action;
SignalWatch * next;
};
static int read_end = -1;
static int write_end = -1;
static SignalWatch *signal_watches = NULL;
static SignalWatch *
lookup_signal_watch (int signo)
{
SignalWatch *w;
for (w = signal_watches; w != NULL; w = w->next)
{
if (w->signo == signo)
return w;
}
return NULL;
}
static void
remove_signal_watch (SignalWatch *watch)
{
SignalWatch *prev, *w;
g_return_if_fail (watch != NULL);
prev = NULL;
for (w = signal_watches; w != NULL; w = w->next)
{
if (w == watch)
{
if (prev)
prev->next = w->next;
else
signal_watches = w->next;
break;
}
prev = w;
}
}
static void
signal_handler (int signo,
siginfo_t *info,
void *data)
{
/* FIXME: I suppose we should handle short
* and non-successful writes ...
*
* And also, there is a deadlock if so many signals arrive that
* write() blocks. Then we will be stuck right here, and the
* main loop will never run. Kinda hard to fix without dropping
* signals ...
*
*/
write (write_end, &signo, sizeof (int));
}
static void
on_read (gpointer data)
{
SignalWatch *watch;
int signo;
/* FIXME: handle short read I suppose */
read (read_end, &signo, sizeof (int));
watch = lookup_signal_watch (signo);
if (watch)
watch->handler (signo, watch->user_data);
}
static gboolean
create_pipe (int *read_end,
int *write_end,
GError **err)
{
int p[2];
if (pipe (p) < 0)
{
/* FIXME - create an error */
return FALSE;
}
/* FIXME: We should probably make the fd's non-blocking */
if (read_end)
*read_end = p[0];
if (write_end)
*write_end = p[1];
return TRUE;
}
static gboolean
install_signal_handler (int signo,
struct sigaction *old_action,
GError **err)
{
struct sigaction action;
memset (&action, 0, sizeof (action));
action.sa_sigaction = signal_handler;
sigemptyset (&action.sa_mask);
action.sa_flags = SA_SIGINFO;
if (sigaction (signo, &action, old_action) < 0)
{
/* FIXME - create an error */
return TRUE;
}
return TRUE;
}
static void
signal_watch_free (SignalWatch *watch)
{
remove_signal_watch (watch);
g_free (watch);
}
gboolean
signal_set_handler (int signo,
SignalFunc handler,
gpointer data,
GError **err)
{
SignalWatch *watch;
g_return_val_if_fail (lookup_signal_watch (signo) == NULL, FALSE);
if (read_end == -1)
{
if (!create_pipe (&read_end, &write_end, err))
return FALSE;
fd_add_watch (read_end, NULL);
fd_set_read_callback (read_end, on_read);
}
watch = g_new0 (SignalWatch, 1);
watch->signo = signo;
watch->handler = handler;
watch->user_data = data;
watch->next = signal_watches;
signal_watches = watch;
if (!install_signal_handler (signo, &watch->old_action, err))
{
signal_watch_free (watch);
return FALSE;
}
return TRUE;
}
void
signal_unset_handler (int signo)
{
SignalWatch *watch;
watch = lookup_signal_watch (signo);
g_return_if_fail (watch != NULL);
sigaction (signo, &watch->old_action, NULL);
signal_watch_free (watch);
}

27
lib/signal-handler.h Normal file
View File

@ -0,0 +1,27 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright (C) 2005 Søren Sandmann (sandmann@daimi.au.dk)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
typedef void (* SignalFunc) (int signo, gpointer data);
gboolean signal_set_handler (int signo,
SignalFunc handler,
gpointer data,
GError **err);
void signal_unset_handler (int signo);

387
lib/stackstash.c Normal file
View File

@ -0,0 +1,387 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* 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 "stackstash.h"
struct StackStash
{
int ref_count;
StackNode * root;
GHashTable * nodes_by_data;
GDestroyNotify destroy;
StackNode * cached_nodes;
GPtrArray * blocks;
};
static void
decorate_node (StackNode *node,
StackStash *stash)
{
StackNode *n;
if (!node)
return;
decorate_node (node->siblings, stash);
decorate_node (node->children, stash);
node->next = g_hash_table_lookup (stash->nodes_by_data, &node->data);
g_hash_table_insert (stash->nodes_by_data, &node->data, node);
/* FIXME: This could be done more efficiently
* by keeping track of the ancestors we have seen.
*/
node->toplevel = TRUE;
for (n = node->parent; n != NULL; n = n->parent)
{
if (n->data == node->data)
{
node->toplevel = FALSE;
break;
}
}
}
static unsigned int
address_hash (gconstpointer key)
{
const uint64_t *addr = key;
return *addr;
}
static gboolean
address_equal (gconstpointer key1, gconstpointer key2)
{
const uint64_t *addr1 = key1;
const uint64_t *addr2 = key2;
return *addr1 == *addr2;
}
static void
stack_stash_decorate (StackStash *stash)
{
if (stash->nodes_by_data)
return;
stash->nodes_by_data = g_hash_table_new (address_hash, address_equal);
decorate_node (stash->root, stash);
}
static void
free_key (gpointer key,
gpointer value,
gpointer data)
{
GDestroyNotify destroy = data;
uint64_t u64 = *(uint64_t *)key;
destroy (U64_TO_POINTER (u64));
}
static void
stack_stash_undecorate (StackStash *stash)
{
if (stash->nodes_by_data)
{
if (stash->destroy)
{
g_hash_table_foreach (
stash->nodes_by_data, free_key, stash->destroy);
}
g_hash_table_destroy (stash->nodes_by_data);
stash->nodes_by_data = NULL;
}
}
static GHashTable *
get_nodes_by_data (StackStash *stash)
{
if (!stash->nodes_by_data)
stack_stash_decorate (stash);
return stash->nodes_by_data;
}
StackNode *
stack_node_new (StackStash *stash)
{
StackNode *node;
if (!stash->cached_nodes)
{
#define BLOCK_SIZE 32768
#define N_NODES (BLOCK_SIZE / sizeof (StackNode))
StackNode *block = g_malloc (BLOCK_SIZE);
int i;
for (i = 0; i < N_NODES; ++i)
{
block[i].next = stash->cached_nodes;
stash->cached_nodes = &(block[i]);
}
g_ptr_array_add (stash->blocks, block);
}
node = stash->cached_nodes;
stash->cached_nodes = node->next;
node->siblings = NULL;
node->children = NULL;
node->data = 0;
node->parent = NULL;
node->size = 0;
node->next = NULL;
node->total = 0;
return node;
}
/* "destroy", if non-NULL, is called once on every address */
static StackStash *
create_stack_stash (GDestroyNotify destroy)
{
StackStash *stash = g_new (StackStash, 1);
stash->root = NULL;
stash->nodes_by_data = NULL;
stash->ref_count = 1;
stash->destroy = destroy;
stash->cached_nodes = NULL;
stash->blocks = g_ptr_array_new ();
return stash;
}
/* Stach */
StackStash *
stack_stash_new (GDestroyNotify destroy)
{
return create_stack_stash (destroy);
}
static void
stack_stash_free (StackStash *stash)
{
int i;
stack_stash_undecorate (stash);
for (i = 0; i < stash->blocks->len; ++i)
g_free (stash->blocks->pdata[i]);
g_ptr_array_free (stash->blocks, TRUE);
g_free (stash);
}
StackNode *
stack_stash_add_trace (StackStash *stash,
uint64_t *addrs,
int n_addrs,
int size)
{
StackNode **location = &(stash->root);
StackNode *parent = NULL;
int i;
if (!n_addrs)
return NULL;
if (stash->nodes_by_data)
stack_stash_undecorate (stash);
for (i = n_addrs - 1; i >= 0; --i)
{
StackNode *match = NULL;
StackNode *prev;
/* FIXME: On x86-64 we don't get proper stacktraces which means
* each node can have tons of children. That makes this loop
* here show up on profiles.
*
* Not sure what can be done about it aside from actually fixing
* x86-64 to get stacktraces.
*/
prev = NULL;
for (match = *location; match; prev = match, match = match->siblings)
{
if (match->data == addrs[i])
{
if (prev)
{
/* move to front */
prev->siblings = match->siblings;
match->siblings = *location;
*location = match;
}
break;
}
}
if (!match)
{
match = stack_node_new (stash);
match->data = addrs[i];
match->siblings = *location;
match->parent = parent;
*location = match;
}
match->total += size;
location = &(match->children);
parent = match;
}
parent->size += size;
return parent;
}
static void
do_callback (StackNode *node,
StackLink *trace,
StackFunction func,
gpointer data)
{
StackLink link;
if (trace)
trace->prev = &link;
link.next = trace;
link.prev = NULL;
while (node)
{
link.data = node->data;
if (node->size)
func (&link, node->size, data);
do_callback (node->children, &link, func, data);
node = node->siblings;
}
if (trace)
trace->prev = NULL;
}
void
stack_stash_foreach (StackStash *stash,
StackFunction stack_func,
gpointer data)
{
do_callback (stash->root, NULL, stack_func, data);
}
void
stack_node_foreach_trace (StackNode *node,
StackFunction func,
gpointer data)
{
StackLink link;
link.next = NULL;
link.data = node->data;
link.prev = NULL;
if (node->size)
func (&link, node->size, data);
do_callback (node->children, &link, func, data);
}
void
stack_stash_unref (StackStash *stash)
{
stash->ref_count--;
if (stash->ref_count == 0)
stack_stash_free (stash);
}
StackStash *
stack_stash_ref (StackStash *stash)
{
stash->ref_count++;
return stash;
}
StackNode *
stack_stash_find_node (StackStash *stash,
gpointer data)
{
uint64_t u64 = POINTER_TO_U64 (data);
g_return_val_if_fail (stash != NULL, NULL);
return g_hash_table_lookup (get_nodes_by_data (stash), &u64);
}
typedef struct
{
StackNodeFunc func;
gpointer data;
} Info;
static void
do_foreach (gpointer key, gpointer value, gpointer data)
{
Info *info = data;
info->func (value, info->data);
}
void
stack_stash_foreach_by_address (StackStash *stash,
StackNodeFunc func,
gpointer data)
{
Info info;
info.func = func;
info.data = data;
g_hash_table_foreach (get_nodes_by_data (stash), do_foreach, &info);
}
StackNode *
stack_stash_get_root (StackStash *stash)
{
return stash->root;
}
void
stack_stash_set_root (StackStash *stash,
StackNode *root)
{
g_return_if_fail (stash->root == NULL);
stash->root = root;
}

86
lib/stackstash.h Normal file
View File

@ -0,0 +1,86 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* 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.
*/
#ifndef STACK_STASH_H
#define STACK_STASH_H
#include <glib.h>
#include <stdint.h>
typedef struct StackStash StackStash;
typedef struct StackNode StackNode;
typedef struct StackLink StackLink;
#define U64_TO_POINTER(u) ((void *)(intptr_t)u)
#define POINTER_TO_U64(p) ((uint64_t)(intptr_t)p)
struct StackNode
{
uint64_t data;
guint total : 32;
guint size : 31;
guint toplevel : 1;
StackNode * parent;
StackNode * siblings;
StackNode * children;
StackNode * next;
};
struct StackLink
{
uint64_t data;
StackLink *next;
StackLink *prev;
};
typedef void (* StackFunction) (StackLink *trace,
gint size,
gpointer data);
typedef void (* StackNodeFunc) (StackNode *node,
gpointer data);
/* Stach */
StackStash *stack_stash_new (GDestroyNotify destroy);
StackNode * stack_node_new (StackStash *stash);
StackNode * stack_stash_add_trace (StackStash *stash,
uint64_t *addrs,
gint n_addrs,
int size);
void stack_stash_foreach (StackStash *stash,
StackFunction stack_func,
gpointer data);
void stack_node_foreach_trace (StackNode *node,
StackFunction stack_func,
gpointer data);
StackNode *stack_stash_find_node (StackStash *stash,
gpointer address);
void stack_stash_foreach_by_address (StackStash *stash,
StackNodeFunc func,
gpointer data);
StackNode *stack_stash_get_root (StackStash *stash);
StackStash *stack_stash_ref (StackStash *stash);
void stack_stash_unref (StackStash *stash);
void stack_stash_set_root (StackStash *stash,
StackNode *root);
#endif

7
lib/testdemangle.c Normal file
View File

@ -0,0 +1,7 @@
#include "elfparser.h"
int
main ()
{
printf ("%s\n", elf_demangle ("_ZN8Inkscape7FiltersL12filter2D_FIRIhLj4EEEvPT_iiPKS2_iiiiPKNS_4Util10FixedPointIjLj16EEEii"));
}

100
lib/testelf.c Normal file
View File

@ -0,0 +1,100 @@
#include <glib.h>
#include "elfparser.h"
#include <string.h>
const char *n;
static void
check (ElfParser *elf, gulong addr)
{
const ElfSym *sym = elf_parser_lookup_symbol (elf, addr);
if (!sym)
{
g_print ("not found\n");
return;
}
n = elf_parser_get_sym_name (elf, sym);
g_print ("%p => ", (void *)addr);
if (sym)
{
g_print ("found: %s (%p)\n",
elf_parser_get_sym_name (elf, sym),
(void *)elf_parser_get_sym_address (elf, sym));
}
else
{
g_print ("not found\n");
}
}
int
main (int argc, char **argv)
{
ElfParser *elf;
ElfParser *debug = NULL;
const char *build_id;
const char *filename;
const char *dir;
if (argc == 1)
filename = "/usr/lib/libgtk-x11-2.0.so";
else
filename = argv[1];
elf = elf_parser_new (filename, NULL);
if (!elf)
{
g_print ("NO ELF!!!!\n");
return -1;
}
dir = g_path_get_dirname (filename);
build_id = elf_parser_get_build_id (elf);
guint crc = elf_parser_get_crc32 (elf);
g_print ("build ID: %s crc: %x\n", build_id, crc);
filename = elf_parser_get_debug_link(elf, &crc);
if (filename)
{
filename = g_build_filename ("/usr", "lib", "debug", dir, filename, NULL);
g_print ("Debug link: %s crc: %x\n", filename, crc);
debug = elf_parser_new (filename, NULL);
if (debug)
{
const char *build = elf_parser_get_build_id (debug);
guint crc_debug = elf_parser_get_crc32 (debug);
g_print ("Debug link build ID: %s crc: %x\n", build, crc_debug);
if (strcmp(build, build_id) != 0 || crc_debug != crc)
g_print ("Build ID or crc not matching!\n");
}
else
{
g_print ("Separate debug symbol file not found\n");
}
}
else
{
g_print ("No debug link\n");
}
#if 0
for (i = 0; i < 5000000; ++i)
#endif
{
elf_parser_get_crc32 (elf);
check (elf, 0x077c80f0 - (0x07787000 - 0)); /* gtk_about_dialog_set_artists (add - (map - offset)) */
check (elf, 0x077c80f0 - (0x07787000 - 0)); /* same (but in the middle of the function */
}
return 0;
}

27
lib/testunwind.c Normal file
View File

@ -0,0 +1,27 @@
#include "elfparser.h"
#include "unwind.h"
int
main (int argc, char **argv)
{
const guchar *data;
ElfParser *elf;
if (argc == 1)
{
g_print ("no arg\n");
return -1;
}
elf = elf_parser_new (argv[1], NULL);
if (!elf)
{
g_print ("NO ELF!!!!\n");
return -1;
}
unwind (elf);
return 0;
}

1127
lib/tracker.c Normal file

File diff suppressed because it is too large Load Diff

29
lib/tracker.h Normal file
View File

@ -0,0 +1,29 @@
#include <stdint.h>
#include "profile.h"
typedef struct tracker_t tracker_t;
tracker_t *tracker_new (void);
void tracker_free (tracker_t *);
void tracker_add_process (tracker_t *tracker,
pid_t pid,
const char *command_line);
void tracker_add_fork (tracker_t *tracker,
pid_t pid,
pid_t child_pid);
void tracker_add_exit (tracker_t *tracker,
pid_t pid);
void tracker_add_map (tracker_t * tracker,
pid_t pid,
uint64_t start,
uint64_t end,
uint64_t offset,
uint64_t inode,
const char *filename);
void tracker_add_sample (tracker_t *tracker,
pid_t pid,
uint64_t *ips,
int n_ips);
Profile *tracker_create_profile (tracker_t *tracker);

362
lib/treeviewutils.c Normal file
View File

@ -0,0 +1,362 @@
/* MemProf -- memory profiler and leak detector
* Copyright 2002, Soeren Sandmann (sandmann@daimi.au.dk)
* Copyright 2003, 2004, Red Hat, Inc.
*
* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, 2005, 2006, 2007, 2008 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 "treeviewutils.h"
static void on_column_clicked (GtkTreeViewColumn *column,
gpointer data);
typedef struct
{
int model_column;
GtkSortType default_order;
} SortInfo;
static void
set_sort_info (GtkTreeView *view,
GtkTreeViewColumn *column,
int model_column,
GtkSortType default_order)
{
SortInfo *info;
info = g_new0 (SortInfo, 1);
info->model_column = model_column;
info->default_order = default_order;
gtk_tree_view_column_set_clickable (column, TRUE);
g_object_set_data (G_OBJECT (column), "column_info", info);
g_signal_connect (column, "clicked", G_CALLBACK (on_column_clicked), view);
}
static SortInfo *
get_sort_info (GtkTreeViewColumn *column)
{
return g_object_get_data (G_OBJECT (column), "column_info");
}
static GtkTreeViewColumn *
find_column_by_model_column (GtkTreeView *view,
int model_column)
{
GList *columns = gtk_tree_view_get_columns (view);
GList *list;
GtkTreeViewColumn *result = NULL;
for (list = columns; list != NULL; list = list->next)
{
GtkTreeViewColumn *column = list->data;
SortInfo *info = get_sort_info (column);
if (info->model_column == model_column)
result = column;
}
g_list_free (columns);
return result;
}
void
tree_view_set_sort_column (GtkTreeView *view,
int model_column,
int sort_type)
{
GList *columns, *list;
GtkTreeSortable *sortable;
GtkTreeViewColumn *column = find_column_by_model_column (view, model_column);
SortInfo *info = get_sort_info (column);
/* For each column in the view, unset the sort indicator */
columns = gtk_tree_view_get_columns (view);
for (list = columns; list != NULL; list = list->next)
{
GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN (list->data);
gtk_tree_view_column_set_sort_indicator (col, FALSE);
}
/* Set the sort indicator for this column */
gtk_tree_view_column_set_sort_indicator (column, TRUE);
gtk_tree_view_column_set_sort_order (column, sort_type);
/* And sort the column */
sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (view));
gtk_tree_sortable_set_sort_column_id (sortable,
info->model_column,
sort_type);
}
static void
on_column_clicked (GtkTreeViewColumn *column,
gpointer data)
{
GtkTreeView *view = data;
GtkSortType sort_type;
SortInfo *info = get_sort_info (column);
/* Find out what our current sort indicator is. If it is NONE, then
* select the default for the column, otherwise, select the opposite
*/
if (!gtk_tree_view_column_get_sort_indicator (column))
{
sort_type = info->default_order;
}
else
{
if (gtk_tree_view_column_get_sort_order (column) == GTK_SORT_ASCENDING)
sort_type = GTK_SORT_DESCENDING;
else
sort_type = GTK_SORT_ASCENDING;
}
tree_view_set_sort_column (view, info->model_column, sort_type);
}
GtkTreeViewColumn *
add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column)
{
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
renderer = gtk_cell_renderer_text_new ();
g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
column = gtk_tree_view_column_new_with_attributes (title, renderer,
"text", model_column,
NULL);
gtk_tree_view_column_set_resizable (column, TRUE);
gtk_tree_view_append_column (view, column);
set_sort_info (view, column, model_column, GTK_SORT_ASCENDING);
return column;
}
static void
pointer_to_text (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell, GtkTreeModel *tree_model,
GtkTreeIter *iter, gpointer data)
{
gpointer p;
gchar *text;
int column = GPOINTER_TO_INT (data);
gtk_tree_model_get (tree_model, iter, column, &p, -1);
text = g_strdup_printf ("%p", p);
g_object_set (cell, "text", text, NULL);
g_free (text);
}
typedef struct {
int column;
char *format;
} ColumnInfo;
static void
double_to_text (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell, GtkTreeModel *tree_model,
GtkTreeIter *iter, gpointer data)
{
gdouble d;
gchar *text;
ColumnInfo *info = data;
gtk_tree_model_get (tree_model, iter, info->column, &d, -1);
text = g_strdup_printf (info->format, d);
g_object_set (cell, "markup", text, NULL);
g_free (text);
}
static void
free_column_info (void *data)
{
ColumnInfo *info = data;
g_free (info->format);
g_free (info);
}
GtkTreeViewColumn *
add_double_format_column (GtkTreeView *view,
const gchar *title,
gint model_column,
const char *format)
{
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
ColumnInfo *column_info = g_new (ColumnInfo, 1);
renderer = gtk_cell_renderer_text_new ();
g_object_set (renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, title);
gtk_tree_view_column_pack_start (column, renderer, TRUE);
gtk_tree_view_column_set_resizable (column, FALSE);
column_info->column = model_column;
column_info->format = g_strdup (format);
gtk_tree_view_column_set_cell_data_func (
column, renderer, double_to_text, column_info, free_column_info);
gtk_tree_view_append_column (view, column);
set_sort_info (view, column, model_column, GTK_SORT_DESCENDING);
return column;
}
GtkTreeViewColumn *
add_pointer_column (GtkTreeView *view, const gchar *title, gint model_column)
{
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new ();
if (title)
gtk_tree_view_column_set_title (column, title);
gtk_tree_view_column_pack_start (column, renderer, TRUE);
gtk_tree_view_column_set_resizable (column, TRUE);
gtk_tree_view_column_set_cell_data_func (
column, renderer, pointer_to_text, GINT_TO_POINTER (model_column), NULL);
gtk_tree_view_append_column (view, column);
return column;
}
void
tree_view_set_model_with_default_sort (GtkTreeView *view,
GtkTreeModel *model,
int model_column,
GtkSortType default_sort)
{
int column;
GtkSortType type;
GtkTreeSortable *old_model;
GtkAdjustment *adjustment;
old_model = GTK_TREE_SORTABLE (gtk_tree_view_get_model (view));
if (!(old_model && gtk_tree_sortable_get_sort_column_id (
GTK_TREE_SORTABLE (old_model), &column, &type)))
{
column = model_column;
type = default_sort;
}
/* Setting the sort column here prevents the "rows_reordered"
* signal from being emitted when the model is attached to
* a treeview. This is desirable because at that point there
* is a handler attached, which means the signal will actually
* be emitted.
*/
gtk_tree_sortable_set_sort_column_id (
GTK_TREE_SORTABLE (model), column, type);
gtk_tree_view_set_model (view, model);
tree_view_set_sort_column (view, column, type);
/* Workaround for GTK+ crack, see bug 405625 */
adjustment = gtk_tree_view_get_vadjustment (view);
if (adjustment)
gtk_adjustment_set_value (adjustment, 0);
}
static void
process_iter (GtkTreeView *view,
GtkTreeIter *iter,
VisibleCallback callback,
gpointer data)
{
GtkTreeModel *model = gtk_tree_view_get_model (view);
GtkTreePath *path;
GtkTreeIter child;
path = gtk_tree_model_get_path (model, iter);
callback (view, path, iter, data);
if (gtk_tree_view_row_expanded (view, path))
{
if (gtk_tree_model_iter_children (model, &child, iter))
{
do
{
process_iter (view, &child, callback, data);
}
while (gtk_tree_model_iter_next (model, &child));
}
}
gtk_tree_path_free (path);
}
void
tree_view_foreach_visible (GtkTreeView *view,
VisibleCallback callback,
gpointer data)
{
GtkTreeModel *model = gtk_tree_view_get_model (view);
GtkTreeIter iter;
if (model && gtk_tree_model_get_iter_first (model, &iter))
process_iter (view, &iter, callback, data);
}
/* Not really a *treeview* utility, but there isn't really a
* better place to put it
*/
void
set_error (GError **err, gint domain, gint code,
const char *format, va_list args)
{
char *msg;
if (!err)
return;
msg = g_strdup_vprintf (format, args);
if (*err == NULL)
{
*err = g_error_new_literal (G_MARKUP_ERROR, code, msg);
}
else
{
/* Warning text from GLib */
g_warning ("GError set over the top of a previous GError or uninitialized memory.\n"
"This indicates a bug in someone's code. You must ensure an error is NULL before it's set.\n"
"The overwriting error message was: %s",
msg);
}
g_free (msg);
}

55
lib/treeviewutils.h Normal file
View File

@ -0,0 +1,55 @@
/* -*- mode: C; c-file-style: "linux" -*- */
/* MemProf -- memory profiler and leak detector
* Copyright 2002, Soeren Sandmann (sandmann@daimi.au.dk)
* Copyright 2003, 2004, Red Hat, Inc.
*
* 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 <gtk/gtk.h>
GtkTreeViewColumn *add_plain_text_column (GtkTreeView *view,
const char *title,
gint model_column);
GtkTreeViewColumn *add_double_format_column (GtkTreeView *view,
const char *title,
int model_column,
const char *format);
GtkTreeViewColumn *add_pointer_column (GtkTreeView *view,
const char *title,
int model_column);
void tree_view_set_model_with_default_sort (GtkTreeView *view,
GtkTreeModel *model,
int model_column,
GtkSortType default_sort);
void tree_view_set_sort_column (GtkTreeView *view,
int model_column,
int sort_type);
typedef void (* VisibleCallback) (GtkTreeView *view,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data);
void tree_view_foreach_visible (GtkTreeView *view,
VisibleCallback callback,
gpointer data);
void
set_error_va (GError **err, gint domain, gint code,
const char *format, va_list args);

399
lib/unwind.c Normal file
View File

@ -0,0 +1,399 @@
#include "elfparser.h"
#include <string.h>
#include "util.h"
/* Pointer encodings, from dwarf2.h. */
typedef enum
{
DW_EH_PE_absptr = 0x00, /* */
DW_EH_PE_omit = 0xff, /* Value is not there */
DW_EH_PE_uleb128 = 0x01,
DW_EH_PE_udata2 = 0x02,
DW_EH_PE_udata4 = 0x03,
DW_EH_PE_udata8 = 0x04,
DW_EH_PE_sleb128 = 0x09,
DW_EH_PE_sdata2 = 0x0A,
DW_EH_PE_sdata4 = 0x0B,
DW_EH_PE_sdata8 = 0x0C,
DW_EH_PE_signed = 0x08,
DW_EH_PE_pcrel = 0x10, /* Value is *(cur + val) */
DW_EH_PE_textrel = 0x20, /* Value is *(&text + val) */
DW_EH_PE_datarel = 0x30, /* Value is *(&data + val) */
DW_EH_PE_funcrel = 0x40, /* Value is *(fde.pc_begin + val) */
DW_EH_PE_aligned = 0x50, /* Value is absolute, and stored
* at next align */
DW_EH_PE_indirect = 0x80
} PointerEncoding;
typedef struct EncodedPointer EncodedPointer;
struct EncodedPointer
{
PointerEncoding encoding;
guint64 value;
};
static guint64
get_length (const guchar **data)
{
guint64 len;
len = *(guint32 *)*data;
*data += 4;
if (len == 0xffffffff)
{
len = *(guint64 *)data;
*data += 8;
}
return len;
}
static guint64
decode_uleb128 (const guchar **data)
{
guint64 result;
int shift;
guchar b;
result = 0;
shift = 0;
do
{
b = *(*data)++;
result |= (b & 0x7f) << shift;
shift += 7;
} while (b & 0x80);
return result;
}
static gint64
decode_sleb128 (const guchar **data)
{
gint64 result;
int shift;
guchar b;
result = 0;
shift = 0;
do
{
b = *(*data)++;
result |= (b & 0x7f) << shift;
shift += 7;
} while (b & 0x80);
if (b & 0x40 && shift < 64)
result |= - (1 << shift);
return result;
}
static void
decode_block (const guchar **data)
{
int len;
/* FIXME */
len = decode_uleb128 (data);
(*data) += len;
}
static gulong
decode_address (const guchar **data)
{
/* FIXME */
gulong r;
r = *(guint32 *)*data;
(*data) += 4;
return r;
}
static const char *
decode_instruction (const guchar **data)
{
int opcode = *(*data)++;
int high2 = (opcode & 0xc0) >> 6;
int low6 = (opcode & 0x3f);
if (high2 == 0x01)
{
return "DW_CFA_advance_loc";
}
else if (high2 == 0x02)
{
g_print ("register: %d\n", low6);
g_print ("offset: "FMT64"\n", decode_uleb128 (data));
return "DW_CFA_offset";
}
else if (high2 == 0x03)
{
return "DW_CFA_restore";
}
else
{
g_assert ((opcode & 0xc0) == 0);
switch (opcode)
{
case 0x0:
return "DW_CFA_nop";
case 0x01:
g_print ("addr: %p\n", (void *)decode_address (data));
return "DW_CFA_set_loc";
case 0x02:
(*data)++;
return "DW_CFA_advance_loc1";
case 0x03:
(*data) += 2;
return "DW_CFA_advance_loc2";
case 0x04:
(*data) += 4;
return "DW_CFA_advance_loc4";
case 0x05:
decode_uleb128 (data);
decode_uleb128 (data);
return "DW_CFA_offset_extended";
case 0x06:
decode_uleb128 (data);
return "DW_CFA_restore_extended";
case 0x07:
decode_uleb128 (data);
return "DW_CFA_undefined";
case 0x08:
decode_uleb128 (data);
return "DW_CFA_same_value";
case 0x09:
decode_uleb128 (data);
decode_uleb128 (data);
return "DW_CFA_register";
case 0x0a:
return "DW_CFA_remember_state";
case 0x0b:
return "DW_CFA_restore_state";
case 0x0c:
g_print ("reg: "FMT64"\n", decode_uleb128 (data));
g_print ("off: "FMT64"\n", decode_uleb128 (data));
return "DW_CFA_def_cfa";
case 0x0d:
decode_uleb128 (data);
return "DW_CFA_def_cfa_register";
case 0x0e:
decode_uleb128 (data);
return "DW_CFA_def_cfa_offset";
case 0x0f:
decode_block (data);
return "DW_CFA_def_cfa_expression";
case 0x10:
decode_uleb128 (data);
decode_block (data);
return "DW_CFA_expression";
case 0x11:
decode_uleb128 (data);
decode_sleb128 (data);
return "DW_CFA_offset_extended_sf";
case 0x12:
decode_uleb128 (data);
decode_sleb128 (data);
return "DW_CFA_def_cfa_sf";
case 0x13:
decode_sleb128 (data);
return "DW_CFA_def_cfa_offset_sf";
case 0x14:
decode_uleb128 (data);
decode_uleb128 (data);
return "DW_CFA_val_offset";
case 0x15:
decode_uleb128 (data);
decode_sleb128 (data);
return "DW_CFA_val_offset_sf";
case 0x16:
decode_uleb128 (data);
decode_block (data);
return "DW_CFA_val_expression";
case 0x1c:
return "DW_CFA_lo_user";
case 0x3f:
return "DW_CFA_hi_user";
default:
return "UNKNOWN INSTRUCTION";
}
}
}
typedef struct CIE CIE;
struct CIE
{
PointerEncoding encoding;
};
static void
decode_cie (const guchar **data, const guchar *end)
{
gboolean has_augmentation;
guint64 aug_len;
const char *augmentation;
CIE *cie;
int i, field;
g_print ("version: %d\n", *(*data)++);
augmentation = (*data);
*data += strlen (*data) + 1;
g_print ("code alignment: "FMT64"\n", decode_uleb128 (data));
g_print ("data alignment: %lld\n", decode_sleb128 (data));
g_print ("return register: "FMT64"\n", decode_uleb128 (data));
g_print ("augmentation: %s\n", augmentation);
if (augmentation[0] == 'z')
{
aug_len = decode_uleb128 (data);
g_print ("len: "FMT64"\n", aug_len);
for (i = 1; augmentation[i] != 0; ++i)
{
if (augmentation[i] == 'L')
{
}
}
}
if (has_augmentation)
{
g_print ("%x\n", **data);
*data += aug_len;
}
while (*data < end)
g_print (" %s\n", decode_instruction (data));
}
static gboolean
decode_fde (const guchar **data, const guchar *end)
{
return FALSE;
}
static gboolean
decode_entry (const guchar **data, gboolean eh_frame)
{
guint64 len;
const guchar *end;
gboolean is_cie;
guint64 id;
len = get_length (data);
if (len == 0)
return FALSE;
end = *data + len;
g_print ("length: "FMT64"\n", len);
/* CIE_id is 0 for eh frames, and 0xffffffff/0xffffffffffffffff for .debug_frame */
id = *(guint32 *)*data;
g_print ("id: %lld\n", id);
is_cie = (eh_frame && id == 0) || (!eh_frame && id == 0xffffffff);
if (is_cie)
g_print ("is cie\n");
else
g_print ("is not cie\n");
*data += 4;
if (is_cie)
decode_cie (data, end);
else
decode_fde (data, end);
return TRUE;
}
/* The correct API is probably something like
*
* gboolean
* unwind (ElfParser *parser,
* gulong *regs
* int n_regs,
* MemoryReader reader,
* gpointer data);
*
*/
void
unwind (ElfParser *elf)
{
const guchar *data;
gboolean eh_f;
if ((data = elf_parser_get_debug_frame (elf)))
{
g_print ("Using .debug_frame\n");
eh_f = FALSE;
}
else if ((data = elf_parser_get_eh_frame (elf)))
{
g_print ("Using .eh_frame\n");
eh_f = TRUE;
}
else
{
g_print ("no debug info found\n");
return;
}
while (decode_entry (&data, eh_f))
return ;
;
}

3
lib/unwind.h Normal file
View File

@ -0,0 +1,3 @@
#include <glib.h>
void unwind (const guchar *data);

40
lib/util.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef UTIL_H
#define UTIL_H
#define FMT64 "%"G_GUINT64_FORMAT
#if defined(__i386__)
#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
#define cpu_relax() asm volatile("rep; nop" ::: "memory");
#endif
#if defined(__x86_64__)
#define rmb() asm volatile("lfence" ::: "memory")
#define cpu_relax() asm volatile("rep; nop" ::: "memory");
#endif
#ifdef __powerpc__
#define rmb() asm volatile ("sync" ::: "memory")
#define cpu_relax() asm volatile ("" ::: "memory");
#endif
#ifdef __s390__
#define rmb() asm volatile("bcr 15,0" ::: "memory")
#define cpu_relax() asm volatile("" ::: "memory");
#endif
#ifdef __sh__
#if defined(__SH4A__) || defined(__SH5__)
# define rmb() asm volatile("synco" ::: "memory")
#else
# define rmb() asm volatile("" ::: "memory")
#endif
#define cpu_relax() asm volatile("" ::: "memory")
#endif
#ifdef __hppa__
#define rmb() asm volatile("" ::: "memory")
#define cpu_relax() asm volatile("" ::: "memory");
#endif
#endif

328
lib/watch.c Normal file
View File

@ -0,0 +1,328 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */
/* - Library for asynchronous communication
* Copyright (C) 2002 S<>ren Sandmann (sandmann@daimi.au.dk)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <glib.h>
#include "watch.h"
typedef struct Watch Watch;
struct Watch {
GSource source;
GPollFD poll_fd;
gboolean removed;
WatchCallback read_callback;
WatchCallback write_callback;
WatchCallback hangup_callback;
WatchCallback error_callback;
WatchCallback priority_callback;
gpointer data;
};
static GHashTable *watched_fds;
static void
init (void)
{
if (!watched_fds)
watched_fds = g_hash_table_new (g_int_hash, g_int_equal);
}
static Watch *
lookup_watch (gint fd)
{
init ();
return g_hash_table_lookup (watched_fds, &fd);
}
static void
internal_add_watch (Watch *watch)
{
gpointer fd = &(watch->poll_fd.fd);
init ();
g_hash_table_insert (watched_fds, fd, watch);
g_source_add_poll ((GSource *)watch, &(watch->poll_fd));
}
static void
internal_remove_watch (Watch *watch)
{
gpointer fd = &(watch->poll_fd.fd);
init ();
watch->removed = TRUE;
g_source_remove_poll ((GSource *)watch, &(watch->poll_fd));
g_hash_table_remove (watched_fds, fd);
}
static gboolean
watch_prepare (GSource *source,
gint *timeout)
{
*timeout = -1;
return FALSE;
}
static gboolean
watch_check (GSource *source)
{
Watch *watch = (Watch *)source;
gint revents = watch->poll_fd.revents;
if (revents & (G_IO_NVAL))
{
/* This can happen if the user closes the file descriptor
* without first removing the watch. We silently ignore it
*/
internal_remove_watch (watch);
g_source_unref (source);
return FALSE;
}
if ((revents & G_IO_HUP) && watch->hangup_callback)
return TRUE;
if ((revents & G_IO_IN) && watch->read_callback)
return TRUE;
if ((revents & G_IO_PRI) && watch->priority_callback)
return TRUE;
if ((revents & G_IO_ERR) && watch->error_callback)
return TRUE;
if ((revents & G_IO_OUT) && watch->write_callback)
return TRUE;
return FALSE;
}
static gboolean
watch_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
Watch *watch = (Watch *)source;
gint revents = watch->poll_fd.revents;
gboolean removed;
g_source_ref (source);
if (!watch->removed && (revents & G_IO_IN) && watch->read_callback)
watch->read_callback (watch->data);
if (!watch->removed && (revents & G_IO_PRI) && watch->priority_callback)
watch->priority_callback (watch->data);
if (!watch->removed && (revents & G_IO_OUT) && watch->write_callback)
watch->write_callback (watch->data);
if (!watch->removed && (revents & G_IO_ERR) && watch->error_callback)
watch->error_callback (watch->data);
if (!watch->removed && (revents & G_IO_HUP) && watch->hangup_callback)
watch->hangup_callback (watch->data);
removed = watch->removed;
g_source_unref (source);
if (removed)
return FALSE;
return TRUE;
}
static void
watch_finalize (GSource *source)
{
}
static void
update_poll_mask (Watch *watch)
{
gint events = 0;
g_source_remove_poll ((GSource *)watch, &(watch->poll_fd));
if (watch->read_callback)
events |= G_IO_IN;
if (watch->write_callback)
events |= G_IO_OUT;
if (watch->priority_callback)
events |= G_IO_PRI;
if (watch->hangup_callback)
events |= G_IO_HUP;
if (watch->error_callback)
events |= G_IO_ERR;
watch->poll_fd.events = events;
g_source_add_poll ((GSource *)watch, &(watch->poll_fd));
}
void
fd_add_watch (gint fd,
gpointer data)
{
static GSourceFuncs watch_funcs = {
watch_prepare,
watch_check,
watch_dispatch,
watch_finalize,
};
Watch *watch = lookup_watch (fd);
g_return_if_fail (fd > 0);
g_return_if_fail (watch == NULL);
watch = (Watch *)g_source_new (&watch_funcs, sizeof (Watch));
g_source_set_can_recurse ((GSource *)watch, TRUE);
g_source_attach ((GSource *)watch, NULL);
watch->poll_fd.fd = fd;
watch->poll_fd.events = 0;
watch->removed = FALSE;
watch->read_callback = NULL;
watch->write_callback = NULL;
watch->hangup_callback = NULL;
watch->error_callback = NULL;
watch->priority_callback = NULL;
watch->data = data;
internal_add_watch (watch);
}
void
fd_set_read_callback (gint fd,
WatchCallback read_cb)
{
Watch *watch = lookup_watch (fd);
g_return_if_fail (fd > 0);
g_return_if_fail (watch != NULL);
if (watch->read_callback != read_cb)
{
watch->read_callback = read_cb;
update_poll_mask (watch);
}
}
void
fd_set_write_callback (gint fd,
WatchCallback write_cb)
{
Watch *watch = lookup_watch (fd);
g_return_if_fail (fd > 0);
g_return_if_fail (watch != NULL);
if (watch->write_callback != write_cb)
{
watch->write_callback = write_cb;
update_poll_mask (watch);
}
}
void
fd_set_hangup_callback (gint fd,
WatchCallback hangup_cb)
{
Watch *watch = lookup_watch (fd);
g_return_if_fail (fd > 0);
g_return_if_fail (watch != NULL);
if (watch->hangup_callback != hangup_cb)
{
watch->hangup_callback = hangup_cb;
update_poll_mask (watch);
}
}
void
fd_set_error_callback (gint fd,
WatchCallback error_cb)
{
Watch *watch = lookup_watch (fd);
g_return_if_fail (fd > 0);
g_return_if_fail (watch != NULL);
if (watch->error_callback != error_cb)
{
watch->error_callback = error_cb;
update_poll_mask (watch);
}
}
void
fd_set_priority_callback (gint fd,
WatchCallback priority_cb)
{
Watch *watch = lookup_watch (fd);
g_return_if_fail (fd > 0);
g_return_if_fail (watch != NULL);
if (watch->priority_callback != priority_cb)
{
watch->priority_callback = priority_cb;
update_poll_mask (watch);
}
}
void
fd_remove_watch (gint fd)
{
Watch *watch = lookup_watch (fd);
g_return_if_fail (fd > 0);
if (!watch)
return;
internal_remove_watch (watch);
g_source_unref ((GSource *)watch);
}
gboolean
fd_is_watched (gint fd)
{
g_return_val_if_fail (fd > 0, FALSE);
if (lookup_watch (fd))
return TRUE;
else
return FALSE;
}

40
lib/watch.h Normal file
View File

@ -0,0 +1,40 @@
/* - Library for asynchronous communication
* Copyright (C) 2002 Søren Sandmann (sandmann@daimi.au.dk)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <glib.h>
/*
* Watching file descriptors
*/
typedef void (* WatchCallback) (gpointer data);
void fd_add_watch (gint fd,
gpointer data);
void fd_set_read_callback (gint fd,
WatchCallback read_cb);
void fd_set_write_callback (gint fd,
WatchCallback write_cb);
void fd_set_hangup_callback (gint fd,
WatchCallback hangup_cb);
void fd_set_error_callback (gint fd,
WatchCallback error_cb);
void fd_set_priority_callback (gint fd,
WatchCallback priority_cb);
void fd_remove_watch (gint fd);
gboolean fd_is_watched (gint fd);

156
lib/xmlstore.c Normal file
View File

@ -0,0 +1,156 @@
typedef struct ParsedItem ParsedItem;
struct XmlItem
{
gboolean is_element;
union
{
struct
{
const char *name;
guint sibling_index;
} element;
char text[1];
} u;
};
struct XmlStore
{
GHashTable *names;
GArray *items;
GList *stack;
guint last_index;
};
static guint
add_item (GArray *array,
const XmlItem *item)
{
}
XmlStore *
xml_store_new (void)
{
XmlStore *store = g_new (XmlStore, 1);
store->names = g_hash_table_new (g_str_hash, g_str_equal);
store->stack = NULL;
store->items = g_array_new (TRUE, TRUE, sizeof (XmlItem));
}
void
xml_store_append_begin (XmlStore *store,
const char *element)
{
XmlItem item;
item.is_element = TRUE;
item.element.name = canonical_name (store, element);
item.element.sibling = NULL;
if (store->last)
store->last->u.element.sibling = &result;
}
typedef enum
{
BEGIN,
TEXT,
END
} XmlItemType;
struct XmlItem
{
XmlItemType type;
char [1] data;
};
struct XmlStore
{
XmlItem * items;
GHashTable *user_data_map;
};
struct ParsedItem
{
XmlItemType type;
union
{
struct
{
char * element;
int n_attrs;
char **attr;
} begin_item;
struct
{
char *text;
} text_item;
struct
{
char *element;
} end_item;
} u;
};
static void
parse_begin_item (XmlItem *item,
ParsedItem *parsed_item)
{
}
static void
parse_end_item (XmlItem *item,
ParsedItem *parsed_item)
{
}
static void
parse_text_item (XmlItem *item,
ParsedItem *parsed_item)
{
}
static ParsedItem *
parsed_item_new (XmlItem *item)
{
ParsedItem *parsed_item = g_new0 (ParsedItem, 0);
switch (item->type)
{
case BEGIN:
parsed_item->type = BEGIN;
parse_begin_item (item, parsed_item);
break;
case END:
parsed_item->type = END;
parse_end_item (item, parsed_item);
break;
case TEXT:
parsed_item->type = TEXT;
parse_text_item (item, parsed_item);
break;
}
return parsed_item;
}
static void
parsed_item_free (ParsedItem *item)
{
}

58
lib/xmlstore.h Normal file
View File

@ -0,0 +1,58 @@
typedef struct XmlIter XmlIter;
typedef struct XmlStore XmlStore;
XmlStore *xml_store_new (void);
void xml_store_free (XmlStore *store);
void xml_store_append_begin (XmlStore *store,
const char *element);
void xml_store_append_end (XmlStore *store,
const char *element);
void xml_store_append_text (XmlStore *store,
const char *text);
void xml_store_write (XmlStore *store,
const char *file,
GError *file);
void xml_store_set_data (XmlIter *iter,
gpointer data);
gpointer xml_store_get_data (XmlIter *iter,
gpointer data);
void xml_store_has_data (XmlIter *iter);
/* An iter stays valid as long as the XmlStore is valid */
XmlIter * xml_store_get_iter (XmlStore *store);
XmlIter * xml_iter_get_sibling (XmlIter *iter);
XmlIter * xml_iter_get_child (XmlIter *iter);
int xml_iter_get_n_children (XmlIter *iter);
gboolean xml_iter_valid (XmlIter *iter);
void
process_tree (XmlIter *iter)
{
XmlIter *i;
if (!xml_iter_valid (iter))
return;
/* siblings */
i = xml_iter_sibling (iter);
while (xml_iter_valid (i))
{
process_tree (i);
i = xml_iter_sibling (i);
}
/* children */
process_tree (xml_iter_child (iter));
process_tree (xml_iter_sibling (iter));
process_tree (xml_iter_child (iter));
}
void
process_store (XmlStore *store)
{
process_tree (xml_store_get_iter (store));
}