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