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