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 @@ -485,6 +485,9 @@ #else #define REG_SP rsp #endif +#define ARCH_IOVEC_FOR_GETREGSET +// Compiler may use FP registers to store pointers. +static constexpr uptr kExtraRegs[] = {NT_X86_XSTATE, NT_FPREGSET}; #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,57 @@ thread_ids_.push_back(tid); } +static bool AppendRegisters(pid_t tid, uptr regset, + InternalMmapVector *buffer, int *pterrno) { + const uptr uptr_sz = sizeof(uptr); + uptr size = buffer->size(); + // NT_X86_XSTATE requires 64bit alignment. + uptr size_up = RoundUpTo(size, 64 / uptr_sz); + buffer->reserve(Max(1024, size_up)); + struct iovec regset_io; + for (;; buffer->resize(buffer->capacity() * 2)) { + buffer->resize(buffer->capacity()); + uptr available_bytes = (buffer->size() - size_up) * uptr_sz; + regset_io.iov_base = buffer->data() + size_up; + regset_io.iov_len = available_bytes; + bool isErr = + internal_iserror(internal_ptrace(PTRACE_GETREGSET, tid, (void *)regset, + (void *)®set_io), + pterrno); + if (isErr) { + VReport(1, "Could not get regset %p from thread %d (errno %d).\n", regset, + tid, *pterrno); + buffer->resize(size); + return false; + } + + // Far enough from the buffer size, no need to resize and repeat. + if (regset_io.iov_len + 64 < available_bytes) + break; + } + buffer->resize(size_up + RoundUpTo(regset_io.iov_len, uptr_sz) / uptr_sz); + 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(); + 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); // 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 +595,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; } diff --git a/compiler-rt/test/lsan/TestCases/use_registers.cpp b/compiler-rt/test/lsan/TestCases/use_registers.cpp --- a/compiler-rt/test/lsan/TestCases/use_registers.cpp +++ b/compiler-rt/test/lsan/TestCases/use_registers.cpp @@ -16,6 +16,9 @@ void *registers_thread_func(void *arg) { int *sync = reinterpret_cast(arg); void *p = malloc(1337); + print_address("Test alloc: ", 1, p); + fflush(stderr); + // To store the pointer, choose a register which is unlikely to be reused by // a function call. #if defined(__i386__) @@ -50,8 +53,6 @@ #else #error "Test is not supported on this architecture." #endif - print_address("Test alloc: ", 1, p); - fflush(stderr); __sync_fetch_and_xor(sync, 1); while (true) sched_yield(); diff --git a/compiler-rt/test/lsan/TestCases/use_registers.cpp b/compiler-rt/test/lsan/TestCases/use_registers_extra.cpp copy from compiler-rt/test/lsan/TestCases/use_registers.cpp copy to compiler-rt/test/lsan/TestCases/use_registers_extra.cpp --- a/compiler-rt/test/lsan/TestCases/use_registers.cpp +++ b/compiler-rt/test/lsan/TestCases/use_registers_extra.cpp @@ -5,56 +5,49 @@ // RUN: %env_lsan_opts=$LSAN_BASE:"use_registers=1" %run %t 2>&1 // RUN: %env_lsan_opts="" %run %t 2>&1 +// FIXME: Support more platforms. +// REQUIRES: x86-target-arch + +#include "sanitizer_common/print_address.h" #include #include #include #include #include -#include "sanitizer_common/print_address.h" -extern "C" -void *registers_thread_func(void *arg) { +int c; + +extern "C" void *registers_thread_func(void *arg) { int *sync = reinterpret_cast(arg); void *p = malloc(1337); + print_address("p: ", 1, p); + fflush(stderr); + // To store the pointer, choose a register which is unlikely to be reused by // a function call. #if defined(__i386__) - asm ( "mov %0, %%esi" + asm(R"( + mov %0, %%ecx + movd %%ecx, %%xmm0 + mov $0, %%ecx + )" : - : "r" (p) - ); + : "r"(p)); #elif defined(__x86_64__) - asm ( "mov %0, %%r15" - : - : "r" (p) - ); -#elif defined(__mips__) - asm ( "move $16, %0" - : - : "r" (p) - ); -#elif defined(__arm__) - asm ( "mov r5, %0" - : - : "r" (p) - ); -#elif defined(__powerpc__) - asm ( "mr 30, %0" - : - : "r" (p) - ); -#elif defined(__s390x__) - asm("lgr %%r10, %0" + asm(R"( + mov %0, %%rax + movq %%rax, %%xmm0 + mov $0, %%rax + )" : : "r"(p)); #else #error "Test is not supported on this architecture." #endif - print_address("Test alloc: ", 1, p); - fflush(stderr); + __sync_fetch_and_xor(sync, 1); while (true) - sched_yield(); + ++c; } int main() { @@ -66,7 +59,7 @@ sched_yield(); return 0; } -// CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] +// CHECK: p: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: