OSDN Git Service

ANRdaemon: move trace result from /sdcard to /data am: d93aa41807
[android-x86/system-extras.git] / simpleperf / record.cpp
index 5398d46..fca2403 100644 (file)
@@ -20,8 +20,8 @@
 #include <algorithm>
 #include <unordered_map>
 
-#include <base/logging.h>
-#include <base/stringprintf.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 
 #include "environment.h"
 #include "perf_regs.h"
@@ -57,6 +57,13 @@ void MoveToBinaryFormat(const T& data, char*& p) {
   p += sizeof(T);
 }
 
+template <class T>
+void MoveToBinaryFormat(const T* data_p, size_t n, char*& p) {
+  size_t size = n * sizeof(T);
+  memcpy(p, data_p, size);
+  p += size;
+}
+
 SampleId::SampleId() {
   memset(this, 0, sizeof(SampleId));
 }
@@ -66,25 +73,7 @@ size_t SampleId::CreateContent(const perf_event_attr& attr) {
   sample_id_all = attr.sample_id_all;
   sample_type = attr.sample_type;
   // Other data are not necessary. TODO: Set missing SampleId data.
-  size_t size = 0;
-  if (sample_id_all) {
-    if (sample_type & PERF_SAMPLE_TID) {
-      size += sizeof(PerfSampleTidType);
-    }
-    if (sample_type & PERF_SAMPLE_TIME) {
-      size += sizeof(PerfSampleTimeType);
-    }
-    if (sample_type & PERF_SAMPLE_ID) {
-      size += sizeof(PerfSampleIdType);
-    }
-    if (sample_type & PERF_SAMPLE_STREAM_ID) {
-      size += sizeof(PerfSampleStreamIdType);
-    }
-    if (sample_type & PERF_SAMPLE_CPU) {
-      size += sizeof(PerfSampleCpuType);
-    }
-  }
-  return size;
+  return Size();
 }
 
 void SampleId::ReadFromBinaryFormat(const perf_event_attr& attr, const char* p, const char* end) {
@@ -154,6 +143,28 @@ void SampleId::Dump(size_t indent) const {
   }
 }
 
+size_t SampleId::Size() const {
+  size_t size = 0;
+  if (sample_id_all) {
+    if (sample_type & PERF_SAMPLE_TID) {
+      size += sizeof(PerfSampleTidType);
+    }
+    if (sample_type & PERF_SAMPLE_TIME) {
+      size += sizeof(PerfSampleTimeType);
+    }
+    if (sample_type & PERF_SAMPLE_ID) {
+      size += sizeof(PerfSampleIdType);
+    }
+    if (sample_type & PERF_SAMPLE_STREAM_ID) {
+      size += sizeof(PerfSampleStreamIdType);
+    }
+    if (sample_type & PERF_SAMPLE_CPU) {
+      size += sizeof(PerfSampleCpuType);
+    }
+  }
+  return size;
+}
+
 Record::Record() {
   memset(&header, 0, sizeof(header));
 }
@@ -169,6 +180,10 @@ void Record::Dump(size_t indent) const {
   sample_id.Dump(indent + 1);
 }
 
+uint64_t Record::Timestamp() const {
+  return sample_id.time_data.time;
+}
+
 MmapRecord::MmapRecord(const perf_event_attr& attr, const perf_event_header* pheader)
     : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
@@ -180,12 +195,6 @@ MmapRecord::MmapRecord(const perf_event_attr& attr, const perf_event_header* phe
   sample_id.ReadFromBinaryFormat(attr, p, end);
 }
 
-void MmapRecord::DumpData(size_t indent) const {
-  PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid,
-                data.tid, data.addr, data.len);
-  PrintIndented(indent, "pgoff 0x%" PRIx64 ", filename %s\n", data.pgoff, filename.c_str());
-}
-
 std::vector<char> MmapRecord::BinaryFormat() const {
   std::vector<char> buf(header.size);
   char* p = buf.data();
@@ -197,6 +206,16 @@ std::vector<char> MmapRecord::BinaryFormat() const {
   return buf;
 }
 
+void MmapRecord::AdjustSizeBasedOnData() {
+  header.size = sizeof(header) + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size();
+}
+
+void MmapRecord::DumpData(size_t indent) const {
+  PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid,
+                data.tid, data.addr, data.len);
+  PrintIndented(indent, "pgoff 0x%" PRIx64 ", filename %s\n", data.pgoff, filename.c_str());
+}
+
 Mmap2Record::Mmap2Record(const perf_event_attr& attr, const perf_event_header* pheader)
     : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
@@ -208,6 +227,21 @@ Mmap2Record::Mmap2Record(const perf_event_attr& attr, const perf_event_header* p
   sample_id.ReadFromBinaryFormat(attr, p, end);
 }
 
+std::vector<char> Mmap2Record::BinaryFormat() const {
+  std::vector<char> buf(header.size);
+  char* p = buf.data();
+  MoveToBinaryFormat(header, p);
+  MoveToBinaryFormat(data, p);
+  strcpy(p, filename.c_str());
+  p += ALIGN(filename.size() + 1, 8);
+  sample_id.WriteToBinaryFormat(p);
+  return buf;
+}
+
+void Mmap2Record::AdjustSizeBasedOnData() {
+  header.size = sizeof(header) + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size();
+}
+
 void Mmap2Record::DumpData(size_t indent) const {
   PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid,
                 data.tid, data.addr, data.len);
@@ -229,10 +263,6 @@ CommRecord::CommRecord(const perf_event_attr& attr, const perf_event_header* phe
   sample_id.ReadFromBinaryFormat(attr, p, end);
 }
 
-void CommRecord::DumpData(size_t indent) const {
-  PrintIndented(indent, "pid %u, tid %u, comm %s\n", data.pid, data.tid, comm.c_str());
-}
-
 std::vector<char> CommRecord::BinaryFormat() const {
   std::vector<char> buf(header.size);
   char* p = buf.data();
@@ -244,6 +274,10 @@ std::vector<char> CommRecord::BinaryFormat() const {
   return buf;
 }
 
+void CommRecord::DumpData(size_t indent) const {
+  PrintIndented(indent, "pid %u, tid %u, comm %s\n", data.pid, data.tid, comm.c_str());
+}
+
 ExitOrForkRecord::ExitOrForkRecord(const perf_event_attr& attr, const perf_event_header* pheader)
     : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
@@ -253,12 +287,7 @@ ExitOrForkRecord::ExitOrForkRecord(const perf_event_attr& attr, const perf_event
   sample_id.ReadFromBinaryFormat(attr, p, end);
 }
 
-void ExitOrForkRecord::DumpData(size_t indent) const {
-  PrintIndented(indent, "pid %u, ppid %u, tid %u, ptid %u\n", data.pid, data.ppid, data.tid,
-                data.ptid);
-}
-
-std::vector<char> ForkRecord::BinaryFormat() const {
+std::vector<char> ExitOrForkRecord::BinaryFormat() const {
   std::vector<char> buf(header.size);
   char* p = buf.data();
   MoveToBinaryFormat(header, p);
@@ -267,6 +296,11 @@ std::vector<char> ForkRecord::BinaryFormat() const {
   return buf;
 }
 
+void ExitOrForkRecord::DumpData(size_t indent) const {
+  PrintIndented(indent, "pid %u, ppid %u, tid %u, ptid %u\n", data.pid, data.ppid, data.tid,
+                data.ptid);
+}
+
 SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader)
     : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
@@ -349,6 +383,77 @@ SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header*
   }
 }
 
+std::vector<char> SampleRecord::BinaryFormat() const {
+  std::vector<char> buf(header.size);
+  char* p = buf.data();
+  MoveToBinaryFormat(header, p);
+  if (sample_type & PERF_SAMPLE_IP) {
+    MoveToBinaryFormat(ip_data, p);
+  }
+  if (sample_type & PERF_SAMPLE_TID) {
+    MoveToBinaryFormat(tid_data, p);
+  }
+  if (sample_type & PERF_SAMPLE_TIME) {
+    MoveToBinaryFormat(time_data, p);
+  }
+  if (sample_type & PERF_SAMPLE_ADDR) {
+    MoveToBinaryFormat(addr_data, p);
+  }
+  if (sample_type & PERF_SAMPLE_ID) {
+    MoveToBinaryFormat(id_data, p);
+  }
+  if (sample_type & PERF_SAMPLE_STREAM_ID) {
+    MoveToBinaryFormat(stream_id_data, p);
+  }
+  if (sample_type & PERF_SAMPLE_CPU) {
+    MoveToBinaryFormat(cpu_data, p);
+  }
+  if (sample_type & PERF_SAMPLE_PERIOD) {
+    MoveToBinaryFormat(period_data, p);
+  }
+  if (sample_type & PERF_SAMPLE_CALLCHAIN) {
+    uint64_t nr = callchain_data.ips.size();
+    MoveToBinaryFormat(nr, p);
+    MoveToBinaryFormat(callchain_data.ips.data(), nr, p);
+  }
+  if (sample_type & PERF_SAMPLE_RAW) {
+    uint32_t size = raw_data.data.size();
+    MoveToBinaryFormat(size, p);
+    MoveToBinaryFormat(raw_data.data.data(), size, p);
+  }
+  if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
+    uint64_t nr = branch_stack_data.stack.size();
+    MoveToBinaryFormat(nr, p);
+    MoveToBinaryFormat(branch_stack_data.stack.data(), nr, p);
+  }
+  if (sample_type & PERF_SAMPLE_REGS_USER) {
+    MoveToBinaryFormat(regs_user_data.abi, p);
+    if (regs_user_data.abi != 0) {
+      MoveToBinaryFormat(regs_user_data.regs.data(), regs_user_data.regs.size(), p);
+    }
+  }
+  if (sample_type & PERF_SAMPLE_STACK_USER) {
+    uint64_t size = stack_user_data.data.size();
+    MoveToBinaryFormat(size, p);
+    if (size != 0) {
+      MoveToBinaryFormat(stack_user_data.data.data(), size, p);
+      MoveToBinaryFormat(stack_user_data.dyn_size, p);
+    }
+  }
+
+  // If record command does stack unwinding, sample records' size may be decreased.
+  // So we can't trust header.size here, and should adjust buffer size based on real need.
+  buf.resize(p - buf.data());
+  return buf;
+}
+
+void SampleRecord::AdjustSizeBasedOnData() {
+  size_t size = BinaryFormat().size();
+  LOG(DEBUG) << "Record (type " << RecordTypeToString(header.type) << ") size is changed from "
+      << header.size << " to " << size;
+  header.size = size;
+}
+
 void SampleRecord::DumpData(size_t indent) const {
   PrintIndented(indent, "sample_type: 0x%" PRIx64 "\n", sample_type);
   if (sample_type & PERF_SAMPLE_IP) {
@@ -400,7 +505,8 @@ void SampleRecord::DumpData(size_t indent) const {
     PrintIndented(indent, "user regs: abi=%" PRId64 "\n", regs_user_data.abi);
     for (size_t i = 0, pos = 0; i < 64; ++i) {
       if ((regs_user_data.reg_mask >> i) & 1) {
-        PrintIndented(indent + 1, "reg (%s) 0x%016" PRIx64 "\n", GetRegName(i).c_str(),
+        PrintIndented(indent + 1, "reg (%s) 0x%016" PRIx64 "\n",
+                      GetRegName(i, ScopedCurrentArch::GetCurrentArch()).c_str(),
                       regs_user_data.regs[pos++]);
       }
     }
@@ -421,23 +527,21 @@ void SampleRecord::DumpData(size_t indent) const {
   }
 }
 
+uint64_t SampleRecord::Timestamp() const {
+  return time_data.time;
+}
+
 BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
   MoveFromBinaryFormat(pid, p);
-  build_id = BuildId(p);
+  build_id = BuildId(p, BUILD_ID_SIZE);
   p += ALIGN(build_id.Size(), 8);
   filename = p;
   p += ALIGN(filename.size() + 1, 64);
   CHECK_EQ(p, end);
 }
 
-void BuildIdRecord::DumpData(size_t indent) const {
-  PrintIndented(indent, "pid %u\n", pid);
-  PrintIndented(indent, "build_id %s\n", build_id.ToString().c_str());
-  PrintIndented(indent, "filename %s\n", filename.c_str());
-}
-
 std::vector<char> BuildIdRecord::BinaryFormat() const {
   std::vector<char> buf(header.size);
   char* p = buf.data();
@@ -450,6 +554,29 @@ std::vector<char> BuildIdRecord::BinaryFormat() const {
   return buf;
 }
 
+void BuildIdRecord::DumpData(size_t indent) const {
+  PrintIndented(indent, "pid %u\n", pid);
+  PrintIndented(indent, "build_id %s\n", build_id.ToString().c_str());
+  PrintIndented(indent, "filename %s\n", filename.c_str());
+}
+
+UnknownRecord::UnknownRecord(const perf_event_header* pheader) : Record(pheader) {
+  const char* p = reinterpret_cast<const char*>(pheader + 1);
+  const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+  data.insert(data.end(), p, end);
+}
+
+std::vector<char> UnknownRecord::BinaryFormat() const {
+  std::vector<char> buf(header.size);
+  char* p = buf.data();
+  MoveToBinaryFormat(header, p);
+  MoveToBinaryFormat(data.data(), data.size(), p);
+  return buf;
+}
+
+void UnknownRecord::DumpData(size_t) const {
+}
+
 static std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
                                                     const perf_event_header* pheader) {
   switch (pheader->type) {
@@ -466,31 +593,10 @@ static std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
     case PERF_RECORD_SAMPLE:
       return std::unique_ptr<Record>(new SampleRecord(attr, pheader));
     default:
-      return std::unique_ptr<Record>(new Record(pheader));
+      return std::unique_ptr<Record>(new UnknownRecord(pheader));
   }
 }
 
-static bool IsRecordHappensBefore(const std::unique_ptr<Record>& r1,
-                                  const std::unique_ptr<Record>& r2) {
-  bool is_r1_sample = (r1->header.type == PERF_RECORD_SAMPLE);
-  bool is_r2_sample = (r2->header.type == PERF_RECORD_SAMPLE);
-  uint64_t time1 = (is_r1_sample ? static_cast<const SampleRecord*>(r1.get())->time_data.time
-                                 : r1->sample_id.time_data.time);
-  uint64_t time2 = (is_r2_sample ? static_cast<const SampleRecord*>(r2.get())->time_data.time
-                                 : r2->sample_id.time_data.time);
-  // The record with smaller time happens first.
-  if (time1 != time2) {
-    return time1 < time2;
-  }
-  // If happening at the same time, make non-sample records before sample records,
-  // because non-sample records may contain useful information to parse sample records.
-  if (is_r1_sample != is_r2_sample) {
-    return is_r1_sample ? false : true;
-  }
-  // Otherwise, don't care of the order.
-  return false;
-}
-
 std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer(const perf_event_attr& attr,
                                                            const char* buf, size_t buf_size) {
   std::vector<std::unique_ptr<Record>> result;
@@ -498,17 +604,30 @@ std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer(const perf_event_attr
   const char* end = buf + buf_size;
   while (p < end) {
     const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
-    if (p + header->size <= end) {
-      result.push_back(ReadRecordFromBuffer(attr, header));
-    }
+    CHECK_LE(p + header->size, end);
+    CHECK_NE(0u, header->size);
+    result.push_back(ReadRecordFromBuffer(attr, header));
     p += header->size;
   }
-  if ((attr.sample_type & PERF_SAMPLE_TIME) && attr.sample_id_all) {
-    std::sort(result.begin(), result.end(), IsRecordHappensBefore);
-  }
   return result;
 }
 
+std::unique_ptr<Record> ReadRecordFromFile(const perf_event_attr& attr, FILE* fp) {
+  std::vector<char> buf(sizeof(perf_event_header));
+  perf_event_header* header = reinterpret_cast<perf_event_header*>(&buf[0]);
+  if (fread(header, sizeof(perf_event_header), 1, fp) != 1) {
+    PLOG(ERROR) << "Failed to read record file";
+    return nullptr;
+  }
+  buf.resize(header->size);
+  header = reinterpret_cast<perf_event_header*>(&buf[0]);
+  if (fread(&buf[sizeof(perf_event_header)], buf.size() - sizeof(perf_event_header), 1, fp) != 1) {
+    PLOG(ERROR) << "Failed to read record file";
+    return nullptr;
+  }
+  return ReadRecordFromBuffer(attr, header);
+}
+
 MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid,
                             uint64_t addr, uint64_t len, uint64_t pgoff,
                             const std::string& filename) {
@@ -568,3 +687,87 @@ BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& buil
                        ALIGN(record.build_id.Size(), 8) + ALIGN(filename.size() + 1, 64);
   return record;
 }
+
+bool RecordCache::RecordWithSeq::IsHappensBefore(const RecordWithSeq& other) const {
+  bool is_sample = (record->header.type == PERF_RECORD_SAMPLE);
+  bool is_other_sample = (other.record->header.type == PERF_RECORD_SAMPLE);
+  uint64_t time = record->Timestamp();
+  uint64_t other_time = other.record->Timestamp();
+  // The record with smaller time happens first.
+  if (time != other_time) {
+    return time < other_time;
+  }
+  // If happening at the same time, make non-sample records before sample records,
+  // because non-sample records may contain useful information to parse sample records.
+  if (is_sample != is_other_sample) {
+    return is_sample ? false : true;
+  }
+  // Otherwise, use the same order as they enter the cache.
+  return seq < other.seq;
+}
+
+bool RecordCache::RecordComparator::operator()(const RecordWithSeq& r1,
+                                               const RecordWithSeq& r2) {
+  return r2.IsHappensBefore(r1);
+}
+
+RecordCache::RecordCache(const perf_event_attr& attr, size_t min_cache_size,
+                         uint64_t min_time_diff_in_ns)
+    : attr_(attr),
+      has_timestamp_(attr.sample_id_all && (attr.sample_type & PERF_SAMPLE_TIME)),
+      min_cache_size_(min_cache_size),
+      min_time_diff_in_ns_(min_time_diff_in_ns),
+      last_time_(0),
+      cur_seq_(0),
+      queue_(RecordComparator()) {
+}
+
+RecordCache::~RecordCache() {
+  PopAll();
+}
+
+void RecordCache::Push(const char* data, size_t size) {
+  std::vector<std::unique_ptr<Record>> records = ReadRecordsFromBuffer(attr_, data, size);
+  if (has_timestamp_) {
+    for (const auto& r : records) {
+      last_time_ = std::max(last_time_, r->Timestamp());
+    }
+  }
+  for (auto& r : records) {
+    queue_.push(CreateRecordWithSeq(r.release()));
+  }
+}
+
+void RecordCache::Push(std::unique_ptr<Record> record) {
+  queue_.push(CreateRecordWithSeq(record.release()));
+}
+
+std::unique_ptr<Record> RecordCache::Pop() {
+  if (queue_.size() < min_cache_size_) {
+    return nullptr;
+  }
+  Record* r = queue_.top().record;
+  if (has_timestamp_) {
+    if (r->Timestamp() + min_time_diff_in_ns_ > last_time_) {
+      return nullptr;
+    }
+  }
+  queue_.pop();
+  return std::unique_ptr<Record>(r);
+}
+
+std::vector<std::unique_ptr<Record>> RecordCache::PopAll() {
+  std::vector<std::unique_ptr<Record>> result;
+  while (!queue_.empty()) {
+    result.emplace_back(queue_.top().record);
+    queue_.pop();
+  }
+  return result;
+}
+
+RecordCache::RecordWithSeq RecordCache::CreateRecordWithSeq(Record *r) {
+  RecordWithSeq result;
+  result.seq = cur_seq_++;
+  result.record = r;
+  return result;
+}