#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"
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) {
}
}
+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));
}
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);
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);
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);
void SampleRecord::AdjustSizeBasedOnData() {
size_t size = BinaryFormat().size();
- LOG(DEBUG) << "SampleRecord size is changed from " << header.size << " to " << size;
+ LOG(DEBUG) << "Record (type " << RecordTypeToString(header.type) << ") size is changed from "
+ << header.size << " to " << size;
header.size = size;
}
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++]);
}
}
}
}
+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);
}
}
-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;
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) {
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;
+}