Add wchan data to ANR dump
Since only privileged processes with CAP_SYS_ADMIN can read kernel stack traces from /proc/*/stack, we dump the waiting channels instead to provide some insight as to where the process might be stuck in the kernel. Bug: 135458700 Fixes: 135458700 Test: adb shell am hang; Check /data/anr/<anr-file> for wchan data. Change-Id: I9f13511ad89a259ce5e5465155db15d45d2c46d8
This commit is contained in:
parent
cd22e3a6f6
commit
1489e27bda
1 changed files with 86 additions and 1 deletions
|
@ -22,9 +22,11 @@
|
|||
#include <sys/poll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
|
||||
#include <android-base/cmsg.h>
|
||||
#include <android-base/file.h>
|
||||
|
@ -42,8 +44,10 @@
|
|||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
using android::base::ReadFileToString;
|
||||
using android::base::SendFileDescriptors;
|
||||
using android::base::unique_fd;
|
||||
using android::base::WriteStringToFd;
|
||||
|
||||
static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
|
||||
const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL;
|
||||
|
@ -65,6 +69,76 @@ static void populate_timeval(struct timeval* tv, const Duration& duration) {
|
|||
tv->tv_usec = static_cast<long>(microseconds.count());
|
||||
}
|
||||
|
||||
static void get_wchan_header(pid_t pid, std::stringstream& buffer) {
|
||||
struct tm now;
|
||||
time_t t = time(nullptr);
|
||||
localtime_r(&t, &now);
|
||||
char timestamp[32];
|
||||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &now);
|
||||
std::string time_now(timestamp);
|
||||
|
||||
std::string path = "/proc/" + std::to_string(pid) + "/cmdline";
|
||||
|
||||
char proc_name_buf[1024];
|
||||
const char* proc_name = nullptr;
|
||||
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path.c_str(), "r"), &fclose);
|
||||
|
||||
if (fp) {
|
||||
proc_name = fgets(proc_name_buf, sizeof(proc_name_buf), fp.get());
|
||||
}
|
||||
|
||||
if (!proc_name) {
|
||||
proc_name = "<unknown>";
|
||||
}
|
||||
|
||||
buffer << "\n----- Waiting Channels: pid " << pid << " at " << time_now << " -----\n"
|
||||
<< "Cmd line: " << proc_name << "\n";
|
||||
}
|
||||
|
||||
static void get_wchan_footer(pid_t pid, std::stringstream& buffer) {
|
||||
buffer << "----- end " << std::to_string(pid) << " -----\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wchan data for each thread in the process,
|
||||
* or empty string if unable to obtain any data.
|
||||
*/
|
||||
static std::string get_wchan_data(pid_t pid) {
|
||||
std::stringstream buffer;
|
||||
std::vector<pid_t> tids;
|
||||
|
||||
if (!android::procinfo::GetProcessTids(pid, &tids)) {
|
||||
LOG(WARNING) << "libdebuggerd_client: Failed to get process tids";
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
std::stringstream data;
|
||||
for (int tid : tids) {
|
||||
std::string path = "/proc/" + std::to_string(pid) + "/task/" + std::to_string(tid) + "/wchan";
|
||||
std::string wchan_str;
|
||||
if (!ReadFileToString(path, &wchan_str, true)) {
|
||||
PLOG(WARNING) << "libdebuggerd_client: Failed to read \"" << path << "\"";
|
||||
continue;
|
||||
}
|
||||
data << "sysTid=" << std::left << std::setw(10) << tid << wchan_str << "\n";
|
||||
}
|
||||
|
||||
if (std::string str = data.str(); !str.empty()) {
|
||||
get_wchan_header(pid, buffer);
|
||||
buffer << "\n" << str << "\n";
|
||||
get_wchan_footer(pid, buffer);
|
||||
buffer << "\n";
|
||||
}
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
static void dump_wchan_data(const std::string& data, int fd, pid_t pid) {
|
||||
if (!WriteStringToFd(data, fd)) {
|
||||
LOG(WARNING) << "libdebuggerd_client: Failed to dump wchan data for pid: " << pid;
|
||||
}
|
||||
}
|
||||
|
||||
bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
|
||||
unique_fd output_fd) {
|
||||
pid_t pid = tid;
|
||||
|
@ -261,6 +335,17 @@ int dump_backtrace_to_file_timeout(pid_t tid, DebuggerdDumpType dump_type, int t
|
|||
if (copy == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// debuggerd_trigger_dump results in every thread in the process being interrupted
|
||||
// by a signal, so we need to fetch the wchan data before calling that.
|
||||
std::string wchan_data = get_wchan_data(tid);
|
||||
|
||||
int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
|
||||
return debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
|
||||
int ret = debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
|
||||
|
||||
// Dump wchan data, since only privileged processes (CAP_SYS_ADMIN) can read
|
||||
// kernel stack traces (/proc/*/stack).
|
||||
dump_wchan_data(wchan_data, fd, tid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue