• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "perf_parser.h"
6 
7 #include <fcntl.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13 #include <algorithm>
14 
15 #include <memory>
16 #include <set>
17 #include <sstream>
18 
19 #include "base/logging.h"
20 
21 #include "address_mapper.h"
22 #include "binary_data_utils.h"
23 #include "compat/proto.h"
24 #include "compat/string.h"
25 #include "dso.h"
26 #include "huge_page_deducer.h"
27 
28 namespace quipper {
29 
30 using BranchStackEntry = PerfDataProto_BranchStackEntry;
31 using CommEvent = PerfDataProto_CommEvent;
32 using ForkEvent = PerfDataProto_ForkEvent;
33 using MMapEvent = PerfDataProto_MMapEvent;
34 using SampleEvent = PerfDataProto_SampleEvent;
35 
36 namespace {
37 
38 // MMAPs are aligned to pages of this many bytes.
39 const uint64_t kMmapPageAlignment = sysconf(_SC_PAGESIZE);
40 
41 // Name and ID of the kernel swapper process.
42 const char kSwapperCommandName[] = "swapper";
43 const uint32_t kSwapperPid = 0;
44 
45 // Returns the offset within a page of size |kMmapPageAlignment|, given an
46 // address. Requires that |kMmapPageAlignment| be a power of 2.
GetPageAlignedOffset(uint64_t addr)47 uint64_t GetPageAlignedOffset(uint64_t addr) {
48   return addr % kMmapPageAlignment;
49 }
50 
IsNullBranchStackEntry(const BranchStackEntry & entry)51 bool IsNullBranchStackEntry(const BranchStackEntry& entry) {
52   return (!entry.from_ip() && !entry.to_ip());
53 }
54 
55 }  // namespace
56 
PerfParser(PerfReader * reader)57 PerfParser::PerfParser(PerfReader* reader) : reader_(reader) {}
58 
~PerfParser()59 PerfParser::~PerfParser() {}
60 
PerfParser(PerfReader * reader,const PerfParserOptions & options)61 PerfParser::PerfParser(PerfReader* reader, const PerfParserOptions& options)
62     : reader_(reader), options_(options) {}
63 
ParseRawEvents()64 bool PerfParser::ParseRawEvents() {
65   if (options_.sort_events_by_time) {
66     reader_->MaybeSortEventsByTime();
67   }
68 
69   // Just in case there was data from a previous call.
70   process_mappers_.clear();
71 
72   // Find huge page mappings.
73   if (options_.deduce_huge_page_mappings) {
74       DeduceHugePages(reader_->mutable_events());
75   }
76 
77   // Combine split mappings.  Because the remapping process makes addresses
78   // contiguous, we cannot try to combine mappings in these situations (as we
79   // collapse maps that were non-contiguous).
80   if (options_.combine_mappings && !options_.do_remap) {
81     CombineMappings(reader_->mutable_events());
82   }
83 
84   // Clear the parsed events to reset their fields. Otherwise, non-sample events
85   // may have residual DSO+offset info.
86   parsed_events_.clear();
87 
88   // Events of type PERF_RECORD_FINISHED_ROUND don't have a timestamp, and are
89   // not needed.
90   // use the partial-sorting of events between rounds to sort faster.
91   parsed_events_.resize(reader_->events().size());
92   size_t write_index = 0;
93   for (int i = 0; i < reader_->events().size(); ++i) {
94     if (reader_->events().Get(i).header().type() == PERF_RECORD_FINISHED_ROUND)
95       continue;
96     parsed_events_[write_index++].event_ptr =
97         reader_->mutable_events()->Mutable(i);
98   }
99   parsed_events_.resize(write_index);
100 
101   ProcessEvents();
102 
103   if (!options_.discard_unused_events) return true;
104 
105   // Some MMAP/MMAP2 events' mapped regions will not have any samples. These
106   // MMAP/MMAP2 events should be dropped. |parsed_events_| should be
107   // reconstructed without these events.
108   write_index = 0;
109   size_t read_index;
110   for (read_index = 0; read_index < parsed_events_.size(); ++read_index) {
111     const ParsedEvent& event = parsed_events_[read_index];
112     if (event.event_ptr->has_mmap_event() &&
113         event.num_samples_in_mmap_region == 0) {
114       continue;
115     }
116     if (read_index != write_index) parsed_events_[write_index] = event;
117     ++write_index;
118   }
119   CHECK_LE(write_index, parsed_events_.size());
120   parsed_events_.resize(write_index);
121 
122   // Update the events in |reader_| to match the updated events.
123   UpdatePerfEventsFromParsedEvents();
124 
125   return true;
126 }
127 
ProcessUserEvents(PerfEvent & event)128 bool PerfParser::ProcessUserEvents(PerfEvent& event) {
129   // New user events from PERF-4.13 is not yet supported
130   switch (event.header().type()) {
131     case PERF_RECORD_AUXTRACE:
132       VLOG(1) << "Parsed event type: " << event.header().type()
133               << ". Doing nothing.";
134       break;
135     default:
136       VLOG(1) << "Unsupported event type: " << event.header().type();
137       break;
138   }
139   return true;
140 }
141 
ProcessEvents()142 bool PerfParser::ProcessEvents() {
143   stats_ = {0};
144 
145   stats_.did_remap = false;  // Explicitly clear the remap flag.
146 
147   // Pid 0 is called the swapper process. Even though perf does not record a
148   // COMM event for pid 0, we act like we did receive a COMM event for it. Perf
149   // does this itself, example:
150   //   http://lxr.free-electrons.com/source/tools/perf/util/session.c#L1120
151   commands_.insert(kSwapperCommandName);
152   pidtid_to_comm_map_[std::make_pair(kSwapperPid, kSwapperPid)] =
153       &(*commands_.find(kSwapperCommandName));
154 
155   // NB: Not necessarily actually sorted by time.
156   for (size_t i = 0; i < parsed_events_.size(); ++i) {
157     ParsedEvent& parsed_event = parsed_events_[i];
158     PerfEvent& event = *parsed_event.event_ptr;
159 
160     // Process user events
161     if (event.header().type() >= PERF_RECORD_USER_TYPE_START) {
162       if (!ProcessUserEvents(event)) {
163         return false;
164       }
165       continue;
166     }
167 
168     switch (event.header().type()) {
169       case PERF_RECORD_SAMPLE:
170         // SAMPLE doesn't have any fields to log at a fixed,
171         // previously-endian-swapped location. This used to log ip.
172         VLOG(1) << "SAMPLE";
173         ++stats_.num_sample_events;
174         if (MapSampleEvent(&parsed_event)) ++stats_.num_sample_events_mapped;
175         break;
176       case PERF_RECORD_MMAP:
177       case PERF_RECORD_MMAP2: {
178         const char* mmap_type_name =
179             event.header().type() == PERF_RECORD_MMAP ? "MMAP" : "MMAP2";
180         VLOG(1) << mmap_type_name << ": " << event.mmap_event().filename();
181         ++stats_.num_mmap_events;
182         // Use the array index of the current mmap event as a unique identifier.
183         CHECK(MapMmapEvent(event.mutable_mmap_event(), i))
184             << "Unable to map " << mmap_type_name << " event!";
185         // No samples in this MMAP region yet, hopefully.
186         parsed_event.num_samples_in_mmap_region = 0;
187         DSOInfo dso_info;
188         dso_info.name = event.mmap_event().filename();
189         if (event.header().type() == PERF_RECORD_MMAP2) {
190           dso_info.maj = event.mmap_event().maj();
191           dso_info.min = event.mmap_event().min();
192           dso_info.ino = event.mmap_event().ino();
193         }
194         name_to_dso_.emplace(dso_info.name, dso_info);
195         break;
196       }
197       case PERF_RECORD_FORK:
198         // clang-format off
199         VLOG(1) << "FORK: " << event.fork_event().ppid()
200                 << ":" << event.fork_event().ptid()
201                 << " -> " << event.fork_event().pid()
202                 << ":" << event.fork_event().tid();
203         // clang-format on
204         ++stats_.num_fork_events;
205         CHECK(MapForkEvent(event.fork_event())) << "Unable to map FORK event!";
206         break;
207       case PERF_RECORD_EXIT:
208         // EXIT events have the same structure as FORK events.
209         // clang-format off
210         VLOG(1) << "EXIT: " << event.fork_event().ppid()
211                 << ":" << event.fork_event().ptid();
212         // clang-format on
213         ++stats_.num_exit_events;
214         break;
215       case PERF_RECORD_COMM:
216       {
217         // clang-format off
218         VLOG(1) << "COMM: " << event.comm_event().pid()
219                 << ":" << event.comm_event().tid() << ": "
220                 << event.comm_event().comm();
221         // clang-format on
222         ++stats_.num_comm_events;
223         CHECK(MapCommEvent(event.comm_event()));
224         commands_.insert(event.comm_event().comm());
225         const PidTid pidtid =
226             std::make_pair(event.comm_event().pid(), event.comm_event().tid());
227         pidtid_to_comm_map_[pidtid] =
228             &(*commands_.find(event.comm_event().comm()));
229         break;
230       }
231       case PERF_RECORD_LOST:
232       case PERF_RECORD_THROTTLE:
233       case PERF_RECORD_UNTHROTTLE:
234       case PERF_RECORD_READ:
235       case PERF_RECORD_AUX:
236         VLOG(1) << "Parsed event type: " << event.header().type()
237                 << ". Doing nothing.";
238         break;
239       case PERF_RECORD_ITRACE_START:
240       case PERF_RECORD_LOST_SAMPLES:
241       case PERF_RECORD_SWITCH:
242       case PERF_RECORD_SWITCH_CPU_WIDE:
243       case PERF_RECORD_NAMESPACES:
244         VLOG(1) << "Parsed event type: " << event.header().type()
245                 << ". Not yet supported.";
246         break;
247       default:
248         LOG(ERROR) << "Unknown event type: " << event.header().type();
249         return false;
250     }
251   }
252   if (!FillInDsoBuildIds()) return false;
253 
254   // Print stats collected from parsing.
255   // clang-format off
256   VLOG(1)   << "Parser processed: "
257             << stats_.num_mmap_events << " MMAP/MMAP2 events, "
258             << stats_.num_comm_events << " COMM events, "
259             << stats_.num_fork_events << " FORK events, "
260             << stats_.num_exit_events << " EXIT events, "
261             << stats_.num_sample_events << " SAMPLE events, "
262             << stats_.num_sample_events_mapped << " of these were mapped";
263   // clang-format on
264 
265   float sample_mapping_percentage =
266       static_cast<float>(stats_.num_sample_events_mapped) /
267       stats_.num_sample_events * 100.;
268   float threshold = options_.sample_mapping_percentage_threshold;
269   if (sample_mapping_percentage < threshold) {
270     LOG(ERROR) << "Mapped " << static_cast<int>(sample_mapping_percentage)
271                << "% of samples, expected at least "
272                << static_cast<int>(threshold) << "%";
273     return false;
274   }
275   stats_.did_remap = options_.do_remap;
276   return true;
277 }
278 
279 namespace {
280 
281 class FdCloser {
282  public:
FdCloser(int fd)283   explicit FdCloser(int fd) : fd_(fd) {}
~FdCloser()284   ~FdCloser() {
285     if (fd_ != -1) close(fd_);
286   }
287 
288  private:
289   FdCloser() = delete;
290   FdCloser(FdCloser&) = delete;
291 
292   int fd_;
293 };
294 
295 // Merges two uint32_t into a uint64_t for hashing in an unordered_set because
296 // there is no default hash method for a pair.
mergeTwoU32(uint32_t first,uint32_t second)297 uint64_t mergeTwoU32(uint32_t first, uint32_t second) {
298   return (uint64_t)first << 32 | second;
299 }
300 
301 // Splits a given uint64_t into two uint32_t. This reverts the above merge
302 // operation to retrieve the two uint32_t from an unordered_set.
splitU64(uint64_t value)303 std::pair<uint32_t, uint32_t> splitU64(uint64_t value) {
304   return std::make_pair(value >> 32,
305                         std::numeric_limits<uint32_t>::max() & value);
306 }
307 
ReadElfBuildIdIfSameInode(const string & dso_path,const DSOInfo & dso,string * buildid)308 bool ReadElfBuildIdIfSameInode(const string& dso_path, const DSOInfo& dso,
309                                string* buildid) {
310   int fd = open(dso_path.c_str(), O_RDONLY);
311   FdCloser fd_closer(fd);
312   if (fd == -1) {
313     if (errno != ENOENT) LOG(ERROR) << "Failed to open ELF file: " << dso_path;
314     return false;
315   }
316 
317   struct stat s;
318   CHECK_GE(fstat(fd, &s), 0);
319   // Only reject based on inode if we actually have device info (from MMAP2).
320   if (dso.maj != 0 && dso.min != 0 && !SameInode(dso, &s)) return false;
321 
322   return ReadElfBuildId(fd, buildid);
323 }
324 
325 // Looks up build ID of a given DSO by reading directly from the file system.
326 // - Does not support reading build ID of the main kernel binary.
327 // - Reads build IDs of kernel modules and other DSOs using functions in dso.h.
FindDsoBuildId(const DSOInfo & dso_info)328 string FindDsoBuildId(const DSOInfo& dso_info) {
329   string buildid_bin;
330   const string& dso_name = dso_info.name;
331   if (IsKernelNonModuleName(dso_name)) return buildid_bin;  // still empty
332   // Does this look like a kernel module?
333   if (dso_name.size() >= 2 && dso_name[0] == '[' && dso_name.back() == ']') {
334     // This may not be successful, but either way, just return. buildid_bin
335     // will be empty if the module was not found.
336     ReadModuleBuildId(dso_name.substr(1, dso_name.size() - 2), &buildid_bin);
337     return buildid_bin;
338   }
339   // Try normal files, possibly inside containers.
340   u32 last_pid = 0;
341   std::vector<uint64_t> threads(dso_info.threads.begin(),
342                                 dso_info.threads.end());
343   std::sort(threads.begin(), threads.end());
344   for (auto pidtid_it : threads) {
345     uint32_t pid, tid;
346     std::tie(pid, tid) = splitU64(pidtid_it);
347     std::stringstream dso_path_stream;
348     dso_path_stream << "/proc/" << tid << "/root/" << dso_name;
349     string dso_path = dso_path_stream.str();
350     if (ReadElfBuildIdIfSameInode(dso_path, dso_info, &buildid_bin)) {
351       return buildid_bin;
352     }
353     // Avoid re-trying the parent process if it's the same for multiple threads.
354     // dso_info.threads is sorted, so threads in a process should be adjacent.
355     if (pid == last_pid || pid == tid) continue;
356     last_pid = pid;
357     // Try the parent process:
358     std::stringstream parent_dso_path_stream;
359     parent_dso_path_stream << "/proc/" << pid << "/root/" << dso_name;
360     string parent_dso_path = parent_dso_path_stream.str();
361     if (ReadElfBuildIdIfSameInode(parent_dso_path, dso_info, &buildid_bin)) {
362       return buildid_bin;
363     }
364   }
365   // Still don't have a buildid. Try our own filesystem:
366   if (ReadElfBuildIdIfSameInode(dso_name, dso_info, &buildid_bin)) {
367     return buildid_bin;
368   }
369   return buildid_bin;  // still empty.
370 }
371 
372 }  // namespace
373 
FillInDsoBuildIds()374 bool PerfParser::FillInDsoBuildIds() {
375   std::map<string, string> filenames_to_build_ids;
376   reader_->GetFilenamesToBuildIDs(&filenames_to_build_ids);
377 
378   std::map<string, string> new_buildids;
379 
380   for (std::pair<const string, DSOInfo>& kv : name_to_dso_) {
381     DSOInfo& dso_info = kv.second;
382     const auto it = filenames_to_build_ids.find(dso_info.name);
383     if (it != filenames_to_build_ids.end()) {
384       dso_info.build_id = it->second;
385     }
386     // If there is both an existing build ID and a new build ID returned by
387     // FindDsoBuildId(), overwrite the existing build ID.
388     if (options_.read_missing_buildids && dso_info.hit) {
389       string buildid_bin = FindDsoBuildId(dso_info);
390       if (!buildid_bin.empty()) {
391         dso_info.build_id = RawDataToHexString(buildid_bin);
392         new_buildids[dso_info.name] = dso_info.build_id;
393       }
394     }
395   }
396 
397   if (new_buildids.empty()) return true;
398   return reader_->InjectBuildIDs(new_buildids);
399 }
400 
UpdatePerfEventsFromParsedEvents()401 void PerfParser::UpdatePerfEventsFromParsedEvents() {
402   // Reorder the events in |reader_| to match the order of |parsed_events_|.
403   // The |event_ptr|'s in |parsed_events_| are pointers to existing events in
404   // |reader_|.
405   RepeatedPtrField<PerfEvent> new_events;
406   new_events.Reserve(parsed_events_.size());
407   for (ParsedEvent& parsed_event : parsed_events_) {
408     PerfEvent* new_event = new_events.Add();
409     new_event->Swap(parsed_event.event_ptr);
410     parsed_event.event_ptr = new_event;
411   }
412 
413   reader_->mutable_events()->Swap(&new_events);
414 }
415 
MapSampleEvent(ParsedEvent * parsed_event)416 bool PerfParser::MapSampleEvent(ParsedEvent* parsed_event) {
417   bool mapping_failed = false;
418 
419   const PerfEvent& event = *parsed_event->event_ptr;
420   if (!event.has_sample_event() ||
421       !(event.sample_event().has_ip() && event.sample_event().has_pid() &&
422         event.sample_event().has_tid())) {
423     return false;
424   }
425   SampleEvent& sample_info = *parsed_event->event_ptr->mutable_sample_event();
426 
427   // Find the associated command.
428   PidTid pidtid = std::make_pair(sample_info.pid(), sample_info.tid());
429   const auto comm_iter = pidtid_to_comm_map_.find(pidtid);
430   if (comm_iter != pidtid_to_comm_map_.end())
431     parsed_event->set_command(comm_iter->second);
432 
433   const uint64_t unmapped_event_ip = sample_info.ip();
434   uint64_t remapped_event_ip = 0;
435 
436   // Map the event IP itself.
437   if (!MapIPAndPidAndGetNameAndOffset(sample_info.ip(), pidtid,
438                                       &remapped_event_ip,
439                                       &parsed_event->dso_and_offset)) {
440     mapping_failed = true;
441   } else {
442     sample_info.set_ip(remapped_event_ip);
443   }
444 
445   if (sample_info.callchain_size() &&
446       !MapCallchain(sample_info.ip(), pidtid, unmapped_event_ip,
447                     sample_info.mutable_callchain(), parsed_event)) {
448     mapping_failed = true;
449   }
450 
451   if (sample_info.branch_stack_size() &&
452       !MapBranchStack(pidtid, sample_info.mutable_branch_stack(),
453                       parsed_event)) {
454     mapping_failed = true;
455   }
456 
457   return !mapping_failed;
458 }
459 
MapCallchain(const uint64_t ip,const PidTid pidtid,const uint64_t original_event_addr,RepeatedField<uint64> * callchain,ParsedEvent * parsed_event)460 bool PerfParser::MapCallchain(const uint64_t ip, const PidTid pidtid,
461                               const uint64_t original_event_addr,
462                               RepeatedField<uint64>* callchain,
463                               ParsedEvent* parsed_event) {
464   if (!callchain) {
465     LOG(ERROR) << "NULL call stack data.";
466     return false;
467   }
468 
469   bool mapping_failed = false;
470 
471   // If the callchain is empty, there is no work to do.
472   if (callchain->empty()) return true;
473 
474   // Keeps track of whether the current entry is kernel or user.
475   parsed_event->callchain.resize(callchain->size());
476   int num_entries_mapped = 0;
477   for (int i = 0; i < callchain->size(); ++i) {
478     uint64_t entry = callchain->Get(i);
479     // When a callchain context entry is found, do not attempt to symbolize it.
480     if (entry >= PERF_CONTEXT_MAX) {
481       continue;
482     }
483     // The sample address has already been mapped so no need to map it.
484     if (entry == original_event_addr) {
485       callchain->Set(i, ip);
486       continue;
487     }
488     uint64_t mapped_addr = 0;
489     if (!MapIPAndPidAndGetNameAndOffset(
490             entry, pidtid, &mapped_addr,
491             &parsed_event->callchain[num_entries_mapped++])) {
492       mapping_failed = true;
493     } else {
494       callchain->Set(i, mapped_addr);
495     }
496   }
497   // Not all the entries were mapped.  Trim |parsed_event->callchain| to
498   // remove unused entries at the end.
499   parsed_event->callchain.resize(num_entries_mapped);
500 
501   return !mapping_failed;
502 }
503 
MapBranchStack(const PidTid pidtid,RepeatedPtrField<BranchStackEntry> * branch_stack,ParsedEvent * parsed_event)504 bool PerfParser::MapBranchStack(
505     const PidTid pidtid, RepeatedPtrField<BranchStackEntry>* branch_stack,
506     ParsedEvent* parsed_event) {
507   if (!branch_stack) {
508     LOG(ERROR) << "NULL branch stack data.";
509     return false;
510   }
511 
512   // First, trim the branch stack to remove trailing null entries.
513   size_t trimmed_size = 0;
514   for (const BranchStackEntry& entry : *branch_stack) {
515     // Count the number of non-null entries before the first null entry.
516     if (IsNullBranchStackEntry(entry)) break;
517     ++trimmed_size;
518   }
519 
520   // If a null entry was found, make sure all subsequent null entries are NULL
521   // as well.
522   for (int i = trimmed_size; i < branch_stack->size(); ++i) {
523     const BranchStackEntry& entry = branch_stack->Get(i);
524     if (!IsNullBranchStackEntry(entry)) {
525       LOG(ERROR) << "Non-null branch stack entry found after null entry: "
526                  << reinterpret_cast<void*>(entry.from_ip()) << " -> "
527                  << reinterpret_cast<void*>(entry.to_ip());
528       return false;
529     }
530   }
531 
532   // Map branch stack addresses.
533   parsed_event->branch_stack.resize(trimmed_size);
534   for (unsigned int i = 0; i < trimmed_size; ++i) {
535     BranchStackEntry* entry = branch_stack->Mutable(i);
536     ParsedEvent::BranchEntry& parsed_entry = parsed_event->branch_stack[i];
537 
538     uint64_t from_mapped = 0;
539     if (!MapIPAndPidAndGetNameAndOffset(entry->from_ip(), pidtid, &from_mapped,
540                                         &parsed_entry.from)) {
541       return false;
542     }
543     entry->set_from_ip(from_mapped);
544 
545     uint64_t to_mapped = 0;
546     if (!MapIPAndPidAndGetNameAndOffset(entry->to_ip(), pidtid, &to_mapped,
547                                         &parsed_entry.to)) {
548       return false;
549     }
550     entry->set_to_ip(to_mapped);
551 
552     parsed_entry.predicted = !entry->mispredicted();
553   }
554 
555   return true;
556 }
557 
MapIPAndPidAndGetNameAndOffset(uint64_t ip,PidTid pidtid,uint64_t * new_ip,ParsedEvent::DSOAndOffset * dso_and_offset)558 bool PerfParser::MapIPAndPidAndGetNameAndOffset(
559     uint64_t ip, PidTid pidtid, uint64_t* new_ip,
560     ParsedEvent::DSOAndOffset* dso_and_offset) {
561   DCHECK(dso_and_offset);
562   // Attempt to find the synthetic address of the IP sample in this order:
563   // 1. Address space of the kernel.
564   // 2. Address space of its own process.
565   // 3. Address space of the parent process.
566 
567   uint64_t mapped_addr = 0;
568 
569   // Sometimes the first event we see is a SAMPLE event and we don't have the
570   // time to create an address mapper for a process. Example, for pid 0.
571   AddressMapper* mapper = GetOrCreateProcessMapper(pidtid.first).first;
572   AddressMapper::MappingList::const_iterator ip_iter;
573   bool mapped =
574       mapper->GetMappedAddressAndListIterator(ip, &mapped_addr, &ip_iter);
575   if (mapped) {
576     uint64_t id = UINT64_MAX;
577     mapper->GetMappedIDAndOffset(ip, ip_iter, &id, &dso_and_offset->offset_);
578     // Make sure the ID points to a valid event.
579     CHECK_LE(id, parsed_events_.size());
580     ParsedEvent& parsed_event = parsed_events_[id];
581     const auto& event = parsed_event.event_ptr;
582     DCHECK(event->has_mmap_event()) << "Expected MMAP or MMAP2 event";
583 
584     // Find the mmap DSO filename in the set of known DSO names.
585     auto dso_iter = name_to_dso_.find(event->mmap_event().filename());
586     CHECK(dso_iter != name_to_dso_.end());
587     dso_and_offset->dso_info_ = &dso_iter->second;
588 
589     dso_iter->second.hit = true;
590     dso_iter->second.threads.insert(mergeTwoU32(pidtid.first, pidtid.second));
591     ++parsed_event.num_samples_in_mmap_region;
592 
593     if (options_.do_remap) {
594       if (GetPageAlignedOffset(mapped_addr) != GetPageAlignedOffset(ip)) {
595         LOG(ERROR) << "Remapped address " << std::hex << mapped_addr << " "
596                    << "does not have the same page alignment offset as "
597                    << "original address " << ip;
598         return false;
599       }
600       *new_ip = mapped_addr;
601     } else {
602       *new_ip = ip;
603     }
604   }
605   return mapped;
606 }
607 
MapMmapEvent(PerfDataProto_MMapEvent * event,uint64_t id)608 bool PerfParser::MapMmapEvent(PerfDataProto_MMapEvent* event, uint64_t id) {
609   // We need to hide only the real kernel addresses.  However, to make things
610   // more secure, and make the mapping idempotent, we should remap all
611   // addresses, both kernel and non-kernel.
612 
613   AddressMapper* mapper = GetOrCreateProcessMapper(event->pid()).first;
614 
615   uint64_t start = event->start();
616   uint64_t len = event->len();
617   uint64_t pgoff = event->pgoff();
618 
619   // |id| == 0 corresponds to the kernel mmap. We have several cases here:
620   //
621   // For ARM and x86, in sudo mode, pgoff == start, example:
622   // start=0x80008200
623   // pgoff=0x80008200
624   // len  =0xfffffff7ff7dff
625   //
626   // For x86-64, in sudo mode, pgoff is between start and start + len. SAMPLE
627   // events lie between pgoff and pgoff + length of the real kernel binary,
628   // example:
629   // start=0x3bc00000
630   // pgoff=0xffffffffbcc00198
631   // len  =0xffffffff843fffff
632   // SAMPLE events will be found after pgoff. For kernels with ASLR, pgoff will
633   // be something only visible to the root user, and will be randomized at
634   // startup. With |remap| set to true, we should hide pgoff in this case. So we
635   // normalize all SAMPLE events relative to pgoff.
636   //
637   // For non-sudo mode, the kernel will be mapped from 0 to the pointer limit,
638   // example:
639   // start=0x0
640   // pgoff=0x0
641   // len  =0xffffffff
642   if (id == 0) {
643     // If pgoff is between start and len, we normalize the event by setting
644     // start to be pgoff just like how it is for ARM and x86. We also set len to
645     // be a much smaller number (closer to the real length of the kernel binary)
646     // because SAMPLEs are actually only seen between |event->pgoff| and
647     // |event->pgoff + kernel text size|.
648     if (pgoff > start && pgoff < start + len) {
649       len = len + start - pgoff;
650       start = pgoff;
651     }
652     // For kernels with ALSR pgoff is critical information that should not be
653     // revealed when |remap| is true.
654     pgoff = 0;
655   }
656 
657   if (!mapper->MapWithID(start, len, id, pgoff, true)) {
658     mapper->DumpToLog();
659     return false;
660   }
661 
662   if (options_.do_remap) {
663     uint64_t mapped_addr;
664     AddressMapper::MappingList::const_iterator start_iter;
665     if (!mapper->GetMappedAddressAndListIterator(start, &mapped_addr,
666                                                  &start_iter)) {
667       LOG(ERROR) << "Failed to map starting address " << std::hex << start;
668       return false;
669     }
670     if (GetPageAlignedOffset(mapped_addr) != GetPageAlignedOffset(start)) {
671       LOG(ERROR) << "Remapped address " << std::hex << mapped_addr << " "
672                  << "does not have the same page alignment offset as start "
673                  << "address " << start;
674       return false;
675     }
676 
677     event->set_start(mapped_addr);
678     event->set_len(len);
679     event->set_pgoff(pgoff);
680   }
681   return true;
682 }
683 
MapCommEvent(const PerfDataProto_CommEvent & event)684 bool PerfParser::MapCommEvent(const PerfDataProto_CommEvent& event) {
685   GetOrCreateProcessMapper(event.pid());
686   return true;
687 }
688 
MapForkEvent(const PerfDataProto_ForkEvent & event)689 bool PerfParser::MapForkEvent(const PerfDataProto_ForkEvent& event) {
690   PidTid parent = std::make_pair(event.ppid(), event.ptid());
691   PidTid child = std::make_pair(event.pid(), event.tid());
692   if (parent != child) {
693     auto parent_iter = pidtid_to_comm_map_.find(parent);
694     if (parent_iter != pidtid_to_comm_map_.end())
695       pidtid_to_comm_map_[child] = parent_iter->second;
696   }
697 
698   const uint32_t pid = event.pid();
699 
700   // If the parent and child pids are the same, this is just a new thread
701   // within the same process, so don't do anything.
702   if (event.ppid() == pid) return true;
703 
704   if (!GetOrCreateProcessMapper(pid, event.ppid()).second) {
705     DVLOG(1) << "Found an existing process mapper with pid: " << pid;
706   }
707 
708   return true;
709 }
710 
GetOrCreateProcessMapper(uint32_t pid,uint32_t ppid)711 std::pair<AddressMapper*, bool> PerfParser::GetOrCreateProcessMapper(
712     uint32_t pid, uint32_t ppid) {
713   const auto& search = process_mappers_.find(pid);
714   if (search != process_mappers_.end()) {
715     return std::make_pair(search->second.get(), false);
716   }
717 
718   auto parent_mapper = process_mappers_.find(ppid);
719   // Recent perf implementations (at least as recent as perf 4.4), add an
720   // explicit FORK event from the swapper process to the init process. There may
721   // be no explicit memory mappings created for the swapper process. In such
722   // cases, we must use the mappings from the kernel process, which are used by
723   // default for a new PID in the absence of an explicit FORK event.
724   if (parent_mapper == process_mappers_.end()) {
725     parent_mapper = process_mappers_.find(kKernelPid);
726   }
727   std::unique_ptr<AddressMapper> mapper;
728   if (parent_mapper != process_mappers_.end()) {
729     mapper.reset(new AddressMapper(*parent_mapper->second));
730   } else {
731     mapper.reset(new AddressMapper());
732     mapper->set_page_alignment(kMmapPageAlignment);
733   }
734 
735   const auto inserted =
736       process_mappers_.insert(search, std::make_pair(pid, std::move(mapper)));
737   return std::make_pair(inserted->second.get(), true);
738 }
739 
740 }  // namespace quipper
741