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