X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=simpleperf%2Frecord.cpp;h=fca2403d370f1bc1172fbd365ab3556a698622d6;hb=05d982d4edde37937c9a6fdb97e36a492e18f7d5;hp=5398d468c0afa501861a9dbebcf9d6e8b11a4ea3;hpb=0218fc2439f587fd7448087f62202de66b1f702d;p=android-x86%2Fsystem-extras.git diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp index 5398d468..fca2403d 100644 --- a/simpleperf/record.cpp +++ b/simpleperf/record.cpp @@ -20,8 +20,8 @@ #include #include -#include -#include +#include +#include #include "environment.h" #include "perf_regs.h" @@ -57,6 +57,13 @@ void MoveToBinaryFormat(const T& data, char*& p) { p += sizeof(T); } +template +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(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 MmapRecord::BinaryFormat() const { std::vector buf(header.size); char* p = buf.data(); @@ -197,6 +206,16 @@ std::vector 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(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 Mmap2Record::BinaryFormat() const { + std::vector 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 CommRecord::BinaryFormat() const { std::vector buf(header.size); char* p = buf.data(); @@ -244,6 +274,10 @@ std::vector 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(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 ForkRecord::BinaryFormat() const { +std::vector ExitOrForkRecord::BinaryFormat() const { std::vector buf(header.size); char* p = buf.data(); MoveToBinaryFormat(header, p); @@ -267,6 +296,11 @@ std::vector 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(pheader + 1); @@ -349,6 +383,77 @@ SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header* } } +std::vector SampleRecord::BinaryFormat() const { + std::vector 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(pheader + 1); const char* end = reinterpret_cast(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 BuildIdRecord::BinaryFormat() const { std::vector buf(header.size); char* p = buf.data(); @@ -450,6 +554,29 @@ std::vector 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(pheader + 1); + const char* end = reinterpret_cast(pheader) + pheader->size; + data.insert(data.end(), p, end); +} + +std::vector UnknownRecord::BinaryFormat() const { + std::vector 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 ReadRecordFromBuffer(const perf_event_attr& attr, const perf_event_header* pheader) { switch (pheader->type) { @@ -466,31 +593,10 @@ static std::unique_ptr ReadRecordFromBuffer(const perf_event_attr& attr, case PERF_RECORD_SAMPLE: return std::unique_ptr(new SampleRecord(attr, pheader)); default: - return std::unique_ptr(new Record(pheader)); + return std::unique_ptr(new UnknownRecord(pheader)); } } -static bool IsRecordHappensBefore(const std::unique_ptr& r1, - const std::unique_ptr& 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(r1.get())->time_data.time - : r1->sample_id.time_data.time); - uint64_t time2 = (is_r2_sample ? static_cast(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> ReadRecordsFromBuffer(const perf_event_attr& attr, const char* buf, size_t buf_size) { std::vector> result; @@ -498,17 +604,30 @@ std::vector> ReadRecordsFromBuffer(const perf_event_attr const char* end = buf + buf_size; while (p < end) { const perf_event_header* header = reinterpret_cast(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 ReadRecordFromFile(const perf_event_attr& attr, FILE* fp) { + std::vector buf(sizeof(perf_event_header)); + perf_event_header* header = reinterpret_cast(&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(&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> 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) { + queue_.push(CreateRecordWithSeq(record.release())); +} + +std::unique_ptr 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(r); +} + +std::vector> RecordCache::PopAll() { + std::vector> 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; +}