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