diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp @@ -482,9 +482,12 @@ typedef user_regs_struct regs_struct; #if defined(__i386__) #define REG_SP esp +static constexpr uptr kExtraRegs[] = {NT_X86_XSTATE, NT_PRFPXREG}; #else #define REG_SP rsp +static constexpr uptr kExtraRegs[] = {NT_X86_XSTATE, NT_FPREGSET}; #endif +#define ARCH_IOVEC_FOR_GETREGSET #elif defined(__powerpc__) || defined(__powerpc64__) typedef pt_regs regs_struct; @@ -501,11 +504,13 @@ #elif defined(__aarch64__) typedef struct user_pt_regs regs_struct; #define REG_SP sp +static constexpr uptr kExtraRegs[] = {}; #define ARCH_IOVEC_FOR_GETREGSET #elif defined(__s390__) typedef _user_regs_struct regs_struct; #define REG_SP gprs[15] +static constexpr uptr kExtraRegs[] = {}; #define ARCH_IOVEC_FOR_GETREGSET #else @@ -532,25 +537,53 @@ thread_ids_.push_back(tid); } +static bool AppendRegisters(pid_t tid, uptr regset, + InternalMmapVector *buffer, int *pterrno) { + struct iovec regset_io; + uptr size = buffer->size(); + for (;; buffer->resize(buffer->capacity() * 2)) { + buffer->resize(buffer->capacity()); + regset_io.iov_base = buffer->data() + size; + regset_io.iov_len = (buffer->size() - size) * sizeof(uptr); + bool isErr = + internal_iserror(internal_ptrace(PTRACE_GETREGSET, tid, (void *)regset, + (void *)®set_io), + pterrno); + if (isErr) { + buffer->resize(size); + return false; + } + + // Far enough from the buffer size, no need to resize and repeat. + if (regset_io.iov_len + 64 < buffer->size() * sizeof(uptr)) + break; + } + buffer->resize(RoundUpTo(regset_io.iov_len, sizeof(uptr)) / sizeof(uptr)); + return true; +} + PtraceRegistersStatus SuspendedThreadsListLinux::GetRegistersAndSP( uptr index, InternalMmapVector *buffer, uptr *sp) const { pid_t tid = GetThreadID(index); - regs_struct regs; int pterrno; #ifdef ARCH_IOVEC_FOR_GETREGSET - struct iovec regset_io; - regset_io.iov_base = ®s; - regset_io.iov_len = sizeof(regs_struct); - bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGSET, tid, - (void*)NT_PRSTATUS, (void*)®set_io), - &pterrno); + buffer->clear(); + buffer->reserve(Max(1024, buffer->capacity())); + bool isErr = !AppendRegisters(tid, NT_PRSTATUS, buffer, &pterrno); + if (!isErr) { + // Accept the first available and do not report errors. + for (uptr regs : kExtraRegs) + if (AppendRegisters(tid, regs, buffer, &pterrno)) + break; + } #else - bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, nullptr, - ®s), &pterrno); + buffer->resize(RoundUpTo(sizeof(regs_struct), sizeof(uptr)) / sizeof(uptr)); + bool isErr = internal_iserror( + internal_ptrace(PTRACE_GETREGS, tid, nullptr, buffer->data()), &pterrno); #endif if (isErr) { - VReport(1, "Could not get registers from thread %d (errno %d).\n", tid, - pterrno); + Report("Could not get registers from thread %d (errno %d).\n", tid, + pterrno); // ESRCH means that the given thread is not suspended or already dead. // Therefore it's unsafe to inspect its data (e.g. walk through stack) and // we should notify caller about this. @@ -558,9 +591,7 @@ : REGISTERS_UNAVAILABLE; } - *sp = regs.REG_SP; - buffer->resize(RoundUpTo(sizeof(regs), sizeof(uptr)) / sizeof(uptr)); - internal_memcpy(buffer->data(), ®s, sizeof(regs)); + *sp = reinterpret_cast(buffer->data())[0].REG_SP; return REGISTERS_AVAILABLE; }