diff options
151 files changed, 3420 insertions, 5569 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 74ec29d..0254bd2 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -51,3 +51,5 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/reboot) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c index 6fc55df..0c46a0c 100755 --- a/adb/usb_vendors.c +++ b/adb/usb_vendors.c @@ -140,6 +140,8 @@ #define VENDOR_ID_PMC 0x04DA // Positivo's USB Vendor ID #define VENDOR_ID_POSITIVO 0x1662 +// Prestigio's USB Vendor ID +#define VENDOR_ID_PRESTIGIO 0x29e4 // Qisda's USB Vendor ID #define VENDOR_ID_QISDA 0x1D45 // Qualcomm's USB Vendor ID @@ -237,6 +239,7 @@ int builtInVendorIds[] = { VENDOR_ID_PHILIPS, VENDOR_ID_PMC, VENDOR_ID_POSITIVO, + VENDOR_ID_PRESTIGIO, VENDOR_ID_QISDA, VENDOR_ID_QUALCOMM, VENDOR_ID_QUANTA, diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index f6d8f0c..8be3541 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -20,8 +20,7 @@ LOCAL_CPPFLAGS := -std=gnu++11 LOCAL_CFLAGS := \ -Wall \ -Wno-array-bounds \ - -Werror \ - -Wno-unused-parameter \ + -Werror ifeq ($(ARCH_ARM_HAVE_VFP),true) LOCAL_CFLAGS_arm += -DWITH_VFP @@ -55,7 +54,7 @@ LOCAL_SRC_FILES_x86 := x86/crashglue.S LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := optional -LOCAL_CFLAGS += -fstack-protector-all -Wno-unused-parameter -Wno-free-nonheap-object +LOCAL_CFLAGS += -fstack-protector-all -Werror -Wno-free-nonheap-object #LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_SHARED_LIBRARIES := libcutils liblog libc @@ -76,6 +75,7 @@ ifeq ($(ARCH_ARM_HAVE_VFP_D32),true) LOCAL_CFLAGS_arm += -DWITH_VFP_D32 endif # ARCH_ARM_HAVE_VFP_D32 endif # ARCH_ARM_HAVE_VFP == true +LOCAL_CFLAGS += -Werror LOCAL_SRC_FILES_arm64 := arm64/vfp.S LOCAL_MODULE_TARGET_ARCH += arm64 diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/arm64/machine.cpp index 7159228..2413d5e 100644 --- a/debuggerd/arm64/machine.cpp +++ b/debuggerd/arm64/machine.cpp @@ -82,8 +82,6 @@ void dump_registers(log_t* log, pid_t tid, int scope_flags) io.iov_base = &r; io.iov_len = sizeof(r); - bool only_in_tombstone = !IS_AT_FAULT(scope_flags); - if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) { _LOG(log, scope_flags, "ptrace error: %s\n", strerror(errno)); return; diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c index 5a2bc3c..01ce0be 100644 --- a/debuggerd/crasher.c +++ b/debuggerd/crasher.c @@ -1,21 +1,23 @@ - -//#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/cdefs.h> #include <sys/ptrace.h> -#include <sys/wait.h> #include <sys/socket.h> - -#include <pthread.h> +#include <sys/wait.h> +#include <unistd.h> #include <cutils/sockets.h> +#include <log/log.h> + +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif extern const char* __progname; @@ -23,13 +25,13 @@ 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(); } } -static int smash_stack(int i) { +static int smash_stack(int i __unused) { printf("crasher: deliberately corrupting stack...\n"); // Unless there's a "big enough" buffer on the stack, gcc // doesn't bother inserting checks. @@ -50,11 +52,6 @@ __attribute__((noinline)) static void overflow_stack(void* p) { overflow_stack(&buf); } -static void test_call1() -{ - *((int*) 32) = 1; -} - static void *noisy(void *x) { char c = (uintptr_t) x; @@ -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 6a1b963..f95e572 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; } @@ -584,8 +594,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"); @@ -607,7 +618,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)); @@ -721,9 +732,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)); } @@ -748,8 +759,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/debuggerd/utility.cpp b/debuggerd/utility.cpp index 9b20914..d4c252f 100644 --- a/debuggerd/utility.cpp +++ b/debuggerd/utility.cpp @@ -24,6 +24,7 @@ #include <sys/wait.h> #include <backtrace/Backtrace.h> +#include <log/log.h> #include <log/logd.h> const int sleep_time_usec = 50000; // 0.05 seconds @@ -64,7 +65,7 @@ void _LOG(log_t* log, int scopeFlags, const char* fmt, ...) { } if (want_log_write) { - __android_log_write(ANDROID_LOG_INFO, "DEBUG", buf); + __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_INFO, "DEBUG", buf); if (want_amfd_write) { int written = write_to_am(log->amfd, buf, len); if (written <= 0) { diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index 73a6e56..7d26c6f 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -889,6 +889,7 @@ int main(int argc, char **argv) {"kernel_offset", required_argument, 0, 'k'}, {"page_size", required_argument, 0, 'n'}, {"ramdisk_offset", required_argument, 0, 'r'}, + {"tags_offset", required_argument, 0, 't'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; @@ -897,7 +898,7 @@ int main(int argc, char **argv) while (1) { int option_index = 0; - c = getopt_long(argc, argv, "wub:k:n:r:s:S:lp:c:i:m:h", longopts, NULL); + c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, NULL); if (c < 0) { break; } @@ -938,6 +939,9 @@ int main(int argc, char **argv) case 'r': ramdisk_offset = strtoul(optarg, 0, 16); break; + case 't': + tags_offset = strtoul(optarg, 0, 16); + break; case 's': serial = optarg; break; diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c index 6145771..45bbfdc 100644 --- a/fs_mgr/fs_mgr_fstab.c +++ b/fs_mgr/fs_mgr_fstab.c @@ -337,7 +337,7 @@ void fs_mgr_free_fstab(struct fstab *fstab) /* Add an entry to the fstab, and return 0 on success or -1 on error */ int fs_mgr_add_entry(struct fstab *fstab, const char *mount_point, const char *fs_type, - const char *blk_device, long long length) + const char *blk_device) { struct fstab_rec *new_fstab_recs; int n = fstab->num_entries; diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.c index 4bde4a1..e5a00d5 100644 --- a/fs_mgr/fs_mgr_main.c +++ b/fs_mgr/fs_mgr_main.c @@ -80,10 +80,10 @@ int main(int argc, char *argv[]) int a_flag=0; int u_flag=0; int n_flag=0; - char *n_name; - char *n_blk_dev; - char *fstab_file; - struct fstab *fstab; + char *n_name=NULL; + char *n_blk_dev=NULL; + char *fstab_file=NULL; + struct fstab *fstab=NULL; klog_init(); klog_set_level(6); diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index 0f90c32..835cf64 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -57,7 +57,7 @@ int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size); int fs_mgr_add_entry(struct fstab *fstab, const char *mount_point, const char *fs_type, - const char *blk_device, long long length); + const char *blk_device); struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path); int fs_mgr_is_voldmanaged(struct fstab_rec *fstab); int fs_mgr_is_nonremovable(struct fstab_rec *fstab); diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp index b2bb516..ed4ddb4 100644 --- a/healthd/healthd_board_default.cpp +++ b/healthd/healthd_board_default.cpp @@ -16,13 +16,13 @@ #include <healthd.h> -void healthd_board_init(struct healthd_config *config) +void healthd_board_init(struct healthd_config*) { // use defaults } -int healthd_board_battery_update(struct android::BatteryProperties *props) +int healthd_board_battery_update(struct android::BatteryProperties*) { // return 0 to log periodic polled battery status to kernel log return 0; 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/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h index 06da2f4..13083bd 100644 --- a/include/backtrace/BacktraceMap.h +++ b/include/backtrace/BacktraceMap.h @@ -45,7 +45,7 @@ public: virtual ~BacktraceMap(); // Get the map data structure for the given address. - const backtrace_map_t* Find(uintptr_t addr); + virtual const backtrace_map_t* Find(uintptr_t addr); // The flags returned are the same flags as used by the mmap call. // The values are PROT_*. 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/log/log.h b/include/log/log.h index d469f40..5b76c1a 100644 --- a/include/log/log.h +++ b/include/log/log.h @@ -550,6 +550,7 @@ typedef enum log_id { LOG_ID_RADIO = 1, LOG_ID_EVENTS = 2, LOG_ID_SYSTEM = 3, + LOG_ID_CRASH = 4, LOG_ID_MAX } log_id_t; diff --git a/include/log/log_read.h b/include/log/log_read.h index bd9de12..54d71a4 100644 --- a/include/log/log_read.h +++ b/include/log/log_read.h @@ -33,6 +33,9 @@ public: uint32_t tv_sec; // good to Feb 5 2106 uint32_t tv_nsec; + static const uint32_t tv_sec_max = 0xFFFFFFFFUL; + static const uint32_t tv_nsec_max = 999999999UL; + log_time(const timespec &T) { tv_sec = T.tv_sec; diff --git a/include/log/logger.h b/include/log/logger.h index 3c6ea30..ed39c4f 100644 --- a/include/log/logger.h +++ b/include/log/logger.h @@ -142,9 +142,7 @@ log_id_t android_logger_get_id(struct logger *logger); int android_logger_clear(struct logger *logger); long android_logger_get_log_size(struct logger *logger); -#ifdef USERDEBUG_BUILD int android_logger_set_log_size(struct logger *logger, unsigned long size); -#endif long android_logger_get_log_readable_size(struct logger *logger); int android_logger_get_log_version(struct logger *logger); @@ -152,12 +150,10 @@ struct logger_list; ssize_t android_logger_get_statistics(struct logger_list *logger_list, char *buf, size_t len); -#ifdef USERDEBUG_BUILD ssize_t android_logger_get_prune_list(struct logger_list *logger_list, char *buf, size_t len); int android_logger_set_prune_list(struct logger_list *logger_list, char *buf, size_t len); -#endif struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index 9c26baf..d662107 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -251,6 +251,7 @@ static const struct fs_path_config android_files[] = { { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" }, { 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" }, diff --git a/include/system/audio.h b/include/system/audio.h index aa7ac02..f36befb 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -471,12 +471,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_device(audio_devices_t device) diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h index f1a4b43..18049cd 100644 --- a/include/sysutils/FrameworkListener.h +++ b/include/sysutils/FrameworkListener.h @@ -36,6 +36,7 @@ private: public: FrameworkListener(const char *socketName); FrameworkListener(const char *socketName, bool withSeq); + FrameworkListener(int sock); virtual ~FrameworkListener() {} protected: diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h index c8c87c3..5b98de2 100644 --- a/include/utils/Unicode.h +++ b/include/utils/Unicode.h @@ -22,8 +22,11 @@ extern "C" { +// Definitions exist in C++11 +#if defined __cplusplus && __cplusplus < 201103L typedef uint32_t char32_t; typedef uint16_t char16_t; +#endif // Standard string functions on char16_t strings. int strcmp16(const char16_t *, const char16_t *); diff --git a/init/Android.mk b/init/Android.mk index 740d10f..15a23be 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -17,6 +17,8 @@ LOCAL_SRC_FILES:= \ ueventd_parser.c \ watchdogd.c +LOCAL_CFLAGS += -Wno-unused-parameter + ifeq ($(strip $(INIT_BOOTCHART)),true) LOCAL_SRC_FILES += bootchart.c LOCAL_CFLAGS += -DBOOTCHART=1 diff --git a/init/builtins.c b/init/builtins.c index e2932d5..d973a6b 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -196,6 +196,8 @@ static void service_start_if_not_disabled(struct service *svc) { if (!(svc->flags & SVC_DISABLED)) { service_start(svc, NULL); + } else { + svc->flags |= SVC_DISABLED_START; } } @@ -238,6 +240,21 @@ int do_domainname(int nargs, char **args) return write_file("/proc/sys/kernel/domainname", args[1]); } +int do_enable(int nargs, char **args) +{ + struct service *svc; + svc = service_find_by_name(args[1]); + if (svc) { + svc->flags &= ~(SVC_DISABLED | SVC_RC_DISABLED); + if (svc->flags & SVC_DISABLED_START) { + service_start(svc, NULL); + } + } else { + return -1; + } + return 0; +} + int do_exec(int nargs, char **args) { return -1; 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/init.c b/init/init.c index 0884236..fc20198 100644 --- a/init/init.c +++ b/init/init.c @@ -164,7 +164,7 @@ void service_start(struct service *svc, const char *dynamic_args) * state and immediately takes it out of the restarting * state if it was in there */ - svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART)); + svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); svc->time_started = 0; /* running processes require no additional work -- if @@ -364,7 +364,7 @@ static void service_stop_or_reset(struct service *svc, int how) { /* The service is still SVC_RUNNING until its process exits, but if it has * already exited it shoudn't attempt a restart yet. */ - svc->flags &= (~SVC_RESTARTING); + svc->flags &= ~(SVC_RESTARTING | SVC_DISABLED_START); if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) { /* Hrm, an illegal flag. Default to SVC_DISABLED */ diff --git a/init/init.h b/init/init.h index 736b75b..c241912 100644 --- a/init/init.h +++ b/init/init.h @@ -74,6 +74,7 @@ struct svcenvinfo { so it can be restarted with its class */ #define SVC_RC_DISABLED 0x80 /* Remember if the disabled flag was set in the rc script */ #define SVC_RESTART 0x100 /* Use to safely restart (stop, wait, start) a service */ +#define SVC_DISABLED_START 0x200 /* a start was requested but it was disabled at the time */ #define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */ diff --git a/init/init_parser.c b/init/init_parser.c index f49e698..02e5bdc 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -98,6 +98,7 @@ static int lookup_keyword(const char *s) if (!strcmp(s, "omainname")) return K_domainname; break; case 'e': + if (!strcmp(s, "nable")) return K_enable; if (!strcmp(s, "xec")) return K_exec; if (!strcmp(s, "xport")) return K_export; break; diff --git a/init/keywords.h b/init/keywords.h index 97fe50c..6625330 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -6,6 +6,7 @@ int do_class_start(int nargs, char **args); int do_class_stop(int nargs, char **args); int do_class_reset(int nargs, char **args); int do_domainname(int nargs, char **args); +int do_enable(int nargs, char **args); int do_exec(int nargs, char **args); int do_export(int nargs, char **args); int do_hostname(int nargs, char **args); @@ -55,6 +56,7 @@ enum { KEYWORD(critical, OPTION, 0, 0) KEYWORD(disabled, OPTION, 0, 0) KEYWORD(domainname, COMMAND, 1, do_domainname) + KEYWORD(enable, COMMAND, 1, do_enable) KEYWORD(exec, COMMAND, 1, do_exec) KEYWORD(export, COMMAND, 2, do_export) KEYWORD(group, OPTION, 0, 0) diff --git a/init/property_service.c b/init/property_service.c index fe7cbb5..7e8d79a 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -269,6 +269,7 @@ static void write_persistent_property(const char *name, const char *value) return; } write(fd, value, strlen(value)); + fsync(fd); close(fd); snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); @@ -556,7 +557,8 @@ static void load_persistent_properties() || (sb.st_gid != 0) || (sb.st_nlink != 1)) { ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%d mode=%o)\n", - entry->d_name, sb.st_uid, sb.st_gid, sb.st_nlink, sb.st_mode); + entry->d_name, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid, + sb.st_nlink, sb.st_mode); close(fd); continue; } diff --git a/init/readme.txt b/init/readme.txt index 42a09cb..613a9e9 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -178,6 +178,16 @@ class_stop <serviceclass> domainname <name> Set the domain name. +enable <servicename> + Turns a disabled service into an enabled one as if the service did not + specify disabled. + If the service is supposed to be running, it will be started now. + Typically used when the bootloader sets a variable that indicates a specific + service should be started when needed. E.g. + on property:ro.boot.myfancyhardware=1 + enable my_fancy_service_for_my_fancy_hardware + + insmod <path> Install the module at <path> diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk index 2e56756..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,20 +96,13 @@ backtrace_test_cflags := \ -fno-builtin \ -O0 \ -g \ - -DGTEST_HAS_STD_STRING \ - -ifneq ($(TARGET_ARCH),arm64) -backtrace_test_cflags += -fstack-protector-all -else - $(info TODO: $(LOCAL_PATH)/Android.mk -fstack-protector not yet available for the AArch64 toolchain) - common_cflags += -fno-stack-protector -endif # arm64 backtrace_test_cflags_target := \ - -DGTEST_OS_LINUX_ANDROID \ + -DENABLE_PSS_TESTS \ backtrace_test_src_files := \ backtrace_test.cpp \ + GetPss.cpp \ thread_utils.c \ backtrace_test_ldlibs := \ diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp index 855810e..05007d9 100644 --- a/libbacktrace/BacktraceImpl.cpp +++ b/libbacktrace/BacktraceImpl.cpp @@ -27,6 +27,7 @@ #include <backtrace/BacktraceMap.h> #include "BacktraceImpl.h" +#include "BacktraceLog.h" #include "thread_utils.h" //------------------------------------------------------------------------- diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h index 48dd11c..7b31c38 100755 --- a/libbacktrace/BacktraceImpl.h +++ b/libbacktrace/BacktraceImpl.h @@ -21,11 +21,6 @@ #include <backtrace/BacktraceMap.h> #include <sys/types.h> -#include <log/log.h> - -// Macro to log the function name along with the warning message. -#define BACK_LOGW(format, ...) \ - ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) class BacktraceImpl { public: diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h new file mode 100755 index 0000000..1632ec2 --- /dev/null +++ b/libbacktrace/BacktraceLog.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#ifndef _LIBBACKTRACE_BACKTRACE_LOG_H +#define _LIBBACKTRACE_BACKTRACE_LOG_H + +#define LOG_TAG "libbacktrace" + +#include <log/log.h> + +// Macro to log the function name along with the warning message. +#define BACK_LOGW(format, ...) \ + ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) + +#endif // _LIBBACKTRACE_BACKTRACE_LOG_H 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 5ffe516..e0bab24 100644 --- a/libbacktrace/BacktraceThread.cpp +++ b/libbacktrace/BacktraceThread.cpp @@ -23,6 +23,7 @@ #include <cutils/atomic.h> +#include "BacktraceLog.h" #include "BacktraceThread.h" #include "thread_utils.h" @@ -135,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; } @@ -195,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 efeee2e..0000000 --- a/libbacktrace/Corkscrew.cpp +++ /dev/null @@ -1,251 +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. - */ - -#define LOG_TAG "libbacktrace" - -#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 "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/GetPss.cpp b/libbacktrace/GetPss.cpp new file mode 100644 index 0000000..442383b --- /dev/null +++ b/libbacktrace/GetPss.cpp @@ -0,0 +1,85 @@ +/* + * 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 <assert.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> + +// This is an extremely simplified version of libpagemap. + +#define _BITS(x, offset, bits) (((x) >> offset) & ((1LL << (bits)) - 1)) + +#define PAGEMAP_PRESENT(x) (_BITS(x, 63, 1)) +#define PAGEMAP_SWAPPED(x) (_BITS(x, 62, 1)) +#define PAGEMAP_SHIFT(x) (_BITS(x, 55, 6)) +#define PAGEMAP_PFN(x) (_BITS(x, 0, 55)) +#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50)) +#define PAGEMAP_SWAP_TYPE(x) (_BITS(x, 0, 5)) + +static bool ReadData(int fd, unsigned long place, uint64_t *data) { + if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) { + return false; + } + if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) { + return false; + } + return true; +} + +size_t GetPssBytes() { + FILE* maps = fopen("/proc/self/maps", "r"); + assert(maps != NULL); + + int pagecount_fd = open("/proc/kpagecount", O_RDONLY); + assert(pagecount_fd >= 0); + + int pagemap_fd = open("/proc/self/pagemap", O_RDONLY); + assert(pagemap_fd >= 0); + + char line[4096]; + size_t total_pss = 0; + int pagesize = getpagesize(); + while (fgets(line, sizeof(line), maps)) { + uintptr_t start, end; + if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) { + total_pss = 0; + break; + } + for (size_t page = start/pagesize; page < end/pagesize; page++) { + uint64_t data; + if (ReadData(pagemap_fd, page, &data)) { + if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) { + uint64_t count; + if (ReadData(pagecount_fd, PAGEMAP_PFN(data), &count)) { + total_pss += (count >= 1) ? pagesize / count : 0; + } + } + } + } + } + + fclose(maps); + + close(pagecount_fd); + close(pagemap_fd); + + return total_pss; +} diff --git a/libbacktrace/GetPss.h b/libbacktrace/GetPss.h new file mode 100644 index 0000000..787c33d --- /dev/null +++ b/libbacktrace/GetPss.h @@ -0,0 +1,22 @@ +/* + * 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. + */ + +#ifndef _LIBBACKTRACE_GET_PSS_H +#define _LIBBACKTRACE_GET_PSS_H + +size_t GetPssBytes(); + +#endif // _LIBBACKTRACE_GET_PSS_H diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp index 81e69bb..67d372a 100755 --- a/libbacktrace/UnwindCurrent.cpp +++ b/libbacktrace/UnwindCurrent.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <sys/ucontext.h> #include <sys/types.h> @@ -25,6 +23,7 @@ #define UNW_LOCAL_ONLY #include <libunwind.h> +#include "BacktraceLog.h" #include "UnwindCurrent.h" #include "UnwindMap.h" @@ -43,7 +42,7 @@ bool UnwindCurrent::Unwind(size_t num_ignore_frames) { BACK_LOGW("unw_getcontext failed %d", ret); return false; } - return UnwindFromContext(num_ignore_frames, true); + return UnwindFromContext(num_ignore_frames, false); } std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { @@ -58,12 +57,14 @@ std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { return ""; } -bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { +bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool within_handler) { // The cursor structure is pretty large, do not put it on the stack. unw_cursor_t* cursor = new unw_cursor_t; int ret = unw_init_local(cursor, &context_); if (ret < 0) { - BACK_LOGW("unw_init_local failed %d", ret); + if (!within_handler) { + BACK_LOGW("unw_init_local failed %d", ret); + } delete cursor; return false; } @@ -75,13 +76,17 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { unw_word_t pc; ret = unw_get_reg(cursor, UNW_REG_IP, &pc); if (ret < 0) { - BACK_LOGW("Failed to read IP %d", ret); + if (!within_handler) { + BACK_LOGW("Failed to read IP %d", ret); + } break; } unw_word_t sp; ret = unw_get_reg(cursor, UNW_REG_SP, &sp); if (ret < 0) { - BACK_LOGW("Failed to read SP %d", ret); + if (!within_handler) { + BACK_LOGW("Failed to read SP %d", ret); + } break; } @@ -99,7 +104,7 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { prev->stack_size = frame->sp - prev->sp; } - if (resolve) { + if (!within_handler) { frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); frame->map = FindMap(frame->pc); } else { @@ -155,7 +160,7 @@ UnwindThread::~UnwindThread() { void UnwindThread::ThreadUnwind( siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) { ExtractContext(sigcontext); - UnwindFromContext(num_ignore_frames, false); + UnwindFromContext(num_ignore_frames, true); } //------------------------------------------------------------------------- diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h index acce110..41080c7 100644 --- a/libbacktrace/UnwindCurrent.h +++ b/libbacktrace/UnwindCurrent.h @@ -34,7 +34,7 @@ public: virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); - bool UnwindFromContext(size_t num_ignore_frames, bool resolve); + bool UnwindFromContext(size_t num_ignore_frames, bool within_handler); void ExtractContext(void* sigcontext); diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp index 8268db6..1615518 100644 --- a/libbacktrace/UnwindMap.cpp +++ b/libbacktrace/UnwindMap.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <pthread.h> #include <sys/types.h> #include <unistd.h> @@ -24,6 +22,7 @@ #include <libunwind.h> +#include "BacktraceLog.h" #include "UnwindMap.h" //------------------------------------------------------------------------- @@ -32,57 +31,21 @@ // only update the local address space once, and keep a reference count // of maps using the same map cursor. //------------------------------------------------------------------------- -static pthread_mutex_t g_map_mutex = PTHREAD_MUTEX_INITIALIZER; -static unw_map_cursor_t g_map_cursor; -static int g_map_references = 0; - UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) { - map_cursor_.map_list = NULL; } UnwindMap::~UnwindMap() { - if (pid_ == getpid()) { - pthread_mutex_lock(&g_map_mutex); - if (--g_map_references == 0) { - // Clear the local address space map. - unw_map_local_set(NULL); - unw_map_cursor_destroy(&map_cursor_); - } - pthread_mutex_unlock(&g_map_mutex); - } else { - unw_map_cursor_destroy(&map_cursor_); - } + unw_map_cursor_destroy(&map_cursor_); + unw_map_cursor_clear(&map_cursor_); } -bool UnwindMap::Build() { - bool return_value = true; - if (pid_ == getpid()) { - pthread_mutex_lock(&g_map_mutex); - if (g_map_references == 0) { - return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); - if (return_value) { - // Set the local address space map to our new map. - unw_map_local_set(&map_cursor_); - g_map_references = 1; - g_map_cursor = map_cursor_; - } - } else { - g_map_references++; - map_cursor_ = g_map_cursor; - } - pthread_mutex_unlock(&g_map_mutex); - } else { - return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); - } - - if (!return_value) - return false; - +bool UnwindMap::GenerateMap() { // Use the map_cursor information to construct the BacktraceMap data // rather than reparsing /proc/self/maps. unw_map_cursor_reset(&map_cursor_); + unw_map_t unw_map; - while (unw_map_cursor_get(&map_cursor_, &unw_map)) { + while (unw_map_cursor_get_next(&map_cursor_, &unw_map)) { backtrace_map_t map; map.start = unw_map.start; @@ -97,11 +60,82 @@ bool UnwindMap::Build() { return true; } +bool UnwindMap::Build() { + return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap(); +} + +UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) { +} + +UnwindMapLocal::~UnwindMapLocal() { + if (map_created_) { + unw_map_local_destroy(); + unw_map_cursor_clear(&map_cursor_); + } +} + +bool UnwindMapLocal::GenerateMap() { + // It's possible for the map to be regenerated while this loop is occurring. + // If that happens, get the map again, but only try at most three times + // before giving up. + for (int i = 0; i < 3; i++) { + maps_.clear(); + + unw_map_local_cursor_get(&map_cursor_); + + unw_map_t unw_map; + int ret; + while ((ret = unw_map_local_cursor_get_next(&map_cursor_, &unw_map)) > 0) { + backtrace_map_t map; + + map.start = unw_map.start; + map.end = unw_map.end; + map.flags = unw_map.flags; + map.name = unw_map.path; + + free(unw_map.path); + + // The maps are in descending order, but we want them in ascending order. + maps_.push_front(map); + } + // Check to see if the map changed while getting the data. + if (ret != -UNW_EINVAL) { + return true; + } + } + + BACK_LOGW("Unable to generate the map."); + return false; +} + +bool UnwindMapLocal::Build() { + return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();; +} + +const backtrace_map_t* UnwindMapLocal::Find(uintptr_t addr) { + const backtrace_map_t* map = BacktraceMap::Find(addr); + if (!map) { + // Check to see if the underlying map changed and regenerate the map + // if it did. + if (unw_map_local_cursor_valid(&map_cursor_) < 0) { + if (GenerateMap()) { + map = BacktraceMap::Find(addr); + } + } + } + return map; +} + //------------------------------------------------------------------------- // BacktraceMap create function. //------------------------------------------------------------------------- BacktraceMap* BacktraceMap::Create(pid_t pid) { - BacktraceMap* map = new UnwindMap(pid); + BacktraceMap* map; + if (pid == getpid()) { + map = new UnwindMapLocal(); + } else { + map = new UnwindMap(pid); + } if (!map->Build()) { delete map; return NULL; diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h index 5a874e8..2fdb29f 100644 --- a/libbacktrace/UnwindMap.h +++ b/libbacktrace/UnwindMap.h @@ -32,8 +32,25 @@ public: unw_map_cursor_t* GetMapCursor() { return &map_cursor_; } -private: +protected: + virtual bool GenerateMap(); + unw_map_cursor_t map_cursor_; }; +class UnwindMapLocal : public UnwindMap { +public: + UnwindMapLocal(); + virtual ~UnwindMapLocal(); + + virtual bool Build(); + + virtual const backtrace_map_t* Find(uintptr_t addr); + +protected: + virtual bool GenerateMap(); + + bool map_created_; +}; + #endif // _LIBBACKTRACE_UNWIND_MAP_H diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp index 732dae8..5ca7e60 100644 --- a/libbacktrace/UnwindPtrace.cpp +++ b/libbacktrace/UnwindPtrace.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> @@ -25,6 +23,7 @@ #include <libunwind.h> #include <libunwind-ptrace.h> +#include "BacktraceLog.h" #include "UnwindMap.h" #include "UnwindPtrace.h" diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index 23eaf92..9744922 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp @@ -16,9 +16,10 @@ #include <dirent.h> #include <errno.h> +#include <inttypes.h> #include <pthread.h> #include <signal.h> -#include <stdbool.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -32,9 +33,13 @@ #include <backtrace/BacktraceMap.h> #include <UniquePtr.h> +// For the THREAD_SIGNAL definition. +#include "BacktraceThread.h" + #include <cutils/atomic.h> #include <gtest/gtest.h> +#include <algorithm> #include <vector> #include "thread_utils.h" @@ -287,7 +292,7 @@ TEST(libbacktrace, ptrace_trace) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump); @@ -300,7 +305,7 @@ TEST(libbacktrace, ptrace_trace_shared_map) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump); @@ -314,7 +319,7 @@ TEST(libbacktrace, ptrace_max_trace) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump); @@ -339,7 +344,7 @@ TEST(libbacktrace, ptrace_ignore_frames) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames); @@ -384,7 +389,7 @@ TEST(libbacktrace, ptrace_threads) { ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0); } ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } // Check to see that all of the threads are running before unwinding. @@ -458,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); @@ -473,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); } @@ -693,3 +704,136 @@ TEST(libbacktrace, format_test) { #endif backtrace->FormatFrameData(&frame)); } + +struct map_test_t { + uintptr_t start; + uintptr_t end; +}; + +bool map_sort(map_test_t i, map_test_t j) { + return i.start < j.start; +} + +static void VerifyMap(pid_t pid) { + char buffer[4096]; + snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid); + + FILE* map_file = fopen(buffer, "r"); + ASSERT_TRUE(map_file != NULL); + std::vector<map_test_t> test_maps; + while (fgets(buffer, sizeof(buffer), map_file)) { + map_test_t map; + ASSERT_EQ(2, sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR " ", &map.start, &map.end)); + test_maps.push_back(map); + } + fclose(map_file); + std::sort(test_maps.begin(), test_maps.end(), map_sort); + + UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid)); + + // Basic test that verifies that the map is in the expected order. + std::vector<map_test_t>::const_iterator test_it = test_maps.begin(); + for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { + ASSERT_TRUE(test_it != test_maps.end()); + ASSERT_EQ(test_it->start, it->start); + ASSERT_EQ(test_it->end, it->end); + ++test_it; + } + ASSERT_TRUE(test_it == test_maps.end()); +} + +TEST(libbacktrace, verify_map_remote) { + pid_t pid; + + if ((pid = fork()) == 0) { + while (true) { + } + _exit(0); + } + ASSERT_LT(0, pid); + + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + + // Wait for the process to get to a stopping point. + WaitForStop(pid); + + // The maps should match exactly since the forked process has been paused. + VerifyMap(pid); + + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + kill(pid, SIGKILL); + ASSERT_EQ(waitpid(pid, NULL, 0), pid); +} + +#if defined(ENABLE_PSS_TESTS) +#include "GetPss.h" + +#define MAX_LEAK_BYTES 32*1024UL + +static void CheckForLeak(pid_t pid, pid_t tid) { + // Do a few runs to get the PSS stable. + for (size_t i = 0; i < 100; i++) { + Backtrace* backtrace = Backtrace::Create(pid, tid); + ASSERT_TRUE(backtrace != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + delete backtrace; + } + size_t stable_pss = GetPssBytes(); + + // Loop enough that even a small leak should be detectable. + for (size_t i = 0; i < 4096; i++) { + Backtrace* backtrace = Backtrace::Create(pid, tid); + ASSERT_TRUE(backtrace != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + delete backtrace; + } + size_t new_pss = GetPssBytes(); + size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss; + // As long as the new pss is within a certain amount, consider everything okay. + ASSERT_LE(abs_diff, MAX_LEAK_BYTES); +} + +TEST(libbacktrace, check_for_leak_local) { + CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD); +} + +TEST(libbacktrace, check_for_leak_local_thread) { + thread_t thread_data = { 0, 0, 0 }; + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, NULL, ThreadLevelRun, &thread_data) == 0); + + // Wait up to 2 seconds for the tid to be set. + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + + CheckForLeak(BACKTRACE_CURRENT_PROCESS, thread_data.tid); + + // Tell the thread to exit its infinite loop. + android_atomic_acquire_store(0, &thread_data.state); + + ASSERT_TRUE(pthread_join(thread, NULL) == 0); +} + +TEST(libbacktrace, check_for_leak_remote) { + pid_t pid; + + if ((pid = fork()) == 0) { + while (true) { + } + _exit(0); + } + ASSERT_LT(0, pid); + + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + + // Wait for the process to get to a stopping point. + WaitForStop(pid); + + CheckForLeak(pid, BACKTRACE_CURRENT_THREAD); + + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + kill(pid, SIGKILL); + ASSERT_EQ(waitpid(pid, NULL, 0), pid); +} +#endif 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/Android.mk b/libcutils/Android.mk index 93bccb0..2dcc965 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -78,6 +78,9 @@ LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c LOCAL_LDLIBS := -lpthread LOCAL_STATIC_LIBRARIES := liblog LOCAL_CFLAGS += $(hostSmpFlag) +ifneq ($(HOST_OS),windows) +LOCAL_CFLAGS += -Werror +endif include $(BUILD_HOST_STATIC_LIBRARY) @@ -89,8 +92,24 @@ LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c LOCAL_LDLIBS := -lpthread LOCAL_STATIC_LIBRARIES := lib64log LOCAL_CFLAGS += $(hostSmpFlag) -m64 +ifneq ($(HOST_OS),windows) +LOCAL_CFLAGS += -Werror +endif include $(BUILD_HOST_STATIC_LIBRARY) +# Tests for host +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE := tst_str_parms +LOCAL_CFLAGS += -DTEST_STR_PARMS +ifneq ($(HOST_OS),windows) +LOCAL_CFLAGS += -Werror +endif +LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c +LOCAL_STATIC_LIBRARIES := liblog +LOCAL_MODULE_TAGS := optional +include $(BUILD_HOST_EXECUTABLE) + # Shared and static library for target # ======================================================== @@ -125,7 +144,7 @@ endif # !arm LOCAL_C_INCLUDES := $(libcutils_c_includes) LOCAL_STATIC_LIBRARIES := liblog -LOCAL_CFLAGS += $(targetSmpFlag) +LOCAL_CFLAGS += $(targetSmpFlag) -Werror include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) @@ -134,13 +153,13 @@ LOCAL_MODULE := libcutils # liblog symbols present in libcutils. LOCAL_WHOLE_STATIC_LIBRARIES := libcutils liblog LOCAL_SHARED_LIBRARIES := liblog -LOCAL_CFLAGS += $(targetSmpFlag) +LOCAL_CFLAGS += $(targetSmpFlag) -Werror LOCAL_C_INCLUDES := $(libcutils_c_includes) include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := tst_str_parms -LOCAL_CFLAGS += -DTEST_STR_PARMS +LOCAL_CFLAGS += -DTEST_STR_PARMS -Werror LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE_TAGS := optional diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c index f03e130..7873964 100644 --- a/libcutils/ashmem-host.c +++ b/libcutils/ashmem-host.c @@ -19,20 +19,24 @@ * an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version. */ -#include <unistd.h> -#include <string.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/stat.h> +#include <errno.h> #include <fcntl.h> +#include <limits.h> #include <stdio.h> -#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> #include <time.h> -#include <limits.h> +#include <unistd.h> #include <cutils/ashmem.h> -int ashmem_create_region(const char *ignored, size_t size) +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif + +int ashmem_create_region(const char *ignored __unused, size_t size) { static const char txt[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -78,17 +82,17 @@ error: return -1; } -int ashmem_set_prot_region(int fd, int prot) +int ashmem_set_prot_region(int fd __unused, int prot __unused) { return 0; } -int ashmem_pin_region(int fd, size_t offset, size_t len) +int ashmem_pin_region(int fd __unused, size_t offset __unused, size_t len __unused) { return ASHMEM_NOT_PURGED; } -int ashmem_unpin_region(int fd, size_t offset, size_t len) +int ashmem_unpin_region(int fd __unused, size_t offset __unused, size_t len __unused) { return ASHMEM_IS_UNPINNED; } diff --git a/libcutils/cpu_info.c b/libcutils/cpu_info.c index 23dda8a..21fa1dc 100644 --- a/libcutils/cpu_info.c +++ b/libcutils/cpu_info.c @@ -1,5 +1,4 @@ -/* libs/cutils/cpu_info.c -** +/* ** Copyright 2007, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,11 +14,12 @@ ** limitations under the License. */ -#include <cutils/cpu_info.h> -#include <stdlib.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <cutils/cpu_info.h> + // we cache the serial number here. // this is also used as a fgets() line buffer when we are reading /proc/cpuinfo static char serial_number[100] = { 0 }; @@ -31,7 +31,6 @@ extern const char* get_cpu_serial_number(void) FILE* file; char* chp, *end; char* whitespace; - int length; // read serial number from /proc/cpuinfo file = fopen("proc/cpuinfo", "r"); 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/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c index c327a55..6dca911 100644 --- a/libcutils/dlmalloc_stubs.c +++ b/libcutils/dlmalloc_stubs.c @@ -15,20 +15,22 @@ */ #include "../../../bionic/libc/bionic/dlmalloc.h" -#include "cutils/log.h" +#include "log/log.h" + +#define UNUSED __attribute__((__unused__)) /* * Stubs for functions defined in bionic/libc/bionic/dlmalloc.c. These * are used in host builds, as the host libc will not contain these * functions. */ -void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), - void* arg) +void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*) UNUSED, + void* arg UNUSED) { ALOGW("Called host unimplemented stub: dlmalloc_inspect_all"); } -int dlmalloc_trim(size_t unused) +int dlmalloc_trim(size_t unused UNUSED) { ALOGW("Called host unimplemented stub: dlmalloc_trim"); return 0; diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c index 5d90a01..67e101d 100644 --- a/libcutils/iosched_policy.c +++ b/libcutils/iosched_policy.c @@ -1,5 +1,4 @@ /* -** ** Copyright 2007-2014, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,12 +14,12 @@ ** limitations under the License. */ +#include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <string.h> -#include <errno.h> -#include <fcntl.h> +#include <unistd.h> #ifdef HAVE_SCHED_H @@ -30,6 +29,9 @@ /* #include <linux/ioprio.h> */ extern int ioprio_set(int which, int who, int ioprio); extern int ioprio_get(int which, int who); +#define __android_unused +#else +#define __android_unused __attribute__((__unused__)) #endif enum { @@ -41,7 +43,7 @@ enum { #define CLASS_SHIFT 13 #define IOPRIO_NORM 4 -int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio) { +int android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_unused, int ioprio __android_unused) { #ifdef HAVE_ANDROID_OS if (ioprio_set(WHO_PROCESS, pid, ioprio | (clazz << CLASS_SHIFT))) { return -1; @@ -50,7 +52,7 @@ int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio) { return 0; } -int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio) { +int android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *ioprio) { #ifdef HAVE_ANDROID_OS int rc; diff --git a/libcutils/partition_utils.c b/libcutils/partition_utils.c index 10539fa..823b162 100644 --- a/libcutils/partition_utils.c +++ b/libcutils/partition_utils.c @@ -14,12 +14,13 @@ * limitations under the License. */ -#include <sys/types.h> -#include <unistd.h> #include <fcntl.h> -#include <sys/stat.h> #include <sys/ioctl.h> #include <sys/mount.h> /* for BLKGETSIZE */ +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + #include <cutils/properties.h> static int only_one_char(char *buf, int len, char c) @@ -39,7 +40,7 @@ static int only_one_char(char *buf, int len, char c) int partition_wiped(char *source) { char buf[4096]; - int fd, ret, wiped; + int fd, ret; if ((fd = open(source, O_RDONLY)) < 0) { return 0; diff --git a/libcutils/process_name.c b/libcutils/process_name.c index a6ab951..9c3dfb8 100644 --- a/libcutils/process_name.c +++ b/libcutils/process_name.c @@ -14,25 +14,27 @@ * limitations under the License. */ +#include <fcntl.h> #include <stdlib.h> #include <string.h> -#include <cutils/process_name.h> -#ifdef HAVE_ANDROID_OS -#include <cutils/properties.h> +#if defined(HAVE_PRCTL) +#include <sys/prctl.h> #endif -#include <unistd.h> -#include <sys/types.h> #include <sys/stat.h> -#include <fcntl.h> +#include <sys/types.h> +#include <unistd.h> -#if defined(HAVE_PRCTL) -#include <sys/prctl.h> +#include <cutils/process_name.h> +#ifdef HAVE_ANDROID_OS +#include <cutils/properties.h> #endif #define PROCESS_NAME_DEVICE "/sys/qemu_trace/process_name" static const char* process_name = "unknown"; +#ifdef HAVE_ANDROID_OS static int running_in_emulator = -1; +#endif void set_process_name(const char* new_name) { #ifdef HAVE_ANDROID_OS diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c index 899a7b4..00e211c 100644 --- a/libcutils/qtaguid.c +++ b/libcutils/qtaguid.c @@ -1,5 +1,4 @@ -/* libcutils/qtaguid.c -** +/* ** Copyright 2011, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -146,7 +145,7 @@ int qtaguid_setCounterSet(int counterSetNum, uid_t uid) { int qtaguid_deleteTagData(int tag, uid_t uid) { char lineBuf[CTRL_MAX_INPUT_LEN]; - int fd, cnt = 0, res = 0; + int cnt = 0, res = 0; uint64_t kTag = (uint64_t)tag << 32; ALOGV("Deleting tag data with tag %" PRIx64 "{%d,0} for uid %d", kTag, tag, uid); @@ -164,8 +163,6 @@ int qtaguid_deleteTagData(int tag, uid_t uid) { } int qtaguid_setPacifier(int on) { - int param_fd; - int res; const char *value; value = on ? "Y" : "N"; diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c index d20d217..9f092d6 100644 --- a/libcutils/sched_policy.c +++ b/libcutils/sched_policy.c @@ -1,6 +1,4 @@ - -/* libs/cutils/sched_policy.c -** +/* ** Copyright 2007, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,14 +16,17 @@ #define LOG_TAG "SchedPolicy" +#include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <string.h> -#include <errno.h> -#include <fcntl.h> +#include <unistd.h> + #include <cutils/sched_policy.h> -#include <cutils/log.h> +#include <log/log.h> + +#define UNUSED __attribute__((__unused__)) /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged. * Call this any place a SchedPolicy is used as an input parameter. @@ -331,12 +332,12 @@ int set_sched_policy(int tid, SchedPolicy policy) /* Stubs for non-Android targets. */ -int set_sched_policy(int tid, SchedPolicy policy) +int set_sched_policy(int tid UNUSED, SchedPolicy policy UNUSED) { return 0; } -int get_sched_policy(int tid, SchedPolicy *policy) +int get_sched_policy(int tid UNUSED, SchedPolicy *policy) { *policy = SP_SYSTEM_DEFAULT; return 0; diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server.c index 7d5dab4..6c849de 100644 --- a/libcutils/socket_inaddr_any_server.c +++ b/libcutils/socket_inaddr_any_server.c @@ -1,5 +1,4 @@ -/* libs/cutils/socket_inaddr_any_server.c -** +/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,13 +14,11 @@ ** limitations under the License. */ -#include <cutils/sockets.h> - +#include <errno.h> +#include <stddef.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <errno.h> -#include <stddef.h> #ifndef HAVE_WINSOCK #include <sys/socket.h> @@ -30,13 +27,14 @@ #include <netinet/in.h> #endif +#include <cutils/sockets.h> + #define LISTEN_BACKLOG 4 /* open listen() port on any interface */ int socket_inaddr_any_server(int port, int type) { struct sockaddr_in addr; - size_t alen; int s, n; memset(&addr, 0, sizeof(addr)); @@ -48,7 +46,7 @@ int socket_inaddr_any_server(int port, int type) if(s < 0) return -1; n = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n)); if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(s); diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c index 5310516..ddcc2da 100644 --- a/libcutils/socket_local_client.c +++ b/libcutils/socket_local_client.c @@ -14,13 +14,13 @@ * limitations under the License. */ -#include <cutils/sockets.h> - +#include <errno.h> +#include <stddef.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <errno.h> -#include <stddef.h> + +#include <cutils/sockets.h> #ifdef HAVE_WINSOCK @@ -128,7 +128,6 @@ int socket_local_client_connect(int fd, const char *name, int namespaceId, { struct sockaddr_un addr; socklen_t alen; - size_t namelen; int err; err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen); diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client.c index cb82c5e..9aed7b7 100644 --- a/libcutils/socket_loopback_client.c +++ b/libcutils/socket_loopback_client.c @@ -1,5 +1,4 @@ -/* libs/cutils/socket_loopback_client.c -** +/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,13 +14,11 @@ ** limitations under the License. */ -#include <cutils/sockets.h> - +#include <errno.h> +#include <stddef.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <errno.h> -#include <stddef.h> #ifndef HAVE_WINSOCK #include <sys/socket.h> @@ -30,6 +27,8 @@ #include <netinet/in.h> #endif +#include <cutils/sockets.h> + /* Connect to port on the loopback IP interface. type is * SOCK_STREAM or SOCK_DGRAM. * return is a file descriptor or -1 on error @@ -37,7 +36,6 @@ int socket_loopback_client(int port, int type) { struct sockaddr_in addr; - socklen_t alen; int s; memset(&addr, 0, sizeof(addr)); diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server.c index 3208488..71afce7 100644 --- a/libcutils/socket_loopback_server.c +++ b/libcutils/socket_loopback_server.c @@ -1,5 +1,4 @@ -/* libs/cutils/socket_loopback_server.c -** +/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,13 +14,11 @@ ** limitations under the License. */ -#include <cutils/sockets.h> - +#include <errno.h> +#include <stddef.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <errno.h> -#include <stddef.h> #define LISTEN_BACKLOG 4 @@ -32,11 +29,12 @@ #include <netinet/in.h> #endif +#include <cutils/sockets.h> + /* open listen() port on loopback interface */ int socket_loopback_server(int port, int type) { struct sockaddr_in addr; - size_t alen; int s, n; memset(&addr, 0, sizeof(addr)); @@ -48,7 +46,7 @@ int socket_loopback_server(int port, int type) if(s < 0) return -1; n = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n)); if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(s); diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client.c index a64006c..c52013d 100644 --- a/libcutils/socket_network_client.c +++ b/libcutils/socket_network_client.c @@ -1,5 +1,4 @@ -/* libs/cutils/socket_network_client.c -** +/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,13 +14,11 @@ ** limitations under the License. */ -#include <cutils/sockets.h> - +#include <errno.h> +#include <stddef.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <errno.h> -#include <stddef.h> #ifndef HAVE_WINSOCK #include <sys/socket.h> @@ -31,6 +28,7 @@ #include <netdb.h> #endif +#include <cutils/sockets.h> /* Connect to port on the IP interface. type is * SOCK_STREAM or SOCK_DGRAM. @@ -40,7 +38,6 @@ int socket_network_client(const char *host, int port, int type) { struct hostent *hp; struct sockaddr_in addr; - socklen_t alen; int s; hp = gethostbyname(host); diff --git a/libcutils/sockets.c b/libcutils/sockets.c index b5a1b3d..15ede2b 100644 --- a/libcutils/sockets.c +++ b/libcutils/sockets.c @@ -14,15 +14,18 @@ * limitations under the License. */ -#include <cutils/log.h> #include <cutils/sockets.h> +#include <log/log.h> #ifdef HAVE_ANDROID_OS /* For the socket trust (credentials) check */ #include <private/android_filesystem_config.h> +#define __android_unused +#else +#define __android_unused __attribute__((__unused__)) #endif -bool socket_peer_is_trusted(int fd) +bool socket_peer_is_trusted(int fd __android_unused) { #ifdef HAVE_ANDROID_OS struct ucred cr; diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c index 7cfbcb3..2e3ce9f 100644 --- a/libcutils/str_parms.c +++ b/libcutils/str_parms.c @@ -25,10 +25,9 @@ #include <string.h> #include <cutils/hashmap.h> -#include <cutils/log.h> #include <cutils/memory.h> - #include <cutils/str_parms.h> +#include <log/log.h> #define UNUSED __attribute__((unused)) @@ -194,23 +193,46 @@ err_create_str_parms: int str_parms_add_str(struct str_parms *str_parms, const char *key, const char *value) { - void *old_val; - void *tmp_key; - void *tmp_val; + void *tmp_key = NULL; + void *tmp_val = NULL; + void *old_val = NULL; + + // strdup and hashmapPut both set errno on failure. + // Set errno to 0 so we can recognize whether anything went wrong. + int saved_errno = errno; + errno = 0; tmp_key = strdup(key); + if (tmp_key == NULL) { + goto clean_up; + } + tmp_val = strdup(value); - old_val = hashmapPut(str_parms->map, tmp_key, tmp_val); + if (tmp_val == NULL) { + goto clean_up; + } - if (old_val) { - free(old_val); - free(tmp_key); - } else if (errno == ENOMEM) { - free(tmp_key); - free(tmp_val); - return -ENOMEM; + old_val = hashmapPut(str_parms->map, tmp_key, tmp_val); + if (old_val == NULL) { + // Did hashmapPut fail? + if (errno == ENOMEM) { + goto clean_up; + } + // For new keys, hashmap takes ownership of tmp_key and tmp_val. + tmp_key = tmp_val = NULL; + } else { + // For existing keys, hashmap takes ownership of tmp_val. + // (It also gives up ownership of old_val.) + tmp_val = NULL; } - return 0; + +clean_up: + free(tmp_key); + free(tmp_val); + free(old_val); + int result = -errno; + errno = saved_errno; + return result; } int str_parms_add_int(struct str_parms *str_parms, const char *key, int value) @@ -337,7 +359,6 @@ static void test_str_parms_str(const char *str) { struct str_parms *str_parms; char *out_str; - int ret; str_parms = str_parms_create_str(str); str_parms_add_str(str_parms, "dude", "woah"); @@ -352,8 +373,6 @@ static void test_str_parms_str(const char *str) int main(void) { - struct str_parms *str_parms; - test_str_parms_str(""); test_str_parms_str(";"); test_str_parms_str("="); @@ -370,6 +389,15 @@ int main(void) test_str_parms_str("foo=bar;baz=bat;"); test_str_parms_str("foo=bar;baz=bat;foo=bar"); + // hashmapPut reports errors by setting errno to ENOMEM. + // Test that we're not confused by running in an environment where this is already true. + errno = ENOMEM; + test_str_parms_str("foo=bar;baz="); + if (errno != ENOMEM) { + abort(); + } + test_str_parms_str("foo=bar;baz="); + return 0; } #endif diff --git a/libcutils/threads.c b/libcutils/threads.c index 42cc928..bf182f0 100644 --- a/libcutils/threads.c +++ b/libcutils/threads.c @@ -1,5 +1,4 @@ -/* libs/cutils/threads.c -** +/* ** Copyright (C) 2007, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,13 +13,12 @@ ** See the License for the specific language governing permissions and ** limitations under the License. */ + #include <cutils/threads.h> #ifdef HAVE_PTHREADS void* thread_store_get( thread_store_t* store ) { - const pthread_key_t k = store->tls; - if (!store->has_tls) return NULL; diff --git a/libcutils/trace.c b/libcutils/trace.c index 9754a44..f57aac2 100644 --- a/libcutils/trace.c +++ b/libcutils/trace.c @@ -28,7 +28,7 @@ #include <cutils/trace.h> #define LOG_TAG "cutils-trace" -#include <cutils/log.h> +#include <log/log.h> volatile int32_t atrace_is_ready = 0; int atrace_marker_fd = -1; @@ -86,7 +86,6 @@ static bool atrace_is_cmdline_match(const char* cmdline) static bool atrace_is_app_tracing_enabled() { bool sys_debuggable = false; - bool proc_debuggable = false; char value[PROPERTY_VALUE_MAX]; bool result = false; diff --git a/liblog/Android.mk b/liblog/Android.mk index a23de2d..5e01903 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -22,10 +22,6 @@ else liblog_sources := logd_write_kern.c endif -ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),) -liblog_cflags := -DUSERDEBUG_BUILD=1 -endif - # some files must not be compiled when building against Mingw # they correspond to features not used by our host development tools # which are also hard or even impossible to port to native Win32 diff --git a/liblog/log_read.c b/liblog/log_read.c index 2dd07e6..11fe848 100644 --- a/liblog/log_read.c +++ b/liblog/log_read.c @@ -17,6 +17,7 @@ #include <errno.h> #include <fcntl.h> #include <inttypes.h> +#include <poll.h> #include <signal.h> #include <stddef.h> #define NOMINMAX /* for windows to suppress definition of min in stdlib.h */ @@ -196,7 +197,8 @@ static const char *LOG_NAME[LOG_ID_MAX] = { [LOG_ID_MAIN] = "main", [LOG_ID_RADIO] = "radio", [LOG_ID_EVENTS] = "events", - [LOG_ID_SYSTEM] = "system" + [LOG_ID_SYSTEM] = "system", + [LOG_ID_CRASH] = "crash", }; const char *android_log_id_to_name(log_id_t log_id) @@ -272,6 +274,8 @@ static ssize_t send_log_msg(struct logger *logger, const char *msg, char *buf, size_t buf_size) { ssize_t ret; + size_t len; + char *cp; int errno_save = 0; int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); @@ -283,12 +287,44 @@ static ssize_t send_log_msg(struct logger *logger, snprintf(buf, buf_size, msg, logger ? logger->id : (unsigned) -1); } - ret = write(sock, buf, strlen(buf) + 1); + len = strlen(buf) + 1; + ret = TEMP_FAILURE_RETRY(write(sock, buf, len)); if (ret <= 0) { goto done; } - ret = read(sock, buf, buf_size); + len = buf_size; + cp = buf; + while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) { + struct pollfd p; + + if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) { + break; + } + + len -= ret; + cp += ret; + + memset(&p, 0, sizeof(p)); + p.fd = sock; + p.events = POLLIN; + + /* Give other side 20ms to refill pipe */ + ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20)); + + if (ret <= 0) { + break; + } + + if (!(p.revents & POLLIN)) { + ret = 0; + break; + } + } + + if (ret >= 0) { + ret += buf_size - len; + } done: if ((ret == -1) && errno) { @@ -340,8 +376,6 @@ long android_logger_get_log_size(struct logger *logger) return atol(buf); } -#ifdef USERDEBUG_BUILD - int android_logger_set_log_size(struct logger *logger, unsigned long size) { char buf[512]; @@ -352,8 +386,6 @@ int android_logger_set_log_size(struct logger *logger, unsigned long size) return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf))); } -#endif /* USERDEBUG_BUILD */ - /* * returns the readable size of the log's ring buffer (that is, amount of the * log consumed) @@ -408,8 +440,6 @@ ssize_t android_logger_get_statistics(struct logger_list *logger_list, return send_log_msg(NULL, NULL, buf, len); } -#ifdef USERDEBUG_BUILD - ssize_t android_logger_get_prune_list(struct logger_list *logger_list UNUSED, char *buf, size_t len) { @@ -432,8 +462,6 @@ int android_logger_set_prune_list(struct logger_list *logger_list UNUSED, return check_log_success(buf, send_log_msg(NULL, NULL, buf, len)); } -#endif /* USERDEBUG_BUILD */ - struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c index 9cccb1d..021fe47 100644 --- a/liblog/log_read_kern.c +++ b/liblog/log_read_kern.c @@ -58,7 +58,8 @@ static const char *LOG_NAME[LOG_ID_MAX] = { [LOG_ID_MAIN] = "main", [LOG_ID_RADIO] = "radio", [LOG_ID_EVENTS] = "events", - [LOG_ID_SYSTEM] = "system" + [LOG_ID_SYSTEM] = "system", + [LOG_ID_CRASH] = "crash" }; const char *android_log_id_to_name(log_id_t log_id) @@ -232,16 +233,12 @@ long android_logger_get_log_size(struct logger *logger) return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR); } -#ifdef USERDEBUG_BUILD - int android_logger_set_log_size(struct logger *logger UNUSED, unsigned long size UNUSED) { return -ENOTSUP; } -#endif /* USERDEBUG_BUILD */ - /* * returns the readable size of the log's ring buffer (that is, amount of the * log consumed) @@ -272,8 +269,6 @@ ssize_t android_logger_get_statistics(struct logger_list *logger_list UNUSED, return -ENOTSUP; } -#ifdef USERDEBUG_BUILD - ssize_t android_logger_get_prune_list(struct logger_list *logger_list UNUSED, char *buf, size_t len) { @@ -289,8 +284,6 @@ int android_logger_set_prune_list(struct logger_list *logger_list UNUSED, return -ENOTSUP; } -#endif /* USERDEBUG_BUILD */ - struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) diff --git a/liblog/logd_write.c b/liblog/logd_write.c index 9c73dad..bd36a65 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -54,7 +54,7 @@ static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; static int logd_fd = -1; #if FAKE_LOG_DEVICE #define WEAK __attribute__((weak)) -static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; +static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 }; #endif /* @@ -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 @@ -243,7 +258,8 @@ static const char *LOG_NAME[LOG_ID_MAX] = { [LOG_ID_MAIN] = "main", [LOG_ID_RADIO] = "radio", [LOG_ID_EVENTS] = "events", - [LOG_ID_SYSTEM] = "system" + [LOG_ID_SYSTEM] = "system", + [LOG_ID_CRASH] = "crash" }; const WEAK char *android_log_id_to_name(log_id_t log_id) @@ -406,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 5ef349b..8c707ad 100644 --- a/liblog/logd_write_kern.c +++ b/liblog/logd_write_kern.c @@ -93,6 +93,9 @@ static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) int log_fd; if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) { + if (log_id == LOG_ID_CRASH) { + log_id = LOG_ID_MAIN; + } log_fd = log_fds[(int)log_id]; } else { return -EBADF; @@ -269,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/logprint.c b/liblog/logprint.c index a7480d5..3bc9f5a 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -938,88 +938,3 @@ done: return ret; } - - - -void logprint_run_tests() -{ -#if 0 - - fprintf(stderr, "tests disabled\n"); - -#else - - int err; - const char *tag; - AndroidLogFormat *p_format; - - p_format = android_log_format_new(); - - fprintf(stderr, "running tests\n"); - - tag = "random"; - - android_log_addFilterRule(p_format,"*:i"); - - assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - android_log_addFilterRule(p_format, "*"); - assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "*:v"); - assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "*:i"); - assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - - android_log_addFilterRule(p_format, "random"); - assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "random:v"); - assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "random:d"); - assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "random:w"); - assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - - android_log_addFilterRule(p_format, "crap:*"); - assert (ANDROID_LOG_VERBOSE== filterPriForTag(p_format, "crap")); - assert(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0); - - // invalid expression - err = android_log_addFilterRule(p_format, "random:z"); - assert (err < 0); - assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - - // Issue #550946 - err = android_log_addFilterString(p_format, " "); - assert(err == 0); - assert(ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); - - // note trailing space - err = android_log_addFilterString(p_format, "*:s random:d "); - assert(err == 0); - assert(ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); - - err = android_log_addFilterString(p_format, "*:s random:z"); - assert(err < 0); - - -#if 0 - char *ret; - char defaultBuffer[512]; - - ret = android_log_formatLogLine(p_format, - defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123, - 123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL); -#endif - - - fprintf(stderr, "tests complete\n"); -#endif -} 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 d726f2d..c6f981f 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp @@ -21,6 +21,7 @@ #include <log/log.h> #include <log/logger.h> #include <log/log_read.h> +#include <log/logprint.h> // enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and // non-syscall libs. Since we are only using this in the emergency of @@ -482,11 +483,68 @@ TEST(liblog, max_payload) { } } + android_logger_list_close(logger_list); + EXPECT_EQ(true, matches); - EXPECT_LE(sizeof(max_payload_buf), max_len); + 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) { @@ -555,3 +613,72 @@ TEST(liblog, android_logger_get_) { android_logger_list_close(logger_list); } + +static bool checkPriForTag(AndroidLogFormat *p_format, const char *tag, android_LogPriority pri) { + return android_log_shouldPrintLine(p_format, tag, pri) + && !android_log_shouldPrintLine(p_format, tag, (android_LogPriority)(pri - 1)); +} + +TEST(liblog, filterRule) { + static const char tag[] = "random"; + + AndroidLogFormat *p_format = android_log_format_new(); + + android_log_addFilterRule(p_format,"*:i"); + + EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_INFO)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + android_log_addFilterRule(p_format, "*"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:v"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:i"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_INFO)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, tag); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:v"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:d"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:w"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_WARN)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, "crap:*"); + EXPECT_TRUE (checkPriForTag(p_format, "crap", ANDROID_LOG_VERBOSE)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0); + + // invalid expression + EXPECT_TRUE (android_log_addFilterRule(p_format, "random:z") < 0); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_WARN)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + // Issue #550946 + EXPECT_TRUE(android_log_addFilterString(p_format, " ") == 0); + EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_WARN)); + + // note trailing space + EXPECT_TRUE(android_log_addFilterString(p_format, "*:s random:d ") == 0); + EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG)); + + EXPECT_TRUE(android_log_addFilterString(p_format, "*:s random:z") < 0); + +#if 0 // bitrot, seek update + char defaultBuffer[512]; + + android_log_formatLogLine(p_format, + defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123, + 123, 123, tag, "nofile", strlen("Hello"), "Hello", NULL); + + fprintf(stderr, "%s\n", defaultBuffer); +#endif + + android_log_format_free(p_format); +} diff --git a/libpixelflinger/codeflinger/disassem.c b/libpixelflinger/codeflinger/disassem.c index aeb8034..39dd614 100644 --- a/libpixelflinger/codeflinger/disassem.c +++ b/libpixelflinger/codeflinger/disassem.c @@ -301,19 +301,14 @@ static u_int disassemble_readword(u_int address); static void disassemble_printaddr(u_int address); u_int -disasm(const disasm_interface_t *di, u_int loc, int altfmt) +disasm(const disasm_interface_t *di, u_int loc, int __unused altfmt) { const struct arm32_insn *i_ptr = &arm32_i[0]; - - u_int insn; - int matchp; + u_int insn = di->di_readword(loc); + int matchp = 0; int branch; char* f_ptr; - int fmt; - - fmt = 0; - matchp = 0; - insn = di->di_readword(loc); + int fmt = 0; /* di->di_printf("loc=%08x insn=%08x : ", loc, insn);*/ @@ -670,7 +665,7 @@ disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, u_int loc) } static void -disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int loc) +disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int __unused loc) { if (((insn >> 8) & 0xf) == 1) di->di_printf("f%d, ", (insn >> 12) & 0x07); diff --git a/libpixelflinger/codeflinger/disassem.h b/libpixelflinger/codeflinger/disassem.h index 02747cd..c7c60b6 100644 --- a/libpixelflinger/codeflinger/disassem.h +++ b/libpixelflinger/codeflinger/disassem.h @@ -49,8 +49,8 @@ extern "C" { typedef struct { u_int (*di_readword)(u_int); - void (*di_printaddr)(u_int); - void (*di_printf)(const char *, ...); + void (*di_printaddr)(u_int); + int (*di_printf)(const char *, ...); } disasm_interface_t; /* Prototypes for callable functions */ diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h index 17d085c..8b757d2 100644 --- a/libsparse/include/sparse/sparse.h +++ b/libsparse/include/sparse/sparse.h @@ -20,6 +20,10 @@ #include <stdbool.h> #include <stdint.h> +#ifdef __cplusplus +extern "C" { +#endif + struct sparse_file; /** @@ -273,4 +277,8 @@ void sparse_file_verbose(struct sparse_file *s); */ extern void (*sparse_print_verbose)(const char *fmt, ...); +#ifdef __cplusplus +} +#endif + #endif diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c index 1df8c6a..2bece4c 100644 --- a/libsuspend/autosuspend_earlysuspend.c +++ b/libsuspend/autosuspend_earlysuspend.c @@ -75,13 +75,8 @@ static int wait_for_fb_sleep(void) return err < 0 ? err : 0; } -static void *earlysuspend_thread_func(void *arg) +static void *earlysuspend_thread_func(void __unused *arg) { - char buf[80]; - char wakeup_count[20]; - int wakeup_count_len; - int ret; - while (1) { if (wait_for_fb_sleep()) { ALOGE("Failed reading wait_for_fb_sleep, exiting earlysuspend thread\n"); 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/EventLogTags.logtags b/libsysutils/EventLogTags.logtags index 7aa5cad..713f8cd 100644 --- a/libsysutils/EventLogTags.logtags +++ b/libsysutils/EventLogTags.logtags @@ -1,5 +1,5 @@ # See system/core/logcat/event.logtags for a description of the format of this file. # FrameworkListener dispatchCommand overflow -78001 dispatchCommand_overflow -65537 netlink_failure (uid|1) +78001 exp_det_dispatchCommand_overflow +65537 exp_det_netlink_failure (uid|1) diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp index a5ffda2..e7b3dd6 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp @@ -39,6 +39,11 @@ FrameworkListener::FrameworkListener(const char *socketName) : init(socketName, false); } +FrameworkListener::FrameworkListener(int sock) : + SocketListener(sock, true) { + init(NULL, false); +} + void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) { mCommands = new FrameworkCommandCollection(); errorRate = 0; @@ -87,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)); @@ -161,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/libusbhost/usbhost.c b/libusbhost/usbhost.c index 8be393e..cd8000a 100644 --- a/libusbhost/usbhost.c +++ b/libusbhost/usbhost.c @@ -263,11 +263,12 @@ int usb_host_read_event(struct usb_host_context *context) D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ? "new" : "gone", path, i); if (i > 0 && i < MAX_USBFS_WD_COUNT) { + int local_ret = 0; if (event->mask & IN_CREATE) { - ret = inotify_add_watch(context->fd, path, + local_ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); - if (ret >= 0) - context->wds[i] = ret; + if (local_ret >= 0) + context->wds[i] = local_ret; done = find_existing_devices_bus(path, context->cb_added, context->data); } else if (event->mask & IN_DELETE) { diff --git a/libutils/BlobCache.cpp b/libutils/BlobCache.cpp index 0fb1d8e..660917b 100644 --- a/libutils/BlobCache.cpp +++ b/libutils/BlobCache.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "BlobCache" //#define LOG_NDEBUG 0 +#include <inttypes.h> #include <stdlib.h> #include <string.h> @@ -54,18 +55,18 @@ BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize void BlobCache::set(const void* key, size_t keySize, const void* value, size_t valueSize) { if (mMaxKeySize < keySize) { - ALOGV("set: not caching because the key is too large: %d (limit: %d)", + ALOGV("set: not caching because the key is too large: %zu (limit: %zu)", keySize, mMaxKeySize); return; } if (mMaxValueSize < valueSize) { - ALOGV("set: not caching because the value is too large: %d (limit: %d)", + ALOGV("set: not caching because the value is too large: %zu (limit: %zu)", valueSize, mMaxValueSize); return; } if (mMaxTotalSize < keySize + valueSize) { ALOGV("set: not caching because the combined key/value size is too " - "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize); + "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize); return; } if (keySize == 0) { @@ -94,15 +95,15 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, continue; } else { ALOGV("set: not caching new key/value pair because the " - "total cache size limit would be exceeded: %d " - "(limit: %d)", + "total cache size limit would be exceeded: %zu " + "(limit: %zu)", keySize + valueSize, mMaxTotalSize); break; } } mCacheEntries.add(CacheEntry(keyBlob, valueBlob)); mTotalSize = newTotalSize; - ALOGV("set: created new cache entry with %d byte key and %d byte value", + ALOGV("set: created new cache entry with %zu byte key and %zu byte value", keySize, valueSize); } else { // Update the existing cache entry. @@ -116,14 +117,14 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, continue; } else { ALOGV("set: not caching new value because the total cache " - "size limit would be exceeded: %d (limit: %d)", + "size limit would be exceeded: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize); break; } } mCacheEntries.editItemAt(index).setValue(valueBlob); mTotalSize = newTotalSize; - ALOGV("set: updated existing cache entry with %d byte key and %d byte " + ALOGV("set: updated existing cache entry with %zu byte key and %zu byte " "value", keySize, valueSize); } break; @@ -133,7 +134,7 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, size_t BlobCache::get(const void* key, size_t keySize, void* value, size_t valueSize) { if (mMaxKeySize < keySize) { - ALOGV("get: not searching because the key is too large: %d (limit %d)", + ALOGV("get: not searching because the key is too large: %zu (limit %zu)", keySize, mMaxKeySize); return 0; } @@ -141,7 +142,7 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, CacheEntry dummyEntry(dummyKey, NULL); ssize_t index = mCacheEntries.indexOf(dummyEntry); if (index < 0) { - ALOGV("get: no cache entry found for key of size %d", keySize); + ALOGV("get: no cache entry found for key of size %zu", keySize); return 0; } @@ -150,10 +151,10 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, sp<Blob> valueBlob(mCacheEntries[index].getValue()); size_t valueBlobSize = valueBlob->getSize(); if (valueBlobSize <= valueSize) { - ALOGV("get: copying %d bytes to caller's buffer", valueBlobSize); + ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize); memcpy(value, valueBlob->getData(), valueBlobSize); } else { - ALOGV("get: caller's buffer is too small for value: %d (needs %d)", + ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)", valueSize, valueBlobSize); } return valueBlobSize; @@ -229,7 +230,7 @@ status_t BlobCache::unflatten(void const* buffer, size_t size) { } const Header* header = reinterpret_cast<const Header*>(buffer); if (header->mMagicNumber != blobCacheMagic) { - ALOGE("unflatten: bad magic number: %d", header->mMagicNumber); + ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber); return BAD_VALUE; } if (header->mBlobCacheVersion != blobCacheVersion || diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp index 9ce370e..933e7aa 100644 --- a/libutils/FileMap.cpp +++ b/libutils/FileMap.cpp @@ -23,6 +23,7 @@ #include <utils/FileMap.h> #include <utils/Log.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> @@ -39,37 +40,32 @@ using namespace android; /*static*/ long FileMap::mPageSize = -1; - -/* - * Constructor. Create an empty object. - */ +// Constructor. Create an empty object. FileMap::FileMap(void) : mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0), mDataPtr(NULL), mDataLength(0) { } -/* - * Destructor. - */ +// Destructor. FileMap::~FileMap(void) { assert(mRefCount == 0); - //printf("+++ removing FileMap %p %u\n", mDataPtr, mDataLength); + //printf("+++ removing FileMap %p %zu\n", mDataPtr, mDataLength); mRefCount = -100; // help catch double-free if (mFileName != NULL) { free(mFileName); } -#ifdef HAVE_POSIX_FILEMAP +#ifdef HAVE_POSIX_FILEMAP if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { - ALOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); + ALOGD("munmap(%p, %zu) failed\n", mBasePtr, mBaseLength); } #endif #ifdef HAVE_WIN32_FILEMAP if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { - ALOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, + ALOGD("UnmapViewOfFile(%p) failed, error = %" PRId32 "\n", mBasePtr, GetLastError() ); } if (mFileMapping != INVALID_HANDLE_VALUE) { @@ -80,14 +76,12 @@ FileMap::~FileMap(void) } -/* - * Create a new mapping on an open file. - * - * Closing the file descriptor does not unmap the pages, so we don't - * claim ownership of the fd. - * - * Returns "false" on failure. - */ +// Create a new mapping on an open file. +// +// Closing the file descriptor does not unmap the pages, so we don't +// claim ownership of the fd. +// +// Returns "false" on failure. bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length, bool readOnly) { @@ -98,32 +92,32 @@ bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t le if (mPageSize == -1) { SYSTEM_INFO si; - + GetSystemInfo( &si ); mPageSize = si.dwAllocationGranularity; } DWORD protect = readOnly ? PAGE_READONLY : PAGE_READWRITE; - + mFileHandle = (HANDLE) _get_osfhandle(fd); mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL); if (mFileMapping == NULL) { - ALOGE("CreateFileMapping(%p, %lx) failed with error %ld\n", + ALOGE("CreateFileMapping(%p, %" PRIx32 ") failed with error %" PRId32 "\n", mFileHandle, protect, GetLastError() ); return false; } - + adjust = offset % mPageSize; adjOffset = offset - adjust; adjLength = length + adjust; - - mBasePtr = MapViewOfFile( mFileMapping, + + mBasePtr = MapViewOfFile( mFileMapping, readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, 0, (DWORD)(adjOffset), adjLength ); if (mBasePtr == NULL) { - ALOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n", + ALOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %" PRId32 "\n", adjOffset, adjLength, GetLastError() ); CloseHandle(mFileMapping); mFileMapping = INVALID_HANDLE_VALUE; @@ -142,7 +136,7 @@ bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t le assert(offset >= 0); assert(length > 0); - /* init on first use */ + // init on first use if (mPageSize == -1) { #if NOT_USING_KLIBC mPageSize = sysconf(_SC_PAGESIZE); @@ -151,7 +145,7 @@ bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t le return false; } #else - /* this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM */ + // this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM mPageSize = 4096; #endif } @@ -168,19 +162,19 @@ try_again: ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset); if (ptr == MAP_FAILED) { - // Cygwin does not seem to like file mapping files from an offset. - // So if we fail, try again with offset zero - if (adjOffset > 0) { - adjust = offset; - goto try_again; - } - - ALOGE("mmap(%ld,%ld) failed: %s\n", - (long) adjOffset, (long) adjLength, strerror(errno)); + // Cygwin does not seem to like file mapping files from an offset. + // So if we fail, try again with offset zero + if (adjOffset > 0) { + adjust = offset; + goto try_again; + } + + ALOGE("mmap(%" PRId64 ",%zu) failed: %s\n", + adjOffset, adjLength, strerror(errno)); return false; } mBasePtr = ptr; -#endif /* HAVE_POSIX_FILEMAP */ +#endif // HAVE_POSIX_FILEMAP mFileName = origFileName != NULL ? strdup(origFileName) : NULL; mBaseLength = adjLength; @@ -190,15 +184,13 @@ try_again: assert(mBasePtr != NULL); - ALOGV("MAP: base %p/%d data %p/%d\n", - mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength); + ALOGV("MAP: base %p/%zu data %p/%zu\n", + mBasePtr, mBaseLength, mDataPtr, mDataLength); return true; } -/* - * Provide guidance to the system. - */ +// Provide guidance to the system. int FileMap::advise(MapAdvice advice) { #if HAVE_MADVISE @@ -220,6 +212,6 @@ int FileMap::advise(MapAdvice advice) ALOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); return cc; #else - return -1; + return -1; #endif // HAVE_MADVISE } diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp index f398a82..385c226 100644 --- a/libutils/RefBase.cpp +++ b/libutils/RefBase.cpp @@ -109,7 +109,7 @@ public: char inc = refs->ref >= 0 ? '+' : '-'; ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); #if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(LOG_TAG); + refs->stack.log(LOG_TAG); #endif refs = refs->next; } @@ -123,7 +123,7 @@ public: char inc = refs->ref >= 0 ? '+' : '-'; ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); #if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(LOG_TAG); + refs->stack.log(LOG_TAG); #endif refs = refs->next; } diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp index 5293cd2..a431e92 100644 --- a/libutils/Timers.cpp +++ b/libutils/Timers.cpp @@ -34,7 +34,7 @@ nsecs_t systemTime(int clock) { -#if defined(HAVE_POSIX_CLOCKS) +#if defined(HAVE_ANDROID_OS) static const clockid_t clocks[] = { CLOCK_REALTIME, CLOCK_MONOTONIC, @@ -47,7 +47,9 @@ nsecs_t systemTime(int clock) clock_gettime(clocks[clock], &t); return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec; #else - // we don't support the clocks here. + // Clock support varies widely across hosts. Mac OS doesn't support + // posix clocks, older glibcs don't support CLOCK_BOOTTIME and Windows + // is windows. struct timeval t; t.tv_sec = t.tv_usec = 0; gettimeofday(&t, NULL); diff --git a/libutils/tests/BasicHashtable_test.cpp b/libutils/tests/BasicHashtable_test.cpp index 7dcf750..a61b1e1 100644 --- a/libutils/tests/BasicHashtable_test.cpp +++ b/libutils/tests/BasicHashtable_test.cpp @@ -397,7 +397,7 @@ TEST_F(BasicHashtableTest, Next_WhenNonEmpty_IteratesOverAllEntries) { const SimpleEntry& entry = h.entryAt(index); ASSERT_GE(entry.key, 0); ASSERT_LT(entry.key, N); - ASSERT_EQ(false, set[entry.key]); + ASSERT_FALSE(set[entry.key]); ASSERT_EQ(entry.key * 10, entry.value); set[entry.key] = true; diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp index e573952..bcbea32 100644 --- a/libutils/tests/LruCache_test.cpp +++ b/libutils/tests/LruCache_test.cpp @@ -184,7 +184,7 @@ TEST_F(LruCacheTest, StressTest) { for (size_t i = 0; i < kNumKeys; i++) { strings[i] = (char *)malloc(16); - sprintf(strings[i], "%d", i); + sprintf(strings[i], "%zu", i); } srandom(12345); diff --git a/logcat/Android.mk b/logcat/Android.mk index dd15cb3..f46a4de 100644 --- a/logcat/Android.mk +++ b/logcat/Android.mk @@ -3,15 +3,13 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),) -LOCAL_CFLAGS += -DUSERDEBUG_BUILD=1 -endif - LOCAL_SRC_FILES:= logcat.cpp event.logtags LOCAL_SHARED_LIBRARIES := liblog -LOCAL_MODULE:= logcat +LOCAL_MODULE := logcat + +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 5a80efe..ea46345 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -227,22 +227,20 @@ static void show_help(const char *cmd) " -T <count> print only the most recent <count> lines (does not imply -d)\n" " -g get the size of the log's ring buffer and exit\n" " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n" - " 'events' or 'all'. Multiple -b parameters are allowed and\n" - " results are interleaved. The default is -b main -b system.\n" + " 'events', 'crash' or 'all'. Multiple -b parameters are\n" + " allowed and results are interleaved. The default is\n" + " -b main -b system -b crash.\n" " -B output the log in binary.\n" - " -S output statistics.\n"); - -#ifdef USERDEBUG_BUILD - - fprintf(stderr, "--------------------- eng & userdebug builds only ---------------------------\n" - " -G <count> set size of log's ring buffer and exit\n" - " -p output prune white and ~black list\n" - " -P '<list> ...' set prune white and ~black list; UID, /PID or !(worst UID)\n" - " default is ~!, prune worst UID.\n" - "-----------------------------------------------------------------------------\n" - ); - -#endif + " -S output statistics.\n" + " -G <size> set size of log ring buffer, may suffix with K or M.\n" + " -p print prune white and ~black list. Service is specified as\n" + " UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n" + " with ~, otherwise weighed for longevity if unadorned. All\n" + " other pruning activity is oldest first. Special case ~!\n" + " represents an automatic quicker pruning for the noisiest\n" + " UID as determined by the current statistics.\n" + " -P '<list> ...' set prune white and ~black list, using same format as\n" + " printed above. Must be quoted.\n"); fprintf(stderr,"\nfilterspecs are a series of \n" " <tag>[:priority]\n\n" @@ -283,19 +281,15 @@ static int setLogFormat(const char * formatString) return 0; } -extern "C" void logprint_run_tests(void); - int main(int argc, char **argv) { int err; int hasSetLogFormat = 0; int clearLog = 0; int getLogSize = 0; -#ifdef USERDEBUG_BUILD unsigned long setLogSize = 0; int getPruneList = 0; char *setPruneList = NULL; -#endif int printStatistics = 0; int mode = O_RDONLY; const char *forceFilters = NULL; @@ -310,11 +304,6 @@ int main(int argc, char **argv) g_logformat = android_log_format_new(); - if (argc == 2 && 0 == strcmp(argv[1], "--test")) { - logprint_run_tests(); - exit(0); - } - if (argc == 2 && 0 == strcmp(argv[1], "--help")) { android::show_help(argv[0]); exit(0); @@ -323,13 +312,7 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, -#ifdef USERDEBUG_BUILD - "cdt:T:gG:sQf:r::n:v:b:BSpP:" -#else - "cdt:T:gsQf:r::n:v:b:BS" -#endif - ); + ret = getopt(argc, argv, "cdt:T:gG:sQf:r::n:v:b:BSpP:"); if (ret < 0) { break; @@ -386,8 +369,6 @@ int main(int argc, char **argv) getLogSize = 1; break; -#ifdef USERDEBUG_BUILD - case 'G': { // would use atol if not for the multiplier char *cp = optarg; @@ -433,8 +414,6 @@ int main(int argc, char **argv) setPruneList = optarg; break; -#endif - case 'b': { if (strcmp(optarg, "all") == 0) { while (devices) { @@ -462,10 +441,17 @@ int main(int argc, char **argv) if (android_name_to_log_id("events") == LOG_ID_EVENTS) { dev->next = new log_device_t("events", true, 'e'); if (dev->next) { + dev = dev->next; android::g_devCount++; needBinary = true; } } + if (android_name_to_log_id("crash") == LOG_ID_CRASH) { + dev->next = new log_device_t("crash", false, 'c'); + if (dev->next) { + android::g_devCount++; + } + } break; } @@ -503,9 +489,6 @@ int main(int argc, char **argv) android::g_logRotateSizeKBytes = DEFAULT_LOG_ROTATE_SIZE_KBYTES; } else { - long logRotateSize; - char *lastDigit; - if (!isdigit(optarg[0])) { fprintf(stderr,"Invalid parameter to -r\n"); android::show_help(argv[0]); @@ -622,6 +605,14 @@ int main(int argc, char **argv) devices->next = new log_device_t("system", false, 's'); android::g_devCount++; } + if (android_name_to_log_id("crash") == LOG_ID_CRASH) { + if (devices->next) { + devices->next->next = new log_device_t("crash", false, 'c'); + } else { + devices->next = new log_device_t("crash", false, 'c'); + } + android::g_devCount++; + } } if (android::g_logRotateSizeKBytes != 0 @@ -704,15 +695,11 @@ int main(int argc, char **argv) } } -#ifdef USERDEBUG_BUILD - if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) { perror("failed to set the log size"); exit(EXIT_FAILURE); } -#endif - if (getLogSize) { long size, readable; @@ -737,8 +724,6 @@ int main(int argc, char **argv) dev = dev->next; } -#ifdef USERDEBUG_BUILD - if (setPruneList) { size_t len = strlen(setPruneList) + 32; // margin to allow rc char *buf = (char *) malloc(len); @@ -753,30 +738,18 @@ int main(int argc, char **argv) } } -#endif - - if ( -#ifdef USERDEBUG_BUILD - printStatistics || getPruneList -#else - printStatistics -#endif - ) { + if (printStatistics || getPruneList) { size_t len = 8192; char *buf; for(int retry = 32; (retry >= 0) && ((buf = new char [len])); delete [] buf, --retry) { -#ifdef USERDEBUG_BUILD if (getPruneList) { android_logger_get_prune_list(logger_list, buf, len); } else { android_logger_get_statistics(logger_list, buf, len); } -#else - android_logger_get_statistics(logger_list, buf, len); -#endif buf[len-1] = '\0'; size_t ret = atol(buf) + 1; if (ret < 4) { @@ -824,11 +797,9 @@ int main(int argc, char **argv) if (getLogSize) { exit(0); } -#ifdef USERDEBUG_BUILD if (setLogSize || setPruneList) { exit(0); } -#endif if (clearLog) { exit(0); } diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk index 733af31..d42b3d0 100644 --- a/logcat/tests/Android.mk +++ b/logcat/tests/Android.mk @@ -30,10 +30,6 @@ test_c_flags := \ -Werror \ -fno-builtin -ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),) -test_c_flags += -DUSERDEBUG_BUILD=1 -endif - test_src_files := \ logcat_test.cpp \ diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp index 0165073..2e8ae8b 100644 --- a/logcat/tests/logcat_test.cpp +++ b/logcat/tests/logcat_test.cpp @@ -392,7 +392,7 @@ TEST(logcat, get_) { ASSERT_EQ(4, count); } -static void caught_blocking(int signum) +static void caught_blocking(int /*signum*/) { unsigned long long v = 0xDEADBEEFA55A0000ULL; @@ -461,7 +461,7 @@ TEST(logcat, blocking) { EXPECT_EQ(1, signals); } -static void caught_blocking_tail(int signum) +static void caught_blocking_tail(int /*signum*/) { unsigned long long v = 0xA55ADEADBEEF0000ULL; @@ -532,7 +532,7 @@ TEST(logcat, blocking_tail) { EXPECT_EQ(1, signals); } -static void caught_blocking_clear(int signum) +static void caught_blocking_clear(int /*signum*/) { unsigned long long v = 0xDEADBEEFA55C0000ULL; @@ -609,7 +609,6 @@ TEST(logcat, blocking_clear) { EXPECT_EQ(1, signals); } -#ifdef USERDEBUG_BUILD static bool get_white_black(char **list) { FILE *fp; @@ -705,4 +704,3 @@ TEST(logcat, white_black_adjust) { free(list); list = NULL; } -#endif // USERDEBUG_BUILD diff --git a/logd/Android.mk b/logd/Android.mk index b0bc746..9f4c64f 100644 --- a/logd/Android.mk +++ b/logd/Android.mk @@ -4,10 +4,6 @@ include $(CLEAR_VARS) LOCAL_MODULE:= logd -ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),) -LOCAL_CFLAGS += -DUSERDEBUG_BUILD=1 -endif - LOCAL_SRC_FILES := \ main.cpp \ LogCommand.cpp \ @@ -19,7 +15,9 @@ LOCAL_SRC_FILES := \ LogBufferElement.cpp \ LogTimes.cpp \ LogStatistics.cpp \ - LogWhiteBlackList.cpp + LogWhiteBlackList.cpp \ + libaudit.c \ + LogAudit.cpp LOCAL_SHARED_LIBRARIES := \ libsysutils \ @@ -27,6 +25,8 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils -LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp index 12b10ca..9d7d152 100644 --- a/logd/CommandListener.cpp +++ b/logd/CommandListener.cpp @@ -21,9 +21,11 @@ #include <netinet/in.h> #include <string.h> #include <stdlib.h> +#include <sys/prctl.h> #include <sys/socket.h> #include <sys/types.h> +#include <cutils/sockets.h> #include <private/android_filesystem_config.h> #include <sysutils/SocketClient.h> @@ -32,20 +34,16 @@ CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/, LogListener * /*swl*/) - : FrameworkListener("logd") + : FrameworkListener(getLogSocket()) , mBuf(*buf) { // registerCmd(new ShutdownCmd(buf, writer, swl)); registerCmd(new ClearCmd(buf)); registerCmd(new GetBufSizeCmd(buf)); -#ifdef USERDEBUG_BUILD registerCmd(new SetBufSizeCmd(buf)); -#endif registerCmd(new GetBufSizeUsedCmd(buf)); registerCmd(new GetStatisticsCmd(buf)); -#ifdef USERDEBUG_BUILD registerCmd(new SetPruneListCmd(buf)); registerCmd(new GetPruneListCmd(buf)); -#endif } CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader, @@ -69,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; @@ -99,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; @@ -117,8 +121,6 @@ int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli, return 0; } -#ifdef USERDEBUG_BUILD - CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf) : LogCommand("setLogSize") , mBuf(*buf) @@ -126,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; @@ -152,8 +155,6 @@ int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli, return 0; } -#endif // USERDEBUG_BUILD - CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf) : LogCommand("getLogSizeUsed") , mBuf(*buf) @@ -161,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; @@ -204,8 +206,8 @@ 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)) { uid = AID_ROOT; } @@ -236,8 +238,6 @@ int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli, return 0; } -#ifdef USERDEBUG_BUILD - CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf) : LogCommand("getPruneList") , mBuf(*buf) @@ -245,6 +245,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) { @@ -264,6 +265,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; @@ -294,4 +296,15 @@ int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli, return 0; } -#endif // USERDEBUG_BUILD +int CommandListener::getLogSocket() { + static const char socketName[] = "logd"; + int sock = android_get_control_socket(socketName); + + if (sock < 0) { + sock = socket_local_server(socketName, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + } + + return sock; +} diff --git a/logd/CommandListener.h b/logd/CommandListener.h index de1dcb9..cd1c306 100644 --- a/logd/CommandListener.h +++ b/logd/CommandListener.h @@ -31,6 +31,8 @@ public: virtual ~CommandListener() {} private: + static int getLogSocket(); + class ShutdownCmd : public LogCommand { LogBuffer &mBuf; LogReader &mReader; @@ -53,15 +55,11 @@ private: LogBufferCmd(Clear) LogBufferCmd(GetBufSize) -#ifdef USERDEBUG_BUILD LogBufferCmd(SetBufSize) -#endif LogBufferCmd(GetBufSizeUsed) LogBufferCmd(GetStatistics) -#ifdef USERDEBUG_BUILD LogBufferCmd(GetPruneList) LogBufferCmd(SetPruneList) -#endif }; #endif diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp new file mode 100644 index 0000000..add0f0e --- /dev/null +++ b/logd/LogAudit.cpp @@ -0,0 +1,203 @@ +/* + * 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 <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" +#include "LogAudit.h" + +LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmsg) + : SocketListener(getLogSocket(), false) + , logbuf(buf) + , reader(reader) + , fdDmesg(-1) { + logDmesg(); + fdDmesg = 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; + } + + logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data); + + return true; +} + +#define AUDIT_LOG_ID LOG_ID_MAIN +#define AUDIT_LOG_PRIO ANDROID_LOG_WARN + +int LogAudit::logPrint(const char *fmt, ...) { + if (fmt == NULL) { + return -EINVAL; + } + + va_list args; + + char *str = NULL; + va_start(args, fmt); + int rc = vasprintf(&str, fmt, args); + va_end(args); + + if (rc < 0) { + return rc; + } + + if (fdDmesg >= 0) { + struct iovec iov[2]; + + iov[0].iov_base = str; + iov[0].iov_len = strlen(str); + iov[1].iov_base = const_cast<char *>("\n"); + iov[1].iov_len = 1; + + writev(fdDmesg, iov, sizeof(iov) / sizeof(iov[0])); + } + + pid_t pid = getpid(); + pid_t tid = gettid(); + uid_t uid = getuid(); + log_time now; + + static const char audit_str[] = " audit("; + char *timeptr = strstr(str, audit_str); + char *cp; + if (timeptr + && ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) + && (*cp == ':')) { + memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3); + strcpy(timeptr + sizeof(audit_str) - 1 + 3, cp); + } else { + now.strptime("", ""); // side effect of setting CLOCK_REALTIME + } + + static const char pid_str[] = " pid="; + char *pidptr = strstr(str, pid_str); + if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) { + cp = pidptr + sizeof(pid_str) - 1; + pid = 0; + while (isdigit(*cp)) { + pid = (pid * 10) + (*cp - '0'); + ++cp; + } + tid = pid; + uid = logbuf->pidToUid(pid); + strcpy(pidptr, cp); + } + + static const char comm_str[] = " comm=\""; + char *comm = strstr(str, comm_str); + if (comm) { + cp = comm; + comm += sizeof(comm_str) - 1; + char *ecomm = strchr(comm, '"'); + if (ecomm) { + *ecomm = '\0'; + } + comm = strdup(comm); + if (ecomm) { + strcpy(cp, ecomm + 1); + } + } else if (pid == getpid()) { + pid = tid; + comm = strdup("auditd"); + } else if (!(comm = logbuf->pidToName(pid))) { + comm = strdup("unknown"); + } + + size_t l = strlen(comm) + 1; + size_t n = l + strlen(str) + 2; + + char *newstr = reinterpret_cast<char *>(malloc(n)); + if (!newstr) { + free(comm); + free(str); + return -ENOMEM; + } + + *newstr = AUDIT_LOG_PRIO; + strcpy(newstr + 1, comm); + free(comm); + strcpy(newstr + 1 + l, str); + free(str); + + logbuf->log(AUDIT_LOG_ID, now, uid, pid, tid, newstr, + (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); + reader->notifyNewLog(); + + free(newstr); + + return rc; +} + +void LogAudit::logDmesg() { + int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0); + if (len <= 0) { + return; + } + + len++; + char buf[len]; + + int rc = klogctl(KLOG_READ_ALL, buf, len); + + buf[len - 1] = '\0'; + + for(char *tok = buf; (rc >= 0) && ((tok = strtok(tok, "\r\n"))); tok = NULL) { + char *audit = strstr(tok, " audit("); + if (!audit) { + continue; + } + + *audit++ = '\0'; + + char *type = strstr(tok, "type="); + if (type) { + rc = logPrint("%s %s", type, audit); + } else { + rc = logPrint("%s", audit); + } + } +} + +int LogAudit::getLogSocket() { + int fd = audit_open(); + if (fd < 0) { + return fd; + } + if (audit_set_pid(fd, getpid(), WAIT_YES) < 0) { + audit_close(fd); + fd = -1; + } + return fd; +} diff --git a/logd/LogAudit.h b/logd/LogAudit.h new file mode 100644 index 0000000..111030a --- /dev/null +++ b/logd/LogAudit.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef _LOGD_LOG_AUDIT_H__ +#define _LOGD_LOG_AUDIT_H__ + +#include <sysutils/SocketListener.h> +#include "LogReader.h" + +class LogAudit : public SocketListener { + LogBuffer *logbuf; + LogReader *reader; + int fdDmesg; + +public: + LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg); + +protected: + virtual bool onDataAvailable(SocketClient *cli); + +private: + static int getLogSocket(); + void logDmesg(); + int logPrint(const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); +}; + +#endif diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp index 1c5cef0..38a237c 100644 --- a/logd/LogBuffer.cpp +++ b/logd/LogBuffer.cpp @@ -28,20 +28,16 @@ // Default #define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here? -#ifdef USERDEBUG_BUILD #define log_buffer_size(id) mMaxSize[id] -#else -#define log_buffer_size(id) LOG_BUFFER_SIZE -#endif LogBuffer::LogBuffer(LastLogTimes *times) : mTimes(*times) { pthread_mutex_init(&mLogElementsLock, NULL); -#ifdef USERDEBUG_BUILD + dgram_qlen_statistics = false; + log_id_for_each(i) { mMaxSize[i] = LOG_BUFFER_SIZE; } -#endif } void LogBuffer::log(log_id_t log_id, log_time realtime, @@ -61,6 +57,23 @@ void LogBuffer::log(log_id_t log_id, log_time realtime, LogBufferElementCollection::iterator last = it; while (--it != mLogElements.begin()) { if ((*it)->getRealTime() <= realtime) { + // halves the peak performance, use with caution + if (dgram_qlen_statistics) { + LogBufferElementCollection::iterator ib = it; + unsigned short buckets, num = 1; + for (unsigned short i = 0; (buckets = stats.dgram_qlen(i)); ++i) { + buckets -= num; + num += buckets; + while (buckets && (--ib != mLogElements.begin())) { + --buckets; + } + if (buckets) { + break; + } + stats.recordDiff( + elem->getRealTime() - (*ib)->getRealTime(), i); + } + } break; } last = it; @@ -152,22 +165,16 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows) { size_t worst_sizes = 0; size_t second_worst_sizes = 0; -#ifdef USERDEBUG_BUILD - if (mPrune.worstUidEnabled()) -#endif - { + 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(); } } } @@ -198,9 +205,7 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows) { break; } worst_sizes -= len; - } -#ifdef USERDEBUG_BUILD - else if (mPrune.naughty(e)) { // BlackListed + } else if (mPrune.naughty(e)) { // BlackListed it = mLogElements.erase(it); stats.subtract(e->getMsgLen(), id, uid, e->getPid()); delete e; @@ -208,34 +213,23 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows) { if (pruneRows == 0) { break; } - } -#endif - else { + } else { ++it; } } - if (!kick -#ifdef USERDEBUG_BUILD - || !mPrune.worstUidEnabled() -#endif - ) { + if (!kick || !mPrune.worstUidEnabled()) { break; // the following loop will ask bad clients to skip/drop } } -#ifdef USERDEBUG_BUILD bool whitelist = false; -#endif it = mLogElements.begin(); while((pruneRows > 0) && (it != mLogElements.end())) { LogBufferElement *e = *it; if (e->getLogId() == id) { if (oldest && (oldest->mStart <= e->getMonotonicTime())) { -#ifdef USERDEBUG_BUILD - if (!whitelist) -#endif - { + if (!whitelist) { if (stats.sizes(id) > (2 * log_buffer_size(id))) { // kick a misbehaving log reader client off the island oldest->release_Locked(); @@ -245,13 +239,13 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows) { } break; } -#ifdef USERDEBUG_BUILD + if (mPrune.nice(e)) { // WhiteListed whitelist = true; it++; continue; } -#endif + it = mLogElements.erase(it); stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid()); delete e; @@ -261,7 +255,6 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows) { } } -#ifdef USERDEBUG_BUILD if (whitelist && (pruneRows > 0)) { it = mLogElements.begin(); while((it != mLogElements.end()) && (pruneRows > 0)) { @@ -285,7 +278,6 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows) { } } } -#endif LogTimeEntry::unlock(); } @@ -305,8 +297,6 @@ unsigned long LogBuffer::getSizeUsed(log_id_t id) { return retval; } -#ifdef USERDEBUG_BUILD - // set the total space allocated to "id" int LogBuffer::setSize(log_id_t id, unsigned long size) { // Reasonable limits ... @@ -327,15 +317,6 @@ unsigned long LogBuffer::getSize(log_id_t id) { return retval; } -#else // ! USERDEBUG_BUILD - -// get the total space allocated to "id" -unsigned long LogBuffer::getSize(log_id_t /*id*/) { - return log_buffer_size(id); -} - -#endif - log_time LogBuffer::flushTo( SocketClient *reader, const log_time start, bool privileged, bool (*filter)(const LogBufferElement *element, void *arg), void *arg) { diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h index cbbb2ce..b8a54b9 100644 --- a/logd/LogBuffer.h +++ b/logd/LogBuffer.h @@ -36,11 +36,11 @@ class LogBuffer { LogStatistics stats; -#ifdef USERDEBUG_BUILD + bool dgram_qlen_statistics; + PruneList mPrune; unsigned long mMaxSize[LOG_ID_MAX]; -#endif public: LastLogTimes &mTimes; @@ -57,18 +57,23 @@ public: void clear(log_id_t id); unsigned long getSize(log_id_t id); -#ifdef USERDEBUG_BUILD int setSize(log_id_t id, unsigned long size); -#endif unsigned long getSizeUsed(log_id_t id); // *strp uses malloc, use free to release. void formatStatistics(char **strp, uid_t uid, unsigned int logMask); -#ifdef USERDEBUG_BUILD + void enableDgramQlenStatistics() { + stats.enableDgramQlenStatistics(); + dgram_qlen_statistics = true; + } + int initPrune(char *cp) { return mPrune.init(cp); } // *strp uses malloc, use free to release. void formatPrune(char **strp) { mPrune.format(strp); } -#endif + + // helper + char *pidToName(pid_t pid) { return stats.pidToName(pid); } + uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); } private: void maybePrune(log_id_t id); diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp index b835b4f..8186cea 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,17 +101,24 @@ 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, + ((size_t) n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); + reader->notifyNewLog(); return true; } int LogListener::getLogSocket() { - int sock = android_get_control_socket("logdw"); + static const char socketName[] = "logdw"; + int sock = android_get_control_socket(socketName); + + if (sock < 0) { + sock = socket_local_server(socketName, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_DGRAM); + } + int on = 1; if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { return -1; diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp index 60a3507..8458c19 100644 --- a/logd/LogReader.cpp +++ b/logd/LogReader.cpp @@ -16,14 +16,16 @@ #include <ctype.h> #include <poll.h> +#include <sys/prctl.h> #include <sys/socket.h> + #include <cutils/sockets.h> #include "LogReader.h" #include "FlushCommand.h" LogReader::LogReader(LogBuffer *logbuf) - : SocketListener("logdr", true) + : SocketListener(getLogSocket(), true) , mLogbuf(*logbuf) { } @@ -35,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); @@ -167,3 +171,16 @@ void LogReader::doSocketDelete(SocketClient *cli) { } LogTimeEntry::unlock(); } + +int LogReader::getLogSocket() { + static const char socketName[] = "logdr"; + int sock = android_get_control_socket(socketName); + + if (sock < 0) { + sock = socket_local_server(socketName, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_SEQPACKET); + } + + return sock; +} diff --git a/logd/LogReader.h b/logd/LogReader.h index b267c75..91559a3 100644 --- a/logd/LogReader.h +++ b/logd/LogReader.h @@ -34,6 +34,8 @@ protected: virtual bool onDataAvailable(SocketClient *cli); private: + static int getLogSocket(); + void doSocketDelete(SocketClient *cli); }; diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp index 49ee50d..81c9bab 100644 --- a/logd/LogStatistics.cpp +++ b/logd/LogStatistics.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <fcntl.h> #include <stdarg.h> #include <time.h> @@ -23,12 +24,50 @@ #include "LogStatistics.h" -PidStatistics::PidStatistics(pid_t pid) +PidStatistics::PidStatistics(pid_t pid, char *name) : pid(pid) , mSizesTotal(0) , mElementsTotal(0) , mSizes(0) - , mElements(0) { } + , mElements(0) + , name(name) + , mGone(false) +{ } + +#ifdef DO_NOT_ERROR_IF_PIDSTATISTICS_USES_A_COPY_CONSTRUCTOR +PidStatistics::PidStatistics(const PidStatistics ©) + : pid(copy->pid) + , name(copy->name ? strdup(copy->name) : NULL) + , mSizesTotal(copy->mSizesTotal) + , mElementsTotal(copy->mElementsTotal) + , mSizes(copy->mSizes) + , mElements(copy->mElements) + , mGone(copy->mGone) +{ } +#endif + +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; +} void PidStatistics::add(unsigned short size) { mSizesTotal += size; @@ -40,7 +79,7 @@ void PidStatistics::add(unsigned short size) { bool PidStatistics::subtract(unsigned short size) { mSizes -= size; --mElements; - return mElements == 0 && kill(pid, 0); + return (mElements == 0) && pidGone(); } void PidStatistics::addTotal(size_t size, size_t element) { @@ -50,8 +89,32 @@ 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 != gone) { + char buffer[512]; + snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid); + int fd = open(buffer, O_RDONLY); + if (fd >= 0) { + ssize_t ret = read(fd, buffer, sizeof(buffer)); + if (ret > 0) { + buffer[sizeof(buffer)-1] = '\0'; + // frameworks intermediate state + if (strcmp(buffer, "<pre-initialized>")) { + retval = strdup(buffer); + } + } + close(fd); + } + } + return retval; +} + UidStatistics::UidStatistics(uid_t uid) - : uid(uid) { + : uid(uid) + , mSizes(0) + , mElements(0) { Pids.clear(); } @@ -64,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; @@ -71,19 +137,12 @@ 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)); - p = new PidStatistics(pid); + // 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); } else { @@ -93,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; @@ -121,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) { @@ -221,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; @@ -275,6 +389,66 @@ LogStatistics::LogStatistics() mSizes[i] = 0; mElements[i] = 0; } + + dgram_qlen_statistics = false; + for(unsigned short bucket = 0; dgram_qlen(bucket); ++bucket) { + mMinimum[bucket].tv_sec = mMinimum[bucket].tv_sec_max; + mMinimum[bucket].tv_nsec = mMinimum[bucket].tv_nsec_max; + } +} + +// Each bucket below represents a dgram_qlen of log messages. By +// finding the minimum period of time from start to finish +// of each dgram_qlen, we can get a performance expectation for +// the user space logger. The net result is that the period +// of time divided by the dgram_qlen will give us the average time +// between log messages; at the point where the average time +// is greater than the throughput capability of the logger +// we will not longer require the benefits of the FIFO formed +// by max_dgram_qlen. We will also expect to see a very visible +// knee in the average time between log messages at this point, +// so we do not necessarily have to compare the rate against the +// measured performance (BM_log_maximum_retry) of the logger. +// +// for example (reformatted): +// +// Minimum time between log events per dgram_qlen: +// 1 2 3 5 10 20 30 50 100 200 300 400 500 600 +// 5u2 12u 13u 15u 16u 27u 30u 36u 407u 3m1 3m3 3m9 3m9 5m5 +// +// demonstrates a clear knee rising at 100, so this means that for this +// case max_dgram_qlen = 100 would be more than sufficient to handle the +// worst that the system could stuff into the logger. The +// BM_log_maximum_retry performance (derated by the log collection) on the +// same system was 33.2us so we would almost be fine with max_dgram_qlen = 50. +// BM_log_maxumum_retry with statistics off is roughly 20us, so +// max_dgram_qlen = 20 would work. We will be more than willing to have +// a large engineering margin so the rule of thumb that lead us to 100 is +// fine. +// +// bucket dgram_qlen are tuned for /proc/sys/net/unix/max_dgram_qlen = 300 +const unsigned short LogStatistics::mBuckets[] = { + 1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 300, 400, 500, 600 +}; + +unsigned short LogStatistics::dgram_qlen(unsigned short bucket) { + if (bucket >= sizeof(mBuckets) / sizeof(mBuckets[0])) { + return 0; + } + return mBuckets[bucket]; +} + +unsigned long long LogStatistics::minimum(unsigned short bucket) { + if (mMinimum[bucket].tv_sec == mMinimum[bucket].tv_sec_max) { + return 0; + } + return mMinimum[bucket].nsec(); +} + +void LogStatistics::recordDiff(log_time diff, unsigned short bucket) { + if ((diff.tv_sec || diff.tv_nsec) && (mMinimum[bucket] > diff)) { + mMinimum[bucket] = diff; + } } void LogStatistics::add(unsigned short size, @@ -337,11 +511,11 @@ size_t LogStatistics::elementsTotal(log_id_t log_id, uid_t uid, pid_t pid) { void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask, log_time oldest) { - const unsigned short spaces_current = 13; - const unsigned short spaces_total = 19; + static const unsigned short spaces_current = 13; + static const unsigned short spaces_total = 19; if (*buf) { - free(buf); + free(*buf); *buf = NULL; } @@ -350,10 +524,22 @@ void LogStatistics::format(char **buf, short spaces = 2; log_id_for_each(i) { - if (logMask & (1 << i)) { - oldLength = string.length(); - 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(); } } @@ -369,6 +555,9 @@ void LogStatistics::format(char **buf, continue; } oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } string.appendFormat("%*s%zu/%zu", spaces, "", sizesTotal(i), elementsTotal(i)); spaces += spaces_total + oldLength - string.length(); @@ -388,18 +577,170 @@ void LogStatistics::format(char **buf, size_t els = elements(i); if (els) { oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } string.appendFormat("%*s%zu/%zu", spaces, "", sizes(i), els); spaces -= string.length() - oldLength; } spaces += spaces_total; } + // Construct list of worst spammers by Pid + static const unsigned char num_spammers = 10; + bool header = false; + log_id_for_each(i) { if (!(logMask & (1 << i))) { continue; } - bool header = false; + PidStatisticsCollection pids; + pids.clear(); + + LidStatistics &l = id(i); + UidStatisticsCollection::iterator iu; + for (iu = l.begin(); iu != l.end(); ++iu) { + UidStatistics &u = *(*iu); + PidStatisticsCollection::iterator ip; + for (ip = u.begin(); ip != u.end(); ++ip) { + PidStatistics *p = (*ip); + if (p->getPid() == p->gone) { + break; + } + + size_t mySizes = p->sizes(); + + PidStatisticsCollection::iterator q; + unsigned char num = 0; + for (q = pids.begin(); q != pids.end(); ++q) { + if (mySizes > (*q)->sizes()) { + pids.insert(q, p); + break; + } + // do we need to traverse deeper in the list? + if (++num > num_spammers) { + break; + } + } + if (q == pids.end()) { + pids.push_back(p); + } + } + } + + size_t threshold = sizes(i); + if (threshold < 65536) { + threshold = 65536; + } + threshold /= 100; + + PidStatisticsCollection::iterator pt = pids.begin(); + + for(int line = 0; + (pt != pids.end()) && (line < num_spammers); + ++line, pt = pids.erase(pt)) { + PidStatistics *p = *pt; + + size_t sizes = p->sizes(); + if (sizes < threshold) { + break; + } + + char *name = p->getName(); + pid_t pid = p->getPid(); + if (!name || !*name) { + name = pidToName(pid); + if (name) { + if (*name) { + p->setName(name); + } else { + free(name); + name = NULL; + } + } + } + + if (!header) { + string.appendFormat("\n\nChattiest clients:\n" + "log id %-*s PID[?] name", + spaces_total, "size/total"); + header = true; + } + + size_t sizesTotal = p->sizesTotal(); + + android::String8 sz(""); + sz.appendFormat((sizes != sizesTotal) ? "%zu/%zu" : "%zu", + sizes, sizesTotal); + + android::String8 pd(""); + pd.appendFormat("%u%c", pid, p->pidGone() ? '?' : ' '); + + string.appendFormat("\n%-7s%-*s %-7s%s", + line ? "" : android_log_id_to_name(i), + spaces_total, sz.string(), pd.string(), + name ? name : ""); + } + + pids.clear(); + } + + if (dgram_qlen_statistics) { + const unsigned short spaces_time = 6; + const unsigned long long max_seconds = 100000; + spaces = 0; + string.append("\n\nMinimum time between log events per dgram_qlen:\n"); + for(unsigned short i = 0; dgram_qlen(i); ++i) { + oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s%u", spaces, "", dgram_qlen(i)); + spaces += spaces_time + oldLength - string.length(); + } + string.append("\n"); + spaces = 0; + unsigned short n; + for(unsigned short i = 0; (n = dgram_qlen(i)); ++i) { + unsigned long long duration = minimum(i); + if (duration) { + duration /= n; + if (duration >= (NS_PER_SEC * max_seconds)) { + duration = NS_PER_SEC * (max_seconds - 1); + } + oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s", spaces, ""); + if (duration >= (NS_PER_SEC * 10)) { + string.appendFormat("%llu", + (duration + (NS_PER_SEC / 2)) + / NS_PER_SEC); + } else if (duration >= (NS_PER_SEC / (1000 / 10))) { + string.appendFormat("%llum", + (duration + (NS_PER_SEC / 2 / 1000)) + / (NS_PER_SEC / 1000)); + } else if (duration >= (NS_PER_SEC / (1000000 / 10))) { + string.appendFormat("%lluu", + (duration + (NS_PER_SEC / 2 / 1000000)) + / (NS_PER_SEC / 1000000)); + } else { + string.appendFormat("%llun", duration); + } + spaces -= string.length() - oldLength; + } + spaces += spaces_time; + } + } + + log_id_for_each(i) { + if (!(logMask & (1 << i))) { + continue; + } + + header = false; bool first = true; UidStatisticsCollection::iterator ut; @@ -433,21 +774,22 @@ void LogStatistics::format(char **buf, if (!oneline) { first = true; - } else if (!first && spaces) { + } else if (!first && (spaces > 0)) { string.appendFormat("%*s", spaces, ""); } 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); - string.appendFormat((first) ? "\n%-12s" : "%-12s", + u, p, pp->pidGone() ? '?' : '\0'); + string.appendFormat(first ? "\n%-12s" : "%-12s", intermediate.string()); intermediate.clear(); @@ -458,10 +800,16 @@ void LogStatistics::format(char **buf, size_t els = up->elements(); if (els == elsTotal) { + if (spaces < 0) { + spaces = 0; + } string.appendFormat("%*s=", spaces, ""); spaces = -1; } else if (els) { oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } string.appendFormat("%*s%zu/%zu", spaces, "", up->sizes(), els); spaces -= string.length() - oldLength; } @@ -477,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. @@ -490,7 +838,7 @@ void LogStatistics::format(char **buf, continue; } els = pp->elements(); - bool gone = kill(p, 0); + bool gone = pp->pidGone(); if (gone && (els == 0)) { // ToDo: garbage collection: move this statistical bucket // from its current UID/PID to UID/? (races and @@ -502,13 +850,13 @@ void LogStatistics::format(char **buf, continue; } - if (!first && spaces) { + if (!first && (spaces > 0)) { string.appendFormat("%*s", spaces, ""); } spaces = 0; - intermediate = string.format((gone) ? "%d/%d?" : "%d/%d", u, p); - string.appendFormat((first) ? "\n%-12s" : "%-12s", + intermediate = string.format(gone ? "%d/%d?" : "%d/%d", u, p); + string.appendFormat(first ? "\n%-12s" : "%-12s", intermediate.string()); intermediate.clear(); @@ -517,10 +865,16 @@ void LogStatistics::format(char **buf, spaces += spaces_total + oldLength - string.length(); if (els == elsTotal) { + if (spaces < 0) { + spaces = 0; + } string.appendFormat("%*s=", spaces, ""); spaces = -1; } else if (els) { oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } string.appendFormat("%*s%zu/%zu", spaces, "", pp->sizes(), els); spaces -= string.length() - oldLength; @@ -531,12 +885,12 @@ void LogStatistics::format(char **buf, } if (gone_els) { - if (!first && spaces) { + if (!first && (spaces > 0)) { string.appendFormat("%*s", spaces, ""); } intermediate = string.format("%d/?", u); - string.appendFormat((first) ? "\n%-12s" : "%-12s", + string.appendFormat(first ? "\n%-12s" : "%-12s", intermediate.string()); intermediate.clear(); @@ -553,3 +907,20 @@ void LogStatistics::format(char **buf, *buf = strdup(string.string()); } + +uid_t LogStatistics::pidToUid(pid_t pid) { + log_id_for_each(i) { + LidStatistics &l = id(i); + UidStatisticsCollection::iterator iu; + for (iu = l.begin(); iu != l.end(); ++iu) { + UidStatistics &u = *(*iu); + PidStatisticsCollection::iterator ip; + for (ip = u.begin(); ip != u.end(); ++ip) { + if ((*ip)->getPid() == pid) { + return u.getUid(); + } + } + } + } + return getuid(); // associate this with the logger +} diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h index d44afa2..3733137 100644 --- a/logd/LogStatistics.h +++ b/logd/LogStatistics.h @@ -36,12 +36,20 @@ class PidStatistics { size_t mSizes; size_t mElements; + char *name; + bool mGone; + public: static const pid_t gone = (pid_t) -1; - PidStatistics(pid_t pid); + PidStatistics(pid_t pid, char *name = NULL); + PidStatistics(const PidStatistics ©); + ~PidStatistics(); pid_t getPid() const { return pid; } + bool pidGone(); + char *getName() const { return name; } + void setName(char *name); void add(unsigned short size); bool subtract(unsigned short size); // returns true if stats and PID gone @@ -52,6 +60,9 @@ public: size_t sizesTotal() const { return mSizesTotal; } size_t elementsTotal() const { return mElementsTotal; } + + // helper + static char *pidToName(pid_t pid); }; typedef android::List<PidStatistics *> PidStatisticsCollection; @@ -61,6 +72,9 @@ class UidStatistics { PidStatisticsCollection Pids; + size_t mSizes; + size_t mElements; + public: UidStatistics(uid_t uid); ~UidStatistics(); @@ -72,14 +86,23 @@ 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); + + // helper + static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); } }; typedef android::List<UidStatistics *> UidStatisticsCollection; @@ -96,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; @@ -114,6 +138,11 @@ class LogStatistics { size_t mSizes[LOG_ID_MAX]; size_t mElements[LOG_ID_MAX]; + bool dgram_qlen_statistics; + + static const unsigned short mBuckets[14]; + log_time mMinimum[sizeof(mBuckets) / sizeof(mBuckets[0])]; + public: const log_time start; @@ -121,8 +150,14 @@ public: LidStatistics &id(log_id_t log_id) { return LogIds[log_id]; } + void enableDgramQlenStatistics() { dgram_qlen_statistics = true; } + static unsigned short dgram_qlen(unsigned short bucket); + unsigned long long minimum(unsigned short bucket); + void recordDiff(log_time diff, unsigned short bucket); + 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]; } @@ -147,6 +182,10 @@ public: // *strp = malloc, balance with free void format(char **strp, uid_t uid, unsigned int logMask, log_time oldest); + + // helper + static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); } + uid_t pidToUid(pid_t pid); }; #endif // _LOGD_LOG_STATISTICS_H__ diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp index 8cb015c..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" @@ -46,14 +48,25 @@ LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client, { } void LogTimeEntry::startReader_Locked(void) { + pthread_attr_t attr; + threadRunning = true; - if (pthread_create(&mThread, NULL, LogTimeEntry::threadStart, this)) { - threadRunning = false; - if (mClient) { - mClient->decRef(); + + if (!pthread_attr_init(&attr)) { + if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) { + if (!pthread_create(&mThread, &attr, + LogTimeEntry::threadStart, this)) { + pthread_attr_destroy(&attr); + return; + } } - decRef_Locked(); + pthread_attr_destroy(&attr); + } + threadRunning = false; + if (mClient) { + mClient->decRef(); } + decRef_Locked(); } void LogTimeEntry::threadStop(void *obj) { @@ -96,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 5f8173f..c6c7b23 100644 --- a/logd/LogWhiteBlackList.cpp +++ b/logd/LogWhiteBlackList.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#ifdef USERDEBUG_BUILD - #include <ctype.h> #include <utils/String8.h> @@ -239,5 +237,3 @@ bool PruneList::nice(LogBufferElement *element) { } return false; } - -#endif // USERDEBUG_BUILD diff --git a/logd/README.auditd b/logd/README.auditd new file mode 100644 index 0000000..3f614a3 --- /dev/null +++ b/logd/README.auditd @@ -0,0 +1,17 @@ +Auditd Daemon + +The audit daemon is a simplified version of its desktop +counterpart designed to gather the audit logs from the +audit kernel subsystem. The audit subsystem of the kernel +includes Linux Security Modules (LSM) messages as well. + +To enable the audit subsystem, you must add this to your +kernel config: +CONFIG_AUDIT=y + +To enable a LSM, you must consult that LSM's documentation, the +example below is for SELinux: +CONFIG_SECURITY_SELINUX=y + +This does not include possible dependencies that may need to be +satisfied for that particular LSM. 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/libaudit.c b/logd/libaudit.c new file mode 100644 index 0000000..ca88d1b --- /dev/null +++ b/logd/libaudit.c @@ -0,0 +1,276 @@ +/* + * Copyright 2012, Samsung Telecommunications of America + * 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. + * + * Written by William Roberts <w.roberts@sta.samsung.com> + * + */ + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#define LOG_TAG "libaudit" +#include <log/log.h> + +#include "libaudit.h" + +/** + * Waits for an ack from the kernel + * @param fd + * The netlink socket fd + * @param seq + * The current sequence number were acking on + * @return + * This function returns 0 on success, else -errno. + */ +static int get_ack(int fd, int16_t seq) +{ + int rc; + struct audit_message rep; + + /* Sanity check, this is an internal interface this shouldn't happen */ + if (fd < 0) { + return -EINVAL; + } + + rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK); + if (rc < 0) { + return rc; + } + + if (rep.nlh.nlmsg_type == NLMSG_ERROR) { + audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0); + rc = ((struct nlmsgerr *)rep.data)->error; + if (rc) { + return -rc; + } + } + + if ((int16_t)rep.nlh.nlmsg_seq != seq) { + SLOGW("Expected sequence number between user space and kernel space is out of skew, " + "expected %u got %u", seq, rep.nlh.nlmsg_seq); + } + + return 0; +} + +/** + * + * @param fd + * The netlink socket fd + * @param type + * The type of netlink message + * @param data + * The data to send + * @param size + * The length of the data in bytes + * @return + * This function returns a positive sequence number on success, else -errno. + */ +static int audit_send(int fd, int type, const void *data, size_t size) +{ + int rc; + static int16_t sequence = 0; + struct audit_message req; + struct sockaddr_nl addr; + + memset(&req, 0, sizeof(req)); + memset(&addr, 0, sizeof(addr)); + + /* We always send netlink messaged */ + addr.nl_family = AF_NETLINK; + + /* Set up the netlink headers */ + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_len = NLMSG_SPACE(size); + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + /* + * Check for a valid fd, even though sendto would catch this, its easier + * to always blindly increment the sequence number + */ + if (fd < 0) { + return -EBADF; + } + + /* Ensure the message is not too big */ + if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) { + SLOGE("netlink message is too large"); + return -EINVAL; + } + + /* Only memcpy in the data if it was specified */ + if (size && data) { + memcpy(NLMSG_DATA(&req.nlh), data, size); + } + + /* + * Only increment the sequence number on a guarantee + * you will send it to the kernel. + * + * Also, the sequence is defined as a u32 in the kernel + * struct. Using an int here might not work on 32/64 bit splits. A + * signed 64 bit value can overflow a u32..but a u32 + * might not fit in the response, so we need to use s32. + * Which is still kind of hackish since int could be 16 bits + * in size. The only safe type to use here is a signed 16 + * bit value. + */ + req.nlh.nlmsg_seq = ++sequence; + + /* While failing and its due to interrupts */ + + rc = TEMP_FAILURE_RETRY(sendto(fd, &req, req.nlh.nlmsg_len, 0, + (struct sockaddr*) &addr, sizeof(addr))); + + /* Not all the bytes were sent */ + if (rc < 0) { + rc = -errno; + SLOGE("Error sending data over the netlink socket: %s", strerror(-errno)); + goto out; + } else if ((uint32_t) rc != req.nlh.nlmsg_len) { + rc = -EPROTO; + goto out; + } + + /* We sent all the bytes, get the ack */ + rc = get_ack(fd, sequence); + + /* If the ack failed, return the error, else return the sequence number */ + rc = (rc == 0) ? (int) sequence : rc; + +out: + /* Don't let sequence roll to negative */ + if (sequence < 0) { + SLOGW("Auditd to Kernel sequence number has rolled over"); + sequence = 0; + } + + return rc; +} + +int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode) +{ + int rc; + struct audit_message rep; + struct audit_status status; + + memset(&status, 0, sizeof(status)); + + /* + * In order to set the auditd PID we send an audit message over the netlink + * socket with the pid field of the status struct set to our current pid, + * and the the mask set to AUDIT_STATUS_PID + */ + status.pid = pid; + status.mask = AUDIT_STATUS_PID; + + /* Let the kernel know this pid will be registering for audit events */ + rc = audit_send(fd, AUDIT_SET, &status, sizeof(status)); + if (rc < 0) { + SLOGE("Could net set pid for audit events, error: %s", strerror(-rc)); + return rc; + } + + /* + * In a request where we need to wait for a response, wait for the message + * and discard it. This message confirms and sync's us with the kernel. + * This daemon is now registered as the audit logger. Only wait if the + * wmode is != WAIT_NO + */ + if (wmode != WAIT_NO) { + /* TODO + * If the daemon dies and restarts the message didn't come back, + * so I went to non-blocking and it seemed to fix the bug. + * Need to investigate further. + */ + audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0); + } + + return 0; +} + +int audit_open() +{ + return socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT); +} + +int audit_get_reply(int fd, struct audit_message *rep, reply_t block, int peek) +{ + ssize_t len; + int flags; + int rc = 0; + + struct sockaddr_nl nladdr; + socklen_t nladdrlen = sizeof(nladdr); + + if (fd < 0) { + return -EBADF; + } + + /* Set up the flags for recv from */ + flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0; + flags |= peek; + + /* + * Get the data from the netlink socket but on error we need to be carefull, + * the interface shows that EINTR can never be returned, other errors, + * however, can be returned. + */ + len = TEMP_FAILURE_RETRY(recvfrom(fd, rep, sizeof(*rep), flags, + (struct sockaddr*) &nladdr, &nladdrlen)); + + /* + * EAGAIN should be re-tried until success or another error manifests. + */ + if (len < 0) { + rc = -errno; + if (block == GET_REPLY_NONBLOCKING && rc == -EAGAIN) { + /* If request is non blocking and errno is EAGAIN, just return 0 */ + return 0; + } + SLOGE("Error receiving from netlink socket, error: %s", strerror(-rc)); + return rc; + } + + if (nladdrlen != sizeof(nladdr)) { + SLOGE("Protocol fault, error: %s", strerror(EPROTO)); + return -EPROTO; + } + + /* Make sure the netlink message was not spoof'd */ + if (nladdr.nl_pid) { + SLOGE("Invalid netlink pid received, expected 0 got: %d", nladdr.nl_pid); + return -EINVAL; + } + + /* Check if the reply from the kernel was ok */ + if (!NLMSG_OK(&rep->nlh, (size_t)len)) { + rc = (len == sizeof(*rep)) ? -EFBIG : -EBADE; + SLOGE("Bad kernel response %s", strerror(-rc)); + } + + return rc; +} + +void audit_close(int fd) +{ + int rc = close(fd); + if (rc < 0) { + SLOGE("Attempting to close invalid fd %d, error: %s", fd, strerror(errno)); + } + return; +} diff --git a/logd/libaudit.h b/logd/libaudit.h new file mode 100644 index 0000000..cb114f9 --- /dev/null +++ b/logd/libaudit.h @@ -0,0 +1,104 @@ +/* + * Copyright 2012, Samsung Telecommunications of America + * 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. + * + * Written by William Roberts <w.roberts@sta.samsung.com> + */ + +#ifndef _LIBAUDIT_H_ +#define _LIBAUDIT_H_ + +#include <stdint.h> +#include <sys/cdefs.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <linux/netlink.h> +#include <linux/audit.h> + +__BEGIN_DECLS + +#define MAX_AUDIT_MESSAGE_LENGTH 8970 + +typedef enum { + GET_REPLY_BLOCKING=0, + GET_REPLY_NONBLOCKING +} reply_t; + +typedef enum { + WAIT_NO, + WAIT_YES +} rep_wait_t; + +/* type == AUDIT_SIGNAL_INFO */ +struct audit_sig_info { + uid_t uid; + pid_t pid; + char ctx[0]; +}; + +struct audit_message { + struct nlmsghdr nlh; + char data[MAX_AUDIT_MESSAGE_LENGTH]; +}; + +/** + * Opens a connection to the Audit netlink socket + * @return + * A valid fd on success or < 0 on error with errno set. + * Returns the same errors as man 2 socket. + */ +extern int audit_open(void); + +/** + * Closes the fd returned from audit_open() + * @param fd + * The fd to close + */ +extern void audit_close(int fd); + +/** + * + * @param fd + * The fd returned by a call to audit_open() + * @param rep + * The response struct to store the response in. + * @param block + * Whether or not to block on IO + * @param peek + * Whether or not we are to remove the message from + * the queue when we do a read on the netlink socket. + * @return + * This function returns 0 on success, else -errno. + */ +extern int audit_get_reply(int fd, struct audit_message *rep, reply_t block, + int peek); + +/** + * Sets a pid to recieve audit netlink events from the kernel + * @param fd + * The fd returned by a call to audit_open() + * @param pid + * The pid whom to set as the reciever of audit messages + * @param wmode + * Whether or not to block on the underlying socket io calls. + * @return + * This function returns 0 on success, -errno on error. + */ +extern int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode); + +__END_DECLS + +#endif diff --git a/logd/main.cpp b/logd/main.cpp index 6216b95..ece5a3a 100644 --- a/logd/main.cpp +++ b/logd/main.cpp @@ -24,13 +24,46 @@ #include <sys/capability.h> #include <sys/stat.h> #include <sys/types.h> +#include <unistd.h> #include <linux/prctl.h> +#include <cutils/properties.h> + #include "private/android_filesystem_config.h" #include "CommandListener.h" #include "LogBuffer.h" #include "LogListener.h" +#include "LogAudit.h" + +// +// The service is designed to be run by init, it does not respond well +// to starting up manually. When starting up manually the sockets will +// fail to open typically for one of the following reasons: +// EADDRINUSE if logger is running. +// EACCESS if started without precautions (below) +// +// Here is a cookbook procedure for starting up logd manually assuming +// init is out of the way, pedantically all permissions and selinux +// security is put back in place: +// +// setenforce 0 +// rm /dev/socket/logd* +// chmod 777 /dev/socket +// # here is where you would attach the debugger or valgrind for example +// runcon u:r:logd:s0 /system/bin/logd </dev/null >/dev/null 2>&1 & +// sleep 1 +// chmod 755 /dev/socket +// chown logd.logd /dev/socket/logd* +// restorecon /dev/socket/logd* +// setenforce 1 +// +// If minimalism prevails, typical for debugging and security is not a concern: +// +// setenforce 0 +// chmod 777 /dev/socket +// logd +// static int drop_privs() { struct sched_param param; @@ -60,7 +93,10 @@ static int drop_privs() { capheader.pid = 0; capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG); - capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG); + capdata[CAP_TO_INDEX(CAP_AUDIT_CONTROL)].permitted |= CAP_TO_MASK(CAP_AUDIT_CONTROL); + + capdata[0].effective = capdata[0].permitted; + capdata[1].effective = capdata[1].permitted; capdata[0].inheritable = 0; capdata[1].inheritable = 0; @@ -71,12 +107,34 @@ 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; + if (auditd && property_get_bool("logd.auditd.dmesg", true)) { + fdDmesg = open("/dev/kmsg", O_WRONLY); + } + if (drop_privs() != 0) { return -1; } @@ -92,6 +150,10 @@ int main() { LogBuffer *logBuf = new LogBuffer(times); + if (property_get_bool("logd.statistics.dgram_qlen", false)) { + logBuf->enableDgramQlenStatistics(); + } + // LogReader listens on /dev/socket/logdr. When a client // connects, log entries in the LogBuffer are written to the client. @@ -118,6 +180,19 @@ int main() { exit(1); } + // LogAudit listens on NETLINK_AUDIT socket for selinux + // initiated log messages. New log entries are added to LogBuffer + // and LogReader is notified to send updates to connected clients. + + 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(); exit(0); } diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk new file mode 100644 index 0000000..123e317 --- /dev/null +++ b/logd/tests/Android.mk @@ -0,0 +1,53 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) + +# ----------------------------------------------------------------------------- +# Benchmarks. (see ../../liblog/tests) +# ----------------------------------------------------------------------------- + +test_module_prefix := logd- +test_tags := tests + +# ----------------------------------------------------------------------------- +# Unit tests. +# ----------------------------------------------------------------------------- + +test_c_flags := \ + -fstack-protector-all \ + -g \ + -Wall -Wextra \ + -Werror \ + -fno-builtin \ + +ifeq ($(TARGET_USES_LOGD),true) +test_c_flags += -DTARGET_USES_LOGD=1 +endif + +test_src_files := \ + logd_test.cpp + +# Build tests for the logger. Run with: +# adb shell /data/nativetest/logd-unit-tests/logd-unit-tests +include $(CLEAR_VARS) +LOCAL_MODULE := $(test_module_prefix)unit-tests +LOCAL_MODULE_TAGS := $(test_tags) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += $(test_c_flags) +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_SRC_FILES := $(test_src_files) +include $(BUILD_NATIVE_TEST) diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp new file mode 100644 index 0000000..5b51b1f --- /dev/null +++ b/logd/tests/logd_test.cpp @@ -0,0 +1,677 @@ +/* + * 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 <poll.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> + +#include <gtest/gtest.h> + +#include "cutils/sockets.h" +#include "log/log.h" +#include "log/logger.h" + +#define __unused __attribute__((__unused__)) + +/* + * returns statistics + */ +static void my_android_logger_get_statistics(char *buf, size_t len) +{ + snprintf(buf, len, "getStatistics 0 1 2 3 4"); + int sock = socket_local_client("logd", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (sock >= 0) { + if (write(sock, buf, strlen(buf) + 1) > 0) { + ssize_t ret; + while ((ret = read(sock, buf, len)) > 0) { + if ((size_t)ret == len) { + break; + } + len -= ret; + buf += ret; + + struct pollfd p = { + .fd = sock, + .events = POLLIN, + .revents = 0 + }; + + ret = poll(&p, 1, 20); + if ((ret <= 0) || !(p.revents & POLLIN)) { + break; + } + } + } + close(sock); + } +} + +static void alloc_statistics(char **buffer, size_t *length) +{ + size_t len = 8192; + char *buf; + + for(int retry = 32; (retry >= 0); delete [] buf, --retry) { + buf = new char [len]; + my_android_logger_get_statistics(buf, len); + + buf[len-1] = '\0'; + size_t ret = atol(buf) + 1; + if (ret < 4) { + delete [] buf; + buf = NULL; + break; + } + bool check = ret <= len; + len = ret; + if (check) { + break; + } + len += len / 8; // allow for some slop + } + *buffer = buf; + *length = len; +} + +static char *find_benchmark_spam(char *cp) +{ + // liblog_benchmarks has been run designed to SPAM. The signature of + // a noisiest UID statistics is one of the following: + // + // 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 93432/1012 + // + // basically if we see a *large* number of 0/????? entries + unsigned long value; + do { + char *benchmark = strstr(cp, " 0/"); + char *benchmark_newline = strstr(cp, "\n0/"); + if (!benchmark) { + benchmark = benchmark_newline; + } + if (benchmark_newline && (benchmark > benchmark_newline)) { + benchmark = benchmark_newline; + } + cp = benchmark; + if (!cp) { + break; + } + cp += 3; + while (isdigit(*cp) || (*cp == 'g') || (*cp == 'o') || (*cp == 'n')) { + ++cp; + } + value = 0; + // ###? or gone + if ((*cp == '?') || (*cp == 'e')) { + while (*++cp == ' '); + while (isdigit(*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; +} + +TEST(logd, statistics) { + size_t len; + char *buf; + + alloc_statistics(&buf, &len); + +#ifdef TARGET_USES_LOGD + ASSERT_TRUE(NULL != buf); +#else + if (!buf) { + return; + } +#endif + + // remove trailing FF + char *cp = buf + len - 1; + *cp = '\0'; + bool truncated = *--cp != '\f'; + if (!truncated) { + *cp = '\0'; + } + + // squash out the byte count + cp = buf; + if (!truncated) { + while (isdigit(*cp) || (*cp == '\n')) { + ++cp; + } + } + + fprintf(stderr, "%s", cp); + + EXPECT_LT((size_t)64, strlen(cp)); + + EXPECT_EQ(0, truncated); + +#ifdef TARGET_USES_LOGD + char *main_logs = strstr(cp, "\nmain:"); + EXPECT_TRUE(NULL != main_logs); + + char *radio_logs = strstr(cp, "\nradio:"); + EXPECT_TRUE(NULL != radio_logs); + + char *system_logs = strstr(cp, "\nsystem:"); + EXPECT_TRUE(NULL != system_logs); + + char *events_logs = strstr(cp, "\nevents:"); + EXPECT_TRUE(NULL != events_logs); +#endif + + // Parse timing stats + + cp = strstr(cp, "Minimum time between log events per dgram_qlen:"); + + if (cp) { + while (*cp && (*cp != '\n')) { + ++cp; + } + if (*cp == '\n') { + ++cp; + } + + char *list_of_spans = cp; + EXPECT_NE('\0', *list_of_spans); + + unsigned short number_of_buckets = 0; + unsigned short *dgram_qlen = NULL; + unsigned short bucket = 0; + while (*cp && (*cp != '\n')) { + bucket = 0; + while (isdigit(*cp)) { + bucket = bucket * 10 + *cp - '0'; + ++cp; + } + while (*cp == ' ') { + ++cp; + } + if (!bucket) { + break; + } + unsigned short *new_dgram_qlen = new unsigned short[number_of_buckets + 1]; + EXPECT_TRUE(new_dgram_qlen != NULL); + if (dgram_qlen) { + memcpy(new_dgram_qlen, dgram_qlen, sizeof(*dgram_qlen) * number_of_buckets); + delete [] dgram_qlen; + } + + dgram_qlen = new_dgram_qlen; + dgram_qlen[number_of_buckets++] = bucket; + } + + char *end_of_spans = cp; + EXPECT_NE('\0', *end_of_spans); + + EXPECT_LT(5, number_of_buckets); + + unsigned long long *times = new unsigned long long [number_of_buckets]; + ASSERT_TRUE(times != NULL); + + memset(times, 0, sizeof(*times) * number_of_buckets); + + while (*cp == '\n') { + ++cp; + } + + unsigned short number_of_values = 0; + unsigned long long value; + while (*cp && (*cp != '\n')) { + EXPECT_GE(number_of_buckets, number_of_values); + + value = 0; + while (isdigit(*cp)) { + value = value * 10ULL + *cp - '0'; + ++cp; + } + + switch(*cp) { + case ' ': + case '\n': + value *= 1000ULL; + /* FALLTHRU */ + case 'm': + value *= 1000ULL; + /* FALLTHRU */ + case 'u': + value *= 1000ULL; + /* FALLTHRU */ + case 'n': + default: + break; + } + while (*++cp == ' '); + + if (!value) { + break; + } + + times[number_of_values] = value; + ++number_of_values; + } + +#ifdef TARGET_USES_LOGD + EXPECT_EQ(number_of_values, number_of_buckets); +#endif + + FILE *fp; + ASSERT_TRUE(NULL != (fp = fopen("/proc/sys/net/unix/max_dgram_qlen", "r"))); + + unsigned max_dgram_qlen = 0; + fscanf(fp, "%u", &max_dgram_qlen); + + fclose(fp); + + // Find launch point + unsigned short launch = 0; + unsigned long long total = 0; + do { + total += times[launch]; + } while (((++launch < number_of_buckets) + && ((total / launch) >= (times[launch] / 8ULL))) + || (launch == 1)); // too soon + + bool failure = number_of_buckets <= launch; + if (!failure) { + unsigned short l = launch; + if (l >= number_of_buckets) { + l = number_of_buckets - 1; + } + failure = max_dgram_qlen < dgram_qlen[l]; + } + + // We can get failure if at any time liblog_benchmarks has been run + // because designed to overload /proc/sys/net/unix/max_dgram_qlen even + // at excessive values like 20000. It does so to measure the raw processing + // performance of logd. + if (failure) { + cp = find_benchmark_spam(cp); + } + + if (cp) { + // Fake a failure, but without the failure code + if (number_of_buckets <= launch) { + printf ("Expected: number_of_buckets > launch, actual: %u vs %u\n", + number_of_buckets, launch); + } + if (launch >= number_of_buckets) { + launch = number_of_buckets - 1; + } + if (max_dgram_qlen < dgram_qlen[launch]) { + printf ("Expected: max_dgram_qlen >= dgram_qlen[%d]," + " actual: %u vs %u\n", + launch, max_dgram_qlen, dgram_qlen[launch]); + } + } else +#ifndef TARGET_USES_LOGD + if (total) +#endif + { + EXPECT_GT(number_of_buckets, launch); + if (launch >= number_of_buckets) { + launch = number_of_buckets - 1; + } + EXPECT_GE(max_dgram_qlen, dgram_qlen[launch]); + } + + delete [] dgram_qlen; + delete [] times; + } + delete [] buf; +} + +static void caught_signal(int signum __unused) { } + +static void dump_log_msg(const char *prefix, + log_msg *msg, unsigned int version, int lid) { + switch(msg->entry.hdr_size) { + case 0: + version = 1; + break; + + case sizeof(msg->entry_v2): + if (version == 0) { + version = 2; + } + break; + } + + fprintf(stderr, "%s: v%u[%u] ", prefix, version, msg->len()); + if (version != 1) { + fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size); + } + fprintf(stderr, "pid=%u tid=%u %u.%09u ", + msg->entry.pid, msg->entry.tid, msg->entry.sec, msg->entry.nsec); + switch(version) { + case 1: + break; + case 2: + fprintf(stderr, "euid=%u ", msg->entry_v2.euid); + break; + case 3: + default: + lid = msg->entry.lid; + break; + } + + switch(lid) { + case 0: + fprintf(stderr, "lid=main "); + break; + case 1: + fprintf(stderr, "lid=radio "); + break; + case 2: + fprintf(stderr, "lid=events "); + break; + case 3: + fprintf(stderr, "lid=system "); + break; + default: + if (lid >= 0) { + fprintf(stderr, "lid=%d ", lid); + } + } + + unsigned int len = msg->entry.len; + fprintf(stderr, "msg[%u]={", len); + unsigned char *cp = reinterpret_cast<unsigned char *>(msg->msg()); + while(len) { + unsigned char *p = cp; + while (*p && (((' ' <= *p) && (*p < 0x7F)) || (*p == '\n'))) { + ++p; + } + if (((p - cp) > 3) && !*p && ((unsigned int)(p - cp) < len)) { + fprintf(stderr, "\""); + while (*cp) { + fprintf(stderr, (*cp != '\n') ? "%c" : "\\n", *cp); + ++cp; + --len; + } + fprintf(stderr, "\""); + } else { + fprintf(stderr, "%02x", *cp); + } + ++cp; + if (--len) { + fprintf(stderr, ", "); + } + } + fprintf(stderr, "}\n"); +} + +TEST(logd, both) { + log_msg msg; + + // check if we can read any logs from logd + bool user_logger_available = false; + bool user_logger_content = false; + + int fd = socket_local_client("logdr", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_SEQPACKET); + if (fd >= 0) { + struct sigaction ignore, old_sigaction; + memset(&ignore, 0, sizeof(ignore)); + ignore.sa_handler = caught_signal; + sigemptyset(&ignore.sa_mask); + sigaction(SIGALRM, &ignore, &old_sigaction); + unsigned int old_alarm = alarm(10); + + static const char ask[] = "dumpAndClose lids=0,1,2,3"; + user_logger_available = write(fd, ask, sizeof(ask)) == sizeof(ask); + + user_logger_content = recv(fd, msg.buf, sizeof(msg), 0) > 0; + + if (user_logger_content) { + dump_log_msg("user", &msg, 3, -1); + } + + alarm(old_alarm); + sigaction(SIGALRM, &old_sigaction, NULL); + + close(fd); + } + + // check if we can read any logs from kernel logger + bool kernel_logger_available = false; + bool kernel_logger_content = false; + + static const char *loggers[] = { + "/dev/log/main", "/dev/log_main", + "/dev/log/radio", "/dev/log_radio", + "/dev/log/events", "/dev/log_events", + "/dev/log/system", "/dev/log_system", + }; + + for (unsigned int i = 0; i < (sizeof(loggers) / sizeof(loggers[0])); ++i) { + fd = open(loggers[i], O_RDONLY); + if (fd < 0) { + continue; + } + kernel_logger_available = true; + fcntl(fd, F_SETFL, O_RDONLY | O_NONBLOCK); + int result = TEMP_FAILURE_RETRY(read(fd, msg.buf, sizeof(msg))); + if (result > 0) { + kernel_logger_content = true; + dump_log_msg("kernel", &msg, 0, i / 2); + } + close(fd); + } + + static const char yes[] = "\xE2\x9C\x93"; + static const char no[] = "\xE2\x9c\x98"; + fprintf(stderr, + "LOGGER Available Content\n" + "user %-13s%s\n" + "kernel %-13s%s\n" + " status %-11s%s\n", + (user_logger_available) ? yes : no, + (user_logger_content) ? yes : no, + (kernel_logger_available) ? yes : no, + (kernel_logger_content) ? yes : no, + (user_logger_available && kernel_logger_available) ? "WARNING" : "ok", + (user_logger_content && kernel_logger_content) ? "ERROR" : "ok"); + + if (user_logger_available && kernel_logger_available) { + printf("WARNING: kernel & user logger; both consuming resources!!!\n"); + } + + EXPECT_EQ(0, user_logger_content && kernel_logger_content); + EXPECT_EQ(0, !user_logger_content && !kernel_logger_content); +} + +// BAD ROBOT +// Benchmark threshold are generally considered bad form unless there is +// is some human love applied to the continued maintenance and whether the +// thresholds are tuned on a per-target basis. Here we check if the values +// are more than double what is expected. Doubling will not prevent failure +// on busy or low-end systems that could have a tendency to stretch values. +// +// The primary goal of this test is to simulate a spammy app (benchmark +// being the worst) and check to make sure the logger can deal with it +// appropriately by checking all the statistics are in an expected range. +// +TEST(logd, benchmark) { + size_t len; + char *buf; + + alloc_statistics(&buf, &len); + bool benchmark_already_run = buf && find_benchmark_spam(buf); + delete [] buf; + + if (benchmark_already_run) { + fprintf(stderr, "WARNING: spam already present and too much history\n" + " false OK for prune by worst UID check\n"); + } + + FILE *fp; + + // Introduce some extreme spam for the worst UID filter + ASSERT_TRUE(NULL != (fp = popen( + "/data/nativetest/liblog-benchmarks/liblog-benchmarks", + "r"))); + + char buffer[5120]; + + static const char *benchmarks[] = { + "BM_log_maximum_retry ", + "BM_log_maximum ", + "BM_clock_overhead ", + "BM_log_overhead ", + "BM_log_latency ", + "BM_log_delay " + }; + static const unsigned int log_maximum_retry = 0; + static const unsigned int log_maximum = 1; + static const unsigned int clock_overhead = 2; + static const unsigned int log_overhead = 3; + static const unsigned int log_latency = 4; + static const unsigned int log_delay = 5; + + unsigned long ns[sizeof(benchmarks) / sizeof(benchmarks[0])]; + + memset(ns, 0, sizeof(ns)); + + while (fgets(buffer, sizeof(buffer), fp)) { + for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) { + if (strncmp(benchmarks[i], buffer, strlen(benchmarks[i]))) { + continue; + } + sscanf(buffer, "%*s %lu %lu", &ns[i], &ns[i]); + fprintf(stderr, "%-22s%8lu\n", benchmarks[i], ns[i]); + } + } + int ret = pclose(fp); + + if (!WIFEXITED(ret) || (WEXITSTATUS(ret) == 127)) { + fprintf(stderr, + "WARNING: " + "/data/nativetest/liblog-benchmarks/liblog-benchmarks missing\n" + " can not perform test\n"); + return; + } + +#ifdef TARGET_USES_LOGD + EXPECT_GE(100000UL, ns[log_maximum_retry]); // 42777 user +#else + EXPECT_GE(10000UL, ns[log_maximum_retry]); // 5636 kernel +#endif + +#ifdef TARGET_USES_LOGD + EXPECT_GE(25000UL, ns[log_maximum]); // 14055 user +#else + EXPECT_GE(10000UL, ns[log_maximum]); // 5637 kernel +#endif + + EXPECT_GE(4000UL, ns[clock_overhead]); // 2008 + +#ifdef TARGET_USES_LOGD + EXPECT_GE(250000UL, ns[log_overhead]); // 113219 user +#else + EXPECT_GE(100000UL, ns[log_overhead]); // 50945 kernel +#endif + +#ifdef TARGET_USES_LOGD + EXPECT_GE(7500UL, ns[log_latency]); // 3718 user space +#else + EXPECT_GE(500000UL, ns[log_latency]); // 254200 kernel +#endif + +#ifdef TARGET_USES_LOGD + EXPECT_GE(20000000UL, ns[log_delay]); // 9542541 user +#else + EXPECT_GE(55000UL, ns[log_delay]); // 27341 kernel +#endif + + for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) { + EXPECT_NE(0UL, ns[i]); + } + + alloc_statistics(&buf, &len); + +#ifdef TARGET_USES_LOGD + bool collected_statistics = !!buf; + EXPECT_EQ(true, collected_statistics); +#else + if (!buf) { + return; + } +#endif + + ASSERT_TRUE(NULL != buf); + + 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/logwrapper/Android.mk b/logwrapper/Android.mk index 917bf37..61b4659 100644 --- a/logwrapper/Android.mk +++ b/logwrapper/Android.mk @@ -11,6 +11,7 @@ LOCAL_SRC_FILES := logwrap.c LOCAL_SHARED_LIBRARIES := libcutils liblog LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) # ======================================================== @@ -22,6 +23,7 @@ LOCAL_SHARED_LIBRARIES := libcutils liblog LOCAL_WHOLE_STATIC_LIBRARIES := liblogwrap LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Werror include $(BUILD_SHARED_LIBRARY) # ======================================================== @@ -31,4 +33,5 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= logwrapper.c LOCAL_MODULE := logwrapper LOCAL_STATIC_LIBRARIES := liblog liblogwrap libcutils +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c index 4ca1db4..d47c9b5 100644 --- a/logwrapper/logwrap.c +++ b/logwrapper/logwrap.c @@ -104,8 +104,6 @@ static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int line static int add_line_to_linear_buf(struct beginning_buf *b_buf, char *line, ssize_t line_len) { - size_t new_len; - char *new_buf; int full = 0; if ((line_len + b_buf->used_len) > b_buf->buf_size) { @@ -124,7 +122,6 @@ static void add_line_to_circular_buf(struct ending_buf *e_buf, { ssize_t free_len; ssize_t needed_space; - char *new_buf; int cnt; if (e_buf->buf == NULL) { @@ -192,7 +189,6 @@ static void print_buf_lines(struct log_info *log_info, char *buf, int buf_size) { char *line_start; char c; - int line_len; int i; line_start = buf; diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c index d598f03..a7daccc 100644 --- a/mkbootimg/mkbootimg.c +++ b/mkbootimg/mkbootimg.c @@ -263,7 +263,7 @@ int main(int argc, char **argv) if(second_data) { if(write(fd, second_data, hdr.second_size) != hdr.second_size) goto fail; - if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail; + if(write_padding(fd, pagesize, hdr.second_size)) goto fail; } return 0; diff --git a/rootdir/init.rc b/rootdir/init.rc index 23a517a..0ef097c 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -7,6 +7,7 @@ import /init.environ.rc import /init.usb.rc import /init.${ro.hardware}.rc +import /init.${ro.zygote}.rc import /init.trace.rc on early-init @@ -176,6 +177,9 @@ on post-fs chown system log /proc/last_kmsg chmod 0440 /proc/last_kmsg + # make the selinux kernel policy world-readable + chmod 0444 /sys/fs/selinux/policy + # create the lost+found directories, so as to enforce our permissions mkdir /cache/lost+found 0770 root root @@ -368,13 +372,13 @@ on boot setprop net.tcp.buffersize.wifi 524288,1048576,2097152,262144,524288,1048576 setprop net.tcp.buffersize.ethernet 524288,1048576,3145728,524288,1048576,2097152 setprop net.tcp.buffersize.lte 524288,1048576,2097152,262144,524288,1048576 - setprop net.tcp.buffersize.umts 4094,87380,110208,4096,16384,110208 - setprop net.tcp.buffersize.hspa 4094,87380,262144,4096,16384,262144 - setprop net.tcp.buffersize.hsupa 4094,87380,262144,4096,16384,262144 - setprop net.tcp.buffersize.hsdpa 4094,87380,262144,4096,16384,262144 - setprop net.tcp.buffersize.hspap 4094,87380,1220608,4096,16384,1220608 - setprop net.tcp.buffersize.edge 4093,26280,35040,4096,16384,35040 - setprop net.tcp.buffersize.gprs 4092,8760,11680,4096,8760,11680 + setprop net.tcp.buffersize.umts 58254,349525,1048576,58254,349525,1048576 + setprop net.tcp.buffersize.hspa 40778,244668,734003,16777,100663,301990 + setprop net.tcp.buffersize.hsupa 40778,244668,734003,16777,100663,301990 + setprop net.tcp.buffersize.hsdpa 61167,367002,1101005,8738,52429,262114 + setprop net.tcp.buffersize.hspap 122334,734003,2202010,32040,192239,576717 + setprop net.tcp.buffersize.edge 4093,26280,70800,4096,16384,70800 + setprop net.tcp.buffersize.gprs 4092,8760,48000,4096,8760,48000 setprop net.tcp.buffersize.evdo 4094,87380,262144,4096,16384,262144 class_start core @@ -500,14 +504,6 @@ service surfaceflinger /system/bin/surfaceflinger group graphics drmrpc onrestart restart zygote -service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server - class main - socket zygote stream 660 root system - onrestart write /sys/android_power/request_state wake - onrestart write /sys/power/state on - onrestart restart media - onrestart restart netd - service drm /system/bin/drmserver class main user drm diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc new file mode 100644 index 0000000..75961e6 --- /dev/null +++ b/rootdir/init.zygote32.rc @@ -0,0 +1,8 @@ +service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server + class main + socket zygote stream 660 root system + onrestart write /sys/android_power/request_state wake + onrestart write /sys/power/state on + onrestart restart media + onrestart restart netd + diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc new file mode 100644 index 0000000..3d60a31 --- /dev/null +++ b/rootdir/init.zygote32_64.rc @@ -0,0 +1,12 @@ +service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote + class main + socket zygote stream 660 root system + onrestart write /sys/android_power/request_state wake + onrestart write /sys/power/state on + onrestart restart media + onrestart restart netd + +service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary + class main + socket zygote_secondary stream 660 root system + onrestart restart zygote diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc new file mode 100644 index 0000000..afb6d63 --- /dev/null +++ b/rootdir/init.zygote64.rc @@ -0,0 +1,8 @@ +service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server + class main + socket zygote stream 660 root system + onrestart write /sys/android_power/request_state wake + onrestart write /sys/power/state on + onrestart restart media + onrestart restart netd + 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 diff --git a/toolbox/Android.mk b/toolbox/Android.mk index 3deb3e7..5383b83 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -95,6 +95,7 @@ LOCAL_SRC_FILES := \ LOCAL_C_INCLUDES := bionic/libc/bionic LOCAL_CFLAGS += \ + -std=gnu99 \ -Wno-unused-parameter \ -include bsd-compatibility.h \ diff --git a/toolbox/date.c b/toolbox/date.c index d6c9052..aa3b72e 100644 --- a/toolbox/date.c +++ b/toolbox/date.c @@ -1,10 +1,13 @@ +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> #include <string.h> -#include <errno.h> #include <time.h> +#include <unistd.h> + #include <linux/android_alarm.h> #include <linux/rtc.h> #include <sys/ioctl.h> @@ -108,6 +111,33 @@ static void settime(char *s) { settime_rtc_tm(&tm); } +static char *parse_time(const char *str, struct timeval *ts) { + char *s; + long fs = 0; /* fractional seconds */ + + ts->tv_sec = strtoumax(str, &s, 10); + + if (*s == '.') { + s++; + int count = 0; + + /* read up to 6 digits (microseconds) */ + while (*s && isdigit(*s)) { + if (++count < 7) { + fs = fs*10 + (*s - '0'); + } + s++; + } + + for (; count < 6; count++) { + fs *= 10; + } + } + + ts->tv_usec = fs; + return s; +} + int date_main(int argc, char *argv[]) { int c; @@ -181,7 +211,7 @@ int date_main(int argc, char *argv[]) //strptime(argv[optind], NULL, &tm); //tv.tv_sec = mktime(&tm); //tv.tv_usec = 0; - strtotimeval(argv[optind], &tv); + parse_time(argv[optind], &tv); printf("time %s -> %lu.%lu\n", argv[optind], tv.tv_sec, tv.tv_usec); res = settime_alarm_timeval(&tv); if (res < 0) diff --git a/toolbox/syren.c b/toolbox/syren.c index 06e329e..47c2460 100644 --- a/toolbox/syren.c +++ b/toolbox/syren.c @@ -123,7 +123,11 @@ syren_main(int argc, char **argv) r = find_reg(argv[2]); if (r == NULL) { - strcpy(name, argv[2]); + if(strlen(argv[2]) >= sizeof(name)){ + fprintf(stderr, "REGNAME too long\n"); + return 0; + } + strlcpy(name, argv[2], sizeof(name)); char *addr_str = strchr(argv[2], ':'); if (addr_str == NULL) return usage(); @@ -131,7 +135,7 @@ syren_main(int argc, char **argv) sio.page = strtoul(argv[2], 0, 0); sio.addr = strtoul(addr_str, 0, 0); } else { - strcpy(name, r->name); + strlcpy(name, r->name, sizeof(name)); sio.page = r->page; sio.addr = r->addr; } diff --git a/toolbox/top.c b/toolbox/top.c index 7642522..7382f1f 100644 --- a/toolbox/top.c +++ b/toolbox/top.c @@ -9,7 +9,7 @@ * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the + * the documentation and/or other materials provided with the * distribution. * * Neither the name of Google, Inc. nor the names of its contributors * may be used to endorse or promote products derived from this @@ -22,7 +22,7 @@ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF @@ -108,16 +108,20 @@ static int proc_thr_cmp(const void *a, const void *b); static int numcmp(long long a, long long b); static void usage(char *cmd); -int top_main(int argc, char *argv[]) { - int i; +static void exit_top(int signal) { + exit(EXIT_FAILURE); +} +int top_main(int argc, char *argv[]) { num_used_procs = num_free_procs = 0; + signal(SIGPIPE, exit_top); + max_procs = 0; delay = 3; iterations = -1; proc_cmp = &proc_cpu_cmp; - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { if (!strcmp(argv[i], "-m")) { if (i + 1 >= argc) { fprintf(stderr, "Option -m expects an argument.\n"); @@ -249,9 +253,9 @@ static void read_procs(void) { continue; pid = atoi(pid_dir->d_name); - + struct proc_info cur_proc; - + if (!threads) { proc = alloc_proc(); @@ -275,7 +279,7 @@ static void read_procs(void) { sprintf(filename, "/proc/%d/status", pid); read_status(filename, &cur_proc); - + proc = NULL; } @@ -310,7 +314,7 @@ static void read_procs(void) { } closedir(task_dir); - + if (!threads) add_proc(proc_num++, proc); } @@ -339,7 +343,7 @@ static int read_stat(char *filename, struct proc_info *proc) { *open_paren = *close_paren = '\0'; strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN); proc->tname[THREAD_NAME_LEN-1] = 0; - + /* Scan rest of string. */ sscanf(close_paren + 1, " %c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " "%lu %lu %*d %*d %*d %*d %*d %*d %*d %lu %ld " @@ -452,7 +456,7 @@ static void print_procs(void) { new_cpu.sirqtime - old_cpu.sirqtime, total_delta_time); printf("\n"); - if (!threads) + if (!threads) printf("%5s %2s %4s %1s %5s %7s %7s %3s %-8s %s\n", "PID", "PR", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "UID", "Name"); else printf("%5s %5s %2s %4s %1s %7s %7s %3s %-8s %-15s %s\n", "PID", "TID", "PR", "CPU%", "S", "VSS", "RSS", "PCY", "UID", "Thread", "Proc"); @@ -476,7 +480,7 @@ static void print_procs(void) { snprintf(group_buf, 20, "%d", proc->gid); group_str = group_buf; } - if (!threads) + if (!threads) printf("%5d %2d %3ld%% %c %5d %6ldK %6ldK %3s %-8.8s %s\n", proc->pid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads, proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->name[0] != 0 ? proc->name : proc->tname); else |