diff options
65 files changed, 734 insertions, 4978 deletions
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c index 5a2bc3c..3e3ab5a 100644 --- a/debuggerd/crasher.c +++ b/debuggerd/crasher.c @@ -1,20 +1,17 @@ - -//#include <cutils/misc.h> - -#include <unistd.h> +#include <assert.h> +#include <errno.h> +#include <pthread.h> +#include <sched.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sched.h> -#include <errno.h> - -#include <signal.h> #include <sys/ptrace.h> -#include <sys/wait.h> #include <sys/socket.h> +#include <sys/wait.h> +#include <unistd.h> -#include <pthread.h> - +#include <cutils/log.h> #include <cutils/sockets.h> extern const char* __progname; @@ -23,8 +20,8 @@ void crash1(void); void crashnostack(void); static int do_action(const char* arg); -static void maybeabort() { - if(time(0) != 42) { +static void maybe_abort() { + if (time(0) != 42) { abort(); } } @@ -119,35 +116,54 @@ static int do_action(const char* arg) if (!strncmp(arg, "thread-", strlen("thread-"))) { return do_action_on_thread(arg + strlen("thread-")); - } else if (!strcmp(arg,"smash-stack")) { + } else if (!strcmp(arg, "smash-stack")) { return smash_stack(42); - } else if (!strcmp(arg,"stack-overflow")) { + } else if (!strcmp(arg, "stack-overflow")) { overflow_stack(NULL); - } else if (!strcmp(arg,"nostack")) { + } else if (!strcmp(arg, "nostack")) { crashnostack(); - } else if (!strcmp(arg,"ctest")) { + } else if (!strcmp(arg, "ctest")) { return ctest(); - } else if (!strcmp(arg,"exit")) { + } else if (!strcmp(arg, "exit")) { exit(1); - } else if (!strcmp(arg,"crash")) { + } else if (!strcmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) { return crash(42); - } else if (!strcmp(arg,"abort")) { - maybeabort(); + } else if (!strcmp(arg, "abort")) { + maybe_abort(); + } else if (!strcmp(arg, "assert")) { + __assert("some_file.c", 123, "false"); + } else if (!strcmp(arg, "assert2")) { + __assert2("some_file.c", 123, "some_function", "false"); + } else if (!strcmp(arg, "LOG_ALWAYS_FATAL")) { + LOG_ALWAYS_FATAL("hello %s", "world"); + } else if (!strcmp(arg, "LOG_ALWAYS_FATAL_IF")) { + LOG_ALWAYS_FATAL_IF(true, "hello %s", "world"); + } else if (!strcmp(arg, "SIGPIPE")) { + int pipe_fds[2]; + pipe(pipe_fds); + close(pipe_fds[0]); + write(pipe_fds[1], "oops", 4); + return EXIT_SUCCESS; } else if (!strcmp(arg, "heap-usage")) { abuse_heap(); } fprintf(stderr, "%s OP\n", __progname); fprintf(stderr, "where OP is:\n"); - fprintf(stderr, " smash-stack overwrite a stack-guard canary\n"); - fprintf(stderr, " stack-overflow recurse until the stack overflows\n"); - fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n"); - fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n"); - fprintf(stderr, " nostack crash with a NULL stack pointer\n"); - fprintf(stderr, " ctest (obsoleted by thread-crash?)\n"); - fprintf(stderr, " exit call exit(1)\n"); - fprintf(stderr, " crash cause a SIGSEGV\n"); - fprintf(stderr, " abort call abort()\n"); + fprintf(stderr, " smash-stack overwrite a stack-guard canary\n"); + fprintf(stderr, " stack-overflow recurse until the stack overflows\n"); + fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n"); + fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n"); + fprintf(stderr, " nostack crash with a NULL stack pointer\n"); + fprintf(stderr, " ctest (obsoleted by thread-crash?)\n"); + fprintf(stderr, " exit call exit(1)\n"); + fprintf(stderr, " abort call abort()\n"); + fprintf(stderr, " assert call assert() without a function\n"); + fprintf(stderr, " assert2 call assert() with a function\n"); + fprintf(stderr, " LOG_ALWAYS_FATAL call LOG_ALWAYS_FATAL\n"); + fprintf(stderr, " LOG_ALWAYS_FATAL_IF call LOG_ALWAYS_FATAL\n"); + fprintf(stderr, " SIGPIPE cause a SIGPIPE\n"); + fprintf(stderr, " SIGSEGV cause a SIGSEGV (synonym: crash)\n"); fprintf(stderr, "prefix any of the above with 'thread-' to not run\n"); fprintf(stderr, "on the process' main thread.\n"); return EXIT_SUCCESS; diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp index a2b164e..76bd7a3 100644 --- a/debuggerd/debuggerd.cpp +++ b/debuggerd/debuggerd.cpp @@ -51,6 +51,7 @@ struct debugger_request_t { pid_t pid, tid; uid_t uid, gid; uintptr_t abort_msg_address; + int32_t original_si_code; }; static int write_string(const char* file, const char* string) { @@ -218,6 +219,7 @@ static int read_request(int fd, debugger_request_t* out_request) { out_request->uid = cr.uid; out_request->gid = cr.gid; out_request->abort_msg_address = msg.abort_msg_address; + out_request->original_si_code = msg.original_si_code; if (msg.action == DEBUGGER_ACTION_CRASH) { // Ensure that the tid reported by the crashing process is valid. @@ -302,9 +304,10 @@ static void handle_request(int fd) { case SIGSTOP: if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { XLOG("stopped -- dumping to tombstone\n"); - tombstone_path = engrave_tombstone( - request.pid, request.tid, signal, request.abort_msg_address, true, true, - &detach_failed, &total_sleep_time_usec); + tombstone_path = engrave_tombstone(request.pid, request.tid, + signal, request.original_si_code, + request.abort_msg_address, true, true, + &detach_failed, &total_sleep_time_usec); } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) { XLOG("stopped -- dumping to fd\n"); dump_backtrace(fd, -1, request.pid, request.tid, &detach_failed, @@ -336,9 +339,10 @@ static void handle_request(int fd) { kill(request.pid, SIGSTOP); // don't dump sibling threads when attaching to GDB because it // makes the process less reliable, apparently... - tombstone_path = engrave_tombstone( - request.pid, request.tid, signal, request.abort_msg_address, !attach_gdb, - false, &detach_failed, &total_sleep_time_usec); + tombstone_path = engrave_tombstone(request.pid, request.tid, + signal, request.original_si_code, + request.abort_msg_address, !attach_gdb, false, + &detach_failed, &total_sleep_time_usec); break; default: diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp index 2319f37..10e2cc4 100755 --- a/debuggerd/tombstone.cpp +++ b/debuggerd/tombstone.cpp @@ -55,7 +55,7 @@ // Must match the path defined in NativeCrashListener.java #define NCRASH_SOCKET_PATH "/data/system/ndebugsocket" -static bool signal_has_address(int sig) { +static bool signal_has_si_addr(int sig) { switch (sig) { case SIGILL: case SIGFPE: @@ -75,7 +75,7 @@ static const char* get_signame(int sig) { case SIGFPE: return "SIGFPE"; case SIGSEGV: return "SIGSEGV"; case SIGPIPE: return "SIGPIPE"; -#ifdef SIGSTKFLT +#if defined(SIGSTKFLT) case SIGSTKFLT: return "SIGSTKFLT"; #endif case SIGSTOP: return "SIGSTOP"; @@ -97,13 +97,17 @@ static const char* get_sigcode(int signo, int code) { case ILL_COPROC: return "ILL_COPROC"; case ILL_BADSTK: return "ILL_BADSTK"; } + static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code"); break; case SIGBUS: switch (code) { case BUS_ADRALN: return "BUS_ADRALN"; case BUS_ADRERR: return "BUS_ADRERR"; case BUS_OBJERR: return "BUS_OBJERR"; + case BUS_MCEERR_AR: return "BUS_MCEERR_AR"; + case BUS_MCEERR_AO: return "BUS_MCEERR_AO"; } + static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code"); break; case SIGFPE: switch (code) { @@ -116,36 +120,36 @@ static const char* get_sigcode(int signo, int code) { case FPE_FLTINV: return "FPE_FLTINV"; case FPE_FLTSUB: return "FPE_FLTSUB"; } + static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code"); break; case SIGSEGV: switch (code) { case SEGV_MAPERR: return "SEGV_MAPERR"; case SEGV_ACCERR: return "SEGV_ACCERR"; } + static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code"); break; case SIGTRAP: switch (code) { case TRAP_BRKPT: return "TRAP_BRKPT"; case TRAP_TRACE: return "TRAP_TRACE"; + case TRAP_BRANCH: return "TRAP_BRANCH"; + case TRAP_HWBKPT: return "TRAP_HWBKPT"; } + static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code"); break; } // Then the other codes... switch (code) { case SI_USER: return "SI_USER"; -#if defined(SI_KERNEL) case SI_KERNEL: return "SI_KERNEL"; -#endif case SI_QUEUE: return "SI_QUEUE"; case SI_TIMER: return "SI_TIMER"; case SI_MESGQ: return "SI_MESGQ"; case SI_ASYNCIO: return "SI_ASYNCIO"; -#if defined(SI_SIGIO) case SI_SIGIO: return "SI_SIGIO"; -#endif -#if defined(SI_TKILL) case SI_TKILL: return "SI_TKILL"; -#endif + case SI_DETHREAD: return "SI_DETHREAD"; } // Then give up... return "?"; @@ -167,20 +171,26 @@ static void dump_build_info(log_t* log) { _LOG(log, SCOPE_AT_FAULT, "Build fingerprint: '%s'\n", fingerprint); } -static void dump_fault_addr(log_t* log, pid_t tid, int sig) { +static void dump_signal_info(log_t* log, pid_t tid, int signal, int si_code) { siginfo_t si; - memset(&si, 0, sizeof(si)); - if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){ + if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) { _LOG(log, SCOPE_AT_FAULT, "cannot get siginfo: %s\n", strerror(errno)); - } else if (signal_has_address(sig)) { - _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr %" PRIPTR "\n", - sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code), - reinterpret_cast<uintptr_t>(si.si_addr)); + return; + } + + // bionic has to re-raise some signals, which overwrites the si_code with SI_TKILL. + si.si_code = si_code; + + char addr_desc[32]; // ", fault addr 0x1234" + if (signal_has_si_addr(signal)) { + snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr); } else { - _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr --------\n", - sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); + snprintf(addr_desc, sizeof(addr_desc), "--------"); } + + _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr %s\n", + signal, get_signame(signal), si.si_code, get_sigcode(signal, si.si_code), addr_desc); } static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, int scope_flags) { @@ -345,7 +355,7 @@ static void dump_nearby_maps(BacktraceMap* map, log_t* log, pid_t tid, int scope _LOG(log, scope_flags, "cannot get siginfo for %d: %s\n", tid, strerror(errno)); return; } - if (!signal_has_address(si.si_signo)) { + if (!signal_has_si_addr(si.si_signo)) { return; } @@ -578,8 +588,9 @@ static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t addre } // Dumps all information about the specified pid to the tombstone. -static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, - bool dump_sibling_threads, int* total_sleep_time_usec) { +static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, int si_code, + uintptr_t abort_msg_address, bool dump_sibling_threads, + int* total_sleep_time_usec) { // don't copy log messages to tombstone unless this is a dev device char value[PROPERTY_VALUE_MAX]; property_get("ro.debuggable", value, "0"); @@ -601,7 +612,7 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a dump_revision_info(log); dump_thread_info(log, pid, tid, SCOPE_AT_FAULT); if (signal) { - dump_fault_addr(log, tid, signal); + dump_signal_info(log, tid, signal, si_code); } UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid)); @@ -715,9 +726,9 @@ static int activity_manager_connect() { return amfd; } -char* engrave_tombstone( - pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, bool dump_sibling_threads, - bool quiet, bool* detach_failed, int* total_sleep_time_usec) { +char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code, + uintptr_t abort_msg_address, bool dump_sibling_threads, bool quiet, + bool* detach_failed, int* total_sleep_time_usec) { if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) { LOG("failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno)); } @@ -742,8 +753,8 @@ char* engrave_tombstone( log.tfd = fd; log.amfd = activity_manager_connect(); log.quiet = quiet; - *detach_failed = dump_crash( - &log, pid, tid, signal, abort_msg_address, dump_sibling_threads, total_sleep_time_usec); + *detach_failed = dump_crash(&log, pid, tid, signal, original_si_code, abort_msg_address, + dump_sibling_threads, total_sleep_time_usec); close(log.amfd); close(fd); diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h index e9878bf..3574e84 100644 --- a/debuggerd/tombstone.h +++ b/debuggerd/tombstone.h @@ -23,7 +23,9 @@ /* Creates a tombstone file and writes the crash dump to it. * Returns the path of the tombstone, which must be freed using free(). */ -char* engrave_tombstone(pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, - bool dump_sibling_threads, bool quiet, bool* detach_failed, int* total_sleep_time_usec); +char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code, + uintptr_t abort_msg_address, + bool dump_sibling_threads, bool quiet, + bool* detach_failed, int* total_sleep_time_usec); #endif // _DEBUGGERD_TOMBSTONE_H diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp index 58da4ee..272b6d7 100644 --- a/healthd/BatteryPropertiesRegistrar.cpp +++ b/healthd/BatteryPropertiesRegistrar.cpp @@ -43,6 +43,8 @@ void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesListener>& listener) { { + if (listener == NULL) + return; Mutex::Autolock _l(mRegistrationLock); // check whether this is a duplicate for (size_t i = 0; i < mListeners.size(); i++) { @@ -58,6 +60,8 @@ void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesLis } void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) { + if (listener == NULL) + return; Mutex::Autolock _l(mRegistrationLock); for (size_t i = 0; i < mListeners.size(); i++) { if (mListeners[i]->asBinder() == listener->asBinder()) { diff --git a/include/android/log.h b/include/android/log.h index 0ea4c29..f5b1900 100644 --- a/include/android/log.h +++ b/include/android/log.h @@ -110,11 +110,11 @@ int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap); /* - * Log an assertion failure and SIGTRAP the process to have a chance - * to inspect it, if a debugger is attached. This uses the FATAL priority. + * Log an assertion failure and abort the process to have a chance + * to inspect it if a debugger is attached. This uses the FATAL priority. */ void __android_log_assert(const char *cond, const char *tag, - const char *fmt, ...) + const char *fmt, ...) #if defined(__GNUC__) __attribute__ ((noreturn)) __attribute__ ((format(printf, 3, 4))) diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h index af80e2c..ae6bfc4 100644 --- a/include/cutils/debugger.h +++ b/include/cutils/debugger.h @@ -42,6 +42,7 @@ typedef struct { debugger_action_t action; pid_t tid; uintptr_t abort_msg_address; + int32_t original_si_code; } debugger_msg_t; /* Dumps a process backtrace, registers, and stack to a tombstone file (requires root). diff --git a/include/system/audio.h b/include/system/audio.h index 56ab4e4..1b7d81a 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -218,16 +218,26 @@ enum { AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT), - AUDIO_CHANNEL_OUT_SURROUND = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD, + /* like AUDIO_CHANNEL_OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_* */ + AUDIO_CHANNEL_OUT_QUAD_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_BACK_CENTER), + AUDIO_CHANNEL_OUT_SIDE_LEFT | + AUDIO_CHANNEL_OUT_SIDE_RIGHT), AUDIO_CHANNEL_OUT_5POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT), + AUDIO_CHANNEL_OUT_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1, + /* like AUDIO_CHANNEL_OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_* */ + AUDIO_CHANNEL_OUT_5POINT1_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_SIDE_LEFT | + AUDIO_CHANNEL_OUT_SIDE_RIGHT), // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1 AUDIO_CHANNEL_OUT_7POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | @@ -291,11 +301,19 @@ enum { AUDIO_CHANNEL_IN_VOICE_DNLINK), }; +/* A channel mask per se only defines the presence or absence of a channel, not the order. + * But see AUDIO_INTERLEAVE_* below for the platform convention of order. + */ typedef uint32_t audio_channel_mask_t; /* Expresses the convention when stereo audio samples are stored interleaved * in an array. This should improve readability by allowing code to use * symbolic indices instead of hard-coded [0] and [1]. + * + * For multi-channel beyond stereo, the platform convention is that channels + * are interleaved in order from least significant channel mask bit + * to most significant channel mask bit, with unused bits skipped. + * Any exceptions to this convention will be noted at the appropriate API. */ enum { AUDIO_INTERLEAVE_LEFT = 0, @@ -514,12 +532,16 @@ static inline bool audio_is_a2dp_device(audio_devices_t device) static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) { - device &= ~AUDIO_DEVICE_BIT_IN; - if ((popcount(device) == 1) && (device & (AUDIO_DEVICE_OUT_ALL_SCO | - AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET))) - return true; - else - return false; + if ((device & AUDIO_DEVICE_BIT_IN) == 0) { + if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL_SCO) == 0)) + return true; + } else { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) == 0)) + return true; + } + + return false; } static inline bool audio_is_usb_out_device(audio_devices_t device) diff --git a/init/devices.c b/init/devices.c index 80c6d75..5d7ad3b 100644 --- a/init/devices.c +++ b/init/devices.c @@ -699,7 +699,7 @@ static void handle_generic_device_event(struct uevent *uevent) static void handle_device_event(struct uevent *uevent) { - if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change")) + if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online")) fixup_sys_perms(uevent->path); if (!strncmp(uevent->subsystem, "block", 5)) { diff --git a/init/property_service.c b/init/property_service.c index 9613973..8247ef4 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -97,6 +97,7 @@ struct { { "persist.gps.", AID_GPS, 0 }, { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, { "selinux." , AID_SYSTEM, 0 }, + { "build.fingerprint", AID_SYSTEM, 0 }, { "partition." , AID_SYSTEM, 0}, { NULL, 0, 0 } }; diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk index fdcb162..a7305da 100755 --- a/libbacktrace/Android.mk +++ b/libbacktrace/Android.mk @@ -46,10 +46,6 @@ libbacktrace_shared_libraries_target := \ libcutils \ libgccdemangle \ -# To enable using libunwind on each arch, add it to this list. -libunwind_architectures := arm arm64 mips x86 x86_64 - -ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures))) libbacktrace_src_files += \ UnwindCurrent.cpp \ UnwindMap.cpp \ @@ -68,24 +64,6 @@ libbacktrace_shared_libraries_host := \ libbacktrace_static_libraries_host := \ libcutils \ -else -libbacktrace_src_files += \ - Corkscrew.cpp \ - -libbacktrace_c_includes := \ - system/core/libcorkscrew \ - -libbacktrace_shared_libraries := \ - libcorkscrew \ - -libbacktrace_shared_libraries_target += \ - libdl \ - -libbacktrace_ldlibs_host := \ - -ldl \ - -endif - module := libbacktrace module_tag := optional build_type := target @@ -118,11 +96,8 @@ backtrace_test_cflags := \ -fno-builtin \ -O0 \ -g \ - -DGTEST_HAS_STD_STRING \ - -fstack-protector-all \ backtrace_test_cflags_target := \ - -DGTEST_OS_LINUX_ANDROID \ -DENABLE_PSS_TESTS \ backtrace_test_src_files := \ diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp index 6eb290d..0056f4b 100644 --- a/libbacktrace/BacktraceMap.cpp +++ b/libbacktrace/BacktraceMap.cpp @@ -108,11 +108,11 @@ bool BacktraceMap::Build() { #if defined(__APPLE__) // cmd is guaranteed to always be big enough to hold this string. - sprintf(cmd, "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_); + snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_); FILE* fp = popen(cmd, "r"); #else // path is guaranteed to always be big enough to hold this string. - sprintf(path, "/proc/%d/maps", pid_); + snprintf(path, sizeof(path), "/proc/%d/maps", pid_); FILE* fp = fopen(path, "r"); #endif if (fp == NULL) { diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp index 4cda19e..e0bab24 100644 --- a/libbacktrace/BacktraceThread.cpp +++ b/libbacktrace/BacktraceThread.cpp @@ -136,7 +136,7 @@ void BacktraceThread::FinishUnwind() { bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) { entry->state = STATE_WAITING; - if (tgkill(Pid(), Tid(), SIGURG) != 0) { + if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) { BACK_LOGW("tgkill failed %s", strerror(errno)); return false; } @@ -196,9 +196,9 @@ bool BacktraceThread::Unwind(size_t num_ignore_frames) { act.sa_sigaction = SignalHandler; act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; sigemptyset(&act.sa_mask); - if (sigaction(SIGURG, &act, &oldact) == 0) { + if (sigaction(THREAD_SIGNAL, &act, &oldact) == 0) { retval = TriggerUnwindOnThread(entry); - sigaction(SIGURG, &oldact, NULL); + sigaction(THREAD_SIGNAL, &oldact, NULL); } else { BACK_LOGW("sigaction failed %s", strerror(errno)); } diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h index 3412d58..9310a44 100644 --- a/libbacktrace/BacktraceThread.h +++ b/libbacktrace/BacktraceThread.h @@ -18,6 +18,7 @@ #define _LIBBACKTRACE_BACKTRACE_THREAD_H #include <inttypes.h> +#include <signal.h> #include <sys/types.h> #include "BacktraceImpl.h" @@ -29,6 +30,14 @@ enum state_e { STATE_CANCEL, }; +// The signal used to cause a thread to dump the stack. +#if defined(__GLIBC__) +// GLIBC reserves __SIGRTMIN signals, so use SIGRTMIN to avoid errors. +#define THREAD_SIGNAL SIGRTMIN +#else +#define THREAD_SIGNAL (__SIGRTMIN+1) +#endif + class BacktraceThreadInterface; struct ThreadEntry { diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp deleted file mode 100644 index 773b0a2..0000000 --- a/libbacktrace/Corkscrew.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <backtrace/Backtrace.h> - -#include <string.h> - -#include <backtrace-arch.h> -#include <corkscrew/backtrace.h> - -#ifndef __USE_GNU -#define __USE_GNU -#endif -#include <dlfcn.h> - -#include "BacktraceLog.h" -#include "Corkscrew.h" - -//------------------------------------------------------------------------- -// CorkscrewMap functions. -//------------------------------------------------------------------------- -CorkscrewMap::CorkscrewMap(pid_t pid) : BacktraceMap(pid), map_info_(NULL) { -} - -CorkscrewMap::~CorkscrewMap() { - if (map_info_) { - free_map_info_list(map_info_); - map_info_ = NULL; - } -} - -bool CorkscrewMap::Build() { - map_info_ = load_map_info_list(pid_); - - // Use the information in map_info_ to construct the BacktraceMap data - // rather than reparsing /proc/self/maps. - map_info_t* cur_map = map_info_; - while (cur_map) { - backtrace_map_t map; - map.start = cur_map->start; - map.end = cur_map->end; - map.flags = 0; - if (cur_map->is_readable) { - map.flags |= PROT_READ; - } - if (cur_map->is_writable) { - map.flags |= PROT_WRITE; - } - if (cur_map->is_executable) { - map.flags |= PROT_EXEC; - } - map.name = cur_map->name; - - // The maps are in descending order, but we want them in ascending order. - maps_.push_front(map); - - cur_map = cur_map->next; - } - return map_info_ != NULL; -} - -//------------------------------------------------------------------------- -// CorkscrewCommon functions. -//------------------------------------------------------------------------- -bool CorkscrewCommon::GenerateFrameData( - backtrace_frame_t* cork_frames, ssize_t num_frames) { - if (num_frames < 0) { - BACK_LOGW("libcorkscrew unwind failed."); - return false; - } - - std::vector<backtrace_frame_data_t>* frames = GetFrames(); - frames->resize(num_frames); - size_t i = 0; - for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin(); - it != frames->end(); ++it, ++i) { - it->num = i; - it->pc = cork_frames[i].absolute_pc; - it->sp = cork_frames[i].stack_top; - it->stack_size = cork_frames[i].stack_size; - it->func_offset = 0; - - it->map = FindMap(it->pc); - it->func_name = GetFunctionName(it->pc, &it->func_offset); - } - return true; -} - -//------------------------------------------------------------------------- -// CorkscrewCurrent functions. -//------------------------------------------------------------------------- -CorkscrewCurrent::CorkscrewCurrent() { -} - -CorkscrewCurrent::~CorkscrewCurrent() { -} - -bool CorkscrewCurrent::Unwind(size_t num_ignore_frames) { - backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; - ssize_t num_frames = unwind_backtrace(frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); - - return GenerateFrameData(frames, num_frames); -} - -std::string CorkscrewCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { - *offset = 0; - - Dl_info info; - const backtrace_map_t* map = FindMap(pc); - if (map) { - if (dladdr((const void*)pc, &info)) { - if (info.dli_sname) { - *offset = pc - map->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase; - return info.dli_sname; - } - } else { - // dladdr(3) didn't find a symbol; maybe it's static? Look in the ELF file... - symbol_table_t* symbol_table = load_symbol_table(map->name.c_str()); - if (symbol_table) { - // First check if we can find the symbol using a relative pc. - std::string name; - const symbol_t* elf_symbol = find_symbol(symbol_table, pc - map->start); - if (elf_symbol) { - name = elf_symbol->name; - *offset = pc - map->start - elf_symbol->start; - } else if ((elf_symbol = find_symbol(symbol_table, pc)) != NULL) { - // Found the symbol using the absolute pc. - name = elf_symbol->name; - *offset = pc - elf_symbol->start; - } - free_symbol_table(symbol_table); - return name; - } - } - } - return ""; -} - -//------------------------------------------------------------------------- -// CorkscrewThread functions. -//------------------------------------------------------------------------- -CorkscrewThread::CorkscrewThread() { -} - -CorkscrewThread::~CorkscrewThread() { -} - -void CorkscrewThread::ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { - backtrace_frame_t cork_frames[MAX_BACKTRACE_FRAMES]; - CorkscrewMap* map = static_cast<CorkscrewMap*>(GetMap()); - ssize_t num_frames = unwind_backtrace_signal_arch( - siginfo, sigcontext, map->GetMapInfo(), cork_frames, - num_ignore_frames, MAX_BACKTRACE_FRAMES); - if (num_frames > 0) { - std::vector<backtrace_frame_data_t>* frames = GetFrames(); - frames->resize(num_frames); - size_t i = 0; - for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin(); - it != frames->end(); ++it, ++i) { - it->num = i; - it->pc = cork_frames[i].absolute_pc; - it->sp = cork_frames[i].stack_top; - it->stack_size = cork_frames[i].stack_size; - it->map = NULL; - it->func_offset = 0; - } - } -} - -//------------------------------------------------------------------------- -// CorkscrewPtrace functions. -//------------------------------------------------------------------------- -CorkscrewPtrace::CorkscrewPtrace() : ptrace_context_(NULL) { -} - -CorkscrewPtrace::~CorkscrewPtrace() { - if (ptrace_context_) { - free_ptrace_context(ptrace_context_); - ptrace_context_ = NULL; - } -} - -bool CorkscrewPtrace::Unwind(size_t num_ignore_frames) { - ptrace_context_ = load_ptrace_context(Tid()); - - backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; - ssize_t num_frames = unwind_backtrace_ptrace( - Tid(), ptrace_context_, frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); - - return GenerateFrameData(frames, num_frames); -} - -std::string CorkscrewPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { - // Get information about a different process. - const map_info_t* map_info; - const symbol_t* symbol; - find_symbol_ptrace(ptrace_context_, pc, &map_info, &symbol); - char* symbol_name = NULL; - if (symbol) { - if (map_info) { - *offset = pc - map_info->start - symbol->start; - } - symbol_name = symbol->name; - return symbol_name; - } - - return ""; -} - -//------------------------------------------------------------------------- -// C++ object creation functions. -//------------------------------------------------------------------------- -Backtrace* CreateCurrentObj(BacktraceMap* map) { - return new BacktraceCurrent(new CorkscrewCurrent(), map); -} - -Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map) { - return new BacktracePtrace(new CorkscrewPtrace(), pid, tid, map); -} - -Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) { - CorkscrewThread* thread_obj = new CorkscrewThread(); - return new BacktraceThread(thread_obj, thread_obj, tid, map); -} - -//------------------------------------------------------------------------- -// BacktraceMap create function. -//------------------------------------------------------------------------- -BacktraceMap* BacktraceMap::Create(pid_t pid) { - BacktraceMap* map = new CorkscrewMap(pid); - if (!map->Build()) { - delete map; - return NULL; - } - return map; -} diff --git a/libbacktrace/Corkscrew.h b/libbacktrace/Corkscrew.h deleted file mode 100644 index 1633398..0000000 --- a/libbacktrace/Corkscrew.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _LIBBACKTRACE_CORKSCREW_H -#define _LIBBACKTRACE_CORKSCREW_H - -#include <inttypes.h> - -#include <string> - -#include <backtrace/Backtrace.h> -#include <backtrace/BacktraceMap.h> - -#include <corkscrew/backtrace.h> - -#include "BacktraceImpl.h" -#include "BacktraceThread.h" - -class CorkscrewMap : public BacktraceMap { -public: - CorkscrewMap(pid_t pid); - virtual ~CorkscrewMap(); - - virtual bool Build(); - - map_info_t* GetMapInfo() { return map_info_; } - -private: - map_info_t* map_info_; -}; - -class CorkscrewCommon : public BacktraceImpl { -public: - bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames); -}; - -class CorkscrewCurrent : public CorkscrewCommon { -public: - CorkscrewCurrent(); - virtual ~CorkscrewCurrent(); - - virtual bool Unwind(size_t num_ignore_threads); - - virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); -}; - -class CorkscrewThread : public CorkscrewCurrent, public BacktraceThreadInterface { -public: - CorkscrewThread(); - virtual ~CorkscrewThread(); - - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); -}; - -class CorkscrewPtrace : public CorkscrewCommon { -public: - CorkscrewPtrace(); - virtual ~CorkscrewPtrace(); - - virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); - - virtual bool Unwind(size_t num_ignore_threads); - -private: - ptrace_context_t* ptrace_context_; -}; - -#endif // _LIBBACKTRACE_CORKSCREW_H diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index a5e141b..9744922 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp @@ -33,6 +33,9 @@ #include <backtrace/BacktraceMap.h> #include <UniquePtr.h> +// For the THREAD_SIGNAL definition. +#include "BacktraceThread.h" + #include <cutils/atomic.h> #include <gtest/gtest.h> @@ -460,9 +463,15 @@ TEST(libbacktrace, thread_level_trace) { // Wait up to 2 seconds for the tid to be set. ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + // Make sure that the thread signal used is not visible when compiled for + // the target. +#if !defined(__GLIBC__) + ASSERT_LT(THREAD_SIGNAL, SIGRTMIN); +#endif + // Save the current signal action and make sure it is restored afterwards. struct sigaction cur_action; - ASSERT_TRUE(sigaction(SIGURG, NULL, &cur_action) == 0); + ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &cur_action) == 0); UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); ASSERT_TRUE(backtrace.get() != NULL); @@ -475,7 +484,7 @@ TEST(libbacktrace, thread_level_trace) { // Verify that the old action was restored. struct sigaction new_action; - ASSERT_TRUE(sigaction(SIGURG, NULL, &new_action) == 0); + ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &new_action) == 0); EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction); EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags); } diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk deleted file mode 100644 index 8f3b68c..0000000 --- a/libcorkscrew/Android.mk +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) - -generic_src_files := \ - backtrace.c \ - backtrace-helper.c \ - demangle.c \ - map_info.c \ - ptrace.c \ - symbol_table.c - -arm_src_files := \ - arch-arm/backtrace-arm.c \ - arch-arm/ptrace-arm.c - -x86_src_files := \ - arch-x86/backtrace-x86.c \ - arch-x86/ptrace-x86.c - -ifneq ($(TARGET_IS_64_BIT),true) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(generic_src_files) - -ifeq ($(TARGET_ARCH),arm) -LOCAL_SRC_FILES += $(arm_src_files) -LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH -endif -ifeq ($(TARGET_ARCH),x86) -LOCAL_SRC_FILES += $(x86_src_files) -LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH -endif -ifeq ($(TARGET_ARCH),mips) -LOCAL_SRC_FILES += \ - arch-mips/backtrace-mips.c \ - arch-mips/ptrace-mips.c -LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH -endif - -LOCAL_SHARED_LIBRARIES += libdl libcutils liblog libgccdemangle - -LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter -LOCAL_MODULE := libcorkscrew -LOCAL_MODULE_TAGS := optional - -include $(BUILD_SHARED_LIBRARY) - -# Build test. -include $(CLEAR_VARS) -LOCAL_SRC_FILES := test.cpp -LOCAL_CFLAGS += -Werror -fno-inline-small-functions -LOCAL_SHARED_LIBRARIES := libcorkscrew -LOCAL_MODULE := libcorkscrew_test -LOCAL_MODULE_TAGS := optional -include $(BUILD_EXECUTABLE) - -endif # TARGET_IS_64_BIT == false - - -ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86) - -# Build libcorkscrew. -include $(CLEAR_VARS) -LOCAL_SRC_FILES += $(generic_src_files) $(x86_src_files) -LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH -LOCAL_STATIC_LIBRARIES += libcutils liblog -LOCAL_LDLIBS += -ldl -ifeq ($(HOST_OS),linux) - LOCAL_SHARED_LIBRARIES += libgccdemangle # TODO: is this even needed on Linux? - LOCAL_LDLIBS += -lrt -endif -LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter -LOCAL_MODULE := libcorkscrew -LOCAL_MODULE_TAGS := optional -include $(BUILD_HOST_SHARED_LIBRARY) - -# Build test. -include $(CLEAR_VARS) -LOCAL_SRC_FILES := test.cpp -LOCAL_CFLAGS += -Werror -LOCAL_SHARED_LIBRARIES := libcorkscrew -LOCAL_MODULE := libcorkscrew_test -LOCAL_MODULE_TAGS := optional -include $(BUILD_HOST_EXECUTABLE) - -endif # $(HOST_OS)-$(HOST_ARCH) == linux-x86 diff --git a/libcorkscrew/MODULE_LICENSE_APACHE2 b/libcorkscrew/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29..0000000 --- a/libcorkscrew/MODULE_LICENSE_APACHE2 +++ /dev/null diff --git a/libcorkscrew/NOTICE b/libcorkscrew/NOTICE deleted file mode 100644 index becc120..0000000 --- a/libcorkscrew/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2011, The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/libcorkscrew/arch-arm/backtrace-arm.c b/libcorkscrew/arch-arm/backtrace-arm.c deleted file mode 100644 index 751efbf..0000000 --- a/libcorkscrew/arch-arm/backtrace-arm.c +++ /dev/null @@ -1,589 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Backtracing functions for ARM. - * - * This implementation uses the exception unwinding tables provided by - * the compiler to unwind call frames. Refer to the ARM Exception Handling ABI - * documentation (EHABI) for more details about what's going on here. - * - * An ELF binary may contain an EXIDX section that provides an index to - * the exception handling table of each function, sorted by program - * counter address. - * - * This implementation also supports unwinding other processes via ptrace(). - * In that case, the EXIDX section is found by reading the ELF section table - * structures using ptrace(). - * - * Because the tables are used for exception handling, it can happen that - * a given function will not have an exception handling table. In particular, - * exceptions are assumed to only ever be thrown at call sites. Therefore, - * by definition leaf functions will not have exception handling tables. - * This may make unwinding impossible in some cases although we can still get - * some idea of the call stack by examining the PC and LR registers. - * - * As we are only interested in backtrace information, we do not need - * to perform all of the work of unwinding such as restoring register - * state and running cleanup functions. Unwinding is performed virtually on - * an abstract machine context consisting of just the ARM core registers. - * Furthermore, we do not run generic "personality functions" because - * we may not be in a position to execute arbitrary code, especially if - * we are running in a signal handler or using ptrace()! - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../backtrace-arch.h" -#include "../backtrace-helper.h" -#include "../ptrace-arch.h" -#include <corkscrew/ptrace.h> - -#include <stdlib.h> -#include <signal.h> -#include <stdbool.h> -#include <limits.h> -#include <errno.h> -#include <sys/ptrace.h> -#include <elf.h> -#include <cutils/log.h> - -#include <ucontext.h> - -/* Unwind state. */ -typedef struct { - uint32_t gregs[16]; -} unwind_state_t; - -static const int R_SP = 13; -static const int R_LR = 14; -static const int R_PC = 15; - -/* Special EXIDX value that indicates that a frame cannot be unwound. */ -static const uint32_t EXIDX_CANTUNWIND = 1; - -/* Get the EXIDX section start and size for the module that contains a - * given program counter address. - * - * When the executable is statically linked, the EXIDX section can be - * accessed by querying the values of the __exidx_start and __exidx_end - * symbols. - * - * When the executable is dynamically linked, the linker exports a function - * called dl_unwind_find_exidx that obtains the EXIDX section for a given - * absolute program counter address. - * - * Bionic exports a helpful function called __gnu_Unwind_Find_exidx that - * handles both cases, so we use that here. - */ -typedef long unsigned int* _Unwind_Ptr; -extern _Unwind_Ptr __gnu_Unwind_Find_exidx(_Unwind_Ptr pc, int *pcount); - -static uintptr_t find_exidx(uintptr_t pc, size_t* out_exidx_size) { - int count; - uintptr_t start = (uintptr_t)__gnu_Unwind_Find_exidx((_Unwind_Ptr)pc, &count); - *out_exidx_size = count; - return start; -} - -/* Transforms a 31-bit place-relative offset to an absolute address. - * We assume the most significant bit is clear. */ -static uintptr_t prel_to_absolute(uintptr_t place, uint32_t prel_offset) { - return place + (((int32_t)(prel_offset << 1)) >> 1); -} - -static uintptr_t get_exception_handler(const memory_t* memory, - const map_info_t* map_info_list, uintptr_t pc) { - if (!pc) { - ALOGV("get_exception_handler: pc is zero, no handler"); - return 0; - } - - uintptr_t exidx_start; - size_t exidx_size; - const map_info_t* mi; - if (memory->tid < 0) { - mi = NULL; - exidx_start = find_exidx(pc, &exidx_size); - } else { - mi = find_map_info(map_info_list, pc); - if (mi && mi->data) { - const map_info_data_t* data = (const map_info_data_t*)mi->data; - exidx_start = data->exidx_start; - exidx_size = data->exidx_size; - } else { - exidx_start = 0; - exidx_size = 0; - } - } - - uintptr_t handler = 0; - int32_t handler_index = -1; - if (exidx_start) { - uint32_t low = 0; - uint32_t high = exidx_size; - while (low < high) { - uint32_t index = (low + high) / 2; - uintptr_t entry = exidx_start + index * 8; - uint32_t entry_prel_pc; - ALOGV("XXX low=%u, high=%u, index=%u", low, high, index); - if (!try_get_word(memory, entry, &entry_prel_pc)) { - break; - } - uintptr_t entry_pc = prel_to_absolute(entry, entry_prel_pc); - ALOGV("XXX entry_pc=0x%08x", entry_pc); - if (pc < entry_pc) { - high = index; - continue; - } - if (index + 1 < exidx_size) { - uintptr_t next_entry = entry + 8; - uint32_t next_entry_prel_pc; - if (!try_get_word(memory, next_entry, &next_entry_prel_pc)) { - break; - } - uintptr_t next_entry_pc = prel_to_absolute(next_entry, next_entry_prel_pc); - ALOGV("XXX next_entry_pc=0x%08x", next_entry_pc); - if (pc >= next_entry_pc) { - low = index + 1; - continue; - } - } - - uintptr_t entry_handler_ptr = entry + 4; - uint32_t entry_handler; - if (!try_get_word(memory, entry_handler_ptr, &entry_handler)) { - break; - } - if (entry_handler & (1L << 31)) { - handler = entry_handler_ptr; // in-place handler data - } else if (entry_handler != EXIDX_CANTUNWIND) { - handler = prel_to_absolute(entry_handler_ptr, entry_handler); - } - handler_index = index; - break; - } - } - if (mi) { - ALOGV("get_exception_handler: pc=0x%08x, module='%s', module_start=0x%08x, " - "exidx_start=0x%08x, exidx_size=%d, handler=0x%08x, handler_index=%d", - pc, mi->name, mi->start, exidx_start, exidx_size, handler, handler_index); - } else { - ALOGV("get_exception_handler: pc=0x%08x, " - "exidx_start=0x%08x, exidx_size=%d, handler=0x%08x, handler_index=%d", - pc, exidx_start, exidx_size, handler, handler_index); - } - return handler; -} - -typedef struct { - uintptr_t ptr; - uint32_t word; -} byte_stream_t; - -static bool try_next_byte(const memory_t* memory, byte_stream_t* stream, uint8_t* out_value) { - uint8_t result; - switch (stream->ptr & 3) { - case 0: - if (!try_get_word(memory, stream->ptr, &stream->word)) { - *out_value = 0; - return false; - } - *out_value = stream->word >> 24; - break; - - case 1: - *out_value = stream->word >> 16; - break; - - case 2: - *out_value = stream->word >> 8; - break; - - default: - *out_value = stream->word; - break; - } - - ALOGV("next_byte: ptr=0x%08x, value=0x%02x", stream->ptr, *out_value); - stream->ptr += 1; - return true; -} - -static void set_reg(unwind_state_t* state, uint32_t reg, uint32_t value) { - ALOGV("set_reg: reg=%d, value=0x%08x", reg, value); - state->gregs[reg] = value; -} - -static bool try_pop_registers(const memory_t* memory, unwind_state_t* state, uint32_t mask) { - uint32_t sp = state->gregs[R_SP]; - bool sp_updated = false; - for (int i = 0; i < 16; i++) { - if (mask & (1 << i)) { - uint32_t value; - if (!try_get_word(memory, sp, &value)) { - return false; - } - if (i == R_SP) { - sp_updated = true; - } - set_reg(state, i, value); - sp += 4; - } - } - if (!sp_updated) { - set_reg(state, R_SP, sp); - } - return true; -} - -/* Executes a built-in personality routine as defined in the EHABI. - * Returns true if unwinding should continue. - * - * The data for the built-in personality routines consists of a sequence - * of unwinding instructions, followed by a sequence of scope descriptors, - * each of which has a length and offset encoded using 16-bit or 32-bit - * values. - * - * We only care about the unwinding instructions. They specify the - * operations of an abstract machine whose purpose is to transform the - * virtual register state (including the stack pointer) such that - * the call frame is unwound and the PC register points to the call site. - */ -static bool execute_personality_routine(const memory_t* memory, - unwind_state_t* state, byte_stream_t* stream, int pr_index) { - size_t size; - switch (pr_index) { - case 0: // Personality routine #0, short frame, descriptors have 16-bit scope. - size = 3; - break; - case 1: // Personality routine #1, long frame, descriptors have 16-bit scope. - case 2: { // Personality routine #2, long frame, descriptors have 32-bit scope. - uint8_t size_byte; - if (!try_next_byte(memory, stream, &size_byte)) { - return false; - } - size = (uint32_t)size_byte * sizeof(uint32_t) + 2; - break; - } - default: // Unknown personality routine. Stop here. - return false; - } - - bool pc_was_set = false; - while (size--) { - uint8_t op; - if (!try_next_byte(memory, stream, &op)) { - return false; - } - if ((op & 0xc0) == 0x00) { - // "vsp = vsp + (xxxxxx << 2) + 4" - set_reg(state, R_SP, state->gregs[R_SP] + ((op & 0x3f) << 2) + 4); - } else if ((op & 0xc0) == 0x40) { - // "vsp = vsp - (xxxxxx << 2) - 4" - set_reg(state, R_SP, state->gregs[R_SP] - ((op & 0x3f) << 2) - 4); - } else if ((op & 0xf0) == 0x80) { - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - uint32_t mask = (((uint32_t)op & 0x0f) << 12) | ((uint32_t)op2 << 4); - if (mask) { - // "Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}" - if (!try_pop_registers(memory, state, mask)) { - return false; - } - if (mask & (1 << R_PC)) { - pc_was_set = true; - } - } else { - // "Refuse to unwind" - return false; - } - } else if ((op & 0xf0) == 0x90) { - if (op != 0x9d && op != 0x9f) { - // "Set vsp = r[nnnn]" - set_reg(state, R_SP, state->gregs[op & 0x0f]); - } else { - // "Reserved as prefix for ARM register to register moves" - // "Reserved as prefix for Intel Wireless MMX register to register moves" - return false; - } - } else if ((op & 0xf8) == 0xa0) { - // "Pop r4-r[4+nnn]" - uint32_t mask = (0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0; - if (!try_pop_registers(memory, state, mask)) { - return false; - } - } else if ((op & 0xf8) == 0xa8) { - // "Pop r4-r[4+nnn], r14" - uint32_t mask = ((0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0) | 0x4000; - if (!try_pop_registers(memory, state, mask)) { - return false; - } - } else if (op == 0xb0) { - // "Finish" - break; - } else if (op == 0xb1) { - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - if (op2 != 0x00 && (op2 & 0xf0) == 0x00) { - // "Pop integer registers under mask {r3, r2, r1, r0}" - if (!try_pop_registers(memory, state, op2)) { - return false; - } - } else { - // "Spare" - return false; - } - } else if (op == 0xb2) { - // "vsp = vsp + 0x204 + (uleb128 << 2)" - uint32_t value = 0; - uint32_t shift = 0; - uint8_t op2; - do { - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - value |= (op2 & 0x7f) << shift; - shift += 7; - } while (op2 & 0x80); - set_reg(state, R_SP, state->gregs[R_SP] + (value << 2) + 0x204); - } else if (op == 0xb3) { - // "Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDX" - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 12); - } else if ((op & 0xf8) == 0xb8) { - // "Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDX" - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 12); - } else if ((op & 0xf8) == 0xc0) { - // "Intel Wireless MMX pop wR[10]-wR[10+nnn]" - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 8); - } else if (op == 0xc6) { - // "Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]" - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8); - } else if (op == 0xc7) { - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - if (op2 != 0x00 && (op2 & 0xf0) == 0x00) { - // "Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}" - set_reg(state, R_SP, state->gregs[R_SP] + __builtin_popcount(op2) * 4); - } else { - // "Spare" - return false; - } - } else if (op == 0xc8) { - // "Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] - // saved (as if) by FSTMFD" - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8); - } else if (op == 0xc9) { - // "Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDD" - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8); - } else if ((op == 0xf8) == 0xd0) { - // "Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDD" - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 8); - } else { - // "Spare" - return false; - } - } - if (!pc_was_set) { - set_reg(state, R_PC, state->gregs[R_LR]); - } - return true; -} - -static bool try_get_half_word(const memory_t* memory, uint32_t pc, uint16_t* out_value) { - uint32_t word; - if (try_get_word(memory, pc & ~2, &word)) { - *out_value = pc & 2 ? word >> 16 : word & 0xffff; - return true; - } - return false; -} - -uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) { - if (pc & 1) { - /* Thumb mode - need to check whether the bl(x) has long offset or not. - * Examples: - * - * arm blx in the middle of thumb: - * 187ae: 2300 movs r3, #0 - * 187b0: f7fe ee1c blx 173ec - * 187b4: 2c00 cmp r4, #0 - * - * arm bl in the middle of thumb: - * 187d8: 1c20 adds r0, r4, #0 - * 187da: f136 fd15 bl 14f208 - * 187de: 2800 cmp r0, #0 - * - * pure thumb: - * 18894: 189b adds r3, r3, r2 - * 18896: 4798 blx r3 - * 18898: b001 add sp, #4 - */ - uint16_t prev1, prev2; - if (try_get_half_word(memory, pc - 5, &prev1) - && ((prev1 & 0xf000) == 0xf000) - && try_get_half_word(memory, pc - 3, &prev2) - && ((prev2 & 0xe000) == 0xe000)) { - pc -= 4; // long offset - } else { - pc -= 2; - } - } else { - /* ARM mode, all instructions are 32bit. Yay! */ - pc -= 4; - } - return pc; -} - -static ssize_t unwind_backtrace_common(const memory_t* memory, - const map_info_t* map_info_list, - unwind_state_t* state, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth) { - size_t ignored_frames = 0; - size_t returned_frames = 0; - - for (size_t index = 0; returned_frames < max_depth; index++) { - uintptr_t pc = index ? rewind_pc_arch(memory, state->gregs[R_PC]) - : state->gregs[R_PC]; - backtrace_frame_t* frame = add_backtrace_entry(pc, - backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames); - if (frame) { - frame->stack_top = state->gregs[R_SP]; - } - - uintptr_t handler = get_exception_handler(memory, map_info_list, pc); - if (!handler) { - // If there is no handler for the PC and this is the first frame, - // then the program may have branched to an invalid address. - // Try starting from the LR instead, otherwise stop unwinding. - if (index == 0 && state->gregs[R_LR] - && state->gregs[R_LR] != state->gregs[R_PC]) { - set_reg(state, R_PC, state->gregs[R_LR]); - continue; - } else { - break; - } - } - - byte_stream_t stream; - stream.ptr = handler; - uint8_t pr; - if (!try_next_byte(memory, &stream, &pr)) { - break; - } - if ((pr & 0xf0) != 0x80) { - // The first word is a place-relative pointer to a generic personality - // routine function. We don't support invoking such functions, so stop here. - break; - } - - // The first byte indicates the personality routine to execute. - // Following bytes provide instructions to the personality routine. - if (!execute_personality_routine(memory, state, &stream, pr & 0x0f)) { - break; - } - if (frame && state->gregs[R_SP] > frame->stack_top) { - frame->stack_size = state->gregs[R_SP] - frame->stack_top; - } - if (!state->gregs[R_PC]) { - break; - } - } - - // Ran out of frames that we could unwind using handlers. - // Add a final entry for the LR if it looks sane and call it good. - if (returned_frames < max_depth - && state->gregs[R_LR] - && state->gregs[R_LR] != state->gregs[R_PC] - && is_executable_map(map_info_list, state->gregs[R_LR])) { - // We don't know where the stack for this extra frame starts so we - // don't return any stack information for it. - add_backtrace_entry(rewind_pc_arch(memory, state->gregs[R_LR]), - backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames); - } - return returned_frames; -} - -ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext, - const map_info_t* map_info_list, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - const ucontext_t* uc = (const ucontext_t*)sigcontext; - - unwind_state_t state; - - state.gregs[0] = uc->uc_mcontext.arm_r0; - state.gregs[1] = uc->uc_mcontext.arm_r1; - state.gregs[2] = uc->uc_mcontext.arm_r2; - state.gregs[3] = uc->uc_mcontext.arm_r3; - state.gregs[4] = uc->uc_mcontext.arm_r4; - state.gregs[5] = uc->uc_mcontext.arm_r5; - state.gregs[6] = uc->uc_mcontext.arm_r6; - state.gregs[7] = uc->uc_mcontext.arm_r7; - state.gregs[8] = uc->uc_mcontext.arm_r8; - state.gregs[9] = uc->uc_mcontext.arm_r9; - state.gregs[10] = uc->uc_mcontext.arm_r10; - state.gregs[11] = uc->uc_mcontext.arm_fp; - state.gregs[12] = uc->uc_mcontext.arm_ip; - state.gregs[13] = uc->uc_mcontext.arm_sp; - state.gregs[14] = uc->uc_mcontext.arm_lr; - state.gregs[15] = uc->uc_mcontext.arm_pc; - - memory_t memory; - init_memory(&memory, map_info_list); - return unwind_backtrace_common(&memory, map_info_list, &state, - backtrace, ignore_depth, max_depth); -} - -ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - struct pt_regs regs; - if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { - return -1; - } - - unwind_state_t state; - for (int i = 0; i < 16; i++) { - state.gregs[i] = regs.uregs[i]; - } - - memory_t memory; - init_memory_ptrace(&memory, tid); - return unwind_backtrace_common(&memory, context->map_info_list, &state, - backtrace, ignore_depth, max_depth); -} diff --git a/libcorkscrew/arch-arm/ptrace-arm.c b/libcorkscrew/arch-arm/ptrace-arm.c deleted file mode 100644 index a50844e..0000000 --- a/libcorkscrew/arch-arm/ptrace-arm.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../ptrace-arch.h" - -#include <elf.h> -#include <cutils/log.h> - -#ifndef PT_ARM_EXIDX -#define PT_ARM_EXIDX 0x70000001 -#endif - -static void load_exidx_header(pid_t pid, map_info_t* mi, - uintptr_t* out_exidx_start, size_t* out_exidx_size) { - uint32_t elf_phoff; - uint32_t elf_phentsize_ehsize; - uint32_t elf_shentsize_phnum; - if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), - &elf_phentsize_ehsize) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), - &elf_shentsize_phnum)) { - uint32_t elf_phentsize = elf_phentsize_ehsize >> 16; - uint32_t elf_phnum = elf_shentsize_phnum & 0xffff; - for (uint32_t i = 0; i < elf_phnum; i++) { - uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize; - uint32_t elf_phdr_type; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) { - break; - } - if (elf_phdr_type == PT_ARM_EXIDX) { - uint32_t elf_phdr_offset; - uint32_t elf_phdr_filesz; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset), - &elf_phdr_offset) - || !try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_filesz), - &elf_phdr_filesz)) { - break; - } - *out_exidx_start = mi->start + elf_phdr_offset; - *out_exidx_size = elf_phdr_filesz / 8; - ALOGV("Parsed EXIDX header info for %s: start=0x%08x, size=%d", mi->name, - *out_exidx_start, *out_exidx_size); - return; - } - } - } - *out_exidx_start = 0; - *out_exidx_size = 0; -} - -void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) { - load_exidx_header(pid, mi, &data->exidx_start, &data->exidx_size); -} - -void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data) { -} diff --git a/libcorkscrew/arch-mips/backtrace-mips.c b/libcorkscrew/arch-mips/backtrace-mips.c deleted file mode 100644 index 832fb86..0000000 --- a/libcorkscrew/arch-mips/backtrace-mips.c +++ /dev/null @@ -1,901 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Backtracing functions for mips - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../backtrace-arch.h" -#include "../backtrace-helper.h" -#include "../ptrace-arch.h" -#include <corkscrew/ptrace.h> -#include "dwarf.h" - -#include <stdlib.h> -#include <signal.h> -#include <stdbool.h> -#include <limits.h> -#include <errno.h> -#include <string.h> -#include <sys/ptrace.h> -#include <cutils/log.h> - -#include <sys/ucontext.h> - -/* For PTRACE_GETREGS */ -typedef struct { - uint64_t regs[32]; - uint64_t lo; - uint64_t hi; - uint64_t epc; - uint64_t badvaddr; - uint64_t status; - uint64_t cause; -} user_regs_struct; - -enum { - REG_ZERO = 0, REG_AT, REG_V0, REG_V1, - REG_A0, REG_A1, REG_A2, REG_A3, - REG_T0, REG_T1, REG_T2, REG_T3, - REG_T4, REG_T5, REG_T6, REG_T7, - REG_S0, REG_S1, REG_S2, REG_S3, - REG_S4, REG_S5, REG_S6, REG_S7, - REG_T8, REG_T9, REG_K0, REG_K1, - REG_GP, REG_SP, REG_S8, REG_RA, -}; - - -/* Unwind state. */ -typedef struct { - uint32_t reg[DWARF_REGISTERS]; -} unwind_state_t; - -uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) { - if (pc == 0) - return pc; - if ((pc & 1) == 0) - return pc-8; /* jal/bal/jalr + branch delay slot */ - return pc; -} - -/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */ -static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) { - static uintptr_t lastptr; - static uint32_t buf; - - ptr += *cursor; - - if (ptr < lastptr || lastptr + 3 < ptr) { - lastptr = (ptr >> 2) << 2; - if (!try_get_word(memory, lastptr, &buf)) { - return false; - } - } - *out_value = (uint8_t)((buf >> ((ptr & 3) * 8)) & 0xff); - ++*cursor; - return true; -} - -/* Getting X bytes. 4 is maximum for now. */ -static bool try_get_xbytes(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t bytes, uint32_t* cursor) { - uint32_t data = 0; - if (bytes > 4) { - ALOGE("can't read more than 4 bytes, trying to read %d", bytes); - return false; - } - for (int i = 0; i < bytes; i++) { - uint8_t buf; - if (!try_get_byte(memory, ptr, &buf, cursor)) { - return false; - } - data |= (uint32_t)buf << (i * 8); - } - *out_value = data; - return true; -} - -/* Reads signed/unsigned LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_leb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor, bool sign_extend) { - uint8_t buf = 0; - uint32_t val = 0; - uint8_t c = 0; - do { - if (!try_get_byte(memory, ptr, &buf, cursor)) { - return false; - } - val |= ((uint32_t)buf & 0x7f) << (c * 7); - c++; - } while (buf & 0x80 && (c * 7) <= 32); - if (c * 7 > 32) { - ALOGE("%s: data exceeds expected 4 bytes maximum", __FUNCTION__); - return false; - } - if (sign_extend) { - if (buf & 0x40) { - val |= ((uint32_t)-1 << (c * 7)); - } - } - *out_value = val; - return true; -} - -/* Reads signed LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_sleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) { - return try_get_leb128(memory, ptr, out_value, cursor, true); -} - -/* Reads unsigned LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_uleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) { - return try_get_leb128(memory, ptr, out_value, cursor, false); -} - -/* Getting data encoded by dwarf encodings. */ -static bool read_dwarf(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t encoding, uint32_t* cursor) { - uint32_t data = 0; - bool issigned = true; - uintptr_t addr = ptr + *cursor; - /* Lower 4 bits is data type/size */ - /* TODO: add more encodings if it becomes necessary */ - switch (encoding & 0xf) { - case DW_EH_PE_absptr: - if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) { - return false; - } - *out_value = data; - return true; - case DW_EH_PE_udata4: - issigned = false; - case DW_EH_PE_sdata4: - if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) { - return false; - } - break; - default: - ALOGE("unrecognized dwarf lower part encoding: 0x%x", encoding); - return false; - } - /* Higher 4 bits is modifier */ - /* TODO: add more encodings if it becomes necessary */ - switch (encoding & 0xf0) { - case 0: - *out_value = data; - break; - case DW_EH_PE_pcrel: - if (issigned) { - *out_value = addr + (int32_t)data; - } else { - *out_value = addr + data; - } - break; - /* Assuming ptr is correct base to calculate datarel */ - case DW_EH_PE_datarel: - if (issigned) { - *out_value = ptr + (int32_t)data; - } else { - *out_value = ptr + data; - } - break; - default: - ALOGE("unrecognized dwarf higher part encoding: 0x%x", encoding); - return false; - } - return true; -} - -/* Having PC find corresponding FDE by reading .eh_frame_hdr section data. */ -static uintptr_t find_fde(const memory_t* memory, - const map_info_t* map_info_list, uintptr_t pc) { - if (!pc) { - ALOGV("find_fde: pc is zero, no eh_frame"); - return 0; - } - const map_info_t* mi = find_map_info(map_info_list, pc); - if (!mi) { - ALOGV("find_fde: no map info for pc:0x%x", pc); - return 0; - } - const map_info_data_t* midata = mi->data; - if (!midata) { - ALOGV("find_fde: no eh_frame_hdr for map: start=0x%x, end=0x%x", mi->start, mi->end); - return 0; - } - - eh_frame_hdr_info_t eh_hdr_info; - memset(&eh_hdr_info, 0, sizeof(eh_frame_hdr_info_t)); - - /* Getting the first word of eh_frame_hdr: - 1st byte is version; - 2nd byte is encoding of pointer to eh_frames; - 3rd byte is encoding of count of FDEs in lookup table; - 4th byte is encoding of lookup table entries. - */ - uintptr_t eh_frame_hdr = midata->eh_frame_hdr; - uint32_t c = 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.version, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr_enc, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_count_enc, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_table_enc, &c)) return 0; - - /* TODO: 3rd byte can be DW_EH_PE_omit, that means no lookup table available and we should - try to parse eh_frame instead. Not sure how often it may occur, skipping now. - */ - if (eh_hdr_info.version != 1) { - ALOGV("find_fde: eh_frame_hdr version %d is not supported", eh_hdr_info.version); - return 0; - } - /* Getting the data: - 2nd word is eh_frame pointer (normally not used, because lookup table has all we need); - 3rd word is count of FDEs in the lookup table; - starting from 4 word there is FDE lookup table (pairs of PC and FDE pointer) sorted by PC; - */ - if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr, eh_hdr_info.eh_frame_ptr_enc, &c)) return 0; - if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.fde_count, eh_hdr_info.fde_count_enc, &c)) return 0; - ALOGV("find_fde: found %d FDEs", eh_hdr_info.fde_count); - - int32_t low = 0; - int32_t high = eh_hdr_info.fde_count; - uintptr_t start = 0; - uintptr_t fde = 0; - /* eh_frame_hdr + c points to lookup table at this point. */ - while (low <= high) { - uint32_t mid = (high + low)/2; - uint32_t entry = c + mid * 8; - if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &entry)) return 0; - if (pc <= start) { - high = mid - 1; - } else { - low = mid + 1; - } - } - /* Value found is at high. */ - if (high < 0) { - ALOGV("find_fde: pc %x is out of FDE bounds: %x", pc, start); - return 0; - } - c += high * 8; - if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &c)) return 0; - if (!read_dwarf(memory, eh_frame_hdr, &fde, eh_hdr_info.fde_table_enc, &c)) return 0; - ALOGV("pc 0x%x, ENTRY %d: start=0x%x, fde=0x%x", pc, high, start, fde); - return fde; -} - -/* Execute single dwarf instruction and update dwarf state accordingly. */ -static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie_info, - dwarf_state_t* dstate, uint32_t* cursor, - dwarf_state_t* stack, uint8_t* stack_ptr) { - uint8_t inst; - uint8_t op = 0; - - if (!try_get_byte(memory, ptr, &inst, cursor)) { - return false; - } - ALOGV("DW_CFA inst: 0x%x", inst); - - /* For some instructions upper 2 bits is opcode and lower 6 bits is operand. See dwarf-2.0 7.23. */ - if (inst & 0xc0) { - op = inst & 0x3f; - inst &= 0xc0; - } - - switch ((dwarf_CFA)inst) { - uint32_t reg = 0; - uint32_t offset = 0; - case DW_CFA_advance_loc: - dstate->loc += op * cie_info->code_align; - ALOGV("DW_CFA_advance_loc: %d to 0x%x", op, dstate->loc); - break; - case DW_CFA_offset: - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - dstate->regs[op].rule = 'o'; - dstate->regs[op].value = offset * cie_info->data_align; - ALOGV("DW_CFA_offset: r%d = o(%d)", op, dstate->regs[op].value); - break; - case DW_CFA_restore: - dstate->regs[op].rule = stack->regs[op].rule; - dstate->regs[op].value = stack->regs[op].value; - ALOGV("DW_CFA_restore: r%d = %c(%d)", op, dstate->regs[op].rule, dstate->regs[op].value); - break; - case DW_CFA_nop: - break; - case DW_CFA_set_loc: // probably we don't have it on mips. - if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false; - if (offset < dstate->loc) { - ALOGE("DW_CFA_set_loc: attempt to move location backward"); - return false; - } - dstate->loc = offset * cie_info->code_align; - ALOGV("DW_CFA_set_loc: %d to 0x%x", offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc1: - if (!try_get_byte(memory, ptr, (uint8_t*)&offset, cursor)) return false; - dstate->loc += (uint8_t)offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc1: %d to 0x%x", (uint8_t)offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc2: - if (!try_get_xbytes(memory, ptr, &offset, 2, cursor)) return false; - dstate->loc += (uint16_t)offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc2: %d to 0x%x", (uint16_t)offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc4: - if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false; - dstate->loc += offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc4: %d to 0x%x", offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_offset_extended: // probably we don't have it on mips. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'o'; - dstate->regs[reg].value = offset * cie_info->data_align; - ALOGV("DW_CFA_offset_extended: r%d = o(%d)", reg, dstate->regs[reg].value); - break; - case DW_CFA_restore_extended: // probably we don't have it on mips. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = stack->regs[reg].rule; - dstate->regs[reg].value = stack->regs[reg].value; - ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value); - break; - case DW_CFA_undefined: // probably we don't have it on mips. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'u'; - dstate->regs[reg].value = 0; - ALOGV("DW_CFA_undefined: r%d", reg); - break; - case DW_CFA_same_value: // probably we don't have it on mips. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 's'; - dstate->regs[reg].value = 0; - ALOGV("DW_CFA_same_value: r%d", reg); - break; - case DW_CFA_register: // probably we don't have it on mips. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - /* that's new register actually, not offset */ - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - if (reg >= DWARF_REGISTERS || offset >= DWARF_REGISTERS) { - ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'r'; - dstate->regs[reg].value = offset; - ALOGV("DW_CFA_register: r%d = r(%d)", reg, dstate->regs[reg].value); - break; - case DW_CFA_remember_state: - if (*stack_ptr == DWARF_STATES_STACK) { - ALOGE("DW_CFA_remember_state: states stack overflow %d", *stack_ptr); - return false; - } - stack[(*stack_ptr)++] = *dstate; - ALOGV("DW_CFA_remember_state: stacktop moves to %d", *stack_ptr); - break; - case DW_CFA_restore_state: - /* We have CIE state saved at 0 position. It's not supposed to be taken - by DW_CFA_restore_state. */ - if (*stack_ptr == 1) { - ALOGE("DW_CFA_restore_state: states stack is empty"); - return false; - } - /* Don't touch location on restore. */ - uintptr_t saveloc = dstate->loc; - *dstate = stack[--*stack_ptr]; - dstate->loc = saveloc; - ALOGV("DW_CFA_restore_state: stacktop moves to %d", *stack_ptr); - break; - case DW_CFA_def_cfa: - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - dstate->cfa_reg = reg; - dstate->cfa_off = offset; - ALOGV("DW_CFA_def_cfa: %x(r%d)", offset, reg); - break; - case DW_CFA_def_cfa_register: - if (!try_get_uleb128(memory, ptr, ®, cursor)) { - return false; - } - dstate->cfa_reg = reg; - ALOGV("DW_CFA_def_cfa_register: r%d", reg); - break; - case DW_CFA_def_cfa_offset: - if (!try_get_uleb128(memory, ptr, &offset, cursor)) { - return false; - } - dstate->cfa_off = offset; - ALOGV("DW_CFA_def_cfa_offset: %x", offset); - break; - default: - ALOGE("unrecognized DW_CFA_* instruction: 0x%x", inst); - return false; - } - return true; -} - -/* Restoring particular register value based on dwarf state. */ -static bool get_old_register_value(const memory_t* memory, uint32_t cfa, - dwarf_state_t* dstate, uint8_t reg, - unwind_state_t* state, unwind_state_t* newstate) { - uint32_t addr; - switch (dstate->regs[reg].rule) { - case 0: - /* We don't have dstate updated for this register, so assuming value kept the same. - Normally we should look into state and return current value as the old one - but we don't have all registers in state to handle this properly */ - ALOGV("get_old_register_value: value of r%d is the same", reg); - // for SP if it's not updated by dwarf rule we assume it's equal to CFA - // for PC if it's not updated by dwarf rule we assume it's equal to RA - if (reg == DWARF_SP) { - ALOGV("get_old_register_value: adjusting sp to CFA: 0x%x", cfa); - newstate->reg[reg] = cfa; - } else if (reg == DWARF_PC) { - ALOGV("get_old_register_value: adjusting PC to RA: 0x%x", newstate->reg[DWARF_RA]); - newstate->reg[reg] = newstate->reg[DWARF_RA]; - } else { - newstate->reg[reg] = state->reg[reg]; - } - break; - case 'o': - addr = cfa + (int32_t)dstate->regs[reg].value; - if (!try_get_word(memory, addr, &newstate->reg[reg])) { - ALOGE("get_old_register_value: can't read from 0x%x", addr); - return false; - } - ALOGV("get_old_register_value: r%d at 0x%x is 0x%x", reg, addr, newstate->reg[reg]); - break; - case 'r': - /* We don't have all registers in state so don't even try to look at 'r' */ - ALOGE("get_old_register_value: register lookup not implemented yet"); - break; - default: - ALOGE("get_old_register_value: unexpected rule:%c value:%d for register %d", - dstate->regs[reg].rule, (int32_t)dstate->regs[reg].value, reg); - return false; - } - return true; -} - -/* Updaing state based on dwarf state. */ -static bool update_state(const memory_t* memory, unwind_state_t* state, - dwarf_state_t* dstate) { - unwind_state_t newstate; - /* We can restore more registers here if we need them. Meanwile doing minimal work here. */ - /* Getting CFA. */ - uintptr_t cfa = 0; - if (dstate->cfa_reg == DWARF_SP) { - cfa = state->reg[DWARF_SP] + dstate->cfa_off; - } else if (dstate->cfa_reg == DWARF_FP) { - cfa = state->reg[DWARF_FP] + dstate->cfa_off; - } else { - ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg); - return false; - } - ALOGV("update_state: new CFA: 0x%x", cfa); - - /* Update registers. Order is important to allow RA to propagate to PC */ - /* Getting FP. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_FP, state, &newstate)) return false; - /* Getting SP. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_SP, state, &newstate)) return false; - /* Getting RA. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_RA, state, &newstate)) return false; - /* Getting PC. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_PC, state, &newstate)) return false; - - ALOGV("update_state: PC: 0x%x; restore PC: 0x%x", state->reg[DWARF_PC], newstate.reg[DWARF_PC]); - ALOGV("update_state: RA: 0x%x; restore RA: 0x%x", state->reg[DWARF_RA], newstate.reg[DWARF_RA]); - ALOGV("update_state: FP: 0x%x; restore FP: 0x%x", state->reg[DWARF_FP], newstate.reg[DWARF_FP]); - ALOGV("update_state: SP: 0x%x; restore SP: 0x%x", state->reg[DWARF_SP], newstate.reg[DWARF_SP]); - - if (newstate.reg[DWARF_PC] == 0) - return false; - - /* End backtrace if registers do not change */ - if ((state->reg[DWARF_PC] == newstate.reg[DWARF_PC]) && - (state->reg[DWARF_RA] == newstate.reg[DWARF_RA]) && - (state->reg[DWARF_FP] == newstate.reg[DWARF_FP]) && - (state->reg[DWARF_SP] == newstate.reg[DWARF_SP])) - return false; - - *state = newstate; - return true; -} - -/* Execute CIE and FDE instructions for FDE found with find_fde. */ -static bool execute_fde(const memory_t* memory, - uintptr_t fde, - unwind_state_t* state) { - uint32_t fde_length = 0; - uint32_t cie_length = 0; - uintptr_t cie = 0; - uintptr_t cie_offset = 0; - cie_info_t cie_i; - cie_info_t* cie_info = &cie_i; - fde_info_t fde_i; - fde_info_t* fde_info = &fde_i; - dwarf_state_t dwarf_state; - dwarf_state_t* dstate = &dwarf_state; - dwarf_state_t stack[DWARF_STATES_STACK]; - uint8_t stack_ptr = 0; - - memset(dstate, 0, sizeof(dwarf_state_t)); - memset(cie_info, 0, sizeof(cie_info_t)); - memset(fde_info, 0, sizeof(fde_info_t)); - - /* Read common CIE or FDE area: - 1st word is length; - 2nd word is ID: 0 for CIE, CIE pointer for FDE. - */ - if (!try_get_word(memory, fde, &fde_length)) { - return false; - } - if ((int32_t)fde_length == -1) { - ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet"); - return false; - } - if (!try_get_word(memory, fde + 4, &cie_offset)) { - return false; - } - if (cie_offset == 0) { - /* This is CIE. We shouldn't be here normally. */ - cie = fde; - cie_length = fde_length; - } else { - /* Find CIE. */ - /* Positive cie_offset goes backward from current field. */ - cie = fde + 4 - cie_offset; - if (!try_get_word(memory, cie, &cie_length)) { - return false; - } - if ((int32_t)cie_length == -1) { - ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet"); - return false; - } - if (!try_get_word(memory, cie + 4, &cie_offset)) { - return false; - } - if (cie_offset != 0) { - ALOGV("execute_fde: can't find CIE"); - return false; - } - } - ALOGV("execute_fde: FDE length: %d", fde_length); - ALOGV("execute_fde: CIE pointer: %x", cie); - ALOGV("execute_fde: CIE length: %d", cie_length); - - /* Read CIE: - Augmentation independent: - 1st byte is version; - next x bytes is /0 terminated augmentation string; - next x bytes is unsigned LEB128 encoded code alignment factor; - next x bytes is signed LEB128 encoded data alignment factor; - next 1 (CIE version 1) or x (CIE version 3 unsigned LEB128) bytes is return register column; - Augmentation dependent: - if 'z' next x bytes is unsigned LEB128 encoded augmentation data size; - if 'L' next 1 byte is LSDA encoding; - if 'R' next 1 byte is FDE encoding; - if 'S' CIE represents signal handler stack frame; - if 'P' next 1 byte is personality encoding folowed by personality function pointer; - Next x bytes is CIE program. - */ - - uint32_t c = 8; - if (!try_get_byte(memory, cie, &cie_info->version, &c)) { - return false; - } - ALOGV("execute_fde: CIE version: %d", cie_info->version); - uint8_t ch; - do { - if (!try_get_byte(memory, cie, &ch, &c)) { - return false; - } - switch (ch) { - case '\0': break; - case 'z': cie_info->aug_z = 1; break; - case 'L': cie_info->aug_L = 1; break; - case 'R': cie_info->aug_R = 1; break; - case 'S': cie_info->aug_S = 1; break; - case 'P': cie_info->aug_P = 1; break; - default: - ALOGV("execute_fde: Unrecognized CIE augmentation char: '%c'", ch); - return false; - break; - } - } while (ch); - if (!try_get_uleb128(memory, cie, &cie_info->code_align, &c)) { - return false; - } - if (!try_get_sleb128(memory, cie, &cie_info->data_align, &c)) { - return false; - } - if (cie_info->version >= 3) { - if (!try_get_uleb128(memory, cie, &cie_info->reg, &c)) { - return false; - } - } else { - if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->reg, &c)) { - return false; - } - } - ALOGV("execute_fde: CIE code alignment factor: %d", cie_info->code_align); - ALOGV("execute_fde: CIE data alignment factor: %d", cie_info->data_align); - if (cie_info->aug_z) { - if (!try_get_uleb128(memory, cie, &cie_info->aug_z, &c)) { - return false; - } - } - if (cie_info->aug_L) { - if (!try_get_byte(memory, cie, &cie_info->aug_L, &c)) { - return false; - } - } else { - /* Default encoding. */ - cie_info->aug_L = DW_EH_PE_absptr; - } - if (cie_info->aug_R) { - if (!try_get_byte(memory, cie, &cie_info->aug_R, &c)) { - return false; - } - } else { - /* Default encoding. */ - cie_info->aug_R = DW_EH_PE_absptr; - } - if (cie_info->aug_P) { - /* Get encoding of personality routine pointer. We don't use it now. */ - if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->aug_P, &c)) { - return false; - } - /* Get routine pointer. */ - if (!read_dwarf(memory, cie, &cie_info->aug_P, (uint8_t)cie_info->aug_P, &c)) { - return false; - } - } - /* CIE program. */ - /* Length field itself (4 bytes) is not included into length. */ - stack[0] = *dstate; - stack_ptr = 1; - while (c < cie_length + 4) { - if (!execute_dwarf(memory, cie, cie_info, dstate, &c, stack, &stack_ptr)) { - return false; - } - } - - /* We went directly to CIE. Normally it shouldn't occur. */ - if (cie == fde) return true; - - /* Go back to FDE. */ - c = 8; - /* Read FDE: - Augmentation independent: - next x bytes (encoded as specified in CIE) is FDE starting address; - next x bytes (encoded as specified in CIE) is FDE number of instructions covered; - Augmentation dependent: - if 'z' next x bytes is unsigned LEB128 encoded augmentation data size; - if 'L' next x bytes is LSDA pointer (encoded as specified in CIE); - Next x bytes is FDE program. - */ - if (!read_dwarf(memory, fde, &fde_info->start, (uint8_t)cie_info->aug_R, &c)) { - return false; - } - dstate->loc = fde_info->start; - ALOGV("execute_fde: FDE start: %x", dstate->loc); - if (!read_dwarf(memory, fde, &fde_info->length, 0, &c)) { - return false; - } - ALOGV("execute_fde: FDE length: %x", fde_info->length); - if (cie_info->aug_z) { - if (!try_get_uleb128(memory, fde, &fde_info->aug_z, &c)) { - return false; - } - } - if (cie_info->aug_L && cie_info->aug_L != DW_EH_PE_omit) { - if (!read_dwarf(memory, fde, &fde_info->aug_L, cie_info->aug_L, &c)) { - return false; - } - } - /* FDE program. */ - /* Length field itself (4 bytes) is not included into length. */ - /* Save CIE state as 0 element of stack. Used by DW_CFA_restore. */ - stack[0] = *dstate; - stack_ptr = 1; - while (c < fde_length + 4 && state->reg[DWARF_PC] >= dstate->loc) { - if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) { - return false; - } - ALOGV("PC: %x, LOC: %x", state->reg[DWARF_PC], dstate->loc); - } - - return update_state(memory, state, dstate); -} - -static bool heuristic_state_update(const memory_t* memory, unwind_state_t* state) -{ - bool found_start = false; - int maxcheck = 1024; - int32_t stack_size = 0; - int32_t ra_offset = 0; - dwarf_state_t dwarf_state; - dwarf_state_t* dstate = &dwarf_state; - - static struct { - uint32_t insn; - uint32_t mask; - } frame0sig[] = { - {0x3c1c0000, 0xffff0000}, /* lui gp,xxxx */ - {0x279c0000, 0xffff0000}, /* addiu gp,gp,xxxx */ - {0x039fe021, 0xffffffff}, /* addu gp,gp,ra */ - }; - const int nframe0sig = sizeof(frame0sig)/sizeof(frame0sig[0]); - int f0 = nframe0sig; - memset(dstate, 0, sizeof(dwarf_state_t)); - - /* Search code backwards looking for function prologue */ - for (uint32_t pc = state->reg[DWARF_PC]-4; maxcheck-- > 0 && !found_start; pc -= 4) { - uint32_t op; - int32_t immediate; - - if (!try_get_word(memory, pc, &op)) - return false; - - // ALOGV("@0x%08x: 0x%08x\n", pc, op); - - // Check for frame 0 signature - if ((op & frame0sig[f0].mask) == frame0sig[f0].insn) { - if (f0 == 0) - return false; - f0--; - } - else { - f0 = nframe0sig; - } - - switch (op & 0xffff0000) { - case 0x27bd0000: // addiu sp, imm - // looking for stack being decremented - immediate = (((int32_t)op) << 16) >> 16; - if (immediate < 0) { - stack_size = -immediate; - ALOGV("@0x%08x: found stack adjustment=%d\n", pc, stack_size); - } - break; - case 0x039f0000: // e021 - - case 0xafbf0000: // sw ra, imm(sp) - ra_offset = (((int32_t)op) << 16) >> 16; - ALOGV("@0x%08x: found ra offset=%d\n", pc, ra_offset); - break; - case 0x3c1c0000: // lui gp - ALOGV("@0x%08x: found function boundary", pc); - found_start = true; - break; - default: - break; - } - } - - dstate->cfa_reg = DWARF_SP; - dstate->cfa_off = stack_size; - - if (ra_offset) { - dstate->regs[DWARF_RA].rule = 'o'; - dstate->regs[DWARF_RA].value = -stack_size + ra_offset; - } - - return update_state(memory, state, dstate); -} - -static ssize_t unwind_backtrace_common(const memory_t* memory, - const map_info_t* map_info_list, - unwind_state_t* state, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth) { - - size_t ignored_frames = 0; - size_t returned_frames = 0; - - ALOGV("Unwinding tid: %d", memory->tid); - ALOGV("PC: %x", state->reg[DWARF_PC]); - ALOGV("RA: %x", state->reg[DWARF_RA]); - ALOGV("FP: %x", state->reg[DWARF_FP]); - ALOGV("SP: %x", state->reg[DWARF_SP]); - - for (size_t index = 0; returned_frames < max_depth; index++) { - uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_PC]); - backtrace_frame_t* frame = add_backtrace_entry( - index ? rewind_pc_arch(memory, state->reg[DWARF_PC]) : state->reg[DWARF_PC], - backtrace, ignore_depth, max_depth, - &ignored_frames, &returned_frames); - uint32_t stack_top = state->reg[DWARF_SP]; - - if (fde) { - /* Use FDE to update state */ - if (!execute_fde(memory, fde, state)) - break; - } - else { - /* FDE is not found, update state heuristically */ - if (!heuristic_state_update(memory, state)) - break; - } - - if (frame) { - frame->stack_top = stack_top; - if (stack_top < state->reg[DWARF_SP]) { - frame->stack_size = state->reg[DWARF_SP] - stack_top; - } - } - ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_SP], frame->stack_size); - } - return returned_frames; -} - -ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), void* sigcontext, - const map_info_t* map_info_list, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - const ucontext_t* uc = (const ucontext_t*)sigcontext; - - unwind_state_t state; - state.reg[DWARF_PC] = uc->uc_mcontext.pc; - state.reg[DWARF_RA] = uc->uc_mcontext.gregs[REG_RA]; - state.reg[DWARF_FP] = uc->uc_mcontext.gregs[REG_S8]; - state.reg[DWARF_SP] = uc->uc_mcontext.gregs[REG_SP]; - - ALOGV("unwind_backtrace_signal_arch: " - "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n", - ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]); - - memory_t memory; - init_memory(&memory, map_info_list); - return unwind_backtrace_common(&memory, map_info_list, - &state, backtrace, ignore_depth, max_depth); -} - -ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - - user_regs_struct regs; - if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { - return -1; - } - - unwind_state_t state; - state.reg[DWARF_PC] = regs.epc; - state.reg[DWARF_RA] = regs.regs[REG_RA]; - state.reg[DWARF_FP] = regs.regs[REG_S8]; - state.reg[DWARF_SP] = regs.regs[REG_SP]; - - ALOGV("unwind_backtrace_ptrace_arch: " - "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n", - ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]); - - memory_t memory; - init_memory_ptrace(&memory, tid); - return unwind_backtrace_common(&memory, context->map_info_list, - &state, backtrace, ignore_depth, max_depth); -} diff --git a/libcorkscrew/arch-mips/dwarf.h b/libcorkscrew/arch-mips/dwarf.h deleted file mode 100644 index 8504ea0..0000000 --- a/libcorkscrew/arch-mips/dwarf.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Dwarf2 data encoding flags. - */ - -#define DW_EH_PE_absptr 0x00 -#define DW_EH_PE_omit 0xff -#define DW_EH_PE_uleb128 0x01 -#define DW_EH_PE_udata2 0x02 -#define DW_EH_PE_udata4 0x03 -#define DW_EH_PE_udata8 0x04 -#define DW_EH_PE_sleb128 0x09 -#define DW_EH_PE_sdata2 0x0A -#define DW_EH_PE_sdata4 0x0B -#define DW_EH_PE_sdata8 0x0C -#define DW_EH_PE_signed 0x08 -#define DW_EH_PE_pcrel 0x10 -#define DW_EH_PE_textrel 0x20 -#define DW_EH_PE_datarel 0x30 -#define DW_EH_PE_funcrel 0x40 -#define DW_EH_PE_aligned 0x50 -#define DW_EH_PE_indirect 0x80 - -/* - * Dwarf2 call frame instructions. - */ - -typedef enum { - DW_CFA_advance_loc = 0x40, - DW_CFA_offset = 0x80, - DW_CFA_restore = 0xc0, - DW_CFA_nop = 0x00, - DW_CFA_set_loc = 0x01, - DW_CFA_advance_loc1 = 0x02, - DW_CFA_advance_loc2 = 0x03, - DW_CFA_advance_loc4 = 0x04, - DW_CFA_offset_extended = 0x05, - DW_CFA_restore_extended = 0x06, - DW_CFA_undefined = 0x07, - DW_CFA_same_value = 0x08, - DW_CFA_register = 0x09, - DW_CFA_remember_state = 0x0a, - DW_CFA_restore_state = 0x0b, - DW_CFA_def_cfa = 0x0c, - DW_CFA_def_cfa_register = 0x0d, - DW_CFA_def_cfa_offset = 0x0e -} dwarf_CFA; - -/* - * eh_frame_hdr information. -*/ - -typedef struct { - uint8_t version; - uint8_t eh_frame_ptr_enc; - uint8_t fde_count_enc; - uint8_t fde_table_enc; - uintptr_t eh_frame_ptr; - uint32_t fde_count; -} eh_frame_hdr_info_t; - -/* - * CIE information. -*/ - -typedef struct { - uint8_t version; - uint32_t code_align; - uint32_t data_align; - uint32_t reg; - uint32_t aug_z; - uint8_t aug_L; - uint8_t aug_R; - uint8_t aug_S; - uint32_t aug_P; -} cie_info_t; - -/* - * FDE information. -*/ - -typedef struct { - uint32_t start; - uint32_t length; // number of instructions covered by FDE - uint32_t aug_z; - uint32_t aug_L; -} fde_info_t; - -/* - * Dwarf state. -*/ - -/* Stack of states: required for DW_CFA_remember_state/DW_CFA_restore_state - 30 should be enough */ -#define DWARF_STATES_STACK 30 - -typedef struct { - char rule; // rule: o - offset(value); r - register(value) - uint32_t value; // value -} reg_rule_t; - -/* Dwarf preserved number of registers for mips */ -typedef enum - { - UNW_MIPS_R0, - UNW_MIPS_R1, - UNW_MIPS_R2, - UNW_MIPS_R3, - UNW_MIPS_R4, - UNW_MIPS_R5, - UNW_MIPS_R6, - UNW_MIPS_R7, - UNW_MIPS_R8, - UNW_MIPS_R9, - UNW_MIPS_R10, - UNW_MIPS_R11, - UNW_MIPS_R12, - UNW_MIPS_R13, - UNW_MIPS_R14, - UNW_MIPS_R15, - UNW_MIPS_R16, - UNW_MIPS_R17, - UNW_MIPS_R18, - UNW_MIPS_R19, - UNW_MIPS_R20, - UNW_MIPS_R21, - UNW_MIPS_R22, - UNW_MIPS_R23, - UNW_MIPS_R24, - UNW_MIPS_R25, - UNW_MIPS_R26, - UNW_MIPS_R27, - UNW_MIPS_R28, - UNW_MIPS_R29, - UNW_MIPS_R30, - UNW_MIPS_R31, - - UNW_MIPS_PC = 34, - - /* FIXME: Other registers! */ - - /* For MIPS, the CFA is the value of SP (r29) at the call site in the - previous frame. */ - UNW_MIPS_CFA, - - UNW_TDEP_LASTREG, - - UNW_TDEP_LAST_REG = UNW_MIPS_R31, - - UNW_TDEP_IP = UNW_MIPS_R31, - UNW_TDEP_SP = UNW_MIPS_R29, - UNW_TDEP_EH = UNW_MIPS_R0 /* FIXME. */ - - } -mips_regnum_t; - -#define DWARF_REGISTERS UNW_TDEP_LASTREG - -typedef struct { - uintptr_t loc; // location (ip) - uint8_t cfa_reg; // index of register where CFA location stored - intptr_t cfa_off; // offset - reg_rule_t regs[DWARF_REGISTERS]; // dwarf preserved registers for mips -} dwarf_state_t; - -/* DWARF registers we are caring about. */ - - -#define DWARF_SP UNW_MIPS_R29 -#define DWARF_RA UNW_MIPS_R31 -#define DWARF_PC UNW_MIPS_PC -#define DWARF_FP UNW_MIPS_CFA /* FIXME is this correct? */ diff --git a/libcorkscrew/arch-mips/ptrace-mips.c b/libcorkscrew/arch-mips/ptrace-mips.c deleted file mode 100644 index ba3b60a..0000000 --- a/libcorkscrew/arch-mips/ptrace-mips.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../ptrace-arch.h" - -#include <stddef.h> -#include <elf.h> -#include <cutils/log.h> - -static void load_eh_frame_hdr(pid_t pid, map_info_t* mi, uintptr_t *eh_frame_hdr) { - uint32_t elf_phoff; - uint32_t elf_phentsize_ehsize; - uint32_t elf_shentsize_phnum; - - - try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff); - ALOGV("reading 0x%08x elf_phoff:%x", mi->start + offsetof(Elf32_Ehdr, e_phoff), elf_phoff); - try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), &elf_phentsize_ehsize); - ALOGV("reading 0x%08x elf_phentsize_ehsize:%x", mi->start + offsetof(Elf32_Ehdr, e_ehsize), elf_phentsize_ehsize); - try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), &elf_shentsize_phnum); - ALOGV("reading 0x%08x elf_shentsize_phnum:%x", mi->start + offsetof(Elf32_Ehdr, e_phnum), elf_shentsize_phnum); - - - - if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), - &elf_phentsize_ehsize) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), - &elf_shentsize_phnum)) { - uint32_t elf_phentsize = elf_phentsize_ehsize >> 16; - uint32_t elf_phnum = elf_shentsize_phnum & 0xffff; - for (uint32_t i = 0; i < elf_phnum; i++) { - uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize; - uint32_t elf_phdr_type; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) { - break; - } - if (elf_phdr_type == PT_GNU_EH_FRAME) { - uint32_t elf_phdr_offset; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset), - &elf_phdr_offset)) { - break; - } - *eh_frame_hdr = mi->start + elf_phdr_offset; - ALOGV("Parsed .eh_frame_hdr info for %s: start=0x%08x", mi->name, *eh_frame_hdr); - return; - } - } - } - *eh_frame_hdr = 0; -} - -void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) { - ALOGV("load_ptrace_map_info_data_arch"); - load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr); -} - -void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)), - map_info_data_t* data __attribute__((unused))) { - ALOGV("free_ptrace_map_info_data_arch"); -} diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c deleted file mode 100755 index df486de..0000000 --- a/libcorkscrew/arch-x86/backtrace-x86.c +++ /dev/null @@ -1,823 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Backtracing functions for x86. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../backtrace-arch.h" -#include "../backtrace-helper.h" -#include "../ptrace-arch.h" -#include <corkscrew/ptrace.h> -#include "dwarf.h" - -#include <stdlib.h> -#include <signal.h> -#include <stdbool.h> -#include <limits.h> -#include <errno.h> -#include <string.h> -#include <sys/ptrace.h> -#include <cutils/log.h> - -#if defined(__APPLE__) - -#define _XOPEN_SOURCE -#include <ucontext.h> - -#else - -// glibc has its own renaming of the Linux kernel's structures. -#define __USE_GNU // For REG_EBP, REG_ESP, and REG_EIP. -#include <ucontext.h> - -#endif - -/* Unwind state. */ -typedef struct { - uint32_t reg[DWARF_REGISTERS]; -} unwind_state_t; - -typedef struct { - backtrace_frame_t* backtrace; - size_t ignore_depth; - size_t max_depth; - size_t ignored_frames; - size_t returned_frames; - memory_t memory; -} backtrace_state_t; - -uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) { - /* TODO: x86 instructions are 1-16 bytes, to define exact size of previous instruction - we have to disassemble from the function entry point up to pc. - Returning pc-1 is probably enough for now, the only drawback is that - it points somewhere between the first byte of instruction we are looking for and - the first byte of the next instruction. */ - - return pc-1; - /* TODO: We should adjust that for the signal frames and return pc for them instead of pc-1. - To recognize signal frames we should read cie_info property. */ -} - -/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */ -static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) { - static uintptr_t lastptr; - static uint32_t buf; - - ptr += *cursor; - - if (ptr < lastptr || lastptr + 3 < ptr) { - lastptr = (ptr >> 2) << 2; - if (!try_get_word(memory, lastptr, &buf)) { - return false; - } - } - *out_value = (uint8_t)((buf >> ((ptr & 3) * 8)) & 0xff); - ++*cursor; - return true; -} - -/* Getting X bytes. 4 is maximum for now. */ -static bool try_get_xbytes(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t bytes, uint32_t* cursor) { - uint32_t data = 0; - if (bytes > 4) { - ALOGE("can't read more than 4 bytes, trying to read %d", bytes); - return false; - } - for (int i = 0; i < bytes; i++) { - uint8_t buf; - if (!try_get_byte(memory, ptr, &buf, cursor)) { - return false; - } - data |= (uint32_t)buf << (i * 8); - } - *out_value = data; - return true; -} - -/* Reads signed/unsigned LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_leb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor, bool sign_extend) { - uint8_t buf = 0; - uint32_t val = 0; - uint8_t c = 0; - do { - if (!try_get_byte(memory, ptr, &buf, cursor)) { - return false; - } - val |= ((uint32_t)buf & 0x7f) << (c * 7); - c++; - } while (buf & 0x80 && (c * 7) <= 32); - if (c * 7 > 32) { - ALOGE("%s: data exceeds expected 4 bytes maximum", __FUNCTION__); - return false; - } - if (sign_extend) { - if (buf & 0x40) { - val |= ((uint32_t)-1 << (c * 7)); - } - } - *out_value = val; - return true; -} - -/* Reads signed LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_sleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) { - return try_get_leb128(memory, ptr, out_value, cursor, true); -} - -/* Reads unsigned LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_uleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) { - return try_get_leb128(memory, ptr, out_value, cursor, false); -} - -/* Getting data encoded by dwarf encodings. */ -static bool read_dwarf(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t encoding, uint32_t* cursor) { - uint32_t data = 0; - bool issigned = true; - uintptr_t addr = ptr + *cursor; - /* Lower 4 bits is data type/size */ - /* TODO: add more encodings if it becomes necessary */ - switch (encoding & 0xf) { - case DW_EH_PE_absptr: - if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) { - return false; - } - *out_value = data; - return true; - case DW_EH_PE_udata4: - issigned = false; - case DW_EH_PE_sdata4: - if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) { - return false; - } - break; - default: - ALOGE("unrecognized dwarf lower part encoding: 0x%x", encoding); - return false; - } - /* Higher 4 bits is modifier */ - /* TODO: add more encodings if it becomes necessary */ - switch (encoding & 0xf0) { - case 0: - *out_value = data; - break; - case DW_EH_PE_pcrel: - if (issigned) { - *out_value = addr + (int32_t)data; - } else { - *out_value = addr + data; - } - break; - /* Assuming ptr is correct base to calculate datarel */ - case DW_EH_PE_datarel: - if (issigned) { - *out_value = ptr + (int32_t)data; - } else { - *out_value = ptr + data; - } - break; - default: - ALOGE("unrecognized dwarf higher part encoding: 0x%x", encoding); - return false; - } - return true; -} - -/* Having PC find corresponding FDE by reading .eh_frame_hdr section data. */ -static uintptr_t find_fde(const memory_t* memory, - const map_info_t* map_info_list, uintptr_t pc) { - if (!pc) { - ALOGV("find_fde: pc is zero, no eh_frame"); - return 0; - } - const map_info_t* mi = find_map_info(map_info_list, pc); - if (!mi) { - ALOGV("find_fde: no map info for pc:0x%x", pc); - return 0; - } - const map_info_data_t* midata = mi->data; - if (!midata) { - ALOGV("find_fde: no eh_frame_hdr for map: start=0x%x, end=0x%x", mi->start, mi->end); - return 0; - } - - eh_frame_hdr_info_t eh_hdr_info; - memset(&eh_hdr_info, 0, sizeof(eh_frame_hdr_info_t)); - - /* Getting the first word of eh_frame_hdr: - 1st byte is version; - 2nd byte is encoding of pointer to eh_frames; - 3rd byte is encoding of count of FDEs in lookup table; - 4th byte is encoding of lookup table entries. - */ - uintptr_t eh_frame_hdr = midata->eh_frame_hdr; - uint32_t c = 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.version, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr_enc, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_count_enc, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_table_enc, &c)) return 0; - - /* TODO: 3rd byte can be DW_EH_PE_omit, that means no lookup table available and we should - try to parse eh_frame instead. Not sure how often it may occur, skipping now. - */ - if (eh_hdr_info.version != 1) { - ALOGV("find_fde: eh_frame_hdr version %d is not supported", eh_hdr_info.version); - return 0; - } - /* Getting the data: - 2nd word is eh_frame pointer (normally not used, because lookup table has all we need); - 3rd word is count of FDEs in the lookup table; - starting from 4 word there is FDE lookup table (pairs of PC and FDE pointer) sorted by PC; - */ - if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr, eh_hdr_info.eh_frame_ptr_enc, &c)) return 0; - if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.fde_count, eh_hdr_info.fde_count_enc, &c)) return 0; - ALOGV("find_fde: found %d FDEs", eh_hdr_info.fde_count); - - int32_t low = 0; - int32_t high = eh_hdr_info.fde_count; - uintptr_t start = 0; - uintptr_t fde = 0; - /* eh_frame_hdr + c points to lookup table at this point. */ - while (low <= high) { - uint32_t mid = (high + low)/2; - uint32_t entry = c + mid * 8; - if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &entry)) return 0; - if (pc <= start) { - high = mid - 1; - } else { - low = mid + 1; - } - } - /* Value found is at high. */ - if (high < 0) { - ALOGV("find_fde: pc %x is out of FDE bounds: %x", pc, start); - return 0; - } - c += high * 8; - if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &c)) return 0; - if (!read_dwarf(memory, eh_frame_hdr, &fde, eh_hdr_info.fde_table_enc, &c)) return 0; - ALOGV("pc 0x%x, ENTRY %d: start=0x%x, fde=0x%x", pc, high, start, fde); - return fde; -} - -/* Execute single dwarf instruction and update dwarf state accordingly. */ -static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie_info, - dwarf_state_t* dstate, uint32_t* cursor, - dwarf_state_t* stack, uint8_t* stack_ptr) { - uint8_t inst; - uint8_t op = 0; - - if (!try_get_byte(memory, ptr, &inst, cursor)) { - return false; - } - ALOGV("DW_CFA inst: 0x%x", inst); - - /* For some instructions upper 2 bits is opcode and lower 6 bits is operand. See dwarf-2.0 7.23. */ - if (inst & 0xc0) { - op = inst & 0x3f; - inst &= 0xc0; - } - - switch ((dwarf_CFA)inst) { - uint32_t reg = 0; - uint32_t offset = 0; - case DW_CFA_advance_loc: - dstate->loc += op * cie_info->code_align; - ALOGV("DW_CFA_advance_loc: %d to 0x%x", op, dstate->loc); - break; - case DW_CFA_offset: - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - dstate->regs[op].rule = 'o'; - dstate->regs[op].value = offset * cie_info->data_align; - ALOGV("DW_CFA_offset: r%d = o(%d)", op, dstate->regs[op].value); - break; - case DW_CFA_restore: - dstate->regs[op].rule = stack->regs[op].rule; - dstate->regs[op].value = stack->regs[op].value; - ALOGV("DW_CFA_restore: r%d = %c(%d)", op, dstate->regs[op].rule, dstate->regs[op].value); - break; - case DW_CFA_nop: - break; - case DW_CFA_set_loc: // probably we don't have it on x86. - if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false; - if (offset < dstate->loc) { - ALOGE("DW_CFA_set_loc: attempt to move location backward"); - return false; - } - dstate->loc = offset * cie_info->code_align; - ALOGV("DW_CFA_set_loc: %d to 0x%x", offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc1: - if (!try_get_byte(memory, ptr, (uint8_t*)&offset, cursor)) return false; - dstate->loc += (uint8_t)offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc1: %d to 0x%x", (uint8_t)offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc2: - if (!try_get_xbytes(memory, ptr, &offset, 2, cursor)) return false; - dstate->loc += (uint16_t)offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc2: %d to 0x%x", (uint16_t)offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc4: - if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false; - dstate->loc += offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc4: %d to 0x%x", offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_offset_extended: // probably we don't have it on x86. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'o'; - dstate->regs[reg].value = offset * cie_info->data_align; - ALOGV("DW_CFA_offset_extended: r%d = o(%d)", reg, dstate->regs[reg].value); - break; - case DW_CFA_restore_extended: // probably we don't have it on x86. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = stack->regs[reg].rule; - dstate->regs[reg].value = stack->regs[reg].value; - ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value); - break; - case DW_CFA_undefined: // probably we don't have it on x86. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'u'; - dstate->regs[reg].value = 0; - ALOGV("DW_CFA_undefined: r%d", reg); - break; - case DW_CFA_same_value: // probably we don't have it on x86. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 's'; - dstate->regs[reg].value = 0; - ALOGV("DW_CFA_same_value: r%d", reg); - break; - case DW_CFA_register: // probably we don't have it on x86. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - /* that's new register actually, not offset */ - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - if (reg >= DWARF_REGISTERS || offset >= DWARF_REGISTERS) { - ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'r'; - dstate->regs[reg].value = offset; - ALOGV("DW_CFA_register: r%d = r(%d)", reg, dstate->regs[reg].value); - break; - case DW_CFA_remember_state: - if (*stack_ptr == DWARF_STATES_STACK) { - ALOGE("DW_CFA_remember_state: states stack overflow %d", *stack_ptr); - return false; - } - stack[(*stack_ptr)++] = *dstate; - ALOGV("DW_CFA_remember_state: stacktop moves to %d", *stack_ptr); - break; - case DW_CFA_restore_state: - /* We have CIE state saved at 0 position. It's not supposed to be taken - by DW_CFA_restore_state. */ - if (*stack_ptr == 1) { - ALOGE("DW_CFA_restore_state: states stack is empty"); - return false; - } - /* Don't touch location on restore. */ - uintptr_t saveloc = dstate->loc; - *dstate = stack[--*stack_ptr]; - dstate->loc = saveloc; - ALOGV("DW_CFA_restore_state: stacktop moves to %d", *stack_ptr); - break; - case DW_CFA_def_cfa: - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - dstate->cfa_reg = reg; - dstate->cfa_off = offset; - ALOGV("DW_CFA_def_cfa: %x(r%d)", offset, reg); - break; - case DW_CFA_def_cfa_register: - if (!try_get_uleb128(memory, ptr, ®, cursor)) { - return false; - } - dstate->cfa_reg = reg; - ALOGV("DW_CFA_def_cfa_register: r%d", reg); - break; - case DW_CFA_def_cfa_offset: - if (!try_get_uleb128(memory, ptr, &offset, cursor)) { - return false; - } - dstate->cfa_off = offset; - ALOGV("DW_CFA_def_cfa_offset: %x", offset); - break; - default: - ALOGE("unrecognized DW_CFA_* instruction: 0x%x", inst); - return false; - } - return true; -} - -/* Restoring particular register value based on dwarf state. */ -static bool get_old_register_value(const memory_t* memory, uint32_t cfa, - dwarf_state_t* dstate, uint8_t reg, - unwind_state_t* state, unwind_state_t* newstate) { - uint32_t addr; - switch (dstate->regs[reg].rule) { - case 0: - /* We don't have dstate updated for this register, so assuming value kept the same. - Normally we should look into state and return current value as the old one - but we don't have all registers in state to handle this properly */ - ALOGV("get_old_register_value: value of r%d is the same", reg); - // for ESP if it's not updated by dwarf rule we assume it's equal to CFA - if (reg == DWARF_ESP) { - ALOGV("get_old_register_value: adjusting esp to CFA: 0x%x", cfa); - newstate->reg[reg] = cfa; - } else { - newstate->reg[reg] = state->reg[reg]; - } - break; - case 'o': - addr = cfa + (int32_t)dstate->regs[reg].value; - if (!try_get_word(memory, addr, &newstate->reg[reg])) { - ALOGE("get_old_register_value: can't read from 0x%x", addr); - return false; - } - ALOGV("get_old_register_value: r%d at 0x%x is 0x%x", reg, addr, newstate->reg[reg]); - break; - case 'r': - /* We don't have all registers in state so don't even try to look at 'r' */ - ALOGE("get_old_register_value: register lookup not implemented yet"); - break; - default: - ALOGE("get_old_register_value: unexpected rule:%c value:%d for register %d", - dstate->regs[reg].rule, (int32_t)dstate->regs[reg].value, reg); - return false; - } - return true; -} - -/* Updaing state based on dwarf state. */ -static bool update_state(const memory_t* memory, unwind_state_t* state, - dwarf_state_t* dstate) { - unwind_state_t newstate; - /* We can restore more registers here if we need them. Meanwile doing minimal work here. */ - /* Getting CFA. */ - uintptr_t cfa = 0; - if (dstate->cfa_reg == DWARF_ESP) { - cfa = state->reg[DWARF_ESP] + dstate->cfa_off; - } else if (dstate->cfa_reg == DWARF_EBP) { - cfa = state->reg[DWARF_EBP] + dstate->cfa_off; - } else { - ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg); - return false; - } - ALOGV("update_state: new CFA: 0x%x", cfa); - /* Getting EIP. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_EIP, state, &newstate)) return false; - /* Getting EBP. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_EBP, state, &newstate)) return false; - /* Getting ESP. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_ESP, state, &newstate)) return false; - - ALOGV("update_state: IP: 0x%x; restore IP: 0x%x", state->reg[DWARF_EIP], newstate.reg[DWARF_EIP]); - ALOGV("update_state: EBP: 0x%x; restore EBP: 0x%x", state->reg[DWARF_EBP], newstate.reg[DWARF_EBP]); - ALOGV("update_state: ESP: 0x%x; restore ESP: 0x%x", state->reg[DWARF_ESP], newstate.reg[DWARF_ESP]); - *state = newstate; - return true; -} - -/* Execute CIE and FDE instructions for FDE found with find_fde. */ -static bool execute_fde(const memory_t* memory, - uintptr_t fde, - unwind_state_t* state) { - uint32_t fde_length = 0; - uint32_t cie_length = 0; - uintptr_t cie = 0; - uintptr_t cie_offset = 0; - cie_info_t cie_i; - cie_info_t* cie_info = &cie_i; - fde_info_t fde_i; - fde_info_t* fde_info = &fde_i; - dwarf_state_t dwarf_state; - dwarf_state_t* dstate = &dwarf_state; - dwarf_state_t stack[DWARF_STATES_STACK]; - uint8_t stack_ptr = 0; - - memset(dstate, 0, sizeof(dwarf_state_t)); - memset(cie_info, 0, sizeof(cie_info_t)); - memset(fde_info, 0, sizeof(fde_info_t)); - - /* Read common CIE or FDE area: - 1st word is length; - 2nd word is ID: 0 for CIE, CIE pointer for FDE. - */ - if (!try_get_word(memory, fde, &fde_length)) { - return false; - } - if ((int32_t)fde_length == -1) { - ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet"); - return false; - } - if (!try_get_word(memory, fde + 4, &cie_offset)) { - return false; - } - if (cie_offset == 0) { - /* This is CIE. We shouldn't be here normally. */ - cie = fde; - cie_length = fde_length; - } else { - /* Find CIE. */ - /* Positive cie_offset goes backward from current field. */ - cie = fde + 4 - cie_offset; - if (!try_get_word(memory, cie, &cie_length)) { - return false; - } - if ((int32_t)cie_length == -1) { - ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet"); - return false; - } - if (!try_get_word(memory, cie + 4, &cie_offset)) { - return false; - } - if (cie_offset != 0) { - ALOGV("execute_fde: can't find CIE"); - return false; - } - } - ALOGV("execute_fde: FDE length: %d", fde_length); - ALOGV("execute_fde: CIE pointer: %x", cie); - ALOGV("execute_fde: CIE length: %d", cie_length); - - /* Read CIE: - Augmentation independent: - 1st byte is version; - next x bytes is /0 terminated augmentation string; - next x bytes is unsigned LEB128 encoded code alignment factor; - next x bytes is signed LEB128 encoded data alignment factor; - next 1 (CIE version 1) or x (CIE version 3 unsigned LEB128) bytes is return register column; - Augmentation dependent: - if 'z' next x bytes is unsigned LEB128 encoded augmentation data size; - if 'L' next 1 byte is LSDA encoding; - if 'R' next 1 byte is FDE encoding; - if 'S' CIE represents signal handler stack frame; - if 'P' next 1 byte is personality encoding folowed by personality function pointer; - Next x bytes is CIE program. - */ - - uint32_t c = 8; - if (!try_get_byte(memory, cie, &cie_info->version, &c)) { - return false; - } - ALOGV("execute_fde: CIE version: %d", cie_info->version); - uint8_t ch; - do { - if (!try_get_byte(memory, cie, &ch, &c)) { - return false; - } - switch (ch) { - case '\0': break; - case 'z': cie_info->aug_z = 1; break; - case 'L': cie_info->aug_L = 1; break; - case 'R': cie_info->aug_R = 1; break; - case 'S': cie_info->aug_S = 1; break; - case 'P': cie_info->aug_P = 1; break; - default: - ALOGV("execute_fde: Unrecognized CIE augmentation char: '%c'", ch); - return false; - break; - } - } while (ch); - if (!try_get_uleb128(memory, cie, &cie_info->code_align, &c)) { - return false; - } - if (!try_get_sleb128(memory, cie, &cie_info->data_align, &c)) { - return false; - } - if (cie_info->version >= 3) { - if (!try_get_uleb128(memory, cie, &cie_info->reg, &c)) { - return false; - } - } else { - if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->reg, &c)) { - return false; - } - } - ALOGV("execute_fde: CIE code alignment factor: %d", cie_info->code_align); - ALOGV("execute_fde: CIE data alignment factor: %d", cie_info->data_align); - if (cie_info->aug_z) { - if (!try_get_uleb128(memory, cie, &cie_info->aug_z, &c)) { - return false; - } - } - if (cie_info->aug_L) { - if (!try_get_byte(memory, cie, &cie_info->aug_L, &c)) { - return false; - } - } else { - /* Default encoding. */ - cie_info->aug_L = DW_EH_PE_absptr; - } - if (cie_info->aug_R) { - if (!try_get_byte(memory, cie, &cie_info->aug_R, &c)) { - return false; - } - } else { - /* Default encoding. */ - cie_info->aug_R = DW_EH_PE_absptr; - } - if (cie_info->aug_P) { - /* Get encoding of personality routine pointer. We don't use it now. */ - if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->aug_P, &c)) { - return false; - } - /* Get routine pointer. */ - if (!read_dwarf(memory, cie, &cie_info->aug_P, (uint8_t)cie_info->aug_P, &c)) { - return false; - } - } - /* CIE program. */ - /* Length field itself (4 bytes) is not included into length. */ - stack[0] = *dstate; - stack_ptr = 1; - while (c < cie_length + 4) { - if (!execute_dwarf(memory, cie, cie_info, dstate, &c, stack, &stack_ptr)) { - return false; - } - } - - /* We went directly to CIE. Normally it shouldn't occur. */ - if (cie == fde) return true; - - /* Go back to FDE. */ - c = 8; - /* Read FDE: - Augmentation independent: - next x bytes (encoded as specified in CIE) is FDE starting address; - next x bytes (encoded as specified in CIE) is FDE number of instructions covered; - Augmentation dependent: - if 'z' next x bytes is unsigned LEB128 encoded augmentation data size; - if 'L' next x bytes is LSDA pointer (encoded as specified in CIE); - Next x bytes is FDE program. - */ - if (!read_dwarf(memory, fde, &fde_info->start, (uint8_t)cie_info->aug_R, &c)) { - return false; - } - dstate->loc = fde_info->start; - ALOGV("execute_fde: FDE start: %x", dstate->loc); - if (!read_dwarf(memory, fde, &fde_info->length, 0, &c)) { - return false; - } - ALOGV("execute_fde: FDE length: %x", fde_info->length); - if (cie_info->aug_z) { - if (!try_get_uleb128(memory, fde, &fde_info->aug_z, &c)) { - return false; - } - } - if (cie_info->aug_L && cie_info->aug_L != DW_EH_PE_omit) { - if (!read_dwarf(memory, fde, &fde_info->aug_L, cie_info->aug_L, &c)) { - return false; - } - } - /* FDE program. */ - /* Length field itself (4 bytes) is not included into length. */ - /* Save CIE state as 0 element of stack. Used by DW_CFA_restore. */ - stack[0] = *dstate; - stack_ptr = 1; - while (c < fde_length + 4 && state->reg[DWARF_EIP] >= dstate->loc) { - if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) { - return false; - } - ALOGV("IP: %x, LOC: %x", state->reg[DWARF_EIP], dstate->loc); - } - - return update_state(memory, state, dstate); -} - -static ssize_t unwind_backtrace_common(const memory_t* memory, - const map_info_t* map_info_list, - unwind_state_t* state, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth) { - - size_t ignored_frames = 0; - size_t returned_frames = 0; - - ALOGV("Unwinding tid: %d", memory->tid); - ALOGV("IP: %x", state->reg[DWARF_EIP]); - ALOGV("BP: %x", state->reg[DWARF_EBP]); - ALOGV("SP: %x", state->reg[DWARF_ESP]); - - for (size_t index = 0; returned_frames < max_depth; index++) { - uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_EIP]); - /* FDE is not found, it may happen if stack is corrupted or calling wrong adress. - Getting return address from stack. - */ - if (!fde) { - uint32_t ip; - ALOGV("trying to restore registers from stack"); - if (!try_get_word(memory, state->reg[DWARF_EBP] + 4, &ip) || - ip == state->reg[DWARF_EIP]) { - ALOGV("can't get IP from stack"); - break; - } - /* We've been able to get IP from stack so recording the frame before continue. */ - backtrace_frame_t* frame = add_backtrace_entry( - index ? rewind_pc_arch(memory, state->reg[DWARF_EIP]) : state->reg[DWARF_EIP], - backtrace, ignore_depth, max_depth, - &ignored_frames, &returned_frames); - state->reg[DWARF_EIP] = ip; - state->reg[DWARF_ESP] = state->reg[DWARF_EBP] + 8; - if (!try_get_word(memory, state->reg[DWARF_EBP], &state->reg[DWARF_EBP])) { - ALOGV("can't get EBP from stack"); - break; - } - ALOGV("restore IP: %x", state->reg[DWARF_EIP]); - ALOGV("restore BP: %x", state->reg[DWARF_EBP]); - ALOGV("restore SP: %x", state->reg[DWARF_ESP]); - continue; - } - backtrace_frame_t* frame = add_backtrace_entry( - index ? rewind_pc_arch(memory, state->reg[DWARF_EIP]) : state->reg[DWARF_EIP], - backtrace, ignore_depth, max_depth, - &ignored_frames, &returned_frames); - - uint32_t stack_top = state->reg[DWARF_ESP]; - - if (!execute_fde(memory, fde, state)) break; - - if (frame) { - frame->stack_top = stack_top; - if (stack_top < state->reg[DWARF_ESP]) { - frame->stack_size = state->reg[DWARF_ESP] - stack_top; - } - } - ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_ESP], frame->stack_size); - } - return returned_frames; -} - -ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), void* sigcontext, - const map_info_t* map_info_list, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - const ucontext_t* uc = (const ucontext_t*)sigcontext; - - unwind_state_t state; -#if defined(__APPLE__) - state.reg[DWARF_EBP] = uc->uc_mcontext->__ss.__ebp; - state.reg[DWARF_ESP] = uc->uc_mcontext->__ss.__esp; - state.reg[DWARF_EIP] = uc->uc_mcontext->__ss.__eip; -#else - state.reg[DWARF_EBP] = uc->uc_mcontext.gregs[REG_EBP]; - state.reg[DWARF_ESP] = uc->uc_mcontext.gregs[REG_ESP]; - state.reg[DWARF_EIP] = uc->uc_mcontext.gregs[REG_EIP]; -#endif - - memory_t memory; - init_memory(&memory, map_info_list); - return unwind_backtrace_common(&memory, map_info_list, - &state, backtrace, ignore_depth, max_depth); -} - -ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { -#if defined(__APPLE__) - return -1; -#else - pt_regs_x86_t regs; - if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { - return -1; - } - - unwind_state_t state; - state.reg[DWARF_EBP] = regs.ebp; - state.reg[DWARF_EIP] = regs.eip; - state.reg[DWARF_ESP] = regs.esp; - - memory_t memory; - init_memory_ptrace(&memory, tid); - return unwind_backtrace_common(&memory, context->map_info_list, - &state, backtrace, ignore_depth, max_depth); -#endif -} diff --git a/libcorkscrew/arch-x86/dwarf.h b/libcorkscrew/arch-x86/dwarf.h deleted file mode 100755 index 962fc55..0000000 --- a/libcorkscrew/arch-x86/dwarf.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Dwarf2 data encoding flags. - */ - -#define DW_EH_PE_absptr 0x00 -#define DW_EH_PE_omit 0xff -#define DW_EH_PE_uleb128 0x01 -#define DW_EH_PE_udata2 0x02 -#define DW_EH_PE_udata4 0x03 -#define DW_EH_PE_udata8 0x04 -#define DW_EH_PE_sleb128 0x09 -#define DW_EH_PE_sdata2 0x0A -#define DW_EH_PE_sdata4 0x0B -#define DW_EH_PE_sdata8 0x0C -#define DW_EH_PE_signed 0x08 -#define DW_EH_PE_pcrel 0x10 -#define DW_EH_PE_textrel 0x20 -#define DW_EH_PE_datarel 0x30 -#define DW_EH_PE_funcrel 0x40 -#define DW_EH_PE_aligned 0x50 -#define DW_EH_PE_indirect 0x80 - -/* - * Dwarf2 call frame instructions. - */ - -typedef enum { - DW_CFA_advance_loc = 0x40, - DW_CFA_offset = 0x80, - DW_CFA_restore = 0xc0, - DW_CFA_nop = 0x00, - DW_CFA_set_loc = 0x01, - DW_CFA_advance_loc1 = 0x02, - DW_CFA_advance_loc2 = 0x03, - DW_CFA_advance_loc4 = 0x04, - DW_CFA_offset_extended = 0x05, - DW_CFA_restore_extended = 0x06, - DW_CFA_undefined = 0x07, - DW_CFA_same_value = 0x08, - DW_CFA_register = 0x09, - DW_CFA_remember_state = 0x0a, - DW_CFA_restore_state = 0x0b, - DW_CFA_def_cfa = 0x0c, - DW_CFA_def_cfa_register = 0x0d, - DW_CFA_def_cfa_offset = 0x0e -} dwarf_CFA; - -/* - * eh_frame_hdr information. -*/ - -typedef struct { - uint8_t version; - uint8_t eh_frame_ptr_enc; - uint8_t fde_count_enc; - uint8_t fde_table_enc; - uintptr_t eh_frame_ptr; - uint32_t fde_count; -} eh_frame_hdr_info_t; - -/* - * CIE information. -*/ - -typedef struct { - uint8_t version; - uint32_t code_align; - uint32_t data_align; - uint32_t reg; - uint32_t aug_z; - uint8_t aug_L; - uint8_t aug_R; - uint8_t aug_S; - uint32_t aug_P; -} cie_info_t; - -/* - * FDE information. -*/ - -typedef struct { - uint32_t start; - uint32_t length; // number of instructions covered by FDE - uint32_t aug_z; - uint32_t aug_L; -} fde_info_t; - -/* - * Dwarf state. -*/ - -/* Stack of states: required for DW_CFA_remember_state/DW_CFA_restore_state - 30 should be enough */ -#define DWARF_STATES_STACK 30 - -typedef struct { - char rule; // rule: o - offset(value); r - register(value) - uint32_t value; // value -} reg_rule_t; - -/* Dwarf preserved number of registers for x86. */ - -#define DWARF_REGISTERS 17 - -typedef struct { - uintptr_t loc; // location (ip) - uint8_t cfa_reg; // index of register where CFA location stored - intptr_t cfa_off; // offset - reg_rule_t regs[DWARF_REGISTERS]; // dwarf preserved registers for x86 -} dwarf_state_t; - -/* DWARF registers we are caring about. */ - -#define DWARF_EAX 0 -#define DWARF_ECX 1 -#define DWARF_EDX 2 -#define DWARF_EBX 3 -#define DWARF_ESP 4 -#define DWARF_EBP 5 -#define DWARF_ESI 6 -#define DWARF_EDI 7 -#define DWARF_EIP 8 - - diff --git a/libcorkscrew/arch-x86/ptrace-x86.c b/libcorkscrew/arch-x86/ptrace-x86.c deleted file mode 100755 index 9c49b93..0000000 --- a/libcorkscrew/arch-x86/ptrace-x86.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../ptrace-arch.h" - -#include <stddef.h> -#include <elf.h> -#include <cutils/log.h> - -static void load_eh_frame_hdr(pid_t pid, map_info_t* mi, uintptr_t *eh_frame_hdr) { - uint32_t elf_phoff; - uint32_t elf_phentsize_ehsize; - uint32_t elf_shentsize_phnum; - if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), - &elf_phentsize_ehsize) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), - &elf_shentsize_phnum)) { - uint32_t elf_phentsize = elf_phentsize_ehsize >> 16; - uint32_t elf_phnum = elf_shentsize_phnum & 0xffff; - for (uint32_t i = 0; i < elf_phnum; i++) { - uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize; - uint32_t elf_phdr_type; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) { - break; - } - if (elf_phdr_type == PT_GNU_EH_FRAME) { - uint32_t elf_phdr_offset; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset), - &elf_phdr_offset)) { - break; - } - *eh_frame_hdr = mi->start + elf_phdr_offset; - ALOGV("Parsed .eh_frame_hdr info for %s: start=0x%08x", mi->name, *eh_frame_hdr); - return; - } - } - } - *eh_frame_hdr = 0; -} - -void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) { - load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr); -} - -void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)), - map_info_data_t* data __attribute__((unused))) { -} diff --git a/libcorkscrew/backtrace-arch.h b/libcorkscrew/backtrace-arch.h deleted file mode 100644 index a46f80b..0000000 --- a/libcorkscrew/backtrace-arch.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Architecture dependent functions. */ - -#ifndef _CORKSCREW_BACKTRACE_ARCH_H -#define _CORKSCREW_BACKTRACE_ARCH_H - -#include "ptrace-arch.h" -#include <corkscrew/backtrace.h> - -#include <signal.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Rewind the program counter by one instruction. */ -uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc); - -ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext, - const map_info_t* map_info_list, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); - -ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); - -#ifdef __cplusplus -} -#endif - -#endif // _CORKSCREW_BACKTRACE_ARCH_H diff --git a/libcorkscrew/backtrace-helper.c b/libcorkscrew/backtrace-helper.c deleted file mode 100644 index bf9d3f3..0000000 --- a/libcorkscrew/backtrace-helper.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "backtrace-helper.h" - -#include <cutils/log.h> - -backtrace_frame_t* add_backtrace_entry(uintptr_t pc, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth, - size_t* ignored_frames, size_t* returned_frames) { - if (*ignored_frames < ignore_depth) { - *ignored_frames += 1; - return NULL; - } - if (*returned_frames >= max_depth) { - return NULL; - } - backtrace_frame_t* frame = &backtrace[*returned_frames]; - frame->absolute_pc = pc; - frame->stack_top = 0; - frame->stack_size = 0; - *returned_frames += 1; - return frame; -} diff --git a/libcorkscrew/backtrace-helper.h b/libcorkscrew/backtrace-helper.h deleted file mode 100644 index 4d8a874..0000000 --- a/libcorkscrew/backtrace-helper.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Backtrace helper functions. */ - -#ifndef _CORKSCREW_BACKTRACE_HELPER_H -#define _CORKSCREW_BACKTRACE_HELPER_H - -#include <corkscrew/backtrace.h> -#include <sys/types.h> -#include <stdbool.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Add a program counter to a backtrace if it will fit. - * Returns the newly added frame, or NULL if none. - */ -backtrace_frame_t* add_backtrace_entry(uintptr_t pc, - backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth, - size_t* ignored_frames, size_t* returned_frames); - -#ifdef __cplusplus -} -#endif - -#endif // _CORKSCREW_BACKTRACE_HELPER_H diff --git a/libcorkscrew/backtrace.c b/libcorkscrew/backtrace.c deleted file mode 100644 index f1dd61d..0000000 --- a/libcorkscrew/backtrace.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "backtrace-arch.h" -#include "backtrace-helper.h" -#include "ptrace-arch.h" -#include <corkscrew/map_info.h> -#include <corkscrew/symbol_table.h> -#include <corkscrew/ptrace.h> -#include <corkscrew/demangle.h> - -#include <unistd.h> -#include <signal.h> -#include <stdlib.h> -#include <string.h> -#include <pthread.h> -#include <unwind.h> -#include <cutils/log.h> -#include <cutils/atomic.h> - -#define __USE_GNU // For dladdr(3) in glibc. -#include <dlfcn.h> - -#if defined(__BIONIC__) - -// Bionic implements and exports gettid but only implements tgkill. -extern int tgkill(int tgid, int tid, int sig); - -#elif defined(__APPLE__) - -#include <sys/syscall.h> - -// Mac OS >= 10.6 has a system call equivalent to Linux's gettid(). -static pid_t gettid() { - return syscall(SYS_thread_selfid); -} - -#else - -// glibc doesn't implement or export either gettid or tgkill. - -#include <unistd.h> -#include <sys/syscall.h> - -static pid_t gettid() { - return syscall(__NR_gettid); -} - -static int tgkill(int tgid, int tid, int sig) { - return syscall(__NR_tgkill, tgid, tid, sig); -} - -#endif - -typedef struct { - backtrace_frame_t* backtrace; - size_t ignore_depth; - size_t max_depth; - size_t ignored_frames; - size_t returned_frames; - memory_t memory; -} backtrace_state_t; - -static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) { - backtrace_state_t* state = (backtrace_state_t*)arg; - uintptr_t pc = _Unwind_GetIP(context); - if (pc) { - // TODO: Get information about the stack layout from the _Unwind_Context. - // This will require a new architecture-specific function to query - // the appropriate registers. Current callers of unwind_backtrace - // don't need this information, so we won't bother collecting it just yet. - add_backtrace_entry(rewind_pc_arch(&state->memory, pc), state->backtrace, - state->ignore_depth, state->max_depth, - &state->ignored_frames, &state->returned_frames); - } - return state->returned_frames < state->max_depth ? _URC_NO_REASON : _URC_END_OF_STACK; -} - -ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - ALOGV("Unwinding current thread %d.", gettid()); - - map_info_t* milist = acquire_my_map_info_list(); - - backtrace_state_t state; - state.backtrace = backtrace; - state.ignore_depth = ignore_depth; - state.max_depth = max_depth; - state.ignored_frames = 0; - state.returned_frames = 0; - init_memory(&state.memory, milist); - - _Unwind_Reason_Code rc = _Unwind_Backtrace(unwind_backtrace_callback, &state); - - release_my_map_info_list(milist); - - if (state.returned_frames) { - return state.returned_frames; - } - return rc == _URC_END_OF_STACK ? 0 : -1; -} - -#ifdef CORKSCREW_HAVE_ARCH -static const int32_t STATE_DUMPING = -1; -static const int32_t STATE_DONE = -2; -static const int32_t STATE_CANCEL = -3; - -static pthread_mutex_t g_unwind_signal_mutex = PTHREAD_MUTEX_INITIALIZER; -static volatile struct { - int32_t tid_state; - const map_info_t* map_info_list; - backtrace_frame_t* backtrace; - size_t ignore_depth; - size_t max_depth; - size_t returned_frames; -} g_unwind_signal_state; - -static void unwind_backtrace_thread_signal_handler(int n __attribute__((unused)), siginfo_t* siginfo, void* sigcontext) { - if (!android_atomic_acquire_cas(gettid(), STATE_DUMPING, &g_unwind_signal_state.tid_state)) { - g_unwind_signal_state.returned_frames = unwind_backtrace_signal_arch( - siginfo, sigcontext, - g_unwind_signal_state.map_info_list, - g_unwind_signal_state.backtrace, - g_unwind_signal_state.ignore_depth, - g_unwind_signal_state.max_depth); - android_atomic_release_store(STATE_DONE, &g_unwind_signal_state.tid_state); - } else { - ALOGV("Received spurious SIGURG on thread %d that was intended for thread %d.", - gettid(), android_atomic_acquire_load(&g_unwind_signal_state.tid_state)); - } -} -#endif - -ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth) { - if (tid == gettid()) { - return unwind_backtrace(backtrace, ignore_depth + 1, max_depth); - } - - ALOGV("Unwinding thread %d from thread %d.", tid, gettid()); - - // TODO: there's no tgkill(2) on Mac OS, so we'd either need the - // mach_port_t or the pthread_t rather than the tid. -#if defined(CORKSCREW_HAVE_ARCH) && !defined(__APPLE__) - struct sigaction act; - struct sigaction oact; - memset(&act, 0, sizeof(act)); - act.sa_sigaction = unwind_backtrace_thread_signal_handler; - act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; - sigemptyset(&act.sa_mask); - - pthread_mutex_lock(&g_unwind_signal_mutex); - map_info_t* milist = acquire_my_map_info_list(); - - ssize_t frames = -1; - if (!sigaction(SIGURG, &act, &oact)) { - g_unwind_signal_state.map_info_list = milist; - g_unwind_signal_state.backtrace = backtrace; - g_unwind_signal_state.ignore_depth = ignore_depth; - g_unwind_signal_state.max_depth = max_depth; - g_unwind_signal_state.returned_frames = 0; - android_atomic_release_store(tid, &g_unwind_signal_state.tid_state); - - // Signal the specific thread that we want to dump. - int32_t tid_state = tid; - if (tgkill(getpid(), tid, SIGURG)) { - ALOGV("Failed to send SIGURG to thread %d.", tid); - } else { - // Wait for the other thread to start dumping the stack, or time out. - int wait_millis = 250; - for (;;) { - tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); - if (tid_state != tid) { - break; - } - if (wait_millis--) { - ALOGV("Waiting for thread %d to start dumping the stack...", tid); - usleep(1000); - } else { - ALOGV("Timed out waiting for thread %d to start dumping the stack.", tid); - break; - } - } - } - - // Try to cancel the dump if it has not started yet. - if (tid_state == tid) { - if (!android_atomic_acquire_cas(tid, STATE_CANCEL, &g_unwind_signal_state.tid_state)) { - ALOGV("Canceled thread %d stack dump.", tid); - tid_state = STATE_CANCEL; - } else { - tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); - } - } - - // Wait indefinitely for the dump to finish or be canceled. - // We cannot apply a timeout here because the other thread is accessing state that - // is owned by this thread, such as milist. It should not take very - // long to take the dump once started. - while (tid_state == STATE_DUMPING) { - ALOGV("Waiting for thread %d to finish dumping the stack...", tid); - usleep(1000); - tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); - } - - if (tid_state == STATE_DONE) { - frames = g_unwind_signal_state.returned_frames; - } - - sigaction(SIGURG, &oact, NULL); - } - - release_my_map_info_list(milist); - pthread_mutex_unlock(&g_unwind_signal_mutex); - return frames; -#else - return -1; -#endif -} - -ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { -#ifdef CORKSCREW_HAVE_ARCH - return unwind_backtrace_ptrace_arch(tid, context, backtrace, ignore_depth, max_depth); -#else - return -1; -#endif -} - -static void init_backtrace_symbol(backtrace_symbol_t* symbol, uintptr_t pc) { - symbol->relative_pc = pc; - symbol->relative_symbol_addr = 0; - symbol->map_name = NULL; - symbol->symbol_name = NULL; - symbol->demangled_name = NULL; -} - -void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames, - backtrace_symbol_t* backtrace_symbols) { - map_info_t* milist = acquire_my_map_info_list(); - for (size_t i = 0; i < frames; i++) { - const backtrace_frame_t* frame = &backtrace[i]; - backtrace_symbol_t* symbol = &backtrace_symbols[i]; - init_backtrace_symbol(symbol, frame->absolute_pc); - - const map_info_t* mi = find_map_info(milist, frame->absolute_pc); - if (mi) { - symbol->relative_pc = frame->absolute_pc - mi->start; - if (mi->name[0]) { - symbol->map_name = strdup(mi->name); - } - Dl_info info; - if (dladdr((const void*)frame->absolute_pc, &info) && info.dli_sname) { - symbol->relative_symbol_addr = (uintptr_t)info.dli_saddr - - (uintptr_t)info.dli_fbase; - symbol->symbol_name = strdup(info.dli_sname); - symbol->demangled_name = demangle_symbol_name(symbol->symbol_name); - } - } - } - release_my_map_info_list(milist); -} - -void get_backtrace_symbols_ptrace(const ptrace_context_t* context, - const backtrace_frame_t* backtrace, size_t frames, - backtrace_symbol_t* backtrace_symbols) { - for (size_t i = 0; i < frames; i++) { - const backtrace_frame_t* frame = &backtrace[i]; - backtrace_symbol_t* symbol = &backtrace_symbols[i]; - init_backtrace_symbol(symbol, frame->absolute_pc); - - const map_info_t* mi; - const symbol_t* s; - find_symbol_ptrace(context, frame->absolute_pc, &mi, &s); - if (mi) { - symbol->relative_pc = frame->absolute_pc - mi->start; - if (mi->name[0]) { - symbol->map_name = strdup(mi->name); - } - } - if (s) { - symbol->relative_symbol_addr = s->start; - symbol->symbol_name = strdup(s->name); - symbol->demangled_name = demangle_symbol_name(symbol->symbol_name); - } - } -} - -void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames) { - for (size_t i = 0; i < frames; i++) { - backtrace_symbol_t* symbol = &backtrace_symbols[i]; - free(symbol->map_name); - free(symbol->symbol_name); - free(symbol->demangled_name); - init_backtrace_symbol(symbol, 0); - } -} - -void format_backtrace_line(unsigned frameNumber, const backtrace_frame_t* frame __attribute__((unused)), - const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize) { - const char* mapName = symbol->map_name ? symbol->map_name : "<unknown>"; - const char* symbolName = symbol->demangled_name ? symbol->demangled_name : symbol->symbol_name; - int fieldWidth = (bufferSize - 80) / 2; - if (symbolName) { - uint32_t pc_offset = symbol->relative_pc - symbol->relative_symbol_addr; - if (pc_offset) { - snprintf(buffer, bufferSize, "#%02u pc %08x %.*s (%.*s+%u)", - frameNumber, (unsigned int) symbol->relative_pc, - fieldWidth, mapName, fieldWidth, symbolName, pc_offset); - } else { - snprintf(buffer, bufferSize, "#%02u pc %08x %.*s (%.*s)", - frameNumber, (unsigned int) symbol->relative_pc, - fieldWidth, mapName, fieldWidth, symbolName); - } - } else { - snprintf(buffer, bufferSize, "#%02u pc %08x %.*s", - frameNumber, (unsigned int) symbol->relative_pc, - fieldWidth, mapName); - } -} diff --git a/libcorkscrew/demangle.c b/libcorkscrew/demangle.c deleted file mode 100644 index 30ab1b0..0000000 --- a/libcorkscrew/demangle.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include <corkscrew/demangle.h> - -#include <cutils/log.h> - -extern char *__cxa_demangle (const char *mangled, char *buf, size_t *len, - int *status); - -char* demangle_symbol_name(const char* name) { -#if defined(__APPLE__) - // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7. - if (name != NULL && name[0] != '_') { - return NULL; - } -#endif - // __cxa_demangle handles NULL by returning NULL - return __cxa_demangle(name, 0, 0, 0); -} diff --git a/libcorkscrew/map_info.c b/libcorkscrew/map_info.c deleted file mode 100644 index 93dffbf..0000000 --- a/libcorkscrew/map_info.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include <corkscrew/map_info.h> - -#include <ctype.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> -#include <pthread.h> -#include <unistd.h> -#include <cutils/log.h> -#include <sys/time.h> - -#if defined(__APPLE__) - -// Mac OS vmmap(1) output: -// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n -// 012345678901234567890123456789012345678901234567890123456789 -// 0 1 2 3 4 5 -static map_info_t* parse_vmmap_line(const char* line) { - unsigned long int start; - unsigned long int end; - char permissions[4]; - int name_pos; - if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n", - &start, &end, permissions, &name_pos) != 3) { - return NULL; - } - - const char* name = line + name_pos; - size_t name_len = strlen(name); - - map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len); - if (mi != NULL) { - mi->start = start; - mi->end = end; - mi->is_readable = permissions[0] == 'r'; - mi->is_writable = permissions[1] == 'w'; - mi->is_executable = permissions[2] == 'x'; - mi->data = NULL; - memcpy(mi->name, name, name_len); - mi->name[name_len - 1] = '\0'; - ALOGV("Parsed map: start=0x%08x, end=0x%08x, " - "is_readable=%d, is_writable=%d is_executable=%d, name=%s", - mi->start, mi->end, - mi->is_readable, mi->is_writable, mi->is_executable, mi->name); - } - return mi; -} - -map_info_t* load_map_info_list(pid_t pid) { - char cmd[1024]; - snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid); - FILE* fp = popen(cmd, "r"); - if (fp == NULL) { - return NULL; - } - - char line[1024]; - map_info_t* milist = NULL; - while (fgets(line, sizeof(line), fp) != NULL) { - map_info_t* mi = parse_vmmap_line(line); - if (mi != NULL) { - mi->next = milist; - milist = mi; - } - } - pclose(fp); - return milist; -} - -#else - -// Linux /proc/<pid>/maps lines: -// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n -// 012345678901234567890123456789012345678901234567890123456789 -// 0 1 2 3 4 5 -static map_info_t* parse_maps_line(const char* line) -{ - unsigned long int start; - unsigned long int end; - char permissions[5]; - int name_pos; - if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end, - permissions, &name_pos) != 3) { - return NULL; - } - - while (isspace(line[name_pos])) { - name_pos += 1; - } - const char* name = line + name_pos; - size_t name_len = strlen(name); - if (name_len && name[name_len - 1] == '\n') { - name_len -= 1; - } - - map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len + 1); - if (mi) { - mi->start = start; - mi->end = end; - mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r'; - mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w'; - mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x'; - mi->data = NULL; - memcpy(mi->name, name, name_len); - mi->name[name_len] = '\0'; - ALOGV("Parsed map: start=0x%08x, end=0x%08x, " - "is_readable=%d, is_writable=%d, is_executable=%d, name=%s", - mi->start, mi->end, - mi->is_readable, mi->is_writable, mi->is_executable, mi->name); - } - return mi; -} - -map_info_t* load_map_info_list(pid_t tid) { - char path[PATH_MAX]; - char line[1024]; - FILE* fp; - map_info_t* milist = NULL; - - snprintf(path, PATH_MAX, "/proc/%d/maps", tid); - fp = fopen(path, "r"); - if (fp) { - while(fgets(line, sizeof(line), fp)) { - map_info_t* mi = parse_maps_line(line); - if (mi) { - mi->next = milist; - milist = mi; - } - } - fclose(fp); - } - return milist; -} - -#endif - -void free_map_info_list(map_info_t* milist) { - while (milist) { - map_info_t* next = milist->next; - free(milist); - milist = next; - } -} - -const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr) { - const map_info_t* mi = milist; - while (mi && !(addr >= mi->start && addr < mi->end)) { - mi = mi->next; - } - return mi; -} - -bool is_readable_map(const map_info_t* milist, uintptr_t addr) { - const map_info_t* mi = find_map_info(milist, addr); - return mi && mi->is_readable; -} - -bool is_writable_map(const map_info_t* milist, uintptr_t addr) { - const map_info_t* mi = find_map_info(milist, addr); - return mi && mi->is_writable; -} - -bool is_executable_map(const map_info_t* milist, uintptr_t addr) { - const map_info_t* mi = find_map_info(milist, addr); - return mi && mi->is_executable; -} - -static pthread_mutex_t g_my_map_info_list_mutex = PTHREAD_MUTEX_INITIALIZER; -static map_info_t* g_my_map_info_list = NULL; - -static const int64_t MAX_CACHE_AGE = 5 * 1000 * 1000000LL; - -typedef struct { - uint32_t refs; - int64_t timestamp; -} my_map_info_data_t; - -static int64_t now_ns() { -#if defined(HAVE_POSIX_CLOCKS) - struct timespec t; - t.tv_sec = t.tv_nsec = 0; - clock_gettime(CLOCK_MONOTONIC, &t); - return t.tv_sec * 1000000000LL + t.tv_nsec; -#else - struct timeval t; - gettimeofday(&t, NULL); - return t.tv_sec * 1000000000LL + t.tv_usec * 1000LL; -#endif -} - -static void dec_ref(map_info_t* milist, my_map_info_data_t* data) { - if (!--data->refs) { - ALOGV("Freed my_map_info_list %p.", milist); - free(data); - free_map_info_list(milist); - } -} - -map_info_t* acquire_my_map_info_list() { - pthread_mutex_lock(&g_my_map_info_list_mutex); - - int64_t time = now_ns(); - if (g_my_map_info_list != NULL) { - my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; - int64_t age = time - data->timestamp; - if (age >= MAX_CACHE_AGE) { - ALOGV("Invalidated my_map_info_list %p, age=%lld.", g_my_map_info_list, age); - dec_ref(g_my_map_info_list, data); - g_my_map_info_list = NULL; - } else { - ALOGV("Reusing my_map_info_list %p, age=%lld.", g_my_map_info_list, age); - } - } - - if (g_my_map_info_list == NULL) { - my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t)); - g_my_map_info_list = load_map_info_list(getpid()); - if (g_my_map_info_list != NULL) { - ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list); - g_my_map_info_list->data = data; - data->refs = 1; - data->timestamp = time; - } else { - free(data); - } - } - - map_info_t* milist = g_my_map_info_list; - if (milist) { - my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; - data->refs += 1; - } - - pthread_mutex_unlock(&g_my_map_info_list_mutex); - return milist; -} - -void release_my_map_info_list(map_info_t* milist) { - if (milist) { - pthread_mutex_lock(&g_my_map_info_list_mutex); - - my_map_info_data_t* data = (my_map_info_data_t*)milist->data; - dec_ref(milist, data); - - pthread_mutex_unlock(&g_my_map_info_list_mutex); - } -} - -void flush_my_map_info_list() { - pthread_mutex_lock(&g_my_map_info_list_mutex); - - if (g_my_map_info_list != NULL) { - my_map_info_data_t* data = (my_map_info_data_t*) g_my_map_info_list->data; - dec_ref(g_my_map_info_list, data); - g_my_map_info_list = NULL; - } - - pthread_mutex_unlock(&g_my_map_info_list_mutex); -} diff --git a/libcorkscrew/ptrace-arch.h b/libcorkscrew/ptrace-arch.h deleted file mode 100755 index 0bcff63..0000000 --- a/libcorkscrew/ptrace-arch.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Architecture dependent functions. */ - -#ifndef _CORKSCREW_PTRACE_ARCH_H -#define _CORKSCREW_PTRACE_ARCH_H - -#include <corkscrew/ptrace.h> -#include <corkscrew/map_info.h> -#include <corkscrew/symbol_table.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Custom extra data we stuff into map_info_t structures as part - * of our ptrace_context_t. */ -typedef struct { -#ifdef __arm__ - uintptr_t exidx_start; - size_t exidx_size; -#elif __mips__ - uintptr_t eh_frame_hdr; -#elif __i386__ - uintptr_t eh_frame_hdr; -#endif - symbol_table_t* symbol_table; -} map_info_data_t; - -void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data); -void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data); - -#ifdef __cplusplus -} -#endif - -#endif // _CORKSCREW_PTRACE_ARCH_H diff --git a/libcorkscrew/ptrace.c b/libcorkscrew/ptrace.c deleted file mode 100644 index be58f7f..0000000 --- a/libcorkscrew/ptrace.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "ptrace-arch.h" -#include <corkscrew/ptrace.h> - -#include <errno.h> -#include <stdlib.h> -#include <sys/ptrace.h> -#include <cutils/log.h> - -static const uint32_t ELF_MAGIC = 0x464C457f; // "ELF\0177" - -#ifndef PAGE_SIZE -#define PAGE_SIZE 4096 -#endif - -#ifndef PAGE_MASK -#define PAGE_MASK (~(PAGE_SIZE - 1)) -#endif - -void init_memory(memory_t* memory, const map_info_t* map_info_list) { - memory->tid = -1; - memory->map_info_list = map_info_list; -} - -void init_memory_ptrace(memory_t* memory, pid_t tid) { - memory->tid = tid; - memory->map_info_list = NULL; -} - -bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value) { - ALOGV("try_get_word: reading word at %p", (void*) ptr); - if (ptr & 3) { - ALOGV("try_get_word: invalid pointer %p", (void*) ptr); - *out_value = 0xffffffffL; - return false; - } - if (memory->tid < 0) { - if (!is_readable_map(memory->map_info_list, ptr)) { - ALOGV("try_get_word: pointer %p not in a readable map", (void*) ptr); - *out_value = 0xffffffffL; - return false; - } - *out_value = *(uint32_t*)ptr; - return true; - } else { -#if defined(__APPLE__) - ALOGV("no ptrace on Mac OS"); - return false; -#else - // ptrace() returns -1 and sets errno when the operation fails. - // To disambiguate -1 from a valid result, we clear errno beforehand. - errno = 0; - *out_value = ptrace(PTRACE_PEEKTEXT, memory->tid, (void*)ptr, NULL); - if (*out_value == 0xffffffffL && errno) { - ALOGV("try_get_word: invalid pointer 0x%08x reading from tid %d, " - "ptrace() errno=%d", ptr, memory->tid, errno); - return false; - } - return true; -#endif - } -} - -bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value) { - memory_t memory; - init_memory_ptrace(&memory, tid); - return try_get_word(&memory, ptr, out_value); -} - -static void load_ptrace_map_info_data(pid_t pid, map_info_t* mi) { - if (mi->is_executable && mi->is_readable) { - uint32_t elf_magic; - if (try_get_word_ptrace(pid, mi->start, &elf_magic) && elf_magic == ELF_MAGIC) { - map_info_data_t* data = (map_info_data_t*)calloc(1, sizeof(map_info_data_t)); - if (data) { - mi->data = data; - if (mi->name[0]) { - data->symbol_table = load_symbol_table(mi->name); - } -#ifdef CORKSCREW_HAVE_ARCH - load_ptrace_map_info_data_arch(pid, mi, data); -#endif - } - } - } -} - -ptrace_context_t* load_ptrace_context(pid_t pid) { - ptrace_context_t* context = - (ptrace_context_t*)calloc(1, sizeof(ptrace_context_t)); - if (context) { - context->map_info_list = load_map_info_list(pid); - for (map_info_t* mi = context->map_info_list; mi; mi = mi->next) { - load_ptrace_map_info_data(pid, mi); - } - } - return context; -} - -static void free_ptrace_map_info_data(map_info_t* mi) { - map_info_data_t* data = (map_info_data_t*)mi->data; - if (data) { - if (data->symbol_table) { - free_symbol_table(data->symbol_table); - } -#ifdef CORKSCREW_HAVE_ARCH - free_ptrace_map_info_data_arch(mi, data); -#endif - free(data); - mi->data = NULL; - } -} - -void free_ptrace_context(ptrace_context_t* context) { - for (map_info_t* mi = context->map_info_list; mi; mi = mi->next) { - free_ptrace_map_info_data(mi); - } - free_map_info_list(context->map_info_list); - free(context); -} - -void find_symbol_ptrace(const ptrace_context_t* context, - uintptr_t addr, const map_info_t** out_map_info, const symbol_t** out_symbol) { - const map_info_t* mi = find_map_info(context->map_info_list, addr); - const symbol_t* symbol = NULL; - if (mi) { - const map_info_data_t* data = (const map_info_data_t*)mi->data; - if (data && data->symbol_table) { - symbol = find_symbol(data->symbol_table, addr - mi->start); - } - } - *out_map_info = mi; - *out_symbol = symbol; -} diff --git a/libcorkscrew/symbol_table.c b/libcorkscrew/symbol_table.c deleted file mode 100644 index 982ccc8..0000000 --- a/libcorkscrew/symbol_table.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include <corkscrew/symbol_table.h> - -#include <stdbool.h> -#include <stdlib.h> -#include <fcntl.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/mman.h> -#include <cutils/log.h> - -#if defined(__APPLE__) -#else - -#include <elf.h> - -static bool is_elf(Elf32_Ehdr* e) { - return (e->e_ident[EI_MAG0] == ELFMAG0 && - e->e_ident[EI_MAG1] == ELFMAG1 && - e->e_ident[EI_MAG2] == ELFMAG2 && - e->e_ident[EI_MAG3] == ELFMAG3); -} - -#endif - -// Compare function for qsort -static int qcompar(const void *a, const void *b) { - const symbol_t* asym = (const symbol_t*)a; - const symbol_t* bsym = (const symbol_t*)b; - if (asym->start > bsym->start) return 1; - if (asym->start < bsym->start) return -1; - return 0; -} - -// Compare function for bsearch -static int bcompar(const void *key, const void *element) { - uintptr_t addr = *(const uintptr_t*)key; - const symbol_t* symbol = (const symbol_t*)element; - if (addr < symbol->start) return -1; - if (addr >= symbol->end) return 1; - return 0; -} - -symbol_table_t* load_symbol_table(const char *filename) { - symbol_table_t* table = NULL; -#if !defined(__APPLE__) - ALOGV("Loading symbol table from '%s'.", filename); - - int fd = open(filename, O_RDONLY); - if (fd < 0) { - goto out; - } - - struct stat sb; - if (fstat(fd, &sb)) { - goto out_close; - } - - size_t length = sb.st_size; - char* base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); - if (base == MAP_FAILED) { - goto out_close; - } - - // Parse the file header - Elf32_Ehdr *hdr = (Elf32_Ehdr*)base; - if (!is_elf(hdr)) { - goto out_close; - } - Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff); - - // Search for the dynamic symbols section - int sym_idx = -1; - int dynsym_idx = -1; - for (Elf32_Half i = 0; i < hdr->e_shnum; i++) { - if (shdr[i].sh_type == SHT_SYMTAB) { - sym_idx = i; - } - if (shdr[i].sh_type == SHT_DYNSYM) { - dynsym_idx = i; - } - } - if (dynsym_idx == -1 && sym_idx == -1) { - goto out_unmap; - } - - table = malloc(sizeof(symbol_table_t)); - if(!table) { - goto out_unmap; - } - table->num_symbols = 0; - - Elf32_Sym *dynsyms = NULL; - int dynnumsyms = 0; - char *dynstr = NULL; - if (dynsym_idx != -1) { - dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset); - dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize; - int dynstr_idx = shdr[dynsym_idx].sh_link; - dynstr = base + shdr[dynstr_idx].sh_offset; - } - - Elf32_Sym *syms = NULL; - int numsyms = 0; - char *str = NULL; - if (sym_idx != -1) { - syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset); - numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize; - int str_idx = shdr[sym_idx].sh_link; - str = base + shdr[str_idx].sh_offset; - } - - int dynsymbol_count = 0; - if (dynsym_idx != -1) { - // Iterate through the dynamic symbol table, and count how many symbols - // are actually defined - for (int i = 0; i < dynnumsyms; i++) { - if (dynsyms[i].st_shndx != SHN_UNDEF) { - dynsymbol_count++; - } - } - } - - size_t symbol_count = 0; - if (sym_idx != -1) { - // Iterate through the symbol table, and count how many symbols - // are actually defined - for (int i = 0; i < numsyms; i++) { - if (syms[i].st_shndx != SHN_UNDEF - && str[syms[i].st_name] - && syms[i].st_value - && syms[i].st_size) { - symbol_count++; - } - } - } - - // Now, create an entry in our symbol table structure for each symbol... - table->num_symbols += symbol_count + dynsymbol_count; - table->symbols = malloc(table->num_symbols * sizeof(symbol_t)); - if (!table->symbols) { - free(table); - table = NULL; - goto out_unmap; - } - - size_t symbol_index = 0; - if (dynsym_idx != -1) { - // ...and populate them - for (int i = 0; i < dynnumsyms; i++) { - if (dynsyms[i].st_shndx != SHN_UNDEF) { - table->symbols[symbol_index].name = strdup(dynstr + dynsyms[i].st_name); - table->symbols[symbol_index].start = dynsyms[i].st_value; - table->symbols[symbol_index].end = dynsyms[i].st_value + dynsyms[i].st_size; - ALOGV(" [%d] '%s' 0x%08x-0x%08x (DYNAMIC)", - symbol_index, table->symbols[symbol_index].name, - table->symbols[symbol_index].start, table->symbols[symbol_index].end); - symbol_index += 1; - } - } - } - - if (sym_idx != -1) { - // ...and populate them - for (int i = 0; i < numsyms; i++) { - if (syms[i].st_shndx != SHN_UNDEF - && str[syms[i].st_name] - && syms[i].st_value - && syms[i].st_size) { - table->symbols[symbol_index].name = strdup(str + syms[i].st_name); - table->symbols[symbol_index].start = syms[i].st_value; - table->symbols[symbol_index].end = syms[i].st_value + syms[i].st_size; - ALOGV(" [%d] '%s' 0x%08x-0x%08x", - symbol_index, table->symbols[symbol_index].name, - table->symbols[symbol_index].start, table->symbols[symbol_index].end); - symbol_index += 1; - } - } - } - - // Sort the symbol table entries, so they can be bsearched later - qsort(table->symbols, table->num_symbols, sizeof(symbol_t), qcompar); - -out_unmap: - munmap(base, length); - -out_close: - close(fd); -#endif - -out: - return table; -} - -void free_symbol_table(symbol_table_t* table) { - if (table) { - for (size_t i = 0; i < table->num_symbols; i++) { - free(table->symbols[i].name); - } - free(table->symbols); - free(table); - } -} - -const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr) { - if (!table) return NULL; - return (const symbol_t*)bsearch(&addr, table->symbols, table->num_symbols, - sizeof(symbol_t), bcompar); -} diff --git a/libcorkscrew/test.cpp b/libcorkscrew/test.cpp deleted file mode 100644 index 22dfa7d..0000000 --- a/libcorkscrew/test.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include <corkscrew/backtrace.h> -#include <corkscrew/symbol_table.h> -#include <stdio.h> -#include <stdlib.h> - -int do_backtrace(float /* just to test demangling */) { - const size_t MAX_DEPTH = 32; - backtrace_frame_t* frames = (backtrace_frame_t*) malloc(sizeof(backtrace_frame_t) * MAX_DEPTH); - ssize_t frame_count = unwind_backtrace(frames, 0, MAX_DEPTH); - fprintf(stderr, "frame_count=%d\n", (int) frame_count); - if (frame_count <= 0) { - return frame_count; - } - - backtrace_symbol_t* backtrace_symbols = (backtrace_symbol_t*) malloc(sizeof(backtrace_symbol_t) * frame_count); - get_backtrace_symbols(frames, frame_count, backtrace_symbols); - - for (size_t i = 0; i < (size_t) frame_count; ++i) { - char line[MAX_BACKTRACE_LINE_LENGTH]; - format_backtrace_line(i, &frames[i], &backtrace_symbols[i], - line, MAX_BACKTRACE_LINE_LENGTH); - if (backtrace_symbols[i].symbol_name != NULL) { - // get_backtrace_symbols found the symbol's name with dladdr(3). - fprintf(stderr, " %s\n", line); - } else { - // We don't have a symbol. Maybe this is a static symbol, and - // we can look it up? - symbol_table_t* symbols = NULL; - if (backtrace_symbols[i].map_name != NULL) { - symbols = load_symbol_table(backtrace_symbols[i].map_name); - } - const symbol_t* symbol = NULL; - if (symbols != NULL) { - symbol = find_symbol(symbols, frames[i].absolute_pc); - } - if (symbol != NULL) { - int offset = frames[i].absolute_pc - symbol->start; - fprintf(stderr, " %s (%s%+d)\n", line, symbol->name, offset); - } else { - fprintf(stderr, " %s (\?\?\?)\n", line); - } - free_symbol_table(symbols); - } - } - - free_backtrace_symbols(backtrace_symbols, frame_count); - free(backtrace_symbols); - free(frames); - return frame_count; -} - -struct C { - int g(int i); -}; - -__attribute__ ((noinline)) int C::g(int i) { - if (i == 0) { - return do_backtrace(0.1); - } - return g(i - 1); -} - -extern "C" __attribute__ ((noinline)) int f() { - C c; - return c.g(5); -} - -int main() { - flush_my_map_info_list(); - f(); - - flush_my_map_info_list(); - f(); - - return 0; -} diff --git a/libcutils/debugger.c b/libcutils/debugger.c index 7d907fc..056de5d 100644 --- a/libcutils/debugger.c +++ b/libcutils/debugger.c @@ -15,6 +15,7 @@ */ #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <cutils/debugger.h> @@ -28,9 +29,9 @@ int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) { } debugger_msg_t msg; + memset(&msg, 0, sizeof(msg)); msg.tid = tid; msg.action = DEBUGGER_ACTION_DUMP_TOMBSTONE; - msg.abort_msg_address = 0; int result = 0; if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) { @@ -62,9 +63,9 @@ int dump_backtrace_to_file(pid_t tid, int fd) { } debugger_msg_t msg; + memset(&msg, 0, sizeof(msg)); msg.tid = tid; msg.action = DEBUGGER_ACTION_DUMP_BACKTRACE; - msg.abort_msg_address = 0; int result = 0; if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) { diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c index d20d217..7c65843 100644 --- a/libcutils/sched_policy.c +++ b/libcutils/sched_policy.c @@ -38,6 +38,7 @@ static inline SchedPolicy _policy(SchedPolicy p) #if defined(HAVE_ANDROID_OS) && defined(HAVE_SCHED_H) && defined(HAVE_PTHREADS) +#include <linux/prctl.h> #include <sched.h> #include <pthread.h> @@ -53,6 +54,9 @@ static inline SchedPolicy _policy(SchedPolicy p) #define CAN_SET_SP_SYSTEM 0 // non-zero means to implement set_sched_policy(tid, SP_SYSTEM) +// timer slack value in nS enforced when the thread moves to background +#define TIMER_SLACK_BG 40000000 + static pthread_once_t the_once = PTHREAD_ONCE_INIT; static int __sys_supports_schedgroups = -1; @@ -324,6 +328,8 @@ int set_sched_policy(int tid, SchedPolicy policy) ¶m); } + prctl(PR_SET_TIMERSLACK_PID, policy == SP_BACKGROUND ? TIMER_SLACK_BG : 0, tid); + return 0; } diff --git a/libdiskconfig/diskutils.c b/libdiskconfig/diskutils.c index 7659632..5d0ee62 100644 --- a/libdiskconfig/diskutils.c +++ b/libdiskconfig/diskutils.c @@ -41,7 +41,7 @@ write_raw_image(const char *dst, const char *src, loff_t offset, int test) int done = 0; uint64_t total = 0; - ALOGI("Writing RAW image '%s' to '%s' (offset=%llu)", src, dst, offset); + ALOGI("Writing RAW image '%s' to '%s' (offset=%llu)", src, dst, (unsigned long long)offset); if ((src_fd = open(src, O_RDONLY)) < 0) { ALOGE("Could not open %s for reading (errno=%d).", src, errno); goto fail; @@ -54,7 +54,7 @@ write_raw_image(const char *dst, const char *src, loff_t offset, int test) } if (lseek64(dst_fd, offset, SEEK_SET) != offset) { - ALOGE("Could not seek to offset %lld in %s.", offset, dst); + ALOGE("Could not seek to offset %lld in %s.", (long long)offset, dst); goto fail; } } @@ -102,7 +102,7 @@ write_raw_image(const char *dst, const char *src, loff_t offset, int test) if (dst_fd >= 0) fsync(dst_fd); - ALOGI("Wrote %" PRIu64 " bytes to %s @ %lld", total, dst, offset); + ALOGI("Wrote %" PRIu64 " bytes to %s @ %lld", total, dst, (long long)offset); close(src_fd); if (dst_fd >= 0) diff --git a/libdiskconfig/write_lst.c b/libdiskconfig/write_lst.c index 826ef7a..90b1c82 100644 --- a/libdiskconfig/write_lst.c +++ b/libdiskconfig/write_lst.c @@ -71,18 +71,18 @@ wlist_commit(int fd, struct write_list *lst, int test) { for(; lst; lst = lst->next) { if (lseek64(fd, lst->offset, SEEK_SET) != (loff_t)lst->offset) { - ALOGE("Cannot seek to the specified position (%lld).", lst->offset); + ALOGE("Cannot seek to the specified position (%lld).", (long long)lst->offset); goto fail; } if (!test) { if (write(fd, lst->data, lst->len) != (int)lst->len) { ALOGE("Failed writing %u bytes at position %lld.", lst->len, - lst->offset); + (long long)lst->offset); goto fail; } } else - ALOGI("Would write %d bytes @ offset %lld.", lst->len, lst->offset); + ALOGI("Would write %d bytes @ offset %lld.", lst->len, (long long)lst->offset); } return 0; diff --git a/liblog/logd_write.c b/liblog/logd_write.c index 94722d3..bd36a65 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -146,9 +146,15 @@ static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) ret = -errno; } } while (ret == -EINTR); - - return ret; #else + static const unsigned header_length = 3; + struct iovec newVec[nr + header_length]; + typeof_log_id_t log_id_buf; + uint16_t tid; + struct timespec ts; + log_time realtime_ts; + size_t i, payload_size; + if (getuid() == AID_LOGD) { /* * ignore log messages we send to ourself. @@ -181,29 +187,33 @@ static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) * }; * }; */ - static const unsigned header_length = 3; - struct iovec newVec[nr + header_length]; - typeof_log_id_t log_id_buf = log_id; - uint16_t tid = gettid(); + + log_id_buf = log_id; + tid = gettid(); newVec[0].iov_base = (unsigned char *) &log_id_buf; newVec[0].iov_len = sizeof_log_id_t; newVec[1].iov_base = (unsigned char *) &tid; newVec[1].iov_len = sizeof(tid); - struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); - log_time realtime_ts; realtime_ts.tv_sec = ts.tv_sec; realtime_ts.tv_nsec = ts.tv_nsec; newVec[2].iov_base = (unsigned char *) &realtime_ts; newVec[2].iov_len = sizeof(log_time); - size_t i; - for (i = header_length; i < nr + header_length; i++) { - newVec[i].iov_base = vec[i-header_length].iov_base; - newVec[i].iov_len = vec[i-header_length].iov_len; + for (payload_size = 0, i = header_length; i < nr + header_length; i++) { + newVec[i].iov_base = vec[i - header_length].iov_base; + payload_size += newVec[i].iov_len = vec[i - header_length].iov_len; + + if (payload_size > LOGGER_ENTRY_MAX_PAYLOAD) { + newVec[i].iov_len -= payload_size - LOGGER_ENTRY_MAX_PAYLOAD; + if (newVec[i].iov_len) { + ++i; + } + break; + } } /* @@ -212,7 +222,7 @@ static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) * ENOTCONN occurs if logd dies. * EAGAIN occurs if logd is overloaded. */ - ret = writev(logd_fd, newVec, nr + header_length); + ret = writev(logd_fd, newVec, i); if (ret < 0) { ret = -errno; if (ret == -ENOTCONN) { @@ -234,8 +244,13 @@ static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) } } } - return ret; + + if (ret > (ssize_t)(sizeof_log_id_t + sizeof(tid) + sizeof(log_time))) { + ret -= sizeof_log_id_t + sizeof(tid) + sizeof(log_time); + } #endif + + return ret; } #if FAKE_LOG_DEVICE @@ -407,9 +422,15 @@ void __android_log_assert(const char *cond, const char *tag, strcpy(buf, "Unspecified assertion failed"); } +#if __BIONIC__ + // Ensure debuggerd gets to see what went wrong by keeping the C library in the loop. + extern __noreturn void __android_fatal(const char* tag, const char* format, ...) __printflike(2, 3); + __android_fatal(tag ? tag : "", "%s", buf); +#else __android_log_write(ANDROID_LOG_FATAL, tag, buf); - __builtin_trap(); /* trap so we have a chance to debug the situation */ +#endif + /* NOTREACHED */ } int __android_log_bwrite(int32_t tag, const void *payload, size_t len) diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c index c29c28f..8c707ad 100644 --- a/liblog/logd_write_kern.c +++ b/liblog/logd_write_kern.c @@ -272,9 +272,15 @@ void __android_log_assert(const char *cond, const char *tag, strcpy(buf, "Unspecified assertion failed"); } +#if __BIONIC__ + // Ensure debuggerd gets to see what went wrong by keeping the C library in the loop. + extern __noreturn void __android_fatal(const char* tag, const char* format, ...) __printflike(2, 3); + __android_fatal(tag ? tag : "", "%s", buf); +#else __android_log_write(ANDROID_LOG_FATAL, tag, buf); - __builtin_trap(); /* trap so we have a chance to debug the situation */ +#endif + /* NOTREACHED */ } int __android_log_bwrite(int32_t tag, const void *payload, size_t len) diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk index db06cf7..d1d9115 100644 --- a/liblog/tests/Android.mk +++ b/liblog/tests/Android.mk @@ -32,7 +32,7 @@ benchmark_c_flags := \ benchmark_src_files := \ benchmark_main.cpp \ - liblog_benchmark.cpp \ + liblog_benchmark.cpp # Build benchmarks for the device. Run with: # adb shell liblog-benchmarks @@ -59,10 +59,22 @@ test_c_flags := \ -g \ -Wall -Wextra \ -Werror \ - -fno-builtin \ + -fno-builtin test_src_files := \ - liblog_test.cpp \ + liblog_test.cpp + +# to prevent breaking the build if bionic not relatively visible to us +ifneq ($(wildcard $(LOCAL_PATH)/../../../../bionic/libc/bionic/libc_logging.cpp),) + +test_src_files += \ + libc_test.cpp + +ifndef ($(TARGET_USES_LOGD),false) +test_c_flags += -DTARGET_USES_LOGD +endif + +endif # Build tests for the device (with .so). Run with: # adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp new file mode 100644 index 0000000..0abc375 --- /dev/null +++ b/liblog/tests/libc_test.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fcntl.h> +#include <sys/cdefs.h> + +#include <gtest/gtest.h> + +// Should be in bionic test suite, *but* we are using liblog to confirm +// end-to-end logging, so let the overly cute oedipus complex begin ... +#include "../../../../bionic/libc/bionic/libc_logging.cpp" // not Standalone +#define _ANDROID_LOG_H // Priorities redefined +#define _LIBS_LOG_LOG_H // log ids redefined +typedef unsigned char log_id_t; // log_id_t missing as a result +#ifdef TARGET_USES_LOGD +#define _LIBS_LOG_LOG_READ_H // log_time redefined +#endif + +#include <log/log.h> +#include <log/logger.h> +#include <log/log_read.h> + +TEST(libc, __libc_android_log_event_int) { + struct logger_list *logger_list; + + pid_t pid = getpid(); + + ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( + LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid))); + + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + int value = ts.tv_nsec; + + __libc_android_log_event_int(0, value); + usleep(1000000); + + int count = 0; + + for (;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + ASSERT_EQ(log_msg.entry.pid, pid); + + if ((log_msg.entry.len != (4 + 1 + 4)) + || ((int)log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char *eventData = log_msg.msg(); + + int incoming = (eventData[0] & 0xFF) | + ((eventData[1] & 0xFF) << 8) | + ((eventData[2] & 0xFF) << 16) | + ((eventData[3] & 0xFF) << 24); + + if (incoming != 0) { + continue; + } + + if (eventData[4] != EVENT_TYPE_INT) { + continue; + } + + incoming = (eventData[4 + 1 + 0] & 0xFF) | + ((eventData[4 + 1 + 1] & 0xFF) << 8) | + ((eventData[4 + 1 + 2] & 0xFF) << 16) | + ((eventData[4 + 1 + 3] & 0xFF) << 24); + + if (incoming == value) { + ++count; + } + } + + EXPECT_EQ(1, count); + + android_logger_list_close(logger_list); +} + +TEST(libc, __libc_fatal_no_abort) { + struct logger_list *logger_list; + + pid_t pid = getpid(); + + ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( + (log_id_t)LOG_ID_MAIN, O_RDONLY | O_NDELAY, 1000, pid))); + + char b[80]; + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + + __libc_fatal_no_abort("%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec); + snprintf(b, sizeof(b),"%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec); + usleep(1000000); + + int count = 0; + + for (;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + ASSERT_EQ(log_msg.entry.pid, pid); + + if ((int)log_msg.id() != LOG_ID_MAIN) { + continue; + } + + char *data = log_msg.msg(); + + if ((*data == ANDROID_LOG_FATAL) + && !strcmp(data + 1, "libc") + && !strcmp(data + 1 + strlen(data + 1) + 1, b)) { + ++count; + } + } + + EXPECT_EQ(1, count); + + android_logger_list_close(logger_list); +} diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp index 24ae738..92b68ac 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp @@ -482,11 +482,68 @@ TEST(liblog, max_payload) { } } + android_logger_list_close(logger_list); + EXPECT_EQ(true, matches); EXPECT_LE(sizeof(max_payload_buf), static_cast<size_t>(max_len)); +} + +TEST(liblog, too_big_payload) { + pid_t pid = getpid(); + static const char big_payload_tag[] = "TEST_big_payload_XXXX"; + char tag[sizeof(big_payload_tag)]; + memcpy(tag, big_payload_tag, sizeof(tag)); + snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF); + + std::string longString(3266519, 'x'); + + ssize_t ret = LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, + ANDROID_LOG_INFO, tag, longString.c_str())); + + struct logger_list *logger_list; + + ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( + LOG_ID_SYSTEM, O_RDONLY | O_NDELAY, 100, 0))); + + ssize_t max_len = 0; + + for(;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) { + continue; + } + + char *data = log_msg.msg() + 1; + + if (strcmp(data, tag)) { + continue; + } + + data += strlen(data) + 1; + + const char *left = data; + const char *right = longString.c_str(); + while (*left && *right && (*left == *right)) { + ++left; + ++right; + } + + if (max_len <= (left - data)) { + max_len = left - data + 1; + } + } android_logger_list_close(logger_list); + + EXPECT_LE(LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag), + static_cast<size_t>(max_len)); + + EXPECT_EQ(ret, max_len + sizeof(big_payload_tag)); } TEST(liblog, dual_reader) { diff --git a/libsparse/Android.mk b/libsparse/Android.mk index a21e090..4aba168 100644 --- a/libsparse/Android.mk +++ b/libsparse/Android.mk @@ -81,6 +81,8 @@ LOCAL_STATIC_LIBRARIES := \ include $(BUILD_EXECUTABLE) +ifneq ($(HOST_OS),windows) + include $(CLEAR_VARS) LOCAL_SRC_FILES := append2simg.c LOCAL_MODULE := append2simg @@ -89,6 +91,7 @@ LOCAL_STATIC_LIBRARIES := \ libz include $(BUILD_HOST_EXECUTABLE) +endif include $(CLEAR_VARS) LOCAL_MODULE := simg_dump.py diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c index 180584f..65e6cc2 100644 --- a/libsparse/append2simg.c +++ b/libsparse/append2simg.c @@ -16,6 +16,7 @@ #define _FILE_OFFSET_BITS 64 #define _LARGEFILE64_SOURCE 1 +#define _GNU_SOURCE #include <errno.h> #include <fcntl.h> @@ -56,6 +57,11 @@ int main(int argc, char *argv[]) char *input_path; off64_t input_len; + int tmp_fd; + char *tmp_path; + + int ret; + if (argc == 3) { output_path = argv[1]; input_path = argv[2]; @@ -64,6 +70,12 @@ int main(int argc, char *argv[]) exit(-1); } + ret = asprintf(&tmp_path, "%s.append2simg", output_path); + if (ret < 0) { + fprintf(stderr, "Couldn't allocate filename\n"); + exit(-1); + } + output = open(output_path, O_RDWR | O_BINARY); if (output < 0) { fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno)); @@ -99,14 +111,30 @@ int main(int argc, char *argv[]) } sparse_output->len += input_len; + tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664); + if (tmp_fd < 0) { + fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno)); + exit(-1); + } + lseek64(output, 0, SEEK_SET); - if (sparse_file_write(sparse_output, output, false, true, false) < 0) { + if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) { fprintf(stderr, "Failed to write sparse file\n"); exit(-1); } sparse_file_destroy(sparse_output); + close(tmp_fd); close(output); close(input); + + ret = rename(tmp_path, output_path); + if (ret < 0) { + fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno)); + exit(-1); + } + + free(tmp_path); + exit(0); } diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk index 1451b0d..246f954 100644 --- a/libsysutils/Android.mk +++ b/libsysutils/Android.mk @@ -18,7 +18,7 @@ LOCAL_MODULE:= libsysutils LOCAL_C_INCLUDES := -LOCAL_CFLAGS := +LOCAL_CFLAGS := -Werror LOCAL_SHARED_LIBRARIES := libcutils liblog diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp index 01ed54e..e7b3dd6 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp @@ -92,7 +92,6 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { char *qlimit = tmp + sizeof(tmp) - 1; bool esc = false; bool quote = false; - int k; bool haveCmdNum = !mWithSeq; memset(argv, 0, sizeof(argv)); @@ -166,7 +165,7 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { goto overflow; argv[argc++] = strdup(tmp); #if 0 - for (k = 0; k < argc; k++) { + for (int k = 0; k < argc; k++) { SLOGD("arg[%d] = '%s'", k, argv[k]); } #endif diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index 34f2016..1c9c70a 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -109,7 +109,7 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, if (ifaddr->ifa_family == AF_INET) { struct in_addr *addr4 = (struct in_addr *) RTA_DATA(rta); if (RTA_PAYLOAD(rta) < sizeof(*addr4)) { - SLOGE("Short IPv4 address (%d bytes) in %s", + SLOGE("Short IPv4 address (%zu bytes) in %s", RTA_PAYLOAD(rta), msgtype); continue; } @@ -117,7 +117,7 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, } else if (ifaddr->ifa_family == AF_INET6) { struct in6_addr *addr6 = (struct in6_addr *) RTA_DATA(rta); if (RTA_PAYLOAD(rta) < sizeof(*addr6)) { - SLOGE("Short IPv6 address (%d bytes) in %s", + SLOGE("Short IPv6 address (%zu bytes) in %s", RTA_PAYLOAD(rta), msgtype); continue; } @@ -152,7 +152,7 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, } if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) { - SLOGE("Short IFA_CACHEINFO (%d vs. %d bytes) in %s", + SLOGE("Short IFA_CACHEINFO (%zu vs. %zu bytes) in %s", RTA_PAYLOAD(rta), sizeof(cacheinfo), msgtype); continue; } @@ -174,7 +174,6 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, } /* -<<<<<<< HEAD * Parse a RTM_NEWNDUSEROPT message. */ bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) { @@ -399,7 +398,6 @@ bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) { const char *s = buffer; const char *end; int param_idx = 0; - int i; int first = 1; if (size == 0) diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp index a7bf92b..1f3fd0e 100644 --- a/logd/CommandListener.cpp +++ b/logd/CommandListener.cpp @@ -21,6 +21,7 @@ #include <netinet/in.h> #include <string.h> #include <stdlib.h> +#include <sys/prctl.h> #include <sys/socket.h> #include <sys/types.h> @@ -66,8 +67,13 @@ CommandListener::ClearCmd::ClearCmd(LogBuffer *buf) , mBuf(*buf) { } +static void setname() { + prctl(PR_SET_NAME, "logd.control"); +} + int CommandListener::ClearCmd::runCommand(SocketClient *cli, int argc, char **argv) { + setname(); if (!clientHasLogCredentials(cli)) { cli->sendMsg("Permission Denied"); return 0; @@ -96,6 +102,7 @@ CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf) int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli, int argc, char **argv) { + setname(); if (argc < 2) { cli->sendMsg("Missing Argument"); return 0; @@ -121,6 +128,7 @@ CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf) int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli, int argc, char **argv) { + setname(); if (!clientHasLogCredentials(cli)) { cli->sendMsg("Permission Denied"); return 0; @@ -154,6 +162,7 @@ CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf) int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli, int argc, char **argv) { + setname(); if (argc < 2) { cli->sendMsg("Missing Argument"); return 0; @@ -197,6 +206,7 @@ static void package_string(char **strp) { int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli, int argc, char **argv) { + setname(); uid_t uid = cli->getUid(); gid_t gid = cli->getGid(); if (clientHasLogCredentials(cli)) { @@ -236,6 +246,7 @@ CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf) int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli, int /*argc*/, char ** /*argv*/) { + setname(); char *buf = NULL; mBuf.formatPrune(&buf); if (!buf) { @@ -255,6 +266,7 @@ CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf) int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli, int argc, char **argv) { + setname(); if (!clientHasLogCredentials(cli)) { cli->sendMsg("Permission Denied"); return 0; diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp index ea6eece..add0f0e 100644 --- a/logd/LogAudit.cpp +++ b/logd/LogAudit.cpp @@ -16,9 +16,11 @@ #include <ctype.h> #include <errno.h> +#include <limits.h> #include <stdarg.h> #include <stdlib.h> #include <sys/klog.h> +#include <sys/prctl.h> #include <sys/uio.h> #include "libaudit.h" @@ -34,8 +36,14 @@ LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmsg) } bool LogAudit::onDataAvailable(SocketClient *cli) { + prctl(PR_SET_NAME, "logd.auditd"); + struct audit_message rep; + rep.nlh.nlmsg_type = 0; + rep.nlh.nlmsg_len = 0; + rep.data[0] = '\0'; + if (audit_get_reply(cli->getSocket(), &rep, GET_REPLY_BLOCKING, 0) < 0) { SLOGE("Failed on audit_get_reply with error: %s", strerror(errno)); return false; @@ -143,11 +151,8 @@ int LogAudit::logPrint(const char *fmt, ...) { strcpy(newstr + 1 + l, str); free(str); - unsigned short len = n; // cap to internal maximum - if (len != n) { - len = -1; - } - logbuf->log(AUDIT_LOG_ID, now, uid, pid, tid, newstr, len); + logbuf->log(AUDIT_LOG_ID, now, uid, pid, tid, newstr, + (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); reader->notifyNewLog(); free(newstr); diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp index 8dcab87..38a237c 100644 --- a/logd/LogBuffer.cpp +++ b/logd/LogBuffer.cpp @@ -167,17 +167,14 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows) { if ((id != LOG_ID_CRASH) && mPrune.worstUidEnabled()) { LidStatistics &l = stats.id(id); - UidStatisticsCollection::iterator iu; - for (iu = l.begin(); iu != l.end(); ++iu) { - UidStatistics *u = (*iu); - size_t sizes = u->sizes(); - if (worst_sizes < sizes) { - second_worst_sizes = worst_sizes; - worst_sizes = sizes; - worst = u->getUid(); - } - if ((second_worst_sizes < sizes) && (sizes < worst_sizes)) { - second_worst_sizes = sizes; + l.sort(); + UidStatisticsCollection::iterator iu = l.begin(); + if (iu != l.end()) { + UidStatistics *u = *iu; + worst = u->getUid(); + worst_sizes = u->sizes(); + if (++iu != l.end()) { + second_worst_sizes = (*iu)->sizes(); } } } diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp index ed5b391..6ff4d3a 100644 --- a/logd/LogListener.cpp +++ b/logd/LogListener.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <limits.h> +#include <sys/prctl.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> @@ -31,7 +33,9 @@ LogListener::LogListener(LogBuffer *buf, LogReader *reader) { } bool LogListener::onDataAvailable(SocketClient *cli) { - char buffer[sizeof_log_id_t + sizeof(log_time) + sizeof(char) + prctl(PR_SET_NAME, "logd.writer"); + + char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) + LOGGER_ENTRY_MAX_PAYLOAD]; struct iovec iov = { buffer, sizeof(buffer) }; memset(buffer, 0, sizeof(buffer)); @@ -97,11 +101,10 @@ bool LogListener::onDataAvailable(SocketClient *cli) { // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a // truncated message to the logs. - unsigned short len = n; // cap to internal maximum - if (len == n) { - logbuf->log(log_id, realtime, cred->uid, cred->pid, tid, msg, len); - reader->notifyNewLog(); - } + + logbuf->log(log_id, realtime, cred->uid, cred->pid, tid, msg, + (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); + reader->notifyNewLog(); return true; } diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp index 51aa2ad..8458c19 100644 --- a/logd/LogReader.cpp +++ b/logd/LogReader.cpp @@ -16,6 +16,7 @@ #include <ctype.h> #include <poll.h> +#include <sys/prctl.h> #include <sys/socket.h> #include <cutils/sockets.h> @@ -36,6 +37,8 @@ void LogReader::notifyNewLog() { } bool LogReader::onDataAvailable(SocketClient *cli) { + prctl(PR_SET_NAME, "logd.reader"); + char buffer[255]; int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1); diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp index 5146030..f2b9a26 100644 --- a/logd/LogStatistics.cpp +++ b/logd/LogStatistics.cpp @@ -31,6 +31,7 @@ PidStatistics::PidStatistics(pid_t pid, char *name) , mSizes(0) , mElements(0) , name(name) + , mGone(false) { } #ifdef DO_NOT_ERROR_IF_PIDSTATISTICS_USES_A_COPY_CONSTRUCTOR @@ -41,6 +42,7 @@ PidStatistics::PidStatistics(const PidStatistics ©) , mElementsTotal(copy->mElementsTotal) , mSizes(copy->mSizes) , mElements(copy->mElements) + , mGone(copy->mGone) { } #endif @@ -48,6 +50,20 @@ PidStatistics::~PidStatistics() { free(name); } +bool PidStatistics::pidGone() { + if (mGone) { + return true; + } + if (pid == gone) { + return true; + } + if (kill(pid, 0) && (errno != EPERM)) { + mGone = true; + return true; + } + return false; +} + void PidStatistics::setName(char *new_name) { free(name); name = new_name; @@ -63,7 +79,7 @@ void PidStatistics::add(unsigned short size) { bool PidStatistics::subtract(unsigned short size) { mSizes -= size; --mElements; - return (mElements == 0) && kill(pid, 0) && (errno != EPERM); + return (mElements == 0) && pidGone(); } void PidStatistics::addTotal(size_t size, size_t element) { @@ -76,7 +92,7 @@ void PidStatistics::addTotal(size_t size, size_t element) { // must call free to release return value char *PidStatistics::pidToName(pid_t pid) { char *retval = NULL; - if (pid != PidStatistics::gone) { + if (pid != gone) { char buffer[512]; snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid); int fd = open(buffer, O_RDONLY); @@ -96,7 +112,9 @@ char *PidStatistics::pidToName(pid_t pid) { } UidStatistics::UidStatistics(uid_t uid) - : uid(uid) { + : uid(uid) + , mSizes(0) + , mElements(0) { Pids.clear(); } @@ -109,6 +127,9 @@ UidStatistics::~UidStatistics() { } void UidStatistics::add(unsigned short size, pid_t pid) { + mSizes += size; + ++mElements; + PidStatistics *p; PidStatisticsCollection::iterator last; PidStatisticsCollection::iterator it; @@ -116,18 +137,11 @@ void UidStatistics::add(unsigned short size, pid_t pid) { p = *it; if (pid == p->getPid()) { p->add(size); - // poor-man sort, bubble upwards if bigger than last - if ((last != it) && ((*last)->sizesTotal() < p->sizesTotal())) { - Pids.erase(it); - Pids.insert(last, p); - } return; } } - // poor-man sort, insert if bigger than last or last is the gone entry. - bool insert = (last != it) - && ((p->getPid() == p->gone) - || ((*last)->sizesTotal() < (size_t) size)); + // insert if the gone entry. + bool insert = (last != it) && (p->getPid() == p->gone); p = new PidStatistics(pid, pidToName(pid)); if (insert) { Pids.insert(last, p); @@ -138,6 +152,9 @@ void UidStatistics::add(unsigned short size, pid_t pid) { } void UidStatistics::subtract(unsigned short size, pid_t pid) { + mSizes -= size; + --mElements; + PidStatisticsCollection::iterator it; for (it = begin(); it != end(); ++it) { PidStatistics *p = *it; @@ -166,28 +183,57 @@ void UidStatistics::subtract(unsigned short size, pid_t pid) { } } +void UidStatistics::sort() { + for (bool pass = true; pass;) { + pass = false; + PidStatisticsCollection::iterator it = begin(); + if (it != end()) { + PidStatisticsCollection::iterator lt = it; + PidStatistics *l = (*lt); + while (++it != end()) { + PidStatistics *n = (*it); + if ((n->getPid() != n->gone) && (n->sizes() > l->sizes())) { + pass = true; + Pids.erase(it); + Pids.insert(lt, n); + it = lt; + n = l; + } + lt = it; + l = n; + } + } + } +} + size_t UidStatistics::sizes(pid_t pid) { - size_t sizes = 0; + if (pid == pid_all) { + return sizes(); + } + PidStatisticsCollection::iterator it; for (it = begin(); it != end(); ++it) { PidStatistics *p = *it; - if ((pid == pid_all) || (pid == p->getPid())) { - sizes += p->sizes(); + if (pid == p->getPid()) { + return p->sizes(); } } - return sizes; + return 0; } size_t UidStatistics::elements(pid_t pid) { - size_t elements = 0; + if (pid == pid_all) { + return elements(); + } + PidStatisticsCollection::iterator it; for (it = begin(); it != end(); ++it) { PidStatistics *p = *it; - if ((pid == pid_all) || (pid == p->getPid())) { - elements += p->elements(); + if (pid == p->getPid()) { + return p->elements(); } } - return elements; + return 0; } size_t UidStatistics::sizesTotal(pid_t pid) { @@ -266,6 +312,29 @@ void LidStatistics::subtract(unsigned short size, uid_t uid, pid_t pid) { } } +void LidStatistics::sort() { + for (bool pass = true; pass;) { + pass = false; + UidStatisticsCollection::iterator it = begin(); + if (it != end()) { + UidStatisticsCollection::iterator lt = it; + UidStatistics *l = (*lt); + while (++it != end()) { + UidStatistics *n = (*it); + if (n->sizes() > l->sizes()) { + pass = true; + Uids.erase(it); + Uids.insert(lt, n); + it = lt; + n = l; + } + lt = it; + l = n; + } + } + } +} + size_t LidStatistics::sizes(uid_t uid, pid_t pid) { size_t sizes = 0; UidStatisticsCollection::iterator it; @@ -455,13 +524,22 @@ void LogStatistics::format(char **buf, short spaces = 2; log_id_for_each(i) { - if (logMask & (1 << i)) { - oldLength = string.length(); - if (spaces < 0) { - spaces = 0; - } - string.appendFormat("%*s%s", spaces, "", android_log_id_to_name(i)); - spaces += spaces_total + oldLength - string.length(); + if (!logMask & (1 << i)) { + continue; + } + oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s%s", spaces, "", android_log_id_to_name(i)); + spaces += spaces_total + oldLength - string.length(); + + LidStatistics &l = id(i); + l.sort(); + + UidStatisticsCollection::iterator iu; + for (iu = l.begin(); iu != l.end(); ++iu) { + (*iu)->sort(); } } @@ -597,8 +675,7 @@ void LogStatistics::format(char **buf, sizes, sizesTotal); android::String8 pd(""); - pd.appendFormat("%u%c", pid, - (kill(pid, 0) && (errno != EPERM)) ? '?' : ' '); + pd.appendFormat("%u%c", pid, p->pidGone() ? '?' : ' '); string.appendFormat("\n%-7s%-*s %-7s%s", line ? "" : android_log_id_to_name(i), @@ -703,14 +780,15 @@ void LogStatistics::format(char **buf, spaces = 0; uid_t u = up->getUid(); - pid_t p = (*pt)->getPid(); + PidStatistics *pp = *pt; + pid_t p = pp->getPid(); intermediate = string.format(oneline ? ((p == PidStatistics::gone) ? "%d/?" - : "%d/%d") + : "%d/%d%c") : "%d", - u, p); + u, p, pp->pidGone() ? '?' : '\0'); string.appendFormat(first ? "\n%-12s" : "%-12s", intermediate.string()); intermediate.clear(); @@ -747,8 +825,8 @@ void LogStatistics::format(char **buf, size_t gone_els = 0; for(; pt != up->end(); ++pt) { - PidStatistics *pp = *pt; - pid_t p = pp->getPid(); + pp = *pt; + p = pp->getPid(); // If a PID no longer has any current logs, and is not // active anymore, skip & report totals for gone. @@ -760,7 +838,7 @@ void LogStatistics::format(char **buf, continue; } els = pp->elements(); - bool gone = kill(p, 0) && (errno != EPERM); + bool gone = pp->pidGone(); if (gone && (els == 0)) { // ToDo: garbage collection: move this statistical bucket // from its current UID/PID to UID/? (races and diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h index 12c68d5..3733137 100644 --- a/logd/LogStatistics.h +++ b/logd/LogStatistics.h @@ -37,6 +37,7 @@ class PidStatistics { size_t mElements; char *name; + bool mGone; public: static const pid_t gone = (pid_t) -1; @@ -46,6 +47,7 @@ public: ~PidStatistics(); pid_t getPid() const { return pid; } + bool pidGone(); char *getName() const { return name; } void setName(char *name); @@ -70,6 +72,9 @@ class UidStatistics { PidStatisticsCollection Pids; + size_t mSizes; + size_t mElements; + public: UidStatistics(uid_t uid); ~UidStatistics(); @@ -81,11 +86,17 @@ public: void add(unsigned short size, pid_t pid); void subtract(unsigned short size, pid_t pid); + void sort(); static const pid_t pid_all = (pid_t) -1; - size_t sizes(pid_t pid = pid_all); - size_t elements(pid_t pid = pid_all); + // fast track current value + size_t sizes() const { return mSizes; }; + size_t elements() const { return mElements; }; + + // statistical track + size_t sizes(pid_t pid); + size_t elements(pid_t pid); size_t sizesTotal(pid_t pid = pid_all); size_t elementsTotal(pid_t pid = pid_all); @@ -108,6 +119,7 @@ public: void add(unsigned short size, uid_t uid, pid_t pid); void subtract(unsigned short size, uid_t uid, pid_t pid); + void sort(); static const pid_t pid_all = (pid_t) -1; static const uid_t uid_all = (uid_t) -1; @@ -145,6 +157,7 @@ public: void add(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid); void subtract(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid); + void sort(); // fast track current value by id only size_t sizes(log_id_t id) const { return mSizes[id]; } diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp index c32ac2d..1a9a548 100644 --- a/logd/LogTimes.cpp +++ b/logd/LogTimes.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <sys/prctl.h> + #include "FlushCommand.h" #include "LogBuffer.h" #include "LogTimes.h" @@ -107,6 +109,8 @@ void LogTimeEntry::threadStop(void *obj) { } void *LogTimeEntry::threadStart(void *obj) { + prctl(PR_SET_NAME, "logd.reader.per"); + LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj); pthread_cleanup_push(threadStop, obj); diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp index f739865..c6c7b23 100644 --- a/logd/LogWhiteBlackList.cpp +++ b/logd/LogWhiteBlackList.cpp @@ -47,7 +47,7 @@ void Prune::format(char **strp) { } PruneList::PruneList() - : mWorstUidEnabled(false) { + : mWorstUidEnabled(true) { mNaughty.clear(); mNice.clear(); } diff --git a/logd/README.property b/logd/README.property new file mode 100644 index 0000000..5d92d09 --- /dev/null +++ b/logd/README.property @@ -0,0 +1,12 @@ +The properties that logd responds to are: + +name type default description +logd.auditd bool true Enable selinux audit daemon +logd.auditd.dmesg bool true selinux audit messages duplicated and + sent on to dmesg log +logd.statistics.dgram_qlen bool false Record dgram_qlen statistics. This + represents a performance impact and + is used to determine the platform's + minimum domain socket network FIFO + size (see source for details) based + on typical load (logcat -S to view) diff --git a/logd/main.cpp b/logd/main.cpp index 04eef4a..ece5a3a 100644 --- a/logd/main.cpp +++ b/logd/main.cpp @@ -107,16 +107,31 @@ static int drop_privs() { return 0; } +// Property helper +static bool property_get_bool(const char *key, bool def) { + char property[PROPERTY_VALUE_MAX]; + property_get(key, property, ""); + + if (!strcasecmp(property, "true")) { + return true; + } + if (!strcasecmp(property, "false")) { + return false; + } + + return def; +} + // Foreground waits for exit of the three main persistent threads that // are started here. The three threads are created to manage UNIX // domain client sockets for writing, reading and controlling the user // space logger. Additional transitory per-client threads are created // for each reader once they register. int main() { + bool auditd = property_get_bool("logd.auditd", true); + int fdDmesg = -1; - char dmesg[PROPERTY_VALUE_MAX]; - property_get("logd.auditd.dmesg", dmesg, "1"); - if (atol(dmesg)) { + if (auditd && property_get_bool("logd.auditd.dmesg", true)) { fdDmesg = open("/dev/kmsg", O_WRONLY); } @@ -135,9 +150,7 @@ int main() { LogBuffer *logBuf = new LogBuffer(times); - char dgram_qlen_statistics[PROPERTY_VALUE_MAX]; - property_get("logd.dgram_qlen.statistics", dgram_qlen_statistics, ""); - if (atol(dgram_qlen_statistics)) { + if (property_get_bool("logd.statistics.dgram_qlen", false)) { logBuf->enableDgramQlenStatistics(); } @@ -171,11 +184,13 @@ int main() { // initiated log messages. New log entries are added to LogBuffer // and LogReader is notified to send updates to connected clients. - // failure is an option ... messages are in dmesg (required by standard) - LogAudit *al = new LogAudit(logBuf, reader, fdDmesg); - if (al->startListener()) { - delete al; - close(fdDmesg); + if (auditd) { + // failure is an option ... messages are in dmesg (required by standard) + LogAudit *al = new LogAudit(logBuf, reader, fdDmesg); + if (al->startListener()) { + delete al; + close(fdDmesg); + } } pause(); diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp index 23e6146..731aff6 100644 --- a/logd/tests/logd_test.cpp +++ b/logd/tests/logd_test.cpp @@ -23,6 +23,7 @@ #include <gtest/gtest.h> #include "cutils/sockets.h" +#include "log/log.h" #include "log/logger.h" #define __unused __attribute__((__unused__)) @@ -96,8 +97,9 @@ static char *find_benchmark_spam(char *cp) // // main: UID/PID Total size/num Now UID/PID[?] Total // 0 7500306/304207 71608/3183 0/4225? 7454388/303656 + // <wrap> 93432/1012 // -or- - // 0/gone 7454388/303656 + // 0/gone 7454388/303656 93432/1012 // // basically if we see a *large* number of 0/????? entries unsigned long value; @@ -126,6 +128,15 @@ static char *find_benchmark_spam(char *cp) value = value * 10ULL + *cp - '0'; ++cp; } + if (*cp != '/') { + value = 0; + continue; + } + while (isdigit(*++cp)); + while (*cp == ' ') ++cp; + if (!isdigit(*cp)) { + value = 0; + } } } while ((value < 900000ULL) && *cp); return cp; @@ -624,7 +635,45 @@ TEST(logd, benchmark) { #endif ASSERT_TRUE(NULL != buf); - EXPECT_TRUE(find_benchmark_spam(buf) != NULL); + + char *benchmark_statistics_found = find_benchmark_spam(buf); + ASSERT_TRUE(benchmark_statistics_found != NULL); + + // Check how effective the SPAM filter is, parse out Now size. + // Total Now + // 0/4225? 7454388/303656 31488/755 + // ^-- benchmark_statistics_found + + unsigned long nowSize = atol(benchmark_statistics_found); delete [] buf; + + ASSERT_NE(0UL, nowSize); + + int sock = socket_local_client("logd", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + static const unsigned long expected_absolute_minimum_log_size = 65536UL; + unsigned long totalSize = expected_absolute_minimum_log_size; + if (sock >= 0) { + static const char getSize[] = { + 'g', 'e', 't', 'L', 'o', 'g', 'S', 'i', 'z', 'e', ' ', + LOG_ID_MAIN + '0', '\0' + }; + if (write(sock, getSize, sizeof(getSize)) > 0) { + char buffer[80]; + memset(buffer, 0, sizeof(buffer)); + read(sock, buffer, sizeof(buffer)); + totalSize = atol(buffer); + if (totalSize < expected_absolute_minimum_log_size) { + totalSize = expected_absolute_minimum_log_size; + } + } + close(sock); + } + // logd allows excursions to 110% of total size + totalSize = (totalSize * 11 ) / 10; + + // 50% threshold for SPAM filter (<20% typical, lots of engineering margin) + ASSERT_GT(totalSize, nowSize * 2); } diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index b8fe716..eff24c3 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc @@ -91,3 +91,5 @@ subsystem adf /sys/devices/virtual/input/input* enable 0660 root input /sys/devices/virtual/input/input* poll_delay 0660 root input /sys/devices/virtual/usb_composite/* enable 0664 root system +/sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system +/sys/devices/system/cpu/cpu* cpufreq/scaling_min_freq 0664 system system |
