• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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