1 /*
2 * Copyright (C) 2016 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 <memory>
18 #include <utility>
19
20 #include <android-base/file.h>
21 #include <android-base/logging.h>
22 #include <android-base/strings.h>
23
24 #include "JITDebugReader.h"
25 #include "dso.h"
26 #include "event_attr.h"
27 #include "event_type.h"
28 #include "record_file.h"
29 #include "report_utils.h"
30 #include "thread_tree.h"
31 #include "tracing.h"
32 #include "utils.h"
33
34 extern "C" {
35
36 struct Sample {
37 uint64_t ip;
38 uint32_t pid;
39 uint32_t tid;
40 const char* thread_comm;
41 uint64_t time;
42 uint32_t in_kernel;
43 uint32_t cpu;
44 uint64_t period;
45 };
46
47 struct TracingFieldFormat {
48 const char* name;
49 uint32_t offset;
50 uint32_t elem_size;
51 uint32_t elem_count;
52 uint32_t is_signed;
53 uint32_t is_dynamic;
54 };
55
56 struct TracingDataFormat {
57 uint32_t size;
58 uint32_t field_count;
59 TracingFieldFormat* fields;
60 };
61
62 struct Event {
63 const char* name;
64 TracingDataFormat tracing_data_format;
65 };
66
67 struct Mapping {
68 uint64_t start;
69 uint64_t end;
70 uint64_t pgoff;
71 };
72
73 struct SymbolEntry {
74 const char* dso_name;
75 uint64_t vaddr_in_file;
76 const char* symbol_name;
77 uint64_t symbol_addr;
78 uint64_t symbol_len;
79 Mapping* mapping;
80 };
81
82 struct CallChainEntry {
83 uint64_t ip;
84 SymbolEntry symbol;
85 };
86
87 struct CallChain {
88 uint32_t nr;
89 CallChainEntry* entries;
90 };
91
92 struct FeatureSection {
93 const char* data;
94 uint32_t data_size;
95 };
96
97 } // extern "C"
98
99 namespace simpleperf {
100
101 struct EventInfo {
102 perf_event_attr attr;
103 std::string name;
104
105 struct TracingInfo {
106 TracingDataFormat data_format;
107 std::vector<std::string> field_names;
108 std::vector<TracingFieldFormat> fields;
109 } tracing_info;
110 };
111
112 class ReportLib {
113 public:
ReportLib()114 ReportLib()
115 : log_severity_(new android::base::ScopedLogSeverity(android::base::INFO)),
116 record_filename_("perf.data"),
117 current_thread_(nullptr),
118 trace_offcpu_(false),
119 callchain_report_builder_(thread_tree_) {}
120
121 bool SetLogSeverity(const char* log_level);
122
SetSymfs(const char * symfs_dir)123 bool SetSymfs(const char* symfs_dir) { return Dso::SetSymFsDir(symfs_dir); }
124
SetRecordFile(const char * record_file)125 bool SetRecordFile(const char* record_file) {
126 record_filename_ = record_file;
127 return true;
128 }
129
130 bool SetKallsymsFile(const char* kallsyms_file);
131
ShowIpForUnknownSymbol()132 void ShowIpForUnknownSymbol() { thread_tree_.ShowIpForUnknownSymbol(); }
ShowArtFrames(bool show)133 void ShowArtFrames(bool show) {
134 bool remove_art_frame = !show;
135 callchain_report_builder_.SetRemoveArtFrame(remove_art_frame);
136 }
MergeJavaMethods(bool merge)137 void MergeJavaMethods(bool merge) { callchain_report_builder_.SetConvertJITFrame(merge); }
AddProguardMappingFile(const char * mapping_file)138 bool AddProguardMappingFile(const char* mapping_file) {
139 return callchain_report_builder_.AddProguardMappingFile(mapping_file);
140 }
141
142 Sample* GetNextSample();
GetEventOfCurrentSample()143 Event* GetEventOfCurrentSample() { return ¤t_event_; }
GetSymbolOfCurrentSample()144 SymbolEntry* GetSymbolOfCurrentSample() { return current_symbol_; }
GetCallChainOfCurrentSample()145 CallChain* GetCallChainOfCurrentSample() { return ¤t_callchain_; }
GetTracingDataOfCurrentSample()146 const char* GetTracingDataOfCurrentSample() { return current_tracing_data_; }
147
148 const char* GetBuildIdForPath(const char* path);
149 FeatureSection* GetFeatureSection(const char* feature_name);
150
151 private:
152 void SetCurrentSample();
153 const EventInfo* FindEventOfCurrentSample();
154 void CreateEvents();
155
156 bool OpenRecordFileIfNecessary();
157 Mapping* AddMapping(const MapEntry& map);
158
159 std::unique_ptr<android::base::ScopedLogSeverity> log_severity_;
160 std::string record_filename_;
161 std::unique_ptr<RecordFileReader> record_file_reader_;
162 ThreadTree thread_tree_;
163 std::unique_ptr<SampleRecord> current_record_;
164 const ThreadEntry* current_thread_;
165 Sample current_sample_;
166 Event current_event_;
167 SymbolEntry* current_symbol_;
168 CallChain current_callchain_;
169 const char* current_tracing_data_;
170 std::vector<std::unique_ptr<Mapping>> current_mappings_;
171 std::vector<CallChainEntry> callchain_entries_;
172 std::string build_id_string_;
173 std::vector<EventInfo> events_;
174 bool trace_offcpu_;
175 std::unordered_map<pid_t, std::unique_ptr<SampleRecord>> next_sample_cache_;
176 FeatureSection feature_section_;
177 std::vector<char> feature_section_data_;
178 CallChainReportBuilder callchain_report_builder_;
179 std::unique_ptr<Tracing> tracing_;
180 };
181
SetLogSeverity(const char * log_level)182 bool ReportLib::SetLogSeverity(const char* log_level) {
183 android::base::LogSeverity severity;
184 if (!GetLogSeverity(log_level, &severity)) {
185 LOG(ERROR) << "Unknown log severity: " << log_level;
186 return false;
187 }
188 log_severity_ = nullptr;
189 log_severity_.reset(new android::base::ScopedLogSeverity(severity));
190 return true;
191 }
192
SetKallsymsFile(const char * kallsyms_file)193 bool ReportLib::SetKallsymsFile(const char* kallsyms_file) {
194 std::string kallsyms;
195 if (!android::base::ReadFileToString(kallsyms_file, &kallsyms)) {
196 LOG(WARNING) << "Failed to read in kallsyms file from " << kallsyms_file;
197 return false;
198 }
199 Dso::SetKallsyms(std::move(kallsyms));
200 return true;
201 }
202
OpenRecordFileIfNecessary()203 bool ReportLib::OpenRecordFileIfNecessary() {
204 if (record_file_reader_ == nullptr) {
205 record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
206 if (record_file_reader_ == nullptr) {
207 return false;
208 }
209 record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
210 auto& meta_info = record_file_reader_->GetMetaInfoFeature();
211 if (auto it = meta_info.find("trace_offcpu"); it != meta_info.end()) {
212 trace_offcpu_ = it->second == "true";
213 }
214 }
215 return true;
216 }
217
GetNextSample()218 Sample* ReportLib::GetNextSample() {
219 if (!OpenRecordFileIfNecessary()) {
220 return nullptr;
221 }
222 while (true) {
223 std::unique_ptr<Record> record;
224 if (!record_file_reader_->ReadRecord(record)) {
225 return nullptr;
226 }
227 if (record == nullptr) {
228 return nullptr;
229 }
230 thread_tree_.Update(*record);
231 if (record->type() == PERF_RECORD_SAMPLE) {
232 if (trace_offcpu_) {
233 SampleRecord* r = static_cast<SampleRecord*>(record.release());
234 auto it = next_sample_cache_.find(r->tid_data.tid);
235 if (it == next_sample_cache_.end()) {
236 next_sample_cache_[r->tid_data.tid].reset(r);
237 continue;
238 } else {
239 record.reset(it->second.release());
240 it->second.reset(r);
241 }
242 }
243 current_record_.reset(static_cast<SampleRecord*>(record.release()));
244 break;
245 } else if (record->type() == PERF_RECORD_TRACING_DATA ||
246 record->type() == SIMPLE_PERF_RECORD_TRACING_DATA) {
247 const auto& r = *static_cast<TracingDataRecord*>(record.get());
248 tracing_.reset(new Tracing(std::vector<char>(r.data, r.data + r.data_size)));
249 }
250 }
251 SetCurrentSample();
252 return ¤t_sample_;
253 }
254
SetCurrentSample()255 void ReportLib::SetCurrentSample() {
256 current_mappings_.clear();
257 callchain_entries_.clear();
258 SampleRecord& r = *current_record_;
259 current_sample_.ip = r.ip_data.ip;
260 current_sample_.pid = r.tid_data.pid;
261 current_sample_.tid = r.tid_data.tid;
262 current_thread_ = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
263 current_sample_.thread_comm = current_thread_->comm;
264 current_sample_.time = r.time_data.time;
265 current_sample_.in_kernel = r.InKernel();
266 current_sample_.cpu = r.cpu_data.cpu;
267 if (trace_offcpu_) {
268 uint64_t next_time =
269 std::max(next_sample_cache_[r.tid_data.tid]->time_data.time, r.time_data.time + 1);
270 current_sample_.period = next_time - r.time_data.time;
271 } else {
272 current_sample_.period = r.period_data.period;
273 }
274
275 size_t kernel_ip_count;
276 std::vector<uint64_t> ips = r.GetCallChain(&kernel_ip_count);
277 std::vector<CallChainReportEntry> report_entries =
278 callchain_report_builder_.Build(current_thread_, ips, kernel_ip_count);
279
280 for (const auto& report_entry : report_entries) {
281 callchain_entries_.resize(callchain_entries_.size() + 1);
282 CallChainEntry& entry = callchain_entries_.back();
283 entry.ip = report_entry.ip;
284 if (report_entry.dso_name != nullptr) {
285 entry.symbol.dso_name = report_entry.dso_name;
286 } else {
287 entry.symbol.dso_name = report_entry.dso->GetReportPath().data();
288 }
289 entry.symbol.vaddr_in_file = report_entry.vaddr_in_file;
290 entry.symbol.symbol_name = report_entry.symbol->DemangledName();
291 entry.symbol.symbol_addr = report_entry.symbol->addr;
292 entry.symbol.symbol_len = report_entry.symbol->len;
293 entry.symbol.mapping = AddMapping(*report_entry.map);
294 }
295 current_sample_.ip = callchain_entries_[0].ip;
296 current_symbol_ = &(callchain_entries_[0].symbol);
297 current_callchain_.nr = callchain_entries_.size() - 1;
298 current_callchain_.entries = &callchain_entries_[1];
299 const EventInfo* event = FindEventOfCurrentSample();
300 current_event_.name = event->name.c_str();
301 current_event_.tracing_data_format = event->tracing_info.data_format;
302 if (current_event_.tracing_data_format.size > 0u && (r.sample_type & PERF_SAMPLE_RAW)) {
303 CHECK_GE(r.raw_data.size, current_event_.tracing_data_format.size);
304 current_tracing_data_ = r.raw_data.data;
305 } else {
306 current_tracing_data_ = nullptr;
307 }
308 }
309
FindEventOfCurrentSample()310 const EventInfo* ReportLib::FindEventOfCurrentSample() {
311 if (events_.empty()) {
312 CreateEvents();
313 }
314 size_t attr_index;
315 if (trace_offcpu_) {
316 // For trace-offcpu, we don't want to show event sched:sched_switch.
317 attr_index = 0;
318 } else {
319 attr_index = record_file_reader_->GetAttrIndexOfRecord(current_record_.get());
320 }
321 return &events_[attr_index];
322 }
323
CreateEvents()324 void ReportLib::CreateEvents() {
325 std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
326 events_.resize(attrs.size());
327 for (size_t i = 0; i < attrs.size(); ++i) {
328 events_[i].attr = *attrs[i].attr;
329 events_[i].name = GetEventNameByAttr(events_[i].attr);
330 EventInfo::TracingInfo& tracing_info = events_[i].tracing_info;
331 if (events_[i].attr.type == PERF_TYPE_TRACEPOINT && tracing_) {
332 TracingFormat format = tracing_->GetTracingFormatHavingId(events_[i].attr.config);
333 tracing_info.field_names.resize(format.fields.size());
334 tracing_info.fields.resize(format.fields.size());
335 for (size_t i = 0; i < format.fields.size(); ++i) {
336 tracing_info.field_names[i] = format.fields[i].name;
337 TracingFieldFormat& field = tracing_info.fields[i];
338 field.name = tracing_info.field_names[i].c_str();
339 field.offset = format.fields[i].offset;
340 field.elem_size = format.fields[i].elem_size;
341 field.elem_count = format.fields[i].elem_count;
342 field.is_signed = format.fields[i].is_signed;
343 field.is_dynamic = format.fields[i].is_dynamic;
344 }
345 if (tracing_info.fields.empty()) {
346 tracing_info.data_format.size = 0;
347 } else {
348 TracingFieldFormat& field = tracing_info.fields.back();
349 tracing_info.data_format.size = field.offset + field.elem_size * field.elem_count;
350 }
351 tracing_info.data_format.field_count = tracing_info.fields.size();
352 tracing_info.data_format.fields = &tracing_info.fields[0];
353 } else {
354 tracing_info.data_format.size = 0;
355 tracing_info.data_format.field_count = 0;
356 tracing_info.data_format.fields = nullptr;
357 }
358 }
359 }
360
AddMapping(const MapEntry & map)361 Mapping* ReportLib::AddMapping(const MapEntry& map) {
362 current_mappings_.emplace_back(std::unique_ptr<Mapping>(new Mapping));
363 Mapping* mapping = current_mappings_.back().get();
364 mapping->start = map.start_addr;
365 mapping->end = map.start_addr + map.len;
366 mapping->pgoff = map.pgoff;
367 return mapping;
368 }
369
GetBuildIdForPath(const char * path)370 const char* ReportLib::GetBuildIdForPath(const char* path) {
371 if (!OpenRecordFileIfNecessary()) {
372 build_id_string_.clear();
373 return build_id_string_.c_str();
374 }
375 BuildId build_id = Dso::FindExpectedBuildIdForPath(path);
376 if (build_id.IsEmpty()) {
377 build_id_string_.clear();
378 } else {
379 build_id_string_ = build_id.ToString();
380 }
381 return build_id_string_.c_str();
382 }
383
GetFeatureSection(const char * feature_name)384 FeatureSection* ReportLib::GetFeatureSection(const char* feature_name) {
385 if (!OpenRecordFileIfNecessary()) {
386 return nullptr;
387 }
388 int feature = PerfFileFormat::GetFeatureId(feature_name);
389 if (feature == -1 || !record_file_reader_->ReadFeatureSection(feature, &feature_section_data_)) {
390 return nullptr;
391 }
392 feature_section_.data = feature_section_data_.data();
393 feature_section_.data_size = feature_section_data_.size();
394 return &feature_section_;
395 }
396
397 } // namespace simpleperf
398
399 using ReportLib = simpleperf::ReportLib;
400
401 extern "C" {
402
403 #define EXPORT __attribute__((visibility("default")))
404
405 // Create a new instance,
406 // pass the instance to the other functions below.
407 ReportLib* CreateReportLib() EXPORT;
408 void DestroyReportLib(ReportLib* report_lib) EXPORT;
409
410 // Set log severity, different levels are:
411 // verbose, debug, info, warning, error, fatal.
412 bool SetLogSeverity(ReportLib* report_lib, const char* log_level) EXPORT;
413 bool SetSymfs(ReportLib* report_lib, const char* symfs_dir) EXPORT;
414 bool SetRecordFile(ReportLib* report_lib, const char* record_file) EXPORT;
415 bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) EXPORT;
416 void ShowIpForUnknownSymbol(ReportLib* report_lib) EXPORT;
417 void ShowArtFrames(ReportLib* report_lib, bool show) EXPORT;
418 void MergeJavaMethods(ReportLib* report_lib, bool merge) EXPORT;
419 bool AddProguardMappingFile(ReportLib* report_lib, const char* mapping_file) EXPORT;
420
421 Sample* GetNextSample(ReportLib* report_lib) EXPORT;
422 Event* GetEventOfCurrentSample(ReportLib* report_lib) EXPORT;
423 SymbolEntry* GetSymbolOfCurrentSample(ReportLib* report_lib) EXPORT;
424 CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) EXPORT;
425 const char* GetTracingDataOfCurrentSample(ReportLib* report_lib) EXPORT;
426
427 const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) EXPORT;
428 FeatureSection* GetFeatureSection(ReportLib* report_lib, const char* feature_name) EXPORT;
429 }
430
431 // Exported methods working with a client created instance
CreateReportLib()432 ReportLib* CreateReportLib() {
433 return new ReportLib();
434 }
435
DestroyReportLib(ReportLib * report_lib)436 void DestroyReportLib(ReportLib* report_lib) {
437 delete report_lib;
438 }
439
SetLogSeverity(ReportLib * report_lib,const char * log_level)440 bool SetLogSeverity(ReportLib* report_lib, const char* log_level) {
441 return report_lib->SetLogSeverity(log_level);
442 }
443
SetSymfs(ReportLib * report_lib,const char * symfs_dir)444 bool SetSymfs(ReportLib* report_lib, const char* symfs_dir) {
445 return report_lib->SetSymfs(symfs_dir);
446 }
447
SetRecordFile(ReportLib * report_lib,const char * record_file)448 bool SetRecordFile(ReportLib* report_lib, const char* record_file) {
449 return report_lib->SetRecordFile(record_file);
450 }
451
ShowIpForUnknownSymbol(ReportLib * report_lib)452 void ShowIpForUnknownSymbol(ReportLib* report_lib) {
453 return report_lib->ShowIpForUnknownSymbol();
454 }
455
ShowArtFrames(ReportLib * report_lib,bool show)456 void ShowArtFrames(ReportLib* report_lib, bool show) {
457 return report_lib->ShowArtFrames(show);
458 }
459
MergeJavaMethods(ReportLib * report_lib,bool merge)460 void MergeJavaMethods(ReportLib* report_lib, bool merge) {
461 return report_lib->MergeJavaMethods(merge);
462 }
463
SetKallsymsFile(ReportLib * report_lib,const char * kallsyms_file)464 bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) {
465 return report_lib->SetKallsymsFile(kallsyms_file);
466 }
467
AddProguardMappingFile(ReportLib * report_lib,const char * mapping_file)468 bool AddProguardMappingFile(ReportLib* report_lib, const char* mapping_file) {
469 return report_lib->AddProguardMappingFile(mapping_file);
470 }
471
GetNextSample(ReportLib * report_lib)472 Sample* GetNextSample(ReportLib* report_lib) {
473 return report_lib->GetNextSample();
474 }
475
GetEventOfCurrentSample(ReportLib * report_lib)476 Event* GetEventOfCurrentSample(ReportLib* report_lib) {
477 return report_lib->GetEventOfCurrentSample();
478 }
479
GetSymbolOfCurrentSample(ReportLib * report_lib)480 SymbolEntry* GetSymbolOfCurrentSample(ReportLib* report_lib) {
481 return report_lib->GetSymbolOfCurrentSample();
482 }
483
GetCallChainOfCurrentSample(ReportLib * report_lib)484 CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) {
485 return report_lib->GetCallChainOfCurrentSample();
486 }
487
GetTracingDataOfCurrentSample(ReportLib * report_lib)488 const char* GetTracingDataOfCurrentSample(ReportLib* report_lib) {
489 return report_lib->GetTracingDataOfCurrentSample();
490 }
491
GetBuildIdForPath(ReportLib * report_lib,const char * path)492 const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) {
493 return report_lib->GetBuildIdForPath(path);
494 }
495
GetFeatureSection(ReportLib * report_lib,const char * feature_name)496 FeatureSection* GetFeatureSection(ReportLib* report_lib, const char* feature_name) {
497 return report_lib->GetFeatureSection(feature_name);
498 }
499