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 ¤t_event_; }
GetSymbolOfCurrentSample()162 SymbolEntry* GetSymbolOfCurrentSample() { return current_symbol_; }
GetCallChainOfCurrentSample()163 CallChain* GetCallChainOfCurrentSample() { return ¤t_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 ¤t_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