diff options
-rw-r--r-- | charger/charger.c | 44 | ||||
-rw-r--r-- | debuggerd/arm/machine.c | 70 | ||||
-rw-r--r-- | debuggerd/debuggerd.c | 130 | ||||
-rw-r--r-- | debuggerd/utility.c | 18 | ||||
-rw-r--r-- | debuggerd/utility.h | 4 |
5 files changed, 245 insertions, 21 deletions
diff --git a/charger/charger.c b/charger/charger.c index d3b414d..03280bf 100644 --- a/charger/charger.c +++ b/charger/charger.c @@ -224,7 +224,7 @@ static void dump_last_kmsg(void) yoink = ptr[cnt]; ptr[cnt] = '\0'; - KLOG_INFO("", "%s", ptr); + klog_write(6, "<6>%s", ptr); ptr[cnt] = yoink; len -= cnt; @@ -745,30 +745,44 @@ static void update_screen_state(struct charger *charger, int64_t now) } } -static void update_input_state(struct charger *charger, - struct input_event *ev, - int64_t now) +static int set_key_callback(int code, int value, void *data) { - int down = !!ev->value; + struct charger *charger = data; + int64_t now = curr_time_ms(); + int down = !!value; - if (ev->type != EV_KEY || ev->code > KEY_MAX) - return; + if (code > KEY_MAX) + return -1; + + /* ignore events that don't modify our state */ + if (charger->keys[code].down == down) + return 0; /* only record the down even timestamp, as the amount * of time the key spent not being pressed is not useful */ if (down) - charger->keys[ev->code].timestamp = now; - charger->keys[ev->code].down = down; - charger->keys[ev->code].pending = true; + charger->keys[code].timestamp = now; + charger->keys[code].down = down; + charger->keys[code].pending = true; if (down) { - LOGV("[%lld] key[%d] down\n", now, ev->code); + LOGV("[%lld] key[%d] down\n", now, code); } else { - int64_t duration = now - charger->keys[ev->code].timestamp; + int64_t duration = now - charger->keys[code].timestamp; int64_t secs = duration / 1000; int64_t msecs = duration - secs * 1000; LOGV("[%lld] key[%d] up (was down for %lld.%lldsec)\n", now, - ev->code, secs, msecs); + code, secs, msecs); } + + return 0; +} + +static void update_input_state(struct charger *charger, + struct input_event *ev) +{ + if (ev->type != EV_KEY) + return; + set_key_callback(ev->code, ev->value, charger); } static void set_next_key_check(struct charger *charger, @@ -876,7 +890,7 @@ static int input_callback(int fd, short revents, void *data) ret = ev_get_input(fd, revents, &ev); if (ret) return -1; - update_input_state(charger, &ev, curr_time_ms()); + update_input_state(charger, &ev); return 0; } @@ -949,6 +963,8 @@ int main(int argc, char **argv) } } + ev_sync_key_state(set_key_callback, charger); + gr_fb_blank(true); charger->next_screen_transition = now - 1; diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c index 88bf054..58a7839 100644 --- a/debuggerd/arm/machine.c +++ b/debuggerd/arm/machine.c @@ -50,6 +50,74 @@ extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, int *frame0_pc_sane, bool at_fault); +/* + * If this isn't clearly a null pointer dereference, dump the + * /proc/maps entries near the fault address. + */ +static void show_nearby_maps(int tfd, int pid, mapinfo *map) +{ + siginfo_t si; + + memset(&si, 0, sizeof(si)); + if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) { + _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno)); + return; + } + if (!signal_has_address(si.si_signo)) + return; + + uintptr_t addr = (uintptr_t) si.si_addr; + addr &= ~0xfff; /* round to 4K page boundary */ + if (addr == 0) /* null-pointer deref */ + return; + + _LOG(tfd, false, "\nmemory map around addr %08x:\n", si.si_addr); + + /* + * Search for a match, or for a hole where the match would be. The list + * is backward from the file content, so it starts at high addresses. + */ + bool found = false; + mapinfo *next = NULL; + mapinfo *prev = NULL; + while (map != NULL) { + if (addr >= map->start && addr < map->end) { + found = true; + next = map->next; + break; + } else if (addr >= map->end) { + /* map would be between "prev" and this entry */ + next = map; + map = NULL; + break; + } + + prev = map; + map = map->next; + } + + /* + * Show "next" then "match" then "prev" so that the addresses appear in + * ascending order (like /proc/pid/maps). + */ + if (next != NULL) { + _LOG(tfd, false, "%08x-%08x %s\n", next->start, next->end, next->name); + } else { + _LOG(tfd, false, "(no map below)\n"); + } + if (map != NULL) { + _LOG(tfd, false, "%08x-%08x %s\n", map->start, map->end, map->name); + } else { + _LOG(tfd, false, "(no map for address)\n"); + } + if (prev != NULL) { + _LOG(tfd, false, "%08x-%08x %s\n", prev->start, prev->end, prev->name); + } else { + _LOG(tfd, false, "(no map above)\n"); + } +} + + void dump_stack_and_code(int tfd, int pid, mapinfo *map, int unwind_depth, unsigned int sp_list[], bool at_fault) @@ -123,6 +191,8 @@ void dump_stack_and_code(int tfd, int pid, mapinfo *map, } } + show_nearby_maps(tfd, pid, map); + p = sp - 64; if (p > sp) p = 0; diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index d03c214..91d9dda 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -31,6 +31,7 @@ #include <cutils/sockets.h> #include <cutils/logd.h> +#include <cutils/logger.h> #include <cutils/properties.h> #include <linux/input.h> @@ -70,14 +71,17 @@ mapinfo *parse_maps_line(char *line) mapinfo *mi; int len = strlen(line); - if(len < 1) return 0; + if (len < 1) return 0; /* not expected */ line[--len] = 0; - if(len < 50) return 0; - if(line[20] != 'x') return 0; + if (len < 50) { + mi = malloc(sizeof(mapinfo) + 1); + } else { + mi = malloc(sizeof(mapinfo) + (len - 47)); + } + if (mi == 0) return 0; - mi = malloc(sizeof(mapinfo) + (len - 47)); - if(mi == 0) return 0; + mi->isExecutable = (line[20] == 'x'); mi->start = strtoul(line, 0, 16); mi->end = strtoul(line + 9, 0, 16); @@ -87,7 +91,11 @@ mapinfo *parse_maps_line(char *line) mi->exidx_start = mi->exidx_end = 0; mi->symbols = 0; mi->next = 0; - strcpy(mi->name, line + 49); + if (len < 50) { + mi->name[0] = '\0'; + } else { + strcpy(mi->name, line + 49); + } return mi; } @@ -165,11 +173,14 @@ void dump_fault_addr(int tfd, int pid, int sig) memset(&si, 0, sizeof(si)); if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){ _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno)); - } else { + } else if (signal_has_address(sig)) { _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr %08x\n", sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code), si.si_addr); + } else { + _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr --------\n", + sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); } } @@ -199,6 +210,9 @@ static void parse_elf_info(mapinfo *milist, pid_t pid) { mapinfo *mi; for (mi = milist; mi != NULL; mi = mi->next) { + if (!mi->isExecutable) + continue; + Elf32_Ehdr ehdr; memset(&ehdr, 0, sizeof(Elf32_Ehdr)); @@ -400,6 +414,101 @@ static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid) return need_cleanup != 0; } +/* + * Reads the contents of the specified log device, filters out the entries + * that don't match the specified pid, and writes them to the tombstone file. + */ +static void dump_log_file(int tfd, unsigned pid, const char* filename) +{ + int logfd = open(filename, O_RDONLY | O_NONBLOCK); + if (logfd < 0) { + XLOG("Unable to open %s: %s\n", filename, strerror(errno)); + return; + } + _LOG(tfd, true, "--------- log %s\n", filename); + + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + struct logger_entry entry; + } log_entry; + + while (true) { + ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN); + if (actual < 0) { + if (errno == EINTR) { + /* interrupted by signal, retry */ + continue; + } else if (errno == EAGAIN) { + /* non-blocking EOF; we're done */ + break; + } else { + _LOG(tfd, true, "Error while reading log: %s\n", + strerror(errno)); + break; + } + } else if (actual == 0) { + _LOG(tfd, true, "Got zero bytes while reading log: %s\n", + strerror(errno)); + break; + } + + /* + * NOTE: if you XLOG something here, this will spin forever, + * because you will be writing as fast as you're reading. Any + * high-frequency debug diagnostics should just be written to + * the tombstone file. + */ + + struct logger_entry* entry = &log_entry.entry; + + if (entry->pid != (int32_t) pid) { + /* wrong pid, ignore */ + continue; + } + + /* + * Msg format is: <priority:1><tag:N>\0<message:N>\0 + * + * We want to display it in the same format as "logcat -v threadtime" + * (although in this case the pid is redundant). + * + * TODO: scan for line breaks ('\n') and display each text line + * on a separate line, prefixed with the header, like logcat does. + */ + static const char* kPrioChars = "!.VDIWEFS"; + unsigned char prio = entry->msg[0]; + const char* tag = entry->msg + 1; + const char* msg = tag + strlen(tag) + 1; + + log_entry.entry.msg[entry->len] = '\0'; + + char timeBuf[32]; + time_t sec = (time_t) entry->sec; + struct tm tmBuf; + struct tm* ptm; + ptm = localtime_r(&sec, &tmBuf); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + _LOG(tfd, true, "%s.%03ld %5d %5d %c %-8s: %s\n", + timeBuf, entry->nsec / 1000000, + entry->pid, entry->tid, + (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'), + tag, msg); + } + + close(logfd); +} + +/* + * Dumps the logs generated by the specified pid to the tombstone, from both + * "system" and "main" log devices. Ideally we'd interleave the output. + */ +static void dump_logs(int tfd, unsigned pid) +{ + dump_log_file(tfd, pid, "/dev/log/system"); + dump_log_file(tfd, pid, "/dev/log/main"); +} + /* Return true if some thread is not detached cleanly */ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, int signal) @@ -424,6 +533,13 @@ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, need_cleanup = dump_sibling_thread_report(fd, pid, tid); } + /* don't copy log to tombstone unless this is a dev device */ + char value[PROPERTY_VALUE_MAX]; + property_get("ro.debuggable", value, "0"); + if (value[0] == '1') { + dump_logs(fd, pid); + } + close(fd); return need_cleanup; } diff --git a/debuggerd/utility.c b/debuggerd/utility.c index 2afdb46..409209c 100644 --- a/debuggerd/utility.c +++ b/debuggerd/utility.c @@ -17,6 +17,7 @@ #include <sys/ptrace.h> #include <sys/exec_elf.h> +#include <signal.h> #include <assert.h> #include <string.h> #include <errno.h> @@ -82,3 +83,20 @@ const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc) } return NULL; } + +/* + * Returns true if the specified signal has an associated address (i.e. it + * sets siginfo_t.si_addr). + */ +bool signal_has_address(int sig) +{ + switch (sig) { + case SIGILL: + case SIGFPE: + case SIGSEGV: + case SIGBUS: + return true; + default: + return false; + } +} diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 45e2067..4a935d2 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -36,6 +36,7 @@ typedef struct mapinfo { unsigned exidx_start; unsigned exidx_end; struct symbol_table *symbols; + bool isExecutable; char name[]; } mapinfo; @@ -56,6 +57,9 @@ const char *map_to_name(mapinfo *mi, unsigned pc, const char* def); /* Log information onto the tombstone */ extern void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...); +/* Determine whether si_addr is valid for this signal */ +bool signal_has_address(int sig); + #define LOG(fmt...) _LOG(-1, 0, fmt) /* Set to 1 for normal debug traces */ |