mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
move source around in preparation to land sysprof2
This commit is contained in:
530
lib/binfile.c
Normal file
530
lib/binfile.c
Normal 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
46
lib/binfile.h
Normal 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
806
lib/collector.c
Normal 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
46
lib/collector.h
Normal 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
9595
lib/demangle.c
Normal file
File diff suppressed because it is too large
Load Diff
814
lib/elfparser.c
Normal file
814
lib/elfparser.c
Normal 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
57
lib/elfparser.h
Normal 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
577
lib/footreedatalist.c
Normal 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
82
lib/footreedatalist.h
Normal 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
3363
lib/footreestore.c
Normal file
File diff suppressed because it is too large
Load Diff
158
lib/footreestore.h
Normal file
158
lib/footreestore.h
Normal 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
605
lib/profile.c
Normal 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
78
lib/profile.h
Normal 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
1180
lib/sfile.c
Normal file
File diff suppressed because it is too large
Load Diff
169
lib/sfile.h
Normal file
169
lib/sfile.h
Normal 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
646
lib/sformat.c
Normal 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
67
lib/sformat.h
Normal 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
215
lib/signal-handler.c
Normal 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
27
lib/signal-handler.h
Normal 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
387
lib/stackstash.c
Normal 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
86
lib/stackstash.h
Normal 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
7
lib/testdemangle.c
Normal 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
100
lib/testelf.c
Normal 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
27
lib/testunwind.c
Normal 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
1127
lib/tracker.c
Normal file
File diff suppressed because it is too large
Load Diff
29
lib/tracker.h
Normal file
29
lib/tracker.h
Normal 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
362
lib/treeviewutils.c
Normal 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
55
lib/treeviewutils.h
Normal 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
399
lib/unwind.c
Normal 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
3
lib/unwind.h
Normal file
@ -0,0 +1,3 @@
|
||||
#include <glib.h>
|
||||
|
||||
void unwind (const guchar *data);
|
||||
40
lib/util.h
Normal file
40
lib/util.h
Normal 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
328
lib/watch.c
Normal 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
40
lib/watch.h
Normal 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
156
lib/xmlstore.c
Normal 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
58
lib/xmlstore.h
Normal 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));
|
||||
}
|
||||
Reference in New Issue
Block a user