1 /* 2 * Copyright (c) 2016, Google Inc. 3 * All rights reserved. 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "perf_data_converter.h" 9 10 #include <algorithm> 11 #include <deque> 12 #include <map> 13 #include <sstream> 14 #include <vector> 15 16 #include "int_compat.h" 17 #include "perf_data_handler.h" 18 #include "string_compat.h" 19 #include "builder.h" 20 #include "quipper/perf_data.pb.h" 21 #include "quipper/perf_parser.h" 22 #include "quipper/perf_reader.h" 23 24 namespace perftools { 25 namespace { 26 27 typedef perftools::profiles::Profile Profile; 28 typedef perftools::profiles::Builder ProfileBuilder; 29 30 typedef uint32 Pid; 31 32 enum ExecutionMode { 33 Unknown, 34 HostKernel, 35 HostUser, 36 GuestKernel, 37 GuestUser, 38 Hypervisor 39 }; 40 41 const char* ExecModeString(ExecutionMode mode) { 42 switch (mode) { 43 case HostKernel: 44 return ExecutionModeHostKernel; 45 case HostUser: 46 return ExecutionModeHostUser; 47 case GuestKernel: 48 return ExecutionModeGuestKernel; 49 case GuestUser: 50 return ExecutionModeGuestUser; 51 case Hypervisor: 52 return ExecutionModeHypervisor; 53 default: 54 std::cerr << "Execution mode not handled: " << mode << std::endl; 55 return ""; 56 } 57 } 58 59 ExecutionMode PerfExecMode(const PerfDataHandler::SampleContext& sample) { 60 if (sample.header.has_misc()) { 61 switch (sample.header.misc() & PERF_RECORD_MISC_CPUMODE_MASK) { 62 case PERF_RECORD_MISC_KERNEL: 63 return HostKernel; 64 case PERF_RECORD_MISC_USER: 65 return HostUser; 66 case PERF_RECORD_MISC_GUEST_KERNEL: 67 return GuestKernel; 68 case PERF_RECORD_MISC_GUEST_USER: 69 return GuestUser; 70 case PERF_RECORD_MISC_HYPERVISOR: 71 return Hypervisor; 72 } 73 } 74 return Unknown; 75 } 76 77 // Adds the string to the profile builder. If the UTF-8 library is included, 78 // this also ensures the string contains structurally valid UTF-8. 79 // In order to successfully unmarshal the proto in Go, all strings inserted into 80 // the profile string table must be valid UTF-8. 81 int64 UTF8StringId(const string& s, ProfileBuilder* builder) { 82 return builder->StringId(s.c_str()); 83 } 84 85 // Returns the file name of the mapping as either the real file path if it's 86 // present or the string representation of the file path MD5 checksum prefix 87 // when the real file path was stripped from the data for privacy reasons. 88 string MappingFilename(const PerfDataHandler::Mapping* m) { 89 if (m->filename != nullptr && !m->filename->empty()) { 90 return *m->filename; 91 } else if (m->filename_md5_prefix != 0) { 92 std::stringstream filename; 93 filename << std::hex << m->filename_md5_prefix; 94 return filename.str(); 95 } 96 return ""; 97 } 98 99 // List of profile location IDs, currently used to represent a call stack. 100 typedef std::vector<uint64> LocationIdVector; 101 102 // It is sufficient to key the location and mapping maps by PID. 103 // However, when Samples include labels, it is necessary to key their maps 104 // not only by PID, but also by anything their labels may contain, since labels 105 // are also distinguishing features. This struct should contain everything 106 // required to uniquely identify a Sample: if two Samples you consider different 107 // end up with the same SampleKey, you should extend SampleKey til they don't. 108 // 109 // If any of these values are not used as labels, they should be set to 0. 110 struct SampleKey { 111 Pid pid = 0; 112 Pid tid = 0; 113 uint64 time_ns = 0; 114 ExecutionMode exec_mode = Unknown; 115 // The index of the sample's command in the profile's string table. 116 uint64 comm = 0; 117 LocationIdVector stack; 118 }; 119 120 struct SampleKeyEqualityTester { 121 bool operator()(const SampleKey a, const SampleKey b) const { 122 return ((a.pid == b.pid) && (a.tid == b.tid) && (a.time_ns == b.time_ns) && 123 (a.exec_mode == b.exec_mode) && (a.comm == b.comm) && 124 (a.stack == b.stack)); 125 } 126 }; 127 128 struct SampleKeyHasher { 129 size_t operator()(const SampleKey k) const { 130 size_t hash = 0; 131 hash ^= std::hash<int32>()(k.pid); 132 hash ^= std::hash<int32>()(k.tid); 133 hash ^= std::hash<uint64>()(k.time_ns); 134 hash ^= std::hash<int>()(k.exec_mode); 135 hash ^= std::hash<uint64>()(k.comm); 136 for (const auto& id : k.stack) { 137 hash ^= std::hash<uint64>()(id); 138 } 139 return hash; 140 } 141 }; 142 143 // While Locations and Mappings are per-address-space (=per-process), samples 144 // can be thread-specific. If the requested sample labels include PID and 145 // TID, we'll need to maintain separate profile sample objects for samples 146 // that are identical except for TID. Likewise, if the requested sample 147 // labels include timestamp_ns, then we'll need to have separate 148 // profile_proto::Samples for samples that are identical except for timestamp. 149 typedef std::unordered_map<SampleKey, perftools::profiles::Sample*, 150 SampleKeyHasher, SampleKeyEqualityTester> SampleMap; 151 152 // Map from a virtual address to a profile location ID. It only keys off the 153 // address, not also the mapping ID since the map / its portions are invalidated 154 // by Comm() and MMap() methods to force re-creation of those locations. 155 // 156 typedef std::map<uint64, uint64> LocationMap; 157 158 // Map from the handler mapping object to profile mapping ID. The mappings 159 // the handler creates are immutable and reasonably shared (as in no new mapping 160 // object is created per, say, each sample), so using the pointers is OK. 161 typedef std::unordered_map<const PerfDataHandler::Mapping*, uint64> MappingMap; 162 163 // Per-process (aggregated when no PID grouping requested) info. 164 // See docs on ProcessProfile in the header file for details on the fields. 165 class ProcessMeta { 166 public: 167 // Constructs the object for the specified PID. 168 explicit ProcessMeta(Pid pid) : pid_(pid) {} 169 170 // Updates the bounding time interval ranges per specified timestamp. 171 void UpdateTimestamps(int64 time_nsec) { 172 if (min_sample_time_ns_ == 0 || time_nsec < min_sample_time_ns_) { 173 min_sample_time_ns_ = time_nsec; 174 } 175 if (max_sample_time_ns_ == 0 || time_nsec > max_sample_time_ns_) { 176 max_sample_time_ns_ = time_nsec; 177 } 178 } 179 180 std::unique_ptr<ProcessProfile> makeProcessProfile(Profile* data) { 181 ProcessProfile* pp = new ProcessProfile(); 182 pp->pid = pid_; 183 pp->data.Swap(data); 184 pp->min_sample_time_ns = min_sample_time_ns_; 185 pp->max_sample_time_ns = max_sample_time_ns_; 186 return std::unique_ptr<ProcessProfile>(pp); 187 } 188 189 private: 190 Pid pid_; 191 int64 min_sample_time_ns_ = 0; 192 int64 max_sample_time_ns_ = 0; 193 }; 194 195 class PerfDataConverter : public PerfDataHandler { 196 public: 197 explicit PerfDataConverter(const quipper::PerfDataProto& perf_data, 198 uint32 sample_labels = kNoLabels, 199 uint32 options = kGroupByPids) 200 : perf_data_(perf_data), 201 sample_labels_(sample_labels), 202 options_(options) {} 203 PerfDataConverter(const PerfDataConverter&) = delete; 204 PerfDataConverter& operator=(const PerfDataConverter&) = delete; 205 virtual ~PerfDataConverter() {} 206 207 ProcessProfiles Profiles(); 208 209 // Callbacks for PerfDataHandler 210 void Sample(const PerfDataHandler::SampleContext& sample) override; 211 void Comm(const CommContext& comm) override; 212 void MMap(const MMapContext& mmap) override; 213 214 private: 215 // Adds a new sample updating the event counters if such sample is not present 216 // in the profile initializing its metrics. Updates the metrics associated 217 // with the sample if the sample was added before. 218 void AddOrUpdateSample(const PerfDataHandler::SampleContext& context, 219 const Pid& pid, const SampleKey& sample_key, 220 ProfileBuilder* builder); 221 222 // Adds a new location to the profile if such location is not present in the 223 // profile, returning the ID of the location. It also adds the profile mapping 224 // corresponding to the specified handler mapping. 225 uint64 AddOrGetLocation(const Pid& pid, uint64 addr, 226 const PerfDataHandler::Mapping* mapping, 227 ProfileBuilder* builder); 228 229 // Adds a new mapping to the profile if such mapping is not present in the 230 // profile, returning the ID of the mapping. It returns 0 to indicate that the 231 // mapping was not added (only happens if smap == 0 currently). 232 uint64 AddOrGetMapping(const Pid& pid, const PerfDataHandler::Mapping* smap, 233 ProfileBuilder* builder); 234 235 // Returns whether pid labels were requested for inclusion in the 236 // profile.proto's Sample.Label field. 237 bool IncludePidLabels() const { return (sample_labels_ & kPidLabel); } 238 // Returns whether tid labels were requested for inclusion in the 239 // profile.proto's Sample.Label field. 240 bool IncludeTidLabels() const { return (sample_labels_ & kTidLabel); } 241 // Returns whether timestamp_ns labels were requested for inclusion in the 242 // profile.proto's Sample.Label field. 243 bool IncludeTimestampNsLabels() const { 244 return (sample_labels_ & kTimestampNsLabel); 245 } 246 // Returns whether execution_mode labels were requested for inclusion in the 247 // profile.proto's Sample.Label field. 248 bool IncludeExecutionModeLabels() const { 249 return (sample_labels_ & kExecutionModeLabel); 250 } 251 // Returns whether comm labels were requested for inclusion in the 252 // profile.proto's Sample.Label field. 253 bool IncludeCommLabels() const { return (sample_labels_ & kCommLabel); } 254 255 SampleKey MakeSampleKey(const PerfDataHandler::SampleContext& sample, 256 ProfileBuilder* builder); 257 258 ProfileBuilder* GetOrCreateBuilder( 259 const PerfDataHandler::SampleContext& sample); 260 261 const quipper::PerfDataProto& perf_data_; 262 // Using deque so that appends do not invalidate existing pointers. 263 std::deque<ProfileBuilder> builders_; 264 std::deque<ProcessMeta> process_metas_; 265 266 struct PerPidInfo { 267 ProfileBuilder* builder = nullptr; 268 ProcessMeta* process_meta = nullptr; 269 LocationMap location_map; 270 MappingMap mapping_map; 271 std::unordered_map<Pid, string> tid_to_comm_map; 272 SampleMap sample_map; 273 void clear() { 274 builder = nullptr; 275 process_meta = nullptr; 276 location_map.clear(); 277 mapping_map.clear(); 278 tid_to_comm_map.clear(); 279 sample_map.clear(); 280 } 281 }; 282 std::unordered_map<Pid, PerPidInfo> per_pid_; 283 284 const uint32 sample_labels_; 285 const uint32 options_; 286 }; 287 288 SampleKey PerfDataConverter::MakeSampleKey( 289 const PerfDataHandler::SampleContext& sample, ProfileBuilder* builder) { 290 SampleKey sample_key; 291 sample_key.pid = sample.sample.has_pid() ? sample.sample.pid() : 0; 292 sample_key.tid = 293 (IncludeTidLabels() && sample.sample.has_tid()) ? sample.sample.tid() : 0; 294 sample_key.time_ns = 295 (IncludeTimestampNsLabels() && sample.sample.has_sample_time_ns()) 296 ? sample.sample.sample_time_ns() 297 : 0; 298 if (IncludeExecutionModeLabels()) { 299 sample_key.exec_mode = PerfExecMode(sample); 300 } 301 if (IncludeCommLabels() && sample.sample.has_pid() && 302 sample.sample.has_tid()) { 303 Pid pid = sample.sample.pid(); 304 Pid tid = sample.sample.tid(); 305 const string& comm = per_pid_[pid].tid_to_comm_map[tid]; 306 if (!comm.empty()) { 307 sample_key.comm = UTF8StringId(comm, builder); 308 } 309 } 310 return sample_key; 311 } 312 313 ProfileBuilder* PerfDataConverter::GetOrCreateBuilder( 314 const PerfDataHandler::SampleContext& sample) { 315 Pid builder_pid = (options_ & kGroupByPids) ? sample.sample.pid() : 0; 316 auto& per_pid = per_pid_[builder_pid]; 317 if (per_pid.builder == nullptr) { 318 builders_.push_back(ProfileBuilder()); 319 per_pid.builder = &builders_.back(); 320 process_metas_.push_back(ProcessMeta(builder_pid)); 321 per_pid.process_meta = &process_metas_.back(); 322 323 ProfileBuilder* builder = per_pid.builder; 324 Profile* profile = builder->mutable_profile(); 325 int unknown_event_idx = 0; 326 for (int event_idx = 0; event_idx < perf_data_.file_attrs_size(); 327 ++event_idx) { 328 // Come up with an event name for this event. perf.data will usually 329 // contain an event_types section of the same cardinality as its 330 // file_attrs; in this case we can just use the name there. Otherwise 331 // we just give it an anonymous name. 332 string event_name = ""; 333 if (perf_data_.file_attrs_size() == perf_data_.event_types_size()) { 334 const auto& event_type = perf_data_.event_types(event_idx); 335 if (event_type.has_name()) { 336 event_name = event_type.name() + "_"; 337 } 338 } 339 if (event_name == "") { 340 event_name = "event_" + std::to_string(unknown_event_idx++) + "_"; 341 } 342 auto sample_type = profile->add_sample_type(); 343 sample_type->set_type(UTF8StringId(event_name + "sample", builder)); 344 sample_type->set_unit(builder->StringId("count")); 345 sample_type = profile->add_sample_type(); 346 sample_type->set_type(UTF8StringId(event_name + "event", builder)); 347 sample_type->set_unit(builder->StringId("count")); 348 } 349 if (sample.main_mapping == nullptr) { 350 auto fake_main = profile->add_mapping(); 351 fake_main->set_id(profile->mapping_size()); 352 fake_main->set_memory_start(0); 353 fake_main->set_memory_limit(1); 354 } else { 355 AddOrGetMapping(sample.sample.pid(), sample.main_mapping, builder); 356 } 357 if (perf_data_.string_metadata().has_perf_version()) { 358 string perf_version = 359 "perf-version:" + perf_data_.string_metadata().perf_version().value(); 360 profile->add_comment(UTF8StringId(perf_version, builder)); 361 } 362 if (perf_data_.string_metadata().has_perf_command_line_whole()) { 363 string perf_command = 364 "perf-command:" + 365 perf_data_.string_metadata().perf_command_line_whole().value(); 366 profile->add_comment(UTF8StringId(perf_command, builder)); 367 } 368 } else { 369 Profile* profile = per_pid.builder->mutable_profile(); 370 if ((options_ & kGroupByPids) && sample.main_mapping != nullptr && 371 sample.main_mapping->filename != nullptr) { 372 const string& filename = 373 profile->string_table(profile->mapping(0).filename()); 374 const string& sample_filename = MappingFilename(sample.main_mapping); 375 376 if (filename != sample_filename) { 377 if (options_ & kFailOnMainMappingMismatch) { 378 LOG(FATAL) << "main mapping mismatch: " << sample.sample.pid() << " " 379 << filename << " " << sample_filename; 380 } else { 381 LOG(WARNING) << "main mapping mismatch: " << sample.sample.pid() 382 << " " << filename << " " << sample_filename; 383 } 384 } 385 } 386 } 387 if (sample.sample.sample_time_ns()) { 388 per_pid.process_meta->UpdateTimestamps(sample.sample.sample_time_ns()); 389 } 390 return per_pid.builder; 391 } 392 393 uint64 PerfDataConverter::AddOrGetMapping(const Pid& pid, 394 const PerfDataHandler::Mapping* smap, 395 ProfileBuilder* builder) { 396 if (builder == nullptr) { 397 std::cerr << "Cannot add mapping to null builder." << std::endl; 398 abort(); 399 } 400 401 if (smap == nullptr) { 402 return 0; 403 } 404 405 MappingMap& mapmap = per_pid_[pid].mapping_map; 406 auto it = mapmap.find(smap); 407 if (it != mapmap.end()) { 408 return it->second; 409 } 410 411 Profile* profile = builder->mutable_profile(); 412 auto mapping = profile->add_mapping(); 413 uint64 mapping_id = profile->mapping_size(); 414 mapping->set_id(mapping_id); 415 mapping->set_memory_start(smap->start); 416 mapping->set_memory_limit(smap->limit); 417 mapping->set_file_offset(smap->file_offset); 418 if (smap->build_id != nullptr && !smap->build_id->empty()) { 419 mapping->set_build_id(UTF8StringId(*smap->build_id, builder)); 420 } 421 mapping->set_filename(UTF8StringId(MappingFilename(smap), builder)); 422 if (mapping->memory_start() >= mapping->memory_limit()) { 423 std::cerr << "The start of the mapping must be strictly less than its" 424 << "limit in file: " << mapping->filename() << std::endl 425 << "Start: " << mapping->memory_start() << std::endl 426 << "Limit: " << mapping->memory_limit() << std::endl; 427 abort(); 428 } 429 mapmap.insert(std::make_pair(smap, mapping_id)); 430 return mapping_id; 431 } 432 433 void PerfDataConverter::AddOrUpdateSample( 434 const PerfDataHandler::SampleContext& context, const Pid& pid, 435 const SampleKey& sample_key, 436 ProfileBuilder* builder) { 437 438 perftools::profiles::Sample* sample = per_pid_[pid].sample_map[sample_key]; 439 440 if (sample == nullptr) { 441 Profile* profile = builder->mutable_profile(); 442 sample = profile->add_sample(); 443 per_pid_[pid].sample_map[sample_key] = sample; 444 for (const auto& location_id : sample_key.stack) { 445 sample->add_location_id(location_id); 446 } 447 // Emit any requested labels. 448 if (IncludePidLabels() && context.sample.has_pid()) { 449 auto* label = sample->add_label(); 450 label->set_key(builder->StringId(PidLabelKey)); 451 label->set_num(static_cast<int64>(context.sample.pid())); 452 } 453 if (IncludeTidLabels() && context.sample.has_tid()) { 454 auto* label = sample->add_label(); 455 label->set_key(builder->StringId(TidLabelKey)); 456 label->set_num(static_cast<int64>(context.sample.tid())); 457 } 458 if (IncludeCommLabels() && sample_key.comm != 0) { 459 auto* label = sample->add_label(); 460 label->set_key(builder->StringId(CommLabelKey)); 461 label->set_str(sample_key.comm); 462 } 463 if (IncludeTimestampNsLabels() && context.sample.has_sample_time_ns()) { 464 auto* label = sample->add_label(); 465 label->set_key(builder->StringId(TimestampNsLabelKey)); 466 int64 timestamp_ns_as_int64 = 467 static_cast<int64>(context.sample.sample_time_ns()); 468 label->set_num(timestamp_ns_as_int64); 469 } 470 if (IncludeExecutionModeLabels() && sample_key.exec_mode != Unknown) { 471 auto* label = sample->add_label(); 472 label->set_key(builder->StringId(ExecutionModeLabelKey)); 473 label->set_str(builder->StringId(ExecModeString(sample_key.exec_mode))); 474 } 475 // Two values per collected event: the first is sample counts, the second is 476 // event counts (unsampled weight for each sample). 477 for (int event_id = 0; event_id < perf_data_.file_attrs_size(); 478 ++event_id) { 479 sample->add_value(0); 480 sample->add_value(0); 481 } 482 } 483 484 int64 weight = 1; 485 // If the sample has a period, use that in preference 486 if (context.sample.period() > 0) { 487 weight = context.sample.period(); 488 } else if (context.file_attrs_index >= 0) { 489 uint64 period = 490 perf_data_.file_attrs(context.file_attrs_index).attr().sample_period(); 491 if (period > 0) { 492 // If sampling used a fixed period, use that as the weight. 493 weight = period; 494 } 495 } 496 int event_index = context.file_attrs_index; 497 sample->set_value(2 * event_index, sample->value(2 * event_index) + 1); 498 sample->set_value(2 * event_index + 1, 499 sample->value(2 * event_index + 1) + weight); 500 } 501 502 uint64 PerfDataConverter::AddOrGetLocation( 503 const Pid& pid, uint64 addr, const PerfDataHandler::Mapping* mapping, 504 ProfileBuilder* builder) { 505 LocationMap& loc_map = per_pid_[pid].location_map; 506 auto loc_it = loc_map.find(addr); 507 if (loc_it != loc_map.end()) { 508 return loc_it->second; 509 } 510 511 Profile* profile = builder->mutable_profile(); 512 perftools::profiles::Location* loc = profile->add_location(); 513 uint64 loc_id = profile->location_size(); 514 loc->set_id(loc_id); 515 loc->set_address(addr); 516 uint64 mapping_id = AddOrGetMapping(pid, mapping, builder); 517 if (mapping_id != 0) { 518 loc->set_mapping_id(mapping_id); 519 } else { 520 if (addr != 0) { 521 std::cerr << "Found unmapped address: " << addr << " in PID " << pid 522 << std::endl; 523 abort(); 524 } 525 } 526 loc_map[addr] = loc_id; 527 return loc_id; 528 } 529 530 void PerfDataConverter::Comm(const CommContext& comm) { 531 Pid pid = comm.comm->pid(); 532 Pid tid = comm.comm->tid(); 533 if (pid == tid) { 534 // pid==tid means an exec() happened, so clear everything from the 535 // existing pid. 536 per_pid_[pid].clear(); 537 } 538 539 per_pid_[pid].tid_to_comm_map[tid] = comm.comm->comm(); 540 } 541 542 // Invalidates the locations in location_map in the mmap event's range. 543 void PerfDataConverter::MMap(const MMapContext& mmap) { 544 LocationMap& loc_map = per_pid_[mmap.pid].location_map; 545 loc_map.erase(loc_map.lower_bound(mmap.mapping->start), 546 loc_map.lower_bound(mmap.mapping->limit)); 547 } 548 549 void PerfDataConverter::Sample(const PerfDataHandler::SampleContext& sample) { 550 if (sample.file_attrs_index < 0 || 551 sample.file_attrs_index >= perf_data_.file_attrs_size()) { 552 LOG(WARNING) << "out of bounds file_attrs_index: " 553 << sample.file_attrs_index; 554 return; 555 } 556 557 Pid event_pid = sample.sample.pid(); 558 ProfileBuilder *builder = GetOrCreateBuilder(sample); 559 SampleKey sample_key = MakeSampleKey(sample, builder); 560 561 uint64 ip = sample.sample_mapping != nullptr ? sample.sample.ip() : 0; 562 if (ip != 0) { 563 const auto start = sample.sample_mapping->start; 564 const auto limit = sample.sample_mapping->limit; 565 if (ip < start || ip >= limit) { 566 std::cerr << "IP is out of bound of mapping." << std::endl 567 << "IP: " << ip << std::endl 568 << "Start: " << start << std::endl 569 << "Limit: " << limit << std::endl; 570 } 571 } 572 573 // Leaf at stack[0] 574 sample_key.stack.push_back( 575 AddOrGetLocation(event_pid, ip, sample.sample_mapping, builder)); 576 577 // LBR callstacks include only user call chains. If this is an LBR sample, 578 // we get the kernel callstack from the sample's callchain, and the user 579 // callstack from the sample's branch_stack. 580 const bool lbr_sample = !sample.branch_stack.empty(); 581 bool skipped_dup = false; 582 for (const auto& frame : sample.callchain) { 583 if (lbr_sample && frame.ip == PERF_CONTEXT_USER) { 584 break; 585 } 586 if (!skipped_dup && sample_key.stack.size() == 1 && frame.ip == ip) { 587 skipped_dup = true; 588 // Newer versions of perf_events include the IP at the leaf of 589 // the callchain. 590 continue; 591 } 592 if (frame.mapping == nullptr) { 593 continue; 594 } 595 uint64 frame_ip = frame.ip; 596 // Why <=? Because this is a return address, which should be 597 // preceded by a call (the "real" context.) If we're at the edge 598 // of the mapping, we're really off its edge. 599 if (frame_ip <= frame.mapping->start) { 600 continue; 601 } 602 // these aren't real callchain entries, just hints as to kernel/user 603 // addresses. 604 if (frame_ip >= PERF_CONTEXT_MAX) { 605 continue; 606 } 607 608 // subtract one so we point to the call instead of the return addr. 609 frame_ip--; 610 sample_key.stack.push_back( 611 AddOrGetLocation(event_pid, frame_ip, frame.mapping, builder)); 612 } 613 for (const auto& frame : sample.branch_stack) { 614 // branch_stack entries are pairs of <from, to> locations corresponding to 615 // addresses of call instructions and target addresses of those calls. 616 // We need only the addresses of the function call instructions, stored in 617 // the 'from' field, to recover the call chains. 618 if (frame.from.mapping == nullptr) { 619 continue; 620 } 621 // An LBR entry includes the address of the call instruction, so we don't 622 // have to do any adjustments. 623 if (frame.from.ip < frame.from.mapping->start) { 624 continue; 625 } 626 sample_key.stack.push_back(AddOrGetLocation(event_pid, frame.from.ip, 627 frame.from.mapping, builder)); 628 } 629 AddOrUpdateSample(sample, event_pid, sample_key, builder); 630 } 631 632 ProcessProfiles PerfDataConverter::Profiles() { 633 ProcessProfiles pps; 634 for (int i = 0; i < builders_.size(); i++) { 635 auto& b = builders_[i]; 636 b.Finalize(); 637 auto pp = process_metas_[i].makeProcessProfile(b.mutable_profile()); 638 pps.push_back(std::move(pp)); 639 } 640 return pps; 641 } 642 643 } // namespace 644 645 ProcessProfiles PerfDataProtoToProfiles(const quipper::PerfDataProto* perf_data, 646 const uint32 sample_labels, 647 const uint32 options) { 648 PerfDataConverter converter(*perf_data, sample_labels, options); 649 PerfDataHandler::Process(*perf_data, &converter); 650 return converter.Profiles(); 651 } 652 653 ProcessProfiles RawPerfDataToProfiles(const void* raw, const int raw_size, 654 const std::map<string, string>& build_ids, 655 const uint32 sample_labels, 656 const uint32 options) { 657 quipper::PerfReader reader; 658 if (!reader.ReadFromPointer(reinterpret_cast<const char*>(raw), raw_size)) { 659 LOG(ERROR) << "Could not read input perf.data"; 660 return ProcessProfiles(); 661 } 662 663 reader.InjectBuildIDs(build_ids); 664 // Perf populates info about the kernel using multiple pathways, 665 // which don't actually all match up how they name kernel data; in 666 // particular, buildids are reported by a different name than the 667 // actual "mmap" info. Normalize these names so our ProcessProfiles 668 // will match kernel mappings to a buildid. 669 reader.LocalizeUsingFilenames({ 670 {"[kernel.kallsyms]_text", "[kernel.kallsyms]"}, 671 {"[kernel.kallsyms]_stext", "[kernel.kallsyms]"}, 672 }); 673 674 // Use PerfParser to modify reader's events to have magic done to them such 675 // as hugepage deduction and sorting events based on time, if timestamps are 676 // present. 677 quipper::PerfParserOptions opts; 678 opts.sort_events_by_time = true; 679 opts.deduce_huge_page_mappings = true; 680 opts.combine_mappings = true; 681 quipper::PerfParser parser(&reader, opts); 682 if (!parser.ParseRawEvents()) { 683 LOG(ERROR) << "Could not parse perf events."; 684 return ProcessProfiles(); 685 } 686 687 return PerfDataProtoToProfiles(&reader.proto(), sample_labels, options); 688 } 689 690 } // namespace perftools 691