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