* commit '252a5c854a08e89fc7337ea679220161fe4ea98f': bionic: import heaptracker as chk_malloc
This commit is contained in:
commit
31431f454a
@ -725,7 +725,10 @@ LOCAL_CFLAGS := \
|
|||||||
LOCAL_C_INCLUDES := $(libc_common_c_includes)
|
LOCAL_C_INCLUDES := $(libc_common_c_includes)
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
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
|
LOCAL_MODULE:= libc_malloc_debug_leak
|
||||||
|
|
||||||
|
@ -138,4 +138,12 @@ void __libc_fini(void* array)
|
|||||||
|
|
||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef LIBC_STATIC
|
||||||
|
{
|
||||||
|
extern void __libc_postfini(void) __attribute__((weak));
|
||||||
|
if (__libc_postfini)
|
||||||
|
__libc_postfini();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,12 @@ void __libc_preinit(void)
|
|||||||
malloc_debug_init();
|
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
|
/* This function is called from the executable's _start entry point
|
||||||
* (see arch-$ARCH/bionic/crtbegin_dynamic.S), which is itself
|
* (see arch-$ARCH/bionic/crtbegin_dynamic.S), which is itself
|
||||||
* called by the dynamic linker after it has loaded all shared
|
* called by the dynamic linker after it has loaded all shared
|
||||||
|
574
libc/bionic/malloc_debug_check.c
Normal file
574
libc/bionic/malloc_debug_check.c
Normal file
@ -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);
|
||||||
|
}
|
126
libc/bionic/malloc_debug_check_mapinfo.c
Normal file
126
libc/bionic/malloc_debug_check_mapinfo.c
Normal file
@ -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;
|
||||||
|
}
|
46
libc/bionic/malloc_debug_check_mapinfo.h
Normal file
46
libc/bionic/malloc_debug_check_mapinfo.h
Normal file
@ -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*/
|
@ -240,17 +240,6 @@ void* memalign(size_t alignment, size_t bytes) {
|
|||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include "logd.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. */
|
/* Table for dispatching malloc calls, depending on environment. */
|
||||||
static MallocDebug gMallocUse __attribute__((aligned(32))) = {
|
static MallocDebug gMallocUse __attribute__((aligned(32))) = {
|
||||||
dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign
|
dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign
|
||||||
@ -288,6 +277,14 @@ static void* libc_malloc_impl_handle = NULL;
|
|||||||
#define MALLOC_ALIGNMENT ((size_t)8U)
|
#define MALLOC_ALIGNMENT ((size_t)8U)
|
||||||
#endif /* MALLOC_ALIGNMENT */
|
#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. */
|
/* Initializes memory allocation framework once per process. */
|
||||||
static void malloc_init_impl(void)
|
static void malloc_init_impl(void)
|
||||||
{
|
{
|
||||||
@ -339,9 +336,17 @@ static void malloc_init_impl(void)
|
|||||||
switch (debug_level) {
|
switch (debug_level) {
|
||||||
case 1:
|
case 1:
|
||||||
case 5:
|
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";
|
so_name = "/system/lib/libc_malloc_debug_leak.so";
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 20:
|
case 20:
|
||||||
// Quick check: debug level 20 can only be handled in emulator.
|
// Quick check: debug level 20 can only be handled in emulator.
|
||||||
if (!qemu_running) {
|
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_init_once_ctl = PTHREAD_ONCE_INIT;
|
||||||
|
static pthread_once_t malloc_fini_once_ctl = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
#endif // !LIBC_STATIC
|
#endif // !LIBC_STATIC
|
||||||
#endif // USE_DL_PREFIX
|
#endif // USE_DL_PREFIX
|
||||||
@ -504,3 +521,14 @@ void malloc_debug_init(void)
|
|||||||
}
|
}
|
||||||
#endif // USE_DL_PREFIX && !LIBC_STATIC
|
#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
|
||||||
|
}
|
||||||
|
@ -82,15 +82,31 @@ struct MallocDebug {
|
|||||||
void* (*memalign)(size_t alignment, size_t bytes);
|
void* (*memalign)(size_t alignment, size_t bytes);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Malloc debugging initialization routine.
|
/* Malloc debugging initialization and finalization routines.
|
||||||
* This routine must be implemented in .so modules that implement malloc
|
*
|
||||||
* debugging. This routine is called once per process from malloc_init_impl
|
* These routines must be implemented in .so modules that implement malloc
|
||||||
* routine implemented in bionic/libc/bionic/malloc_debug_common.c when 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.
|
* 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 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
|
#ifdef __cplusplus
|
||||||
}; /* end of extern "C" */
|
}; /* end of extern "C" */
|
||||||
|
@ -61,22 +61,11 @@
|
|||||||
extern int gMallocLeakZygoteChild;
|
extern int gMallocLeakZygoteChild;
|
||||||
extern pthread_mutex_t gAllocationsMutex;
|
extern pthread_mutex_t gAllocationsMutex;
|
||||||
extern HashTable gHashTable;
|
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 MALLOC_ALIGNMENT 8
|
||||||
#define GUARD 0x48151642
|
#define GUARD 0x48151642
|
||||||
#define DEBUG 0
|
#define DEBUG 0
|
||||||
@ -210,250 +199,12 @@ static void remove_entry(HashEntry* entry)
|
|||||||
gHashTable.count--;
|
gHashTable.count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// stack trace functions
|
// malloc fill 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
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
#define CHK_FILL_FREE 0xef
|
#define CHK_FILL_FREE 0xef
|
||||||
#define CHK_SENTINEL_VALUE (char)0xeb
|
#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)
|
void* fill_malloc(size_t bytes)
|
||||||
{
|
{
|
||||||
@ -501,6 +252,9 @@ void* fill_memalign(size_t alignment, size_t bytes)
|
|||||||
|
|
||||||
#define MEMALIGN_GUARD ((void*)0xA1A41520)
|
#define MEMALIGN_GUARD ((void*)0xA1A41520)
|
||||||
|
|
||||||
|
extern __LIBC_HIDDEN__
|
||||||
|
int get_backtrace(intptr_t* addrs, size_t max_entries);
|
||||||
|
|
||||||
void* leak_malloc(size_t bytes)
|
void* leak_malloc(size_t bytes)
|
||||||
{
|
{
|
||||||
// allocate enough space infront of the allocation to store the pointer for
|
// 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;
|
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;
|
|
||||||
}
|
|
||||||
|
@ -287,7 +287,7 @@ static void dump_malloc_descriptor(char* str,
|
|||||||
/*
|
/*
|
||||||
* Logging helper macros.
|
* Logging helper macros.
|
||||||
*/
|
*/
|
||||||
#define debug_log(format, ...) \
|
#define qemu_debug_log(format, ...) \
|
||||||
do { \
|
do { \
|
||||||
__libc_android_log_print(ANDROID_LOG_DEBUG, "memcheck", \
|
__libc_android_log_print(ANDROID_LOG_DEBUG, "memcheck", \
|
||||||
(format), ##__VA_ARGS__ ); \
|
(format), ##__VA_ARGS__ ); \
|
||||||
@ -296,7 +296,7 @@ static void dump_malloc_descriptor(char* str,
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define error_log(format, ...) \
|
#define qemu_error_log(format, ...) \
|
||||||
do { \
|
do { \
|
||||||
__libc_android_log_print(ANDROID_LOG_ERROR, "memcheck", \
|
__libc_android_log_print(ANDROID_LOG_ERROR, "memcheck", \
|
||||||
(format), ##__VA_ARGS__ ); \
|
(format), ##__VA_ARGS__ ); \
|
||||||
@ -305,7 +305,7 @@ static void dump_malloc_descriptor(char* str,
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define info_log(format, ...) \
|
#define qemu_info_log(format, ...) \
|
||||||
do { \
|
do { \
|
||||||
__libc_android_log_print(ANDROID_LOG_INFO, "memcheck", \
|
__libc_android_log_print(ANDROID_LOG_INFO, "memcheck", \
|
||||||
(format), ##__VA_ARGS__ ); \
|
(format), ##__VA_ARGS__ ); \
|
||||||
@ -692,7 +692,7 @@ memcheck_initialize(int alignment, const char* memcheck_param)
|
|||||||
|
|
||||||
notify_qemu_libc_initialized(malloc_pid);
|
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,
|
malloc_pid, qemu_instrumented_malloc, qemu_instrumented_free,
|
||||||
qemu_instrumented_calloc, qemu_instrumented_realloc,
|
qemu_instrumented_calloc, qemu_instrumented_realloc,
|
||||||
qemu_instrumented_memalign);
|
qemu_instrumented_memalign);
|
||||||
@ -717,7 +717,7 @@ qemu_instrumented_malloc(size_t bytes)
|
|||||||
desc.suffix_size = DEFAULT_SUFFIX_SIZE;
|
desc.suffix_size = DEFAULT_SUFFIX_SIZE;
|
||||||
desc.ptr = dlmalloc(mallocdesc_alloc_size(&desc));
|
desc.ptr = dlmalloc(mallocdesc_alloc_size(&desc));
|
||||||
if (desc.ptr == NULL) {
|
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));
|
malloc_pid, getpid(), bytes, mallocdesc_alloc_size(&desc));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -797,7 +797,7 @@ qemu_instrumented_calloc(size_t n_elements, size_t elem_size)
|
|||||||
|
|
||||||
if (n_elements == 0 || elem_size == 0) {
|
if (n_elements == 0 || elem_size == 0) {
|
||||||
// Just let go zero bytes allocation.
|
// 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());
|
malloc_pid, getpid());
|
||||||
return qemu_instrumented_malloc(0);
|
return qemu_instrumented_malloc(0);
|
||||||
}
|
}
|
||||||
@ -874,14 +874,14 @@ qemu_instrumented_realloc(void* mem, size_t bytes)
|
|||||||
|
|
||||||
if (mem == NULL) {
|
if (mem == NULL) {
|
||||||
// Nothing to realloc. just do regular malloc.
|
// 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);
|
malloc_pid, getpid(), mem, bytes);
|
||||||
return qemu_instrumented_malloc(bytes);
|
return qemu_instrumented_malloc(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytes == 0) {
|
if (bytes == 0) {
|
||||||
// This is a "free" condition.
|
// 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);
|
malloc_pid, getpid(), mem, bytes);
|
||||||
qemu_instrumented_free(mem);
|
qemu_instrumented_free(mem);
|
||||||
|
|
||||||
@ -977,7 +977,7 @@ qemu_instrumented_memalign(size_t alignment, size_t bytes)
|
|||||||
|
|
||||||
if (bytes == 0) {
|
if (bytes == 0) {
|
||||||
// Just let go zero bytes allocation.
|
// 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);
|
malloc_pid, getpid(), alignment, bytes);
|
||||||
return qemu_instrumented_malloc(0);
|
return qemu_instrumented_malloc(0);
|
||||||
}
|
}
|
||||||
|
76
libc/bionic/malloc_debug_stacktrace.c
Normal file
76
libc/bionic/malloc_debug_stacktrace.c
Normal file
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user