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 <optional>
19 #include <queue>
20 #include <utility>
21
22 #include <android-base/file.h>
23 #include <android-base/logging.h>
24 #include <android-base/strings.h>
25
26 #include "JITDebugReader.h"
27 #include "RecordFilter.h"
28 #include "dso.h"
29 #include "event_attr.h"
30 #include "event_type.h"
31 #include "record_file.h"
32 #include "report_utils.h"
33 #include "thread_tree.h"
34 #include "tracing.h"
35 #include "utils.h"
36
37 extern "C" {
38
39 struct Sample {
40 uint64_t ip;
41 uint32_t pid;
42 uint32_t tid;
43 const char* thread_comm;
44 uint64_t time;
45 uint32_t in_kernel;
46 uint32_t cpu;
47 uint64_t period;
48 };
49
50 struct TracingFieldFormat {
51 const char* name;
52 uint32_t offset;
53 uint32_t elem_size;
54 uint32_t elem_count;
55 uint32_t is_signed;
56 uint32_t is_dynamic;
57 };
58
59 struct TracingDataFormat {
60 uint32_t size;
61 uint32_t field_count;
62 TracingFieldFormat* fields;
63 };
64
65 struct Event {
66 const char* name;
67 TracingDataFormat tracing_data_format;
68 };
69
70 struct Mapping {
71 uint64_t start;
72 uint64_t end;
73 uint64_t pgoff;
74 };
75
76 struct SymbolEntry {
77 const char* dso_name;
78 uint64_t vaddr_in_file;
79 const char* symbol_name;
80 uint64_t symbol_addr;
81 uint64_t symbol_len;
82 Mapping* mapping;
83 };
84
85 struct CallChainEntry {
86 uint64_t ip;
87 SymbolEntry symbol;
88 };
89
90 struct CallChain {
91 uint32_t nr;
92 CallChainEntry* entries;
93 };
94
95 struct EventCounter {
96 const char* name;
97 uint64_t id;
98 uint64_t count;
99 };
100
101 struct EventCountersView {
102 size_t nr;
103 EventCounter* event_counter;
104 };
105
106 struct FeatureSection {
107 const char* data;
108 uint32_t data_size;
109 };
110
111 } // extern "C"
112
113 namespace simpleperf {
114 namespace {
115
116 struct EventInfo {
117 perf_event_attr attr;
118 std::string name;
119
120 struct TracingInfo {
121 TracingDataFormat data_format;
122 std::vector<std::string> field_names;
123 std::vector<TracingFieldFormat> fields;
124 } tracing_info;
125 };
126
127 // If a recording file is generated with --trace-offcpu, we can select TraceOffCpuMode to report.
128 // It affects which samples are reported, and how period in each sample is calculated.
129 enum class TraceOffCpuMode {
130 // Only report on-cpu samples, with period representing time spent on cpu.
131 ON_CPU,
132 // Only report off-cpu samples, with period representing time spent off cpu.
133 OFF_CPU,
134 // Report both on-cpu and off-cpu samples.
135 ON_OFF_CPU,
136 // Report on-cpu and off-cpu samples under the same event type.
137 MIXED_ON_OFF_CPU,
138 };
139
TraceOffCpuModeToString(TraceOffCpuMode mode)140 static std::string TraceOffCpuModeToString(TraceOffCpuMode mode) {
141 switch (mode) {
142 case TraceOffCpuMode::ON_CPU:
143 return "on-cpu";
144 case TraceOffCpuMode::OFF_CPU:
145 return "off-cpu";
146 case TraceOffCpuMode::ON_OFF_CPU:
147 return "on-off-cpu";
148 case TraceOffCpuMode::MIXED_ON_OFF_CPU:
149 return "mixed-on-off-cpu";
150 }
151 }
152
StringToTraceOffCpuMode(const std::string & s)153 static std::optional<TraceOffCpuMode> StringToTraceOffCpuMode(const std::string& s) {
154 if (s == "on-cpu") {
155 return TraceOffCpuMode::ON_CPU;
156 }
157 if (s == "off-cpu") {
158 return TraceOffCpuMode::OFF_CPU;
159 }
160 if (s == "on-off-cpu") {
161 return TraceOffCpuMode::ON_OFF_CPU;
162 }
163 if (s == "mixed-on-off-cpu") {
164 return TraceOffCpuMode::MIXED_ON_OFF_CPU;
165 }
166 return std::nullopt;
167 }
168
169 struct TraceOffCpuData {
170 std::vector<TraceOffCpuMode> supported_modes;
171 std::string supported_modes_string;
172 std::optional<TraceOffCpuMode> mode;
173 std::unordered_map<pid_t, std::unique_ptr<SampleRecord>> thread_map;
174 };
175
176 } // namespace
177
178 class ReportLib {
179 public:
ReportLib()180 ReportLib()
181 : log_severity_(new android::base::ScopedLogSeverity(android::base::INFO)),
182 record_filename_("perf.data"),
183 current_thread_(nullptr),
184 callchain_report_builder_(thread_tree_),
185 record_filter_(thread_tree_) {}
186
187 bool SetLogSeverity(const char* log_level);
188
SetSymfs(const char * symfs_dir)189 bool SetSymfs(const char* symfs_dir) { return Dso::SetSymFsDir(symfs_dir); }
190
SetRecordFile(const char * record_file)191 bool SetRecordFile(const char* record_file) {
192 if (record_file_reader_) {
193 LOG(ERROR) << "recording file " << record_filename_ << " has been opened";
194 return false;
195 }
196 record_filename_ = record_file;
197 return OpenRecordFileIfNecessary();
198 }
199
200 bool SetKallsymsFile(const char* kallsyms_file);
201
ShowIpForUnknownSymbol()202 void ShowIpForUnknownSymbol() { thread_tree_.ShowIpForUnknownSymbol(); }
ShowArtFrames(bool show)203 void ShowArtFrames(bool show) {
204 bool remove_art_frame = !show;
205 callchain_report_builder_.SetRemoveArtFrame(remove_art_frame);
206 }
RemoveMethod(const char * method_name_regex)207 bool RemoveMethod(const char* method_name_regex) {
208 return callchain_report_builder_.RemoveMethod(method_name_regex);
209 }
MergeJavaMethods(bool merge)210 void MergeJavaMethods(bool merge) { callchain_report_builder_.SetConvertJITFrame(merge); }
AddProguardMappingFile(const char * mapping_file)211 bool AddProguardMappingFile(const char* mapping_file) {
212 return callchain_report_builder_.AddProguardMappingFile(mapping_file);
213 }
214 const char* GetSupportedTraceOffCpuModes();
215 bool SetTraceOffCpuMode(const char* mode);
216 bool SetSampleFilter(const char** filters, int filters_len);
217 bool AggregateThreads(const char** thread_name_regex, int thread_name_regex_len);
218
219 Sample* GetNextSample();
GetEventOfCurrentSample()220 Event* GetEventOfCurrentSample() { return ¤t_event_; }
GetSymbolOfCurrentSample()221 SymbolEntry* GetSymbolOfCurrentSample() { return current_symbol_; }
GetCallChainOfCurrentSample()222 CallChain* GetCallChainOfCurrentSample() { return ¤t_callchain_; }
GetEventCountersOfCurrentSample()223 EventCountersView* GetEventCountersOfCurrentSample() {
224 event_counters_view_.nr = event_counters_.size();
225 event_counters_view_.event_counter = event_counters_.data();
226 return &event_counters_view_;
227 }
GetTracingDataOfCurrentSample()228 const char* GetTracingDataOfCurrentSample() { return current_tracing_data_; }
GetProcessNameOfCurrentSample()229 const char* GetProcessNameOfCurrentSample() {
230 const ThreadEntry* thread = thread_tree_.FindThread(current_sample_.pid);
231 return (thread != nullptr) ? thread->comm : "unknown";
232 }
233
234 const char* GetBuildIdForPath(const char* path);
235 FeatureSection* GetFeatureSection(const char* feature_name);
236
237 private:
238 std::unique_ptr<SampleRecord> GetNextSampleRecord();
239 void ProcessSampleRecord(std::unique_ptr<Record> r);
240 void ProcessSwitchRecord(std::unique_ptr<Record> r);
241 void AddSampleRecordToQueue(SampleRecord* r);
242 bool SetCurrentSample(std::unique_ptr<SampleRecord> sample_record);
243 void SetEventCounters(const SampleRecord& r);
244 const EventInfo& FindEvent(const SampleRecord& r);
245 void CreateEvents();
246
247 bool OpenRecordFileIfNecessary();
248 Mapping* AddMapping(const MapEntry& map);
249
250 std::unique_ptr<android::base::ScopedLogSeverity> log_severity_;
251 std::string record_filename_;
252 std::unique_ptr<RecordFileReader> record_file_reader_;
253 ThreadTree thread_tree_;
254 std::queue<std::unique_ptr<SampleRecord>> sample_record_queue_;
255 const ThreadEntry* current_thread_;
256 Sample current_sample_;
257 Event current_event_;
258 SymbolEntry* current_symbol_;
259 CallChain current_callchain_;
260 std::vector<EventCounter> event_counters_;
261 EventCountersView event_counters_view_;
262 const char* current_tracing_data_;
263 std::vector<std::unique_ptr<Mapping>> current_mappings_;
264 std::vector<CallChainEntry> callchain_entries_;
265 std::string build_id_string_;
266 std::vector<EventInfo> events_;
267 TraceOffCpuData trace_offcpu_;
268 FeatureSection feature_section_;
269 std::vector<char> feature_section_data_;
270 CallChainReportBuilder callchain_report_builder_;
271 ThreadReportBuilder thread_report_builder_;
272 std::unique_ptr<Tracing> tracing_;
273 RecordFilter record_filter_;
274 };
275
SetLogSeverity(const char * log_level)276 bool ReportLib::SetLogSeverity(const char* log_level) {
277 android::base::LogSeverity severity;
278 if (!GetLogSeverity(log_level, &severity)) {
279 LOG(ERROR) << "Unknown log severity: " << log_level;
280 return false;
281 }
282 log_severity_ = nullptr;
283 log_severity_.reset(new android::base::ScopedLogSeverity(severity));
284 return true;
285 }
286
SetKallsymsFile(const char * kallsyms_file)287 bool ReportLib::SetKallsymsFile(const char* kallsyms_file) {
288 std::string kallsyms;
289 if (!android::base::ReadFileToString(kallsyms_file, &kallsyms)) {
290 LOG(WARNING) << "Failed to read in kallsyms file from " << kallsyms_file;
291 return false;
292 }
293 Dso::SetKallsyms(std::move(kallsyms));
294 return true;
295 }
296
GetSupportedTraceOffCpuModes()297 const char* ReportLib::GetSupportedTraceOffCpuModes() {
298 if (!OpenRecordFileIfNecessary()) {
299 return nullptr;
300 }
301 std::string& s = trace_offcpu_.supported_modes_string;
302 s.clear();
303 for (auto mode : trace_offcpu_.supported_modes) {
304 if (!s.empty()) {
305 s += ",";
306 }
307 s += TraceOffCpuModeToString(mode);
308 }
309 return s.data();
310 }
311
SetTraceOffCpuMode(const char * mode)312 bool ReportLib::SetTraceOffCpuMode(const char* mode) {
313 auto mode_value = StringToTraceOffCpuMode(mode);
314 if (!mode_value) {
315 return false;
316 }
317 if (!OpenRecordFileIfNecessary()) {
318 return false;
319 }
320 auto& modes = trace_offcpu_.supported_modes;
321 if (std::find(modes.begin(), modes.end(), mode_value) == modes.end()) {
322 return false;
323 }
324 trace_offcpu_.mode = mode_value;
325 return true;
326 }
327
SetSampleFilter(const char ** filters,int filters_len)328 bool ReportLib::SetSampleFilter(const char** filters, int filters_len) {
329 std::vector<std::string> args;
330 for (int i = 0; i < filters_len; i++) {
331 args.emplace_back(filters[i]);
332 }
333 OptionFormatMap option_formats = GetRecordFilterOptionFormats(false);
334 OptionValueMap options;
335 std::vector<std::pair<OptionName, OptionValue>> ordered_options;
336 if (!ConvertArgsToOptions(args, option_formats, "", &options, &ordered_options, nullptr)) {
337 return false;
338 }
339 return record_filter_.ParseOptions(options);
340 }
341
AggregateThreads(const char ** thread_name_regex,int thread_name_regex_len)342 bool ReportLib::AggregateThreads(const char** thread_name_regex, int thread_name_regex_len) {
343 std::vector<std::string> regs(thread_name_regex_len);
344 for (int i = 0; i < thread_name_regex_len; ++i) {
345 regs[i] = thread_name_regex[i];
346 }
347 return thread_report_builder_.AggregateThreads(regs);
348 }
349
OpenRecordFileIfNecessary()350 bool ReportLib::OpenRecordFileIfNecessary() {
351 if (record_file_reader_ == nullptr) {
352 record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
353 if (record_file_reader_ == nullptr) {
354 return false;
355 }
356 if (!record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_)) {
357 return false;
358 }
359 auto& meta_info = record_file_reader_->GetMetaInfoFeature();
360 if (auto it = meta_info.find("trace_offcpu"); it != meta_info.end() && it->second == "true") {
361 // If recorded with --trace-offcpu, default is to report on-off-cpu samples.
362 std::string event_name = GetEventNameByAttr(record_file_reader_->AttrSection()[0].attr);
363 if (!android::base::StartsWith(event_name, "cpu-clock") &&
364 !android::base::StartsWith(event_name, "task-clock")) {
365 LOG(ERROR) << "Recording file " << record_filename_ << " is no longer supported. "
366 << "--trace-offcpu must be used with `-e cpu-clock` or `-e task-clock`.";
367 return false;
368 }
369 trace_offcpu_.mode = TraceOffCpuMode::MIXED_ON_OFF_CPU;
370 trace_offcpu_.supported_modes.push_back(TraceOffCpuMode::MIXED_ON_OFF_CPU);
371 trace_offcpu_.supported_modes.push_back(TraceOffCpuMode::ON_OFF_CPU);
372 trace_offcpu_.supported_modes.push_back(TraceOffCpuMode::ON_CPU);
373 trace_offcpu_.supported_modes.push_back(TraceOffCpuMode::OFF_CPU);
374 }
375 if (!record_filter_.CheckClock(record_file_reader_->GetClockId())) {
376 LOG(ERROR) << "Recording file " << record_filename_ << " doesn't match the clock of filter.";
377 return false;
378 }
379 }
380 return true;
381 }
382
GetNextSample()383 Sample* ReportLib::GetNextSample() {
384 if (!OpenRecordFileIfNecessary()) {
385 return nullptr;
386 }
387
388 while (true) {
389 std::unique_ptr<SampleRecord> r = GetNextSampleRecord();
390 if (!r) {
391 break;
392 }
393 if (SetCurrentSample(std::move(r))) {
394 return ¤t_sample_;
395 }
396 }
397 return nullptr;
398 }
399
GetNextSampleRecord()400 std::unique_ptr<SampleRecord> ReportLib::GetNextSampleRecord() {
401 while (sample_record_queue_.empty()) {
402 std::unique_ptr<Record> record;
403 if (!record_file_reader_->ReadRecord(record) || record == nullptr) {
404 return nullptr;
405 }
406 thread_tree_.Update(*record);
407 if (record->type() == PERF_RECORD_SAMPLE) {
408 ProcessSampleRecord(std::move(record));
409 } else if (record->type() == PERF_RECORD_SWITCH ||
410 record->type() == PERF_RECORD_SWITCH_CPU_WIDE) {
411 ProcessSwitchRecord(std::move(record));
412 } else if (record->type() == PERF_RECORD_TRACING_DATA ||
413 record->type() == SIMPLE_PERF_RECORD_TRACING_DATA) {
414 const auto& r = *static_cast<TracingDataRecord*>(record.get());
415 tracing_ = Tracing::Create(std::vector<char>(r.data, r.data + r.data_size));
416 if (!tracing_) {
417 return nullptr;
418 }
419 }
420 }
421 std::unique_ptr<SampleRecord> result = std::move(sample_record_queue_.front());
422 sample_record_queue_.pop();
423 return result;
424 }
425
ProcessSampleRecord(std::unique_ptr<Record> r)426 void ReportLib::ProcessSampleRecord(std::unique_ptr<Record> r) {
427 auto sr = static_cast<SampleRecord*>(r.get());
428 if (!trace_offcpu_.mode) {
429 r.release();
430 AddSampleRecordToQueue(sr);
431 return;
432 }
433 size_t attr_index = record_file_reader_->GetAttrIndexOfRecord(sr);
434 bool offcpu_sample = attr_index > 0;
435 if (trace_offcpu_.mode == TraceOffCpuMode::ON_CPU) {
436 if (!offcpu_sample) {
437 r.release();
438 AddSampleRecordToQueue(sr);
439 }
440 return;
441 }
442 uint32_t tid = sr->tid_data.tid;
443 auto it = trace_offcpu_.thread_map.find(tid);
444 if (it == trace_offcpu_.thread_map.end() || !it->second) {
445 // If there is no previous off-cpu sample, then store the current off-cpu sample.
446 if (offcpu_sample) {
447 r.release();
448 if (it == trace_offcpu_.thread_map.end()) {
449 trace_offcpu_.thread_map[tid].reset(sr);
450 } else {
451 it->second.reset(sr);
452 }
453 }
454 } else {
455 // If there is a previous off-cpu sample, update its period.
456 SampleRecord* prev_sr = it->second.get();
457 prev_sr->period_data.period =
458 (prev_sr->Timestamp() < sr->Timestamp()) ? (sr->Timestamp() - prev_sr->Timestamp()) : 1;
459 it->second.release();
460 AddSampleRecordToQueue(prev_sr);
461 if (offcpu_sample) {
462 r.release();
463 it->second.reset(sr);
464 }
465 }
466 if (!offcpu_sample && (trace_offcpu_.mode == TraceOffCpuMode::ON_OFF_CPU ||
467 trace_offcpu_.mode == TraceOffCpuMode::MIXED_ON_OFF_CPU)) {
468 r.release();
469 AddSampleRecordToQueue(sr);
470 }
471 }
472
ProcessSwitchRecord(std::unique_ptr<Record> r)473 void ReportLib::ProcessSwitchRecord(std::unique_ptr<Record> r) {
474 if (r->header.misc & PERF_RECORD_MISC_SWITCH_OUT) {
475 return;
476 }
477 uint32_t tid = r->sample_id.tid_data.tid;
478 auto it = trace_offcpu_.thread_map.find(tid);
479 if (it != trace_offcpu_.thread_map.end() && it->second) {
480 // If there is a previous off-cpu sample, update its period.
481 SampleRecord* prev_sr = it->second.get();
482 prev_sr->period_data.period =
483 (prev_sr->Timestamp() < r->Timestamp()) ? (r->Timestamp() - prev_sr->Timestamp()) : 1;
484 it->second.release();
485 AddSampleRecordToQueue(prev_sr);
486 }
487 }
488
AddSampleRecordToQueue(SampleRecord * r)489 void ReportLib::AddSampleRecordToQueue(SampleRecord* r) {
490 if (record_filter_.Check(*r)) {
491 sample_record_queue_.emplace(r);
492 }
493 }
494
SetCurrentSample(std::unique_ptr<SampleRecord> sample_record)495 bool ReportLib::SetCurrentSample(std::unique_ptr<SampleRecord> sample_record) {
496 const SampleRecord& r = *sample_record;
497 current_mappings_.clear();
498 callchain_entries_.clear();
499 current_sample_.ip = r.ip_data.ip;
500 current_thread_ = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
501 ThreadReport thread_report = thread_report_builder_.Build(*current_thread_);
502 current_sample_.pid = thread_report.pid;
503 current_sample_.tid = thread_report.tid;
504 current_sample_.thread_comm = thread_report.thread_name;
505 current_sample_.time = r.time_data.time;
506 current_sample_.in_kernel = r.InKernel();
507 current_sample_.cpu = r.cpu_data.cpu;
508 current_sample_.period = r.period_data.period;
509
510 size_t kernel_ip_count;
511 std::vector<uint64_t> ips = r.GetCallChain(&kernel_ip_count);
512 std::vector<CallChainReportEntry> report_entries =
513 callchain_report_builder_.Build(current_thread_, ips, kernel_ip_count);
514 if (report_entries.empty()) {
515 // Skip samples with callchain fully removed by RemoveMethod().
516 return false;
517 }
518
519 for (const auto& report_entry : report_entries) {
520 callchain_entries_.resize(callchain_entries_.size() + 1);
521 CallChainEntry& entry = callchain_entries_.back();
522 entry.ip = report_entry.ip;
523 if (report_entry.dso_name != nullptr) {
524 entry.symbol.dso_name = report_entry.dso_name;
525 } else {
526 entry.symbol.dso_name = report_entry.dso->GetReportPath().data();
527 }
528 entry.symbol.vaddr_in_file = report_entry.vaddr_in_file;
529 entry.symbol.symbol_name = report_entry.symbol->DemangledName();
530 entry.symbol.symbol_addr = report_entry.symbol->addr;
531 entry.symbol.symbol_len = report_entry.symbol->len;
532 entry.symbol.mapping = AddMapping(*report_entry.map);
533 }
534 current_sample_.ip = callchain_entries_[0].ip;
535 current_symbol_ = &(callchain_entries_[0].symbol);
536 current_callchain_.nr = callchain_entries_.size() - 1;
537 current_callchain_.entries = &callchain_entries_[1];
538 const EventInfo& event = FindEvent(r);
539 current_event_.name = event.name.c_str();
540 current_event_.tracing_data_format = event.tracing_info.data_format;
541 if (current_event_.tracing_data_format.size > 0u && (r.sample_type & PERF_SAMPLE_RAW)) {
542 CHECK_GE(r.raw_data.size, current_event_.tracing_data_format.size);
543 current_tracing_data_ = r.raw_data.data;
544 } else {
545 current_tracing_data_ = nullptr;
546 }
547 SetEventCounters(r);
548 return true;
549 }
550
SetEventCounters(const SampleRecord & r)551 void ReportLib::SetEventCounters(const SampleRecord& r) {
552 const std::vector<uint64_t>& ids = r.read_data.ids;
553 const std::vector<uint64_t>& counts = r.read_data.counts;
554 CHECK_EQ(ids.size(), counts.size());
555
556 event_counters_.clear();
557 for (size_t i = 0; i < ids.size(); i++) {
558 uint64_t event_id = ids[i];
559 uint64_t count = counts[i];
560 std::optional<size_t> attr_index = record_file_reader_->GetAttrIndexByEventId(event_id);
561 if (!attr_index) {
562 LOG(ERROR) << "Failed to find event name for event id " << event_id;
563 continue;
564 }
565
566 event_counters_.emplace_back(events_[*attr_index].name.c_str(), event_id, count);
567 }
568 }
569
FindEvent(const SampleRecord & r)570 const EventInfo& ReportLib::FindEvent(const SampleRecord& r) {
571 if (events_.empty()) {
572 CreateEvents();
573 }
574 if (trace_offcpu_.mode == TraceOffCpuMode::MIXED_ON_OFF_CPU) {
575 // To mix on-cpu and off-cpu samples, pretend they are from the same event type.
576 // Otherwise, some report scripts may split them.
577 return events_[0];
578 }
579 size_t attr_index = record_file_reader_->GetAttrIndexOfRecord(&r);
580 return events_[attr_index];
581 }
582
CreateEvents()583 void ReportLib::CreateEvents() {
584 const EventAttrIds& attrs = record_file_reader_->AttrSection();
585 events_.resize(attrs.size());
586 for (size_t i = 0; i < attrs.size(); ++i) {
587 events_[i].attr = attrs[i].attr;
588 events_[i].name = GetEventNameByAttr(events_[i].attr);
589 EventInfo::TracingInfo& tracing_info = events_[i].tracing_info;
590 tracing_info.data_format.size = 0;
591 tracing_info.data_format.field_count = 0;
592 tracing_info.data_format.fields = nullptr;
593
594 if (events_[i].attr.type == PERF_TYPE_TRACEPOINT && tracing_) {
595 std::optional<TracingFormat> opt_format =
596 tracing_->GetTracingFormatHavingId(events_[i].attr.config);
597 if (!opt_format.has_value() || opt_format.value().fields.empty()) {
598 continue;
599 }
600 const TracingFormat& format = opt_format.value();
601 tracing_info.field_names.resize(format.fields.size());
602 tracing_info.fields.resize(format.fields.size());
603 for (size_t i = 0; i < format.fields.size(); ++i) {
604 tracing_info.field_names[i] = format.fields[i].name;
605 TracingFieldFormat& field = tracing_info.fields[i];
606 field.name = tracing_info.field_names[i].c_str();
607 field.offset = format.fields[i].offset;
608 field.elem_size = format.fields[i].elem_size;
609 field.elem_count = format.fields[i].elem_count;
610 field.is_signed = format.fields[i].is_signed;
611 field.is_dynamic = format.fields[i].is_dynamic;
612 }
613 TracingFieldFormat& field = tracing_info.fields.back();
614 tracing_info.data_format.size = field.offset + field.elem_size * field.elem_count;
615 tracing_info.data_format.field_count = tracing_info.fields.size();
616 tracing_info.data_format.fields = &tracing_info.fields[0];
617 }
618 }
619 }
620
AddMapping(const MapEntry & map)621 Mapping* ReportLib::AddMapping(const MapEntry& map) {
622 current_mappings_.emplace_back(std::unique_ptr<Mapping>(new Mapping));
623 Mapping* mapping = current_mappings_.back().get();
624 mapping->start = map.start_addr;
625 mapping->end = map.start_addr + map.len;
626 mapping->pgoff = map.pgoff;
627 return mapping;
628 }
629
GetBuildIdForPath(const char * path)630 const char* ReportLib::GetBuildIdForPath(const char* path) {
631 if (!OpenRecordFileIfNecessary()) {
632 build_id_string_.clear();
633 return build_id_string_.c_str();
634 }
635 BuildId build_id = Dso::FindExpectedBuildIdForPath(path);
636 if (build_id.IsEmpty()) {
637 build_id_string_.clear();
638 } else {
639 build_id_string_ = build_id.ToString();
640 }
641 return build_id_string_.c_str();
642 }
643
GetFeatureSection(const char * feature_name)644 FeatureSection* ReportLib::GetFeatureSection(const char* feature_name) {
645 if (!OpenRecordFileIfNecessary()) {
646 return nullptr;
647 }
648 int feature = PerfFileFormat::GetFeatureId(feature_name);
649 if (feature == -1 || !record_file_reader_->ReadFeatureSection(feature, &feature_section_data_)) {
650 return nullptr;
651 }
652 feature_section_.data = feature_section_data_.data();
653 feature_section_.data_size = feature_section_data_.size();
654 return &feature_section_;
655 }
656
657 } // namespace simpleperf
658
659 using ReportLib = simpleperf::ReportLib;
660
661 extern "C" {
662
663 #define EXPORT __attribute__((visibility("default")))
664
665 // Create a new instance,
666 // pass the instance to the other functions below.
667 ReportLib* CreateReportLib() EXPORT;
668 void DestroyReportLib(ReportLib* report_lib) EXPORT;
669
670 // Set log severity, different levels are:
671 // verbose, debug, info, warning, error, fatal.
672 bool SetLogSeverity(ReportLib* report_lib, const char* log_level) EXPORT;
673 bool SetSymfs(ReportLib* report_lib, const char* symfs_dir) EXPORT;
674 bool SetRecordFile(ReportLib* report_lib, const char* record_file) EXPORT;
675 bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) EXPORT;
676 void ShowIpForUnknownSymbol(ReportLib* report_lib) EXPORT;
677 void ShowArtFrames(ReportLib* report_lib, bool show) EXPORT;
678 bool RemoveMethod(ReportLib* report_lib, const char* method_name_regex) EXPORT;
679 void MergeJavaMethods(ReportLib* report_lib, bool merge) EXPORT;
680 bool AddProguardMappingFile(ReportLib* report_lib, const char* mapping_file) EXPORT;
681 const char* GetSupportedTraceOffCpuModes(ReportLib* report_lib) EXPORT;
682 bool SetTraceOffCpuMode(ReportLib* report_lib, const char* mode) EXPORT;
683 bool SetSampleFilter(ReportLib* report_lib, const char** filters, int filters_len) EXPORT;
684 bool AggregateThreads(ReportLib* report_lib, const char** thread_name_regex,
685 int thread_name_regex_len) EXPORT;
686
687 Sample* GetNextSample(ReportLib* report_lib) EXPORT;
688 Event* GetEventOfCurrentSample(ReportLib* report_lib) EXPORT;
689 SymbolEntry* GetSymbolOfCurrentSample(ReportLib* report_lib) EXPORT;
690 CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) EXPORT;
691 EventCountersView* GetEventCountersOfCurrentSample(ReportLib* report_lib) EXPORT;
692 const char* GetTracingDataOfCurrentSample(ReportLib* report_lib) EXPORT;
693 const char* GetProcessNameOfCurrentSample(ReportLib* report_lib) EXPORT;
694
695 const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) EXPORT;
696 FeatureSection* GetFeatureSection(ReportLib* report_lib, const char* feature_name) EXPORT;
697 }
698
699 // Exported methods working with a client created instance
CreateReportLib()700 ReportLib* CreateReportLib() {
701 return new ReportLib();
702 }
703
DestroyReportLib(ReportLib * report_lib)704 void DestroyReportLib(ReportLib* report_lib) {
705 delete report_lib;
706 }
707
SetLogSeverity(ReportLib * report_lib,const char * log_level)708 bool SetLogSeverity(ReportLib* report_lib, const char* log_level) {
709 return report_lib->SetLogSeverity(log_level);
710 }
711
SetSymfs(ReportLib * report_lib,const char * symfs_dir)712 bool SetSymfs(ReportLib* report_lib, const char* symfs_dir) {
713 return report_lib->SetSymfs(symfs_dir);
714 }
715
SetRecordFile(ReportLib * report_lib,const char * record_file)716 bool SetRecordFile(ReportLib* report_lib, const char* record_file) {
717 return report_lib->SetRecordFile(record_file);
718 }
719
ShowIpForUnknownSymbol(ReportLib * report_lib)720 void ShowIpForUnknownSymbol(ReportLib* report_lib) {
721 return report_lib->ShowIpForUnknownSymbol();
722 }
723
ShowArtFrames(ReportLib * report_lib,bool show)724 void ShowArtFrames(ReportLib* report_lib, bool show) {
725 return report_lib->ShowArtFrames(show);
726 }
727
RemoveMethod(ReportLib * report_lib,const char * method_name_regex)728 bool RemoveMethod(ReportLib* report_lib, const char* method_name_regex) {
729 return report_lib->RemoveMethod(method_name_regex);
730 }
731
MergeJavaMethods(ReportLib * report_lib,bool merge)732 void MergeJavaMethods(ReportLib* report_lib, bool merge) {
733 return report_lib->MergeJavaMethods(merge);
734 }
735
SetKallsymsFile(ReportLib * report_lib,const char * kallsyms_file)736 bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) {
737 return report_lib->SetKallsymsFile(kallsyms_file);
738 }
739
AddProguardMappingFile(ReportLib * report_lib,const char * mapping_file)740 bool AddProguardMappingFile(ReportLib* report_lib, const char* mapping_file) {
741 return report_lib->AddProguardMappingFile(mapping_file);
742 }
743
GetSupportedTraceOffCpuModes(ReportLib * report_lib)744 const char* GetSupportedTraceOffCpuModes(ReportLib* report_lib) {
745 return report_lib->GetSupportedTraceOffCpuModes();
746 }
747
SetTraceOffCpuMode(ReportLib * report_lib,const char * mode)748 bool SetTraceOffCpuMode(ReportLib* report_lib, const char* mode) {
749 return report_lib->SetTraceOffCpuMode(mode);
750 }
751
SetSampleFilter(ReportLib * report_lib,const char ** filters,int filters_len)752 bool SetSampleFilter(ReportLib* report_lib, const char** filters, int filters_len) {
753 return report_lib->SetSampleFilter(filters, filters_len);
754 }
755
AggregateThreads(ReportLib * report_lib,const char ** thread_name_regex,int thread_name_regex_len)756 bool AggregateThreads(ReportLib* report_lib, const char** thread_name_regex,
757 int thread_name_regex_len) {
758 return report_lib->AggregateThreads(thread_name_regex, thread_name_regex_len);
759 }
760
GetNextSample(ReportLib * report_lib)761 Sample* GetNextSample(ReportLib* report_lib) {
762 return report_lib->GetNextSample();
763 }
764
GetEventOfCurrentSample(ReportLib * report_lib)765 Event* GetEventOfCurrentSample(ReportLib* report_lib) {
766 return report_lib->GetEventOfCurrentSample();
767 }
768
GetSymbolOfCurrentSample(ReportLib * report_lib)769 SymbolEntry* GetSymbolOfCurrentSample(ReportLib* report_lib) {
770 return report_lib->GetSymbolOfCurrentSample();
771 }
772
GetCallChainOfCurrentSample(ReportLib * report_lib)773 CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) {
774 return report_lib->GetCallChainOfCurrentSample();
775 }
776
GetEventCountersOfCurrentSample(ReportLib * report_lib)777 EventCountersView* GetEventCountersOfCurrentSample(ReportLib* report_lib) {
778 return report_lib->GetEventCountersOfCurrentSample();
779 }
780
GetTracingDataOfCurrentSample(ReportLib * report_lib)781 const char* GetTracingDataOfCurrentSample(ReportLib* report_lib) {
782 return report_lib->GetTracingDataOfCurrentSample();
783 }
784
GetProcessNameOfCurrentSample(ReportLib * report_lib)785 const char* GetProcessNameOfCurrentSample(ReportLib* report_lib) {
786 return report_lib->GetProcessNameOfCurrentSample();
787 }
788
GetBuildIdForPath(ReportLib * report_lib,const char * path)789 const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) {
790 return report_lib->GetBuildIdForPath(path);
791 }
792
GetFeatureSection(ReportLib * report_lib,const char * feature_name)793 FeatureSection* GetFeatureSection(ReportLib* report_lib, const char* feature_name) {
794 return report_lib->GetFeatureSection(feature_name);
795 }
796