1 /*
2 * Copyright (C) 2018 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 "src/profiling/memory/unwinding.h"
18
19 #include <cxxabi.h>
20 #include <fcntl.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unwindstack/RegsGetLocal.h>
24
25 #include "perfetto/ext/base/file_utils.h"
26 #include "perfetto/ext/base/scoped_file.h"
27 #include "src/profiling/common/unwind_support.h"
28 #include "src/profiling/memory/client.h"
29 #include "src/profiling/memory/wire_protocol.h"
30 #include "test/gtest_and_gmock.h"
31
32 namespace perfetto {
33 namespace profiling {
34 namespace {
35
TEST(UnwindingTest,StackOverlayMemoryOverlay)36 TEST(UnwindingTest, StackOverlayMemoryOverlay) {
37 base::ScopedFile proc_mem(base::OpenFile("/proc/self/mem", O_RDONLY));
38 ASSERT_TRUE(proc_mem);
39 uint8_t fake_stack[1] = {120};
40 std::shared_ptr<FDMemory> mem(
41 std::make_shared<FDMemory>(std::move(proc_mem)));
42 StackOverlayMemory memory(mem, 0u, fake_stack, 1);
43 uint8_t buf[1] = {};
44 ASSERT_EQ(memory.Read(0u, buf, 1), 1u);
45 ASSERT_EQ(buf[0], 120);
46 }
47
TEST(UnwindingTest,StackOverlayMemoryNonOverlay)48 TEST(UnwindingTest, StackOverlayMemoryNonOverlay) {
49 uint8_t value = 52;
50
51 base::ScopedFile proc_mem(base::OpenFile("/proc/self/mem", O_RDONLY));
52 ASSERT_TRUE(proc_mem);
53 uint8_t fake_stack[1] = {120};
54 std::shared_ptr<FDMemory> mem(
55 std::make_shared<FDMemory>(std::move(proc_mem)));
56 StackOverlayMemory memory(mem, 0u, fake_stack, 1);
57 uint8_t buf[1] = {1};
58 ASSERT_EQ(memory.Read(reinterpret_cast<uint64_t>(&value), buf, 1), 1u);
59 ASSERT_EQ(buf[0], value);
60 }
61
TEST(UnwindingTest,FDMapsParse)62 TEST(UnwindingTest, FDMapsParse) {
63 #if defined(ADDRESS_SANITIZER)
64 PERFETTO_LOG("Skipping /proc/self/maps as ASAN distorts what is where");
65 GTEST_SKIP();
66 #else
67 base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
68 ASSERT_TRUE(proc_maps);
69 FDMaps maps(std::move(proc_maps));
70 ASSERT_TRUE(maps.Parse());
71 unwindstack::MapInfo* map_info =
72 maps.Find(reinterpret_cast<uint64_t>(&proc_maps)).get();
73 ASSERT_NE(map_info, nullptr);
74 ASSERT_EQ(map_info->name(), "[stack]");
75 #endif
76 }
77
AssertFunctionOffset()78 void __attribute__((noinline)) AssertFunctionOffset() {
79 constexpr auto kMaxFunctionSize = 1000u;
80 // Need to zero-initialize to make MSAN happy. MSAN does not see the writes
81 // from AsmGetRegs (as it is in assembly) and complains otherwise.
82 char reg_data[kMaxRegisterDataSize] = {};
83 unwindstack::AsmGetRegs(reg_data);
84 auto regs = CreateRegsFromRawData(unwindstack::Regs::CurrentArch(), reg_data);
85 ASSERT_GT(regs->pc(), reinterpret_cast<uint64_t>(&AssertFunctionOffset));
86 ASSERT_LT(regs->pc() - reinterpret_cast<uint64_t>(&AssertFunctionOffset),
87 kMaxFunctionSize);
88 }
89
TEST(UnwindingTest,TestFunctionOffset)90 TEST(UnwindingTest, TestFunctionOffset) {
91 AssertFunctionOffset();
92 }
93
94 // This is needed because ASAN thinks copying the whole stack is a buffer
95 // underrun.
96 void __attribute__((noinline))
UnsafeMemcpy(void * dst,const void * src,size_t n)97 UnsafeMemcpy(void* dst, const void* src, size_t n)
98 __attribute__((no_sanitize("address", "hwaddress", "memory"))) {
99 const uint8_t* from = reinterpret_cast<const uint8_t*>(src);
100 uint8_t* to = reinterpret_cast<uint8_t*>(dst);
101 for (size_t i = 0; i < n; ++i)
102 to[i] = from[i];
103 }
104
105 struct RecordMemory {
106 std::unique_ptr<uint8_t[]> payload;
107 std::unique_ptr<AllocMetadata> metadata;
108 };
109
GetRecord(WireMessage * msg)110 RecordMemory __attribute__((noinline)) GetRecord(WireMessage* msg) {
111 std::unique_ptr<AllocMetadata> metadata(new AllocMetadata);
112 *metadata = {};
113
114 const char* stackend = GetThreadStackRange().end;
115 const char* stackptr = reinterpret_cast<char*>(__builtin_frame_address(0));
116 // Need to zero-initialize to make MSAN happy. MSAN does not see the writes
117 // from AsmGetRegs (as it is in assembly) and complains otherwise.
118 memset(metadata->register_data, 0, sizeof(metadata->register_data));
119 unwindstack::AsmGetRegs(metadata->register_data);
120
121 if (stackend < stackptr) {
122 PERFETTO_FATAL("Stacktop >= stackend.");
123 return {nullptr, nullptr};
124 }
125 size_t stack_size = static_cast<size_t>(stackend - stackptr);
126
127 metadata->alloc_size = 10;
128 metadata->alloc_address = 0x10;
129 metadata->stack_pointer = reinterpret_cast<uint64_t>(stackptr);
130 metadata->arch = unwindstack::Regs::CurrentArch();
131 metadata->sequence_number = 1;
132
133 std::unique_ptr<uint8_t[]> payload(new uint8_t[stack_size]);
134 UnsafeMemcpy(&payload[0], stackptr, stack_size);
135
136 *msg = {};
137 msg->alloc_header = metadata.get();
138 msg->payload = reinterpret_cast<char*>(payload.get());
139 msg->payload_size = static_cast<size_t>(stack_size);
140 return {std::move(payload), std::move(metadata)};
141 }
142
TEST(UnwindingTest,DoUnwind)143 TEST(UnwindingTest, DoUnwind) {
144 base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
145 base::ScopedFile proc_mem(base::OpenFile("/proc/self/mem", O_RDONLY));
146 GlobalCallstackTrie callsites;
147 UnwindingMetadata metadata(std::move(proc_maps), std::move(proc_mem));
148 WireMessage msg;
149 auto record = GetRecord(&msg);
150 AllocRecord out;
151 ASSERT_TRUE(DoUnwind(&msg, &metadata, &out));
152 ASSERT_GT(out.frames.size(), 0u);
153 int st;
154 std::unique_ptr<char, base::FreeDeleter> demangled(abi::__cxa_demangle(
155 out.frames[0].function_name.c_str(), nullptr, nullptr, &st));
156 ASSERT_EQ(st, 0) << "mangled: " << demangled.get()
157 << ", frames: " << out.frames.size();
158 ASSERT_STREQ(demangled.get(),
159 "perfetto::profiling::(anonymous "
160 "namespace)::GetRecord(perfetto::profiling::WireMessage*)");
161 }
162
TEST(UnwindingTest,DoUnwindReparse)163 TEST(UnwindingTest, DoUnwindReparse) {
164 base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
165 base::ScopedFile proc_mem(base::OpenFile("/proc/self/mem", O_RDONLY));
166 GlobalCallstackTrie callsites;
167 UnwindingMetadata metadata(std::move(proc_maps), std::move(proc_mem));
168 // Force reparse in DoUnwind.
169 metadata.fd_maps.Reset();
170 WireMessage msg;
171 auto record = GetRecord(&msg);
172 AllocRecord out;
173 ASSERT_TRUE(DoUnwind(&msg, &metadata, &out));
174 ASSERT_GT(out.frames.size(), 0u);
175 int st;
176 std::unique_ptr<char, base::FreeDeleter> demangled(abi::__cxa_demangle(
177 out.frames[0].function_name.c_str(), nullptr, nullptr, &st));
178 ASSERT_EQ(st, 0) << "mangled: " << demangled.get()
179 << ", frames: " << out.frames.size();
180 ASSERT_STREQ(demangled.get(),
181 "perfetto::profiling::(anonymous "
182 "namespace)::GetRecord(perfetto::profiling::WireMessage*)");
183 }
184
TEST(AllocRecordArenaTest,Smoke)185 TEST(AllocRecordArenaTest, Smoke) {
186 AllocRecordArena a;
187 auto borrowed = a.BorrowAllocRecord();
188 EXPECT_NE(borrowed, nullptr);
189 a.ReturnAllocRecord(std::move(borrowed));
190 }
191
192 } // namespace
193 } // namespace profiling
194 } // namespace perfetto
195