Skip to content

Andorid thread dump #27

@nzcv

Description

@nzcv
// thread_dump.cpp
#include "gt.h"
#include <android/log.h>
#include <atomic>
#include <dirent.h>
#include <signal.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <ucontext.h>
#include <unistd.h>
#include <unwind.h>
#include <dlfcn.h>
#include <vector>


#define LOG_TAG "GTNative"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

static pid_t gettid_linux() { return (pid_t)syscall(SYS_gettid); }

static std::atomic<int> g_dumping{0};
static int g_sig = SIGUSR2;

// Use libunwind for Android
struct BacktraceState {
  void **current;
  void **end;
};

static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context *context,
                                          void *arg) {
  BacktraceState *state = static_cast<BacktraceState *>(arg);
  uintptr_t pc = _Unwind_GetIP(context);
  if (pc) {
    if (state->current == state->end) {
      return _URC_END_OF_STACK;
    } else {
      *state->current++ = reinterpret_cast<void *>(pc);
    }
  }
  return _URC_NO_REASON;
}


struct StackInfo {
  pid_t tid;
  void *buffer[1024];
  int size;
};

static StackInfo StackVector[1024];
static std::atomic<int> si;

// IMPORTANT: do as little as possible in handler in real code
static void handler(int sig, siginfo_t *info, void *uctx) {
  (void)sig;
  (void)info;
  (void)uctx;
  if (!g_dumping.load(std::memory_order_relaxed))
    return;

  int idx = si.fetch_add(1, std::memory_order_relaxed);
  if (idx >= 1024)
    return; // Buffer overflow protection
  
  StackInfo &stack_info = StackVector[idx];
  stack_info.tid = gettid_linux();
  
  BacktraceState state = {stack_info.buffer, stack_info.buffer + 1024};
  _Unwind_Backtrace(unwindCallback, &state);
  stack_info.size = state.current - stack_info.buffer;
  ALOGE("Thread %d dump stack: %d", stack_info.tid, stack_info.size);
}

static void install_handler() {
  struct sigaction sa;
  memset(&sa, 0, sizeof(sa));
  sa.sa_sigaction = handler;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
  sigaction(g_sig, &sa, nullptr);
}

static std::vector<pid_t> list_tids() {
  std::vector<pid_t> tids;
  DIR *d = opendir("/proc/self/task");
  if (!d)
    return tids;
  while (dirent *e = readdir(d)) {
    if (e->d_name[0] == '.')
      continue;
    char *end = nullptr;
    long tid = strtol(e->d_name, &end, 10);
    if (*end == '\0' && tid > 0)
      tids.push_back((pid_t)tid);
  }
  closedir(d);
  return tids;
}

void dump_all_threads_except_self() {
  pid_t self = gettid_linux();
  auto tids = list_tids();

  g_dumping.store(1, std::memory_order_relaxed);

  for (pid_t tid : tids) {
    if (tid == self)
      continue;
    // Send signal to specific thread
    syscall(SYS_tgkill, getpid(), tid, g_sig);
  }

  g_dumping.store(0, std::memory_order_relaxed);
}

__attribute__((constructor)) void ___tpmm_ctor() {
  LOGI("GTCrashHandler Constructor2");
  install_handler();
  // new thread with httpserver
  std::thread([&]() {
    httplib::Server svr;
    svr.Get("/", [](const httplib::Request &req, httplib::Response &res) {
      res.set_content("GTCrashed!", "text/plain");
      dump_all_threads_except_self();
      //sleep 10s
      sleep(10);
      for (int i = 0; i < si; i++) {
        // dladdr
        StackInfo &stack_info = StackVector[i];
        ALOGE("Thread %d dump stack: %d", stack_info.tid, stack_info.size);
        for (int j = 0; j < stack_info.size; j++) {
          Dl_info info;
          if (dladdr(stack_info.buffer[j], &info)) {
            auto offset = (uintptr_t)stack_info.buffer[j] - (uintptr_t)info.dli_fbase;
            ALOGE("  #%d %p %s %s(%lx)", j, stack_info.buffer[j], info.dli_fname, info.dli_sname, offset);
          }
        }
      }
    });
    svr.listen("0.0.0.0", 5000);
  }).detach();
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions