mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
Land Sysprof 2.x
This is a major redesign a modernization of Sysprof. The core data structures and design are largely the same, but it has been ported to Gtk3 and has lots of additions that should make your profiling experience smoother. Especially for those that are new to profiling. There are some very simple help docs added, but we really need the experts to come in and write some documentation here.
This commit is contained in:
540
lib/util/binfile.c
Normal file
540
lib/util/binfile.c
Normal file
@ -0,0 +1,540 @@
|
||||
/* 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;
|
||||
guint 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,
|
||||
...)
|
||||
G_GNUC_PRINTF (1, 2);
|
||||
|
||||
static char **
|
||||
get_lines (const char *format,
|
||||
...)
|
||||
{
|
||||
va_list args;
|
||||
char *filename;
|
||||
char **result = NULL;
|
||||
char *contents;
|
||||
|
||||
va_start (args, format);
|
||||
filename = g_strdup_vprintf (format, args);
|
||||
va_end (args);
|
||||
|
||||
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: %"G_GUINT64_FORMAT", memory: %"G_GUINT64_FORMAT")\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/util/binfile.h
Normal file
46
lib/util/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
|
||||
40
lib/util/demangle.cpp
Normal file
40
lib/util/demangle.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/* demangle.cpp
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "demangle.h"
|
||||
|
||||
gchar *
|
||||
sysprof_cplus_demangle (const gchar *name)
|
||||
{
|
||||
char *real_name;
|
||||
gchar *ret;
|
||||
int status;
|
||||
|
||||
real_name = abi::__cxa_demangle (name, 0, 0, &status);
|
||||
|
||||
if (real_name == NULL)
|
||||
return NULL;
|
||||
|
||||
ret = g_strdup (real_name);
|
||||
free (real_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
30
lib/util/demangle.h
Normal file
30
lib/util/demangle.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* demangle.h
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DEMANGLE_H
|
||||
#define DEMANGLE_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gchar *sysprof_cplus_demangle (const gchar *name);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* DEMANGLE_H */
|
||||
811
lib/util/elfparser.c
Normal file
811
lib/util/elfparser.c
Normal file
@ -0,0 +1,811 @@
|
||||
/* 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 "demangle.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;
|
||||
|
||||
guint n_sections;
|
||||
Section ** sections;
|
||||
|
||||
guint 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)
|
||||
{
|
||||
guint 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;
|
||||
G_GNUC_UNUSED gsize section_headers;
|
||||
guint 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_unref (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)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < parser->n_sections; ++i)
|
||||
section_free (parser->sections[i]);
|
||||
g_free (parser->sections);
|
||||
|
||||
if (parser->file)
|
||||
g_mapped_file_unref (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);
|
||||
}
|
||||
|
||||
gchar *
|
||||
elf_demangle (const char *name)
|
||||
{
|
||||
gchar *demangled = sysprof_cplus_demangle (name);
|
||||
|
||||
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);
|
||||
guint 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;
|
||||
}
|
||||
|
||||
static 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/util/elfparser.h
Normal file
57
lib/util/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);
|
||||
|
||||
387
lib/util/stackstash.c
Normal file
387
lib/util/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);
|
||||
guint 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)
|
||||
{
|
||||
guint 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,
|
||||
const 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;
|
||||
}
|
||||
85
lib/util/stackstash.h
Normal file
85
lib/util/stackstash.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* 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);
|
||||
|
||||
StackStash *stack_stash_new (GDestroyNotify destroy);
|
||||
StackNode *stack_node_new (StackStash *stash);
|
||||
StackNode *stack_stash_add_trace (StackStash *stash,
|
||||
const 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
|
||||
32
lib/util/util.h
Normal file
32
lib/util/util.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef SP_UTIL_H
|
||||
#define SP_UTIL_H
|
||||
|
||||
#if defined(__i386__)
|
||||
#define read_barrier() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#define read_barrier() asm volatile("lfence" ::: "memory")
|
||||
#endif
|
||||
|
||||
#ifdef __powerpc__
|
||||
#define read_barrier() asm volatile ("sync" ::: "memory")
|
||||
#endif
|
||||
|
||||
#ifdef __s390__
|
||||
#define read_barrier() asm volatile("bcr 15,0" ::: "memory")
|
||||
#endif
|
||||
|
||||
#ifdef __sh__
|
||||
#if defined(__SH4A__) || defined(__SH5__)
|
||||
# define read_barrier() asm volatile("synco" ::: "memory")
|
||||
#else
|
||||
# define read_barrier() asm volatile("" ::: "memory")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __hppa__
|
||||
#define read_barrier() asm volatile("" ::: "memory")
|
||||
#endif
|
||||
|
||||
#endif /* SP_UTIL_H */
|
||||
Reference in New Issue
Block a user