1 /*
2 * Copyright (C) 2024 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/trace_processor/importers/perf/perf_tracker.h"
18
19 #include <cstdint>
20 #include <limits>
21 #include <memory>
22 #include <optional>
23
24 #include "perfetto/ext/base/flat_hash_map.h"
25 #include "src/trace_processor/importers/common/address_range.h"
26 #include "src/trace_processor/importers/common/mapping_tracker.h"
27 #include "src/trace_processor/importers/common/virtual_memory_mapping.h"
28 #include "src/trace_processor/importers/perf/aux_data_tokenizer.h"
29 #include "src/trace_processor/importers/perf/perf_event.h"
30 #include "src/trace_processor/importers/perf/spe_tokenizer.h"
31 #include "src/trace_processor/storage/trace_storage.h"
32
33 namespace perfetto::trace_processor::perf_importer {
34 namespace {
35
36 using third_party::simpleperf::proto::pbzero::FileFeature;
37 using DexFile = FileFeature::DexFile;
38 using ElfFile = FileFeature::ElfFile;
39 using KernelModule = FileFeature::KernelModule;
40 using DsoType = FileFeature::DsoType;
41 using Symbol = FileFeature::Symbol;
42
InsertSymbols(const FileFeature::Decoder & file,AddressRangeMap<std::string> & out)43 void InsertSymbols(const FileFeature::Decoder& file,
44 AddressRangeMap<std::string>& out) {
45 for (auto raw_symbol = file.symbol(); raw_symbol; ++raw_symbol) {
46 Symbol::Decoder symbol(*raw_symbol);
47 out.TrimOverlapsAndEmplace(
48 AddressRange::FromStartAndSize(symbol.vaddr(), symbol.len()),
49 symbol.name().ToStdString());
50 }
51 }
52
IsBpfMapping(const CreateMappingParams & params)53 bool IsBpfMapping(const CreateMappingParams& params) {
54 return params.name == "[bpf]";
55 }
56
57 } // namespace
58
PerfTracker(TraceProcessorContext * context)59 PerfTracker::PerfTracker(TraceProcessorContext* context)
60 : context_(context),
61 mapping_table_(context->storage->stack_profile_mapping_table()) {
62 RegisterAuxTokenizer(PERF_AUXTRACE_ARM_SPE, &SpeTokenizer::Create);
63 }
64
65 PerfTracker::~PerfTracker() = default;
66
67 base::StatusOr<std::unique_ptr<AuxDataTokenizer>>
CreateAuxDataTokenizer(AuxtraceInfoRecord info)68 PerfTracker::CreateAuxDataTokenizer(AuxtraceInfoRecord info) {
69 auto it = factories_.Find(info.type);
70 if (!it) {
71 return std::unique_ptr<AuxDataTokenizer>(
72 new DummyAuxDataTokenizer(context_));
73 }
74
75 return (*it)(context_, std::move(info));
76 }
77
AddSimpleperfFile2(const FileFeature::Decoder & file)78 void PerfTracker::AddSimpleperfFile2(const FileFeature::Decoder& file) {
79 Dso dso;
80 switch (file.type()) {
81 case DsoType::DSO_KERNEL:
82 InsertSymbols(file, kernel_symbols_);
83 return;
84
85 case DsoType::DSO_ELF_FILE: {
86 ElfFile::Decoder elf(file.elf_file());
87 dso.load_bias = file.min_vaddr() - elf.file_offset_of_min_vaddr();
88 break;
89 }
90
91 case DsoType::DSO_KERNEL_MODULE: {
92 KernelModule::Decoder module(file.kernel_module());
93 dso.load_bias = file.min_vaddr() - module.memory_offset_of_min_vaddr();
94 break;
95 }
96
97 case DsoType::DSO_DEX_FILE:
98 case DsoType::DSO_SYMBOL_MAP_FILE:
99 case DsoType::DSO_UNKNOWN_FILE:
100 default:
101 return;
102 }
103
104 InsertSymbols(file, dso.symbols);
105 files_.Insert(context_->storage->InternString(file.path()), std::move(dso));
106 }
107
SymbolizeFrames()108 void PerfTracker::SymbolizeFrames() {
109 const StringId kEmptyString = context_->storage->InternString("");
110 for (auto frame = context_->storage->mutable_stack_profile_frame_table()
111 ->IterateRows();
112 frame; ++frame) {
113 if (frame.name() != kNullStringId && frame.name() != kEmptyString) {
114 continue;
115 }
116
117 if (!TrySymbolizeFrame(frame.row_reference())) {
118 SymbolizeKernelFrame(frame.row_reference());
119 }
120 }
121 }
122
SymbolizeKernelFrame(tables::StackProfileFrameTable::RowReference frame)123 void PerfTracker::SymbolizeKernelFrame(
124 tables::StackProfileFrameTable::RowReference frame) {
125 const auto mapping = *mapping_table_.FindById(frame.mapping());
126 uint64_t address = static_cast<uint64_t>(frame.rel_pc()) +
127 static_cast<uint64_t>(mapping.start());
128 auto symbol = kernel_symbols_.Find(address);
129 if (symbol == kernel_symbols_.end()) {
130 return;
131 }
132 frame.set_name(
133 context_->storage->InternString(base::StringView(symbol->second)));
134 }
135
TrySymbolizeFrame(tables::StackProfileFrameTable::RowReference frame)136 bool PerfTracker::TrySymbolizeFrame(
137 tables::StackProfileFrameTable::RowReference frame) {
138 const auto mapping = *mapping_table_.FindById(frame.mapping());
139 auto* file = files_.Find(mapping.name());
140 if (!file) {
141 return false;
142 }
143
144 // Load bias is something we can only determine by looking at the actual elf
145 // file. Thus PERF_RECORD_MMAP{2} events do not record it. So we need to
146 // potentially do an adjustment here if the load_bias tracked in the mapping
147 // table and the one reported by the file are mismatched.
148 uint64_t adj = file->load_bias - static_cast<uint64_t>(mapping.load_bias());
149
150 auto symbol = file->symbols.Find(static_cast<uint64_t>(frame.rel_pc()) + adj);
151 if (symbol == file->symbols.end()) {
152 return false;
153 }
154 frame.set_name(
155 context_->storage->InternString(base::StringView(symbol->second)));
156 return true;
157 }
158
CreateKernelMemoryMapping(int64_t trace_ts,CreateMappingParams params)159 void PerfTracker::CreateKernelMemoryMapping(int64_t trace_ts,
160 CreateMappingParams params) {
161 // Ignore BPF mapping that spans the entire memory range
162 if (IsBpfMapping(params) &&
163 params.memory_range.size() == std::numeric_limits<uint64_t>::max()) {
164 return;
165 }
166 const KernelMemoryMapping* mapping =
167 &context_->mapping_tracker->CreateKernelMemoryMapping(std::move(params));
168
169 context_->storage->mutable_mmap_record_table()->Insert(
170 {trace_ts, std::nullopt, mapping->mapping_id()});
171 }
172
CreateUserMemoryMapping(int64_t trace_ts,UniquePid upid,CreateMappingParams params)173 void PerfTracker::CreateUserMemoryMapping(int64_t trace_ts,
174 UniquePid upid,
175 CreateMappingParams params) {
176 const UserMemoryMapping* mapping =
177 &context_->mapping_tracker->CreateUserMemoryMapping(upid,
178 std::move(params));
179
180 context_->storage->mutable_mmap_record_table()->Insert(
181 {trace_ts, upid, mapping->mapping_id()});
182 }
183
NotifyEndOfFile()184 void PerfTracker::NotifyEndOfFile() {
185 SymbolizeFrames();
186 }
187
RegisterAuxTokenizer(uint32_t type,AuxDataTokenizerFactory factory)188 void PerfTracker::RegisterAuxTokenizer(uint32_t type,
189 AuxDataTokenizerFactory factory) {
190 PERFETTO_CHECK(factories_.Insert(type, std::move(factory)).second);
191 }
192
193 } // namespace perfetto::trace_processor::perf_importer
194