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: 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 147 bool SetSymfs(const char* symfs_dir) { return Dso::SetSymFsDir(symfs_dir); } 148 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 156 void ShowIpForUnknownSymbol() { thread_tree_.ShowIpForUnknownSymbol(); } 157 void ShowArtFrames(bool show) { show_art_frames_ = show; } 158 void MergeJavaMethods(bool merge) { merge_java_methods_ = merge; } 159 160 Sample* GetNextSample(); 161 Event* GetEventOfCurrentSample() { return ¤t_event_; } 162 SymbolEntry* GetSymbolOfCurrentSample() { return current_symbol_; } 163 CallChain* GetCallChainOfCurrentSample() { return ¤t_callchain_; } 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 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 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 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 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 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 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 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 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 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 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 466 ReportLib* CreateReportLib() { 467 return new ReportLib(); 468 } 469 470 void DestroyReportLib(ReportLib* report_lib) { 471 delete report_lib; 472 } 473 474 bool SetLogSeverity(ReportLib* report_lib, const char* log_level) { 475 return report_lib->SetLogSeverity(log_level); 476 } 477 478 bool SetSymfs(ReportLib* report_lib, const char* symfs_dir) { 479 return report_lib->SetSymfs(symfs_dir); 480 } 481 482 bool SetRecordFile(ReportLib* report_lib, const char* record_file) { 483 return report_lib->SetRecordFile(record_file); 484 } 485 486 void ShowIpForUnknownSymbol(ReportLib* report_lib) { 487 return report_lib->ShowIpForUnknownSymbol(); 488 } 489 490 void ShowArtFrames(ReportLib* report_lib, bool show) { 491 return report_lib->ShowArtFrames(show); 492 } 493 494 void MergeJavaMethods(ReportLib* report_lib, bool merge) { 495 return report_lib->MergeJavaMethods(merge); 496 } 497 498 bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) { 499 return report_lib->SetKallsymsFile(kallsyms_file); 500 } 501 502 Sample* GetNextSample(ReportLib* report_lib) { 503 return report_lib->GetNextSample(); 504 } 505 506 Event* GetEventOfCurrentSample(ReportLib* report_lib) { 507 return report_lib->GetEventOfCurrentSample(); 508 } 509 510 SymbolEntry* GetSymbolOfCurrentSample(ReportLib* report_lib) { 511 return report_lib->GetSymbolOfCurrentSample(); 512 } 513 514 CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) { 515 return report_lib->GetCallChainOfCurrentSample(); 516 } 517 518 const char* GetTracingDataOfCurrentSample(ReportLib* report_lib) { 519 return report_lib->GetTracingDataOfCurrentSample(); 520 } 521 522 const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) { 523 return report_lib->GetBuildIdForPath(path); 524 } 525 526 FeatureSection* GetFeatureSection(ReportLib* report_lib, const char* feature_name) { 527 return report_lib->GetFeatureSection(feature_name); 528 } 529