• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
64   ASSERT_TRUE(proc_maps);
65   FDMaps maps(std::move(proc_maps));
66   ASSERT_TRUE(maps.Parse());
67   unwindstack::MapInfo* map_info =
68       maps.Find(reinterpret_cast<uint64_t>(&proc_maps));
69   ASSERT_NE(map_info, nullptr);
70   ASSERT_EQ(map_info->name(), "[stack]");
71 }
72 
AssertFunctionOffset()73 void __attribute__((noinline)) AssertFunctionOffset() {
74   constexpr auto kMaxFunctionSize = 1000u;
75   // Need to zero-initialize to make MSAN happy. MSAN does not see the writes
76   // from AsmGetRegs (as it is in assembly) and complains otherwise.
77   char reg_data[kMaxRegisterDataSize] = {};
78   unwindstack::AsmGetRegs(reg_data);
79   auto regs = CreateRegsFromRawData(unwindstack::Regs::CurrentArch(), reg_data);
80   ASSERT_GT(regs->pc(), reinterpret_cast<uint64_t>(&AssertFunctionOffset));
81   ASSERT_LT(regs->pc() - reinterpret_cast<uint64_t>(&AssertFunctionOffset),
82             kMaxFunctionSize);
83 }
84 
TEST(UnwindingTest,TestFunctionOffset)85 TEST(UnwindingTest, TestFunctionOffset) {
86   AssertFunctionOffset();
87 }
88 
89 // This is needed because ASAN thinks copying the whole stack is a buffer
90 // underrun.
91 void __attribute__((noinline))
UnsafeMemcpy(void * dst,const void * src,size_t n)92 UnsafeMemcpy(void* dst, const void* src, size_t n)
93     __attribute__((no_sanitize("address", "hwaddress", "memory"))) {
94   const uint8_t* from = reinterpret_cast<const uint8_t*>(src);
95   uint8_t* to = reinterpret_cast<uint8_t*>(dst);
96   for (size_t i = 0; i < n; ++i)
97     to[i] = from[i];
98 }
99 
100 struct RecordMemory {
101   std::unique_ptr<uint8_t[]> payload;
102   std::unique_ptr<AllocMetadata> metadata;
103 };
104 
GetRecord(WireMessage * msg)105 RecordMemory __attribute__((noinline)) GetRecord(WireMessage* msg) {
106   std::unique_ptr<AllocMetadata> metadata(new AllocMetadata);
107   *metadata = {};
108 
109   const char* stackend = GetThreadStackRange().end;
110   const char* stackptr = reinterpret_cast<char*>(__builtin_frame_address(0));
111   // Need to zero-initialize to make MSAN happy. MSAN does not see the writes
112   // from AsmGetRegs (as it is in assembly) and complains otherwise.
113   memset(metadata->register_data, 0, sizeof(metadata->register_data));
114   unwindstack::AsmGetRegs(metadata->register_data);
115 
116   if (stackend < stackptr) {
117     PERFETTO_FATAL("Stacktop >= stackend.");
118     return {nullptr, nullptr};
119   }
120   size_t stack_size = static_cast<size_t>(stackend - stackptr);
121 
122   metadata->alloc_size = 10;
123   metadata->alloc_address = 0x10;
124   metadata->stack_pointer = reinterpret_cast<uint64_t>(stackptr);
125   metadata->arch = unwindstack::Regs::CurrentArch();
126   metadata->sequence_number = 1;
127 
128   std::unique_ptr<uint8_t[]> payload(new uint8_t[stack_size]);
129   UnsafeMemcpy(&payload[0], stackptr, stack_size);
130 
131   *msg = {};
132   msg->alloc_header = metadata.get();
133   msg->payload = reinterpret_cast<char*>(payload.get());
134   msg->payload_size = static_cast<size_t>(stack_size);
135   return {std::move(payload), std::move(metadata)};
136 }
137 
TEST(UnwindingTest,DoUnwind)138 TEST(UnwindingTest, DoUnwind) {
139   base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
140   base::ScopedFile proc_mem(base::OpenFile("/proc/self/mem", O_RDONLY));
141   GlobalCallstackTrie callsites;
142   UnwindingMetadata metadata(std::move(proc_maps), std::move(proc_mem));
143   WireMessage msg;
144   auto record = GetRecord(&msg);
145   AllocRecord out;
146   ASSERT_TRUE(DoUnwind(&msg, &metadata, &out));
147   ASSERT_GT(out.frames.size(), 0u);
148   int st;
149   std::unique_ptr<char, base::FreeDeleter> demangled(abi::__cxa_demangle(
150       out.frames[0].function_name.c_str(), nullptr, nullptr, &st));
151   ASSERT_EQ(st, 0) << "mangled: " << demangled.get()
152                    << ", frames: " << out.frames.size();
153   ASSERT_STREQ(demangled.get(),
154                "perfetto::profiling::(anonymous "
155                "namespace)::GetRecord(perfetto::profiling::WireMessage*)");
156 }
157 
TEST(UnwindingTest,DoUnwindReparse)158 TEST(UnwindingTest, DoUnwindReparse) {
159   base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
160   base::ScopedFile proc_mem(base::OpenFile("/proc/self/mem", O_RDONLY));
161   GlobalCallstackTrie callsites;
162   UnwindingMetadata metadata(std::move(proc_maps), std::move(proc_mem));
163   // Force reparse in DoUnwind.
164   metadata.fd_maps.Reset();
165   WireMessage msg;
166   auto record = GetRecord(&msg);
167   AllocRecord out;
168   ASSERT_TRUE(DoUnwind(&msg, &metadata, &out));
169   ASSERT_GT(out.frames.size(), 0u);
170   int st;
171   std::unique_ptr<char, base::FreeDeleter> demangled(abi::__cxa_demangle(
172       out.frames[0].function_name.c_str(), nullptr, nullptr, &st));
173   ASSERT_EQ(st, 0) << "mangled: " << demangled.get()
174                    << ", frames: " << out.frames.size();
175   ASSERT_STREQ(demangled.get(),
176                "perfetto::profiling::(anonymous "
177                "namespace)::GetRecord(perfetto::profiling::WireMessage*)");
178 }
179 
TEST(AllocRecordArenaTest,Smoke)180 TEST(AllocRecordArenaTest, Smoke) {
181   AllocRecordArena a;
182   auto borrowed = a.BorrowAllocRecord();
183   EXPECT_NE(borrowed, nullptr);
184   a.ReturnAllocRecord(std::move(borrowed));
185 }
186 
187 }  // namespace
188 }  // namespace profiling
189 }  // namespace perfetto
190