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
ExecModeString(ExecutionMode mode)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
PerfExecMode(const PerfDataHandler::SampleContext & sample)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.
UTF8StringId(const string & s,ProfileBuilder * builder)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.
MappingFilename(const PerfDataHandler::Mapping * m)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 {
operator ()perftools::__anonfe9becae0111::SampleKeyEqualityTester121 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 {
operator ()perftools::__anonfe9becae0111::SampleKeyHasher129 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.
ProcessMeta(Pid pid)168 explicit ProcessMeta(Pid pid) : pid_(pid) {}
169
170 // Updates the bounding time interval ranges per specified timestamp.
UpdateTimestamps(int64 time_nsec)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
makeProcessProfile(Profile * data)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:
PerfDataConverter(const quipper::PerfDataProto & perf_data,uint32 sample_labels=kNoLabels,uint32 options=kGroupByPids)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;
~PerfDataConverter()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.
IncludePidLabels() const237 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.
IncludeTidLabels() const240 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.
IncludeTimestampNsLabels() const243 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.
IncludeExecutionModeLabels() const248 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.
IncludeCommLabels() const253 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;
clearperftools::__anonfe9becae0111::PerfDataConverter::PerPidInfo273 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
MakeSampleKey(const PerfDataHandler::SampleContext & sample,ProfileBuilder * builder)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
GetOrCreateBuilder(const PerfDataHandler::SampleContext & sample)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
AddOrGetMapping(const Pid & pid,const PerfDataHandler::Mapping * smap,ProfileBuilder * builder)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
AddOrUpdateSample(const PerfDataHandler::SampleContext & context,const Pid & pid,const SampleKey & sample_key,ProfileBuilder * builder)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
AddOrGetLocation(const Pid & pid,uint64 addr,const PerfDataHandler::Mapping * mapping,ProfileBuilder * builder)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
Comm(const CommContext & comm)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.
MMap(const MMapContext & mmap)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
Sample(const PerfDataHandler::SampleContext & sample)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
Profiles()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
PerfDataProtoToProfiles(const quipper::PerfDataProto * perf_data,const uint32 sample_labels,const uint32 options)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
RawPerfDataToProfiles(const void * raw,const int raw_size,const std::map<string,string> & build_ids,const uint32 sample_labels,const uint32 options)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