mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
2006-11-02 Soren Sandmann <sandmann@daimi.au.dk> Valgrind: * binparser.c (bin_parser_free): Add this function * elfparser.c (elf_parser_free): Call bin_parser_free() * sysprof.c (compute_text_width, add_text): Plug leaks * collector.c (add_trace_to_stash): Copy n_addresses to a stack variable instead of reading it out of the mmap'ed area all the time. (That way if there is an overrun, we won't write too much into the address array).
458 lines
8.0 KiB
C
458 lines
8.0 KiB
C
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <glib.h>
|
|
#include <stdarg.h>
|
|
#include "binparser.h"
|
|
|
|
typedef struct ParserFrame ParserFrame;
|
|
|
|
struct BinRecord
|
|
{
|
|
BinFormat * format;
|
|
int index;
|
|
gsize offset;
|
|
BinParser * parser;
|
|
};
|
|
|
|
struct BinField
|
|
{
|
|
guint64 offset;
|
|
int width;
|
|
int align;
|
|
char * name;
|
|
};
|
|
|
|
struct BinFormat
|
|
{
|
|
gboolean big_endian;
|
|
|
|
int n_fields;
|
|
BinField * fields;
|
|
};
|
|
|
|
struct BinParser
|
|
{
|
|
gsize offset;
|
|
const guchar * data;
|
|
gsize length;
|
|
|
|
gboolean cache_in_use;
|
|
BinRecord cache;
|
|
};
|
|
|
|
BinParser *
|
|
bin_parser_new (const guchar *data,
|
|
gsize length)
|
|
{
|
|
BinParser *parser = g_new0 (BinParser, 1);
|
|
|
|
parser->offset = 0;
|
|
parser->data = data;
|
|
parser->length = length;
|
|
parser->cache_in_use = FALSE;
|
|
|
|
return parser;
|
|
}
|
|
|
|
void
|
|
bin_parser_free (BinParser *parser)
|
|
{
|
|
g_free (parser);
|
|
}
|
|
|
|
static GQueue *
|
|
read_varargs (va_list args,
|
|
const char * name,
|
|
BinField * field)
|
|
{
|
|
GQueue *queue = g_queue_new ();
|
|
gpointer p;
|
|
|
|
if (name)
|
|
{
|
|
g_queue_push_tail (queue, (gpointer)name);
|
|
g_queue_push_tail (queue, field);
|
|
|
|
p = va_arg (args, gpointer);
|
|
while (p)
|
|
{
|
|
g_queue_push_tail (queue, p);
|
|
p = va_arg (args, gpointer);
|
|
}
|
|
}
|
|
|
|
return queue;
|
|
}
|
|
|
|
static guint64
|
|
align (guint64 offset, int alignment)
|
|
{
|
|
/* Note that we can speed this up by assuming alignment'
|
|
* is a power of two, since
|
|
*
|
|
* offset % alignment == offset & (alignemnt - 1)
|
|
*
|
|
*/
|
|
|
|
if (offset % alignment != 0)
|
|
offset += (alignment - (offset % alignment));
|
|
|
|
return offset;
|
|
}
|
|
|
|
gsize
|
|
bin_format_get_size (BinFormat *format)
|
|
{
|
|
BinField *last_field = &(format->fields[format->n_fields - 1]);
|
|
BinField *first_field = &(format->fields[0]);
|
|
|
|
return align (last_field->offset + last_field->width, first_field->width);
|
|
}
|
|
|
|
BinFormat *
|
|
bin_format_new (gboolean big_endian,
|
|
const char *name, BinField *field,
|
|
...)
|
|
{
|
|
GQueue *queue = g_queue_new ();
|
|
BinFormat *format = g_new0 (BinFormat, 1);
|
|
GList *list;
|
|
int i;
|
|
guint64 offset;
|
|
va_list args;
|
|
|
|
format->big_endian = big_endian;
|
|
|
|
/* Build queue of child types */
|
|
va_start (args, field);
|
|
queue = read_varargs (args, name, field);
|
|
va_end (args);
|
|
|
|
g_assert (queue->length % 2 == 0);
|
|
|
|
format->n_fields = queue->length / 2;
|
|
format->fields = g_new (BinField, format->n_fields);
|
|
|
|
i = 0;
|
|
offset = 0;
|
|
for (list = queue->head; list != NULL; list = list->next->next)
|
|
{
|
|
const char *name = list->data;
|
|
BinField *field = list->next->data;
|
|
|
|
offset = align (offset, field->align);
|
|
|
|
format->fields[i].name = g_strdup (name);
|
|
format->fields[i].width = field->width;
|
|
format->fields[i].offset = offset;
|
|
|
|
offset += field->width;
|
|
++i;
|
|
|
|
g_free (field);
|
|
}
|
|
|
|
g_queue_free (queue);
|
|
|
|
return format;
|
|
}
|
|
|
|
static const BinField *
|
|
get_field (BinFormat *format,
|
|
const gchar *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < format->n_fields; ++i)
|
|
{
|
|
BinField *field = &(format->fields[i]);
|
|
|
|
if (strcmp (field->name, name) == 0)
|
|
return field;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static guint64
|
|
convert_uint (const guchar *data,
|
|
gboolean big_endian,
|
|
int width)
|
|
{
|
|
guint8 r8;
|
|
guint16 r16;
|
|
guint32 r32;
|
|
guint64 r64;
|
|
|
|
#if 0
|
|
if (width == 4)
|
|
g_print ("converting at %p %d %d %d %d\n", data, data[0], data[1], data[2], data[3]);
|
|
#endif
|
|
|
|
switch (width)
|
|
{
|
|
case 1:
|
|
r8 = *(guint8 *)data;
|
|
return r8;
|
|
|
|
case 2:
|
|
r16 = *(guint16 *)data;
|
|
|
|
if (big_endian)
|
|
r16 = GUINT16_FROM_BE (r16);
|
|
else
|
|
r16 = GUINT16_FROM_LE (r16);
|
|
|
|
return r16;
|
|
|
|
case 4:
|
|
r32 = *(guint32 *)data;
|
|
|
|
if (big_endian)
|
|
r32 = GUINT32_FROM_BE (r32);
|
|
else
|
|
r32 = GUINT32_FROM_LE (r32);
|
|
|
|
return r32;
|
|
|
|
case 8:
|
|
r64 = *(guint64 *)data;
|
|
|
|
if (big_endian)
|
|
r64 = GUINT64_FROM_BE (r64);
|
|
else
|
|
r64 = GUINT64_FROM_LE (r64);
|
|
|
|
return r64;
|
|
|
|
default:
|
|
g_assert_not_reached();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
guint32
|
|
bin_parser_get_uint32 (BinParser *parser)
|
|
{
|
|
guint32 result;
|
|
|
|
/* FIXME: This is broken for two reasons:
|
|
*
|
|
* (1) It assumes file_endian==machine_endian
|
|
*
|
|
* (2) It doesn't check for file overrun.
|
|
*
|
|
*/
|
|
result = *(guint32 *)(parser->data + parser->offset);
|
|
|
|
parser->offset += 4;
|
|
|
|
return result;
|
|
}
|
|
|
|
static BinField *
|
|
new_field_uint (int width)
|
|
{
|
|
BinField *field = g_new0 (BinField, 1);
|
|
|
|
field->width = width;
|
|
field->align = width;
|
|
|
|
return field;
|
|
}
|
|
|
|
BinField *
|
|
bin_field_new_uint8 (void)
|
|
{
|
|
return new_field_uint (1);
|
|
}
|
|
|
|
BinField *
|
|
bin_field_new_uint16 (void)
|
|
{
|
|
return new_field_uint (2);
|
|
}
|
|
|
|
BinField *
|
|
bin_field_new_uint32 (void)
|
|
{
|
|
return new_field_uint (4);
|
|
}
|
|
|
|
BinField *
|
|
bin_field_new_uint64 (void)
|
|
{
|
|
return new_field_uint (8);
|
|
}
|
|
|
|
const gchar *
|
|
bin_parser_get_string (BinParser *parser)
|
|
{
|
|
const char *result;
|
|
|
|
/* FIXME: check that the string is within the file */
|
|
|
|
result = (const char *)parser->data + parser->offset;
|
|
|
|
parser->offset += strlen (result) + 1;
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
bin_parser_align (BinParser *parser,
|
|
gsize byte_width)
|
|
{
|
|
parser->offset = align (parser->offset, 4);
|
|
}
|
|
|
|
void
|
|
bin_parser_goto (BinParser *parser,
|
|
gsize offset)
|
|
{
|
|
parser->offset = offset;
|
|
}
|
|
|
|
BinParser *
|
|
bin_record_get_parser (BinRecord *record)
|
|
{
|
|
return record->parser;
|
|
}
|
|
|
|
const gchar *
|
|
bin_record_get_string_indirect (BinRecord *record,
|
|
const char *name,
|
|
gsize str_table)
|
|
{
|
|
BinParser *parser = record->parser;
|
|
const char *result = NULL;
|
|
gsize index;
|
|
gsize saved_offset;
|
|
|
|
saved_offset = bin_parser_get_offset (record->parser);
|
|
|
|
index = bin_record_get_uint (record, name);
|
|
|
|
bin_parser_goto (record->parser, str_table + index);
|
|
|
|
result = bin_parser_get_string (parser);
|
|
|
|
bin_parser_goto (record->parser, saved_offset);
|
|
|
|
return result;
|
|
}
|
|
|
|
gsize
|
|
bin_parser_get_offset (BinParser *parser)
|
|
{
|
|
g_return_val_if_fail (parser != NULL, 0);
|
|
|
|
return parser->offset;
|
|
}
|
|
|
|
const guchar *
|
|
bin_parser_get_data (BinParser *parser)
|
|
{
|
|
return parser->data;
|
|
}
|
|
|
|
gsize
|
|
bin_parser_get_length (BinParser *parser)
|
|
{
|
|
return parser->length;
|
|
}
|
|
|
|
|
|
/* Record */
|
|
BinRecord *
|
|
bin_parser_get_record (BinParser *parser,
|
|
BinFormat *format,
|
|
gsize offset)
|
|
{
|
|
BinRecord *record;
|
|
|
|
if (!parser->cache_in_use)
|
|
{
|
|
parser->cache_in_use = TRUE;
|
|
record = &(parser->cache);
|
|
}
|
|
else
|
|
{
|
|
record = g_new0 (BinRecord, 1);
|
|
}
|
|
|
|
record->parser = parser;
|
|
record->index = 0;
|
|
record->offset = offset;
|
|
record->format = format;
|
|
|
|
return record;
|
|
}
|
|
|
|
void
|
|
bin_record_free (BinRecord *record)
|
|
{
|
|
if (record == &(record->parser->cache))
|
|
record->parser->cache_in_use = FALSE;
|
|
else
|
|
g_free (record);
|
|
}
|
|
|
|
guint64
|
|
bin_record_get_uint (BinRecord *record,
|
|
const char *name)
|
|
{
|
|
const guint8 *pos;
|
|
const BinField *field;
|
|
|
|
field = get_field (record->format, name);
|
|
pos = record->parser->data + record->offset + field->offset;
|
|
|
|
#if 0
|
|
g_print (" record offset: %d\n", record->offset);
|
|
g_print (" record index: %d\n", record->index);
|
|
g_print (" field offset %d\n", field->offset);
|
|
#endif
|
|
|
|
if (record->offset + field->offset + field->width > record->parser->length)
|
|
{
|
|
/* FIXME: generate error */
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
g_print (" uint %d at %p => %d\n", field->width, pos, convert_uint (pos, record->format->big_endian, field->width));
|
|
#endif
|
|
|
|
return convert_uint (pos, record->format->big_endian, field->width);
|
|
}
|
|
|
|
void
|
|
bin_record_index (BinRecord *record,
|
|
int index)
|
|
{
|
|
gsize format_size = bin_format_get_size (record->format);
|
|
|
|
record->offset -= record->index * format_size;
|
|
record->offset += index * format_size;
|
|
record->index = index;
|
|
}
|
|
|
|
gsize
|
|
bin_record_get_offset (BinRecord *record)
|
|
{
|
|
return record->offset;
|
|
}
|
|
|
|
/* Fields */
|
|
|
|
BinField *
|
|
bin_field_new_fixed_array (int n_elements,
|
|
int element_size)
|
|
{
|
|
BinField *field = g_new0 (BinField, 1);
|
|
field->width = n_elements * element_size;
|
|
field->align = element_size;
|
|
return field;
|
|
}
|