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 #include "perfetto/base/scoped_file.h"
19 #include "src/profiling/memory/client.h"
20 #include "src/profiling/memory/wire_protocol.h"
21 
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 
25 #include <cxxabi.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 
30 #include <unwindstack/RegsGetLocal.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), 1);
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), 1);
59   ASSERT_EQ(buf[0], value);
60 }
61 
TEST(UnwindingTest,FileDescriptorMapsParse)62 TEST(UnwindingTest, FileDescriptorMapsParse) {
63   base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
64   ASSERT_TRUE(proc_maps);
65   FileDescriptorMaps 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 
73 // This is needed because ASAN thinks copying the whole stack is a buffer
74 // underrun.
75 void __attribute__((noinline))
UnsafeMemcpy(void * dst,const void * src,size_t n)76 UnsafeMemcpy(void* dst, const void* src, size_t n)
77     __attribute__((no_sanitize("address", "hwaddress"))) {
78   const uint8_t* from = reinterpret_cast<const uint8_t*>(src);
79   uint8_t* to = reinterpret_cast<uint8_t*>(dst);
80   for (size_t i = 0; i < n; ++i)
81     to[i] = from[i];
82 }
83 
84 struct RecordMemory {
85   std::unique_ptr<uint8_t[]> payload;
86   std::unique_ptr<AllocMetadata> metadata;
87 };
88 
GetRecord(WireMessage * msg)89 RecordMemory __attribute__((noinline)) GetRecord(WireMessage* msg) {
90   std::unique_ptr<AllocMetadata> metadata(new AllocMetadata);
91   *metadata = {};
92 
93   const char* stackbase = GetThreadStackBase();
94   const char* stacktop = reinterpret_cast<char*>(__builtin_frame_address(0));
95   unwindstack::AsmGetRegs(metadata->register_data);
96 
97   if (stackbase < stacktop) {
98     PERFETTO_DFATAL("Stacktop >= stackbase.");
99     return {nullptr, nullptr};
100   }
101   uint64_t stack_size = static_cast<uint64_t>(stackbase - stacktop);
102 
103   metadata->alloc_size = 10;
104   metadata->alloc_address = 0x10;
105   metadata->stack_pointer = reinterpret_cast<uint64_t>(stacktop);
106   metadata->stack_pointer_offset = sizeof(AllocMetadata);
107   metadata->arch = unwindstack::Regs::CurrentArch();
108   metadata->sequence_number = 1;
109 
110   std::unique_ptr<uint8_t[]> payload(new uint8_t[stack_size]);
111   UnsafeMemcpy(&payload[0], stacktop, stack_size);
112 
113   *msg = {};
114   msg->alloc_header = metadata.get();
115   msg->payload = reinterpret_cast<char*>(payload.get());
116   msg->payload_size = static_cast<size_t>(stack_size);
117   return {std::move(payload), std::move(metadata)};
118 }
119 
120 // TODO(rsavitski): Investigate TSAN unwinding.
121 #if defined(THREAD_SANITIZER)
122 #define MAYBE_DoUnwind DISABLED_DoUnwind
123 #else
124 #define MAYBE_DoUnwind DoUnwind
125 #endif
126 
TEST(UnwindingTest,MAYBE_DoUnwind)127 TEST(UnwindingTest, MAYBE_DoUnwind) {
128   base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
129   base::ScopedFile proc_mem(base::OpenFile("/proc/self/mem", O_RDONLY));
130   GlobalCallstackTrie callsites;
131   UnwindingMetadata metadata(getpid(), std::move(proc_maps),
132                              std::move(proc_mem));
133   WireMessage msg;
134   auto record = GetRecord(&msg);
135   AllocRecord out;
136   ASSERT_TRUE(DoUnwind(&msg, &metadata, &out));
137   int st;
138   std::unique_ptr<char, base::FreeDeleter> demangled(abi::__cxa_demangle(
139       out.frames[0].frame.function_name.c_str(), nullptr, nullptr, &st));
140   ASSERT_EQ(st, 0);
141   ASSERT_STREQ(demangled.get(),
142                "perfetto::profiling::(anonymous "
143                "namespace)::GetRecord(perfetto::profiling::WireMessage*)");
144 }
145 
146 }  // namespace
147 }  // namespace profiling
148 }  // namespace perfetto
149