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 ¤t_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 ¤t_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 ¤t_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 ¤t_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