• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RunAndValidateParseLines(std::string raw_contents)41 void RunAndValidateParseLines(std::string raw_contents) {
42   std::istringstream stream(raw_contents);
43   auto read_callback = [&stream](char* buffer, size_t size) {
44     stream.get(buffer, static_cast<int>(size), '\0');
45     return strlen(buffer);
46   };
47   std::vector<std::string> lines = GetLines(read_callback);
48   std::istringstream validation(raw_contents);
49   for (const std::string& actual : lines) {
50     std::string expected;
51     getline(validation, expected);
52     EXPECT_EQ(actual, expected);
53   }
54 }
55 
TEST(LocalSymbolizerTest,ParseLineWindows)56 TEST(LocalSymbolizerTest, ParseLineWindows) {
57   std::string file_name;
58   uint32_t lineno;
59   ASSERT_TRUE(
60       ParseLlvmSymbolizerLine("C:\\Foo\\Bar.cc:123:1", &file_name, &lineno));
61   EXPECT_EQ(file_name, "C:\\Foo\\Bar.cc");
62   EXPECT_EQ(lineno, 123u);
63 }
64 
TEST(LocalSymbolizerTest,ParseLinesExpectedOutput)65 TEST(LocalSymbolizerTest, ParseLinesExpectedOutput) {
66   std::string raw_contents =
67       "FSlateRHIRenderingPolicy::DrawElements(FRHICommandListImmediate&, "
68       "FSlateBackBuffer&, TRefCountPtr<FRHITexture2D>&, "
69       "TRefCountPtr<FRHITexture2D>&, TRefCountPtr<FRHITexture2D>&, int, "
70       "TArray<FSlateRenderBatch, TSizedDefaultAllocator<32> > const&, "
71       "FSlateRenderingParams const&)\n"
72       "F:/P4/EngineReleaseA/Engine/Source/Runtime/SlateRHIRenderer/"
73       "Private\\SlateRHIRenderingPolicy.cpp:1187:19\n";
74   RunAndValidateParseLines(raw_contents);
75 }
76 
TEST(LocalSymbolizerTest,ParseLinesErrorOutput)77 TEST(LocalSymbolizerTest, ParseLinesErrorOutput) {
78   std::string raw_contents =
79       "LLVMSymbolizer: error reading file: No such file or directory\n"
80       "??\n"
81       "??:0:0\n";
82   RunAndValidateParseLines(raw_contents);
83 }
84 
TEST(LocalSymbolizerTest,ParseLinesSingleCharRead)85 TEST(LocalSymbolizerTest, ParseLinesSingleCharRead) {
86   std::string raw_contents =
87       "FSlateRHIRenderingPolicy::DrawElements(FRHICommandListImmediate&, "
88       "FSlateBackBuffer&, TRefCountPtr<FRHITexture2D>&, "
89       "TRefCountPtr<FRHITexture2D>&, TRefCountPtr<FRHITexture2D>&, int, "
90       "TArray<FSlateRenderBatch, TSizedDefaultAllocator<32> > const&, "
91       "FSlateRenderingParams const&)\n"
92       "F:/P4/EngineReleaseA/Engine/Source/Runtime/SlateRHIRenderer/"
93       "Private\\SlateRHIRenderingPolicy.cpp:1187:19\n";
94   std::istringstream stream(raw_contents);
95   auto read_callback = [&stream](char* buffer, size_t) {
96     stream.get(buffer, 1, '\0');
97     return strlen(buffer);
98   };
99   std::vector<std::string> lines = GetLines(read_callback);
100   std::istringstream validation(raw_contents);
101   for (const std::string& actual : lines) {
102     std::string expected;
103     getline(validation, expected);
104     EXPECT_EQ(actual, expected);
105   }
106 }
107 
108 // Creates a very simple ELF file content with the first 20 bytes of `build_id`
109 // as build id (if build id is shorter the remainin bytes are zero).
CreateElfWithBuildId(const std::string & build_id)110 std::string CreateElfWithBuildId(const std::string& build_id) {
111   struct SimpleElf {
112     Elf64::Ehdr ehdr;
113     Elf64::Shdr shdr;
114     Elf64::Nhdr nhdr;
115     char note_name[4];
116     char note_desc[20];
117   } e;
118   memset(&e, 0, sizeof e);
119 
120   e.ehdr.e_ident[EI_MAG0] = ELFMAG0;
121   e.ehdr.e_ident[EI_MAG1] = ELFMAG1;
122   e.ehdr.e_ident[EI_MAG2] = ELFMAG2;
123   e.ehdr.e_ident[EI_MAG3] = ELFMAG3;
124   e.ehdr.e_ident[EI_CLASS] = ELFCLASS64;
125   e.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
126   e.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
127   e.ehdr.e_version = EV_CURRENT;
128   e.ehdr.e_shentsize = sizeof(Elf64::Shdr);
129   e.ehdr.e_shnum = 1;
130   e.ehdr.e_ehsize = sizeof e.ehdr;
131   e.ehdr.e_shoff = offsetof(SimpleElf, shdr);
132 
133   e.shdr.sh_type = SHT_NOTE;
134   e.shdr.sh_offset = offsetof(SimpleElf, nhdr);
135 
136   e.nhdr.n_type = NT_GNU_BUILD_ID;
137   e.nhdr.n_namesz = sizeof e.note_name;
138   e.nhdr.n_descsz = sizeof e.note_desc;
139   strcpy(e.note_name, "GNU");
140   memcpy(e.note_desc, build_id.c_str(),
141          std::min(build_id.size(), sizeof(e.note_desc)));
142 
143   e.shdr.sh_size = offsetof(SimpleElf, note_desc) + sizeof(e.note_desc) -
144                    offsetof(SimpleElf, nhdr);
145 
146   return std::string(reinterpret_cast<const char*>(&e), sizeof e);
147 }
148 
149 #if defined(MEMORY_SANITIZER)
150 // fts_read() causes some error under msan.
151 #define NOMSAN_SimpleTree DISABLED_SimpleTree
152 #else
153 #define NOMSAN_SimpleTree SimpleTree
154 #endif
TEST(LocalBinaryIndexerTest,NOMSAN_SimpleTree)155 TEST(LocalBinaryIndexerTest, NOMSAN_SimpleTree) {
156   base::TmpDirTree tmp;
157   tmp.AddDir("dir1");
158   tmp.AddFile("dir1/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
159   tmp.AddFile("dir1/nonelf1", "OTHERDATA");
160   tmp.AddDir("dir2");
161   tmp.AddFile("dir2/elf1", CreateElfWithBuildId("BBBBBBBBBBBBBBBBBBBB"));
162   tmp.AddFile("dir2/nonelf1", "other text");
163 
164   LocalBinaryIndexer indexer({tmp.path() + "/dir1", tmp.path() + "/dir2"});
165 
166   std::optional<FoundBinary> bin1 =
167       indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
168   ASSERT_TRUE(bin1.has_value());
169 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
170   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/dir1\\elf1");
171 #else
172   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/dir1/elf1");
173 #endif
174   std::optional<FoundBinary> bin2 =
175       indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
176   ASSERT_TRUE(bin2.has_value());
177 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
178   EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2\\elf1");
179 #else
180   EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2/elf1");
181 #endif
182 }
183 
184 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
185     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
186     PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
187 
188 #if defined(MEMORY_SANITIZER)
189 // fts_read() causes some error under msan.
190 #define NOMSAN_Symlinks DISABLED_Symlinks
191 #else
192 #define NOMSAN_Symlinks Symlinks
193 #endif
TEST(LocalBinaryIndexerTest,NOMSAN_Symlinks)194 TEST(LocalBinaryIndexerTest, NOMSAN_Symlinks) {
195   base::TmpDirTree tmp;
196   tmp.AddDir("real");
197   tmp.AddFile("real/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
198   tmp.AddDir("real/dir1");
199   tmp.AddFile("real/dir1/elf2", CreateElfWithBuildId("BBBBBBBBBBBBBBBBBBBB"));
200   tmp.AddFile("real/dir1/elf3", CreateElfWithBuildId("CCCCCCCCCCCCCCCCCCCC"));
201   tmp.AddDir("sym");
202   EXPECT_EQ(symlink(tmp.AbsolutePath("real/elf1").c_str(),
203                     tmp.AbsolutePath("sym/elf1").c_str()),
204             0);
205   tmp.TrackFile("sym/elf1");
206   EXPECT_EQ(symlink(tmp.AbsolutePath("real/dir1").c_str(),
207                     tmp.AbsolutePath("sym/dir1").c_str()),
208             0);
209   tmp.TrackFile("sym/dir1");
210 
211   LocalBinaryIndexer indexer({tmp.AbsolutePath("sym")});
212 
213   std::optional<FoundBinary> bin1 =
214       indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
215   ASSERT_TRUE(bin1.has_value());
216   EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("sym/elf1"));
217 
218   std::optional<FoundBinary> bin2 =
219       indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
220   ASSERT_TRUE(bin2.has_value());
221   EXPECT_EQ(bin2.value().file_name, tmp.AbsolutePath("sym/dir1/elf2"));
222 
223   std::optional<FoundBinary> bin3 =
224       indexer.FindBinary("", "CCCCCCCCCCCCCCCCCCCC");
225   ASSERT_TRUE(bin3.has_value());
226   EXPECT_EQ(bin3.value().file_name, tmp.AbsolutePath("sym/dir1/elf3"));
227 }
228 
229 #if defined(MEMORY_SANITIZER)
230 // fts_read() causes some error under msan.
231 #define NOMSAN_RecursiveSymlinks DISABLED_RecursiveSymlinks
232 #else
233 #define NOMSAN_RecursiveSymlinks RecursiveSymlinks
234 #endif
TEST(LocalBinaryIndexerTest,NOMSAN_RecursiveSymlinks)235 TEST(LocalBinaryIndexerTest, NOMSAN_RecursiveSymlinks) {
236   base::TmpDirTree tmp;
237   tmp.AddDir("main");
238   tmp.AddFile("main/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
239   tmp.AddDir("main/dir1");
240   EXPECT_EQ(symlink(tmp.AbsolutePath("main").c_str(),
241                     tmp.AbsolutePath("main/dir1/sym").c_str()),
242             0);
243   tmp.TrackFile("main/dir1/sym");
244 
245   LocalBinaryIndexer indexer({tmp.AbsolutePath("main")});
246 
247   std::optional<FoundBinary> bin1 =
248       indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
249   ASSERT_TRUE(bin1.has_value());
250   EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("main/elf1"));
251 }
252 
253 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||
254         // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) ||
255         // PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
256 
TEST(LocalBinaryFinderTest,AbsolutePath)257 TEST(LocalBinaryFinderTest, AbsolutePath) {
258   base::TmpDirTree tmp;
259   tmp.AddDir("root");
260   tmp.AddDir("root/dir");
261   tmp.AddFile("root/dir/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
262 
263   LocalBinaryFinder finder({tmp.path() + "/root"});
264 
265   std::optional<FoundBinary> bin1 =
266       finder.FindBinary("/dir/elf1.so", "AAAAAAAAAAAAAAAAAAAA");
267   ASSERT_TRUE(bin1.has_value());
268   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/dir/elf1.so");
269 }
270 
TEST(LocalBinaryFinderTest,AbsolutePathWithoutBaseApk)271 TEST(LocalBinaryFinderTest, AbsolutePathWithoutBaseApk) {
272   base::TmpDirTree tmp;
273   tmp.AddDir("root");
274   tmp.AddDir("root/dir");
275   tmp.AddFile("root/dir/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
276 
277   LocalBinaryFinder finder({tmp.path() + "/root"});
278 
279   std::optional<FoundBinary> bin1 =
280       finder.FindBinary("/dir/base.apk!elf1.so", "AAAAAAAAAAAAAAAAAAAA");
281   ASSERT_TRUE(bin1.has_value());
282   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/dir/elf1.so");
283 }
284 
TEST(LocalBinaryFinderTest,OnlyFilename)285 TEST(LocalBinaryFinderTest, OnlyFilename) {
286   base::TmpDirTree tmp;
287   tmp.AddDir("root");
288   tmp.AddFile("root/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
289 
290   LocalBinaryFinder finder({tmp.path() + "/root"});
291 
292   std::optional<FoundBinary> bin1 =
293       finder.FindBinary("/ignored_dir/elf1.so", "AAAAAAAAAAAAAAAAAAAA");
294   ASSERT_TRUE(bin1.has_value());
295   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/elf1.so");
296 }
297 
TEST(LocalBinaryFinderTest,OnlyFilenameWithoutBaseApk)298 TEST(LocalBinaryFinderTest, OnlyFilenameWithoutBaseApk) {
299   base::TmpDirTree tmp;
300   tmp.AddDir("root");
301   tmp.AddFile("root/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
302 
303   LocalBinaryFinder finder({tmp.path() + "/root"});
304 
305   std::optional<FoundBinary> bin1 = finder.FindBinary(
306       "/ignored_dir/base.apk!elf1.so", "AAAAAAAAAAAAAAAAAAAA");
307   ASSERT_TRUE(bin1.has_value());
308   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/elf1.so");
309 }
310 
TEST(LocalBinaryFinderTest,BuildIdSubdir)311 TEST(LocalBinaryFinderTest, BuildIdSubdir) {
312   base::TmpDirTree tmp;
313   tmp.AddDir("root");
314   tmp.AddDir("root/.build-id");
315   tmp.AddDir("root/.build-id/41");
316   tmp.AddFile("root/.build-id/41/41414141414141414141414141414141414141.debug",
317               CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
318 
319   LocalBinaryFinder finder({tmp.path() + "/root"});
320 
321   std::optional<FoundBinary> bin1 =
322       finder.FindBinary("/ignored_dir/ignored_name.so", "AAAAAAAAAAAAAAAAAAAA");
323   ASSERT_TRUE(bin1.has_value());
324   EXPECT_EQ(
325       bin1.value().file_name,
326       tmp.path() +
327           "/root/.build-id/41/41414141414141414141414141414141414141.debug");
328 }
329 
330 }  // namespace
331 }  // namespace profiling
332 }  // namespace perfetto
333 
334 #endif
335