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->GetReportPath().data();
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 }
358 }
359
360 callchain_entries_.push_back(entry);
361 }
362 current_sample_.ip = callchain_entries_[0].ip;
363 current_symbol_ = &(callchain_entries_[0].symbol);
364 current_callchain_.nr = callchain_entries_.size() - 1;
365 current_callchain_.entries = &callchain_entries_[1];
366 const EventInfo* event = FindEventOfCurrentSample();
367 current_event_.name = event->name.c_str();
368 current_event_.tracing_data_format = event->tracing_info.data_format;
369 if (current_event_.tracing_data_format.size > 0u && (r.sample_type & PERF_SAMPLE_RAW)) {
370 CHECK_GE(r.raw_data.size, current_event_.tracing_data_format.size);
371 current_tracing_data_ = r.raw_data.data;
372 } else {
373 current_tracing_data_ = nullptr;
374 }
375 }
376
FindEventOfCurrentSample()377 const EventInfo* ReportLib::FindEventOfCurrentSample() {
378 if (events_.empty()) {
379 CreateEvents();
380 }
381 size_t attr_index;
382 if (trace_offcpu_) {
383 // For trace-offcpu, we don't want to show event sched:sched_switch.
384 attr_index = 0;
385 } else {
386 attr_index = record_file_reader_->GetAttrIndexOfRecord(current_record_.get());
387 }
388 return &events_[attr_index];
389 }
390
CreateEvents()391 void ReportLib::CreateEvents() {
392 std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
393 events_.resize(attrs.size());
394 for (size_t i = 0; i < attrs.size(); ++i) {
395 events_[i].attr = *attrs[i].attr;
396 events_[i].name = GetEventNameByAttr(events_[i].attr);
397 EventInfo::TracingInfo& tracing_info = events_[i].tracing_info;
398 if (events_[i].attr.type == PERF_TYPE_TRACEPOINT && tracing_) {
399 TracingFormat format = tracing_->GetTracingFormatHavingId(events_[i].attr.config);
400 tracing_info.field_names.resize(format.fields.size());
401 tracing_info.fields.resize(format.fields.size());
402 for (size_t i = 0; i < format.fields.size(); ++i) {
403 tracing_info.field_names[i] = format.fields[i].name;
404 TracingFieldFormat& field = tracing_info.fields[i];
405 field.name = tracing_info.field_names[i].c_str();
406 field.offset = format.fields[i].offset;
407 field.elem_size = format.fields[i].elem_size;
408 field.elem_count = format.fields[i].elem_count;
409 field.is_signed = format.fields[i].is_signed;
410 }
411 if (tracing_info.fields.empty()) {
412 tracing_info.data_format.size = 0;
413 } else {
414 TracingFieldFormat& field = tracing_info.fields.back();
415 tracing_info.data_format.size = field.offset + field.elem_size * field.elem_count;
416 }
417 tracing_info.data_format.field_count = tracing_info.fields.size();
418 tracing_info.data_format.fields = &tracing_info.fields[0];
419 } else {
420 tracing_info.data_format.size = 0;
421 tracing_info.data_format.field_count = 0;
422 tracing_info.data_format.fields = nullptr;
423 }
424 }
425 }
426
AddMapping(const MapEntry & map)427 Mapping* ReportLib::AddMapping(const MapEntry& map) {
428 current_mappings_.emplace_back(std::unique_ptr<Mapping>(new Mapping));
429 Mapping* mapping = current_mappings_.back().get();
430 mapping->start = map.start_addr;
431 mapping->end = map.start_addr + map.len;
432 mapping->pgoff = map.pgoff;
433 return mapping;
434 }
435
GetBuildIdForPath(const char * path)436 const char* ReportLib::GetBuildIdForPath(const char* path) {
437 if (!OpenRecordFileIfNecessary()) {
438 build_id_string_.clear();
439 return build_id_string_.c_str();
440 }
441 BuildId build_id = Dso::FindExpectedBuildIdForPath(path);
442 if (build_id.IsEmpty()) {
443 build_id_string_.clear();
444 } else {
445 build_id_string_ = build_id.ToString();
446 }
447 return build_id_string_.c_str();
448 }
449
GetFeatureSection(const char * feature_name)450 FeatureSection* ReportLib::GetFeatureSection(const char* feature_name) {
451 if (!OpenRecordFileIfNecessary()) {
452 return nullptr;
453 }
454 int feature = PerfFileFormat::GetFeatureId(feature_name);
455 if (feature == -1 || !record_file_reader_->ReadFeatureSection(feature, &feature_section_data_)) {
456 return nullptr;
457 }
458 feature_section_.data = feature_section_data_.data();
459 feature_section_.data_size = feature_section_data_.size();
460 return &feature_section_;
461 }
462
463 // Exported methods working with a client created instance
CreateReportLib()464 ReportLib* CreateReportLib() {
465 return new ReportLib();
466 }
467
DestroyReportLib(ReportLib * report_lib)468 void DestroyReportLib(ReportLib* report_lib) {
469 delete report_lib;
470 }
471
SetLogSeverity(ReportLib * report_lib,const char * log_level)472 bool SetLogSeverity(ReportLib* report_lib, const char* log_level) {
473 return report_lib->SetLogSeverity(log_level);
474 }
475
SetSymfs(ReportLib * report_lib,const char * symfs_dir)476 bool SetSymfs(ReportLib* report_lib, const char* symfs_dir) {
477 return report_lib->SetSymfs(symfs_dir);
478 }
479
SetRecordFile(ReportLib * report_lib,const char * record_file)480 bool SetRecordFile(ReportLib* report_lib, const char* record_file) {
481 return report_lib->SetRecordFile(record_file);
482 }
483
ShowIpForUnknownSymbol(ReportLib * report_lib)484 void ShowIpForUnknownSymbol(ReportLib* report_lib) {
485 return report_lib->ShowIpForUnknownSymbol();
486 }
487
ShowArtFrames(ReportLib * report_lib,bool show)488 void ShowArtFrames(ReportLib* report_lib, bool show) {
489 return report_lib->ShowArtFrames(show);
490 }
491
MergeJavaMethods(ReportLib * report_lib,bool merge)492 void MergeJavaMethods(ReportLib* report_lib, bool merge) {
493 return report_lib->MergeJavaMethods(merge);
494 }
495
SetKallsymsFile(ReportLib * report_lib,const char * kallsyms_file)496 bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) {
497 return report_lib->SetKallsymsFile(kallsyms_file);
498 }
499
GetNextSample(ReportLib * report_lib)500 Sample* GetNextSample(ReportLib* report_lib) {
501 return report_lib->GetNextSample();
502 }
503
GetEventOfCurrentSample(ReportLib * report_lib)504 Event* GetEventOfCurrentSample(ReportLib* report_lib) {
505 return report_lib->GetEventOfCurrentSample();
506 }
507
GetSymbolOfCurrentSample(ReportLib * report_lib)508 SymbolEntry* GetSymbolOfCurrentSample(ReportLib* report_lib) {
509 return report_lib->GetSymbolOfCurrentSample();
510 }
511
GetCallChainOfCurrentSample(ReportLib * report_lib)512 CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) {
513 return report_lib->GetCallChainOfCurrentSample();
514 }
515
GetTracingDataOfCurrentSample(ReportLib * report_lib)516 const char* GetTracingDataOfCurrentSample(ReportLib* report_lib) {
517 return report_lib->GetTracingDataOfCurrentSample();
518 }
519
GetBuildIdForPath(ReportLib * report_lib,const char * path)520 const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) {
521 return report_lib->GetBuildIdForPath(path);
522 }
523
GetFeatureSection(ReportLib * report_lib,const char * feature_name)524 FeatureSection* GetFeatureSection(ReportLib* report_lib, const char* feature_name) {
525 return report_lib->GetFeatureSection(feature_name);
526 }
527