summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--debuggerd/arm/machine.c70
-rw-r--r--debuggerd/debuggerd.c130
-rw-r--r--debuggerd/utility.c18
-rw-r--r--debuggerd/utility.h4
-rw-r--r--include/cutils/compiler.h12
-rw-r--r--liblog/logprint.c8
-rw-r--r--rootdir/init.rc1
7 files changed, 235 insertions, 8 deletions
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 */
diff --git a/include/cutils/compiler.h b/include/cutils/compiler.h
index 09112d5..70f884a 100644
--- a/include/cutils/compiler.h
+++ b/include/cutils/compiler.h
@@ -29,4 +29,16 @@
# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
#endif
+/**
+ * exports marked symbols
+ *
+ * if used on a C++ class declaration, this macro must be inserted
+ * after the "class" keyword. For instance:
+ *
+ * template <typename TYPE>
+ * class ANDROID_API Singleton { }
+ */
+
+#define ANDROID_API __attribute__((visibility("default")))
+
#endif // ANDROID_CUTILS_COMPILER_H
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 59fed9b..f2dd79f 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -374,6 +374,13 @@ int android_log_processLogBuffer(struct logger_entry *buf,
entry->messageLen = buf->len - preambleAndNullLen;
entry->message = entry->tag + tag_len + 1;
+ if (entry->messageLen != strlen(entry->message)) {
+ fprintf(stderr,
+ "+++ LOG: Message length inconsistent. Expected %d, got %d\n",
+ entry->messageLen, strlen(entry->message));
+ return -1;
+ }
+
return 0;
}
@@ -830,7 +837,6 @@ char *android_log_formatLogLine (
while(pm < (entry->message + entry->messageLen)) {
const char *lineStart;
size_t lineLen;
-
lineStart = pm;
// Find the next end-of-line in message
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 46af96c..3af0943 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -64,6 +64,7 @@ loglevel 3
write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
write /proc/sys/kernel/sched_compat_yield 1
write /proc/sys/kernel/sched_child_runs_first 0
+ write /proc/sys/kernel/randomize_va_space 2
# Create cgroup mount points for process groups
mkdir /dev/cpuctl