Compare commits

...

1 Commits

Author SHA1 Message Date
o9000
01f823cf79 memory tracing 2017-12-19 12:35:35 +01:00
10 changed files with 965 additions and 1 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
build build
*.user *.user
version.h version.h
*.todo
*.pyc

View File

@@ -147,6 +147,9 @@ set( SOURCES src/config.c
src/util/cache.c src/util/cache.c
src/util/color.c src/util/color.c
src/util/gradient.c src/util/gradient.c
src/util/addr2line.c
src/util/print.c
src/util/mem.c
src/util/uevent.c src/util/uevent.c
src/util/window.c ) src/util/window.c )
@@ -265,9 +268,14 @@ if( RT_LIBRARY )
endif( RT_LIBRARY ) endif( RT_LIBRARY )
target_link_libraries( tint2 m ) target_link_libraries( tint2 m )
if(ENABLE_BACKTRACE)
target_link_libraries( tint2 dl )
target_link_libraries( tint2 z )
target_link_libraries( tint2 bfd )
endif(ENABLE_BACKTRACE)
add_dependencies( tint2 version ) add_dependencies( tint2 version )
set_target_properties( tint2 PROPERTIES COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=c99 ${ASAN_C_FLAGS} ${TRACING_C_FLAGS}" ) set_target_properties( tint2 PROPERTIES COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=c11 ${ASAN_C_FLAGS} ${TRACING_C_FLAGS}" )
set_target_properties( tint2 PROPERTIES LINK_FLAGS "-pthread -fno-strict-aliasing ${ASAN_L_FLAGS} ${BACKTRACE_L_FLAGS} ${TRACING_L_FLAGS}" ) set_target_properties( tint2 PROPERTIES LINK_FLAGS "-pthread -fno-strict-aliasing ${ASAN_L_FLAGS} ${BACKTRACE_L_FLAGS} ${TRACING_L_FLAGS}" )
install( TARGETS tint2 DESTINATION bin ) install( TARGETS tint2 DESTINATION bin )

238
src/util/addr2line.c Normal file
View File

@@ -0,0 +1,238 @@
/* addr2line.c -- convert addresses to line number and function name
Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
2007, 2009 Free Software Foundation, Inc.
Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
This file is part of GNU Binutils.
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, 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, 51 Franklin Street - Fifth Floor, Boston,
MA 02110-1301, USA. */
/* Derived from objdump.c and nm.c by Ulrich.Lauther@mchp.siemens.de */
#ifdef ENABLE_EXECINFO
#include <bfd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "addr2line.h"
#include "print.h"
static bfd_boolean unwind_inlines = 1; /* -i, unwind inlined functions. */
static bfd_boolean with_addresses = 0; /* -a, show addresses. */
static bfd_boolean with_functions = 1; /* -f, show function names. */
static bfd_boolean do_demangle = 1; /* -C, demangle names. */
static bfd_boolean base_names = 1; /* -s, strip directory names. */
typedef struct Lookup {
char *exe_file_name;
void *address;
char *result;
bfd *abfd;
asymbol **syms;
bfd_vma pc;
const char *filename;
const char *functionname;
unsigned int line;
unsigned int discriminator;
bfd_boolean found;
} Lookup;
static asymbol **slurp_symtab(bfd *);
static void find_address_in_section(bfd *, asection *, void *unused);
static void translate_address(Lookup *lookup);
static asymbol **slurp_symtab(bfd *abfd)
{
if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
return NULL;
long storage = bfd_get_symtab_upper_bound(abfd);
bfd_boolean dynamic = FALSE;
if (storage == 0) {
storage = bfd_get_dynamic_symtab_upper_bound(abfd);
dynamic = TRUE;
}
if (storage < 0)
return NULL;
asymbol **syms = (asymbol **)malloc(storage);
long symcount;
if (dynamic)
symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
else
symcount = bfd_canonicalize_symtab(abfd, syms);
if (symcount < 0)
return syms;
// If there are no symbols left after canonicalization and
// we have not tried the dynamic symbols then give them a go.
if (symcount == 0 && !dynamic && (storage = bfd_get_dynamic_symtab_upper_bound(abfd)) > 0) {
free(syms);
syms = (asymbol **)malloc(storage);
symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
}
return syms;
}
static void find_address_in_section(bfd *abfd, asection *section, void *unused)
{
Lookup *lookup = (Lookup *)abfd->usrdata;
bfd_vma vma;
bfd_size_type size;
if (lookup->found)
return;
if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)
return;
vma = bfd_get_section_vma(abfd, section);
if (lookup->pc < vma)
return;
size = bfd_get_section_size(section);
if (lookup->pc >= vma + size)
return;
lookup->found = bfd_find_nearest_line_discriminator(abfd,
section,
lookup->syms,
lookup->pc - vma,
&lookup->filename,
&lookup->functionname,
&lookup->line,
&lookup->discriminator);
}
static void translate_address(Lookup *lookup)
{
Buffer *buffer = NULL;
lookup->pc = (bfd_vma)lookup->address;
if (with_addresses) {
char tmp[256];
bfd_sprintf_vma(lookup->abfd, tmp, lookup->pc);
buffer = buffer_printf(buffer, "0x%s: ", tmp);
}
lookup->found = FALSE;
bfd_map_over_sections(lookup->abfd, find_address_in_section, NULL);
if (!lookup->found) {
if (with_functions)
buffer = buffer_printf(buffer, "?? ");
buffer = buffer_printf(buffer, "??:0");
} else {
while (1) {
if (with_functions) {
const char *name;
char *alloc = NULL;
name = lookup->functionname;
if (name == NULL || *name == '\0')
name = "??";
else if (do_demangle) {
alloc = bfd_demangle(lookup->abfd, name, 3);
if (alloc != NULL)
name = alloc;
}
buffer = buffer_printf(buffer, "%s", name);
buffer = buffer_printf(buffer, " at ");
if (alloc != NULL)
free(alloc);
}
if (base_names && lookup->filename != NULL) {
const char *h = strrchr(lookup->filename, '/');
if (h != NULL)
lookup->filename = h + 1;
}
buffer = buffer_printf(buffer, "%s:", lookup->filename ? lookup->filename : "??");
if (lookup->line != 0) {
if (lookup->discriminator != 0)
buffer = buffer_printf(buffer, "%u (discriminator %u)", lookup->line, lookup->discriminator);
else
buffer = buffer_printf(buffer, "%u", lookup->line);
} else {
buffer = buffer_printf(buffer, "??");
}
if (!unwind_inlines)
lookup->found = FALSE;
else
lookup->found =
bfd_find_inliner_info(lookup->abfd, &lookup->filename, &lookup->functionname, &lookup->line);
if (!lookup->found)
break;
buffer = buffer_printf(buffer, " (inlined by) ");
}
}
if (buffer)
lookup->result = buffer->data;
}
Lookup *addr2line_init(const char *file_name)
{
bfd *abfd = bfd_openr(file_name, NULL);
if (abfd == NULL)
return NULL;
// Decompress sections.
abfd->flags |= BFD_DECOMPRESS;
if (bfd_check_format(abfd, bfd_archive))
goto err;
char **matching = NULL;
if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
free(matching);
goto err;
}
Lookup *lookup = (Lookup *)calloc(1, sizeof(Lookup));
lookup->abfd = abfd;
lookup->abfd->usrdata = lookup;
lookup->syms = slurp_symtab(abfd);
lookup->exe_file_name = strdup(file_name);
return lookup;
err:
bfd_close(abfd);
return NULL;
}
void addr2line_destroy(Lookup *lookup)
{
free(lookup->syms);
bfd_close(lookup->abfd);
free(lookup->exe_file_name);
free(lookup);
}
char *addr2line_lookup(Lookup *lookup, void *address)
{
lookup->address = address;
lookup->result = NULL;
translate_address(lookup);
return lookup->result;
}
#endif

14
src/util/addr2line.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef ADDR2LINE
#define ADDR2LINE
#ifdef ENABLE_EXECINFO
typedef struct Lookup Lookup;
Lookup *addr2line_init(const char *file_name);
void addr2line_destroy(Lookup *lookup);
char *addr2line_lookup(Lookup *lookup, void *address);
#endif
#endif

471
src/util/mem.c Normal file
View File

@@ -0,0 +1,471 @@
#ifdef ENABLE_EXECINFO
#include <dlfcn.h>
#include <execinfo.h>
#include <pthread.h>
#include <stdc-predef.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <zlib.h>
#include "addr2line.h"
#include "mem.h"
#include "print.h"
#ifndef RTLD_NEXT
# define RTLD_NEXT ((void *) -1l)
#endif
#ifndef thread_local
# if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
# define thread_local _Thread_local
# elif defined _WIN32 && ( \
defined _MSC_VER || \
defined __ICL || \
defined __DMC__ || \
defined __BORLANDC__ )
# define thread_local __declspec(thread)
/* note that ICC (linux) and Clang are covered by __GNUC__ */
# elif defined __GNUC__ || \
defined __SUNPRO_C || \
defined __xlC__
# define thread_local __thread
# else
# error "Cannot define thread_local"
# endif
#endif
#define get_caller() __builtin_extract_return_addr(__builtin_return_address(0))
#define size_t_mul_overflow(a, b) (a > 0 && b > SIZE_MAX / a)
typedef struct ListItem {
struct ListItem *next;
struct ListItem *prev;
void *data;
} ListItem;
typedef struct List {
ListItem *head;
ListItem *tail;
} List;
void list_append(List *list, void *data)
{
ListItem *n = (ListItem*)calloc(1, sizeof(ListItem));
n->data = data;
if (!list->tail) {
list->head = list->tail = n;
} else {
list->tail->next = n;
n->prev = list->tail;
list->tail = list->tail->next;
}
}
typedef size_t (*HashFunc)(void *data);
typedef int (*EqualFunc)(void *data1, void *data2);
typedef void *(*CopyFunc)(void *data);
typedef struct HashTable {
List *buckets;
size_t num_buckets;
size_t (*hash_func)(void *data);
int (*equal_func)(void *data1, void *data2);
void *(*copy_func)(void *data);
} HashTable;
size_t hash_void(void *data)
{
return (size_t)data;
}
int equal_void(void *data1, void *data2)
{
return data1 == data2;
}
void *copy_void(void *data)
{
return data;
}
void hash_table_init(HashTable *table, size_t num_buckets, HashFunc hash_func, EqualFunc equal_func, CopyFunc copy_func)
{
if (table->buckets)
return;
if (num_buckets < 1)
num_buckets = 4096;
table->num_buckets = num_buckets;
table->buckets = (List*)calloc(table->num_buckets, sizeof(List));
table->hash_func = hash_func ? hash_func : hash_void;
table->equal_func = equal_func ? equal_func : equal_void;
table->copy_func = copy_func ? copy_func : copy_void;
}
void *hash_table_add(HashTable *table, void *data)
{
size_t hash = table->hash_func(data);
size_t bucket = hash % table->num_buckets;
for (ListItem *existing = table->buckets[bucket].head; existing; existing = existing->next) {
if (table->equal_func(existing->data, data))
return existing->data;
}
list_append(&table->buckets[bucket], table->copy_func(data));
return data;
}
#define MAX_TRACE_SIZE 120
typedef struct Trace {
void *entries[MAX_TRACE_SIZE+8];
size_t id;
} Trace;
#define LARGE_THRESH (10 * 1024 * 1024)
static void *(*calloc_original)(size_t count, size_t size) = NULL;
static void *(*realloc_original)(void *p, size_t size) = NULL;
static void *(*malloc_original)(size_t size) = NULL;
static void *(*free_original)(void *p) = NULL;
static gzFile mem_log = NULL;
static pid_t mem_log_owner = 0;
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
static HashTable traces;
static size_t next_trace_id = 0;
pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
static HashTable addr2lines;
pthread_mutex_t addr2lines_mutex = PTHREAD_MUTEX_INITIALIZER;
thread_local int paused = 0;
static double get_unix_time()
{
struct timeval tv;
if (gettimeofday(&tv, NULL) != 0)
return 0;
return tv.tv_sec + 1.0e-6 * tv.tv_usec;
}
__attribute__ ((noinline))
static void get_backtrace(Trace *trace) {
int size = backtrace(trace->entries, MAX_TRACE_SIZE);
trace->entries[size] = NULL;
for (void **p = trace->entries; *p; p++) {
*p = *(p+2);
}
trace->id = 0;
}
int trace_size(Trace *trace)
{
int size = 0;
for (void **p = trace->entries; *p; p++) {
size++;
}
return size;
}
typedef struct FilenameLookup {
char *file_name;
Lookup *lookup;
} FilenameLookup;
size_t hash_lookup(void *data)
{
FilenameLookup *lookup = (FilenameLookup *)data;
size_t seed = 14695981039346656037ULL;
size_t hash = seed;
for (char *p = lookup->file_name; *p; p++) {
hash = (hash ^ (size_t)(*p)) * 1099511628211ULL;
}
return hash;
}
int equal_lookup(void *data1, void *data2)
{
FilenameLookup *lookup1 = (FilenameLookup *)data1;
FilenameLookup *lookup2 = (FilenameLookup *)data2;
return strcmp(lookup1->file_name, lookup2->file_name) == 0;
}
char *resolve_symbol(char *file_name, void *address)
{
FilenameLookup *lookup = (FilenameLookup *)calloc(1, sizeof(FilenameLookup));
lookup->file_name = strdup(file_name);
pthread_mutex_lock(&addr2lines_mutex);
hash_table_init(&addr2lines, 0, hash_lookup, equal_lookup, NULL);
FilenameLookup *existing = (FilenameLookup *)hash_table_add(&addr2lines, lookup);
if (existing == lookup) {
lookup->lookup = addr2line_init(lookup->file_name);
} else {
free(lookup->file_name);
free(lookup);
}
char *result = addr2line_lookup(existing->lookup, address);
pthread_mutex_unlock(&addr2lines_mutex);
return result;
}
char *backtrace_to_string(Trace *trace)
{
int size = trace_size(trace);
char **strings = backtrace_symbols(trace->entries, size);
if (!strings)
return NULL;
Buffer *buffer = NULL;
buffer = buffer_printf(buffer, "t %lu ", trace->id);
for (int i = 0; i < size; i++) {
buffer = buffer_printf(buffer, "%s", strings[i]);
// /lib/x86_64-linux-gnu/libglib-2.0.so.0(g_realloc+0xf) [0x7efda85c96af]
char *func_start = strstr(strings[i], "(");
if (func_start) {
*func_start = 0;
char *file_name = strings[i];
char *resolved = resolve_symbol(file_name, trace->entries[i]);
if (resolved) {
buffer = buffer_printf(buffer, " %s", resolved);
free(resolved);
}
}
buffer = buffer_printf(buffer, "|");
}
free(strings);
buffer = buffer_printf(buffer, "\n");
return buffer->data;
}
static size_t hash_trace(Trace *trace)
{
size_t seed = 14695981039346656037ULL;
size_t hash = seed;
for (void **p = trace->entries; *p; p++) {
hash = (hash ^ (size_t)(*p)) * 1099511628211ULL;
}
return hash;
}
static int equal_traces(Trace *a, Trace *b)
{
void **pa, **pb;
for (pa = a->entries, pb = b->entries; *pa && *pb; pa++, pb++) {
if (*pa != *pb)
return 0;
}
if (*pa || *pb)
return 0;
return 1;
}
static Trace *copy_trace(Trace *trace)
{
Trace *result = (Trace*)calloc(1, sizeof(Trace));
*result = *trace;
return result;
}
static int trace_assign_id(Trace *trace)
{
int created = 0;
pthread_mutex_lock(&trace_mutex);
hash_table_init(&traces, 1024 * 1024, (HashFunc)hash_trace, (EqualFunc)equal_traces, (CopyFunc)copy_trace);
trace->id = 0;
Trace *existing = (Trace*)hash_table_add(&traces, trace);
if (!existing->id) {
next_trace_id++;
existing->id = trace->id = next_trace_id;
created = 1;
} else {
trace->id = existing->id;
}
pthread_mutex_unlock(&trace_mutex);
return created;
}
static void log_flush();
static void log_init()
{
char path[256];
sprintf(path, "/tmp/tint2.%lu.memtrace", (size_t)getpid());
pthread_mutex_lock(&log_mutex);
if (!mem_log) {
mem_log = gzopen(path, "w");
mem_log_owner = getpid();
atexit(log_flush);
}
pthread_mutex_unlock(&log_mutex);
if (mem_log)
fprintf(stderr, "Writing memory log to %s\n", path);
}
static void log_write(char *buffer)
{
if (mem_log) {
pthread_mutex_lock(&log_mutex);
if (getpid() == mem_log_owner)
gzwrite(mem_log, buffer, (unsigned)strlen(buffer));
pthread_mutex_unlock(&log_mutex);
}
}
static void log_flush()
{
if (mem_log) {
pthread_mutex_lock(&log_mutex);
if (getpid() == mem_log_owner)
gzflush(mem_log, Z_SYNC_FLUSH);
pthread_mutex_unlock(&log_mutex);
}
}
static void malloc_profile_load_pointers()
{
realloc_original = dlsym(RTLD_NEXT, "realloc");
malloc_original = dlsym(RTLD_NEXT, "malloc");
calloc_original = dlsym(RTLD_NEXT, "calloc");
free_original = dlsym(RTLD_NEXT, "free");
int trace_mem = getenv("TRACE_MEM") != NULL;
if (trace_mem)
log_init();
else
paused = 1;
}
void *calloc(size_t count, size_t size)
{
if (paused)
return calloc_original ? calloc_original(count, size) : 0;
paused = 1;
if (!calloc_original) {
malloc_profile_load_pointers();
if (!calloc_original)
return NULL;
}
void *result = calloc_original(count, size);
if (mem_log) {
Trace trace;
get_backtrace(&trace);
int created = trace_assign_id(&trace);
if (created) {
char *str = backtrace_to_string(&trace);
if (str) {
log_write(str);
free(str);
}
}
char buffer[1024];
sprintf(buffer, "[%f] t %lu : %p = c %lu %lu\n", get_unix_time(), trace.id, result, count, size);
log_write(buffer);
if (size_t_mul_overflow(count, size) || (count * size) > LARGE_THRESH)
log_flush();
}
paused = 0;
return result;
}
void *realloc(void *p, size_t size)
{
if (paused)
return realloc_original ? realloc_original(p, size) : 0;
paused = 1;
if (!realloc_original) {
malloc_profile_load_pointers();
if (!realloc_original)
return NULL;
}
void *result = realloc_original(p, size);
if (mem_log) {
Trace trace;
get_backtrace(&trace);
int created = trace_assign_id(&trace);
if (created) {
char *str = backtrace_to_string(&trace);
if (str) {
log_write(str);
free(str);
}
}
char buffer[1024];
sprintf(buffer, "[%f] t %lu : %p = r %p %lu\n", get_unix_time(), trace.id, result, p, size);
log_write(buffer);
if (size > LARGE_THRESH)
log_flush();
}
paused = 0;
return result;
}
void *malloc(size_t size)
{
if (paused)
return malloc_original ? malloc_original(size) : 0;
paused = 1;
if (!malloc_original) {
malloc_profile_load_pointers();
if (!malloc_original)
return NULL;
}
void *result = malloc_original(size);
if (mem_log) {
Trace trace;
get_backtrace(&trace);
int created = trace_assign_id(&trace);
if (created) {
char *str = backtrace_to_string(&trace);
if (str) {
log_write(str);
free(str);
}
}
char buffer[1024];
sprintf(buffer, "[%f] t %lu : %p = m %lu\n", get_unix_time(), trace.id, result, size);
log_write(buffer);
if (size > LARGE_THRESH)
log_flush();
}
paused = 0;
return result;
}
void free(void *p)
{
if (paused) {
if (free_original)
free_original(p);
return;
}
paused = 1;
if (!free_original) {
malloc_profile_load_pointers();
if (!free_original)
return;
}
free_original(p);
if (mem_log) {
Trace trace;
get_backtrace(&trace);
int created = trace_assign_id(&trace);
if (created) {
char *str = backtrace_to_string(&trace);
if (str) {
log_write(str);
free(str);
}
}
char buffer[1024];
sprintf(buffer, "[%f] t %lu : 0 = f %p\n", get_unix_time(), trace.id, p);
log_write(buffer);
}
paused = 0;
}
#endif

4
src/util/mem.h Normal file
View File

@@ -0,0 +1,4 @@
#ifndef MEM_H
#define MEM_H
#endif

40
src/util/print.c Normal file
View File

@@ -0,0 +1,40 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "print.h"
Buffer *vbuffer_printf(Buffer *buffer, const char *fmt, va_list ap)
{
va_list ap1;
va_copy(ap1, ap);
int ret = vsnprintf(NULL, 0, fmt, ap1) + 1;
va_end(ap1);
if (ret < 1)
return buffer;
size_t size = (size_t)ret;
if (!buffer)
buffer = (Buffer*)calloc(1, sizeof(Buffer));
if (!buffer->data) {
buffer->size = 2 * size + 128;
buffer->data = (char*) calloc(buffer->size, 1);
} else if (strlen(buffer->data) + size >= buffer->size + 1) {
buffer->size = 2 * (size + buffer->size) + 128;
buffer->data = (char*) realloc(buffer->data, buffer->size);
}
vsnprintf(buffer->data + strlen(buffer->data), size, fmt, ap);
return buffer;
}
Buffer *buffer_printf(Buffer *buffer, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
buffer = vbuffer_printf(buffer, fmt, ap);
va_end(ap);
return buffer;
}

16
src/util/print.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef PRINT_H
#define PRINT_H
#include <stdarg.h>
#include <stddef.h>
#include <sys/types.h>
typedef struct Buffer {
char *data;
size_t size;
} Buffer;
Buffer *vbuffer_printf(Buffer *buffer, const char *fmt, va_list ap);
Buffer *buffer_printf(Buffer *buffer, const char *fmt, ...);
#endif

165
test/memtrace-analyze.py Executable file
View File

@@ -0,0 +1,165 @@
#!/usr/bin/env python2
import gzip
import sys
def commas(v):
return "{:12,}".format(v)
class Event:
def __init__(self):
self.alloc = False
self.free = False
self.ts = None
self.size = None
self.addr = None
self.trace_name = None
class Analyzer:
def __init__(self):
self.traces = {}
self.events = []
self.all_allocations = {}
self.new_allocations = {}
self.all_mem = 0
self.old_mem = 0
def read(self, path):
with gzip.open(path, "rb") as f:
try:
for line in f:
line = line.strip()
if line.startswith("t"):
self.add_trace(line)
elif line.startswith("["):
self.add_call(line)
except:
pass
def add_trace(self, line):
t, name, content = line.split(" ", 2)
name = int(name)
assert(name > 0)
assert(name not in self.traces)
self.traces[name] = content.split("|")
def add_call(self, line):
first, second = line.split(":", 1)
first = first.strip()
ts, t, tname = first.split(" ")
ts = float(ts.replace("[", "").replace("]", ""))
assert(t == "t")
tname = int(tname)
assert(tname in self.traces)
second = second.strip()
result, eq, func, params = second.split(" ", 3)
assert(eq == "=")
params = params.split(" ")
e = Event()
e.ts = ts
e.trace_name = tname
if func == "m" and result != "(nil)":
e.alloc = True
e.addr = int(result, 16)
e.size = int(params[0])
elif func == "c" and result != "(nil)":
e.alloc = True
e.addr = int(result, 16)
e.size = int(params[0]) * int(params[1])
elif func == "r":
old_addr = params[0]
size = int(params[1])
if old_addr == "(nil)":
if result != "(nil)":
e.alloc = True
e.addr = int(result, 16)
e.size = size
elif old_addr != "(nil)":
if result != "(nil)":
e.free = True
e.addr = int(old_addr, 16)
self.events.append(e)
e = Event()
e.ts = ts
e.trace_name = tname
e.alloc = True
e.addr = int(result, 16)
e.size = size
elif func == "f":
addr = params[0]
if addr != "(nil)":
e.free = True
e.addr = int(addr, 16)
if e.alloc or e.free:
self.events.append(e)
def analyze(self):
self.all_allocations = {}
self.new_allocations = {}
self.all_mem = 0
self.old_mem = 0
self.peak_mem = 0
period = 1
next_ts_print = self.events[0].ts
for e in self.events:
if e.ts >= next_ts_print:
self.print_allocations()
next_ts_print += period
self.process_event(e)
self.print_allocations()
self.print_leaks()
def print_allocations(self):
print "Memory usage: current:", commas(self.all_mem), " | delta:", commas(self.all_mem - self.old_mem), " | peak:", commas(self.peak_mem)
self.old_mem = self.all_mem
self.new_alllocations = {}
def print_leaks(self):
if not self.all_allocations:
return
print "Memory leaked:", commas(self.all_mem)
leaks = [e for addr, e in self.all_allocations.iteritems()]
leaks.sort(key=lambda e: e.size)
for e in leaks:
print "Leak:", commas(e.size)
print "Allocated in:\n ", "\n ".join(self.traces[e.trace_name])
def process_event(self, e):
if e.alloc:
assert(e.addr not in self.all_allocations)
self.all_allocations[e.addr] = e
assert(e.addr not in self.new_allocations)
self.new_allocations[e.addr] = e
self.all_mem += e.size
self.peak_mem = max(self.peak_mem, self.all_mem)
else:
assert(e.addr in self.all_allocations)
e_alloc = self.all_allocations[e.addr]
if e.ts - e_alloc.ts > 1. and e_alloc.size > 1e3 and False:
print "Long-lived alloc:", commas(e_alloc.size), "freed after", commas(e.ts - e_alloc.ts) + "s"
print "Allocated in:\n ", "\n ".join(self.traces[e_alloc.trace_name])
print "Freed in:\n ", "\n ".join(self.traces[e.trace_name])
if e_alloc.addr in self.new_allocations:
if e_alloc.size > 1e7 and False:
print "Large alloc:", commas(e_alloc.size), "freed after", commas(e.ts - e_alloc.ts) + "s"
print "Allocated in:\n ", "\n ".join(self.traces[e_alloc.trace_name])
print "Freed in:\n ", "\n ".join(self.traces[e.trace_name])
del self.new_allocations[e_alloc.addr]
del self.all_allocations[e_alloc.addr]
self.all_mem -= e_alloc.size
def main():
a = Analyzer()
a.read(sys.argv[1])
a.analyze()
if __name__ == "__main__":
main()

View File

@@ -237,3 +237,9 @@ src/signals.c
src/signals.h src/signals.h
src/tracing.c src/tracing.c
src/tracing.h src/tracing.h
src/util/mem.c
src/util/mem.h
src/util/addr2line.c
src/util/addr2line.h
src/util/print.h
src/util/print.c