diff options
author | Elliott Hughes <enh@google.com> | 2013-01-22 17:44:15 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2013-01-22 17:44:15 +0000 |
commit | ca483765bd0dc16294b9e67dd0de5c6d53b1bfa3 (patch) | |
tree | f3676bbac701bc026573ee833e8e9cb50478c445 /libc | |
parent | 5496bbf6a3592fd99cee6b8c20c8624c2aeea0c1 (diff) | |
parent | 1e980b6bc8315d00a07312b25486531247abd98c (diff) | |
download | bionic-ca483765bd0dc16294b9e67dd0de5c6d53b1bfa3.zip bionic-ca483765bd0dc16294b9e67dd0de5c6d53b1bfa3.tar.gz bionic-ca483765bd0dc16294b9e67dd0de5c6d53b1bfa3.tar.bz2 |
Merge "Fix the duplication in the debugging code."
Diffstat (limited to 'libc')
21 files changed, 1039 insertions, 492 deletions
diff --git a/libc/Android.mk b/libc/Android.mk index c98d2ff..05373dc 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -272,6 +272,7 @@ libc_common_src_files := \ libc_bionic_src_files := \ bionic/assert.cpp \ + bionic/debug_format.cpp \ bionic/dirent.cpp \ bionic/eventfd.cpp \ bionic/__fgets_chk.cpp \ @@ -881,7 +882,7 @@ LOCAL_SRC_FILES := \ $(libc_static_common_src_files) \ bionic/dlmalloc.c \ bionic/malloc_debug_common.cpp \ - bionic/pthread_debug.c \ + bionic/pthread_debug.cpp \ bionic/libc_init_dynamic.c ifeq ($(TARGET_ARCH),arm) @@ -934,10 +935,10 @@ LOCAL_CFLAGS := \ LOCAL_C_INCLUDES := $(libc_common_c_includes) LOCAL_SRC_FILES := \ + bionic/debug_mapinfo.cpp \ + bionic/debug_stacktrace.cpp \ bionic/malloc_debug_leak.cpp \ bionic/malloc_debug_check.cpp \ - bionic/malloc_debug_check_mapinfo.cpp \ - bionic/malloc_debug_stacktrace.cpp LOCAL_MODULE:= libc_malloc_debug_leak LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk diff --git a/libc/arch-arm/bionic/atexit_legacy.c b/libc/arch-arm/bionic/atexit_legacy.c index 4abe839..6e299ac 100644 --- a/libc/arch-arm/bionic/atexit_legacy.c +++ b/libc/arch-arm/bionic/atexit_legacy.c @@ -50,10 +50,9 @@ atexit(void (*func)(void)) * calling library may have been dlclose()'d, causing the program to * crash. */ - static char const warning[] = - "WARNING: generic atexit() called from legacy shared library\n"; + static char const warning[] = "WARNING: generic atexit() called from legacy shared library\n"; - __libc_android_log_print(ANDROID_LOG_WARN, "libc", warning); + __libc_android_log_write(ANDROID_LOG_WARN, "libc", warning); fprintf(stderr, warning); return (__cxa_atexit((void (*)(void *))func, NULL, NULL)); diff --git a/libc/arch-mips/bionic/cacheflush.c b/libc/arch-mips/bionic/cacheflush.c index 05085b6..1911687 100644 --- a/libc/arch-mips/bionic/cacheflush.c +++ b/libc/arch-mips/bionic/cacheflush.c @@ -29,9 +29,9 @@ #include <sys/cachectl.h> #ifdef DEBUG -#include <logd.h> -#define XLOG(...) \ - __libc_android_log_print(ANDROID_LOG_DEBUG,"libc-cacheflush",__VA_ARGS__) +#include <private/logd.h> +#include <private/debug_format.h> +#define XLOG(...) __libc_format_log(ANDROID_LOG_DEBUG,"libc-cacheflush",__VA_ARGS__) #endif /* diff --git a/libc/bionic/assert.cpp b/libc/bionic/assert.cpp index 7c0a860..e38c16d 100644 --- a/libc/bionic/assert.cpp +++ b/libc/bionic/assert.cpp @@ -32,6 +32,7 @@ #include <assert.h> #include <stdio.h> #include <stdlib.h> +#include <private/debug_format.h> #include <private/logd.h> // We log to stderr for the benefit of "adb shell" users, and the log for the benefit @@ -39,7 +40,7 @@ void __assert(const char* file, int line, const char* failed_expression) { const char* fmt = "%s:%d: assertion \"%s\" failed\n"; - __libc_android_log_print(ANDROID_LOG_FATAL, "libc", fmt, file, line, failed_expression); + __libc_format_log(ANDROID_LOG_FATAL, "libc", fmt, file, line, failed_expression); fprintf(stderr, fmt, file, line, failed_expression); abort(); /* NOTREACHED */ @@ -47,7 +48,7 @@ void __assert(const char* file, int line, const char* failed_expression) { void __assert2(const char* file, int line, const char* function, const char* failed_expression) { const char* fmt = "%s:%d: %s: assertion \"%s\" failed\n"; - __libc_android_log_print(ANDROID_LOG_FATAL, "libc", fmt, file, line, function, failed_expression); + __libc_format_log(ANDROID_LOG_FATAL, "libc", fmt, file, line, function, failed_expression); fprintf(stderr, fmt, file, line, function, failed_expression); abort(); /* NOTREACHED */ diff --git a/libc/bionic/debug_format.cpp b/libc/bionic/debug_format.cpp new file mode 100644 index 0000000..e8d6a45 --- /dev/null +++ b/libc/bionic/debug_format.cpp @@ -0,0 +1,634 @@ +/* + * Copyright (C) 2010 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. + */ + +// Temporarily disable _FORTIFY_SOURCE to get this code to +// compile under GCC 4.7 +#undef _FORTIFY_SOURCE + +#include "debug_format.h" + +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +/* define UNIT_TESTS to build this file as a single executable that runs + * the formatter's unit tests + */ +#define xxUNIT_TESTS + +/*** Generic output sink + ***/ + +struct Out { + void *opaque; + void (*send)(void *opaque, const char *data, int len); +}; + +static void out_send(Out *o, const char *data, size_t len) { + o->send(o->opaque, data, (int)len); +} + +static void +out_send_repeat(Out *o, char ch, int count) +{ + char pad[8]; + const int padSize = (int)sizeof(pad); + + memset(pad, ch, sizeof(pad)); + while (count > 0) { + int avail = count; + if (avail > padSize) { + avail = padSize; + } + o->send(o->opaque, pad, avail); + count -= avail; + } +} + +/* forward declaration */ +static void out_vformat(Out* o, const char* format, va_list args); + +/*** Bounded buffer output + ***/ + +struct BufOut { + Out out[1]; + char *buffer; + char *pos; + char *end; + int total; +}; + +static void buf_out_send(void *opaque, const char *data, int len) { + BufOut *bo = reinterpret_cast<BufOut*>(opaque); + + if (len < 0) { + len = strlen(data); + } + + bo->total += len; + + while (len > 0) { + int avail = bo->end - bo->pos; + if (avail == 0) + break; + if (avail > len) + avail = len; + memcpy(bo->pos, data, avail); + bo->pos += avail; + bo->pos[0] = '\0'; + len -= avail; + } +} + +static Out* +buf_out_init(BufOut *bo, char *buffer, size_t size) +{ + if (size == 0) + return NULL; + + bo->out->opaque = bo; + bo->out->send = buf_out_send; + bo->buffer = buffer; + bo->end = buffer + size - 1; + bo->pos = bo->buffer; + bo->pos[0] = '\0'; + bo->total = 0; + + return bo->out; +} + +static int +buf_out_length(BufOut *bo) +{ + return bo->total; +} + +static int +vformat_buffer(char *buff, size_t buffsize, const char *format, va_list args) +{ + BufOut bo; + Out *out; + + out = buf_out_init(&bo, buff, buffsize); + if (out == NULL) + return 0; + + out_vformat(out, format, args); + + return buf_out_length(&bo); +} + +int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) { + va_list args; + va_start(args, format); + int result = vformat_buffer(buffer, buffer_size, format, args); + va_end(args); + return result; +} + + +/*** File descriptor output + ***/ + +struct FdOut { + Out out[1]; + int fd; + int total; +}; + +static void +fd_out_send(void *opaque, const char *data, int len) +{ + FdOut *fdo = reinterpret_cast<FdOut*>(opaque); + + if (len < 0) + len = strlen(data); + + while (len > 0) { + int ret = write(fdo->fd, data, len); + if (ret < 0) { + if (errno == EINTR) + continue; + break; + } + data += ret; + len -= ret; + fdo->total += ret; + } +} + +static Out* +fd_out_init(FdOut *fdo, int fd) +{ + fdo->out->opaque = fdo; + fdo->out->send = fd_out_send; + fdo->fd = fd; + fdo->total = 0; + + return fdo->out; +} + +static int +fd_out_length(FdOut *fdo) +{ + return fdo->total; +} + + +int __libc_format_fd(int fd, const char* format, ...) { + FdOut fdo; + Out* out = fd_out_init(&fdo, fd); + if (out == NULL) { + return 0; + } + + va_list args; + va_start(args, format); + out_vformat(out, format, args); + va_end(args); + + return fd_out_length(&fdo); +} + +/*** Log output + ***/ + +#include <unistd.h> +#include <fcntl.h> +#include <sys/uio.h> + +int __libc_format_log_va_list(int priority, const char* tag, const char* fmt, va_list args) { + char buf[1024]; + int result = vformat_buffer(buf, sizeof buf, fmt, args); + + static int log_fd = -1; + if (log_fd == -1) { + log_fd = open("/dev/log/main", O_WRONLY); + if (log_fd == -1) { + return result; + } + } + + struct iovec vec[3]; + vec[0].iov_base = (unsigned char *) &priority; + vec[0].iov_len = 1; + vec[1].iov_base = (void *) tag; + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = (void *) buf; + vec[2].iov_len = strlen(buf) + 1; + + TEMP_FAILURE_RETRY(writev(log_fd, vec, 3)); + + return result; +} + +int __libc_format_log(int priority, const char* tag, const char* format, ...) { + va_list args; + va_start(args, format); + int result = __libc_format_log_va_list(priority, tag, format, args); + va_end(args); + return result; +} + +/*** formatted output implementation + ***/ + +/* Parse a decimal string from 'format + *ppos', + * return the value, and writes the new position past + * the decimal string in '*ppos' on exit. + * + * NOTE: Does *not* handle a sign prefix. + */ +static unsigned +parse_decimal(const char *format, int *ppos) +{ + const char* p = format + *ppos; + unsigned result = 0; + + for (;;) { + int ch = *p; + unsigned d = (unsigned)(ch - '0'); + + if (d >= 10U) + break; + + result = result*10 + d; + p++; + } + *ppos = p - format; + return result; +} + +// Writes number 'value' in base 'base' into buffer 'buf' of size 'buf_size' bytes. +// Assumes that buf_size > 0. +static void format_number(char* buf, size_t buf_size, uint64_t value, int base, bool caps) { + char* p = buf; + char* end = buf + buf_size - 1; + + // Generate digit string in reverse order. + while (value) { + unsigned d = value % base; + value /= base; + if (p != end) { + char ch; + if (d < 10) { + ch = '0' + d; + } else { + ch = (caps ? 'A' : 'a') + (d - 10); + } + *p++ = ch; + } + } + + // Special case for 0. + if (p == buf) { + if (p != end) { + *p++ = '0'; + } + } + *p = '\0'; + + // Reverse digit string in-place. + size_t length = p - buf; + for (size_t i = 0, j = length - 1; i < j; ++i, --j) { + char ch = buf[i]; + buf[i] = buf[j]; + buf[j] = ch; + } +} + +/* Write an integer (octal or decimal) into a buffer, assumes buffsize > 2 */ +static void +format_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSigned) +{ + // TODO: this is incorrect for MIN_INT. + if (isSigned && (int64_t)value < 0) { + buffer[0] = '-'; + buffer += 1; + buffsize -= 1; + value = (uint64_t)(-(int64_t)value); + } + + format_number(buffer, buffsize, value, base, false); +} + +// Assumes buf_size > 2. +static void format_hex(char* buf, size_t buf_size, uint64_t value, bool caps) { + format_number(buf, buf_size, value, 16, caps); +} + + +/* Perform formatted output to an output target 'o' */ +static void +out_vformat(Out *o, const char *format, va_list args) +{ + int nn = 0; + + for (;;) { + int mm; + int padZero = 0; + int padLeft = 0; + char sign = '\0'; + int width = -1; + int prec = -1; + size_t bytelen = sizeof(int); + const char* str; + int slen; + char buffer[32]; /* temporary buffer used to format numbers */ + + char c; + + /* first, find all characters that are not 0 or '%' */ + /* then send them to the output directly */ + mm = nn; + do { + c = format[mm]; + if (c == '\0' || c == '%') + break; + mm++; + } while (1); + + if (mm > nn) { + out_send(o, format+nn, mm-nn); + nn = mm; + } + + /* is this it ? then exit */ + if (c == '\0') + break; + + /* nope, we are at a '%' modifier */ + nn++; // skip it + + /* parse flags */ + for (;;) { + c = format[nn++]; + if (c == '\0') { /* single trailing '%' ? */ + c = '%'; + out_send(o, &c, 1); + return; + } + else if (c == '0') { + padZero = 1; + continue; + } + else if (c == '-') { + padLeft = 1; + continue; + } + else if (c == ' ' || c == '+') { + sign = c; + continue; + } + break; + } + + /* parse field width */ + if ((c >= '0' && c <= '9')) { + nn --; + width = (int)parse_decimal(format, &nn); + c = format[nn++]; + } + + /* parse precision */ + if (c == '.') { + prec = (int)parse_decimal(format, &nn); + c = format[nn++]; + } + + /* length modifier */ + switch (c) { + case 'h': + bytelen = sizeof(short); + if (format[nn] == 'h') { + bytelen = sizeof(char); + nn += 1; + } + c = format[nn++]; + break; + case 'l': + bytelen = sizeof(long); + if (format[nn] == 'l') { + bytelen = sizeof(long long); + nn += 1; + } + c = format[nn++]; + break; + case 'z': + bytelen = sizeof(size_t); + c = format[nn++]; + break; + case 't': + bytelen = sizeof(ptrdiff_t); + c = format[nn++]; + break; + default: + ; + } + + /* conversion specifier */ + if (c == 's') { + /* string */ + str = va_arg(args, const char*); + } else if (c == 'c') { + /* character */ + /* NOTE: char is promoted to int when passed through the stack */ + buffer[0] = (char) va_arg(args, int); + buffer[1] = '\0'; + str = buffer; + } else if (c == 'p') { + uint64_t value = (uintptr_t) va_arg(args, void*); + buffer[0] = '0'; + buffer[1] = 'x'; + format_hex(buffer + 2, sizeof buffer-2, value, false); + str = buffer; + } else { + /* integers - first read value from stack */ + uint64_t value; + int isSigned = (c == 'd' || c == 'i' || c == 'o'); + + /* NOTE: int8_t and int16_t are promoted to int when passed + * through the stack + */ + switch (bytelen) { + case 1: value = (uint8_t) va_arg(args, int); break; + case 2: value = (uint16_t) va_arg(args, int); break; + case 4: value = va_arg(args, uint32_t); break; + case 8: value = va_arg(args, uint64_t); break; + default: return; /* should not happen */ + } + + /* sign extension, if needed */ + if (isSigned) { + int shift = 64 - 8*bytelen; + value = (uint64_t)(((int64_t)(value << shift)) >> shift); + } + + /* format the number properly into our buffer */ + switch (c) { + case 'i': case 'd': + format_integer(buffer, sizeof buffer, value, 10, isSigned); + break; + case 'o': + format_integer(buffer, sizeof buffer, value, 8, isSigned); + break; + case 'x': case 'X': + format_hex(buffer, sizeof buffer, value, (c == 'X')); + break; + default: + buffer[0] = '\0'; + } + /* then point to it */ + str = buffer; + } + + /* if we are here, 'str' points to the content that must be + * outputted. handle padding and alignment now */ + + slen = strlen(str); + + if (sign != '\0' || prec != -1) { + __assert(__FILE__, __LINE__, "sign/precision unsupported"); + } + + if (slen < width && !padLeft) { + char padChar = padZero ? '0' : ' '; + out_send_repeat(o, padChar, width - slen); + } + + out_send(o, str, slen); + + if (slen < width && padLeft) { + char padChar = padZero ? '0' : ' '; + out_send_repeat(o, padChar, width - slen); + } + } +} + + +#ifdef UNIT_TESTS + +#include <stdio.h> + +static int gFails = 0; + +#define MARGIN 40 + +#define UTEST_CHECK(condition,message) \ + printf("Checking %-*s: ", MARGIN, message); fflush(stdout); \ + if (!(condition)) { \ + printf("KO\n"); \ + gFails += 1; \ + } else { \ + printf("ok\n"); \ + } + +static void +utest_BufOut(void) +{ + char buffer[16]; + BufOut bo[1]; + Out* out; + int ret; + + buffer[0] = '1'; + out = buf_out_init(bo, buffer, sizeof buffer); + UTEST_CHECK(buffer[0] == '\0', "buf_out_init clears initial byte"); + out_send(out, "abc", 3); + UTEST_CHECK(!memcmp(buffer, "abc", 4), "out_send() works with BufOut"); + out_send_repeat(out, 'X', 4); + UTEST_CHECK(!memcmp(buffer, "abcXXXX", 8), "out_send_repeat() works with BufOut"); + buffer[sizeof buffer-1] = 'x'; + out_send_repeat(out, 'Y', 2*sizeof(buffer)); + UTEST_CHECK(buffer[sizeof buffer-1] == '\0', "overflows always zero-terminates"); + + out = buf_out_init(bo, buffer, sizeof buffer); + out_send_repeat(out, 'X', 2*sizeof(buffer)); + ret = buf_out_length(bo); + UTEST_CHECK(ret == 2*sizeof(buffer), "correct size returned on overflow"); +} + +static void +utest_expect(const char* result, const char* format, ...) +{ + va_list args; + BufOut bo[1]; + char buffer[256]; + Out* out = buf_out_init(bo, buffer, sizeof buffer); + + printf("Checking %-*s: ", MARGIN, format); fflush(stdout); + va_start(args, format); + out_vformat(out, format, args); + va_end(args); + + if (strcmp(result, buffer)) { + printf("KO. got '%s' expecting '%s'\n", buffer, result); + gFails += 1; + } else { + printf("ok. got '%s'\n", result); + } +} + +int main(void) +{ + utest_BufOut(); + utest_expect("", ""); + utest_expect("a", "a"); + utest_expect("01234", "01234", ""); + utest_expect("01234", "%s", "01234"); + utest_expect("aabbcc", "aa%scc", "bb"); + utest_expect("a", "%c", 'a'); + utest_expect("1234", "%d", 1234); + utest_expect("-8123", "%d", -8123); + utest_expect("16", "%hd", 0x7fff0010); + utest_expect("16", "%hhd", 0x7fffff10); + utest_expect("68719476736", "%lld", 0x1000000000LL); + utest_expect("70000", "%ld", 70000); + utest_expect("0xb0001234", "%p", (void*)0xb0001234); + utest_expect("12ab", "%x", 0x12ab); + utest_expect("12AB", "%X", 0x12ab); + utest_expect("00123456", "%08x", 0x123456); + utest_expect("01234", "0%d", 1234); + utest_expect(" 1234", "%5d", 1234); + utest_expect("01234", "%05d", 1234); + utest_expect(" 1234", "%8d", 1234); + utest_expect("1234 ", "%-8d", 1234); + utest_expect("abcdef ", "%-11s", "abcdef"); + utest_expect("something:1234", "%s:%d", "something", 1234); + utest_expect("005:5:05", "%03d:%d:%02d", 5, 5, 5); + utest_expect("5,0x0", "%d,%p", 5, NULL); + utest_expect("68719476736,6,7,8", "%lld,%d,%d,%d", 0x1000000000LL, 6, 7, 8); + return gFails != 0; +} + +#endif /* UNIT_TESTS */ diff --git a/libc/bionic/debug_mapinfo.cpp b/libc/bionic/debug_mapinfo.cpp new file mode 100644 index 0000000..174cc28 --- /dev/null +++ b/libc/bionic/debug_mapinfo.cpp @@ -0,0 +1,96 @@ +/* + * 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 "debug_mapinfo.h" + +// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 + +static mapinfo_t* parse_maps_line(char* line) { + int len = strlen(line); + + if (len < 1) return 0; + line[--len] = 0; + + if (len < 50) return 0; + if (line[20] != 'x') return 0; + + mapinfo_t* mi = static_cast<mapinfo_t*>(dlmalloc(sizeof(mapinfo_t) + (len - 47))); + if (mi == 0) return 0; + + mi->start = strtoul(line, 0, 16); + mi->end = strtoul(line + 9, 0, 16); + mi->next = 0; + strcpy(mi->name, line + 49); + + return mi; +} + +__LIBC_HIDDEN__ mapinfo_t* mapinfo_create(int pid) { + struct mapinfo_t* milist = NULL; + char data[1024]; // Used to read lines as well as to construct the filename. + snprintf(data, sizeof(data), "/proc/%d/maps", pid); + FILE* fp = fopen(data, "r"); + if (fp != NULL) { + while (fgets(data, sizeof(data), fp) != NULL) { + mapinfo_t* mi = parse_maps_line(data); + if (mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + return milist; +} + +__LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi) { + while (mi) { + mapinfo_t* del = mi; + mi = mi->next; + dlfree(del); + } +} + +// Find the containing map info for the PC. +__LIBC_HIDDEN__ const mapinfo_t* mapinfo_find(mapinfo_t* mi, unsigned pc, unsigned* rel_pc) { + *rel_pc = pc; + for (; mi != NULL; mi = mi->next) { + if ((pc >= mi->start) && (pc < mi->end)) { + *rel_pc -= mi->start; + return mi; + } + } + return NULL; +} diff --git a/libc/bionic/malloc_debug_check_mapinfo.h b/libc/bionic/debug_mapinfo.h index e19f71e..6df55c5 100644 --- a/libc/bionic/malloc_debug_check_mapinfo.h +++ b/libc/bionic/debug_mapinfo.h @@ -26,21 +26,20 @@ * SUCH DAMAGE. */ -#ifndef MALLOC_DEBUG_CHECK_MAPINFO_H -#define MALLOC_DEBUG_CHECK_MAPINFO_H +#ifndef DEBUG_MAPINFO_H +#define DEBUG_MAPINFO_H #include <sys/cdefs.h> -struct mapinfo { - struct mapinfo* next; +struct mapinfo_t { + struct mapinfo_t* next; unsigned start; unsigned end; char name[]; }; -__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); +__LIBC_HIDDEN__ mapinfo_t* mapinfo_create(int pid); +__LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi); +__LIBC_HIDDEN__ const mapinfo_t* mapinfo_find(mapinfo_t* mi, unsigned pc, unsigned* rel_pc); -#endif /*MALLOC_DEBUG_CHECK_MAPINFO_H*/ +#endif /* DEBUG_MAPINFO_H */ diff --git a/libc/bionic/debug_stacktrace.cpp b/libc/bionic/debug_stacktrace.cpp new file mode 100644 index 0000000..4b080a4 --- /dev/null +++ b/libc/bionic/debug_stacktrace.cpp @@ -0,0 +1,112 @@ +/* + * 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 "debug_stacktrace.h" + +#include <dlfcn.h> +#include <unistd.h> +#include <unwind.h> +#include <sys/types.h> + +#include "debug_format.h" +#include "debug_mapinfo.h" +#include "logd.h" + +/* 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 = static_cast<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 = addrs; + _Unwind_Backtrace(trace_function, &state); + return max_entries - state.count; +} + +__LIBC_HIDDEN__ void log_backtrace(mapinfo_t* map_info, intptr_t* addrs, size_t c) { + intptr_t self_bt[16]; + if (addrs == NULL) { + c = get_backtrace(self_bt, 16); + addrs = self_bt; + } + + __libc_format_log(ANDROID_LOG_ERROR, "libc", + "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); + + int index = 0; + for (size_t i = 0 ; i < c; ++i) { + void* offset = 0; + const char* symbol = NULL; + + Dl_info info; + if (dladdr((void*) addrs[i], &info) != 0) { + offset = info.dli_saddr; + symbol = info.dli_sname; + } + + // This test is a bit sketchy, but it allows us to skip the + // stack trace entries due to this debugging code. it works + // because those don't have a symbol (they're not exported). + if (symbol != NULL || index > 0) { + unsigned int rel_pc; + const mapinfo_t* mi = mapinfo_find(map_info, addrs[i], &rel_pc); + const char* soname = mi ? mi->name : info.dli_fname; + if (soname == NULL) { + soname = "unknown"; + } + if (symbol) { + __libc_format_log(ANDROID_LOG_ERROR, "libc", " #%02d pc %08x %s (%s+0x%x)", + index, rel_pc, soname, symbol, addrs[i] - (intptr_t)offset); + } else { + __libc_format_log(ANDROID_LOG_ERROR, "libc", " #%02d pc %08x %s", + index, rel_pc, soname); + } + ++index; + } + } +} diff --git a/libc/bionic/malloc_debug_stacktrace.cpp b/libc/bionic/debug_stacktrace.h index 32b8ac0..3c66ea6 100644 --- a/libc/bionic/malloc_debug_stacktrace.cpp +++ b/libc/bionic/debug_stacktrace.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,48 +25,21 @@ * 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 -// ============================================================================= +#ifndef DEBUG_STACKTRACE_H +#define DEBUG_STACKTRACE_H + +#include <stdint.h> +#include <sys/cdefs.h> struct stack_crawl_state_t { - size_t count; - intptr_t* addrs; + size_t count; + intptr_t* addrs; }; +struct mapinfo_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 = static_cast<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* stack_frames, size_t max_entries); +__LIBC_HIDDEN__ void log_backtrace(mapinfo_t* map_info, intptr_t* stack_frames, size_t frame_count); -__LIBC_HIDDEN__ int get_backtrace(intptr_t* addrs, size_t max_entries) { - stack_crawl_state_t state; - state.count = max_entries; - state.addrs = addrs; - _Unwind_Backtrace(trace_function, &state); - return max_entries - state.count; -} +#endif /* DEBUG_STACKTRACE_H */ diff --git a/libc/bionic/logd_write.c b/libc/bionic/logd_write.c index 71a6f8e..11c0e68 100644 --- a/libc/bionic/logd_write.c +++ b/libc/bionic/logd_write.c @@ -250,9 +250,7 @@ void __libc_android_log_event_uid(int32_t tag) __LIBC_HIDDEN__ void __fortify_chk_fail(const char *msg, uint32_t tag) { - __libc_android_log_print(ANDROID_LOG_FATAL, "libc", - "FORTIFY_SOURCE: %s. Calling abort().\n", - msg); + __libc_format_log(ANDROID_LOG_FATAL, "libc", "FORTIFY_SOURCE: %s. Calling abort().\n", msg); if (tag != 0) { __libc_android_log_event_uid(tag); } diff --git a/libc/bionic/malloc_debug_check.cpp b/libc/bionic/malloc_debug_check.cpp index 5ad3486..60ee0cc 100644 --- a/libc/bionic/malloc_debug_check.cpp +++ b/libc/bionic/malloc_debug_check.cpp @@ -45,18 +45,19 @@ #include <unistd.h> #include <unwind.h> +#include "debug_mapinfo.h" +#include "debug_stacktrace.h" #include "dlmalloc.h" #include "logd.h" -#include "malloc_debug_check_mapinfo.h" #include "malloc_debug_common.h" #include "ScopedPthreadMutexLocker.h" -static mapinfo *milist; +static mapinfo_t* gMapInfo; /* libc.debug.malloc.backlog */ extern unsigned int malloc_double_free_backlog; -#define MAX_BACKTRACE_DEPTH 15 +#define MAX_BACKTRACE_DEPTH 16 #define ALLOCATION_TAG 0x1ee7d00d #define BACKLOG_TAG 0xbabecafe #define FREE_POISON 0xa5 @@ -67,20 +68,10 @@ extern unsigned int malloc_double_free_backlog; #define REAR_GUARD_LEN (1<<5) static void log_message(const char* format, ...) { - extern const MallocDebug __libc_malloc_default_dispatch; - extern const MallocDebug* __libc_malloc_dispatch; - extern pthread_mutex_t gAllocationsMutex; - - va_list args; - { - ScopedPthreadMutexLocker locker(&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; - } + va_list args; + va_start(args, format); + __libc_format_log_va_list(ANDROID_LOG_ERROR, "libc", format, args); + va_end(args); } struct hdr_t { @@ -121,28 +112,6 @@ static hdr_t *backlog_tail; static hdr_t *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(hdr_t *hdr) { memset(hdr->front_guard, FRONT_GUARD, FRONT_GUARD_LEN); } @@ -292,11 +261,11 @@ static inline int check_allocation_locked(hdr_t *hdr, int *safe) { if (!valid && *safe) { log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", user(hdr), hdr->size); - print_backtrace(hdr->bt, hdr->bt_depth); + log_backtrace(gMapInfo, 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); + log_backtrace(gMapInfo, hdr->freed_bt, hdr->freed_bt_depth); } } @@ -381,18 +350,18 @@ extern "C" void chk_free(void *ptr) { user(hdr), hdr->size); log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", user(hdr), hdr->size); - print_backtrace(hdr->bt, hdr->bt_depth); + log_backtrace(gMapInfo, 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_backtrace(gMapInfo, 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); + log_backtrace(gMapInfo, bt, depth); } else { log_message("+++ ALLOCATION %p IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", user(hdr)); - print_backtrace(bt, depth); + log_backtrace(gMapInfo, bt, depth); /* Leak here so that we do not crash */ //dlfree(user(hdr)); } @@ -428,14 +397,14 @@ extern "C" void *chk_realloc(void *ptr, size_t size) { 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); + log_backtrace(gMapInfo, 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_backtrace(gMapInfo, 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); + log_backtrace(gMapInfo, 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 @@ -445,7 +414,7 @@ extern "C" void *chk_realloc(void *ptr, size_t size) { } else { log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", user(hdr), size); - print_backtrace(bt, depth); + log_backtrace(gMapInfo, 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 @@ -467,8 +436,7 @@ extern "C" void *chk_calloc(int nmemb, size_t size) { size_t total_size = nmemb * size; hdr_t* hdr = static_cast<hdr_t*>(dlcalloc(1, sizeof(hdr_t) + total_size + sizeof(ftr_t))); if (hdr) { - hdr->bt_depth = get_backtrace( - hdr->bt, MAX_BACKTRACE_DEPTH); + hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH); add(hdr, total_size); return user(hdr); } @@ -476,21 +444,20 @@ extern "C" void *chk_calloc(int nmemb, size_t size) { } static void heaptracker_free_leaked_memory() { + size_t total = num; if (num) { - log_message("+++ THERE ARE %d LEAKED ALLOCATIONS\n", num); + log_message("+++ Leaked allocations: %d\n", num); } hdr_t *del = NULL; while (head) { int safe; del = head; - log_message("+++ DELETING %d BYTES OF LEAKED MEMORY AT %p (%d REMAINING)\n", - del->size, user(del), num); + log_message("+++ Leaked block of size %d at %p (leak %d of %d)\n", + del->size, user(del), 1 + total - num, total); 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); + log_backtrace(gMapInfo, del->bt, del->bt_depth); } dlfree(del); } @@ -507,13 +474,14 @@ static void heaptracker_free_leaked_memory() { * See comments on MallocDebugInit in malloc_debug_common.h */ extern "C" int malloc_debug_initialize() { - if (!malloc_double_free_backlog) - malloc_double_free_backlog = BACKLOG_DEFAULT_LEN; - milist = init_mapinfo(getpid()); - return 0; + if (!malloc_double_free_backlog) { + malloc_double_free_backlog = BACKLOG_DEFAULT_LEN; + } + gMapInfo = mapinfo_create(getpid()); + return 0; } extern "C" void malloc_debug_finalize() { - heaptracker_free_leaked_memory(); - deinit_mapinfo(milist); + heaptracker_free_leaked_memory(); + mapinfo_destroy(gMapInfo); } diff --git a/libc/bionic/malloc_debug_check_mapinfo.cpp b/libc/bionic/malloc_debug_check_mapinfo.cpp deleted file mode 100644 index 8cc2c99..0000000 --- a/libc/bionic/malloc_debug_check_mapinfo.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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) { - int len = strlen(line); - - if (len < 1) return 0; - line[--len] = 0; - - if (len < 50) return 0; - if (line[20] != 'x') return 0; - - mapinfo* mi = static_cast<mapinfo*>(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]; // Used to read lines as well as to construct the filename. - snprintf(data, sizeof(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_common.cpp b/libc/bionic/malloc_debug_common.cpp index 624a40e..bba0472 100644 --- a/libc/bionic/malloc_debug_common.cpp +++ b/libc/bionic/malloc_debug_common.cpp @@ -294,8 +294,8 @@ static void* libc_malloc_impl_handle = NULL; unsigned int malloc_double_free_backlog; static void InitMalloc(MallocDebug* table, int debug_level, const char* prefix) { - __libc_android_log_print(ANDROID_LOG_INFO, "libc", "%s: using libc.debug.malloc %d (%s)\n", - __progname, debug_level, prefix); + __libc_format_log(ANDROID_LOG_INFO, "libc", "%s: using libc.debug.malloc %d (%s)\n", + __progname, debug_level, prefix); char symbol[128]; @@ -429,7 +429,7 @@ static void malloc_init_impl() { dlclose(libc_malloc_impl_handle); return; } - if (malloc_debug_initialize()) { + if (malloc_debug_initialize() == -1) { dlclose(libc_malloc_impl_handle); return; } @@ -487,11 +487,19 @@ static void malloc_init_impl() { } static void malloc_fini_impl() { - if (libc_malloc_impl_handle) { + // Our BSD stdio implementation doesn't close the standard streams, it only flushes them. + // And it doesn't do that until its atexit handler (_cleanup) is run, and we run first! + // It's great that other unclosed FILE*s show up as malloc leaks, but we need to manually + // clean up the standard streams ourselves. + fclose(stdin); + fclose(stdout); + fclose(stderr); + + if (libc_malloc_impl_handle != NULL) { MallocDebugFini malloc_debug_finalize = reinterpret_cast<MallocDebugFini>(dlsym(libc_malloc_impl_handle, "malloc_debug_finalize")); - if (malloc_debug_finalize) { + if (malloc_debug_finalize != NULL) { malloc_debug_finalize(); } } diff --git a/libc/bionic/malloc_debug_common.h b/libc/bionic/malloc_debug_common.h index 78ad5e5..3d12f87 100644 --- a/libc/bionic/malloc_debug_common.h +++ b/libc/bionic/malloc_debug_common.h @@ -35,6 +35,8 @@ #include <stdlib.h> +#include <private/debug_format.h> + #define HASHTABLE_SIZE 1543 #define BACKTRACE_SIZE 32 /* flag definitions, currently sharing storage with "size" */ @@ -97,10 +99,10 @@ typedef void (*MallocDebugFini)(); // ============================================================================= #define debug_log(format, ...) \ - __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak_check", (format), ##__VA_ARGS__ ) + __libc_format_log(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__ ) + __libc_format_log(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__ ) + __libc_format_log(ANDROID_LOG_INFO, "malloc_leak_check", (format), ##__VA_ARGS__ ) #endif // MALLOC_DEBUG_COMMON_H diff --git a/libc/bionic/malloc_debug_leak.cpp b/libc/bionic/malloc_debug_leak.cpp index 090a981..68b6ae2 100644 --- a/libc/bionic/malloc_debug_leak.cpp +++ b/libc/bionic/malloc_debug_leak.cpp @@ -45,6 +45,7 @@ #include <unistd.h> #include <unwind.h> +#include "debug_stacktrace.h" #include "dlmalloc.h" #include "logd.h" #include "malloc_debug_common.h" @@ -255,8 +256,6 @@ extern "C" void* fill_memalign(size_t alignment, size_t bytes) { static void* MEMALIGN_GUARD = reinterpret_cast<void*>(0xA1A41520); -extern __LIBC_HIDDEN__ int get_backtrace(intptr_t* addrs, size_t max_entries); - extern "C" void* leak_malloc(size_t bytes) { // allocate enough space infront of the allocation to store the pointer for // the alloc structure. This will making free'ing the structer really fast! diff --git a/libc/bionic/malloc_debug_qemu.cpp b/libc/bionic/malloc_debug_qemu.cpp index e586b1b..812a451 100644 --- a/libc/bionic/malloc_debug_qemu.cpp +++ b/libc/bionic/malloc_debug_qemu.cpp @@ -257,8 +257,8 @@ static void dump_malloc_descriptor(char* str, INFO_TRACING_ENABLED) /* Prints a string to the emulator's stdout. - * In early stages of system loading, logging mesages via - * __libc_android_log_print API is not available, because ADB API has not been + * In early stages of system loading, logging messages to logcat + * is not available, because ADB API has not been * hooked up yet. So, in order to see such messages we need to print them to * the emulator's stdout. * Parameters passed to this macro are the same as parameters for printf @@ -289,8 +289,7 @@ static void dump_malloc_descriptor(char* str, */ #define qemu_debug_log(format, ...) \ do { \ - __libc_android_log_print(ANDROID_LOG_DEBUG, "memcheck", \ - (format), ##__VA_ARGS__); \ + __libc_format_log(ANDROID_LOG_DEBUG, "memcheck", (format), ##__VA_ARGS__); \ if (tracing_flags & DEBUG_TRACING_ENABLED) { \ qemu_log(ANDROID_LOG_DEBUG, (format), ##__VA_ARGS__); \ } \ @@ -298,8 +297,7 @@ static void dump_malloc_descriptor(char* str, #define qemu_error_log(format, ...) \ do { \ - __libc_android_log_print(ANDROID_LOG_ERROR, "memcheck", \ - (format), ##__VA_ARGS__); \ + __libc_format_log(ANDROID_LOG_ERROR, "memcheck", (format), ##__VA_ARGS__); \ if (tracing_flags & ERROR_TRACING_ENABLED) { \ qemu_log(ANDROID_LOG_ERROR, (format), ##__VA_ARGS__); \ } \ @@ -307,8 +305,7 @@ static void dump_malloc_descriptor(char* str, #define qemu_info_log(format, ...) \ do { \ - __libc_android_log_print(ANDROID_LOG_INFO, "memcheck", \ - (format), ##__VA_ARGS__); \ + __libc_format_log(ANDROID_LOG_INFO, "memcheck", (format), ##__VA_ARGS__); \ if (tracing_flags & INFO_TRACING_ENABLED) { \ qemu_log(ANDROID_LOG_INFO, (format), ##__VA_ARGS__); \ } \ @@ -318,20 +315,19 @@ static void dump_malloc_descriptor(char* str, * Param: * type - Message type: debug, error, or info * desc - MallocDesc instance to dump. - * frmt + rest - Formats message preceding dumped descriptor. + * fmt + rest - Formats message preceding dumped descriptor. */ -#define log_mdesc(type, desc, frmt, ...) \ +#define log_mdesc(type, desc, fmt, ...) \ do { \ if (tracing_enabled(type)) { \ char log_str[4096]; \ - size_t str_len; \ - snprintf(log_str, sizeof(log_str), frmt, ##__VA_ARGS__); \ + __libc_format_buffer(log_str, sizeof(log_str), fmt, ##__VA_ARGS__); \ log_str[sizeof(log_str) - 1] = '\0'; \ - str_len = strlen(log_str); \ + size_t str_len = strlen(log_str); \ dump_malloc_descriptor(log_str + str_len, \ sizeof(log_str) - str_len, \ (desc)); \ - type##_log(log_str); \ + type##_log("%s", log_str); \ } \ } while (0) diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c index f294723..f2a7ebe 100644 --- a/libc/bionic/pthread.c +++ b/libc/bionic/pthread.c @@ -50,6 +50,7 @@ #include "bionic_pthread.h" #include "bionic_ssp.h" #include "bionic_tls.h" +#include "debug_format.h" #include "pthread_internal.h" #include "thread_private.h" @@ -229,7 +230,7 @@ int _init_thread(pthread_internal_t* thread, pid_t kernel_id, const pthread_attr // For backwards compatibility reasons, we just warn about failures here. // error = errno; const char* msg = "pthread_create sched_setscheduler call failed: %s\n"; - __libc_android_log_print(ANDROID_LOG_WARN, "libc", msg, strerror(errno)); + __libc_format_log(ANDROID_LOG_WARN, "libc", msg, strerror(errno)); } } diff --git a/libc/bionic/pthread_debug.c b/libc/bionic/pthread_debug.cpp index 7ee208c..c557211 100644 --- a/libc/bionic/pthread_debug.c +++ b/libc/bionic/pthread_debug.cpp @@ -31,9 +31,7 @@ #include <sys/system_properties.h> #include <sys/mman.h> -#if HAVE_DLADDR -#include <dlfcn.h> -#endif +//#include <dlfcn.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -42,8 +40,12 @@ #include <unwind.h> #include <unistd.h> -#include "logd.h" #include "bionic_tls.h" +#include "debug_mapinfo.h" +#include "debug_stacktrace.h" +#include "logd.h" + +#include <private/debug_format.h> /* * =========================================================================== @@ -97,20 +99,16 @@ the lock has been acquired. // ============================================================================= #define LOGD(format, ...) \ - __libc_android_log_print(ANDROID_LOG_DEBUG, \ - "pthread_debug", (format), ##__VA_ARGS__ ) + __libc_format_log(ANDROID_LOG_DEBUG, "pthread_debug", (format), ##__VA_ARGS__ ) #define LOGW(format, ...) \ - __libc_android_log_print(ANDROID_LOG_WARN, \ - "pthread_debug", (format), ##__VA_ARGS__ ) + __libc_format_log(ANDROID_LOG_WARN, "pthread_debug", (format), ##__VA_ARGS__ ) #define LOGE(format, ...) \ - __libc_android_log_print(ANDROID_LOG_ERROR, \ - "pthread_debug", (format), ##__VA_ARGS__ ) + __libc_format_log(ANDROID_LOG_ERROR, "pthread_debug", (format), ##__VA_ARGS__ ) #define LOGI(format, ...) \ - __libc_android_log_print(ANDROID_LOG_INFO, \ - "pthread_debug", (format), ##__VA_ARGS__ ) + __libc_format_log(ANDROID_LOG_INFO, "pthread_debug", (format), ##__VA_ARGS__ ) static const char* const kStartBanner = "==============================================================="; @@ -120,185 +118,9 @@ static const char* const kEndBanner = extern char* __progname; -// ============================================================================= -// map info functions -// ============================================================================= - -typedef struct mapinfo { - struct mapinfo *next; - unsigned start; - unsigned end; - char name[]; -} mapinfo; - -static mapinfo* sMapInfo = NULL; - -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 = malloc(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; -} - -static 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; -} - -static void deinit_mapinfo(mapinfo *mi) -{ - mapinfo *del; - while(mi) { - del = mi; - mi = mi->next; - free(del); - } -} - -/* Find the containing map info for the pc */ -static 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; -} - -// ============================================================================= -// stack trace functions -// ============================================================================= - #define STACK_TRACE_DEPTH 16 -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; -} - -static void log_backtrace(intptr_t* addrs, size_t c) -{ - int index = 0; - size_t i; - for (i=0 ; i<c; i++) { - unsigned int relpc; - void* offset = 0; - const char* symbol = NULL; - -#if HAVE_DLADDR - Dl_info info; - if (dladdr((void*)addrs[i], &info)) { - offset = info.dli_saddr; - symbol = info.dli_sname; - } -#endif - - if (symbol || index>0 || !HAVE_DLADDR) { - /* - * this test is a bit sketchy, but it allows us to skip the - * stack trace entries due to this debugging code. it works - * because those don't have a symbol (they're not exported) - */ - mapinfo const* mi = pc_to_mapinfo(sMapInfo, addrs[i], &relpc); - char const* soname = mi ? mi->name : NULL; -#if HAVE_DLADDR - if (!soname) - soname = info.dli_fname; -#endif - if (!soname) - soname = "unknown"; - - if (symbol) { - LOGW(" " - "#%02d pc %08lx %s (%s+0x%x)", - index, relpc, soname, symbol, - addrs[i] - (intptr_t)offset); - } else { - LOGW(" " - "#%02d pc %08lx %s", - index, relpc, soname); - } - index++; - } - } -} +static mapinfo_t* gMapInfo; /****************************************************************************/ @@ -322,18 +144,21 @@ static pthread_mutex_t sDbgLock = PTHREAD_MUTEX_INITIALIZER; static size_t sDbgAllocOffset = DBG_ALLOC_BLOCK_SIZE; static char* sDbgAllocPtr = NULL; -static void* DbgAllocLocked(size_t size) { +template <typename T> +static T* DbgAllocLocked(size_t count = 1) { + size_t size = sizeof(T) * count; if ((sDbgAllocOffset + size) > DBG_ALLOC_BLOCK_SIZE) { sDbgAllocOffset = 0; - sDbgAllocPtr = mmap(NULL, DBG_ALLOC_BLOCK_SIZE, PROT_READ|PROT_WRITE, - MAP_ANON | MAP_PRIVATE, 0, 0); + sDbgAllocPtr = reinterpret_cast<char*>(mmap(NULL, DBG_ALLOC_BLOCK_SIZE, + PROT_READ|PROT_WRITE, + MAP_ANON | MAP_PRIVATE, 0, 0)); if (sDbgAllocPtr == MAP_FAILED) { return NULL; } } void* addr = sDbgAllocPtr + sDbgAllocOffset; sDbgAllocOffset += size; - return addr; + return reinterpret_cast<T*>(addr); } static void* debug_realloc(void *ptr, size_t size, size_t old_size) { @@ -460,9 +285,9 @@ static int pthread_mutex_unlock_unchecked(pthread_mutex_t *mutex) { /****************************************************************************/ -static void dup_backtrace(CallStack* stack, int count, intptr_t const* addrs) { +static void dup_backtrace(CallStack* stack, size_t count, intptr_t const* addrs) { stack->depth = count; - stack->addrs = DbgAllocLocked(count * sizeof(intptr_t)); + stack->addrs = DbgAllocLocked<intptr_t>(count); memcpy(stack->addrs, addrs, count * sizeof(intptr_t)); } @@ -545,9 +370,9 @@ static int traverseTree(MutexInfo* obj, MutexInfo const* objParent) /* Turn off prediction temporarily in this thread while logging */ sPthreadDebugDisabledThread = gettid(); - if (sMapInfo == NULL) { - // note: we're protected by sDbgLock - sMapInfo = init_mapinfo(getpid()); + if (gMapInfo == NULL) { + // note: we're protected by sDbgLock. + gMapInfo = mapinfo_create(getpid()); } LOGW("%s\n", kStartBanner); @@ -555,7 +380,7 @@ static int traverseTree(MutexInfo* obj, MutexInfo const* objParent) LOGW("Illegal lock attempt:\n"); LOGW("--- pthread_mutex_t at %p\n", obj->mutex); stackDepth = get_backtrace(addrs, STACK_TRACE_DEPTH); - log_backtrace(addrs, stackDepth); + log_backtrace(gMapInfo, addrs, stackDepth); LOGW("+++ Currently held locks in this thread (in reverse order):"); MutexInfo* cur = obj; @@ -566,7 +391,7 @@ static int traverseTree(MutexInfo* obj, MutexInfo const* objParent) if (parent->owner == ourtid) { LOGW("--- pthread_mutex_t at %p\n", parent->mutex); if (sPthreadDebugLevel >= CAPTURE_CALLSTACK) { - log_backtrace(parent->stackTrace, parent->stackDepth); + log_backtrace(gMapInfo, parent->stackTrace, parent->stackDepth); } cur = parent; break; @@ -589,13 +414,13 @@ static int traverseTree(MutexInfo* obj, MutexInfo const* objParent) if (sPthreadDebugLevel >= CAPTURE_CALLSTACK) { int index = historyListHas(&obj->parents, objParent); if ((size_t)index < (size_t)obj->stacks.count) { - log_backtrace( - obj->stacks.stack[index].addrs, - obj->stacks.stack[index].depth); + log_backtrace(gMapInfo, + obj->stacks.stack[index].addrs, + obj->stacks.stack[index].depth); } else { - log_backtrace( - obj->stackTrace, - obj->stackDepth); + log_backtrace(gMapInfo, + obj->stackTrace, + obj->stackDepth); } } result = 0; @@ -640,8 +465,8 @@ static void mutex_lock_checked(MutexInfo* mrl, MutexInfo* object) linkParentToChild(mrl, object); if (!traverseTree(object, mrl)) { - deinit_mapinfo(sMapInfo); - sMapInfo = NULL; + mapinfo_destroy(gMapInfo); + gMapInfo = NULL; LOGW("%s\n", kEndBanner); unlinkParentFromChild(mrl, object); // reenable pthread debugging for this thread @@ -758,7 +583,7 @@ static HashEntry* hashmap_lookup(HashTable* table, if (entry == NULL) { // create a new entry - entry = (HashEntry*)DbgAllocLocked(sizeof(HashEntry)); + entry = DbgAllocLocked<HashEntry>(); entry->data = NULL; entry->slot = slot; entry->prev = NULL; @@ -785,8 +610,9 @@ static MutexInfo* get_mutex_info(pthread_mutex_t *mutex) &mutex, sizeof(mutex), &MutexInfo_equals); if (entry->data == NULL) { - entry->data = (MutexInfo*)DbgAllocLocked(sizeof(MutexInfo)); - initMutexInfo(entry->data, mutex); + MutexInfo* mutex_info = DbgAllocLocked<MutexInfo>(); + entry->data = mutex_info; + initMutexInfo(mutex_info, mutex); } pthread_mutex_unlock_unchecked(&sDbgLock); @@ -808,8 +634,9 @@ static ThreadInfo* get_thread_info(pid_t pid) &pid, sizeof(pid), &ThreadInfo_equals); if (entry->data == NULL) { - entry->data = (ThreadInfo*)DbgAllocLocked(sizeof(ThreadInfo)); - initThreadInfo(entry->data, pid); + ThreadInfo* thread_info = DbgAllocLocked<ThreadInfo>(); + entry->data = thread_info; + initThreadInfo(thread_info, pid); } pthread_mutex_unlock_unchecked(&sDbgLock); @@ -848,8 +675,7 @@ static MutexInfo* get_most_recently_locked() { * after system properties have been initialized */ -__LIBC_HIDDEN__ -void pthread_debug_init(void) { +extern "C" __LIBC_HIDDEN__ void pthread_debug_init() { char env[PROP_VALUE_MAX]; if (__system_property_get("debug.libc.pthread", env)) { int level = atoi(env); @@ -872,8 +698,7 @@ void pthread_debug_init(void) { * the checks before the lock is held.) */ -__LIBC_HIDDEN__ -void pthread_debug_mutex_lock_check(pthread_mutex_t *mutex) +extern "C" __LIBC_HIDDEN__ void pthread_debug_mutex_lock_check(pthread_mutex_t *mutex) { if (sPthreadDebugLevel == 0) return; // prediction disabled for this thread @@ -890,8 +715,7 @@ void pthread_debug_mutex_lock_check(pthread_mutex_t *mutex) * still held (ie: before calling the real unlock) */ -__LIBC_HIDDEN__ -void pthread_debug_mutex_unlock_check(pthread_mutex_t *mutex) +extern "C" __LIBC_HIDDEN__ void pthread_debug_mutex_unlock_check(pthread_mutex_t *mutex) { if (sPthreadDebugLevel == 0) return; // prediction disabled for this thread diff --git a/libc/bionic/ssp.cpp b/libc/bionic/ssp.cpp index fdf8832..08c36c5 100644 --- a/libc/bionic/ssp.cpp +++ b/libc/bionic/ssp.cpp @@ -63,8 +63,8 @@ void __stack_chk_fail() { path[count] = '\0'; } - // Do a best effort at logging. This ends up calling writev(2). - __libc_android_log_print(ANDROID_LOG_FATAL, path, "stack corruption detected: aborted"); + // Do a best effort at logging. + __libc_android_log_write(ANDROID_LOG_FATAL, path, "stack corruption detected: aborted"); // Make sure there is no default action for SIGABRT. struct sigaction sa; diff --git a/libc/bionic/stubs.cpp b/libc/bionic/stubs.cpp index 48281b4..3f24d1b 100644 --- a/libc/bionic/stubs.cpp +++ b/libc/bionic/stubs.cpp @@ -32,6 +32,7 @@ #include <mntent.h> #include <netdb.h> #include <private/android_filesystem_config.h> +#include <private/debug_format.h> #include <private/logd.h> #include <pthread.h> #include <pwd.h> @@ -435,7 +436,7 @@ protoent* getprotobynumber(int /*proto*/) { static void unimplemented_stub(const char* function) { const char* fmt = "%s(3) is not implemented on Android\n"; - __libc_android_log_print(ANDROID_LOG_WARN, "libc", fmt, function); + __libc_format_log(ANDROID_LOG_WARN, "libc", fmt, function); fprintf(stderr, fmt, function); } diff --git a/libc/private/debug_format.h b/libc/private/debug_format.h new file mode 100644 index 0000000..0bc1148 --- /dev/null +++ b/libc/private/debug_format.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 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 _DEBUG_FORMAT_H +#define _DEBUG_FORMAT_H + +#include <sys/cdefs.h> +#include <stdarg.h> +#include <stddef.h> + +__BEGIN_DECLS + +// Formatting routines for the C library's internal debugging. +// Unlike the usual alternatives, these don't allocate. + +__LIBC_HIDDEN__ int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) + __attribute__((__format__(printf, 3, 4))); + +__LIBC_HIDDEN__ int __libc_format_fd(int fd, const char* format, ...) + __attribute__((__format__(printf, 2, 3))); + +__LIBC_HIDDEN__ int __libc_format_log(int priority, const char* tag, const char* format, ...) + __attribute__((__format__(printf, 3, 4))); + +__LIBC_HIDDEN__ int __libc_format_log_va_list(int priority, const char* tag, const char* format, + va_list ap); + +__END_DECLS + +#endif /* _DEBUG_FORMAT_H */ |