1 /* 2 * Copyright (C) 2015 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 "record_file.h" 18 19 #include <fcntl.h> 20 #include <string.h> 21 #include <set> 22 #include <vector> 23 24 #include <android-base/logging.h> 25 26 #include "event_attr.h" 27 #include "record.h" 28 #include "utils.h" 29 30 using namespace PerfFileFormat; 31 32 namespace PerfFileFormat { 33 34 static const std::map<int, std::string> feature_name_map = { 35 {FEAT_TRACING_DATA, "tracing_data"}, 36 {FEAT_BUILD_ID, "build_id"}, 37 {FEAT_HOSTNAME, "hostname"}, 38 {FEAT_OSRELEASE, "osrelease"}, 39 {FEAT_VERSION, "version"}, 40 {FEAT_ARCH, "arch"}, 41 {FEAT_NRCPUS, "nrcpus"}, 42 {FEAT_CPUDESC, "cpudesc"}, 43 {FEAT_CPUID, "cpuid"}, 44 {FEAT_TOTAL_MEM, "total_mem"}, 45 {FEAT_CMDLINE, "cmdline"}, 46 {FEAT_EVENT_DESC, "event_desc"}, 47 {FEAT_CPU_TOPOLOGY, "cpu_topology"}, 48 {FEAT_NUMA_TOPOLOGY, "numa_topology"}, 49 {FEAT_BRANCH_STACK, "branch_stack"}, 50 {FEAT_PMU_MAPPINGS, "pmu_mappings"}, 51 {FEAT_GROUP_DESC, "group_desc"}, 52 {FEAT_FILE, "file"}, 53 {FEAT_META_INFO, "meta_info"}, 54 }; 55 56 std::string GetFeatureName(int feature_id) { 57 auto it = feature_name_map.find(feature_id); 58 return it == feature_name_map.end() ? "" : it->second; 59 } 60 61 int GetFeatureId(const std::string& feature_name) { 62 for (auto& pair : feature_name_map) { 63 if (pair.second == feature_name) { 64 return pair.first; 65 } 66 } 67 return -1; 68 } 69 70 } // namespace PerfFileFormat 71 72 std::unique_ptr<RecordFileReader> RecordFileReader::CreateInstance(const std::string& filename) { 73 std::string mode = std::string("rb") + CLOSE_ON_EXEC_MODE; 74 FILE* fp = fopen(filename.c_str(), mode.c_str()); 75 if (fp == nullptr) { 76 PLOG(ERROR) << "failed to open record file '" << filename << "'"; 77 return nullptr; 78 } 79 auto reader = std::unique_ptr<RecordFileReader>(new RecordFileReader(filename, fp)); 80 if (!reader->ReadHeader() || !reader->ReadAttrSection() || 81 !reader->ReadFeatureSectionDescriptors()) { 82 return nullptr; 83 } 84 return reader; 85 } 86 87 RecordFileReader::RecordFileReader(const std::string& filename, FILE* fp) 88 : filename_(filename), record_fp_(fp), event_id_pos_in_sample_records_(0), 89 event_id_reverse_pos_in_non_sample_records_(0), read_record_size_(0) { 90 } 91 92 RecordFileReader::~RecordFileReader() { 93 if (record_fp_ != nullptr) { 94 Close(); 95 } 96 } 97 98 bool RecordFileReader::Close() { 99 bool result = true; 100 if (fclose(record_fp_) != 0) { 101 PLOG(ERROR) << "failed to close record file '" << filename_ << "'"; 102 result = false; 103 } 104 record_fp_ = nullptr; 105 return result; 106 } 107 108 bool RecordFileReader::ReadHeader() { 109 if (!Read(&header_, sizeof(header_))) { 110 return false; 111 } 112 if (memcmp(header_.magic, PERF_MAGIC, sizeof(header_.magic)) != 0) { 113 LOG(ERROR) << filename_ << " is not a valid profiling record file."; 114 return false; 115 } 116 return true; 117 } 118 119 bool RecordFileReader::ReadAttrSection() { 120 size_t attr_count = header_.attrs.size / header_.attr_size; 121 if (header_.attr_size != sizeof(FileAttr)) { 122 LOG(DEBUG) << "attr size (" << header_.attr_size << ") in " << filename_ 123 << " doesn't match expected size (" << sizeof(FileAttr) << ")"; 124 } 125 if (attr_count == 0) { 126 LOG(ERROR) << "no attr in file " << filename_; 127 return false; 128 } 129 if (fseek(record_fp_, header_.attrs.offset, SEEK_SET) != 0) { 130 PLOG(ERROR) << "fseek() failed"; 131 return false; 132 } 133 for (size_t i = 0; i < attr_count; ++i) { 134 std::vector<char> buf(header_.attr_size); 135 if (!Read(buf.data(), buf.size())) { 136 return false; 137 } 138 // The size of perf_event_attr is changing between different linux kernel versions. 139 // Make sure we copy correct data to memory. 140 FileAttr attr; 141 memset(&attr, 0, sizeof(attr)); 142 size_t section_desc_size = sizeof(attr.ids); 143 size_t perf_event_attr_size = header_.attr_size - section_desc_size; 144 memcpy(&attr.attr, &buf[0], std::min(sizeof(attr.attr), perf_event_attr_size)); 145 memcpy(&attr.ids, &buf[perf_event_attr_size], section_desc_size); 146 file_attrs_.push_back(attr); 147 } 148 if (file_attrs_.size() > 1) { 149 std::vector<perf_event_attr> attrs; 150 for (const auto& file_attr : file_attrs_) { 151 attrs.push_back(file_attr.attr); 152 } 153 if (!GetCommonEventIdPositionsForAttrs(attrs, &event_id_pos_in_sample_records_, 154 &event_id_reverse_pos_in_non_sample_records_)) { 155 return false; 156 } 157 } 158 for (size_t i = 0; i < file_attrs_.size(); ++i) { 159 std::vector<uint64_t> ids; 160 if (!ReadIdsForAttr(file_attrs_[i], &ids)) { 161 return false; 162 } 163 event_ids_for_file_attrs_.push_back(ids); 164 for (auto id : ids) { 165 event_id_to_attr_map_[id] = i; 166 } 167 } 168 return true; 169 } 170 171 bool RecordFileReader::ReadFeatureSectionDescriptors() { 172 std::vector<int> features; 173 for (size_t i = 0; i < sizeof(header_.features); ++i) { 174 for (size_t j = 0; j < 8; ++j) { 175 if (header_.features[i] & (1 << j)) { 176 features.push_back(i * 8 + j); 177 } 178 } 179 } 180 uint64_t feature_section_offset = header_.data.offset + header_.data.size; 181 if (fseek(record_fp_, feature_section_offset, SEEK_SET) != 0) { 182 PLOG(ERROR) << "fseek() failed"; 183 return false; 184 } 185 for (const auto& id : features) { 186 SectionDesc desc; 187 if (!Read(&desc, sizeof(desc))) { 188 return false; 189 } 190 feature_section_descriptors_.emplace(id, desc); 191 } 192 return true; 193 } 194 195 bool RecordFileReader::ReadIdsForAttr(const FileAttr& attr, std::vector<uint64_t>* ids) { 196 size_t id_count = attr.ids.size / sizeof(uint64_t); 197 if (fseek(record_fp_, attr.ids.offset, SEEK_SET) != 0) { 198 PLOG(ERROR) << "fseek() failed"; 199 return false; 200 } 201 ids->resize(id_count); 202 if (!Read(ids->data(), attr.ids.size)) { 203 return false; 204 } 205 return true; 206 } 207 208 bool RecordFileReader::ReadDataSection( 209 const std::function<bool(std::unique_ptr<Record>)>& callback, bool sorted) { 210 std::unique_ptr<Record> record; 211 while (ReadRecord(record, sorted)) { 212 if (record == nullptr) { 213 return true; 214 } 215 if (!callback(std::move(record))) { 216 return false; 217 } 218 } 219 return false; 220 } 221 222 bool RecordFileReader::ReadRecord(std::unique_ptr<Record>& record, 223 bool sorted) { 224 if (read_record_size_ == 0) { 225 if (fseek(record_fp_, header_.data.offset, SEEK_SET) != 0) { 226 PLOG(ERROR) << "fseek() failed"; 227 return false; 228 } 229 bool has_timestamp = true; 230 for (const auto& attr : file_attrs_) { 231 if (!IsTimestampSupported(attr.attr)) { 232 has_timestamp = false; 233 break; 234 } 235 } 236 record_cache_.reset(new RecordCache(has_timestamp)); 237 } 238 record = nullptr; 239 while (read_record_size_ < header_.data.size && record == nullptr) { 240 record = ReadRecord(&read_record_size_); 241 if (record == nullptr) { 242 return false; 243 } 244 if (record->type() == SIMPLE_PERF_RECORD_EVENT_ID) { 245 ProcessEventIdRecord(*static_cast<EventIdRecord*>(record.get())); 246 } else if (record->type() == PERF_RECORD_SAMPLE) { 247 SampleRecord* r = static_cast<SampleRecord*>(record.get()); 248 // Although we have removed ip == 0 callchains when recording dwarf based callgraph, 249 // stack frame based callgraph can also generate ip == 0 callchains. Remove them here 250 // to avoid caller's effort. 251 if (r->sample_type & PERF_SAMPLE_CALLCHAIN) { 252 size_t i; 253 for (i = 0; i < r->callchain_data.ip_nr; ++i) { 254 if (r->callchain_data.ips[i] == 0) { 255 break; 256 } 257 } 258 r->callchain_data.ip_nr = i; 259 } 260 } 261 if (sorted) { 262 record_cache_->Push(std::move(record)); 263 record = record_cache_->Pop(); 264 } 265 } 266 if (record == nullptr) { 267 record = record_cache_->ForcedPop(); 268 } 269 return true; 270 } 271 272 std::unique_ptr<Record> RecordFileReader::ReadRecord(uint64_t* nbytes_read) { 273 char header_buf[Record::header_size()]; 274 if (!Read(header_buf, Record::header_size())) { 275 return nullptr; 276 } 277 RecordHeader header(header_buf); 278 std::unique_ptr<char[]> p; 279 if (header.type == SIMPLE_PERF_RECORD_SPLIT) { 280 // Read until meeting a RECORD_SPLIT_END record. 281 std::vector<char> buf; 282 size_t cur_size = 0; 283 char header_buf[Record::header_size()]; 284 while (header.type == SIMPLE_PERF_RECORD_SPLIT) { 285 size_t bytes_to_read = header.size - Record::header_size(); 286 buf.resize(cur_size + bytes_to_read); 287 if (!Read(&buf[cur_size], bytes_to_read)) { 288 return nullptr; 289 } 290 cur_size += bytes_to_read; 291 *nbytes_read += header.size; 292 if (!Read(header_buf, Record::header_size())) { 293 return nullptr; 294 } 295 header = RecordHeader(header_buf); 296 } 297 if (header.type != SIMPLE_PERF_RECORD_SPLIT_END) { 298 LOG(ERROR) << "SPLIT records are not followed by a SPLIT_END record."; 299 return nullptr; 300 } 301 *nbytes_read += header.size; 302 header = RecordHeader(buf.data()); 303 p.reset(new char[header.size]); 304 memcpy(p.get(), buf.data(), buf.size()); 305 } else { 306 p.reset(new char[header.size]); 307 memcpy(p.get(), header_buf, Record::header_size()); 308 if (header.size > Record::header_size()) { 309 if (!Read(p.get() + Record::header_size(), header.size - Record::header_size())) { 310 return nullptr; 311 } 312 } 313 *nbytes_read += header.size; 314 } 315 316 const perf_event_attr* attr = &file_attrs_[0].attr; 317 if (file_attrs_.size() > 1 && header.type < PERF_RECORD_USER_DEFINED_TYPE_START) { 318 bool has_event_id = false; 319 uint64_t event_id; 320 if (header.type == PERF_RECORD_SAMPLE) { 321 if (header.size > event_id_pos_in_sample_records_ + sizeof(uint64_t)) { 322 has_event_id = true; 323 event_id = *reinterpret_cast<uint64_t*>(p.get() + event_id_pos_in_sample_records_); 324 } 325 } else { 326 if (header.size > event_id_reverse_pos_in_non_sample_records_) { 327 has_event_id = true; 328 event_id = *reinterpret_cast<uint64_t*>(p.get() + header.size - event_id_reverse_pos_in_non_sample_records_); 329 } 330 } 331 if (has_event_id) { 332 auto it = event_id_to_attr_map_.find(event_id); 333 if (it != event_id_to_attr_map_.end()) { 334 attr = &file_attrs_[it->second].attr; 335 } 336 } 337 } 338 return ReadRecordFromOwnedBuffer(*attr, header.type, p.release()); 339 } 340 341 bool RecordFileReader::Read(void* buf, size_t len) { 342 if (len != 0 && fread(buf, len, 1, record_fp_) != 1) { 343 PLOG(FATAL) << "failed to read file " << filename_; 344 return false; 345 } 346 return true; 347 } 348 349 void RecordFileReader::ProcessEventIdRecord(const EventIdRecord& r) { 350 for (size_t i = 0; i < r.count; ++i) { 351 event_ids_for_file_attrs_[r.data[i].attr_id].push_back(r.data[i].event_id); 352 event_id_to_attr_map_[r.data[i].event_id] = r.data[i].attr_id; 353 } 354 } 355 356 size_t RecordFileReader::GetAttrIndexOfRecord(const Record* record) { 357 auto it = event_id_to_attr_map_.find(record->Id()); 358 if (it != event_id_to_attr_map_.end()) { 359 return it->second; 360 } 361 return 0; 362 } 363 364 bool RecordFileReader::ReadFeatureSection(int feature, std::vector<char>* data) { 365 const std::map<int, SectionDesc>& section_map = FeatureSectionDescriptors(); 366 auto it = section_map.find(feature); 367 if (it == section_map.end()) { 368 return false; 369 } 370 SectionDesc section = it->second; 371 data->resize(section.size); 372 if (section.size == 0) { 373 return true; 374 } 375 if (fseek(record_fp_, section.offset, SEEK_SET) != 0) { 376 PLOG(ERROR) << "fseek() failed"; 377 return false; 378 } 379 if (!Read(data->data(), data->size())) { 380 return false; 381 } 382 return true; 383 } 384 385 std::vector<std::string> RecordFileReader::ReadCmdlineFeature() { 386 std::vector<char> buf; 387 if (!ReadFeatureSection(FEAT_CMDLINE, &buf)) { 388 return std::vector<std::string>(); 389 } 390 const char* p = buf.data(); 391 const char* end = buf.data() + buf.size(); 392 std::vector<std::string> cmdline; 393 uint32_t arg_count; 394 MoveFromBinaryFormat(arg_count, p); 395 CHECK_LE(p, end); 396 for (size_t i = 0; i < arg_count; ++i) { 397 uint32_t len; 398 MoveFromBinaryFormat(len, p); 399 CHECK_LE(p + len, end); 400 cmdline.push_back(p); 401 p += len; 402 } 403 return cmdline; 404 } 405 406 std::vector<BuildIdRecord> RecordFileReader::ReadBuildIdFeature() { 407 std::vector<char> buf; 408 if (!ReadFeatureSection(FEAT_BUILD_ID, &buf)) { 409 return std::vector<BuildIdRecord>(); 410 } 411 const char* p = buf.data(); 412 const char* end = buf.data() + buf.size(); 413 std::vector<BuildIdRecord> result; 414 while (p < end) { 415 auto header = reinterpret_cast<const perf_event_header*>(p); 416 CHECK_LE(p + header->size, end); 417 char* binary = new char[header->size]; 418 memcpy(binary, p, header->size); 419 p += header->size; 420 BuildIdRecord record(binary); 421 record.OwnBinary(); 422 // Set type explicitly as the perf.data produced by perf doesn't set it. 423 record.SetTypeAndMisc(PERF_RECORD_BUILD_ID, record.misc()); 424 result.push_back(std::move(record)); 425 } 426 return result; 427 } 428 429 std::string RecordFileReader::ReadFeatureString(int feature) { 430 std::vector<char> buf; 431 if (!ReadFeatureSection(feature, &buf)) { 432 return std::string(); 433 } 434 const char* p = buf.data(); 435 const char* end = buf.data() + buf.size(); 436 uint32_t len; 437 MoveFromBinaryFormat(len, p); 438 CHECK_LE(p + len, end); 439 return p; 440 } 441 442 bool RecordFileReader::ReadFileFeature(size_t& read_pos, 443 std::string* file_path, 444 uint32_t* file_type, 445 uint64_t* min_vaddr, 446 std::vector<Symbol>* symbols) { 447 auto it = feature_section_descriptors_.find(FEAT_FILE); 448 if (it == feature_section_descriptors_.end()) { 449 return false; 450 } 451 if (read_pos >= it->second.size) { 452 return false; 453 } 454 if (read_pos == 0) { 455 if (fseek(record_fp_, it->second.offset, SEEK_SET) != 0) { 456 PLOG(ERROR) << "fseek() failed"; 457 return false; 458 } 459 } 460 uint32_t size; 461 if (!Read(&size, 4)) { 462 return false; 463 } 464 std::vector<char> buf(size); 465 if (!Read(buf.data(), size)) { 466 return false; 467 } 468 read_pos += 4 + size; 469 const char* p = buf.data(); 470 *file_path = p; 471 p += file_path->size() + 1; 472 MoveFromBinaryFormat(*file_type, p); 473 MoveFromBinaryFormat(*min_vaddr, p); 474 uint32_t symbol_count; 475 MoveFromBinaryFormat(symbol_count, p); 476 symbols->clear(); 477 symbols->reserve(symbol_count); 478 for (uint32_t i = 0; i < symbol_count; ++i) { 479 uint64_t start_vaddr; 480 uint32_t len; 481 MoveFromBinaryFormat(start_vaddr, p); 482 MoveFromBinaryFormat(len, p); 483 std::string name = p; 484 p += name.size() + 1; 485 symbols->emplace_back(name, start_vaddr, len); 486 } 487 CHECK_EQ(size, static_cast<size_t>(p - buf.data())); 488 return true; 489 } 490 491 bool RecordFileReader::ReadMetaInfoFeature(std::unordered_map<std::string, std::string>* info_map) { 492 std::vector<char> buf; 493 if (!ReadFeatureSection(FEAT_META_INFO, &buf)) { 494 return false; 495 } 496 const char* p = buf.data(); 497 const char* end = buf.data() + buf.size(); 498 while (p < end) { 499 const char* key = p; 500 const char* value = key + strlen(key) + 1; 501 CHECK(value < end); 502 (*info_map)[p] = value; 503 p = value + strlen(value) + 1; 504 } 505 return true; 506 } 507 508 void RecordFileReader::LoadBuildIdAndFileFeatures(ThreadTree& thread_tree) { 509 std::vector<BuildIdRecord> records = ReadBuildIdFeature(); 510 std::vector<std::pair<std::string, BuildId>> build_ids; 511 for (auto& r : records) { 512 build_ids.push_back(std::make_pair(r.filename, r.build_id)); 513 } 514 Dso::SetBuildIds(build_ids); 515 516 if (HasFeature(PerfFileFormat::FEAT_FILE)) { 517 std::string file_path; 518 uint32_t file_type; 519 uint64_t min_vaddr; 520 std::vector<Symbol> symbols; 521 size_t read_pos = 0; 522 while (ReadFileFeature( 523 read_pos, &file_path, &file_type, &min_vaddr, &symbols)) { 524 thread_tree.AddDsoInfo(file_path, file_type, min_vaddr, &symbols); 525 } 526 } 527 } 528 529 std::vector<std::unique_ptr<Record>> RecordFileReader::DataSection() { 530 std::vector<std::unique_ptr<Record>> records; 531 ReadDataSection([&](std::unique_ptr<Record> record) { 532 records.push_back(std::move(record)); 533 return true; 534 }); 535 return records; 536 } 537