Dump threads in tombstone fallback path.
When dumping a tombstone using the fallback path, only the main thread was showing up. Modify the code to dump the threads using a slightly different path for the tombstone generation code. In addition, while looking at this code, two MTE variables were not set in the tombstone fallback code. Added those variables so MTE devices will work properly in this fallback path. Modified the tombstone unit tests for seccomp to have multiple threads and verify those threads show up in the tombstone. Bug: 208933016 Test: Ran unit tests. Test: Ran debuggerd <PID> on a privileged process and verified Test: all threads dumped. Also verified that the tagged_addr_ctrl Test: variable is present on the raven device. Change-Id: I16eadb0cc2c37a7dbc5cac16af9b5051008b5127
This commit is contained in:
parent
efc9366188
commit
b999b82eb7
8 changed files with 196 additions and 149 deletions
|
@ -1409,6 +1409,16 @@ __attribute__((noinline)) extern "C" bool raise_debugger_signal(DebuggerdDumpTyp
|
|||
return true;
|
||||
}
|
||||
|
||||
extern "C" void foo() {
|
||||
LOG(INFO) << "foo";
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
|
||||
extern "C" void bar() {
|
||||
LOG(INFO) << "bar";
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, seccomp_tombstone) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
|
@ -1416,6 +1426,11 @@ TEST_F(CrasherTest, seccomp_tombstone) {
|
|||
static const auto dump_type = kDebuggerdTombstone;
|
||||
StartProcess(
|
||||
[]() {
|
||||
std::thread a(foo);
|
||||
std::thread b(bar);
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
|
||||
raise_debugger_signal(dump_type);
|
||||
_exit(0);
|
||||
},
|
||||
|
@ -1430,16 +1445,8 @@ TEST_F(CrasherTest, seccomp_tombstone) {
|
|||
std::string result;
|
||||
ConsumeFd(std::move(output_fd), &result);
|
||||
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
|
||||
}
|
||||
|
||||
extern "C" void foo() {
|
||||
LOG(INFO) << "foo";
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
|
||||
extern "C" void bar() {
|
||||
LOG(INFO) << "bar";
|
||||
std::this_thread::sleep_for(1s);
|
||||
ASSERT_BACKTRACE_FRAME(result, "foo");
|
||||
ASSERT_BACKTRACE_FRAME(result, "bar");
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, seccomp_backtrace) {
|
||||
|
|
|
@ -98,32 +98,6 @@ static void debuggerd_fallback_tombstone(int output_fd, int proto_fd, ucontext_t
|
|||
__linker_disable_fallback_allocator();
|
||||
}
|
||||
|
||||
static void iterate_siblings(bool (*callback)(pid_t, int), int output_fd) {
|
||||
pid_t current_tid = gettid();
|
||||
char buf[BUFSIZ];
|
||||
snprintf(buf, sizeof(buf), "/proc/%d/task", current_tid);
|
||||
DIR* dir = opendir(buf);
|
||||
|
||||
if (!dir) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
struct dirent* ent;
|
||||
while ((ent = readdir(dir))) {
|
||||
char* end;
|
||||
long tid = strtol(ent->d_name, &end, 10);
|
||||
if (end == ent->d_name || *end != '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tid != current_tid) {
|
||||
callback(tid, output_fd);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
|
||||
// Make sure the thread actually got the signal.
|
||||
struct pollfd pfd = {
|
||||
|
@ -216,21 +190,21 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
|
|||
}
|
||||
|
||||
// Only allow one thread to perform a trace at a time.
|
||||
static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
int ret = pthread_mutex_trylock(&trace_mutex);
|
||||
if (ret != 0) {
|
||||
async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
|
||||
strerror(ret));
|
||||
static std::mutex trace_mutex;
|
||||
if (!trace_mutex.try_lock()) {
|
||||
async_safe_format_log(ANDROID_LOG_INFO, "libc", "trace lock failed");
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> scoped_lock(trace_mutex, std::adopt_lock);
|
||||
|
||||
// Fetch output fd from tombstoned.
|
||||
unique_fd tombstone_socket, output_fd;
|
||||
if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, nullptr,
|
||||
kDebuggerdNativeBacktrace)) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"missing crash_dump_fallback() in selinux policy?");
|
||||
goto exit;
|
||||
return;
|
||||
}
|
||||
|
||||
dump_backtrace_header(output_fd.get());
|
||||
|
@ -239,15 +213,15 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
|
|||
debuggerd_fallback_trace(output_fd.get(), ucontext);
|
||||
|
||||
// Send a signal to all of our siblings, asking them to dump their stack.
|
||||
iterate_siblings(
|
||||
[](pid_t tid, int output_fd) {
|
||||
pid_t current_tid = gettid();
|
||||
if (!iterate_tids(current_tid, [&output_fd](pid_t tid) {
|
||||
// Use a pipe, to be able to detect situations where the thread gracefully exits before
|
||||
// receiving our signal.
|
||||
unique_fd pipe_read, pipe_write;
|
||||
if (!Pipe(&pipe_read, &pipe_write)) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
|
||||
strerror(errno));
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t expected = pack_thread_fd(-1, -1);
|
||||
|
@ -257,7 +231,7 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
|
|||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"thread %d is already outputting to fd %d?", tid, fd);
|
||||
close(sent_fd);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
siginfo_t siginfo = {};
|
||||
|
@ -269,10 +243,10 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
|
|||
if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
|
||||
tid, strerror(errno));
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = forward_output(pipe_read.get(), output_fd, tid);
|
||||
bool success = forward_output(pipe_read.get(), output_fd.get(), tid);
|
||||
if (!success) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"timeout expired while waiting for thread %d to dump", tid);
|
||||
|
@ -288,15 +262,14 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
output_fd.get());
|
||||
return;
|
||||
})) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s",
|
||||
current_tid, strerror(errno));
|
||||
}
|
||||
|
||||
dump_backtrace_footer(output_fd.get());
|
||||
tombstoned_notify_completion(tombstone_socket.get());
|
||||
|
||||
exit:
|
||||
pthread_mutex_unlock(&trace_mutex);
|
||||
}
|
||||
|
||||
static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
@ -73,22 +74,40 @@ void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_m
|
|||
|
||||
std::map<pid_t, ThreadInfo> threads;
|
||||
threads[tid] = ThreadInfo{
|
||||
.registers = std::move(regs),
|
||||
.uid = uid,
|
||||
.tid = tid,
|
||||
.thread_name = std::move(thread_name),
|
||||
.pid = pid,
|
||||
.command_line = std::move(command_line),
|
||||
.selinux_label = std::move(selinux_label),
|
||||
.siginfo = siginfo,
|
||||
.registers = std::move(regs), .uid = uid, .tid = tid, .thread_name = std::move(thread_name),
|
||||
.pid = pid, .command_line = std::move(command_line), .selinux_label = std::move(selinux_label),
|
||||
.siginfo = siginfo,
|
||||
#if defined(__aarch64__)
|
||||
// Only supported on aarch64 for now.
|
||||
.tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
|
||||
.pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
|
||||
#endif
|
||||
};
|
||||
if (pid == tid) {
|
||||
const ThreadInfo& thread = threads[pid];
|
||||
if (!iterate_tids(pid, [&threads, &thread](pid_t tid) {
|
||||
threads[tid] = ThreadInfo{
|
||||
.uid = thread.uid,
|
||||
.tid = tid,
|
||||
.pid = thread.pid,
|
||||
.command_line = thread.command_line,
|
||||
.thread_name = get_thread_name(tid),
|
||||
.tagged_addr_ctrl = thread.tagged_addr_ctrl,
|
||||
.pac_enabled_keys = thread.pac_enabled_keys,
|
||||
};
|
||||
})) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
|
||||
auto process_memory =
|
||||
unwindstack::Memory::CreateProcessMemoryCached(getpid());
|
||||
unwinder.SetProcessMemory(process_memory);
|
||||
if (!unwinder.Init()) {
|
||||
async_safe_fatal("failed to init unwinder object");
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object");
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessInfo process_info;
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
|
||||
#include <android/log.h>
|
||||
#include <bionic/macros.h>
|
||||
#include <bionic/reserved_signals.h>
|
||||
#include <log/log.h>
|
||||
#include <log/log_read.h>
|
||||
#include <log/logprint.h>
|
||||
|
@ -346,6 +347,93 @@ void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& fr
|
|||
f->set_build_id(frame.map_info->GetPrintableBuildID());
|
||||
}
|
||||
|
||||
static void dump_registers(unwindstack::Unwinder* unwinder,
|
||||
const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,
|
||||
bool memory_dump) {
|
||||
if (regs == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
unwindstack::Maps* maps = unwinder->GetMaps();
|
||||
unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
|
||||
|
||||
regs->IterateRegisters([&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
|
||||
Register r;
|
||||
r.set_name(name);
|
||||
r.set_u64(value);
|
||||
*thread.add_registers() = r;
|
||||
|
||||
if (memory_dump) {
|
||||
MemoryDump dump;
|
||||
|
||||
dump.set_register_name(name);
|
||||
std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
|
||||
if (map_info) {
|
||||
dump.set_mapping_name(map_info->name());
|
||||
}
|
||||
|
||||
constexpr size_t kNumBytesAroundRegister = 256;
|
||||
constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
|
||||
char buf[kNumBytesAroundRegister];
|
||||
uint8_t tags[kNumTagsAroundRegister];
|
||||
ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
|
||||
if (bytes == -1) {
|
||||
return;
|
||||
}
|
||||
dump.set_begin_address(value);
|
||||
dump.set_memory(buf, bytes);
|
||||
|
||||
bool has_tags = false;
|
||||
#if defined(__aarch64__)
|
||||
for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
|
||||
if (tags[i] != 0) {
|
||||
has_tags = true;
|
||||
}
|
||||
}
|
||||
#endif // defined(__aarch64__)
|
||||
|
||||
if (has_tags) {
|
||||
dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
|
||||
}
|
||||
|
||||
*thread.add_memory_dump() = std::move(dump);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void log_unwinder_error(unwindstack::Unwinder* unwinder) {
|
||||
if (unwinder->LastErrorCode() == unwindstack::ERROR_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
|
||||
unwinder->LastErrorCodeString());
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
|
||||
unwinder->LastErrorAddress());
|
||||
}
|
||||
|
||||
static void dump_thread_backtrace(unwindstack::Unwinder* unwinder, Thread& thread) {
|
||||
if (unwinder->NumFrames() == 0) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
|
||||
log_unwinder_error(unwinder);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unwinder->elf_from_memory_not_file()) {
|
||||
auto backtrace_note = thread.mutable_backtrace_note();
|
||||
*backtrace_note->Add() =
|
||||
"Function names and BuildId information is missing for some frames due";
|
||||
*backtrace_note->Add() = "to unreadable libraries. For unwinds of apps, only shared libraries";
|
||||
*backtrace_note->Add() = "found under the lib/ directory are readable.";
|
||||
*backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
|
||||
}
|
||||
unwinder->SetDisplayBuildID(true);
|
||||
for (const auto& frame : unwinder->frames()) {
|
||||
BacktraceFrame* f = thread.add_current_backtrace();
|
||||
fill_in_backtrace_frame(f, frame);
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
|
||||
const ThreadInfo& thread_info, bool memory_dump = false) {
|
||||
Thread thread;
|
||||
|
@ -355,97 +443,32 @@ static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
|
|||
thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
|
||||
thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);
|
||||
|
||||
unwindstack::Maps* maps = unwinder->GetMaps();
|
||||
unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
|
||||
|
||||
thread_info.registers->IterateRegisters(
|
||||
[&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
|
||||
Register r;
|
||||
r.set_name(name);
|
||||
r.set_u64(value);
|
||||
*thread.add_registers() = r;
|
||||
|
||||
if (memory_dump) {
|
||||
MemoryDump dump;
|
||||
|
||||
dump.set_register_name(name);
|
||||
std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
|
||||
if (map_info) {
|
||||
dump.set_mapping_name(map_info->name());
|
||||
}
|
||||
|
||||
constexpr size_t kNumBytesAroundRegister = 256;
|
||||
constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
|
||||
char buf[kNumBytesAroundRegister];
|
||||
uint8_t tags[kNumTagsAroundRegister];
|
||||
size_t start_offset = 0;
|
||||
ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
|
||||
if (bytes == -1) {
|
||||
return;
|
||||
}
|
||||
dump.set_begin_address(value);
|
||||
|
||||
if (start_offset + bytes > sizeof(buf)) {
|
||||
async_safe_fatal("dump_memory overflowed? start offset = %zu, bytes read = %zd",
|
||||
start_offset, bytes);
|
||||
}
|
||||
|
||||
dump.set_memory(buf, bytes);
|
||||
|
||||
bool has_tags = false;
|
||||
#if defined(__aarch64__)
|
||||
for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
|
||||
if (tags[i] != 0) {
|
||||
has_tags = true;
|
||||
}
|
||||
}
|
||||
#endif // defined(__aarch64__)
|
||||
|
||||
if (has_tags) {
|
||||
dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
|
||||
}
|
||||
|
||||
*thread.add_memory_dump() = std::move(dump);
|
||||
}
|
||||
});
|
||||
|
||||
std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
|
||||
unwinder->SetRegs(regs_copy.get());
|
||||
unwinder->Unwind();
|
||||
if (unwinder->NumFrames() == 0) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
|
||||
if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
|
||||
unwinder->LastErrorCodeString());
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
|
||||
unwinder->LastErrorAddress());
|
||||
if (thread_info.pid == getpid() && thread_info.pid != thread_info.tid) {
|
||||
// Fallback path for non-main thread, doing unwind from running process.
|
||||
unwindstack::ThreadUnwinder thread_unwinder(kMaxFrames, unwinder->GetMaps());
|
||||
if (!thread_unwinder.Init()) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
|
||||
"Unable to initialize ThreadUnwinder object.");
|
||||
log_unwinder_error(&thread_unwinder);
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<unwindstack::Regs> initial_regs;
|
||||
thread_unwinder.UnwindWithSignal(BIONIC_SIGNAL_BACKTRACE, thread_info.tid, &initial_regs);
|
||||
dump_registers(&thread_unwinder, initial_regs, thread, memory_dump);
|
||||
dump_thread_backtrace(&thread_unwinder, thread);
|
||||
} else {
|
||||
if (unwinder->elf_from_memory_not_file()) {
|
||||
auto backtrace_note = thread.mutable_backtrace_note();
|
||||
*backtrace_note->Add() =
|
||||
"Function names and BuildId information is missing for some frames due";
|
||||
*backtrace_note->Add() =
|
||||
"to unreadable libraries. For unwinds of apps, only shared libraries";
|
||||
*backtrace_note->Add() = "found under the lib/ directory are readable.";
|
||||
*backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
|
||||
}
|
||||
unwinder->SetDisplayBuildID(true);
|
||||
for (const auto& frame : unwinder->frames()) {
|
||||
BacktraceFrame* f = thread.add_current_backtrace();
|
||||
fill_in_backtrace_frame(f, frame);
|
||||
}
|
||||
dump_registers(unwinder, thread_info.registers, thread, memory_dump);
|
||||
std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
|
||||
unwinder->SetRegs(regs_copy.get());
|
||||
unwinder->Unwind();
|
||||
dump_thread_backtrace(unwinder, thread);
|
||||
}
|
||||
|
||||
auto& threads = *tombstone->mutable_threads();
|
||||
threads[thread_info.tid] = thread;
|
||||
}
|
||||
|
||||
static void dump_main_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
|
||||
const ThreadInfo& thread_info) {
|
||||
dump_thread(tombstone, unwinder, thread_info, true);
|
||||
}
|
||||
|
||||
static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
|
||||
unwindstack::Maps* maps = unwinder->GetMaps();
|
||||
std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
|
||||
|
@ -663,7 +686,8 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwind
|
|||
|
||||
dump_abort_message(&result, unwinder, process_info);
|
||||
|
||||
dump_main_thread(&result, unwinder, main_thread);
|
||||
// Dump the main thread, but save the memory around the registers.
|
||||
dump_thread(&result, unwinder, main_thread, /* memory_dump */ true);
|
||||
|
||||
for (const auto& [tid, thread_info] : threads) {
|
||||
if (tid != target_thread) {
|
||||
|
|
|
@ -25,7 +25,7 @@ tgkill: 1
|
|||
rt_sigprocmask: 1
|
||||
rt_sigaction: 1
|
||||
rt_tgsigqueueinfo: 1
|
||||
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
|
||||
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
|
||||
madvise: 1
|
||||
mprotect: arg2 in 0x1|0x2
|
||||
munmap: 1
|
||||
|
|
|
@ -37,7 +37,7 @@ rt_tgsigqueueinfo: 1
|
|||
#define PR_SET_VMA 0x53564d41
|
||||
#if defined(__aarch64__)
|
||||
// PR_PAC_RESET_KEYS happens on aarch64 in pthread_create path.
|
||||
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS
|
||||
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
|
||||
#else
|
||||
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA
|
||||
#endif
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <time.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
|
@ -74,3 +75,24 @@ std::string get_timestamp() {
|
|||
n = strftime(s, sz, "%z", &tm), s += n, sz -= n;
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool iterate_tids(pid_t pid, std::function<void(pid_t)> callback) {
|
||||
char buf[BUFSIZ];
|
||||
snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
|
||||
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(buf), closedir);
|
||||
if (dir == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir.get())) != nullptr) {
|
||||
pid_t tid = atoi(entry->d_name);
|
||||
if (tid == 0) {
|
||||
continue;
|
||||
}
|
||||
if (pid != tid) {
|
||||
callback(tid);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -27,3 +28,4 @@ std::string get_process_name(pid_t pid);
|
|||
std::string get_thread_name(pid_t tid);
|
||||
|
||||
std::string get_timestamp();
|
||||
bool iterate_tids(pid_t, std::function<void(pid_t)>);
|
||||
|
|
Loading…
Reference in a new issue