1 /*
2 * Copyright (C) 2020 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 "perfetto/base/build_config.h"
18 #include "test/gtest_and_gmock.h"
19
20 // This translation unit is built only on Linux and MacOS. See //gn/BUILD.gn.
21 #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
22
23 #include <cstddef>
24
25 #include "src/base/test/tmp_dir_tree.h"
26 #include "src/base/test/utils.h"
27 #include "src/profiling/symbolizer/elf.h"
28 #include "src/profiling/symbolizer/local_symbolizer.h"
29 #include "src/profiling/symbolizer/subprocess.h"
30
31 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
32 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
33 PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
34 #include <unistd.h>
35 #endif
36
37 namespace perfetto {
38 namespace profiling {
39 namespace {
40
TEST(LocalSymbolizerTest,ParseJsonLine)41 TEST(LocalSymbolizerTest, ParseJsonLine) {
42 std::vector<SymbolizedFrame> result;
43 ASSERT_TRUE(ParseLlvmSymbolizerJsonLine(
44 "{\"Address\":\"0x1b72f\",\"ModuleName\":\"...\",\"Symbol\":[{\"Column\":"
45 "0,\"Discriminator\":0,\"FileName\":\"foo.h\",\"FunctionName\":\"foo\","
46 "\"Line\":10,\"StartAddress\":\"\",\"StartFileName\":\"...\","
47 "\"StartLine\":0},{\"Column\":"
48 "0,\"Discriminator\":0,\"FileName\":\"bar.h\",\"FunctionName\":\"bar\","
49 "\"Line\":20,\"StartAddress\":\"\",\"StartFileName\":\"...\","
50 "\"StartLine\":0}]}",
51 &result));
52 EXPECT_EQ(result.size(), 2u);
53 EXPECT_EQ(result[0].file_name, "foo.h");
54 EXPECT_EQ(result[0].function_name, "foo");
55 EXPECT_EQ(result[0].line, 10u);
56 EXPECT_EQ(result[1].file_name, "bar.h");
57 EXPECT_EQ(result[1].function_name, "bar");
58 EXPECT_EQ(result[1].line, 20u);
59 }
60
61 // Creates a very simple ELF file content with the first 20 bytes of `build_id`
62 // as build id (if build id is shorter the remainin bytes are zero).
CreateElfWithBuildId(const std::string & build_id)63 std::string CreateElfWithBuildId(const std::string& build_id) {
64 struct SimpleElf {
65 Elf64::Ehdr ehdr;
66 Elf64::Shdr shdr;
67 Elf64::Nhdr nhdr;
68 char note_name[4];
69 char note_desc[20];
70 } e;
71 memset(&e, 0, sizeof e);
72
73 e.ehdr.e_ident[EI_MAG0] = ELFMAG0;
74 e.ehdr.e_ident[EI_MAG1] = ELFMAG1;
75 e.ehdr.e_ident[EI_MAG2] = ELFMAG2;
76 e.ehdr.e_ident[EI_MAG3] = ELFMAG3;
77 e.ehdr.e_ident[EI_CLASS] = ELFCLASS64;
78 e.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
79 e.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
80 e.ehdr.e_version = EV_CURRENT;
81 e.ehdr.e_shentsize = sizeof(Elf64::Shdr);
82 e.ehdr.e_shnum = 1;
83 e.ehdr.e_ehsize = sizeof e.ehdr;
84 e.ehdr.e_shoff = offsetof(SimpleElf, shdr);
85
86 e.shdr.sh_type = SHT_NOTE;
87 e.shdr.sh_offset = offsetof(SimpleElf, nhdr);
88
89 e.nhdr.n_type = NT_GNU_BUILD_ID;
90 e.nhdr.n_namesz = sizeof e.note_name;
91 e.nhdr.n_descsz = sizeof e.note_desc;
92 strcpy(e.note_name, "GNU");
93 memcpy(e.note_desc, build_id.c_str(),
94 std::min(build_id.size(), sizeof(e.note_desc)));
95
96 e.shdr.sh_size = offsetof(SimpleElf, note_desc) + sizeof(e.note_desc) -
97 offsetof(SimpleElf, nhdr);
98
99 return std::string(reinterpret_cast<const char*>(&e), sizeof e);
100 }
101
102 #if defined(MEMORY_SANITIZER)
103 // fts_read() causes some error under msan.
104 #define NOMSAN_SimpleTree DISABLED_SimpleTree
105 #else
106 #define NOMSAN_SimpleTree SimpleTree
107 #endif
TEST(LocalBinaryIndexerTest,NOMSAN_SimpleTree)108 TEST(LocalBinaryIndexerTest, NOMSAN_SimpleTree) {
109 base::TmpDirTree tmp;
110 tmp.AddDir("dir1");
111 tmp.AddFile("dir1/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
112 tmp.AddFile("dir1/nonelf1", "OTHERDATA");
113 tmp.AddDir("dir2");
114 tmp.AddFile("dir2/elf1", CreateElfWithBuildId("BBBBBBBBBBBBBBBBBBBB"));
115 tmp.AddFile("dir2/nonelf1", "other text");
116
117 LocalBinaryIndexer indexer({tmp.path() + "/dir1", tmp.path() + "/dir2"});
118
119 std::optional<FoundBinary> bin1 =
120 indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
121 ASSERT_TRUE(bin1.has_value());
122 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
123 EXPECT_EQ(bin1.value().file_name, tmp.path() + "/dir1\\elf1");
124 #else
125 EXPECT_EQ(bin1.value().file_name, tmp.path() + "/dir1/elf1");
126 #endif
127 std::optional<FoundBinary> bin2 =
128 indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
129 ASSERT_TRUE(bin2.has_value());
130 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
131 EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2\\elf1");
132 #else
133 EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2/elf1");
134 #endif
135 }
136
137 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
138 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
139 PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
140
141 #if defined(MEMORY_SANITIZER)
142 // fts_read() causes some error under msan.
143 #define NOMSAN_Symlinks DISABLED_Symlinks
144 #else
145 #define NOMSAN_Symlinks Symlinks
146 #endif
TEST(LocalBinaryIndexerTest,NOMSAN_Symlinks)147 TEST(LocalBinaryIndexerTest, NOMSAN_Symlinks) {
148 base::TmpDirTree tmp;
149 tmp.AddDir("real");
150 tmp.AddFile("real/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
151 tmp.AddDir("real/dir1");
152 tmp.AddFile("real/dir1/elf2", CreateElfWithBuildId("BBBBBBBBBBBBBBBBBBBB"));
153 tmp.AddFile("real/dir1/elf3", CreateElfWithBuildId("CCCCCCCCCCCCCCCCCCCC"));
154 tmp.AddDir("sym");
155 EXPECT_EQ(symlink(tmp.AbsolutePath("real/elf1").c_str(),
156 tmp.AbsolutePath("sym/elf1").c_str()),
157 0);
158 tmp.TrackFile("sym/elf1");
159 EXPECT_EQ(symlink(tmp.AbsolutePath("real/dir1").c_str(),
160 tmp.AbsolutePath("sym/dir1").c_str()),
161 0);
162 tmp.TrackFile("sym/dir1");
163
164 LocalBinaryIndexer indexer({tmp.AbsolutePath("sym")});
165
166 std::optional<FoundBinary> bin1 =
167 indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
168 ASSERT_TRUE(bin1.has_value());
169 EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("sym/elf1"));
170
171 std::optional<FoundBinary> bin2 =
172 indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
173 ASSERT_TRUE(bin2.has_value());
174 EXPECT_EQ(bin2.value().file_name, tmp.AbsolutePath("sym/dir1/elf2"));
175
176 std::optional<FoundBinary> bin3 =
177 indexer.FindBinary("", "CCCCCCCCCCCCCCCCCCCC");
178 ASSERT_TRUE(bin3.has_value());
179 EXPECT_EQ(bin3.value().file_name, tmp.AbsolutePath("sym/dir1/elf3"));
180 }
181
182 #if defined(MEMORY_SANITIZER)
183 // fts_read() causes some error under msan.
184 #define NOMSAN_RecursiveSymlinks DISABLED_RecursiveSymlinks
185 #else
186 #define NOMSAN_RecursiveSymlinks RecursiveSymlinks
187 #endif
TEST(LocalBinaryIndexerTest,NOMSAN_RecursiveSymlinks)188 TEST(LocalBinaryIndexerTest, NOMSAN_RecursiveSymlinks) {
189 base::TmpDirTree tmp;
190 tmp.AddDir("main");
191 tmp.AddFile("main/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
192 tmp.AddDir("main/dir1");
193 EXPECT_EQ(symlink(tmp.AbsolutePath("main").c_str(),
194 tmp.AbsolutePath("main/dir1/sym").c_str()),
195 0);
196 tmp.TrackFile("main/dir1/sym");
197
198 LocalBinaryIndexer indexer({tmp.AbsolutePath("main")});
199
200 std::optional<FoundBinary> bin1 =
201 indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
202 ASSERT_TRUE(bin1.has_value());
203 EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("main/elf1"));
204 }
205
206 #endif // PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||
207 // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) ||
208 // PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
209
TEST(LocalBinaryFinderTest,AbsolutePath)210 TEST(LocalBinaryFinderTest, AbsolutePath) {
211 base::TmpDirTree tmp;
212 tmp.AddDir("root");
213 tmp.AddDir("root/dir");
214 tmp.AddFile("root/dir/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
215
216 LocalBinaryFinder finder({tmp.path() + "/root"});
217
218 std::optional<FoundBinary> bin1 =
219 finder.FindBinary("/dir/elf1.so", "AAAAAAAAAAAAAAAAAAAA");
220 ASSERT_TRUE(bin1.has_value());
221 EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/dir/elf1.so");
222 }
223
TEST(LocalBinaryFinderTest,AbsolutePathWithoutBaseApk)224 TEST(LocalBinaryFinderTest, AbsolutePathWithoutBaseApk) {
225 base::TmpDirTree tmp;
226 tmp.AddDir("root");
227 tmp.AddDir("root/dir");
228 tmp.AddFile("root/dir/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
229
230 LocalBinaryFinder finder({tmp.path() + "/root"});
231
232 std::optional<FoundBinary> bin1 =
233 finder.FindBinary("/dir/base.apk!elf1.so", "AAAAAAAAAAAAAAAAAAAA");
234 ASSERT_TRUE(bin1.has_value());
235 EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/dir/elf1.so");
236 }
237
TEST(LocalBinaryFinderTest,OnlyFilename)238 TEST(LocalBinaryFinderTest, OnlyFilename) {
239 base::TmpDirTree tmp;
240 tmp.AddDir("root");
241 tmp.AddFile("root/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
242
243 LocalBinaryFinder finder({tmp.path() + "/root"});
244
245 std::optional<FoundBinary> bin1 =
246 finder.FindBinary("/ignored_dir/elf1.so", "AAAAAAAAAAAAAAAAAAAA");
247 ASSERT_TRUE(bin1.has_value());
248 EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/elf1.so");
249 }
250
TEST(LocalBinaryFinderTest,OnlyFilenameWithoutBaseApk)251 TEST(LocalBinaryFinderTest, OnlyFilenameWithoutBaseApk) {
252 base::TmpDirTree tmp;
253 tmp.AddDir("root");
254 tmp.AddFile("root/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
255
256 LocalBinaryFinder finder({tmp.path() + "/root"});
257
258 std::optional<FoundBinary> bin1 = finder.FindBinary(
259 "/ignored_dir/base.apk!elf1.so", "AAAAAAAAAAAAAAAAAAAA");
260 ASSERT_TRUE(bin1.has_value());
261 EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/elf1.so");
262 }
263
TEST(LocalBinaryFinderTest,BuildIdSubdir)264 TEST(LocalBinaryFinderTest, BuildIdSubdir) {
265 base::TmpDirTree tmp;
266 tmp.AddDir("root");
267 tmp.AddDir("root/.build-id");
268 tmp.AddDir("root/.build-id/41");
269 tmp.AddFile("root/.build-id/41/41414141414141414141414141414141414141.debug",
270 CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
271
272 LocalBinaryFinder finder({tmp.path() + "/root"});
273
274 std::optional<FoundBinary> bin1 =
275 finder.FindBinary("/ignored_dir/ignored_name.so", "AAAAAAAAAAAAAAAAAAAA");
276 ASSERT_TRUE(bin1.has_value());
277 EXPECT_EQ(
278 bin1.value().file_name,
279 tmp.path() +
280 "/root/.build-id/41/41414141414141414141414141414141414141.debug");
281 }
282
283 } // namespace
284 } // namespace profiling
285 } // namespace perfetto
286
287 #endif
288