xref: /aosp_15_r20/external/perfetto/src/profiling/memory/unwinding_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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