xref: /aosp_15_r20/external/perfetto/src/kallsyms/kernel_symbol_map_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2019 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 "src/kallsyms/kernel_symbol_map.h"
18 
19 #include <cinttypes>
20 #include <random>
21 #include <unordered_map>
22 
23 #include "perfetto/ext/base/file_utils.h"
24 #include "perfetto/ext/base/string_utils.h"
25 #include "perfetto/ext/base/string_view.h"
26 #include "perfetto/ext/base/temp_file.h"
27 
28 #include "test/gtest_and_gmock.h"
29 
30 namespace perfetto {
31 namespace {
32 
33 using TokenId = KernelSymbolMap::TokenTable::TokenId;
34 
Lookup(KernelSymbolMap::TokenTable & tokens,TokenId id)35 std::string Lookup(KernelSymbolMap::TokenTable& tokens, TokenId id) {
36   base::StringView sv = tokens.Lookup(id);
37   std::string str;
38   str.reserve(sv.size() + 1);
39   for (const char c : sv)
40     str.push_back(c & 0x7f);
41   return str;
42 }
43 
TEST(KernelSymbolMapTest,TokenTable)44 TEST(KernelSymbolMapTest, TokenTable) {
45   KernelSymbolMap::TokenTable tokens;
46   ASSERT_EQ(tokens.Add("a"), 1u);
47   ASSERT_EQ(tokens.Add("bb"), 2u);
48   ASSERT_EQ(tokens.Add("ccc"), 3u);
49   ASSERT_EQ(tokens.Add("foobar"), 4u);
50   ASSERT_EQ(tokens.Add("foobaz"), 5u);
51   ASSERT_EQ(tokens.Add("_"), 6u);
52   ASSERT_EQ(Lookup(tokens, 0), "");
53   ASSERT_EQ(Lookup(tokens, 1), "a");
54   ASSERT_EQ(Lookup(tokens, 2), "bb");
55   ASSERT_EQ(Lookup(tokens, 3), "ccc");
56   ASSERT_EQ(Lookup(tokens, 4), "foobar");
57   ASSERT_EQ(Lookup(tokens, 5), "foobaz");
58   ASSERT_EQ(Lookup(tokens, 6), "_");
59   ASSERT_EQ(Lookup(tokens, 0), "");
60   ASSERT_EQ(Lookup(tokens, 42), "<error>");
61 }
62 
TEST(KernelSymbolMapTest,ManyTokens)63 TEST(KernelSymbolMapTest, ManyTokens) {
64   KernelSymbolMap::TokenTable tokens;
65   std::unordered_map<TokenId, std::string> tok_map;
66   static std::minstd_rand rng(0);
67   for (int rep = 0; rep < 10000; rep++) {
68     static const size_t kNameMax = 128;
69     std::string sym_name;
70     sym_name.reserve(kNameMax);
71     size_t len = 1 + (rng() % kNameMax);
72     for (size_t j = 0; j < len; j++)
73       sym_name.push_back(rng() & 0x7f);
74     TokenId id = tokens.Add(sym_name);
75     ASSERT_EQ(tok_map.count(id), 0u);
76     tok_map[id] = sym_name;
77   }
78   for (const auto& kv : tok_map) {
79     ASSERT_EQ(Lookup(tokens, kv.first), kv.second);
80   }
81 }
82 
TEST(KernelSymbolMapTest,EdgeCases)83 TEST(KernelSymbolMapTest, EdgeCases) {
84   base::TempFile tmp = base::TempFile::Create();
85   static const char kContents[] = R"(ffffff8f73e2fa10 t one
86 ffffff8f73e2fa20 t two_
87 ffffff8f73e2fa30 t _three  [module_name_ignored]
88 ffffff8f73e2fa40 x ignore
89 ffffff8f73e2fa40 t _fo_ur_
90 ffffff8f73e2fa50 t _five__.cfi
91 ffffff8f73e2fa60 t __si__x__
92 ffffff8f73e2fa70 t ___se___v_e__n___
93 ffffff8f73e2fa80 t _eight_omg_this_name_is_so_loooooooooooooooooooooooooooooooooooong_should_be_truncated_exactly_here_because_this_is_the_128_char_TRUNCATED_HERE
94 ffffff8f73e2fa90 t NiNe
95 ffffff8f73e2faa0 t tls_get.cfi_jt
96 )";
97   base::WriteAll(tmp.fd(), kContents, sizeof(kContents));
98   base::FlushFile(tmp.fd());
99 
100   KernelSymbolMap kallsyms;
101   EXPECT_EQ(kallsyms.Lookup(0x42), "");
102 
103   kallsyms.Parse(tmp.path().c_str());
104   EXPECT_EQ(kallsyms.num_syms(), 10u);
105 
106   // Test first exact lookups.
107   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa10ULL), "one");
108   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa20ULL), "two_");
109   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa30LL), "_three");
110   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa40ULL), "_fo_ur_");
111   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa60ULL), "__si__x__");
112   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa70ULL), "___se___v_e__n___");
113   EXPECT_EQ(
114       kallsyms.Lookup(0xffffff8f73e2fa80ULL),
115       "_eight_omg_this_name_is_so_loooooooooooooooooooooooooooooooooooong_"
116       "should_be_truncated_exactly_here_because_this_is_the_128_char");
117   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa90ULL), "NiNe");
118 
119   // Now check bound searches.
120   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa00ULL), "");
121   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa11ULL), "one");
122   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa19ULL), "one");
123   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa71ULL), "___se___v_e__n___");
124   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa91ULL), "NiNe");
125   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa99ULL), "NiNe");
126 
127   // We strip arm64 clang control flow integrity suffixes straight away.
128   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2fa50ULL), "_five__");
129   EXPECT_EQ(kallsyms.Lookup(0xffffff8f73e2faa0ULL), "tls_get");
130 
131   // This is too far from the last symbol and should fail.
132   EXPECT_EQ(kallsyms.Lookup(0xffffff8fffffffffULL), "");
133 }
134 
TEST(KernelSymbolMapTest,GoldenTest)135 TEST(KernelSymbolMapTest, GoldenTest) {
136   std::string fake_kallsyms;
137   fake_kallsyms.reserve(8 * 1024 * 1024);
138   static std::minstd_rand rng(0);
139   static const size_t kNameMax = 64;
140   std::map<uint64_t, std::string> symbols;
141   for (int rep = 0; rep < 1000; rep++) {
142     uint64_t addr = static_cast<uint64_t>(rng());
143     if (symbols.count(addr))
144       continue;
145     static const char kCharset[] = "_abcdef";
146     char sym_name[kNameMax + 1];  // Deliberately not initialized.
147     size_t sym_name_len = 1 + (rng() % kNameMax);
148     for (size_t i = 0; i < sym_name_len; i++)
149       sym_name[i] = kCharset[rng() % strlen(kCharset)];
150     sym_name[sym_name_len] = '\0';
151     base::StackString<128> line("%" PRIx64 " t %s\n", addr, sym_name);
152     symbols[addr] = sym_name;
153     fake_kallsyms += line.ToStdString();
154   }
155   base::TempFile tmp = base::TempFile::Create();
156   base::WriteAll(tmp.fd(), fake_kallsyms.data(), fake_kallsyms.size());
157   base::FlushFile(tmp.fd());
158 
159   KernelSymbolMap kallsyms;
160   kallsyms.Parse(tmp.path().c_str());
161   ASSERT_EQ(kallsyms.num_syms(), symbols.size());
162   for (const auto& kv : symbols) {
163     ASSERT_EQ(kallsyms.Lookup(kv.first), kv.second);
164   }
165 }
166 
167 }  // namespace
168 }  // namespace perfetto
169