blob: 7497b65b1c3a3d845add4781e776f5cec102f40a [file] [log] [blame]
Christopher Ferris2a25c4a2017-07-07 16:35:48 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <errno.h>
18#include <string.h>
19
20#include <signal.h>
21#include <stdint.h>
22#include <sys/ptrace.h>
23#include <sys/syscall.h>
24#include <unistd.h>
25
26#include <gtest/gtest.h>
27
28#include <atomic>
29#include <memory>
30#include <sstream>
31#include <string>
32#include <thread>
33
34#include "Elf.h"
35#include "MapInfo.h"
36#include "Maps.h"
37#include "Memory.h"
38#include "Regs.h"
39#include "RegsGetLocal.h"
40
41static std::atomic_bool g_ready(false);
42static volatile bool g_ready_for_remote = false;
43static std::atomic_bool g_finish(false);
44static std::atomic_uintptr_t g_ucontext;
45
46static void Signal(int, siginfo_t*, void* sigcontext) {
47 g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
48 while (!g_finish.load()) {
49 }
50}
51
52static std::string ErrorMsg(const char** function_names, size_t index,
53 std::stringstream& unwind_stream) {
54 return std::string(
55 "Unwind completed without finding all frames\n"
56 " Looking for function: ") +
57 function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str();
58}
59
60static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs) {
61 const char* function_names[] = {
62 "InnerFunction", "MiddleFunction", "OuterFunction",
63 };
64 size_t function_name_index = 0;
65
66 std::stringstream unwind_stream;
67 unwind_stream << std::hex;
68 for (size_t frame_num = 0; frame_num < 64; frame_num++) {
69 ASSERT_NE(0U, regs->pc()) << ErrorMsg(function_names, function_name_index, unwind_stream);
70 MapInfo* map_info = maps->Find(regs->pc());
71 ASSERT_TRUE(map_info != nullptr) << ErrorMsg(function_names, function_name_index, unwind_stream);
72
73 Elf* elf = map_info->GetElf(pid, true);
74 uint64_t rel_pc = regs->GetRelPc(elf, map_info);
75 uint64_t adjusted_rel_pc = rel_pc;
76 if (frame_num != 0) {
77 adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
78 }
79 unwind_stream << " PC: 0x" << regs->pc() << " Rel: 0x" << adjusted_rel_pc;
80 unwind_stream << " Map: ";
81 if (!map_info->name.empty()) {
82 unwind_stream << map_info->name;
83 } else {
84 unwind_stream << " anonymous";
85 }
86 unwind_stream << "<" << map_info->start << "-" << map_info->end << ">";
87
88 std::string name;
89 uint64_t func_offset;
90 if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
91 if (name == function_names[function_name_index]) {
92 function_name_index++;
93 if (function_name_index == sizeof(function_names) / sizeof(const char*)) {
94 return;
95 }
96 }
97 unwind_stream << " " << name;
98 }
99 unwind_stream << "\n";
100 ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, memory))
101 << ErrorMsg(function_names, function_name_index, unwind_stream);
102 }
103 ASSERT_TRUE(false) << ErrorMsg(function_names, function_name_index, unwind_stream);
104}
105
106// This test assumes that this code is compiled with optimizations turned
107// off. If this doesn't happen, then all of the calls will be optimized
108// away.
109extern "C" void InnerFunction(bool local) {
110 if (local) {
111 LocalMaps maps;
112 ASSERT_TRUE(maps.Parse());
113 std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
114 RegsGetLocal(regs.get());
115 MemoryLocal memory;
116
117 VerifyUnwind(getpid(), &memory, &maps, regs.get());
118 } else {
119 g_ready_for_remote = true;
120 g_ready = true;
121 while (!g_finish.load()) {
122 }
123 }
124}
125
126extern "C" void MiddleFunction(bool local) {
127 InnerFunction(local);
128}
129
130extern "C" void OuterFunction(bool local) {
131 MiddleFunction(local);
132}
133
134TEST(UnwindTest, local) {
135 OuterFunction(true);
136}
137
138TEST(UnwindTest, remote) {
139 pid_t pid;
140 if ((pid = fork()) == 0) {
141 OuterFunction(false);
142 exit(0);
143 }
144 ASSERT_NE(-1, pid);
145
146 bool ready = false;
147 uint64_t addr = reinterpret_cast<uint64_t>(&g_ready_for_remote);
148 for (size_t i = 0; i < 100; i++) {
149 ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
150 for (size_t j = 0; j < 100; j++) {
151 siginfo_t si;
152 if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
153 // Check to see if process is ready to be unwound.
154 MemoryRemote memory(pid);
155 // Read the remote value to see if we are ready.
156 bool value;
157 if (memory.Read(addr, &value, sizeof(value)) && value) {
158 ready = true;
159 break;
160 }
161 }
162 usleep(1000);
163 }
164 if (ready) {
165 break;
166 }
167 ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
168 usleep(1000);
169 }
170 ASSERT_TRUE(read) << "Timed out waiting for remote process to be ready.";
171
172 RemoteMaps maps(pid);
173 ASSERT_TRUE(maps.Parse());
174 MemoryRemote memory(pid);
175 uint32_t machine_type;
176 std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type));
177 ASSERT_TRUE(regs.get() != nullptr);
178
179 VerifyUnwind(pid, &memory, &maps, regs.get());
180
181 ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
182
183 kill(pid, SIGKILL);
184 ASSERT_EQ(pid, wait(nullptr));
185}
186
187TEST(UnwindTest, from_context) {
188 std::atomic_int tid(0);
189 std::thread thread([&]() {
190 tid = syscall(__NR_gettid);
191 OuterFunction(false);
192 });
193
194 struct sigaction act, oldact;
195 memset(&act, 0, sizeof(act));
196 act.sa_sigaction = Signal;
197 act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
198 ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
199 // Wait for the tid to get set.
200 for (size_t i = 0; i < 100; i++) {
201 if (tid.load() != 0) {
202 break;
203 }
204 usleep(1000);
205 }
206 ASSERT_NE(0, tid.load());
207 // Portable tgkill method.
208 ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Failed because "
209 << strerror(errno);
210
211 // Wait for context data.
212 void* ucontext;
213 for (size_t i = 0; i < 200; i++) {
214 ucontext = reinterpret_cast<void*>(g_ucontext.load());
215 if (ucontext != nullptr) {
216 break;
217 }
218 usleep(1000);
219 }
220 ASSERT_TRUE(ucontext != nullptr) << "Timed out waiting for thread to respond to signal.";
221
222 LocalMaps maps;
223 ASSERT_TRUE(maps.Parse());
224 std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::GetMachineType(), ucontext));
225 MemoryLocal memory;
226
227 VerifyUnwind(tid.load(), &memory, &maps, regs.get());
228
229 ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
230
231 g_finish = true;
232 thread.join();
233}