|  | /* | 
|  | * Copyright (C) 2021 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "test/gtest_and_gmock.h" | 
|  |  | 
|  | #include "perfetto/ext/base/file_utils.h" | 
|  | #include "perfetto/ext/base/temp_file.h" | 
|  | #include "src/profiling/symbolizer/breakpad_parser.h" | 
|  |  | 
|  | namespace perfetto { | 
|  | namespace profiling { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Used to initialize parser objects. | 
|  | constexpr char kFakeFilePath[] = "bad/file/path"; | 
|  |  | 
|  | TEST(BreakpadParserTest, FileIsEmpty) { | 
|  | base::TempFile file = base::TempFile::Create(); | 
|  | BreakpadParser parser(file.path()); | 
|  | ASSERT_TRUE(parser.ParseFile()); | 
|  | EXPECT_TRUE(parser.symbols_for_testing().empty()); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, FileNotOpened) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | ASSERT_FALSE(parser.ParseFile()); | 
|  | EXPECT_TRUE(parser.symbols_for_testing().empty()); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, ContainsNoFuncRecord) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | constexpr char kTestFileContents[] = | 
|  | "MODULE mac x86_64 E3A0F28FBCB43C15986D8608AF1DD2380 exif.so\n" | 
|  | "FILE 0 /Applications/../MacOSX10.10.sdk/usr/include/ctype.h\n" | 
|  | "1031 2 39 4\n" | 
|  | "PUBLIC 313c0 0 items\n" | 
|  | "STACK CFI 1014 .cfa: $rbp 16 +\n"; | 
|  | ASSERT_TRUE(parser.ParseFromString(kTestFileContents)); | 
|  | EXPECT_TRUE(parser.symbols_for_testing().empty()); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, ContainsOneFuncRecord) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | constexpr char kTestFileContents[] = | 
|  | "MODULE mac x86_64 E3A0F28FBCB43C15986D8608AF1DD2380 exif.so\n" | 
|  | "FUNC 1010 23 0 foo::bar()\n" | 
|  | "1031 2 39 4\n" | 
|  | "PUBLIC 2e7c0 0 items\n"; | 
|  | ASSERT_TRUE(parser.ParseFromString(kTestFileContents)); | 
|  | ASSERT_EQ(parser.symbols_for_testing().size(), 1u); | 
|  | EXPECT_STREQ(parser.symbols_for_testing()[0].symbol_name.c_str(), | 
|  | "foo::bar()"); | 
|  | EXPECT_EQ(parser.symbols_for_testing()[0].start_address, | 
|  | static_cast<uint64_t>(0x1010)); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, ContainsManyFuncRecords) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | constexpr char kTestFileContents[] = | 
|  | "MODULE mac x86_64 E3A0F28FBCB43C15986D8608AF1DD2380 exif.so\n" | 
|  | "FUNC 1010 23 0 foo_foo\n" | 
|  | "1031 2 39 4\n" | 
|  | "FUNC 1040 84 0 bar_1\n" | 
|  | "1040 4 44 5\n" | 
|  | "FUNC 10d0 6b 0 baz_baz()\n"; | 
|  | ASSERT_TRUE(parser.ParseFromString(kTestFileContents)); | 
|  | ASSERT_EQ(parser.symbols_for_testing().size(), 3u); | 
|  | EXPECT_STREQ(parser.symbols_for_testing()[0].symbol_name.c_str(), "foo_foo"); | 
|  | EXPECT_EQ(parser.symbols_for_testing()[0].start_address, | 
|  | static_cast<uint64_t>(0x1010)); | 
|  | EXPECT_EQ(parser.symbols_for_testing()[0].function_size, 35u); | 
|  | EXPECT_STREQ(parser.symbols_for_testing()[1].symbol_name.c_str(), "bar_1"); | 
|  | EXPECT_EQ(parser.symbols_for_testing()[1].start_address, | 
|  | static_cast<uint64_t>(0x1040)); | 
|  | EXPECT_EQ(parser.symbols_for_testing()[1].function_size, 132u); | 
|  | EXPECT_STREQ(parser.symbols_for_testing()[2].symbol_name.c_str(), | 
|  | "baz_baz()"); | 
|  | EXPECT_EQ(parser.symbols_for_testing()[2].start_address, | 
|  | static_cast<uint64_t>(0x10d0)); | 
|  | EXPECT_EQ(parser.symbols_for_testing()[2].function_size, 107u); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, OptionalArgument) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | constexpr char kTestFileContents[] = | 
|  | "MODULE mac x86_64 E3A0F28FBCB43C15986D8608AF1DD2380 exif.so\n" | 
|  | "FUNC m 1010 23 0 foo_foo()\n" | 
|  | "1031 2 39 4\n" | 
|  | "FUNC m 1040 84 0 bar_1\n"; | 
|  | ASSERT_TRUE(parser.ParseFromString(kTestFileContents)); | 
|  | ASSERT_EQ(parser.symbols_for_testing().size(), 2u); | 
|  | EXPECT_STREQ(parser.symbols_for_testing()[0].symbol_name.c_str(), | 
|  | "foo_foo()"); | 
|  | EXPECT_EQ(parser.symbols_for_testing()[0].start_address, | 
|  | static_cast<uint64_t>(0x1010)); | 
|  | EXPECT_STREQ(parser.symbols_for_testing()[1].symbol_name.c_str(), "bar_1"); | 
|  | EXPECT_EQ(parser.symbols_for_testing()[1].start_address, | 
|  | static_cast<uint64_t>(0x1040)); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, FuncNameWithSpaces) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | constexpr char kTestFileContents[] = | 
|  | "MODULE mac x86_64 E3A0F28FBCB43C15986D8608AF1DD2380 exif.so\n" | 
|  | "FUNC 1010 23 0 foo foo foo\n" | 
|  | "1031 2 39 4\n" | 
|  | "FUNC 1040 84 0 bar\n" | 
|  | "1040 4 44 5\n" | 
|  | "FUNC 10d0 6b 0 baz\n"; | 
|  | ASSERT_TRUE(parser.ParseFromString(kTestFileContents)); | 
|  | ASSERT_EQ(parser.symbols_for_testing().size(), 3u); | 
|  | EXPECT_STREQ(parser.symbols_for_testing()[0].symbol_name.c_str(), | 
|  | "foo foo foo"); | 
|  | EXPECT_EQ(parser.symbols_for_testing()[0].start_address, | 
|  | static_cast<uint64_t>(0x1010)); | 
|  | EXPECT_STREQ(parser.symbols_for_testing()[2].symbol_name.c_str(), "baz"); | 
|  | EXPECT_EQ(parser.symbols_for_testing()[2].start_address, | 
|  | static_cast<uint64_t>(0x10d0)); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, NonHexAddress) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | constexpr char kTestFileContents[] = | 
|  | "MODULE mac x86_64 E3A0F28FBCB43C15986D8608AF1DD2380 exif.so\n" | 
|  | "FUNC foo 23 0 foo\n" | 
|  | "1031 2 39 4\n" | 
|  | "FUNC 1040 84 0 bar\n" | 
|  | "1040 4 44 5\n" | 
|  | "FUNC 10d0 6b 0 baz\n"; | 
|  | ASSERT_FALSE(parser.ParseFromString(kTestFileContents)); | 
|  | EXPECT_TRUE(parser.symbols_for_testing().empty()); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, NoModuleRecord) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | constexpr char kTestFileContents[] = | 
|  | "FUNC foo 23 0 foo()\n" | 
|  | "1031 2 39 4\n" | 
|  | "FUNC 1040 84 0 bar\n" | 
|  | "1040 4 44 5\n" | 
|  | "FUNC 10d0 6b 0 baz\n"; | 
|  | ASSERT_FALSE(parser.ParseFromString(kTestFileContents)); | 
|  | EXPECT_TRUE(parser.symbols_for_testing().empty()); | 
|  | } | 
|  |  | 
|  | // To make it easy to read, each FUNC record is followed by two LINE records: | 
|  | // one showing the start address of the ending instruction and one showing the | 
|  | // address where the function ends. | 
|  | constexpr char kGetSymbolTestContents[] = | 
|  | "MODULE mac x86_64 E3A0F28FBCB43C15986D8608AF1DD2380 exif.so\n" | 
|  | "FUNC 1010 23 0 foo\n" | 
|  | "1031 2 39 4\n" | 
|  | "1033 0 0 0\n" | 
|  | "FUNC 1040 84 0 bar\n" | 
|  | "10b6 e 44 5\n" | 
|  | "10c4 0 0 0\n" | 
|  | "FUNC 10d0 6b 0 baz\n" | 
|  | "1136 5 44 5\n" | 
|  | "113b 0 0 0\n"; | 
|  |  | 
|  | TEST(BreakpadParserTest, GivenStartAddr) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | ASSERT_TRUE(parser.ParseFromString(kGetSymbolTestContents)); | 
|  | ASSERT_EQ(parser.symbols_for_testing().size(), 3u); | 
|  | EXPECT_EQ(*parser.GetSymbol(0x1010U), "foo"); | 
|  | EXPECT_EQ(*parser.GetSymbol(0x10d0U), "baz"); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, GivenAddrInRange) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | ASSERT_TRUE(parser.ParseFromString(kGetSymbolTestContents)); | 
|  | ASSERT_EQ(parser.symbols_for_testing().size(), 3u); | 
|  | EXPECT_EQ(*parser.GetSymbol(0x1030U), "foo"); | 
|  | EXPECT_EQ(*parser.GetSymbol(0x10c0U), "bar"); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, AddrTooLow) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | ASSERT_TRUE(parser.ParseFromString(kGetSymbolTestContents)); | 
|  | ASSERT_EQ(parser.symbols_for_testing().size(), 3u); | 
|  | EXPECT_FALSE(parser.GetSymbol(0x1000U)); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, AddrTooHigh) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | ASSERT_TRUE(parser.ParseFromString(kGetSymbolTestContents)); | 
|  | ASSERT_EQ(parser.symbols_for_testing().size(), 3u); | 
|  | EXPECT_FALSE(parser.GetSymbol(0x3000U)); | 
|  | } | 
|  |  | 
|  | TEST(BreakpadParserTest, AddrBetweenFunctions) { | 
|  | BreakpadParser parser(kFakeFilePath); | 
|  | ASSERT_TRUE(parser.ParseFromString(kGetSymbolTestContents)); | 
|  | ASSERT_EQ(parser.symbols_for_testing().size(), 3u); | 
|  | EXPECT_FALSE(parser.GetSymbol(0x1036U)); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace profiling | 
|  | }  // namespace perfetto |