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