• 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   symlink(tmp.AbsolutePath("real/elf1").c_str(),
203           tmp.AbsolutePath("sym/elf1").c_str());
204   tmp.TrackFile("sym/elf1");
205   symlink(tmp.AbsolutePath("real/dir1").c_str(),
206           tmp.AbsolutePath("sym/dir1").c_str());
207   tmp.TrackFile("sym/dir1");
208 
209   LocalBinaryIndexer indexer({tmp.AbsolutePath("sym")});
210 
211   std::optional<FoundBinary> bin1 =
212       indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
213   ASSERT_TRUE(bin1.has_value());
214   EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("sym/elf1"));
215 
216   std::optional<FoundBinary> bin2 =
217       indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
218   ASSERT_TRUE(bin2.has_value());
219   EXPECT_EQ(bin2.value().file_name, tmp.AbsolutePath("sym/dir1/elf2"));
220 
221   std::optional<FoundBinary> bin3 =
222       indexer.FindBinary("", "CCCCCCCCCCCCCCCCCCCC");
223   ASSERT_TRUE(bin3.has_value());
224   EXPECT_EQ(bin3.value().file_name, tmp.AbsolutePath("sym/dir1/elf3"));
225 }
226 
227 #if defined(MEMORY_SANITIZER)
228 // fts_read() causes some error under msan.
229 #define NOMSAN_RecursiveSymlinks DISABLED_RecursiveSymlinks
230 #else
231 #define NOMSAN_RecursiveSymlinks RecursiveSymlinks
232 #endif
TEST(LocalBinaryIndexerTest,NOMSAN_RecursiveSymlinks)233 TEST(LocalBinaryIndexerTest, NOMSAN_RecursiveSymlinks) {
234   base::TmpDirTree tmp;
235   tmp.AddDir("main");
236   tmp.AddFile("main/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
237   tmp.AddDir("main/dir1");
238   symlink(tmp.AbsolutePath("main").c_str(),
239           tmp.AbsolutePath("main/dir1/sym").c_str());
240   tmp.TrackFile("main/dir1/sym");
241 
242   LocalBinaryIndexer indexer({tmp.AbsolutePath("main")});
243 
244   std::optional<FoundBinary> bin1 =
245       indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
246   ASSERT_TRUE(bin1.has_value());
247   EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("main/elf1"));
248 }
249 
250 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||
251         // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) ||
252         // PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
253 
TEST(LocalBinaryFinderTest,AbsolutePath)254 TEST(LocalBinaryFinderTest, AbsolutePath) {
255   base::TmpDirTree tmp;
256   tmp.AddDir("root");
257   tmp.AddDir("root/dir");
258   tmp.AddFile("root/dir/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
259 
260   LocalBinaryFinder finder({tmp.path() + "/root"});
261 
262   std::optional<FoundBinary> bin1 =
263       finder.FindBinary("/dir/elf1.so", "AAAAAAAAAAAAAAAAAAAA");
264   ASSERT_TRUE(bin1.has_value());
265   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/dir/elf1.so");
266 }
267 
TEST(LocalBinaryFinderTest,AbsolutePathWithoutBaseApk)268 TEST(LocalBinaryFinderTest, AbsolutePathWithoutBaseApk) {
269   base::TmpDirTree tmp;
270   tmp.AddDir("root");
271   tmp.AddDir("root/dir");
272   tmp.AddFile("root/dir/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
273 
274   LocalBinaryFinder finder({tmp.path() + "/root"});
275 
276   std::optional<FoundBinary> bin1 =
277       finder.FindBinary("/dir/base.apk!elf1.so", "AAAAAAAAAAAAAAAAAAAA");
278   ASSERT_TRUE(bin1.has_value());
279   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/dir/elf1.so");
280 }
281 
TEST(LocalBinaryFinderTest,OnlyFilename)282 TEST(LocalBinaryFinderTest, OnlyFilename) {
283   base::TmpDirTree tmp;
284   tmp.AddDir("root");
285   tmp.AddFile("root/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
286 
287   LocalBinaryFinder finder({tmp.path() + "/root"});
288 
289   std::optional<FoundBinary> bin1 =
290       finder.FindBinary("/ignored_dir/elf1.so", "AAAAAAAAAAAAAAAAAAAA");
291   ASSERT_TRUE(bin1.has_value());
292   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/elf1.so");
293 }
294 
TEST(LocalBinaryFinderTest,OnlyFilenameWithoutBaseApk)295 TEST(LocalBinaryFinderTest, OnlyFilenameWithoutBaseApk) {
296   base::TmpDirTree tmp;
297   tmp.AddDir("root");
298   tmp.AddFile("root/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
299 
300   LocalBinaryFinder finder({tmp.path() + "/root"});
301 
302   std::optional<FoundBinary> bin1 = finder.FindBinary(
303       "/ignored_dir/base.apk!elf1.so", "AAAAAAAAAAAAAAAAAAAA");
304   ASSERT_TRUE(bin1.has_value());
305   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/elf1.so");
306 }
307 
TEST(LocalBinaryFinderTest,BuildIdSubdir)308 TEST(LocalBinaryFinderTest, BuildIdSubdir) {
309   base::TmpDirTree tmp;
310   tmp.AddDir("root");
311   tmp.AddDir("root/.build-id");
312   tmp.AddDir("root/.build-id/41");
313   tmp.AddFile("root/.build-id/41/41414141414141414141414141414141414141.debug",
314               CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
315 
316   LocalBinaryFinder finder({tmp.path() + "/root"});
317 
318   std::optional<FoundBinary> bin1 =
319       finder.FindBinary("/ignored_dir/ignored_name.so", "AAAAAAAAAAAAAAAAAAAA");
320   ASSERT_TRUE(bin1.has_value());
321   EXPECT_EQ(
322       bin1.value().file_name,
323       tmp.path() +
324           "/root/.build-id/41/41414141414141414141414141414141414141.debug");
325 }
326 
327 }  // namespace
328 }  // namespace profiling
329 }  // namespace perfetto
330 
331 #endif
332