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