diff options
author | Iliyan Malchev <malchev@google.com> | 2012-06-02 08:14:36 -0700 |
---|---|---|
committer | Iliyan Malchev <malchev@google.com> | 2012-06-02 08:14:36 -0700 |
commit | 252a5c854a08e89fc7337ea679220161fe4ea98f (patch) | |
tree | 8e0e154a5c1c94613138ba8cee0b77b8e34d0a49 | |
parent | f0ddaa2fac00ac20059c0b2c142da9de2838a7b6 (diff) | |
parent | e1dd3c287ba836281de0197670018bd9bbfbd62b (diff) | |
download | bionic-252a5c854a08e89fc7337ea679220161fe4ea98f.zip bionic-252a5c854a08e89fc7337ea679220161fe4ea98f.tar.gz bionic-252a5c854a08e89fc7337ea679220161fe4ea98f.tar.bz2 |
resolved conflicts for merge of e1dd3c28 to jb-dev-plus-aosp
Change-Id: I58b9c13d20771aa39b703ec05cbff8aeaad38fe8
-rw-r--r-- | libc/Android.mk | 5 | ||||
-rw-r--r-- | libc/bionic/libc_init_common.c | 8 | ||||
-rw-r--r-- | libc/bionic/libc_init_dynamic.c | 6 | ||||
-rw-r--r-- | libc/bionic/malloc_debug_check.c | 574 | ||||
-rw-r--r-- | libc/bionic/malloc_debug_check_mapinfo.c | 126 | ||||
-rw-r--r-- | libc/bionic/malloc_debug_check_mapinfo.h | 46 | ||||
-rw-r--r-- | libc/bionic/malloc_debug_common.c | 52 | ||||
-rw-r--r-- | libc/bionic/malloc_debug_common.h | 28 | ||||
-rw-r--r-- | libc/bionic/malloc_debug_leak.c | 265 | ||||
-rw-r--r-- | libc/bionic/malloc_debug_qemu.c | 18 | ||||
-rw-r--r-- | libc/bionic/malloc_debug_stacktrace.c | 76 |
11 files changed, 916 insertions, 288 deletions
diff --git a/libc/Android.mk b/libc/Android.mk index 6c535dc..f55f9fe 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -731,7 +731,10 @@ LOCAL_CFLAGS := \ LOCAL_C_INCLUDES := $(libc_common_c_includes) LOCAL_SRC_FILES := \ - bionic/malloc_debug_leak.c + bionic/malloc_debug_leak.c \ + bionic/malloc_debug_check.c \ + bionic/malloc_debug_check_mapinfo.c \ + bionic/malloc_debug_stacktrace.c LOCAL_MODULE:= libc_malloc_debug_leak diff --git a/libc/bionic/libc_init_common.c b/libc/bionic/libc_init_common.c index 7fb1246..4ce4db6 100644 --- a/libc/bionic/libc_init_common.c +++ b/libc/bionic/libc_init_common.c @@ -138,4 +138,12 @@ void __libc_fini(void* array) func(); } + +#ifndef LIBC_STATIC + { + extern void __libc_postfini(void) __attribute__((weak)); + if (__libc_postfini) + __libc_postfini(); + } +#endif } diff --git a/libc/bionic/libc_init_dynamic.c b/libc/bionic/libc_init_dynamic.c index 1c8480c..3a7e8e2 100644 --- a/libc/bionic/libc_init_dynamic.c +++ b/libc/bionic/libc_init_dynamic.c @@ -89,6 +89,12 @@ void __libc_preinit(void) malloc_debug_init(); } +void __libc_postfini(void) +{ + extern void malloc_debug_fini(void); + malloc_debug_fini(); +} + /* This function is called from the executable's _start entry point * (see arch-$ARCH/bionic/crtbegin_dynamic.S), which is itself * called by the dynamic linker after it has loaded all shared diff --git a/libc/bionic/malloc_debug_check.c b/libc/bionic/malloc_debug_check.c new file mode 100644 index 0000000..4ae21fe --- /dev/null +++ b/libc/bionic/malloc_debug_check.c @@ -0,0 +1,574 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> +#include <pthread.h> +#include <time.h> +#include <stdio.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stddef.h> +#include <stdarg.h> +#include <fcntl.h> +#include <unwind.h> +#include <dlfcn.h> +#include <stdbool.h> + +#include <sys/types.h> +#include <sys/system_properties.h> + +#include "dlmalloc.h" +#include "logd.h" + +#include "malloc_debug_common.h" +#include "malloc_debug_check_mapinfo.h" + +static mapinfo *milist; + +/* libc.debug.malloc.backlog */ +extern unsigned int malloc_double_free_backlog; + +#define MAX_BACKTRACE_DEPTH 15 +#define ALLOCATION_TAG 0x1ee7d00d +#define BACKLOG_TAG 0xbabecafe +#define FREE_POISON 0xa5 +#define BACKLOG_DEFAULT_LEN 100 +#define FRONT_GUARD 0xaa +#define FRONT_GUARD_LEN (1<<5) +#define REAR_GUARD 0xbb +#define REAR_GUARD_LEN (1<<5) + +static void print_backtrace(const intptr_t *bt, unsigned int depth); + +static void log_message(const char* format, ...) +{ + extern pthread_mutex_t gAllocationsMutex; + extern const MallocDebug __libc_malloc_default_dispatch; + extern const MallocDebug* __libc_malloc_dispatch; + + va_list args; + + pthread_mutex_lock(&gAllocationsMutex); + { + const MallocDebug* current_dispatch = __libc_malloc_dispatch; + __libc_malloc_dispatch = &__libc_malloc_default_dispatch; + va_start(args, format); + __libc_android_log_vprint(ANDROID_LOG_ERROR, "libc", + format, args); + va_end(args); + __libc_malloc_dispatch = current_dispatch; + } + pthread_mutex_unlock(&gAllocationsMutex); +} + +struct hdr { + uint32_t tag; + struct hdr *prev; + struct hdr *next; + intptr_t bt[MAX_BACKTRACE_DEPTH]; + int bt_depth; + intptr_t freed_bt[MAX_BACKTRACE_DEPTH]; + int freed_bt_depth; + size_t size; + char front_guard[FRONT_GUARD_LEN]; +} __attribute__((packed)); + +struct ftr { + char rear_guard[REAR_GUARD_LEN]; +} __attribute__((packed)); + +static inline struct ftr * to_ftr(struct hdr *hdr) +{ + return (struct ftr *)(((char *)(hdr + 1)) + hdr->size); +} + +static inline void *user(struct hdr *hdr) +{ + return hdr + 1; +} + +static inline struct hdr *meta(void *user) +{ + return ((struct hdr *)user) - 1; +} + +/* Call this on exit() to get leaked memory */ +void free_leaked_memory(void); + +static unsigned num; +static struct hdr *tail; +static struct hdr *head; +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +static unsigned backlog_num; +static struct hdr *backlog_tail; +static struct hdr *backlog_head; +static pthread_mutex_t backlog_lock = PTHREAD_MUTEX_INITIALIZER; + +extern __LIBC_HIDDEN__ +int get_backtrace(intptr_t* addrs, size_t max_entries); + +static void print_backtrace(const intptr_t *bt, unsigned int depth) +{ + const mapinfo *mi; + unsigned int cnt; + unsigned int rel_pc; + intptr_t self_bt[MAX_BACKTRACE_DEPTH]; + + if (!bt) { + depth = get_backtrace(self_bt, MAX_BACKTRACE_DEPTH); + bt = self_bt; + } + + log_message("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); + for (cnt = 0; cnt < depth && cnt < MAX_BACKTRACE_DEPTH; cnt++) { + mi = pc_to_mapinfo(milist, bt[cnt], &rel_pc); + log_message("\t#%02d pc %08x %s\n", cnt, + mi ? (intptr_t)rel_pc : bt[cnt], + mi ? mi->name : "(unknown)"); + } +} + +static inline void init_front_guard(struct hdr *hdr) +{ + memset(hdr->front_guard, FRONT_GUARD, FRONT_GUARD_LEN); +} + +static inline bool is_front_guard_valid(struct hdr *hdr) +{ + unsigned i; + for (i = 0; i < FRONT_GUARD_LEN; i++) + if (hdr->front_guard[i] != FRONT_GUARD) + return 0; + return 1; +} + +static inline void init_rear_guard(struct hdr *hdr) +{ + struct ftr *ftr = to_ftr(hdr); + memset(ftr->rear_guard, REAR_GUARD, REAR_GUARD_LEN); +} + +static inline bool is_rear_guard_valid(struct hdr *hdr) +{ + unsigned i; + int valid = 1; + int first_mismatch = -1; + struct ftr *ftr = to_ftr(hdr); + for (i = 0; i < REAR_GUARD_LEN; i++) { + if (ftr->rear_guard[i] != REAR_GUARD) { + if (first_mismatch < 0) + first_mismatch = i; + valid = 0; + } + else if (first_mismatch >= 0) { + log_message("+++ REAR GUARD MISMATCH [%d, %d)\n", first_mismatch, i); + first_mismatch = -1; + } + } + + if (first_mismatch >= 0) + log_message("+++ REAR GUARD MISMATCH [%d, %d)\n", first_mismatch, i); + return valid; +} + +static inline void add_locked(struct hdr *hdr, struct hdr **tail, struct hdr **head) +{ + hdr->prev = NULL; + hdr->next = *head; + if (*head) + (*head)->prev = hdr; + else + *tail = hdr; + *head = hdr; +} + +static inline int del_locked(struct hdr *hdr, struct hdr **tail, struct hdr **head) +{ + if (hdr->prev) + hdr->prev->next = hdr->next; + else + *head = hdr->next; + if (hdr->next) + hdr->next->prev = hdr->prev; + else + *tail = hdr->prev; + return 0; +} + +static inline void add(struct hdr *hdr, size_t size) +{ + pthread_mutex_lock(&lock); + hdr->tag = ALLOCATION_TAG; + hdr->size = size; + init_front_guard(hdr); + init_rear_guard(hdr); + num++; + add_locked(hdr, &tail, &head); + pthread_mutex_unlock(&lock); +} + +static inline int del(struct hdr *hdr) +{ + if (hdr->tag != ALLOCATION_TAG) + return -1; + + pthread_mutex_lock(&lock); + del_locked(hdr, &tail, &head); + num--; + pthread_mutex_unlock(&lock); + return 0; +} + +static inline void poison(struct hdr *hdr) +{ + memset(user(hdr), FREE_POISON, hdr->size); +} + +static int was_used_after_free(struct hdr *hdr) +{ + unsigned i; + const char *data = (const char *)user(hdr); + for (i = 0; i < hdr->size; i++) + if (data[i] != FREE_POISON) + return 1; + return 0; +} + +/* returns 1 if valid, *safe == 1 if safe to dump stack */ +static inline int check_guards(struct hdr *hdr, int *safe) +{ + *safe = 1; + if (!is_front_guard_valid(hdr)) { + if (hdr->front_guard[0] == FRONT_GUARD) { + log_message("+++ ALLOCATION %p SIZE %d HAS A CORRUPTED FRONT GUARD\n", + user(hdr), hdr->size); + } else { + log_message("+++ ALLOCATION %p HAS A CORRUPTED FRONT GUARD "\ + "(NOT DUMPING STACKTRACE)\n", user(hdr)); + /* Allocation header is probably corrupt, do not print stack trace */ + *safe = 0; + } + return 0; + } + + if (!is_rear_guard_valid(hdr)) { + log_message("+++ ALLOCATION %p SIZE %d HAS A CORRUPTED REAR GUARD\n", + user(hdr), hdr->size); + return 0; + } + + return 1; +} + +/* returns 1 if valid, *safe == 1 if safe to dump stack */ +static inline int check_allocation_locked(struct hdr *hdr, int *safe) +{ + int valid = 1; + *safe = 1; + + if (hdr->tag != ALLOCATION_TAG && hdr->tag != BACKLOG_TAG) { + log_message("+++ ALLOCATION %p HAS INVALID TAG %08x (NOT DUMPING STACKTRACE)\n", + user(hdr), hdr->tag); + /* Allocation header is probably corrupt, do not dequeue or dump stack + * trace. + */ + *safe = 0; + return 0; + } + + if (hdr->tag == BACKLOG_TAG && was_used_after_free(hdr)) { + log_message("+++ ALLOCATION %p SIZE %d WAS USED AFTER BEING FREED\n", + user(hdr), hdr->size); + valid = 0; + /* check the guards to see if it's safe to dump a stack trace */ + (void)check_guards(hdr, safe); + } + else + valid = check_guards(hdr, safe); + + if (!valid && *safe) { + log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", + user(hdr), hdr->size); + print_backtrace(hdr->bt, hdr->bt_depth); + if (hdr->tag == BACKLOG_TAG) { + log_message("+++ ALLOCATION %p SIZE %d FREED HERE:\n", + user(hdr), hdr->size); + print_backtrace(hdr->freed_bt, hdr->freed_bt_depth); + } + } + + return valid; +} + +static inline int del_and_check_locked(struct hdr *hdr, + struct hdr **tail, struct hdr **head, unsigned *cnt, + int *safe) +{ + int valid; + valid = check_allocation_locked(hdr, safe); + if (safe) { + (*cnt)--; + del_locked(hdr, tail, head); + } + return valid; +} + +static inline void del_from_backlog_locked(struct hdr *hdr) +{ + int safe; + (void)del_and_check_locked(hdr, + &backlog_tail, &backlog_head, &backlog_num, + &safe); + hdr->tag = 0; /* clear the tag */ +} + +static inline void del_from_backlog(struct hdr *hdr) +{ + pthread_mutex_lock(&backlog_lock); + del_from_backlog_locked(hdr); + pthread_mutex_unlock(&backlog_lock); +} + +static inline int del_leak(struct hdr *hdr, int *safe) +{ + int valid; + pthread_mutex_lock(&lock); + valid = del_and_check_locked(hdr, + &tail, &head, &num, + safe); + pthread_mutex_unlock(&lock); + return valid; +} + +static inline void add_to_backlog(struct hdr *hdr) +{ + pthread_mutex_lock(&backlog_lock); + hdr->tag = BACKLOG_TAG; + backlog_num++; + add_locked(hdr, &backlog_tail, &backlog_head); + poison(hdr); + /* If we've exceeded the maximum backlog, clear it up */ + while (backlog_num > malloc_double_free_backlog) { + struct hdr *gone = backlog_tail; + del_from_backlog_locked(gone); + dlfree(gone); + } + pthread_mutex_unlock(&backlog_lock); +} + +void* chk_malloc(size_t size) +{ + struct hdr *hdr; + +// log_message("%s: %s\n", __FILE__, __FUNCTION__); + + hdr = dlmalloc(sizeof(struct hdr) + size + sizeof(struct ftr)); + if (hdr) { + hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH); + add(hdr, size); + return user(hdr); + } + return NULL; +} + +void* chk_memalign(size_t alignment, size_t bytes) +{ +// log_message("%s: %s\n", __FILE__, __FUNCTION__); + // XXX: it's better to use malloc, than being wrong + return chk_malloc(bytes); +} + +void chk_free(void *ptr) +{ + struct hdr *hdr; + +// log_message("%s: %s\n", __FILE__, __FUNCTION__); + + if (!ptr) /* ignore free(NULL) */ + return; + + hdr = meta(ptr); + + if (del(hdr) < 0) { + intptr_t bt[MAX_BACKTRACE_DEPTH]; + int depth; + depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH); + if (hdr->tag == BACKLOG_TAG) { + log_message("+++ ALLOCATION %p SIZE %d BYTES MULTIPLY FREED!\n", + user(hdr), hdr->size); + log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", + user(hdr), hdr->size); + print_backtrace(hdr->bt, hdr->bt_depth); + /* hdr->freed_bt_depth should be nonzero here */ + log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n", + user(hdr), hdr->size); + print_backtrace(hdr->freed_bt, hdr->freed_bt_depth); + log_message("+++ ALLOCATION %p SIZE %d NOW BEING FREED HERE:\n", + user(hdr), hdr->size); + print_backtrace(bt, depth); + } + else { + log_message("+++ ALLOCATION %p IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", + user(hdr)); + print_backtrace(bt, depth); + /* Leak here so that we do not crash */ + //dlfree(user(hdr)); + } + } + else { + hdr->freed_bt_depth = get_backtrace(hdr->freed_bt, + MAX_BACKTRACE_DEPTH); + add_to_backlog(hdr); + } +} + +void *chk_realloc(void *ptr, size_t size) +{ + struct hdr *hdr; + +// log_message("%s: %s\n", __FILE__, __FUNCTION__); + + if (!size) { + chk_free(ptr); + return NULL; + } + + if (!ptr) + return chk_malloc(size); + + hdr = meta(ptr); + + if (del(hdr) < 0) { + intptr_t bt[MAX_BACKTRACE_DEPTH]; + int depth; + depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH); + if (hdr->tag == BACKLOG_TAG) { + log_message("+++ REALLOCATION %p SIZE %d OF FREED MEMORY!\n", + user(hdr), size, hdr->size); + log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", + user(hdr), hdr->size); + print_backtrace(hdr->bt, hdr->bt_depth); + /* hdr->freed_bt_depth should be nonzero here */ + log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n", + user(hdr), hdr->size); + print_backtrace(hdr->freed_bt, hdr->freed_bt_depth); + log_message("+++ ALLOCATION %p SIZE %d NOW BEING REALLOCATED HERE:\n", + user(hdr), hdr->size); + print_backtrace(bt, depth); + + /* We take the memory out of the backlog and fall through so the + * reallocation below succeeds. Since we didn't really free it, we + * can default to this behavior. + */ + del_from_backlog(hdr); + } + else { + log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", + user(hdr), size); + print_backtrace(bt, depth); + // just get a whole new allocation and leak the old one + return dlrealloc(0, size); + // return dlrealloc(user(hdr), size); // assuming it was allocated externally + } + } + + hdr = dlrealloc(hdr, sizeof(struct hdr) + size + sizeof(struct ftr)); + if (hdr) { + hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH); + add(hdr, size); + return user(hdr); + } + + return NULL; +} + +void *chk_calloc(int nmemb, size_t size) +{ +// log_message("%s: %s\n", __FILE__, __FUNCTION__); + struct hdr *hdr; + size_t total_size = nmemb * size; + hdr = dlcalloc(1, sizeof(struct hdr) + total_size + sizeof(struct ftr)); + if (hdr) { + hdr->bt_depth = get_backtrace( + hdr->bt, MAX_BACKTRACE_DEPTH); + add(hdr, total_size); + return user(hdr); + } + return NULL; +} + +static void heaptracker_free_leaked_memory(void) +{ + struct hdr *del; int cnt; + + if (num) + log_message("+++ THERE ARE %d LEAKED ALLOCATIONS\n", num); + + while (head) { + int safe; + del = head; + log_message("+++ DELETING %d BYTES OF LEAKED MEMORY AT %p (%d REMAINING)\n", + del->size, user(del), num); + if (del_leak(del, &safe)) { + /* safe == 1, because the allocation is valid */ + log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", + user(del), del->size); + print_backtrace(del->bt, del->bt_depth); + } + dlfree(del); + } + +// log_message("+++ DELETING %d BACKLOGGED ALLOCATIONS\n", backlog_num); + while (backlog_head) { + del = backlog_tail; + del_from_backlog(del); + dlfree(del); + } +} + +/* Initializes malloc debugging framework. + * See comments on MallocDebugInit in malloc_debug_common.h + */ +int malloc_debug_initialize(void) +{ + if (!malloc_double_free_backlog) + malloc_double_free_backlog = BACKLOG_DEFAULT_LEN; + milist = init_mapinfo(getpid()); + return 0; +} + +void malloc_debug_finalize(void) +{ + heaptracker_free_leaked_memory(); + deinit_mapinfo(milist); +} diff --git a/libc/bionic/malloc_debug_check_mapinfo.c b/libc/bionic/malloc_debug_check_mapinfo.c new file mode 100644 index 0000000..044fc65 --- /dev/null +++ b/libc/bionic/malloc_debug_check_mapinfo.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "dlmalloc.h" +#include "malloc_debug_check_mapinfo.h" + +// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 + +static mapinfo *parse_maps_line(char *line) +{ + mapinfo *mi; + int len = strlen(line); + + if(len < 1) return 0; + line[--len] = 0; + + if(len < 50) return 0; + if(line[20] != 'x') return 0; + + mi = dlmalloc(sizeof(mapinfo) + (len - 47)); + if(mi == 0) return 0; + + mi->start = strtoul(line, 0, 16); + mi->end = strtoul(line + 9, 0, 16); + /* To be filled in parse_elf_info if the mapped section starts with + * elf_header + */ + mi->next = 0; + strcpy(mi->name, line + 49); + + return mi; +} + +__LIBC_HIDDEN__ +mapinfo *init_mapinfo(int pid) +{ + struct mapinfo *milist = NULL; + char data[1024]; + sprintf(data, "/proc/%d/maps", pid); + FILE *fp = fopen(data, "r"); + if(fp) { + while(fgets(data, sizeof(data), fp)) { + mapinfo *mi = parse_maps_line(data); + if(mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + + return milist; +} + +__LIBC_HIDDEN__ +void deinit_mapinfo(mapinfo *mi) +{ + mapinfo *del; + while(mi) { + del = mi; + mi = mi->next; + dlfree(del); + } +} + +/* Map a pc address to the name of the containing ELF file */ +__LIBC_HIDDEN__ +const char *map_to_name(mapinfo *mi, unsigned pc, const char* def) +{ + while(mi) { + if((pc >= mi->start) && (pc < mi->end)){ + return mi->name; + } + mi = mi->next; + } + return def; +} + +/* Find the containing map info for the pc */ +__LIBC_HIDDEN__ +const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc) +{ + *rel_pc = pc; + while(mi) { + if((pc >= mi->start) && (pc < mi->end)){ + // Only calculate the relative offset for shared libraries + if (strstr(mi->name, ".so")) { + *rel_pc -= mi->start; + } + return mi; + } + mi = mi->next; + } + return NULL; +} diff --git a/libc/bionic/malloc_debug_check_mapinfo.h b/libc/bionic/malloc_debug_check_mapinfo.h new file mode 100644 index 0000000..8a01cd3 --- /dev/null +++ b/libc/bionic/malloc_debug_check_mapinfo.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef MALLOC_DEBUG_CHECK_MAPINFO_H +#define MALLOC_DEBUG_CHECK_MAPINFO_H + +#include <sys/cdefs.h> + +typedef struct mapinfo { + struct mapinfo *next; + unsigned start; + unsigned end; + char name[]; +} mapinfo; + +__LIBC_HIDDEN__ mapinfo *init_mapinfo(int pid); +__LIBC_HIDDEN__ void deinit_mapinfo(mapinfo *mi); +__LIBC_HIDDEN__ const char *map_to_name(mapinfo *mi, unsigned pc, const char* def); +__LIBC_HIDDEN__ const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc); + +#endif/*MALLOC_DEBUG_CHECK_MAPINFO_H*/ diff --git a/libc/bionic/malloc_debug_common.c b/libc/bionic/malloc_debug_common.c index 6837e39..4105ab8 100644 --- a/libc/bionic/malloc_debug_common.c +++ b/libc/bionic/malloc_debug_common.c @@ -240,17 +240,6 @@ void* memalign(size_t alignment, size_t bytes) { #include <dlfcn.h> #include "logd.h" -// ============================================================================= -// log functions -// ============================================================================= - -#define debug_log(format, ...) \ - __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", (format), ##__VA_ARGS__ ) -#define error_log(format, ...) \ - __libc_android_log_print(ANDROID_LOG_ERROR, "libc", (format), ##__VA_ARGS__ ) -#define info_log(format, ...) \ - __libc_android_log_print(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ ) - /* Table for dispatching malloc calls, depending on environment. */ static MallocDebug gMallocUse __attribute__((aligned(32))) = { dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign @@ -288,6 +277,14 @@ static void* libc_malloc_impl_handle = NULL; #define MALLOC_ALIGNMENT ((size_t)8U) #endif /* MALLOC_ALIGNMENT */ +/* This variable is set to the value of property libc.debug.malloc.backlog, + * when the value of libc.debug.malloc = 10. It determines the size of the + * backlog we use to detect multiple frees. If the property is not set, the + * backlog length defaults to an internal constant defined in + * malloc_debug_check.c + */ +unsigned int malloc_double_free_backlog; + /* Initializes memory allocation framework once per process. */ static void malloc_init_impl(void) { @@ -339,9 +336,17 @@ static void malloc_init_impl(void) switch (debug_level) { case 1: case 5: - case 10: + case 10: { + char debug_backlog[PROP_VALUE_MAX]; + if (__system_property_get("libc.debug.malloc.backlog", debug_backlog)) { + malloc_double_free_backlog = atoi(debug_backlog); + info_log("%s: setting backlog length to %d\n", + __progname, malloc_double_free_backlog); + } + so_name = "/system/lib/libc_malloc_debug_leak.so"; break; + } case 20: // Quick check: debug level 20 can only be handled in emulator. if (!qemu_running) { @@ -485,7 +490,19 @@ static void malloc_init_impl(void) } } +static void malloc_fini_impl(void) +{ + if (libc_malloc_impl_handle) { + MallocDebugFini malloc_debug_finalize = NULL; + malloc_debug_finalize = + dlsym(libc_malloc_impl_handle, "malloc_debug_finalize"); + if (malloc_debug_finalize) + malloc_debug_finalize(); + } +} + static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT; +static pthread_once_t malloc_fini_once_ctl = PTHREAD_ONCE_INIT; #endif // !LIBC_STATIC #endif // USE_DL_PREFIX @@ -504,3 +521,14 @@ void malloc_debug_init(void) } #endif // USE_DL_PREFIX && !LIBC_STATIC } + +void malloc_debug_fini(void) +{ + /* We need to finalize malloc iff we implement here custom + * malloc routines (i.e. USE_DL_PREFIX is defined) for libc.so */ +#if defined(USE_DL_PREFIX) && !defined(LIBC_STATIC) + if (pthread_once(&malloc_fini_once_ctl, malloc_fini_impl)) { + error_log("Unable to finalize malloc_debug component."); + } +#endif // USE_DL_PREFIX && !LIBC_STATIC +} diff --git a/libc/bionic/malloc_debug_common.h b/libc/bionic/malloc_debug_common.h index 87600d6..c78846b 100644 --- a/libc/bionic/malloc_debug_common.h +++ b/libc/bionic/malloc_debug_common.h @@ -82,15 +82,31 @@ struct MallocDebug { void* (*memalign)(size_t alignment, size_t bytes); }; -/* Malloc debugging initialization routine. - * This routine must be implemented in .so modules that implement malloc - * debugging. This routine is called once per process from malloc_init_impl - * routine implemented in bionic/libc/bionic/malloc_debug_common.c when malloc +/* Malloc debugging initialization and finalization routines. + * + * These routines must be implemented in .so modules that implement malloc + * debugging. The are is called once per process from malloc_init_impl and + * malloc_fini_impl respectively. + * + * They are implemented in bionic/libc/bionic/malloc_debug_common.c when malloc * debugging gets initialized for the process. - * Return: - * 0 on success, -1 on failure. + * + * MallocDebugInit returns: + * 0 on success, -1 on failure. */ typedef int (*MallocDebugInit)(void); +typedef void (*MallocDebugFini)(void); + +// ============================================================================= +// log functions +// ============================================================================= + +#define debug_log(format, ...) \ + __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak_check", (format), ##__VA_ARGS__ ) +#define error_log(format, ...) \ + __libc_android_log_print(ANDROID_LOG_ERROR, "malloc_leak_check", (format), ##__VA_ARGS__ ) +#define info_log(format, ...) \ + __libc_android_log_print(ANDROID_LOG_INFO, "malloc_leak_check", (format), ##__VA_ARGS__ ) #ifdef __cplusplus }; /* end of extern "C" */ diff --git a/libc/bionic/malloc_debug_leak.c b/libc/bionic/malloc_debug_leak.c index e584502..316d5fe 100644 --- a/libc/bionic/malloc_debug_leak.c +++ b/libc/bionic/malloc_debug_leak.c @@ -61,22 +61,11 @@ extern int gMallocLeakZygoteChild; extern pthread_mutex_t gAllocationsMutex; extern HashTable gHashTable; -extern const MallocDebug __libc_malloc_default_dispatch; -extern const MallocDebug* __libc_malloc_dispatch; // ============================================================================= -// log functions +// stack trace functions // ============================================================================= -#define debug_log(format, ...) \ - __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak_check", (format), ##__VA_ARGS__ ) -#define error_log(format, ...) \ - __libc_android_log_print(ANDROID_LOG_ERROR, "malloc_leak_check", (format), ##__VA_ARGS__ ) -#define info_log(format, ...) \ - __libc_android_log_print(ANDROID_LOG_INFO, "malloc_leak_check", (format), ##__VA_ARGS__ ) - -static int gTrapOnError = 1; - #define MALLOC_ALIGNMENT 8 #define GUARD 0x48151642 #define DEBUG 0 @@ -210,250 +199,12 @@ static void remove_entry(HashEntry* entry) gHashTable.count--; } - -// ============================================================================= -// stack trace functions -// ============================================================================= - -typedef struct -{ - size_t count; - intptr_t* addrs; -} stack_crawl_state_t; - - -/* depends how the system includes define this */ -#ifdef HAVE_UNWIND_CONTEXT_STRUCT -typedef struct _Unwind_Context __unwind_context; -#else -typedef _Unwind_Context __unwind_context; -#endif - -static _Unwind_Reason_Code trace_function(__unwind_context *context, void *arg) -{ - stack_crawl_state_t* state = (stack_crawl_state_t*)arg; - if (state->count) { - intptr_t ip = (intptr_t)_Unwind_GetIP(context); - if (ip) { - state->addrs[0] = ip; - state->addrs++; - state->count--; - return _URC_NO_REASON; - } - } - /* - * If we run out of space to record the address or 0 has been seen, stop - * unwinding the stack. - */ - return _URC_END_OF_STACK; -} - -static inline -int get_backtrace(intptr_t* addrs, size_t max_entries) -{ - stack_crawl_state_t state; - state.count = max_entries; - state.addrs = (intptr_t*)addrs; - _Unwind_Backtrace(trace_function, (void*)&state); - return max_entries - state.count; -} - // ============================================================================= -// malloc check functions +// malloc fill functions // ============================================================================= #define CHK_FILL_FREE 0xef #define CHK_SENTINEL_VALUE (char)0xeb -#define CHK_SENTINEL_HEAD_SIZE 16 -#define CHK_SENTINEL_TAIL_SIZE 16 -#define CHK_OVERHEAD_SIZE ( CHK_SENTINEL_HEAD_SIZE + \ - CHK_SENTINEL_TAIL_SIZE + \ - sizeof(size_t) ) - -static void dump_stack_trace() -{ - intptr_t addrs[20]; - int c = get_backtrace(addrs, 20); - char buf[16]; - char tmp[16*20]; - int i; - - tmp[0] = 0; // Need to initialize tmp[0] for the first strcat - for (i=0 ; i<c; i++) { - snprintf(buf, sizeof buf, "%2d: %08x\n", i, addrs[i]); - strlcat(tmp, buf, sizeof tmp); - } - __libc_android_log_print(ANDROID_LOG_ERROR, "libc", "call stack:\n%s", tmp); -} - -static int is_valid_malloc_pointer(void* addr) -{ - return 1; -} - -static void assert_log_message(const char* format, ...) -{ - va_list args; - - pthread_mutex_lock(&gAllocationsMutex); - { - const MallocDebug* current_dispatch = __libc_malloc_dispatch; - __libc_malloc_dispatch = &__libc_malloc_default_dispatch; - va_start(args, format); - __libc_android_log_vprint(ANDROID_LOG_ERROR, "libc", - format, args); - va_end(args); - dump_stack_trace(); - if (gTrapOnError) { - __builtin_trap(); - } - __libc_malloc_dispatch = current_dispatch; - } - pthread_mutex_unlock(&gAllocationsMutex); -} - -static void assert_valid_malloc_pointer(void* mem) -{ - if (mem && !is_valid_malloc_pointer(mem)) { - assert_log_message( - "*** MALLOC CHECK: buffer %p, is not a valid " - "malloc pointer (are you mixing up new/delete " - "and malloc/free?)", mem); - } -} - -/* Check that a given address corresponds to a guarded block, - * and returns its original allocation size in '*allocated'. - * 'func' is the capitalized name of the caller function. - * Returns 0 on success, or -1 on failure. - * NOTE: Does not return if gTrapOnError is set. - */ -static int chk_mem_check(void* mem, - size_t* allocated, - const char* func) -{ - char* buffer; - size_t offset, bytes; - int i; - char* buf; - - /* first check the bytes in the sentinel header */ - buf = (char*)mem - CHK_SENTINEL_HEAD_SIZE; - for (i=0 ; i<CHK_SENTINEL_HEAD_SIZE ; i++) { - if (buf[i] != CHK_SENTINEL_VALUE) { - assert_log_message( - "*** %s CHECK: buffer %p " - "corrupted %d bytes before allocation", - func, mem, CHK_SENTINEL_HEAD_SIZE-i); - return -1; - } - } - - /* then the ones in the sentinel trailer */ - buffer = (char*)mem - CHK_SENTINEL_HEAD_SIZE; - offset = dlmalloc_usable_size(buffer) - sizeof(size_t); - bytes = *(size_t *)(buffer + offset); - - buf = (char*)mem + bytes; - for (i=CHK_SENTINEL_TAIL_SIZE-1 ; i>=0 ; i--) { - if (buf[i] != CHK_SENTINEL_VALUE) { - assert_log_message( - "*** %s CHECK: buffer %p, size=%lu, " - "corrupted %d bytes after allocation", - func, buffer, bytes, i+1); - return -1; - } - } - - *allocated = bytes; - return 0; -} - - -void* chk_malloc(size_t bytes) -{ - size_t size = bytes + CHK_OVERHEAD_SIZE; - if (size < bytes) { // Overflow. - return NULL; - } - uint8_t* buffer = (uint8_t*) dlmalloc(size); - if (buffer) { - memset(buffer, CHK_SENTINEL_VALUE, bytes + CHK_OVERHEAD_SIZE); - size_t offset = dlmalloc_usable_size(buffer) - sizeof(size_t); - *(size_t *)(buffer + offset) = bytes; - buffer += CHK_SENTINEL_HEAD_SIZE; - } - return buffer; -} - -void chk_free(void* mem) -{ - assert_valid_malloc_pointer(mem); - if (mem) { - size_t size; - char* buffer; - - if (chk_mem_check(mem, &size, "FREE") == 0) { - buffer = (char*)mem - CHK_SENTINEL_HEAD_SIZE; - memset(buffer, CHK_FILL_FREE, size + CHK_OVERHEAD_SIZE); - dlfree(buffer); - } - } -} - -void* chk_calloc(size_t n_elements, size_t elem_size) -{ - size_t size; - void* ptr; - - /* Fail on overflow - just to be safe even though this code runs only - * within the debugging C library, not the production one */ - if (n_elements && MAX_SIZE_T / n_elements < elem_size) { - return NULL; - } - size = n_elements * elem_size; - ptr = chk_malloc(size); - if (ptr != NULL) { - memset(ptr, 0, size); - } - return ptr; -} - -void* chk_realloc(void* mem, size_t bytes) -{ - char* buffer; - int ret; - size_t old_bytes = 0; - - assert_valid_malloc_pointer(mem); - - if (mem != NULL && chk_mem_check(mem, &old_bytes, "REALLOC") < 0) - return NULL; - - char* new_buffer = chk_malloc(bytes); - if (mem == NULL) { - return new_buffer; - } - - if (new_buffer) { - if (bytes > old_bytes) - bytes = old_bytes; - memcpy(new_buffer, mem, bytes); - chk_free(mem); - } - - return new_buffer; -} - -void* chk_memalign(size_t alignment, size_t bytes) -{ - // XXX: it's better to use malloc, than being wrong - return chk_malloc(bytes); -} - -// ============================================================================= -// malloc fill functions -// ============================================================================= void* fill_malloc(size_t bytes) { @@ -501,6 +252,9 @@ void* fill_memalign(size_t alignment, size_t bytes) #define MEMALIGN_GUARD ((void*)0xA1A41520) +extern __LIBC_HIDDEN__ +int get_backtrace(intptr_t* addrs, size_t max_entries); + void* leak_malloc(size_t bytes) { // allocate enough space infront of the allocation to store the pointer for @@ -645,12 +399,3 @@ void* leak_memalign(size_t alignment, size_t bytes) } return base; } - -/* Initializes malloc debugging framework. - * See comments on MallocDebugInit in malloc_debug_common.h - */ -int malloc_debug_initialize(void) -{ - // We don't really have anything that requires initialization here. - return 0; -} diff --git a/libc/bionic/malloc_debug_qemu.c b/libc/bionic/malloc_debug_qemu.c index 4b694e9..bcbf1e6 100644 --- a/libc/bionic/malloc_debug_qemu.c +++ b/libc/bionic/malloc_debug_qemu.c @@ -287,7 +287,7 @@ static void dump_malloc_descriptor(char* str, /* * Logging helper macros. */ -#define debug_log(format, ...) \ +#define qemu_debug_log(format, ...) \ do { \ __libc_android_log_print(ANDROID_LOG_DEBUG, "memcheck", \ (format), ##__VA_ARGS__ ); \ @@ -296,7 +296,7 @@ static void dump_malloc_descriptor(char* str, } \ } while (0) -#define error_log(format, ...) \ +#define qemu_error_log(format, ...) \ do { \ __libc_android_log_print(ANDROID_LOG_ERROR, "memcheck", \ (format), ##__VA_ARGS__ ); \ @@ -305,7 +305,7 @@ static void dump_malloc_descriptor(char* str, } \ } while (0) -#define info_log(format, ...) \ +#define qemu_info_log(format, ...) \ do { \ __libc_android_log_print(ANDROID_LOG_INFO, "memcheck", \ (format), ##__VA_ARGS__ ); \ @@ -692,7 +692,7 @@ memcheck_initialize(int alignment, const char* memcheck_param) notify_qemu_libc_initialized(malloc_pid); - debug_log("Instrumented for pid=%03u: malloc=%p, free=%p, calloc=%p, realloc=%p, memalign=%p", + qemu_debug_log("Instrumented for pid=%03u: malloc=%p, free=%p, calloc=%p, realloc=%p, memalign=%p", malloc_pid, qemu_instrumented_malloc, qemu_instrumented_free, qemu_instrumented_calloc, qemu_instrumented_realloc, qemu_instrumented_memalign); @@ -717,7 +717,7 @@ qemu_instrumented_malloc(size_t bytes) desc.suffix_size = DEFAULT_SUFFIX_SIZE; desc.ptr = dlmalloc(mallocdesc_alloc_size(&desc)); if (desc.ptr == NULL) { - error_log("<libc_pid=%03u, pid=%03u> malloc(%u): dlmalloc(%u) failed.", + qemu_error_log("<libc_pid=%03u, pid=%03u> malloc(%u): dlmalloc(%u) failed.", malloc_pid, getpid(), bytes, mallocdesc_alloc_size(&desc)); return NULL; } @@ -797,7 +797,7 @@ qemu_instrumented_calloc(size_t n_elements, size_t elem_size) if (n_elements == 0 || elem_size == 0) { // Just let go zero bytes allocation. - info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc", + qemu_info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc", malloc_pid, getpid()); return qemu_instrumented_malloc(0); } @@ -874,14 +874,14 @@ qemu_instrumented_realloc(void* mem, size_t bytes) if (mem == NULL) { // Nothing to realloc. just do regular malloc. - info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to malloc", + qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to malloc", malloc_pid, getpid(), mem, bytes); return qemu_instrumented_malloc(bytes); } if (bytes == 0) { // This is a "free" condition. - info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to free and malloc", + qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to free and malloc", malloc_pid, getpid(), mem, bytes); qemu_instrumented_free(mem); @@ -977,7 +977,7 @@ qemu_instrumented_memalign(size_t alignment, size_t bytes) if (bytes == 0) { // Just let go zero bytes allocation. - info_log("::: <libc_pid=%03u, pid=%03u>: memalign(%X, %u) redir to malloc", + qemu_info_log("::: <libc_pid=%03u, pid=%03u>: memalign(%X, %u) redir to malloc", malloc_pid, getpid(), alignment, bytes); return qemu_instrumented_malloc(0); } diff --git a/libc/bionic/malloc_debug_stacktrace.c b/libc/bionic/malloc_debug_stacktrace.c new file mode 100644 index 0000000..c71b1c5 --- /dev/null +++ b/libc/bionic/malloc_debug_stacktrace.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <unwind.h> +#include <sys/types.h> + +// ============================================================================= +// stack trace functions +// ============================================================================= + +typedef struct +{ + size_t count; + intptr_t* addrs; +} stack_crawl_state_t; + + +/* depends how the system includes define this */ +#ifdef HAVE_UNWIND_CONTEXT_STRUCT +typedef struct _Unwind_Context __unwind_context; +#else +typedef _Unwind_Context __unwind_context; +#endif + +static _Unwind_Reason_Code trace_function(__unwind_context *context, void *arg) +{ + stack_crawl_state_t* state = (stack_crawl_state_t*)arg; + if (state->count) { + intptr_t ip = (intptr_t)_Unwind_GetIP(context); + if (ip) { + state->addrs[0] = ip; + state->addrs++; + state->count--; + return _URC_NO_REASON; + } + } + /* + * If we run out of space to record the address or 0 has been seen, stop + * unwinding the stack. + */ + return _URC_END_OF_STACK; +} + +__LIBC_HIDDEN__ +int get_backtrace(intptr_t* addrs, size_t max_entries) +{ + stack_crawl_state_t state; + state.count = max_entries; + state.addrs = (intptr_t*)addrs; + _Unwind_Backtrace(trace_function, (void*)&state); + return max_entries - state.count; +} |