1 /*
2 * Copyright (C) 2015 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 "dso.h"
18
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include <algorithm>
23 #include <limits>
24 #include <memory>
25 #include <vector>
26
27 #include <android-base/file.h>
28 #include <android-base/logging.h>
29 #include <android-base/strings.h>
30
31 #include "environment.h"
32 #include "read_apk.h"
33 #include "read_dex_file.h"
34 #include "read_elf.h"
35 #include "utils.h"
36
37 using namespace simpleperf;
38
39 namespace simpleperf_dso_impl {
40
RemovePathSeparatorSuffix(const std::string & path)41 std::string RemovePathSeparatorSuffix(const std::string& path) {
42 // Don't remove path separator suffix for '/'.
43 if (android::base::EndsWith(path, OS_PATH_SEPARATOR) && path.size() > 1u) {
44 return path.substr(0, path.size() - 1);
45 }
46 return path;
47 }
48
Reset()49 void DebugElfFileFinder::Reset() {
50 vdso_64bit_.clear();
51 vdso_32bit_.clear();
52 symfs_dir_.clear();
53 build_id_to_file_map_.clear();
54 }
55
SetSymFsDir(const std::string & symfs_dir)56 bool DebugElfFileFinder::SetSymFsDir(const std::string& symfs_dir) {
57 symfs_dir_ = RemovePathSeparatorSuffix(symfs_dir);
58 if (!IsDir(symfs_dir_)) {
59 LOG(ERROR) << "Invalid symfs_dir '" << symfs_dir_ << "'";
60 return false;
61 }
62 std::string build_id_list_file = symfs_dir_ + OS_PATH_SEPARATOR + "build_id_list";
63 std::string build_id_list;
64 if (android::base::ReadFileToString(build_id_list_file, &build_id_list)) {
65 for (auto& line : android::base::Split(build_id_list, "\n")) {
66 std::vector<std::string> items = android::base::Split(line, "=");
67 if (items.size() == 2u) {
68 build_id_to_file_map_[items[0]] = symfs_dir_ + OS_PATH_SEPARATOR + items[1];
69 }
70 }
71 }
72 return true;
73 }
74
AddSymbolDir(const std::string & symbol_dir)75 bool DebugElfFileFinder::AddSymbolDir(const std::string& symbol_dir) {
76 if (!IsDir(symbol_dir)) {
77 LOG(ERROR) << "Invalid symbol dir " << symbol_dir;
78 return false;
79 }
80 std::string dir = RemovePathSeparatorSuffix(symbol_dir);
81 CollectBuildIdInDir(dir);
82 return true;
83 }
84
CollectBuildIdInDir(const std::string & dir)85 void DebugElfFileFinder::CollectBuildIdInDir(const std::string& dir) {
86 for (const std::string& entry : GetEntriesInDir(dir)) {
87 std::string path = dir + OS_PATH_SEPARATOR + entry;
88 if (IsDir(path)) {
89 CollectBuildIdInDir(path);
90 } else {
91 BuildId build_id;
92 ElfStatus status;
93 auto elf = ElfFile::Open(path, &status);
94 if (status == ElfStatus::NO_ERROR && elf->GetBuildId(&build_id) == ElfStatus::NO_ERROR) {
95 build_id_to_file_map_[build_id.ToString()] = path;
96 }
97 }
98 }
99 }
100
SetVdsoFile(const std::string & vdso_file,bool is_64bit)101 void DebugElfFileFinder::SetVdsoFile(const std::string& vdso_file, bool is_64bit) {
102 if (is_64bit) {
103 vdso_64bit_ = vdso_file;
104 } else {
105 vdso_32bit_ = vdso_file;
106 }
107 }
108
CheckDebugFilePath(const std::string & path,BuildId & build_id,bool report_build_id_mismatch)109 static bool CheckDebugFilePath(const std::string& path, BuildId& build_id,
110 bool report_build_id_mismatch) {
111 ElfStatus status;
112 auto elf = ElfFile::Open(path, &status);
113 if (!elf) {
114 return false;
115 }
116 BuildId debug_build_id;
117 status = elf->GetBuildId(&debug_build_id);
118 if (status != ElfStatus::NO_ERROR && status != ElfStatus::NO_BUILD_ID) {
119 return false;
120 }
121
122 // Native libraries in apks and kernel modules may not have build ids.
123 // So build_id and debug_build_id can either be empty, or have the same value.
124 bool match = build_id == debug_build_id;
125 if (!match && report_build_id_mismatch) {
126 LOG(WARNING) << path << " isn't used because of build id mismatch: expected " << build_id
127 << ", real " << debug_build_id;
128 }
129 return match;
130 }
131
FindDebugFile(const std::string & dso_path,bool force_64bit,BuildId & build_id)132 std::string DebugElfFileFinder::FindDebugFile(const std::string& dso_path, bool force_64bit,
133 BuildId& build_id) {
134 if (dso_path == "[vdso]") {
135 if (force_64bit && !vdso_64bit_.empty()) {
136 return vdso_64bit_;
137 } else if (!force_64bit && !vdso_32bit_.empty()) {
138 return vdso_32bit_;
139 }
140 }
141 if (build_id.IsEmpty()) {
142 // Try reading build id from file if we don't already have one.
143 GetBuildIdFromDsoPath(dso_path, &build_id);
144 }
145
146 // 1. Try build_id_to_file_map.
147 if (!build_id_to_file_map_.empty()) {
148 if (!build_id.IsEmpty() || GetBuildIdFromDsoPath(dso_path, &build_id)) {
149 auto it = build_id_to_file_map_.find(build_id.ToString());
150 if (it != build_id_to_file_map_.end() && CheckDebugFilePath(it->second, build_id, false)) {
151 return it->second;
152 }
153 }
154 }
155 if (!symfs_dir_.empty()) {
156 // 2. Try concatenating symfs_dir and dso_path.
157 std::string path = GetPathInSymFsDir(dso_path);
158 if (CheckDebugFilePath(path, build_id, true)) {
159 return path;
160 }
161 // 3. Try concatenating symfs_dir and basename of dso_path.
162 path = symfs_dir_ + OS_PATH_SEPARATOR + android::base::Basename(dso_path);
163 if (CheckDebugFilePath(path, build_id, false)) {
164 return path;
165 }
166 }
167 // 4. Try concatenating /usr/lib/debug and dso_path.
168 // Linux host can store debug shared libraries in /usr/lib/debug.
169 if (CheckDebugFilePath("/usr/lib/debug" + dso_path, build_id, false)) {
170 return "/usr/lib/debug" + dso_path;
171 }
172 return dso_path;
173 }
174
GetPathInSymFsDir(const std::string & path)175 std::string DebugElfFileFinder::GetPathInSymFsDir(const std::string& path) {
176 auto add_symfs_prefix = [&](const std::string& path) {
177 if (android::base::StartsWith(path, OS_PATH_SEPARATOR)) {
178 return symfs_dir_ + path;
179 }
180 return symfs_dir_ + OS_PATH_SEPARATOR + path;
181 };
182 if (OS_PATH_SEPARATOR == '/') {
183 return add_symfs_prefix(path);
184 }
185 // Paths in recorded perf.data uses '/' as path separator. When reporting on Windows, it needs
186 // to be converted to '\\'.
187 auto tuple = SplitUrlInApk(path);
188 if (std::get<0>(tuple)) {
189 std::string apk_path = std::get<1>(tuple);
190 std::string entry_path = std::get<2>(tuple);
191 std::replace(apk_path.begin(), apk_path.end(), '/', OS_PATH_SEPARATOR);
192 return GetUrlInApk(add_symfs_prefix(apk_path), entry_path);
193 }
194 std::string elf_path = path;
195 std::replace(elf_path.begin(), elf_path.end(), '/', OS_PATH_SEPARATOR);
196 return add_symfs_prefix(elf_path);
197 }
198 } // namespace simpleperf_dso_imp
199
200 static OneTimeFreeAllocator symbol_name_allocator;
201
Symbol(std::string_view name,uint64_t addr,uint64_t len)202 Symbol::Symbol(std::string_view name, uint64_t addr, uint64_t len)
203 : addr(addr),
204 len(len),
205 name_(symbol_name_allocator.AllocateString(name)),
206 demangled_name_(nullptr),
207 dump_id_(UINT_MAX) {
208 }
209
DemangledName() const210 const char* Symbol::DemangledName() const {
211 if (demangled_name_ == nullptr) {
212 const std::string s = Dso::Demangle(name_);
213 if (s == name_) {
214 demangled_name_ = name_;
215 } else {
216 demangled_name_ = symbol_name_allocator.AllocateString(s);
217 }
218 }
219 return demangled_name_;
220 }
221
222 bool Dso::demangle_ = true;
223 std::string Dso::vmlinux_;
224 std::string Dso::kallsyms_;
225 bool Dso::read_kernel_symbols_from_proc_;
226 std::unordered_map<std::string, BuildId> Dso::build_id_map_;
227 size_t Dso::dso_count_;
228 uint32_t Dso::g_dump_id_;
229 simpleperf_dso_impl::DebugElfFileFinder Dso::debug_elf_file_finder_;
230
SetDemangle(bool demangle)231 void Dso::SetDemangle(bool demangle) { demangle_ = demangle; }
232
233 extern "C" char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n,
234 int* status);
235
Demangle(const std::string & name)236 std::string Dso::Demangle(const std::string& name) {
237 if (!demangle_) {
238 return name;
239 }
240 int status;
241 bool is_linker_symbol = (name.find(linker_prefix) == 0);
242 const char* mangled_str = name.c_str();
243 if (is_linker_symbol) {
244 mangled_str += linker_prefix.size();
245 }
246 std::string result = name;
247 char* demangled_name = __cxa_demangle(mangled_str, nullptr, nullptr, &status);
248 if (status == 0) {
249 if (is_linker_symbol) {
250 result = std::string("[linker]") + demangled_name;
251 } else {
252 result = demangled_name;
253 }
254 free(demangled_name);
255 } else if (is_linker_symbol) {
256 result = std::string("[linker]") + mangled_str;
257 }
258 return result;
259 }
260
SetSymFsDir(const std::string & symfs_dir)261 bool Dso::SetSymFsDir(const std::string& symfs_dir) {
262 return debug_elf_file_finder_.SetSymFsDir(symfs_dir);
263 }
264
AddSymbolDir(const std::string & symbol_dir)265 bool Dso::AddSymbolDir(const std::string& symbol_dir) {
266 return debug_elf_file_finder_.AddSymbolDir(symbol_dir);
267 }
268
SetVmlinux(const std::string & vmlinux)269 void Dso::SetVmlinux(const std::string& vmlinux) { vmlinux_ = vmlinux; }
270
SetBuildIds(const std::vector<std::pair<std::string,BuildId>> & build_ids)271 void Dso::SetBuildIds(
272 const std::vector<std::pair<std::string, BuildId>>& build_ids) {
273 std::unordered_map<std::string, BuildId> map;
274 for (auto& pair : build_ids) {
275 LOG(DEBUG) << "build_id_map: " << pair.first << ", "
276 << pair.second.ToString();
277 map.insert(pair);
278 }
279 build_id_map_ = std::move(map);
280 }
281
SetVdsoFile(const std::string & vdso_file,bool is_64bit)282 void Dso::SetVdsoFile(const std::string& vdso_file, bool is_64bit) {
283 debug_elf_file_finder_.SetVdsoFile(vdso_file, is_64bit);
284 }
285
FindExpectedBuildIdForPath(const std::string & path)286 BuildId Dso::FindExpectedBuildIdForPath(const std::string& path) {
287 auto it = build_id_map_.find(path);
288 if (it != build_id_map_.end()) {
289 return it->second;
290 }
291 return BuildId();
292 }
293
GetExpectedBuildId()294 BuildId Dso::GetExpectedBuildId() {
295 return FindExpectedBuildIdForPath(path_);
296 }
297
Dso(DsoType type,const std::string & path,const std::string & debug_file_path)298 Dso::Dso(DsoType type, const std::string& path, const std::string& debug_file_path)
299 : type_(type),
300 path_(path),
301 debug_file_path_(debug_file_path),
302 is_loaded_(false),
303 dump_id_(UINT_MAX),
304 symbol_dump_id_(0),
305 symbol_warning_loglevel_(android::base::WARNING) {
306 size_t pos = path.find_last_of("/\\");
307 if (pos != std::string::npos) {
308 file_name_ = path.substr(pos + 1);
309 } else {
310 file_name_ = path;
311 }
312 dso_count_++;
313 }
314
~Dso()315 Dso::~Dso() {
316 if (--dso_count_ == 0) {
317 // Clean up global variables when no longer used.
318 symbol_name_allocator.Clear();
319 demangle_ = true;
320 vmlinux_.clear();
321 kallsyms_.clear();
322 read_kernel_symbols_from_proc_ = false;
323 build_id_map_.clear();
324 g_dump_id_ = 0;
325 debug_elf_file_finder_.Reset();
326 }
327 }
328
CreateDumpId()329 uint32_t Dso::CreateDumpId() {
330 CHECK(!HasDumpId());
331 return dump_id_ = g_dump_id_++;
332 }
333
CreateSymbolDumpId(const Symbol * symbol)334 uint32_t Dso::CreateSymbolDumpId(const Symbol* symbol) {
335 CHECK(!symbol->HasDumpId());
336 symbol->dump_id_ = symbol_dump_id_++;
337 return symbol->dump_id_;
338 }
339
FindSymbol(uint64_t vaddr_in_dso)340 const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) {
341 if (!is_loaded_) {
342 Load();
343 }
344 auto it = std::upper_bound(symbols_.begin(), symbols_.end(),
345 Symbol("", vaddr_in_dso, 0),
346 Symbol::CompareValueByAddr);
347 if (it != symbols_.begin()) {
348 --it;
349 if (it->addr <= vaddr_in_dso && (it->addr + it->len > vaddr_in_dso)) {
350 return &*it;
351 }
352 }
353 if (!unknown_symbols_.empty()) {
354 auto it = unknown_symbols_.find(vaddr_in_dso);
355 if (it != unknown_symbols_.end()) {
356 return &it->second;
357 }
358 }
359 return nullptr;
360 }
361
SetSymbols(std::vector<Symbol> * symbols)362 void Dso::SetSymbols(std::vector<Symbol>* symbols) {
363 symbols_ = std::move(*symbols);
364 symbols->clear();
365 }
366
AddUnknownSymbol(uint64_t vaddr_in_dso,const std::string & name)367 void Dso::AddUnknownSymbol(uint64_t vaddr_in_dso, const std::string& name) {
368 unknown_symbols_.insert(std::make_pair(vaddr_in_dso, Symbol(name, vaddr_in_dso, 1)));
369 }
370
IsForJavaMethod()371 bool Dso::IsForJavaMethod() {
372 if (type_ == DSO_DEX_FILE) {
373 return true;
374 }
375 if (type_ == DSO_ELF_FILE) {
376 // JITDebugReader generates jit symfiles in "jit_app_cache:<file_start>-<file_end>" format.
377 if (path_.find(':') != std::string::npos) {
378 return true;
379 }
380 }
381 return false;
382 }
383
Load()384 void Dso::Load() {
385 is_loaded_ = true;
386 std::vector<Symbol> symbols = LoadSymbols();
387 if (symbols_.empty()) {
388 symbols_ = std::move(symbols);
389 } else {
390 std::vector<Symbol> merged_symbols;
391 std::set_union(symbols_.begin(), symbols_.end(), symbols.begin(), symbols.end(),
392 std::back_inserter(merged_symbols), Symbol::CompareValueByAddr);
393 symbols_ = std::move(merged_symbols);
394 }
395 }
396
ReportReadElfSymbolResult(ElfStatus result,const std::string & path,const std::string & debug_file_path,android::base::LogSeverity warning_loglevel=android::base::WARNING)397 static void ReportReadElfSymbolResult(ElfStatus result, const std::string& path,
398 const std::string& debug_file_path,
399 android::base::LogSeverity warning_loglevel = android::base::WARNING) {
400 if (result == ElfStatus::NO_ERROR) {
401 LOG(VERBOSE) << "Read symbols from " << debug_file_path << " successfully";
402 } else if (result == ElfStatus::NO_SYMBOL_TABLE) {
403 if (path == "[vdso]") {
404 // Vdso only contains dynamic symbol table, and we can't change that.
405 return;
406 }
407 // Lacking symbol table isn't considered as an error but worth reporting.
408 LOG(warning_loglevel) << debug_file_path << " doesn't contain symbol table";
409 } else {
410 LOG(warning_loglevel) << "failed to read symbols from " << debug_file_path << ": " << result;
411 }
412 }
413
SortAndFixSymbols(std::vector<Symbol> & symbols)414 static void SortAndFixSymbols(std::vector<Symbol>& symbols) {
415 std::sort(symbols.begin(), symbols.end(), Symbol::CompareValueByAddr);
416 Symbol* prev_symbol = nullptr;
417 for (auto& symbol : symbols) {
418 if (prev_symbol != nullptr && prev_symbol->len == 0) {
419 prev_symbol->len = symbol.addr - prev_symbol->addr;
420 }
421 prev_symbol = &symbol;
422 }
423 }
424
425 class DexFileDso : public Dso {
426 public:
DexFileDso(const std::string & path,const std::string & debug_file_path)427 DexFileDso(const std::string& path, const std::string& debug_file_path)
428 : Dso(DSO_DEX_FILE, path, debug_file_path) {}
429
AddDexFileOffset(uint64_t dex_file_offset)430 void AddDexFileOffset(uint64_t dex_file_offset) override {
431 auto it = std::lower_bound(dex_file_offsets_.begin(), dex_file_offsets_.end(),
432 dex_file_offset);
433 if (it != dex_file_offsets_.end() && *it == dex_file_offset) {
434 return;
435 }
436 dex_file_offsets_.insert(it, dex_file_offset);
437 }
438
DexFileOffsets()439 const std::vector<uint64_t>* DexFileOffsets() override {
440 return &dex_file_offsets_;
441 }
442
IpToVaddrInFile(uint64_t ip,uint64_t map_start,uint64_t map_pgoff)443 uint64_t IpToVaddrInFile(uint64_t ip, uint64_t map_start, uint64_t map_pgoff) override {
444 return ip - map_start + map_pgoff;
445 }
446
LoadSymbols()447 std::vector<Symbol> LoadSymbols() override {
448 std::vector<Symbol> symbols;
449 std::vector<DexFileSymbol> dex_file_symbols;
450 auto tuple = SplitUrlInApk(debug_file_path_);
451 bool status = false;
452 if (std::get<0>(tuple)) {
453 std::unique_ptr<ArchiveHelper> ahelper = ArchiveHelper::CreateInstance(std::get<1>(tuple));
454 ZipEntry entry;
455 std::vector<uint8_t> data;
456 if (ahelper &&
457 ahelper->FindEntry(std::get<2>(tuple), &entry) && ahelper->GetEntryData(entry, &data)) {
458 status = ReadSymbolsFromDexFileInMemory(data.data(), data.size(), dex_file_offsets_,
459 &dex_file_symbols);
460 }
461 } else {
462 status = ReadSymbolsFromDexFile(debug_file_path_, dex_file_offsets_, &dex_file_symbols);
463 }
464 if (!status) {
465 android::base::LogSeverity level = symbols_.empty() ? android::base::WARNING
466 : android::base::DEBUG;
467 LOG(level) << "Failed to read symbols from " << debug_file_path_;
468 return symbols;
469 }
470 LOG(VERBOSE) << "Read symbols from " << debug_file_path_ << " successfully";
471 for (auto& symbol : dex_file_symbols) {
472 symbols.emplace_back(symbol.name, symbol.offset, symbol.len);
473 }
474 SortAndFixSymbols(symbols);
475 return symbols;
476 }
477
478 private:
479 std::vector<uint64_t> dex_file_offsets_;
480 };
481
482 class ElfDso : public Dso {
483 public:
ElfDso(const std::string & path,const std::string & debug_file_path)484 ElfDso(const std::string& path, const std::string& debug_file_path)
485 : Dso(DSO_ELF_FILE, path, debug_file_path) {}
486
GetReportPath() const487 std::string_view GetReportPath() const override {
488 if (size_t colon_pos = path_.find(':'); colon_pos != std::string::npos) {
489 return "[JIT app cache]";
490 }
491 return path_;
492 }
493
SetMinExecutableVaddr(uint64_t min_vaddr,uint64_t file_offset)494 void SetMinExecutableVaddr(uint64_t min_vaddr, uint64_t file_offset) override {
495 min_vaddr_ = min_vaddr;
496 file_offset_of_min_vaddr_ = file_offset;
497 }
498
GetMinExecutableVaddr(uint64_t * min_vaddr,uint64_t * file_offset)499 void GetMinExecutableVaddr(uint64_t* min_vaddr, uint64_t* file_offset) override {
500 if (type_ == DSO_DEX_FILE) {
501 return dex_file_dso_->GetMinExecutableVaddr(min_vaddr, file_offset);
502 }
503 if (min_vaddr_ == uninitialized_value) {
504 min_vaddr_ = 0;
505 BuildId build_id = GetExpectedBuildId();
506
507 ElfStatus status;
508 auto elf = ElfFile::Open(debug_file_path_, &build_id, &status);
509 if (elf) {
510 min_vaddr_ = elf->ReadMinExecutableVaddr(&file_offset_of_min_vaddr_);
511 } else {
512 LOG(WARNING) << "failed to read min virtual address of " << debug_file_path_ << ": "
513 << status;
514 }
515 }
516 *min_vaddr = min_vaddr_;
517 *file_offset = file_offset_of_min_vaddr_;
518 }
519
IpToVaddrInFile(uint64_t ip,uint64_t map_start,uint64_t map_pgoff)520 uint64_t IpToVaddrInFile(uint64_t ip, uint64_t map_start, uint64_t map_pgoff) override {
521 if (type_ == DSO_DEX_FILE) {
522 return dex_file_dso_->IpToVaddrInFile(ip, map_start, map_pgoff);
523 }
524 uint64_t min_vaddr;
525 uint64_t file_offset_of_min_vaddr;
526 GetMinExecutableVaddr(&min_vaddr, &file_offset_of_min_vaddr);
527 if (file_offset_of_min_vaddr == uninitialized_value) {
528 return ip - map_start + min_vaddr;
529 }
530 // Apps may make part of the executable segment of a shared library writeable, which can
531 // generate multiple executable segments at runtime. So use map_pgoff to calculate
532 // vaddr_in_file.
533 return ip - map_start + map_pgoff - file_offset_of_min_vaddr + min_vaddr;
534 }
535
AddDexFileOffset(uint64_t dex_file_offset)536 void AddDexFileOffset(uint64_t dex_file_offset) override {
537 if (type_ == DSO_ELF_FILE) {
538 // When simpleperf does unwinding while recording, it processes mmap records before reading
539 // dex file linked list (via JITDebugReader). To process mmap records, it creates Dso
540 // objects of type ELF_FILE. Then after reading dex file linked list, it realizes some
541 // ELF_FILE Dso objects should actually be DEX_FILE, because they have dex file offsets.
542 // So here converts ELF_FILE Dso into DEX_FILE Dso.
543 type_ = DSO_DEX_FILE;
544 dex_file_dso_.reset(new DexFileDso(path_, path_));
545 }
546 dex_file_dso_->AddDexFileOffset(dex_file_offset);
547 }
548
DexFileOffsets()549 const std::vector<uint64_t>* DexFileOffsets() override {
550 return dex_file_dso_ ? dex_file_dso_->DexFileOffsets() : nullptr;
551 }
552
553 protected:
LoadSymbols()554 std::vector<Symbol> LoadSymbols() override {
555 if (dex_file_dso_) {
556 return dex_file_dso_->LoadSymbols();
557 }
558 std::vector<Symbol> symbols;
559 BuildId build_id = GetExpectedBuildId();
560 auto symbol_callback = [&](const ElfFileSymbol& symbol) {
561 if (symbol.is_func || (symbol.is_label && symbol.is_in_text_section)) {
562 symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len);
563 }
564 };
565 ElfStatus status;
566 auto elf = ElfFile::Open(debug_file_path_, &build_id, &status);
567 if (elf) {
568 status = elf->ParseSymbols(symbol_callback);
569 }
570 ReportReadElfSymbolResult(status, path_, debug_file_path_,
571 symbols_.empty() ? android::base::WARNING : android::base::DEBUG);
572 SortAndFixSymbols(symbols);
573 return symbols;
574 }
575
576 private:
577 static constexpr uint64_t uninitialized_value = std::numeric_limits<uint64_t>::max();
578
579 uint64_t min_vaddr_ = uninitialized_value;
580 uint64_t file_offset_of_min_vaddr_ = uninitialized_value;
581 std::unique_ptr<DexFileDso> dex_file_dso_;
582 };
583
584 class KernelDso : public Dso {
585 public:
KernelDso(const std::string & path,const std::string & debug_file_path)586 KernelDso(const std::string& path, const std::string& debug_file_path)
587 : Dso(DSO_KERNEL, path, debug_file_path) {}
588
IpToVaddrInFile(uint64_t ip,uint64_t,uint64_t)589 uint64_t IpToVaddrInFile(uint64_t ip, uint64_t, uint64_t) override {
590 return ip;
591 }
592
593 protected:
LoadSymbols()594 std::vector<Symbol> LoadSymbols() override {
595 std::vector<Symbol> symbols;
596 BuildId build_id = GetExpectedBuildId();
597 if (!vmlinux_.empty()) {
598 auto symbol_callback = [&](const ElfFileSymbol& symbol) {
599 if (symbol.is_func) {
600 symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len);
601 }
602 };
603 ElfStatus status;
604 auto elf = ElfFile::Open(vmlinux_, &build_id, &status);
605 if (elf) {
606 status = elf->ParseSymbols(symbol_callback);
607 }
608 ReportReadElfSymbolResult(status, path_, vmlinux_);
609 } else if (!kallsyms_.empty()) {
610 symbols = ReadSymbolsFromKallsyms(kallsyms_);
611 } else if (read_kernel_symbols_from_proc_ || !build_id.IsEmpty()) {
612 // Try /proc/kallsyms only when asked to do so, or when build id matches.
613 // Otherwise, it is likely to use /proc/kallsyms on host for perf.data recorded on device.
614 bool can_read_kallsyms = true;
615 if (!build_id.IsEmpty()) {
616 BuildId real_build_id;
617 if (!GetKernelBuildId(&real_build_id) || build_id != real_build_id) {
618 LOG(DEBUG) << "failed to read symbols from /proc/kallsyms: Build id mismatch";
619 can_read_kallsyms = false;
620 }
621 }
622 if (can_read_kallsyms) {
623 std::string kallsyms;
624 if (!android::base::ReadFileToString("/proc/kallsyms", &kallsyms)) {
625 LOG(DEBUG) << "failed to read /proc/kallsyms";
626 } else {
627 symbols = ReadSymbolsFromKallsyms(kallsyms);
628 }
629 }
630 }
631 SortAndFixSymbols(symbols);
632 if (!symbols.empty()) {
633 symbols.back().len = std::numeric_limits<uint64_t>::max() - symbols.back().addr;
634 }
635 return symbols;
636 }
637
638 private:
ReadSymbolsFromKallsyms(std::string & kallsyms)639 std::vector<Symbol> ReadSymbolsFromKallsyms(std::string& kallsyms) {
640 std::vector<Symbol> symbols;
641 auto symbol_callback = [&](const KernelSymbol& symbol) {
642 if (strchr("TtWw", symbol.type) && symbol.addr != 0u) {
643 symbols.emplace_back(symbol.name, symbol.addr, 0);
644 }
645 return false;
646 };
647 ProcessKernelSymbols(kallsyms, symbol_callback);
648 if (symbols.empty()) {
649 LOG(WARNING) << "Symbol addresses in /proc/kallsyms on device are all zero. "
650 "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible.";
651 }
652 return symbols;
653 }
654 };
655
656 class KernelModuleDso : public Dso {
657 public:
KernelModuleDso(const std::string & path,const std::string & debug_file_path)658 KernelModuleDso(const std::string& path, const std::string& debug_file_path)
659 : Dso(DSO_KERNEL_MODULE, path, debug_file_path) {}
660
IpToVaddrInFile(uint64_t ip,uint64_t map_start,uint64_t)661 uint64_t IpToVaddrInFile(uint64_t ip, uint64_t map_start, uint64_t) override {
662 return ip - map_start;
663 }
664
665 protected:
LoadSymbols()666 std::vector<Symbol> LoadSymbols() override {
667 std::vector<Symbol> symbols;
668 BuildId build_id = GetExpectedBuildId();
669 auto symbol_callback = [&](const ElfFileSymbol& symbol) {
670 if (symbol.is_func || symbol.is_in_text_section) {
671 symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len);
672 }
673 };
674 ElfStatus status;
675 auto elf = ElfFile::Open(debug_file_path_, &build_id, &status);
676 if (elf) {
677 status = elf->ParseSymbols(symbol_callback);
678 }
679 ReportReadElfSymbolResult(status, path_, debug_file_path_,
680 symbols_.empty() ? android::base::WARNING : android::base::DEBUG);
681 SortAndFixSymbols(symbols);
682 return symbols;
683 }
684 };
685
686 class UnknownDso : public Dso {
687 public:
UnknownDso(const std::string & path)688 UnknownDso(const std::string& path) : Dso(DSO_UNKNOWN_FILE, path, path) {}
689
IpToVaddrInFile(uint64_t ip,uint64_t,uint64_t)690 uint64_t IpToVaddrInFile(uint64_t ip, uint64_t, uint64_t) override {
691 return ip;
692 }
693
694 protected:
LoadSymbols()695 std::vector<Symbol> LoadSymbols() override {
696 return std::vector<Symbol>();
697 }
698 };
699
CreateDso(DsoType dso_type,const std::string & dso_path,bool force_64bit)700 std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path,
701 bool force_64bit) {
702 switch (dso_type) {
703 case DSO_ELF_FILE: {
704 BuildId build_id = FindExpectedBuildIdForPath(dso_path);
705 return std::unique_ptr<Dso>(new ElfDso(dso_path,
706 debug_elf_file_finder_.FindDebugFile(dso_path, force_64bit, build_id)));
707 }
708 case DSO_KERNEL:
709 return std::unique_ptr<Dso>(new KernelDso(dso_path, dso_path));
710 case DSO_KERNEL_MODULE: {
711 BuildId build_id = FindExpectedBuildIdForPath(dso_path);
712 return std::unique_ptr<Dso>(new KernelModuleDso(
713 dso_path, debug_elf_file_finder_.FindDebugFile(dso_path, force_64bit, build_id)));
714 }
715 case DSO_DEX_FILE:
716 return std::unique_ptr<Dso>(new DexFileDso(dso_path, dso_path));
717 case DSO_UNKNOWN_FILE:
718 return std::unique_ptr<Dso>(new UnknownDso(dso_path));
719 default:
720 LOG(FATAL) << "Unexpected dso_type " << static_cast<int>(dso_type);
721 }
722 return nullptr;
723 }
724
CreateElfDsoWithBuildId(const std::string & dso_path,BuildId & build_id)725 std::unique_ptr<Dso> Dso::CreateElfDsoWithBuildId(const std::string& dso_path, BuildId& build_id) {
726 return std::unique_ptr<Dso>(
727 new ElfDso(dso_path, debug_elf_file_finder_.FindDebugFile(dso_path, false, build_id)));
728 }
729
DsoTypeToString(DsoType dso_type)730 const char* DsoTypeToString(DsoType dso_type) {
731 switch (dso_type) {
732 case DSO_KERNEL:
733 return "dso_kernel";
734 case DSO_KERNEL_MODULE:
735 return "dso_kernel_module";
736 case DSO_ELF_FILE:
737 return "dso_elf_file";
738 case DSO_DEX_FILE:
739 return "dso_dex_file";
740 default:
741 return "unknown";
742 }
743 }
744
GetBuildIdFromDsoPath(const std::string & dso_path,BuildId * build_id)745 bool GetBuildIdFromDsoPath(const std::string& dso_path, BuildId* build_id) {
746 ElfStatus status;
747 auto elf = ElfFile::Open(dso_path, &status);
748 if (status == ElfStatus::NO_ERROR && elf->GetBuildId(build_id) == ElfStatus::NO_ERROR) {
749 return true;
750 }
751 return false;
752 }
753