Add full support for initing registers.
- Fixes a few bugs in untested functionality. - Add tests for the way the register handling code is used. - Fix a few tests that were not reaping child processes. Bug: 23762183 Test: Ran unit tests on host (32 bit and 64 bit). Test: Ran unit tests on angler (32 bit and 64 bit). Change-Id: I573d6617b4f1561f6e8494d7213c52086d112d97
This commit is contained in:
parent
896297b2ef
commit
2a25c4aab5
13 changed files with 878 additions and 45 deletions
|
@ -64,6 +64,15 @@ cc_defaults {
|
|||
"Symbols.cpp",
|
||||
],
|
||||
|
||||
arch: {
|
||||
x86: {
|
||||
srcs: ["AsmGetRegsX86.S"],
|
||||
},
|
||||
x86_64: {
|
||||
srcs: ["AsmGetRegsX86_64.S"],
|
||||
},
|
||||
},
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
|
@ -112,6 +121,7 @@ cc_defaults {
|
|||
"tests/MemoryTest.cpp",
|
||||
"tests/RegsTest.cpp",
|
||||
"tests/SymbolsTest.cpp",
|
||||
"tests/UnwindTest.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
|
|
62
libunwindstack/AsmGetRegsX86.S
Normal file
62
libunwindstack/AsmGetRegsX86.S
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
.text
|
||||
.global AsmGetRegs
|
||||
.balign 16
|
||||
.type AsmGetRegs, @function
|
||||
AsmGetRegs:
|
||||
.cfi_startproc
|
||||
mov 4(%esp), %eax
|
||||
movl $0, (%eax)
|
||||
movl %ecx, 4(%eax)
|
||||
movl %edx, 8(%eax)
|
||||
movl %ebx, 12(%eax)
|
||||
|
||||
/* ESP */
|
||||
leal 4(%esp), %ecx
|
||||
movl %ecx, 16(%eax)
|
||||
|
||||
movl %ebp, 20(%eax)
|
||||
movl %esi, 24(%eax)
|
||||
movl %edi, 28(%eax)
|
||||
|
||||
/* EIP */
|
||||
movl (%esp), %ecx
|
||||
movl %ecx, 32(%eax)
|
||||
|
||||
movl %cs, 36(%eax)
|
||||
movl %ss, 40(%eax)
|
||||
movl %ds, 44(%eax)
|
||||
movl %es, 48(%eax)
|
||||
movl %fs, 52(%eax)
|
||||
movl %gs, 56(%eax)
|
||||
ret
|
||||
|
||||
.cfi_endproc
|
||||
.size AsmGetRegs, .-AsmGetRegs
|
62
libunwindstack/AsmGetRegsX86_64.S
Normal file
62
libunwindstack/AsmGetRegsX86_64.S
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
.text
|
||||
.global AsmGetRegs
|
||||
.balign 16
|
||||
.type AsmGetRegs, @function
|
||||
AsmGetRegs:
|
||||
.cfi_startproc
|
||||
movq %rax, (%rdi)
|
||||
movq %rdx, 8(%rdi)
|
||||
movq %rcx, 16(%rdi)
|
||||
movq %rbx, 24(%rdi)
|
||||
movq %rsi, 32(%rdi)
|
||||
movq %rdi, 40(%rdi)
|
||||
movq %rbp, 48(%rdi)
|
||||
|
||||
/* RSP */
|
||||
lea 8(%rsp), %rax
|
||||
movq %rax, 56(%rdi)
|
||||
|
||||
movq %r8, 64(%rdi)
|
||||
movq %r9, 72(%rdi)
|
||||
movq %r10, 80(%rdi)
|
||||
movq %r11, 88(%rdi)
|
||||
movq %r12, 96(%rdi)
|
||||
movq %r13, 104(%rdi)
|
||||
movq %r14, 112(%rdi)
|
||||
movq %r15, 120(%rdi)
|
||||
|
||||
/* RIP */
|
||||
movq (%rsp), %rax
|
||||
movq %rax, 128(%rdi)
|
||||
ret
|
||||
|
||||
.cfi_endproc
|
||||
.size AsmGetRegs, .-AsmGetRegs
|
|
@ -83,47 +83,51 @@ enum Arm64Reg : uint16_t {
|
|||
ARM64_REG_LR = ARM64_REG_R30,
|
||||
};
|
||||
|
||||
// Matches the numbers for the registers as generated by compilers.
|
||||
// If this is changed, then unwinding will fail.
|
||||
enum X86Reg : uint16_t {
|
||||
X86_REG_EAX = 0,
|
||||
X86_REG_ECX,
|
||||
X86_REG_EDX,
|
||||
X86_REG_EBX,
|
||||
X86_REG_ESP,
|
||||
X86_REG_EBP,
|
||||
X86_REG_ESI,
|
||||
X86_REG_EDI,
|
||||
X86_REG_EIP,
|
||||
X86_REG_EFL,
|
||||
X86_REG_CS,
|
||||
X86_REG_SS,
|
||||
X86_REG_DS,
|
||||
X86_REG_ES,
|
||||
X86_REG_FS,
|
||||
X86_REG_GS,
|
||||
X86_REG_ECX = 1,
|
||||
X86_REG_EDX = 2,
|
||||
X86_REG_EBX = 3,
|
||||
X86_REG_ESP = 4,
|
||||
X86_REG_EBP = 5,
|
||||
X86_REG_ESI = 6,
|
||||
X86_REG_EDI = 7,
|
||||
X86_REG_EIP = 8,
|
||||
X86_REG_EFL = 9,
|
||||
X86_REG_CS = 10,
|
||||
X86_REG_SS = 11,
|
||||
X86_REG_DS = 12,
|
||||
X86_REG_ES = 13,
|
||||
X86_REG_FS = 14,
|
||||
X86_REG_GS = 15,
|
||||
X86_REG_LAST,
|
||||
|
||||
X86_REG_SP = X86_REG_ESP,
|
||||
X86_REG_PC = X86_REG_EIP,
|
||||
};
|
||||
|
||||
// Matches the numbers for the registers as generated by compilers.
|
||||
// If this is changed, then unwinding will fail.
|
||||
enum X86_64Reg : uint16_t {
|
||||
X86_64_REG_RAX = 0,
|
||||
X86_64_REG_RDX,
|
||||
X86_64_REG_RCX,
|
||||
X86_64_REG_RBX,
|
||||
X86_64_REG_RSI,
|
||||
X86_64_REG_RDI,
|
||||
X86_64_REG_RBP,
|
||||
X86_64_REG_RSP,
|
||||
X86_64_REG_R8,
|
||||
X86_64_REG_R9,
|
||||
X86_64_REG_R10,
|
||||
X86_64_REG_R11,
|
||||
X86_64_REG_R12,
|
||||
X86_64_REG_R13,
|
||||
X86_64_REG_R14,
|
||||
X86_64_REG_R15,
|
||||
X86_64_REG_RIP,
|
||||
X86_64_REG_RDX = 1,
|
||||
X86_64_REG_RCX = 2,
|
||||
X86_64_REG_RBX = 3,
|
||||
X86_64_REG_RSI = 4,
|
||||
X86_64_REG_RDI = 5,
|
||||
X86_64_REG_RBP = 6,
|
||||
X86_64_REG_RSP = 7,
|
||||
X86_64_REG_R8 = 8,
|
||||
X86_64_REG_R9 = 9,
|
||||
X86_64_REG_R10 = 10,
|
||||
X86_64_REG_R11 = 11,
|
||||
X86_64_REG_R12 = 12,
|
||||
X86_64_REG_R13 = 13,
|
||||
X86_64_REG_R14 = 14,
|
||||
X86_64_REG_R15 = 15,
|
||||
X86_64_REG_RIP = 16,
|
||||
X86_64_REG_LAST,
|
||||
|
||||
X86_64_REG_SP = X86_64_REG_RSP,
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "Machine.h"
|
||||
#include "MapInfo.h"
|
||||
#include "Regs.h"
|
||||
#include "Ucontext.h"
|
||||
#include "User.h"
|
||||
|
||||
template <typename AddressType>
|
||||
|
@ -88,6 +89,11 @@ uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
|
|||
return rel_pc - 4;
|
||||
}
|
||||
|
||||
void RegsArm::SetFromRaw() {
|
||||
set_pc(regs_[ARM_REG_PC]);
|
||||
set_sp(regs_[ARM_REG_SP]);
|
||||
}
|
||||
|
||||
RegsArm64::RegsArm64()
|
||||
: RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
|
||||
|
||||
|
@ -102,6 +108,11 @@ uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
|
|||
return rel_pc - 4;
|
||||
}
|
||||
|
||||
void RegsArm64::SetFromRaw() {
|
||||
set_pc(regs_[ARM64_REG_PC]);
|
||||
set_sp(regs_[ARM64_REG_SP]);
|
||||
}
|
||||
|
||||
RegsX86::RegsX86()
|
||||
: RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
|
||||
|
||||
|
@ -116,6 +127,11 @@ uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
|
|||
return rel_pc - 1;
|
||||
}
|
||||
|
||||
void RegsX86::SetFromRaw() {
|
||||
set_pc(regs_[X86_REG_PC]);
|
||||
set_sp(regs_[X86_REG_SP]);
|
||||
}
|
||||
|
||||
RegsX86_64::RegsX86_64()
|
||||
: RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
|
||||
|
||||
|
@ -131,15 +147,17 @@ uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
|
|||
return rel_pc - 1;
|
||||
}
|
||||
|
||||
void RegsX86_64::SetFromRaw() {
|
||||
set_pc(regs_[X86_64_REG_PC]);
|
||||
set_sp(regs_[X86_64_REG_SP]);
|
||||
}
|
||||
|
||||
static Regs* ReadArm(void* remote_data) {
|
||||
arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
|
||||
|
||||
RegsArm* regs = new RegsArm();
|
||||
memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
|
||||
|
||||
regs->set_pc(user->regs[ARM_REG_PC]);
|
||||
regs->set_sp(user->regs[ARM_REG_SP]);
|
||||
|
||||
regs->SetFromRaw();
|
||||
return regs;
|
||||
}
|
||||
|
||||
|
@ -148,9 +166,10 @@ static Regs* ReadArm64(void* remote_data) {
|
|||
|
||||
RegsArm64* regs = new RegsArm64();
|
||||
memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
|
||||
regs->set_pc(user->pc);
|
||||
regs->set_sp(user->sp);
|
||||
|
||||
uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
|
||||
reg_data[ARM64_REG_PC] = user->pc;
|
||||
reg_data[ARM64_REG_SP] = user->sp;
|
||||
regs->SetFromRaw();
|
||||
return regs;
|
||||
}
|
||||
|
||||
|
@ -168,9 +187,7 @@ static Regs* ReadX86(void* remote_data) {
|
|||
(*regs)[X86_REG_ESP] = user->esp;
|
||||
(*regs)[X86_REG_EIP] = user->eip;
|
||||
|
||||
regs->set_pc(user->eip);
|
||||
regs->set_sp(user->esp);
|
||||
|
||||
regs->SetFromRaw();
|
||||
return regs;
|
||||
}
|
||||
|
||||
|
@ -196,9 +213,7 @@ static Regs* ReadX86_64(void* remote_data) {
|
|||
(*regs)[X86_64_REG_RSP] = user->rsp;
|
||||
(*regs)[X86_64_REG_RIP] = user->rip;
|
||||
|
||||
regs->set_pc(user->rip);
|
||||
regs->set_sp(user->rsp);
|
||||
|
||||
regs->SetFromRaw();
|
||||
return regs;
|
||||
}
|
||||
|
||||
|
@ -231,3 +246,111 @@ Regs* Regs::RemoteGet(pid_t pid, uint32_t* machine_type) {
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Regs* CreateFromArmUcontext(void* ucontext) {
|
||||
arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext);
|
||||
|
||||
RegsArm* regs = new RegsArm();
|
||||
memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
|
||||
regs->SetFromRaw();
|
||||
return regs;
|
||||
}
|
||||
|
||||
static Regs* CreateFromArm64Ucontext(void* ucontext) {
|
||||
arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext);
|
||||
|
||||
RegsArm64* regs = new RegsArm64();
|
||||
memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
|
||||
regs->SetFromRaw();
|
||||
return regs;
|
||||
}
|
||||
|
||||
static Regs* CreateFromX86Ucontext(void* ucontext) {
|
||||
x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
|
||||
|
||||
RegsX86* regs = new RegsX86();
|
||||
// Put the registers in the expected order.
|
||||
(*regs)[X86_REG_GS] = x86_ucontext->uc_mcontext.gs;
|
||||
(*regs)[X86_REG_FS] = x86_ucontext->uc_mcontext.fs;
|
||||
(*regs)[X86_REG_ES] = x86_ucontext->uc_mcontext.es;
|
||||
(*regs)[X86_REG_DS] = x86_ucontext->uc_mcontext.ds;
|
||||
(*regs)[X86_REG_EDI] = x86_ucontext->uc_mcontext.edi;
|
||||
(*regs)[X86_REG_ESI] = x86_ucontext->uc_mcontext.esi;
|
||||
(*regs)[X86_REG_EBP] = x86_ucontext->uc_mcontext.ebp;
|
||||
(*regs)[X86_REG_ESP] = x86_ucontext->uc_mcontext.esp;
|
||||
(*regs)[X86_REG_EBX] = x86_ucontext->uc_mcontext.ebx;
|
||||
(*regs)[X86_REG_EDX] = x86_ucontext->uc_mcontext.edx;
|
||||
(*regs)[X86_REG_ECX] = x86_ucontext->uc_mcontext.ecx;
|
||||
(*regs)[X86_REG_EAX] = x86_ucontext->uc_mcontext.eax;
|
||||
(*regs)[X86_REG_EIP] = x86_ucontext->uc_mcontext.eip;
|
||||
regs->SetFromRaw();
|
||||
return regs;
|
||||
}
|
||||
|
||||
static Regs* CreateFromX86_64Ucontext(void* ucontext) {
|
||||
x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
|
||||
|
||||
RegsX86_64* regs = new RegsX86_64();
|
||||
// Put the registers in the expected order.
|
||||
|
||||
// R8-R15
|
||||
memcpy(&(*regs)[X86_64_REG_R8], &x86_64_ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
|
||||
|
||||
// Rest of the registers.
|
||||
(*regs)[X86_64_REG_RDI] = x86_64_ucontext->uc_mcontext.rdi;
|
||||
(*regs)[X86_64_REG_RSI] = x86_64_ucontext->uc_mcontext.rsi;
|
||||
(*regs)[X86_64_REG_RBP] = x86_64_ucontext->uc_mcontext.rbp;
|
||||
(*regs)[X86_64_REG_RBX] = x86_64_ucontext->uc_mcontext.rbx;
|
||||
(*regs)[X86_64_REG_RDX] = x86_64_ucontext->uc_mcontext.rdx;
|
||||
(*regs)[X86_64_REG_RAX] = x86_64_ucontext->uc_mcontext.rax;
|
||||
(*regs)[X86_64_REG_RCX] = x86_64_ucontext->uc_mcontext.rcx;
|
||||
(*regs)[X86_64_REG_RSP] = x86_64_ucontext->uc_mcontext.rsp;
|
||||
(*regs)[X86_64_REG_RIP] = x86_64_ucontext->uc_mcontext.rip;
|
||||
|
||||
regs->SetFromRaw();
|
||||
return regs;
|
||||
}
|
||||
|
||||
Regs* Regs::CreateFromUcontext(uint32_t machine_type, void* ucontext) {
|
||||
switch (machine_type) {
|
||||
case EM_386:
|
||||
return CreateFromX86Ucontext(ucontext);
|
||||
case EM_X86_64:
|
||||
return CreateFromX86_64Ucontext(ucontext);
|
||||
case EM_ARM:
|
||||
return CreateFromArmUcontext(ucontext);
|
||||
case EM_AARCH64:
|
||||
return CreateFromArm64Ucontext(ucontext);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t Regs::GetMachineType() {
|
||||
#if defined(__arm__)
|
||||
return EM_ARM;
|
||||
#elif defined(__aarch64__)
|
||||
return EM_AARCH64;
|
||||
#elif defined(__i386__)
|
||||
return EM_386;
|
||||
#elif defined(__x86_64__)
|
||||
return EM_X86_64;
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
Regs* Regs::CreateFromLocal() {
|
||||
Regs* regs;
|
||||
#if defined(__arm__)
|
||||
regs = new RegsArm();
|
||||
#elif defined(__aarch64__)
|
||||
regs = new RegsArm64();
|
||||
#elif defined(__i386__)
|
||||
regs = new RegsX86();
|
||||
#elif defined(__x86_64__)
|
||||
regs = new RegsX86_64();
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
return regs;
|
||||
}
|
||||
|
|
|
@ -54,10 +54,15 @@ class Regs {
|
|||
|
||||
virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
|
||||
|
||||
virtual void SetFromRaw() = 0;
|
||||
|
||||
uint16_t sp_reg() { return sp_reg_; }
|
||||
uint16_t total_regs() { return total_regs_; }
|
||||
|
||||
static uint32_t GetMachineType();
|
||||
static Regs* RemoteGet(pid_t pid, uint32_t* machine_type);
|
||||
static Regs* CreateFromUcontext(uint32_t machine_type, void* ucontext);
|
||||
static Regs* CreateFromLocal();
|
||||
|
||||
protected:
|
||||
uint16_t total_regs_;
|
||||
|
@ -98,6 +103,8 @@ class RegsArm : public RegsImpl<uint32_t> {
|
|||
virtual ~RegsArm() = default;
|
||||
|
||||
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
|
||||
|
||||
void SetFromRaw() override;
|
||||
};
|
||||
|
||||
class RegsArm64 : public RegsImpl<uint64_t> {
|
||||
|
@ -106,6 +113,8 @@ class RegsArm64 : public RegsImpl<uint64_t> {
|
|||
virtual ~RegsArm64() = default;
|
||||
|
||||
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
|
||||
|
||||
void SetFromRaw() override;
|
||||
};
|
||||
|
||||
class RegsX86 : public RegsImpl<uint32_t> {
|
||||
|
@ -114,6 +123,8 @@ class RegsX86 : public RegsImpl<uint32_t> {
|
|||
virtual ~RegsX86() = default;
|
||||
|
||||
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
|
||||
|
||||
void SetFromRaw() override;
|
||||
};
|
||||
|
||||
class RegsX86_64 : public RegsImpl<uint64_t> {
|
||||
|
@ -122,6 +133,8 @@ class RegsX86_64 : public RegsImpl<uint64_t> {
|
|||
virtual ~RegsX86_64() = default;
|
||||
|
||||
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
|
||||
|
||||
void SetFromRaw() override;
|
||||
};
|
||||
|
||||
#endif // _LIBUNWINDSTACK_REGS_H
|
||||
|
|
100
libunwindstack/RegsGetLocal.h
Normal file
100
libunwindstack/RegsGetLocal.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_REGS_GET_LOCAL_H
|
||||
#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H
|
||||
|
||||
#if defined(__arm__)
|
||||
|
||||
inline void RegsGetLocal(Regs* regs) {
|
||||
void* reg_data = regs->RawData();
|
||||
asm volatile(
|
||||
".align 2\n"
|
||||
"bx pc\n"
|
||||
"nop\n"
|
||||
".code 32\n"
|
||||
"stmia %[base], {r0-r12}\n"
|
||||
"add %[base], #52\n"
|
||||
"mov r1, r13\n"
|
||||
"mov r2, r14\n"
|
||||
"mov r3, r15\n"
|
||||
"stmia %[base], {r1-r3}\n"
|
||||
"orr %[base], pc, #1\n"
|
||||
"bx %[base]\n"
|
||||
: [base] "+r"(reg_data)
|
||||
:
|
||||
: "memory");
|
||||
|
||||
regs->SetFromRaw();
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
inline void RegsGetLocal(Regs* regs) {
|
||||
void* reg_data = regs->RawData();
|
||||
asm volatile(
|
||||
"1:\n"
|
||||
"stp x0, x1, [%[base], #0]\n"
|
||||
"stp x2, x3, [%[base], #16]\n"
|
||||
"stp x4, x5, [%[base], #32]\n"
|
||||
"stp x6, x7, [%[base], #48]\n"
|
||||
"stp x8, x9, [%[base], #64]\n"
|
||||
"stp x10, x11, [%[base], #80]\n"
|
||||
"stp x12, x13, [%[base], #96]\n"
|
||||
"stp x14, x15, [%[base], #112]\n"
|
||||
"stp x16, x17, [%[base], #128]\n"
|
||||
"stp x18, x19, [%[base], #144]\n"
|
||||
"stp x20, x21, [%[base], #160]\n"
|
||||
"stp x22, x23, [%[base], #176]\n"
|
||||
"stp x24, x25, [%[base], #192]\n"
|
||||
"stp x26, x27, [%[base], #208]\n"
|
||||
"stp x28, x29, [%[base], #224]\n"
|
||||
"str x30, [%[base], #240]\n"
|
||||
"mov x12, sp\n"
|
||||
"adr x13, 1b\n"
|
||||
"stp x12, x13, [%[base], #248]\n"
|
||||
: [base] "+r"(reg_data)
|
||||
:
|
||||
: "x12", "x13", "memory");
|
||||
|
||||
regs->SetFromRaw();
|
||||
}
|
||||
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
extern "C" void AsmGetRegs(void* regs);
|
||||
|
||||
inline void RegsGetLocal(Regs* regs) {
|
||||
AsmGetRegs(regs->RawData());
|
||||
|
||||
regs->SetFromRaw();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _LIBUNWINDSTACK_REGS_GET_LOCAL_H
|
180
libunwindstack/Ucontext.h
Normal file
180
libunwindstack/Ucontext.h
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_UCONTEXT_H
|
||||
#define _LIBUNWINDSTACK_UCONTEXT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// ARM ucontext structures
|
||||
//-------------------------------------------------------------------
|
||||
struct arm_stack_t {
|
||||
uint32_t ss_sp; // void __user*
|
||||
int32_t ss_flags; // int
|
||||
uint32_t ss_size; // size_t
|
||||
};
|
||||
|
||||
struct arm_mcontext_t {
|
||||
uint32_t trap_no; // unsigned long
|
||||
uint32_t error_code; // unsigned long
|
||||
uint32_t oldmask; // unsigned long
|
||||
uint32_t regs[ARM_REG_LAST]; // unsigned long
|
||||
uint32_t cpsr; // unsigned long
|
||||
uint32_t fault_address; // unsigned long
|
||||
};
|
||||
|
||||
struct arm_ucontext_t {
|
||||
uint32_t uc_flags; // unsigned long
|
||||
uint32_t uc_link; // struct ucontext*
|
||||
arm_stack_t uc_stack;
|
||||
arm_mcontext_t uc_mcontext;
|
||||
// Nothing else is used, so don't define it.
|
||||
};
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// ARM64 ucontext structures
|
||||
//-------------------------------------------------------------------
|
||||
struct arm64_stack_t {
|
||||
uint64_t ss_sp; // void __user*
|
||||
int32_t ss_flags; // int
|
||||
uint64_t ss_size; // size_t
|
||||
};
|
||||
|
||||
struct arm64_sigset_t {
|
||||
uint64_t sig; // unsigned long
|
||||
};
|
||||
|
||||
struct arm64_mcontext_t {
|
||||
uint64_t fault_address; // __u64
|
||||
uint64_t regs[ARM64_REG_LAST]; // __u64
|
||||
uint64_t pstate; // __u64
|
||||
// Nothing else is used, so don't define it.
|
||||
};
|
||||
|
||||
struct arm64_ucontext_t {
|
||||
uint64_t uc_flags; // unsigned long
|
||||
uint64_t uc_link; // struct ucontext*
|
||||
arm64_stack_t uc_stack;
|
||||
arm64_sigset_t uc_sigmask;
|
||||
// The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64.
|
||||
char __padding[128 - sizeof(arm64_sigset_t)];
|
||||
// The full structure requires 16 byte alignment, but our partial structure
|
||||
// doesn't, so force the alignment.
|
||||
arm64_mcontext_t uc_mcontext __attribute__((aligned(16)));
|
||||
};
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// X86 ucontext structures
|
||||
//-------------------------------------------------------------------
|
||||
struct x86_stack_t {
|
||||
uint32_t ss_sp; // void __user*
|
||||
int32_t ss_flags; // int
|
||||
uint32_t ss_size; // size_t
|
||||
};
|
||||
|
||||
struct x86_mcontext_t {
|
||||
uint32_t gs;
|
||||
uint32_t fs;
|
||||
uint32_t es;
|
||||
uint32_t ds;
|
||||
uint32_t edi;
|
||||
uint32_t esi;
|
||||
uint32_t ebp;
|
||||
uint32_t esp;
|
||||
uint32_t ebx;
|
||||
uint32_t edx;
|
||||
uint32_t ecx;
|
||||
uint32_t eax;
|
||||
uint32_t trapno;
|
||||
uint32_t err;
|
||||
uint32_t eip;
|
||||
uint32_t cs;
|
||||
uint32_t efl;
|
||||
uint32_t uesp;
|
||||
uint32_t ss;
|
||||
// Only care about the registers, skip everything else.
|
||||
};
|
||||
|
||||
struct x86_ucontext_t {
|
||||
uint32_t uc_flags; // unsigned long
|
||||
uint32_t uc_link; // struct ucontext*
|
||||
x86_stack_t uc_stack;
|
||||
x86_mcontext_t uc_mcontext;
|
||||
// Nothing else is used, so don't define it.
|
||||
};
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// X86_64 ucontext structures
|
||||
//-------------------------------------------------------------------
|
||||
struct x86_64_stack_t {
|
||||
uint64_t ss_sp; // void __user*
|
||||
int32_t ss_flags; // int
|
||||
uint64_t ss_size; // size_t
|
||||
};
|
||||
|
||||
struct x86_64_mcontext_t {
|
||||
uint64_t r8;
|
||||
uint64_t r9;
|
||||
uint64_t r10;
|
||||
uint64_t r11;
|
||||
uint64_t r12;
|
||||
uint64_t r13;
|
||||
uint64_t r14;
|
||||
uint64_t r15;
|
||||
uint64_t rdi;
|
||||
uint64_t rsi;
|
||||
uint64_t rbp;
|
||||
uint64_t rbx;
|
||||
uint64_t rdx;
|
||||
uint64_t rax;
|
||||
uint64_t rcx;
|
||||
uint64_t rsp;
|
||||
uint64_t rip;
|
||||
uint64_t efl;
|
||||
uint64_t csgsfs;
|
||||
uint64_t err;
|
||||
uint64_t trapno;
|
||||
uint64_t oldmask;
|
||||
uint64_t cr2;
|
||||
// Only care about the registers, skip everything else.
|
||||
};
|
||||
|
||||
typedef struct x86_64_ucontext {
|
||||
uint64_t uc_flags; // unsigned long
|
||||
uint64_t uc_link; // struct ucontext*
|
||||
x86_64_stack_t uc_stack;
|
||||
x86_64_mcontext_t uc_mcontext;
|
||||
// Nothing else is used, so don't define it.
|
||||
} x86_64_ucontext_t;
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
#endif // _LIBUNWINDSTACK_UCONTEXT_H
|
|
@ -201,4 +201,5 @@ TEST_F(MapInfoCreateMemoryTest, remote_memory) {
|
|||
ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
ASSERT_EQ(pid, wait(nullptr));
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ TEST_F(MemoryRemoteTest, read) {
|
|||
ASSERT_TRUE(Detach(pid));
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
ASSERT_EQ(pid, wait(nullptr));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRemoteTest, read_fail) {
|
||||
|
@ -131,6 +132,7 @@ TEST_F(MemoryRemoteTest, read_fail) {
|
|||
ASSERT_TRUE(Detach(pid));
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
ASSERT_EQ(pid, wait(nullptr));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRemoteTest, read_overflow) {
|
||||
|
@ -160,4 +162,5 @@ TEST_F(MemoryRemoteTest, read_illegal) {
|
|||
ASSERT_TRUE(Detach(pid));
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
ASSERT_EQ(pid, wait(nullptr));
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ class RegsFake : public RegsImpl<TypeParam> {
|
|||
|
||||
uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
|
||||
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
|
||||
void SetFromRaw() override {}
|
||||
bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
|
||||
};
|
||||
|
||||
|
|
|
@ -56,7 +56,8 @@ class RegsTestImpl : public RegsImpl<TypeParam> {
|
|||
: RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {}
|
||||
virtual ~RegsTestImpl() = default;
|
||||
|
||||
uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; }
|
||||
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
|
||||
void SetFromRaw() override {}
|
||||
};
|
||||
|
||||
class RegsTest : public ::testing::Test {
|
||||
|
@ -264,3 +265,43 @@ TEST_F(RegsTest, elf_invalid) {
|
|||
ASSERT_EQ(0x800U, regs_x86_64.GetRelPc(&invalid_elf, &map_info));
|
||||
ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf));
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, arm_set_from_raw) {
|
||||
RegsArm arm;
|
||||
uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData());
|
||||
regs[13] = 0x100;
|
||||
regs[15] = 0x200;
|
||||
arm.SetFromRaw();
|
||||
EXPECT_EQ(0x100U, arm.sp());
|
||||
EXPECT_EQ(0x200U, arm.pc());
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, arm64_set_from_raw) {
|
||||
RegsArm64 arm64;
|
||||
uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData());
|
||||
regs[31] = 0xb100000000ULL;
|
||||
regs[32] = 0xc200000000ULL;
|
||||
arm64.SetFromRaw();
|
||||
EXPECT_EQ(0xb100000000U, arm64.sp());
|
||||
EXPECT_EQ(0xc200000000U, arm64.pc());
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, x86_set_from_raw) {
|
||||
RegsX86 x86;
|
||||
uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData());
|
||||
regs[4] = 0x23450000;
|
||||
regs[8] = 0xabcd0000;
|
||||
x86.SetFromRaw();
|
||||
EXPECT_EQ(0x23450000U, x86.sp());
|
||||
EXPECT_EQ(0xabcd0000U, x86.pc());
|
||||
}
|
||||
|
||||
TEST_F(RegsTest, x86_64_set_from_raw) {
|
||||
RegsX86_64 x86_64;
|
||||
uint64_t* regs = reinterpret_cast<uint64_t*>(x86_64.RawData());
|
||||
regs[7] = 0x1200000000ULL;
|
||||
regs[16] = 0x4900000000ULL;
|
||||
x86_64.SetFromRaw();
|
||||
EXPECT_EQ(0x1200000000U, x86_64.sp());
|
||||
EXPECT_EQ(0x4900000000U, x86_64.pc());
|
||||
}
|
||||
|
|
233
libunwindstack/tests/UnwindTest.cpp
Normal file
233
libunwindstack/tests/UnwindTest.cpp
Normal file
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "Elf.h"
|
||||
#include "MapInfo.h"
|
||||
#include "Maps.h"
|
||||
#include "Memory.h"
|
||||
#include "Regs.h"
|
||||
#include "RegsGetLocal.h"
|
||||
|
||||
static std::atomic_bool g_ready(false);
|
||||
static volatile bool g_ready_for_remote = false;
|
||||
static std::atomic_bool g_finish(false);
|
||||
static std::atomic_uintptr_t g_ucontext;
|
||||
|
||||
static void Signal(int, siginfo_t*, void* sigcontext) {
|
||||
g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
|
||||
while (!g_finish.load()) {
|
||||
}
|
||||
}
|
||||
|
||||
static std::string ErrorMsg(const char** function_names, size_t index,
|
||||
std::stringstream& unwind_stream) {
|
||||
return std::string(
|
||||
"Unwind completed without finding all frames\n"
|
||||
" Looking for function: ") +
|
||||
function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str();
|
||||
}
|
||||
|
||||
static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs) {
|
||||
const char* function_names[] = {
|
||||
"InnerFunction", "MiddleFunction", "OuterFunction",
|
||||
};
|
||||
size_t function_name_index = 0;
|
||||
|
||||
std::stringstream unwind_stream;
|
||||
unwind_stream << std::hex;
|
||||
for (size_t frame_num = 0; frame_num < 64; frame_num++) {
|
||||
ASSERT_NE(0U, regs->pc()) << ErrorMsg(function_names, function_name_index, unwind_stream);
|
||||
MapInfo* map_info = maps->Find(regs->pc());
|
||||
ASSERT_TRUE(map_info != nullptr) << ErrorMsg(function_names, function_name_index, unwind_stream);
|
||||
|
||||
Elf* elf = map_info->GetElf(pid, true);
|
||||
uint64_t rel_pc = regs->GetRelPc(elf, map_info);
|
||||
uint64_t adjusted_rel_pc = rel_pc;
|
||||
if (frame_num != 0) {
|
||||
adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
|
||||
}
|
||||
unwind_stream << " PC: 0x" << regs->pc() << " Rel: 0x" << adjusted_rel_pc;
|
||||
unwind_stream << " Map: ";
|
||||
if (!map_info->name.empty()) {
|
||||
unwind_stream << map_info->name;
|
||||
} else {
|
||||
unwind_stream << " anonymous";
|
||||
}
|
||||
unwind_stream << "<" << map_info->start << "-" << map_info->end << ">";
|
||||
|
||||
std::string name;
|
||||
uint64_t func_offset;
|
||||
if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
|
||||
if (name == function_names[function_name_index]) {
|
||||
function_name_index++;
|
||||
if (function_name_index == sizeof(function_names) / sizeof(const char*)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
unwind_stream << " " << name;
|
||||
}
|
||||
unwind_stream << "\n";
|
||||
ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, memory))
|
||||
<< ErrorMsg(function_names, function_name_index, unwind_stream);
|
||||
}
|
||||
ASSERT_TRUE(false) << ErrorMsg(function_names, function_name_index, unwind_stream);
|
||||
}
|
||||
|
||||
// This test assumes that this code is compiled with optimizations turned
|
||||
// off. If this doesn't happen, then all of the calls will be optimized
|
||||
// away.
|
||||
extern "C" void InnerFunction(bool local) {
|
||||
if (local) {
|
||||
LocalMaps maps;
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
|
||||
RegsGetLocal(regs.get());
|
||||
MemoryLocal memory;
|
||||
|
||||
VerifyUnwind(getpid(), &memory, &maps, regs.get());
|
||||
} else {
|
||||
g_ready_for_remote = true;
|
||||
g_ready = true;
|
||||
while (!g_finish.load()) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void MiddleFunction(bool local) {
|
||||
InnerFunction(local);
|
||||
}
|
||||
|
||||
extern "C" void OuterFunction(bool local) {
|
||||
MiddleFunction(local);
|
||||
}
|
||||
|
||||
TEST(UnwindTest, local) {
|
||||
OuterFunction(true);
|
||||
}
|
||||
|
||||
TEST(UnwindTest, remote) {
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
OuterFunction(false);
|
||||
exit(0);
|
||||
}
|
||||
ASSERT_NE(-1, pid);
|
||||
|
||||
bool ready = false;
|
||||
uint64_t addr = reinterpret_cast<uint64_t>(&g_ready_for_remote);
|
||||
for (size_t i = 0; i < 100; i++) {
|
||||
ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
|
||||
for (size_t j = 0; j < 100; j++) {
|
||||
siginfo_t si;
|
||||
if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
|
||||
// Check to see if process is ready to be unwound.
|
||||
MemoryRemote memory(pid);
|
||||
// Read the remote value to see if we are ready.
|
||||
bool value;
|
||||
if (memory.Read(addr, &value, sizeof(value)) && value) {
|
||||
ready = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
if (ready) {
|
||||
break;
|
||||
}
|
||||
ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
|
||||
usleep(1000);
|
||||
}
|
||||
ASSERT_TRUE(read) << "Timed out waiting for remote process to be ready.";
|
||||
|
||||
RemoteMaps maps(pid);
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
MemoryRemote memory(pid);
|
||||
uint32_t machine_type;
|
||||
std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type));
|
||||
ASSERT_TRUE(regs.get() != nullptr);
|
||||
|
||||
VerifyUnwind(pid, &memory, &maps, regs.get());
|
||||
|
||||
ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
ASSERT_EQ(pid, wait(nullptr));
|
||||
}
|
||||
|
||||
TEST(UnwindTest, from_context) {
|
||||
std::atomic_int tid(0);
|
||||
std::thread thread([&]() {
|
||||
tid = syscall(__NR_gettid);
|
||||
OuterFunction(false);
|
||||
});
|
||||
|
||||
struct sigaction act, oldact;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_sigaction = Signal;
|
||||
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
|
||||
ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
|
||||
// Wait for the tid to get set.
|
||||
for (size_t i = 0; i < 100; i++) {
|
||||
if (tid.load() != 0) {
|
||||
break;
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
ASSERT_NE(0, tid.load());
|
||||
// Portable tgkill method.
|
||||
ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Failed because "
|
||||
<< strerror(errno);
|
||||
|
||||
// Wait for context data.
|
||||
void* ucontext;
|
||||
for (size_t i = 0; i < 200; i++) {
|
||||
ucontext = reinterpret_cast<void*>(g_ucontext.load());
|
||||
if (ucontext != nullptr) {
|
||||
break;
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
ASSERT_TRUE(ucontext != nullptr) << "Timed out waiting for thread to respond to signal.";
|
||||
|
||||
LocalMaps maps;
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::GetMachineType(), ucontext));
|
||||
MemoryLocal memory;
|
||||
|
||||
VerifyUnwind(tid.load(), &memory, &maps, regs.get());
|
||||
|
||||
ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
|
||||
|
||||
g_finish = true;
|
||||
thread.join();
|
||||
}
|
Loading…
Reference in a new issue