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 
23 #include "dso.h"
24 #include "event_attr.h"
25 #include "record_file.h"
26 #include "thread_tree.h"
27 #include "utils.h"
28 
29 class ReportLib;
30 
31 extern "C" {
32 
33 #define EXPORT __attribute__((visibility("default")))
34 
35 struct Sample {
36   uint64_t ip;
37   uint32_t pid;
38   uint32_t tid;
39   const char* thread_comm;
40   uint64_t time;
41   uint32_t in_kernel;
42   uint32_t cpu;
43   uint64_t period;
44 };
45 
46 struct Event {
47   const char* name;
48 };
49 
50 struct Mapping {
51   uint64_t start;
52   uint64_t end;
53   uint64_t pgoff;
54 };
55 
56 struct SymbolEntry {
57   const char* dso_name;
58   uint64_t vaddr_in_file;
59   const char* symbol_name;
60   uint64_t symbol_addr;
61   Mapping* mapping;
62 };
63 
64 struct CallChainEntry {
65   uint64_t ip;
66   SymbolEntry symbol;
67 };
68 
69 struct CallChain {
70   uint32_t nr;
71   CallChainEntry* entries;
72 };
73 
74 // Create a new instance,
75 // pass the instance to the other functions below.
76 ReportLib* CreateReportLib() EXPORT;
77 void DestroyReportLib(ReportLib* report_lib) EXPORT;
78 
79 // Set log severity, different levels are:
80 // verbose, debug, info, warning, error, fatal.
81 bool SetLogSeverity(ReportLib* report_lib, const char* log_level) EXPORT;
82 bool SetSymfs(ReportLib* report_lib, const char* symfs_dir) EXPORT;
83 bool SetRecordFile(ReportLib* report_lib, const char* record_file) EXPORT;
84 bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) EXPORT;
85 void ShowIpForUnknownSymbol(ReportLib* report_lib) EXPORT;
86 
87 Sample* GetNextSample(ReportLib* report_lib) EXPORT;
88 Event* GetEventOfCurrentSample(ReportLib* report_lib) EXPORT;
89 SymbolEntry* GetSymbolOfCurrentSample(ReportLib* report_lib) EXPORT;
90 CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) EXPORT;
91 
92 const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) EXPORT;
93 }
94 
95 struct EventAttrWithName {
96   perf_event_attr attr;
97   std::string name;
98 };
99 
100 enum {
101   UPDATE_FLAG_OF_SAMPLE = 1 << 0,
102   UPDATE_FLAG_OF_EVENT = 1 << 1,
103   UPDATE_FLAG_OF_SYMBOL = 1 << 2,
104   UPDATE_FLAG_OF_CALLCHAIN = 1 << 3,
105 };
106 
107 class ReportLib {
108  public:
ReportLib()109   ReportLib()
110       : log_severity_(
111             new android::base::ScopedLogSeverity(android::base::INFO)),
112         record_filename_("perf.data"),
113         current_thread_(nullptr),
114         update_flag_(0)
115          {}
116 
117   bool SetLogSeverity(const char* log_level);
118 
SetSymfs(const char * symfs_dir)119   bool SetSymfs(const char* symfs_dir) { return Dso::SetSymFsDir(symfs_dir); }
120 
SetRecordFile(const char * record_file)121   bool SetRecordFile(const char* record_file) {
122     record_filename_ = record_file;
123     return true;
124   }
125 
126   bool SetKallsymsFile(const char* kallsyms_file);
127 
ShowIpForUnknownSymbol()128   void ShowIpForUnknownSymbol() { thread_tree_.ShowIpForUnknownSymbol(); }
129 
130   Sample* GetNextSample();
131   Event* GetEventOfCurrentSample();
132   SymbolEntry* GetSymbolOfCurrentSample();
133   CallChain* GetCallChainOfCurrentSample();
134 
135   const char* GetBuildIdForPath(const char* path);
136 
137  private:
138   Sample* GetCurrentSample();
139   bool OpenRecordFileIfNecessary();
140   Mapping* AddMapping(const MapEntry& map);
141 
142   std::unique_ptr<android::base::ScopedLogSeverity> log_severity_;
143   std::string record_filename_;
144   std::unique_ptr<RecordFileReader> record_file_reader_;
145   ThreadTree thread_tree_;
146   std::unique_ptr<SampleRecord> current_record_;
147   const ThreadEntry* current_thread_;
148   Sample current_sample_;
149   Event current_event_;
150   SymbolEntry current_symbol_;
151   CallChain current_callchain_;
152   std::vector<std::unique_ptr<Mapping>> current_mappings_;
153   std::vector<CallChainEntry> callchain_entries_;
154   std::string build_id_string_;
155   int update_flag_;
156   std::vector<EventAttrWithName> event_attrs_;
157 };
158 
SetLogSeverity(const char * log_level)159 bool ReportLib::SetLogSeverity(const char* log_level) {
160   android::base::LogSeverity severity;
161   if (!GetLogSeverity(log_level, &severity)) {
162     LOG(ERROR) << "Unknown log severity: " << log_level;
163     return false;
164   }
165   log_severity_ = nullptr;
166   log_severity_.reset(new android::base::ScopedLogSeverity(severity));
167   return true;
168 }
169 
SetKallsymsFile(const char * kallsyms_file)170 bool ReportLib::SetKallsymsFile(const char* kallsyms_file) {
171   std::string kallsyms;
172   if (!android::base::ReadFileToString(kallsyms_file, &kallsyms)) {
173     LOG(WARNING) << "Failed to read in kallsyms file from " << kallsyms_file;
174     return false;
175   }
176   Dso::SetKallsyms(std::move(kallsyms));
177   return true;
178 }
179 
OpenRecordFileIfNecessary()180 bool ReportLib::OpenRecordFileIfNecessary() {
181   if (record_file_reader_ == nullptr) {
182     record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
183     if (record_file_reader_ == nullptr) {
184       return false;
185     }
186     record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
187   }
188   return true;
189 }
190 
GetNextSample()191 Sample* ReportLib::GetNextSample() {
192   if (!OpenRecordFileIfNecessary()) {
193     return nullptr;
194   }
195   while (true) {
196     std::unique_ptr<Record> record;
197     if (!record_file_reader_->ReadRecord(record)) {
198       return nullptr;
199     }
200     if (record == nullptr) {
201       return nullptr;
202     }
203     thread_tree_.Update(*record);
204     if (record->type() == PERF_RECORD_SAMPLE) {
205       current_record_.reset(static_cast<SampleRecord*>(record.release()));
206       break;
207     }
208   }
209   update_flag_ = 0;
210   current_mappings_.clear();
211   return GetCurrentSample();
212 }
213 
GetCurrentSample()214 Sample* ReportLib::GetCurrentSample() {
215   if (!(update_flag_ & UPDATE_FLAG_OF_SAMPLE)) {
216     SampleRecord& r = *current_record_;
217     current_sample_.ip = r.ip_data.ip;
218     current_sample_.pid = r.tid_data.pid;
219     current_sample_.tid = r.tid_data.tid;
220     current_thread_ =
221         thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
222     current_sample_.thread_comm = current_thread_->comm;
223     current_sample_.time = r.time_data.time;
224     current_sample_.in_kernel = r.InKernel();
225     current_sample_.cpu = r.cpu_data.cpu;
226     current_sample_.period = r.period_data.period;
227     update_flag_ |= UPDATE_FLAG_OF_SAMPLE;
228   }
229   return &current_sample_;
230 }
231 
GetEventOfCurrentSample()232 Event* ReportLib::GetEventOfCurrentSample() {
233   if (!(update_flag_ & UPDATE_FLAG_OF_EVENT)) {
234     if (event_attrs_.empty()) {
235       std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
236       for (const auto& attr_with_id : attrs) {
237         EventAttrWithName attr;
238         attr.attr = *attr_with_id.attr;
239         attr.name = GetEventNameByAttr(attr.attr);
240         event_attrs_.push_back(attr);
241       }
242     }
243     size_t attr_index =
244         record_file_reader_->GetAttrIndexOfRecord(current_record_.get());
245     current_event_.name = event_attrs_[attr_index].name.c_str();
246     update_flag_ |= UPDATE_FLAG_OF_EVENT;
247   }
248   return &current_event_;
249 }
250 
GetSymbolOfCurrentSample()251 SymbolEntry* ReportLib::GetSymbolOfCurrentSample() {
252   if (!(update_flag_ & UPDATE_FLAG_OF_SYMBOL)) {
253     SampleRecord& r = *current_record_;
254     const MapEntry* map =
255         thread_tree_.FindMap(current_thread_, r.ip_data.ip, r.InKernel());
256     uint64_t vaddr_in_file;
257     const Symbol* symbol =
258         thread_tree_.FindSymbol(map, r.ip_data.ip, &vaddr_in_file);
259     current_symbol_.dso_name = map->dso->Path().c_str();
260     current_symbol_.vaddr_in_file = vaddr_in_file;
261     current_symbol_.symbol_name = symbol->DemangledName();
262     current_symbol_.symbol_addr = symbol->addr;
263     current_symbol_.mapping = AddMapping(*map);
264     update_flag_ |= UPDATE_FLAG_OF_SYMBOL;
265   }
266   return &current_symbol_;
267 }
268 
GetCallChainOfCurrentSample()269 CallChain* ReportLib::GetCallChainOfCurrentSample() {
270   if (!(update_flag_ & UPDATE_FLAG_OF_CALLCHAIN)) {
271     SampleRecord& r = *current_record_;
272     callchain_entries_.clear();
273 
274     if (r.sample_type & PERF_SAMPLE_CALLCHAIN) {
275       bool first_ip = true;
276       bool in_kernel = r.InKernel();
277       for (uint64_t i = 0; i < r.callchain_data.ip_nr; ++i) {
278         uint64_t ip = r.callchain_data.ips[i];
279         if (ip >= PERF_CONTEXT_MAX) {
280           switch (ip) {
281             case PERF_CONTEXT_KERNEL:
282               in_kernel = true;
283               break;
284             case PERF_CONTEXT_USER:
285               in_kernel = false;
286               break;
287             default:
288               LOG(DEBUG) << "Unexpected perf_context in callchain: " << std::hex
289                          << ip;
290           }
291         } else {
292           if (first_ip) {
293             first_ip = false;
294             // Remove duplication with sample ip.
295             if (ip == r.ip_data.ip) {
296               continue;
297             }
298           }
299           const MapEntry* map =
300               thread_tree_.FindMap(current_thread_, ip, in_kernel);
301           uint64_t vaddr_in_file;
302           const Symbol* symbol =
303               thread_tree_.FindSymbol(map, ip, &vaddr_in_file);
304           CallChainEntry entry;
305           entry.ip = ip;
306           entry.symbol.dso_name = map->dso->Path().c_str();
307           entry.symbol.vaddr_in_file = vaddr_in_file;
308           entry.symbol.symbol_name = symbol->DemangledName();
309           entry.symbol.symbol_addr = symbol->addr;
310           entry.symbol.mapping = AddMapping(*map);
311           callchain_entries_.push_back(entry);
312         }
313       }
314     }
315     current_callchain_.nr = callchain_entries_.size();
316     current_callchain_.entries = callchain_entries_.data();
317     update_flag_ |= UPDATE_FLAG_OF_CALLCHAIN;
318   }
319   return &current_callchain_;
320 }
321 
AddMapping(const MapEntry & map)322 Mapping* ReportLib::AddMapping(const MapEntry& map) {
323   current_mappings_.emplace_back(std::unique_ptr<Mapping>(new Mapping));
324   Mapping* mapping = current_mappings_.back().get();
325   mapping->start = map.start_addr;
326   mapping->end = map.start_addr + map.len;
327   mapping->pgoff = map.pgoff;
328   return mapping;
329 }
330 
GetBuildIdForPath(const char * path)331 const char* ReportLib::GetBuildIdForPath(const char* path) {
332   if (!OpenRecordFileIfNecessary()) {
333     build_id_string_.clear();
334     return build_id_string_.c_str();
335   }
336   BuildId build_id = Dso::FindExpectedBuildIdForPath(path);
337   if (build_id.IsEmpty()) {
338     build_id_string_.clear();
339   } else {
340     build_id_string_ = build_id.ToString();
341   }
342   return build_id_string_.c_str();
343 }
344 
345 // Exported methods working with a client created instance
CreateReportLib()346 ReportLib* CreateReportLib() {
347   return new ReportLib();
348 }
349 
DestroyReportLib(ReportLib * report_lib)350 void DestroyReportLib(ReportLib* report_lib) {
351   delete report_lib;
352 }
353 
SetLogSeverity(ReportLib * report_lib,const char * log_level)354 bool SetLogSeverity(ReportLib* report_lib, const char* log_level) {
355   return report_lib->SetLogSeverity(log_level);
356 }
357 
SetSymfs(ReportLib * report_lib,const char * symfs_dir)358 bool SetSymfs(ReportLib* report_lib, const char* symfs_dir) {
359   return report_lib->SetSymfs(symfs_dir);
360 }
361 
SetRecordFile(ReportLib * report_lib,const char * record_file)362 bool SetRecordFile(ReportLib* report_lib, const char* record_file) {
363   return report_lib->SetRecordFile(record_file);
364 }
365 
ShowIpForUnknownSymbol(ReportLib * report_lib)366 void ShowIpForUnknownSymbol(ReportLib* report_lib) {
367   return report_lib->ShowIpForUnknownSymbol();
368 }
369 
SetKallsymsFile(ReportLib * report_lib,const char * kallsyms_file)370 bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) {
371   return report_lib->SetKallsymsFile(kallsyms_file);
372 }
373 
GetNextSample(ReportLib * report_lib)374 Sample* GetNextSample(ReportLib* report_lib) {
375   return report_lib->GetNextSample();
376 }
377 
GetEventOfCurrentSample(ReportLib * report_lib)378 Event* GetEventOfCurrentSample(ReportLib* report_lib) {
379   return report_lib->GetEventOfCurrentSample();
380 }
381 
GetSymbolOfCurrentSample(ReportLib * report_lib)382 SymbolEntry* GetSymbolOfCurrentSample(ReportLib* report_lib) {
383   return report_lib->GetSymbolOfCurrentSample();
384 }
385 
GetCallChainOfCurrentSample(ReportLib * report_lib)386 CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) {
387   return report_lib->GetCallChainOfCurrentSample();
388 }
389 
GetBuildIdForPath(ReportLib * report_lib,const char * path)390 const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) {
391   return report_lib->GetBuildIdForPath(path);
392 }
393