xref: /aosp_15_r20/system/extras/simpleperf/report_lib_interface.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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 &current_event_; }
GetSymbolOfCurrentSample()221   SymbolEntry* GetSymbolOfCurrentSample() { return current_symbol_; }
GetCallChainOfCurrentSample()222   CallChain* GetCallChainOfCurrentSample() { return &current_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 &current_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