Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c89f9e9 authored by Josh Gao's avatar Josh Gao
Browse files

debuggerd: fix several bugs caused by fork/setuid change.

Previously, we weren't PTRACE_ATTACHing to all of the threads of a
process, and we were also trying to do it after forking and dropping
privileges. This patch ensures that all ptrace attaching/detaching
happens in one place, before forking/exiting respectively.

Bug: http://b/26443860
Bug: http://b/26436605
Bug: http://b/26436486
Change-Id: Id94e0c1d9d56c051d0dd281d895aaa3285079198
parent 047597b3
Loading
Loading
Loading
Loading
+7 −39
Original line number Original line Diff line number Diff line
@@ -67,8 +67,7 @@ static void dump_process_footer(log_t* log, pid_t pid) {
  _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
  _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
}
}


static void dump_thread(
static void dump_thread(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid) {
    log_t* log, pid_t tid, bool attached, bool* detach_failed, int* total_sleep_time_usec) {
  char path[PATH_MAX];
  char path[PATH_MAX];
  char threadnamebuf[1024];
  char threadnamebuf[1024];
  char* threadname = NULL;
  char* threadname = NULL;
@@ -88,56 +87,25 @@ static void dump_thread(


  _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid);
  _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid);


  if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) {
  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
    _LOG(log, logtype::BACKTRACE, "Could not attach to thread: %s\n", strerror(errno));
    return;
  }

  if (!attached && wait_for_sigstop(tid, total_sleep_time_usec, detach_failed) == -1) {
    return;
  }

  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(tid, BACKTRACE_CURRENT_THREAD));
  if (backtrace->Unwind(0)) {
  if (backtrace->Unwind(0)) {
    dump_backtrace_to_log(backtrace.get(), log, "  ");
    dump_backtrace_to_log(backtrace.get(), log, "  ");
  } else {
  } else {
    ALOGE("Unwind failed: tid = %d", tid);
    ALOGE("Unwind failed: tid = %d", tid);
  }
  }

  if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
    ALOGE("ptrace detach from %d failed: %s\n", tid, strerror(errno));
    *detach_failed = true;
  }
}
}


void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
void dump_backtrace(int fd, int amfd, BacktraceMap* map, pid_t pid, pid_t tid,
                    int* total_sleep_time_usec) {
                    const std::set<pid_t>& siblings) {
  log_t log;
  log_t log;
  log.tfd = fd;
  log.tfd = fd;
  log.amfd = amfd;
  log.amfd = amfd;


  dump_process_header(&log, pid);
  dump_process_header(&log, pid);
  dump_thread(&log, tid, true, detach_failed, total_sleep_time_usec);
  dump_thread(&log, map, pid, tid);

  char task_path[64];
  snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
  DIR* d = opendir(task_path);
  if (d != NULL) {
    struct dirent* de = NULL;
    while ((de = readdir(d)) != NULL) {
      if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
        continue;
      }


      char* end;
  for (pid_t sibling : siblings) {
      pid_t new_tid = strtoul(de->d_name, &end, 10);
    dump_thread(&log, map, pid, sibling);
      if (*end || new_tid == tid) {
        continue;
      }

      dump_thread(&log, new_tid, false, detach_failed, total_sleep_time_usec);
    }
    closedir(d);
  }
  }


  dump_process_footer(&log, pid);
  dump_process_footer(&log, pid);
+5 −2
Original line number Original line Diff line number Diff line
@@ -19,14 +19,17 @@


#include <sys/types.h>
#include <sys/types.h>


#include <set>

#include "utility.h"
#include "utility.h"


class Backtrace;
class Backtrace;
class BacktraceMap;


// Dumps a backtrace using a format similar to what Dalvik uses so that the result
// Dumps a backtrace using a format similar to what Dalvik uses so that the result
// can be intermixed in a bug report.
// can be intermixed in a bug report.
void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
void dump_backtrace(int fd, int amfd, BacktraceMap* map, pid_t pid, pid_t tid,
                    int* total_sleep_time_usec);
                    const std::set<pid_t>& siblings);


/* Dumps the backtrace in the backtrace data structure to the log. */
/* Dumps the backtrace in the backtrace data structure to the log. */
void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix);
void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix);
+162 −97
Original line number Original line Diff line number Diff line
@@ -14,14 +14,14 @@
 * limitations under the License.
 * limitations under the License.
 */
 */


#include <stdio.h>
#include <dirent.h>
#include <errno.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <pthread.h>
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stdarg.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/types.h>
#include <dirent.h>
#include <time.h>
#include <time.h>


#include <elf.h>
#include <elf.h>
@@ -31,6 +31,8 @@
#include <sys/stat.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/wait.h>


#include <set>

#include <selinux/android.h>
#include <selinux/android.h>


#include <log/logger.h>
#include <log/logger.h>
@@ -57,6 +59,8 @@
#define SOCKET_NAME DEBUGGER_SOCKET_NAME
#define SOCKET_NAME DEBUGGER_SOCKET_NAME
#endif
#endif


extern "C" int tgkill(int tgid, int tid, int sig);

struct debugger_request_t {
struct debugger_request_t {
  debugger_action_t action;
  debugger_action_t action;
  pid_t pid, tid;
  pid_t pid, tid;
@@ -335,6 +339,121 @@ static void redirect_to_32(int fd, debugger_request_t* request) {
}
}
#endif
#endif


static void ptrace_siblings(pid_t pid, pid_t main_tid, std::set<pid_t>& tids) {
  char task_path[64];

  snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);

  std::unique_ptr<DIR, int (*)(DIR*)> d(opendir(task_path), closedir);

  // Bail early if the task directory cannot be opened.
  if (!d) {
    ALOGE("debuggerd: failed to open /proc/%d/task: %s", pid, strerror(errno));
    return;
  }

  struct dirent* de;
  while ((de = readdir(d.get())) != NULL) {
    // Ignore "." and "..".
    if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
      continue;
    }

    char* end;
    pid_t tid = strtoul(de->d_name, &end, 10);
    if (*end) {
      continue;
    }

    if (tid == main_tid) {
      continue;
    }

    if (ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) {
      ALOGE("debuggerd: ptrace attach to %d failed: %s", tid, strerror(errno));
      continue;
    }

    tids.insert(tid);
  }
}

static bool perform_dump(const debugger_request_t& request, int fd, int tombstone_fd,
                         BacktraceMap* backtrace_map, const std::set<pid_t>& siblings) {
  if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
    ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno));
    return false;
  }

  int total_sleep_time_usec = 0;
  while (true) {
    int signal = wait_for_signal(request.tid, &total_sleep_time_usec);
    switch (signal) {
      case -1:
        ALOGE("debuggerd: timed out waiting for signal");
        return false;

      case SIGSTOP:
        if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
          ALOGV("debuggerd: stopped -- dumping to tombstone");
          engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal,
                            request.original_si_code, request.abort_msg_address);
        } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
          ALOGV("debuggerd: stopped -- dumping to fd");
          dump_backtrace(fd, -1, backtrace_map, request.pid, request.tid, siblings);
        } else {
          ALOGV("debuggerd: stopped -- continuing");
          if (ptrace(PTRACE_CONT, request.tid, 0, 0) != 0) {
            ALOGE("debuggerd: ptrace continue failed: %s", strerror(errno));
            return false;
          }
          continue;  // loop again
        }
        break;

      case SIGABRT:
      case SIGBUS:
      case SIGFPE:
      case SIGILL:
      case SIGSEGV:
#ifdef SIGSTKFLT
      case SIGSTKFLT:
#endif
      case SIGTRAP:
        ALOGV("stopped -- fatal signal\n");
        // Send a SIGSTOP to the process to make all of
        // the non-signaled threads stop moving.  Without
        // this we get a lot of "ptrace detach failed:
        // No such process".
        kill(request.pid, SIGSTOP);
        engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal,
                          request.original_si_code, request.abort_msg_address);
        break;

      default:
        ALOGE("debuggerd: process stopped due to unexpected signal %d\n", signal);
        break;
    }
    break;
  }

  return true;
}

static bool drop_privileges() {
  if (setresgid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
    ALOGE("debuggerd: failed to setresgid");
    return false;
  }

  if (setresuid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
    ALOGE("debuggerd: failed to setresuid");
    return false;
  }

  return true;
}

static void handle_request(int fd) {
static void handle_request(int fd) {
  ALOGV("handle_request(%d)\n", fd);
  ALOGV("handle_request(%d)\n", fd);


@@ -405,117 +524,63 @@ static void handle_request(int fd) {
  // ensure that it can run as soon as we call PTRACE_CONT below.
  // ensure that it can run as soon as we call PTRACE_CONT below.
  // See details in bionic/libc/linker/debugger.c, in function
  // See details in bionic/libc/linker/debugger.c, in function
  // debugger_signal_handler().
  // debugger_signal_handler().
  if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) {
    ALOGE("debuggerd: ptrace attach failed: %s\n", strerror(errno));
    exit(1);
  }

  // Generate the backtrace map before dropping privileges.
  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));


  // Now that we've done everything that requires privileges, we can drop them.
  // Attach to the target process.
  if (setresgid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
  if (ptrace(PTRACE_ATTACH, request.tid, 0, 0) != 0) {
    ALOGE("debuggerd: failed to setresgid");
    ALOGE("debuggerd: ptrace attach failed: %s", strerror(errno));
    exit(1);
    exit(1);
  }
  }


  if (setresuid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
  // Don't attach to the sibling threads if we want to attach gdb.
    ALOGE("debuggerd: failed to setresuid");
  // Supposedly, it makes the process less reliable.
    exit(1);
  }

  bool detach_failed = false;
  bool tid_unresponsive = false;
  bool attach_gdb = should_attach_gdb(&request);
  bool attach_gdb = should_attach_gdb(&request);
  if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
  std::set<pid_t> siblings;
    ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno));
  if (!attach_gdb) {
    exit(1);
    ptrace_siblings(request.pid, request.tid, siblings);
  }

  int total_sleep_time_usec = 0;
  while (true) {
    int signal = wait_for_sigstop(request.tid, &total_sleep_time_usec, &detach_failed);
    if (signal == -1) {
      tid_unresponsive = true;
      break;
    }

    switch (signal) {
      case SIGSTOP:
        if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
          ALOGV("stopped -- dumping to tombstone\n");
          engrave_tombstone(tombstone_fd, backtrace_map.get(), request.pid, request.tid, signal,
                            request.original_si_code, request.abort_msg_address, true,
                            &detach_failed, &total_sleep_time_usec);
        } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
          ALOGV("stopped -- dumping to fd\n");
          dump_backtrace(fd, -1, request.pid, request.tid, &detach_failed, &total_sleep_time_usec);
        } else {
          ALOGV("stopped -- continuing\n");
          status = ptrace(PTRACE_CONT, request.tid, 0, 0);
          if (status) {
            ALOGE("debuggerd: ptrace continue failed: %s\n", strerror(errno));
          }
          continue;  // loop again
  }
  }
        break;


      case SIGABRT:
  // Generate the backtrace map before dropping privileges.
      case SIGBUS:
  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));
      case SIGFPE:
      case SIGILL:
      case SIGSEGV:
#ifdef SIGSTKFLT
      case SIGSTKFLT:
#endif
      case SIGTRAP:
        ALOGV("stopped -- fatal signal\n");
        // Send a SIGSTOP to the process to make all of
        // the non-signaled threads stop moving.  Without
        // this we get a lot of "ptrace detach failed:
        // No such process".
        kill(request.pid, SIGSTOP);
        // don't dump sibling threads when attaching to GDB because it
        // makes the process less reliable, apparently...
        engrave_tombstone(tombstone_fd, backtrace_map.get(), request.pid, request.tid, signal,
                          request.original_si_code, request.abort_msg_address, !attach_gdb,
                          &detach_failed, &total_sleep_time_usec);
        break;


      default:
  bool succeeded = false;
        ALOGE("debuggerd: process stopped due to unexpected signal %d\n", signal);
        break;
    }
    break;
  }


  // Now that we've done everything that requires privileges, we can drop them.
  if (drop_privileges()) {
    succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings);
    if (succeeded) {
      if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
      if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
        if (!tombstone_path.empty()) {
        if (!tombstone_path.empty()) {
          write(fd, tombstone_path.c_str(), tombstone_path.length());
          write(fd, tombstone_path.c_str(), tombstone_path.length());
        }
        }
      }
      }
    }


  if (!tid_unresponsive) {
    ALOGV("detaching");
    if (attach_gdb) {
    if (attach_gdb) {
      // stop the process so we can debug
      // Stop the process so we can debug.
      kill(request.pid, SIGSTOP);
      tgkill(request.pid, request.tid, SIGSTOP);
    }
  }
  }
    if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) {

  if (ptrace(PTRACE_DETACH, request.tid, 0, 0) != 0) {
    ALOGE("debuggerd: ptrace detach from %d failed: %s", request.tid, strerror(errno));
    ALOGE("debuggerd: ptrace detach from %d failed: %s", request.tid, strerror(errno));
      detach_failed = true;
  }
    } else if (attach_gdb) {

      // if debug.db.uid is set, its value indicates if we should wait
  for (pid_t sibling : siblings) {
    ptrace(PTRACE_DETACH, sibling, 0, 0);
  }

  if (succeeded && attach_gdb) {
    // if debug.debuggerd.wait_for_gdb is set, its value indicates if we should wait
    // for user action for the crashing process.
    // for user action for the crashing process.
    // in this case, we log a message and turn the debug LED on
    // in this case, we log a message and turn the debug LED on
    // waiting for a gdb connection (for instance)
    // waiting for a gdb connection (for instance)
    wait_for_user_action(request);
    wait_for_user_action(request);
  }
  }
  }


  // Resume the stopped process so it can crash in peace, and exit.
  // Resume the stopped process.
  kill(request.pid, SIGCONT);
  kill(request.pid, SIGCONT);
  exit(0);

  exit(!succeeded);
}
}


static int do_server() {
static int do_server() {
+64 −112
Original line number Original line Diff line number Diff line
@@ -328,6 +328,33 @@ static std::string get_addr_string(uintptr_t addr) {
  return addr_str;
  return addr_str;
}
}


static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) {
  if (address == 0) {
    return;
  }

  address += sizeof(size_t);  // Skip the buffer length.

  char msg[512];
  memset(msg, 0, sizeof(msg));
  char* p = &msg[0];
  while (p < &msg[sizeof(msg)]) {
    word_t data;
    size_t len = sizeof(word_t);
    if (!backtrace->ReadWord(address, &data)) {
      break;
    }
    address += sizeof(word_t);

    while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0) {
      len--;
    }
  }
  msg[sizeof(msg) - 1] = '\0';

  _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
}

static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) {
static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) {
  bool print_fault_address_marker = false;
  bool print_fault_address_marker = false;
  uintptr_t addr = 0;
  uintptr_t addr = 0;
@@ -416,67 +443,37 @@ static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) {
  }
  }
}
}


// Return true if some thread is not detached cleanly
static void dump_thread(log_t* log, pid_t pid, pid_t tid, BacktraceMap* map, int signal,
static bool dump_sibling_thread_report(
                        int si_code, uintptr_t abort_msg_address, bool primary_thread) {
    log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec, BacktraceMap* map) {
  log->current_tid = tid;
  char task_path[64];
  if (!primary_thread) {

    _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
  snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);

  DIR* d = opendir(task_path);
  // Bail early if the task directory cannot be opened
  if (d == NULL) {
    ALOGE("Cannot open /proc/%d/task\n", pid);
    return false;
  }

  bool detach_failed = false;
  struct dirent* de;
  while ((de = readdir(d)) != NULL) {
    // Ignore "." and ".."
    if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
      continue;
    }

    // The main thread at fault has been handled individually
    char* end;
    pid_t new_tid = strtoul(de->d_name, &end, 10);
    if (*end || new_tid == tid) {
      continue;
  }
  }
  dump_thread_info(log, pid, tid);


    // Skip this thread if cannot ptrace it
  if (signal) {
    if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
    dump_signal_info(log, tid, signal, si_code);
      ALOGE("ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
      continue;
  }
  }


    if (wait_for_sigstop(new_tid, total_sleep_time_usec, &detach_failed) == -1) {
  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
      continue;
  if (primary_thread) {
    dump_abort_message(backtrace.get(), log, abort_msg_address);
  }
  }

  dump_registers(log, tid);
    log->current_tid = new_tid;
    _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
    dump_thread_info(log, pid, new_tid);

    dump_registers(log, new_tid);
    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map));
  if (backtrace->Unwind(0)) {
  if (backtrace->Unwind(0)) {
    dump_backtrace_and_stack(backtrace.get(), log);
    dump_backtrace_and_stack(backtrace.get(), log);
  } else {
  } else {
      ALOGE("Unwind of sibling failed: pid = %d, tid = %d", pid, new_tid);
    ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
  }
  }


    log->current_tid = log->crashed_tid;
  if (primary_thread) {

    dump_memory_and_code(log, backtrace.get());
    if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
    if (map) {
      ALOGE("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
      dump_all_maps(backtrace.get(), map, log, tid);
      detach_failed = true;
    }
    }
  }
  }


  closedir(d);
  log->current_tid = log->crashed_tid;
  return detach_failed;
}
}


// Reads the contents of the specified log device, filters out the entries
// Reads the contents of the specified log device, filters out the entries
@@ -605,36 +602,10 @@ static void dump_logs(log_t* log, pid_t pid, unsigned int tail) {
  dump_log_file(log, pid, "main", tail);
  dump_log_file(log, pid, "main", tail);
}
}


static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) {
  if (address == 0) {
    return;
  }

  address += sizeof(size_t); // Skip the buffer length.

  char msg[512];
  memset(msg, 0, sizeof(msg));
  char* p = &msg[0];
  while (p < &msg[sizeof(msg)]) {
    word_t data;
    size_t len = sizeof(word_t);
    if (!backtrace->ReadWord(address, &data)) {
      break;
    }
    address += sizeof(word_t);

    while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0)
       len--;
  }
  msg[sizeof(msg) - 1] = '\0';

  _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
}

// Dumps all information about the specified pid to the tombstone.
// Dumps all information about the specified pid to the tombstone.
static bool dump_crash(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid, int signal, int si_code,
static void dump_crash(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid,
                       uintptr_t abort_msg_address, bool dump_sibling_threads,
                       const std::set<pid_t>& siblings, int signal, int si_code,
                       int* total_sleep_time_usec) {
                       uintptr_t abort_msg_address) {
  // don't copy log messages to tombstone unless this is a dev device
  // don't copy log messages to tombstone unless this is a dev device
  char value[PROPERTY_VALUE_MAX];
  char value[PROPERTY_VALUE_MAX];
  property_get("ro.debuggable", value, "0");
  property_get("ro.debuggable", value, "0");
@@ -653,32 +624,15 @@ static bool dump_crash(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid, int
  _LOG(log, logtype::HEADER,
  _LOG(log, logtype::HEADER,
       "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
       "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
  dump_header_info(log);
  dump_header_info(log);
  dump_thread_info(log, pid, tid);
  dump_thread(log, pid, tid, map, signal, si_code, abort_msg_address, true);

  if (signal) {
    dump_signal_info(log, tid, signal, si_code);
  }

  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
  dump_abort_message(backtrace.get(), log, abort_msg_address);
  dump_registers(log, tid);
  if (backtrace->Unwind(0)) {
    dump_backtrace_and_stack(backtrace.get(), log);
  } else {
    ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
  }
  dump_memory_and_code(log, backtrace.get());
  if (map) {
    dump_all_maps(backtrace.get(), map, log, tid);
  }

  if (want_logs) {
  if (want_logs) {
    dump_logs(log, pid, 5);
    dump_logs(log, pid, 5);
  }
  }


  bool detach_failed = false;
  if (!siblings.empty()) {
  if (dump_sibling_threads) {
    for (pid_t sibling : siblings) {
    detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map);
      dump_thread(log, pid, sibling, map, 0, 0, 0, false);
    }
  }
  }


  if (want_logs) {
  if (want_logs) {
@@ -694,7 +648,7 @@ static bool dump_crash(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid, int
    TEMP_FAILURE_RETRY( read(log->amfd, &eodMarker, 1) );
    TEMP_FAILURE_RETRY( read(log->amfd, &eodMarker, 1) );
  }
  }


  return detach_failed;
  return;
}
}


// open_tombstone - find an available tombstone slot, if any, of the
// open_tombstone - find an available tombstone slot, if any, of the
@@ -780,16 +734,15 @@ static int activity_manager_connect() {
  return amfd;
  return amfd;
}
}


void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid, int signal,
void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid,
                       int original_si_code, uintptr_t abort_msg_address, bool dump_sibling_threads,
                       const std::set<pid_t>& siblings, int signal, int original_si_code,
                       bool* detach_failed, int* total_sleep_time_usec) {
                       uintptr_t abort_msg_address) {
  log_t log;
  log_t log;
  log.current_tid = tid;
  log.current_tid = tid;
  log.crashed_tid = tid;
  log.crashed_tid = tid;


  if (tombstone_fd < 0) {
  if (tombstone_fd < 0) {
    ALOGE("debuggerd: skipping tombstone write, nothing to do.\n");
    ALOGE("debuggerd: skipping tombstone write, nothing to do.\n");
    *detach_failed = false;
    return;
    return;
  }
  }


@@ -798,8 +751,7 @@ void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid
  // being closed.
  // being closed.
  int amfd = activity_manager_connect();
  int amfd = activity_manager_connect();
  log.amfd = amfd;
  log.amfd = amfd;
  *detach_failed = dump_crash(&log, map, pid, tid, signal, original_si_code, abort_msg_address,
  dump_crash(&log, map, pid, tid, siblings, signal, original_si_code, abort_msg_address);
                              dump_sibling_threads, total_sleep_time_usec);


  // This file descriptor can be -1, any error is ignored.
  // This file descriptor can be -1, any error is ignored.
  close(amfd);
  close(amfd);
+5 −5
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@
#include <stdbool.h>
#include <stdbool.h>
#include <stddef.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/types.h>
#include <set>
#include <string>
#include <string>


class BacktraceMap;
class BacktraceMap;
@@ -30,10 +31,9 @@ class BacktraceMap;
 */
 */
int open_tombstone(std::string* path);
int open_tombstone(std::string* path);


/* Creates a tombstone file and writes the crash dump to it.
/* Creates a tombstone file and writes the crash dump to it. */
 * Returns the path of the tombstone, which must be freed using free(). */
void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid,
void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid, int signal,
                       const std::set<pid_t>& siblings, int signal, int original_si_code,
                       int original_si_code, uintptr_t abort_msg_address, bool dump_sibling_threads,
                       uintptr_t abort_msg_address);
                       bool* detach_failed, int* total_sleep_time_usec);


#endif // _DEBUGGERD_TOMBSTONE_H
#endif // _DEBUGGERD_TOMBSTONE_H
Loading