/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define HILOG_TAG "PerfRecord" #include "perf_event_record.h" #include "spe_decoder.h" #include #include "utilities.h" using namespace OHOS::HiviewDFX; using namespace std; namespace OHOS { namespace Developtools { namespace HiPerf { void *g_sampleMemCache = nullptr; // for read record from buf thread void *g_sampleMemCacheMain = nullptr; // for main thread:collecttionsymbol constexpr size_t SAMPLE_CACHE_SIZE = 4 * 1024; std::unique_ptr GetPerfEventRecord(const int type, uint8_t *p, const perf_event_attr &attr) { HLOG_ASSERT(p); uint8_t *data = p; // check kernel switch (type) { case PERF_RECORD_SAMPLE: return std::make_unique(data, attr); case PERF_RECORD_MMAP: return std::make_unique(data); case PERF_RECORD_MMAP2: return std::make_unique(data); case PERF_RECORD_LOST: return std::make_unique(data); case PERF_RECORD_COMM: return std::make_unique(data); case PERF_RECORD_EXIT: return std::make_unique(data); case PERF_RECORD_THROTTLE: return std::make_unique(data); case PERF_RECORD_UNTHROTTLE: return std::make_unique(data); case PERF_RECORD_FORK: return std::make_unique(data); case PERF_RECORD_READ: return std::make_unique(data); case PERF_RECORD_AUX: return std::make_unique(data); case PERF_RECORD_AUXTRACE: return std::make_unique(data); case PERF_RECORD_ITRACE_START: return std::make_unique(data); case PERF_RECORD_LOST_SAMPLES: return std::make_unique(data); case PERF_RECORD_SWITCH: return std::make_unique(data); case PERF_RECORD_SWITCH_CPU_WIDE: return std::make_unique(data); default: HLOGE("unknown record type %d\n", type); return nullptr; } } std::unique_ptr GetPerfSampleFromCache(const int type, uint8_t *p, const perf_event_attr &attr) { HLOG_ASSERT(p); uint8_t *data = p; if (type == PERF_RECORD_SAMPLE) { if (g_sampleMemCache != nullptr) { memset_s(g_sampleMemCache, SAMPLE_CACHE_SIZE, 0, SAMPLE_CACHE_SIZE); return std::unique_ptr(new (g_sampleMemCache) PerfRecordSample(data, attr)); } else { g_sampleMemCache = std::malloc(SAMPLE_CACHE_SIZE); memset_s(g_sampleMemCache, SAMPLE_CACHE_SIZE, 0, SAMPLE_CACHE_SIZE); return std::unique_ptr(new (g_sampleMemCache) PerfRecordSample(data, attr)); } } return GetPerfEventRecord(type, p, attr); } std::unique_ptr GetPerfSampleFromCacheMain(const int type, uint8_t *p, const perf_event_attr &attr) { HLOG_ASSERT(p); uint8_t *data = p; if (type == PERF_RECORD_SAMPLE) { if (g_sampleMemCacheMain != nullptr) { memset_s(g_sampleMemCacheMain, SAMPLE_CACHE_SIZE, 0, SAMPLE_CACHE_SIZE); return std::unique_ptr(new (g_sampleMemCacheMain) PerfRecordSample(data, attr)); } else { g_sampleMemCacheMain = std::malloc(SAMPLE_CACHE_SIZE); memset_s(g_sampleMemCacheMain, SAMPLE_CACHE_SIZE, 0, SAMPLE_CACHE_SIZE); return std::unique_ptr(new (g_sampleMemCacheMain) PerfRecordSample(data, attr)); } } return GetPerfEventRecord(type, p, attr); } template inline void PushToBinary(bool condition, uint8_t *&p, const T &v) { if (condition) { *(reinterpret_cast(p)) = v; p += sizeof(T); } } template inline void PushToBinary2(bool condition, uint8_t *&p, const T1 &v1, const T2 &v2) { if (condition) { *(reinterpret_cast(p)) = v1; p += sizeof(T1); *(reinterpret_cast(p)) = v2; p += sizeof(T2); } } template inline void PopFromBinary(bool condition, uint8_t*& p, T& v, u64& size) { HIPERF_ASSERT(sizeof(T) <= size, "PopFromBinary error\n"); if (condition) { v = *(reinterpret_cast(p)); p += sizeof(T); size -= sizeof(T); } } template inline void PopFromBinary2(bool condition, uint8_t*& p, T1& v1, T2& v2, u64& size) { HIPERF_ASSERT(sizeof(T1) + sizeof(T2) <= size, "PopFromBinary2 error\n"); if (condition) { v1 = *(reinterpret_cast(p)); p += sizeof(T1); v2 = *(reinterpret_cast(p)); p += sizeof(T2); size -= (sizeof(T1) + sizeof(T2)); } } inline void SetPointerOffset(uint8_t*& p, u64 offset, u64& size) { HIPERF_ASSERT(offset <= size, "SetPointerOffset error\n"); size -= offset; p += offset; } // PerfEventRecord PerfEventRecord::PerfEventRecord(perf_event_type type, bool inKernel, const std::string &name) : name_(name) { header.type = type; header.misc = inKernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER; header.size = sizeof(header); } PerfEventRecord::PerfEventRecord(perf_event_hiperf_ext_type type, const std::string &name) : name_(name) { header.type = type; header.misc = PERF_RECORD_MISC_USER; header.size = sizeof(header); } PerfEventRecord::PerfEventRecord(uint8_t *p, const std::string &name) : name_(name) { if (p == nullptr) { header.type = PERF_RECORD_MMAP; header.misc = PERF_RECORD_MISC_USER; header.size = 0; return; } header = *(reinterpret_cast(p)); } void PerfEventRecord::GetHeaderBinary(std::vector &buf) const { if (buf.size() < GetHeaderSize()) { buf.resize(GetHeaderSize()); } uint8_t *p = buf.data(); *(reinterpret_cast(p)) = header; } void PerfEventRecord::Dump(int indent, std::string outputFilename, FILE *outputDump) const { if (outputDump != nullptr) { g_outputDump = outputDump; } else if (!outputFilename.empty() && g_outputDump == nullptr) { std::string resolvedPath = CanonicalizeSpecPath(outputFilename.c_str()); g_outputDump = fopen(resolvedPath.c_str(), "w"); if (g_outputDump == nullptr) { printf("unable open file to '%s' because '%d'\n", outputFilename.c_str(), errno); return; } } PRINT_INDENT(indent, "\n"); PRINT_INDENT(indent, "record %s: type %u, misc %u, size %zu\n", GetName().c_str(), GetType(), GetMisc(), GetSize()); DumpData(indent + 1); } void PerfEventRecord::DumpLog(const std::string &prefix) const { HLOGV("%s: record %s: type %u, misc %u, size %zu\n", prefix.c_str(), GetName().c_str(), GetType(), GetMisc(), GetSize()); } std::vector PerfRecordSample::ips_ = {}; std::vector PerfRecordSample::callFrames_ = {}; std::vector PerfRecordSample::serverPidMap_ = {}; PerfRecordAuxtrace::PerfRecordAuxtrace(uint8_t *p) : PerfEventRecord(p, "auxtrace") { if (header.size >= sizeof(header)) { size_t copySize = header.size - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordAuxtrace retren failed !!!"); } rawData_ = p + header.size; } PerfRecordAuxtrace::PerfRecordAuxtrace(u64 size, u64 offset, u64 reference, u32 idx, u32 tid, u32 cpu, u32 pid) : PerfEventRecord(PERF_RECORD_AUXTRACE, "auxtrace") { data_.size = size; data_.offset = offset; data_.reference = reference; data_.idx = idx; data_.tid = tid; data_.cpu = cpu; data_.reserved__ = pid; header.size = sizeof(header) + sizeof(data_); } bool PerfRecordAuxtrace::GetBinary1(std::vector &buf) const { if (buf.size() < header.size) { buf.resize(header.size); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); size_t copySize = header.size - GetHeaderSize(); if (memcpy_s(p, sizeof(data_), reinterpret_cast(&data_), copySize) != 0) { HLOGE("memcpy_s return failed"); return false; } return true; } bool PerfRecordAuxtrace::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); size_t copySize = header.size - GetHeaderSize(); if (memcpy_s(p, sizeof(data_), reinterpret_cast(&data_), copySize) != 0) { HLOGE("memcpy_s return failed"); return false; } p += header.size - GetHeaderSize(); if (memcpy_s(p, data_.size, static_cast(rawData_), data_.size) != 0) { HLOGE("memcpy_s return failed"); return false; } return true; } void PerfRecordAuxtrace::DumpData(int indent) const { PRINT_INDENT(indent, "size 0x%llx, offset 0x%llx, reference 0x%llx, idx %u, tid %u, cpu %u, pid %u\n", data_.size, data_.offset, data_.reference, data_.idx, data_.tid, data_.cpu, data_.reserved__); #if defined(is_ohos) && is_ohos SpeDumpRawData(rawData_, data_.size, indent, g_outputDump); #endif } void PerfRecordAuxtrace::DumpLog(const std::string &prefix) const { HLOGV("size %llu, offset 0x%llx, reference 0x%llx, idx %u, tid %u, cpu %u\n", data_.size, data_.offset, data_.reference, data_.idx, data_.tid, data_.cpu); } size_t PerfRecordAuxtrace::GetSize() const { return header.size + data_.size; } void PerfRecordSample::DumpLog(const std::string &prefix) const { HLOGV("%s: SAMPLE: id= %llu size %d pid %u tid %u ips %llu regs %llu, stacks %llu time %llu", prefix.c_str(), data_.sample_id, header.size, data_.pid, data_.tid, data_.nr, data_.reg_nr, data_.dyn_size, data_.time); } void PerfRecordSample::RecoverCallStack() { data_.ips = ips_.data(); data_.nr = ips_.size(); removeStack_ = true; } void PerfRecordSample::ReplaceWithCallStack(size_t originalSize) { // first we check if we have some user unwind stack need to merge ? if (callFrames_.size() != 0) { // when we have some kernel ips , we cp it first // new size is user call frames + kernel call frames // + PERF_CONTEXT_USER(last + 1) + expand mark(also PERF_CONTEXT_USER) const unsigned int perfContextSize = 2; ips_.reserve(data_.nr + callFrames_.size() + perfContextSize); if (data_.nr > 0) { ips_.assign(data_.ips, data_.ips + data_.nr); } // add user context mark ips_.emplace_back(PERF_CONTEXT_USER); // we also need make a expand mark just for debug only const size_t beginIpsSize = ips_.size(); bool ret = std::all_of(callFrames_.begin(), callFrames_.end(), [&](const DfxFrame &frame) { ips_.emplace_back(frame.pc); if (originalSize != 0 and (originalSize != callFrames_.size()) and ips_.size() == (originalSize + beginIpsSize)) { // just for debug // so we can see which frame begin is expand call frames ips_.emplace_back(PERF_CONTEXT_USER); } return true; }); if (ret) { HLOGV("combed %zu", callFrames_.size()); } else { HLOGV("failed to combed %zu", callFrames_.size()); } if (sampleType_ & PERF_SAMPLE_REGS_USER) { header.size -= data_.reg_nr * sizeof(u64); data_.reg_nr = 0; data_.user_abi = 0; } if (sampleType_ & PERF_SAMPLE_STACK_USER) { // 1. remove the user stack header.size -= data_.stack_size; header.size -= sizeof(data_.dyn_size); // 2. clean the size data_.stack_size = 0; data_.dyn_size = 0; } if (sampleType_ & PERF_SAMPLE_CALLCHAIN) { HLOGV("ips change from %llu -> %zu", data_.nr, ips_.size()); // 3. remove the nr size header.size -= data_.nr * sizeof(u64); // 4. add new nr size data_.nr = ips_.size(); header.size += data_.nr * sizeof(u64); // 5. change ips potin to our ips array and hold it. data_.ips = ips_.data(); } } else { // nothing need change return; } } PerfRecordSample::PerfRecordSample(uint8_t *p, const perf_event_attr &attr) : PerfEventRecord(p, "sample") { if (p == nullptr) { HLOG_ASSERT(p); return; } // clear the static vector data Clean(); sampleType_ = attr.sample_type; uint8_t *start = p; u64 dataSize = static_cast(RECORD_SIZE_LIMIT); SetPointerOffset(p, sizeof(header), dataSize); // parse record according SAMPLE_TYPE PopFromBinary(sampleType_ & PERF_SAMPLE_IDENTIFIER, p, data_.sample_id, dataSize); PopFromBinary(sampleType_ & PERF_SAMPLE_IP, p, data_.ip, dataSize); PopFromBinary2(sampleType_ & PERF_SAMPLE_TID, p, data_.pid, data_.tid, dataSize); PopFromBinary(sampleType_ & PERF_SAMPLE_TIME, p, data_.time, dataSize); PopFromBinary(sampleType_ & PERF_SAMPLE_ADDR, p, data_.addr, dataSize); PopFromBinary(sampleType_ & PERF_SAMPLE_ID, p, data_.id, dataSize); PopFromBinary(sampleType_ & PERF_SAMPLE_STREAM_ID, p, data_.stream_id, dataSize); PopFromBinary2(sampleType_ & PERF_SAMPLE_CPU, p, data_.cpu, data_.res, dataSize); PopFromBinary(sampleType_ & PERF_SAMPLE_PERIOD, p, data_.period, dataSize); PopFromBinary(sampleType_ & PERF_SAMPLE_CALLCHAIN, p, data_.nr, dataSize); if (data_.nr > 0) { // the pointer is from input(p), require caller keep input(p) with *this together // think it in next time data_.ips = reinterpret_cast(p); SetPointerOffset(p, data_.nr * sizeof(u64), dataSize); } PopFromBinary(sampleType_ & PERF_SAMPLE_RAW, p, data_.raw_size, dataSize); if (data_.raw_size > 0) { data_.raw_data = p; SetPointerOffset(p, data_.raw_size * sizeof(u8), dataSize); } PopFromBinary(sampleType_ & PERF_SAMPLE_BRANCH_STACK, p, data_.bnr, dataSize); if (data_.bnr > 0) { data_.lbr = reinterpret_cast(p); SetPointerOffset(p, data_.bnr * sizeof(PerfBranchEntry), dataSize); } PopFromBinary(sampleType_ & PERF_SAMPLE_REGS_USER, p, data_.user_abi, dataSize); if (data_.user_abi > 0) { data_.reg_mask = attr.sample_regs_user; data_.reg_nr = __builtin_popcountll(data_.reg_mask); data_.user_regs = reinterpret_cast(p); SetPointerOffset(p, data_.reg_nr * sizeof(u64), dataSize); } PopFromBinary(sampleType_ & PERF_SAMPLE_SERVER_PID, p, data_.server_nr, dataSize); if (data_.server_nr > 0) { data_.server_pids = reinterpret_cast(p); SetPointerOffset(p, data_.server_nr * sizeof(u64), dataSize); } PopFromBinary(sampleType_ & PERF_SAMPLE_STACK_USER, p, data_.stack_size, dataSize); if (data_.stack_size > 0) { data_.stack_data = p; SetPointerOffset(p, data_.stack_size, dataSize); PopFromBinary(true, p, data_.dyn_size, dataSize); } uint32_t remain = header.size - (p - start); if (data_.nr == 0 && dumpRemoveStack_ && remain == sizeof(stackId_)) { PopFromBinary(true, p, stackId_.value, dataSize); } } bool PerfRecordSample::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); PushToBinary(sampleType_ & PERF_SAMPLE_IDENTIFIER, p, data_.sample_id); PushToBinary(sampleType_ & PERF_SAMPLE_IP, p, data_.ip); PushToBinary2(sampleType_ & PERF_SAMPLE_TID, p, data_.pid, data_.tid); PushToBinary(sampleType_ & PERF_SAMPLE_TIME, p, data_.time); PushToBinary(sampleType_ & PERF_SAMPLE_ADDR, p, data_.addr); PushToBinary(sampleType_ & PERF_SAMPLE_ID, p, data_.id); PushToBinary(sampleType_ & PERF_SAMPLE_STREAM_ID, p, data_.stream_id); PushToBinary2(sampleType_ & PERF_SAMPLE_CPU, p, data_.cpu, data_.res); PushToBinary(sampleType_ & PERF_SAMPLE_PERIOD, p, data_.period); PushToBinary(sampleType_ & PERF_SAMPLE_CALLCHAIN, p, data_.nr); if (data_.nr > 0 && !removeStack_) { std::copy(data_.ips + skipKernel_, data_.ips + data_.nr + skipKernel_, reinterpret_cast(p)); p += data_.nr * sizeof(u64); } PushToBinary(sampleType_ & PERF_SAMPLE_RAW, p, data_.raw_size); if (data_.raw_size > 0) { std::copy(data_.raw_data, data_.raw_data + data_.raw_size, p); p += data_.raw_size * sizeof(u8); } PushToBinary(sampleType_ & PERF_SAMPLE_BRANCH_STACK, p, data_.bnr); if (data_.bnr > 0) { std::copy(data_.lbr, data_.lbr + data_.bnr, reinterpret_cast(p)); p += data_.bnr * sizeof(PerfBranchEntry); } PushToBinary(sampleType_ & PERF_SAMPLE_REGS_USER, p, data_.user_abi); if (data_.user_abi > 0 && data_.reg_nr > 0) { std::copy(data_.user_regs, data_.user_regs + data_.reg_nr, reinterpret_cast(p)); p += data_.reg_nr * sizeof(u64); } PushToBinary(sampleType_ & PERF_SAMPLE_SERVER_PID, p, data_.server_nr); if (data_.server_nr > 0) { std::copy(data_.server_pids + skipPid_, data_.server_pids + data_.server_nr + skipPid_, reinterpret_cast(p)); p += data_.server_nr * sizeof(u64); } PushToBinary(sampleType_ & PERF_SAMPLE_STACK_USER, p, data_.stack_size); if (data_.stack_size > 0) { std::copy(data_.stack_data, data_.stack_data + data_.stack_size, p); p += data_.stack_size * sizeof(u8); PushToBinary(true, p, data_.dyn_size); } PushToBinary(removeStack_, p, stackId_.value); return true; } void PerfRecordSample::DumpData(int indent) const { PRINT_INDENT(indent, "sample_type: 0x%" PRIx64 "\n", sampleType_); // dump record according sampleType if (sampleType_ & (PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER)) { PRINT_INDENT(indent, "ID %" PRIu64 "\n", static_cast(data_.sample_id)); } if (sampleType_ & PERF_SAMPLE_IP) { PRINT_INDENT(indent, "ip %llx\n", data_.ip); } if (sampleType_ & PERF_SAMPLE_TID) { PRINT_INDENT(indent, "pid %u, tid %u\n", data_.pid, data_.tid); } if (sampleType_ & PERF_SAMPLE_TIME) { PRINT_INDENT(indent, "time %llu\n", data_.time); } if (sampleType_ & PERF_SAMPLE_ADDR) { PRINT_INDENT(indent, "addr %p\n", reinterpret_cast(data_.addr)); } if (sampleType_ & PERF_SAMPLE_STREAM_ID) { PRINT_INDENT(indent, "stream_id %" PRIu64 "\n", static_cast(data_.stream_id)); } if (sampleType_ & PERF_SAMPLE_CPU) { PRINT_INDENT(indent, "cpu %u, res %u\n", data_.cpu, data_.res); } if (sampleType_ & PERF_SAMPLE_PERIOD) { PRINT_INDENT(indent, "period %" PRIu64 "\n", static_cast(data_.period)); } if (stackId_.section.id > 0) { PRINT_INDENT(indent, "stackid %" PRIu64 "\n", static_cast(stackId_.section.id)); } if (sampleType_ & PERF_SAMPLE_CALLCHAIN) { bool userContext = false; PRINT_INDENT(indent, "callchain nr=%lld\n", data_.nr); for (uint64_t i = 0; i < data_.nr; ++i) { std::string_view supplement = ""; if ((sampleType_ & PERF_SAMPLE_STACK_USER) == 0 || data_.ips[i] != PERF_CONTEXT_USER) { PRINT_INDENT(indent + 1, "0x%llx%s\n", data_.ips[i], supplement.data()); continue; } // is PERF_SAMPLE_STACK_USER type and is PERF_CONTEXT_USER if (!userContext) { userContext = true; supplement = " "; } else { supplement = " "; } PRINT_INDENT(indent + 1, "0x%llx%s\n", data_.ips[i], supplement.data()); } } if (sampleType_ & PERF_SAMPLE_RAW) { PRINT_INDENT(indent, "raw size=%u\n", data_.raw_size); const uint32_t *data = reinterpret_cast(data_.raw_data); size_t size = data_.raw_size / sizeof(uint32_t); for (size_t i = 0; i < size; ++i) { PRINT_INDENT(indent + 1, "0x%08x (%x)\n", data[i], data[i]); } } if (sampleType_ & PERF_SAMPLE_BRANCH_STACK) { PRINT_INDENT(indent, "branch_stack nr=%lld\n", data_.bnr); for (uint64_t i = 0; i < data_.bnr; ++i) { auto &item = data_.lbr[i]; PRINT_INDENT(indent + 1, "from 0x%llx, to 0x%llx, flags 0x%llx\n", item.from, item.to, item.flags); } } if (sampleType_ & PERF_SAMPLE_REGS_USER) { PRINT_INDENT(indent, "user regs: abi=%lld, reg_nr=%lld\n", data_.user_abi, data_.reg_nr); for (uint64_t i = 0; i < data_.reg_nr; ++i) { PRINT_INDENT(indent + 1, "0x%llx\n", data_.user_regs[i]); } } if (sampleType_ & PERF_SAMPLE_SERVER_PID) { PRINT_INDENT(indent, "server nr=%lld\n", data_.server_nr); for (uint64_t i = 0; i < data_.server_nr; ++i) { PRINT_INDENT(indent + 1, "pid: %llu\n", data_.server_pids[i]); } } if (sampleType_ & PERF_SAMPLE_STACK_USER) { PRINT_INDENT(indent, "user stack: size %llu dyn_size %lld\n", data_.stack_size, data_.dyn_size); } } inline pid_t PerfRecordSample::GetPid() const { return data_.pid; } void PerfRecordSample::Clean() { ips_.clear(); callFrames_.clear(); serverPidMap_.clear(); } PerfRecordMmap::PerfRecordMmap(uint8_t *p) : PerfEventRecord(p, "mmap") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordMmap retren failed !!!"); } data_.filename[KILO - 1] = '\0'; } PerfRecordMmap::PerfRecordMmap(bool inKernel, u32 pid, u32 tid, u64 addr, u64 len, u64 pgoff, const std::string &filename) : PerfEventRecord(PERF_RECORD_MMAP, inKernel, "mmap") { data_.pid = pid; data_.tid = tid; data_.addr = addr; data_.len = len; data_.pgoff = pgoff; if (strncpy_s(data_.filename, KILO, filename.c_str(), filename.size()) != 0) { HLOGE("strncpy_s failed"); } header.size = sizeof(header) + sizeof(data_) - KILO + filename.size() + 1; } bool PerfRecordMmap::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); // data_.filename[] is variable-length std::copy(reinterpret_cast(&data_), reinterpret_cast(&data_) + GetSize() - GetHeaderSize(), p); return true; } void PerfRecordMmap::DumpData(int indent) const { #if defined(is_ohos) && is_ohos if (IsRoot()) { PRINT_INDENT(indent, "pid %u, tid %u, addr 0x%llx, len 0x%llx\n", data_.pid, data_.tid, data_.addr, data_.len); PRINT_INDENT(indent, "pgoff 0x%llx, filename %s\n", data_.pgoff, data_.filename); } #endif } void PerfRecordMmap::DumpLog(const std::string &prefix) const { HLOGV("%s: MMAP: size %d pid %u tid %u dso '%s' (0x%llx-0x%llx)@0x%llx", prefix.c_str(), header.size, data_.pid, data_.tid, data_.filename, data_.addr, data_.addr + data_.len, data_.pgoff); } PerfRecordMmap2::PerfRecordMmap2(uint8_t *p) : PerfEventRecord(p, "mmap2") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordMmap2 retren failed !!!"); } } PerfRecordMmap2::PerfRecordMmap2(bool inKernel, u32 pid, u32 tid, u64 addr, u64 len, u64 pgoff, u32 maj, u32 min, u64 ino, u32 prot, u32 flags, const std::string &filename) : PerfEventRecord(PERF_RECORD_MMAP2, inKernel, "mmap2") { data_.pid = pid; data_.tid = tid; data_.addr = addr; data_.len = len; data_.pgoff = pgoff; data_.maj = maj; data_.min = min; data_.ino = ino; data_.ino_generation = 0; data_.prot = prot; data_.flags = flags; if (strncpy_s(data_.filename, KILO, filename.c_str(), filename.size()) != 0) { HLOGE("strncpy_s failed"); } header.size = sizeof(header) + sizeof(data_) - KILO + filename.size() + 1; } PerfRecordMmap2::PerfRecordMmap2(bool inKernel, u32 pid, u32 tid, std::shared_ptr item) : PerfEventRecord(PERF_RECORD_MMAP2, inKernel, "mmap2") { data_.pid = pid; data_.tid = tid; if (item != nullptr) { data_.addr = item->begin; data_.len = item->end - item->begin; data_.pgoff = item->offset; data_.maj = item->major; data_.min = item->minor; data_.ino = item->inode; data_.ino_generation = 0; // r--p 00000000 103:3e 12307 /data/storage/el1/bundle/entry.hap // why prot get from this is 7. rwxp DfxMap::PermsToProts(item->perms, data_.prot, data_.flags); if (strncpy_s(data_.filename, KILO, item->name.c_str(), item->name.size()) != 0) { HLOGE("strncpy_s failed"); } header.size = sizeof(header) + sizeof(data_) - KILO + item->name.size() + 1; } else { data_.addr = 0; data_.len = 0; data_.pgoff = 0; data_.maj = 0; data_.min = 0; data_.ino = 0; data_.ino_generation = 0; if (memset_s(data_.filename, KILO, 0, KILO) != EOK) { HLOGE("memset_s failed"); } } } bool PerfRecordMmap2::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); // data_.filename[] is variable-length std::copy(reinterpret_cast(&data_), reinterpret_cast(&data_) + GetSize() - GetHeaderSize(), p); return true; } void PerfRecordMmap2::DumpData(int indent) const { #if defined(is_ohos) && is_ohos if (IsRoot()) { PRINT_INDENT(indent, "pid %u, tid %u, addr 0x%llx, len 0x%llx\n", data_.pid, data_.tid, data_.addr, data_.len); PRINT_INDENT(indent, "pgoff 0x%llx, maj %u, min %u, ino %llu, ino_generation %llu\n", data_.pgoff, data_.maj, data_.min, data_.ino, data_.ino_generation); PRINT_INDENT(indent, "prot %u, flags %u, filename %s\n", data_.prot, data_.flags, data_.filename); } #endif } void PerfRecordMmap2::DumpLog(const std::string &prefix) const { HLOGV("%s: MMAP2: size %d pid %u tid %u dso '%s' (0x%llx-0x%llx)@0x%llx", prefix.c_str(), header.size, data_.pid, data_.tid, data_.filename, data_.addr, data_.addr + data_.len, data_.pgoff); } PerfRecordLost::PerfRecordLost(uint8_t *p) : PerfEventRecord(p, "lost") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordLost retren failed !!!"); } } bool PerfRecordLost::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); auto pDest = reinterpret_cast(p); *pDest = data_; return true; } void PerfRecordLost::DumpData(int indent) const { PRINT_INDENT(indent, "id %llu, lost %llu\n", data_.id, data_.lost); } PerfRecordComm::PerfRecordComm(uint8_t *p) : PerfEventRecord(p, "comm") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordComm retren failed !!!"); } data_.comm[KILO - 1] = '\0'; } PerfRecordComm::PerfRecordComm(bool inKernel, u32 pid, u32 tid, const std::string &comm) : PerfEventRecord(PERF_RECORD_COMM, inKernel, "comm") { data_.pid = pid; data_.tid = tid; if (strncpy_s(data_.comm, KILO, comm.c_str(), comm.size()) != 0) { HLOGE("strncpy_s failed !!!"); } header.size = sizeof(header) + sizeof(data_) - KILO + comm.size() + 1; } bool PerfRecordComm::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); // data_.comm[] is variable-length std::copy(reinterpret_cast(&data_), reinterpret_cast(&data_) + GetSize() - GetHeaderSize(), p); return true; } void PerfRecordComm::DumpData(int indent) const { PRINT_INDENT(indent, "pid %u, tid %u, comm %s\n", data_.pid, data_.tid, data_.comm); } void PerfRecordComm::DumpLog(const std::string &prefix) const { HLOGV("pid %u, tid %u, comm %s\n", data_.pid, data_.tid, data_.comm); } PerfRecordExit::PerfRecordExit(uint8_t *p) : PerfEventRecord(p, "exit") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordExit retren failed !!!"); } } bool PerfRecordExit::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); auto pDest = reinterpret_cast(p); *pDest = data_; return true; } void PerfRecordExit::DumpData(int indent) const { PRINT_INDENT(indent, "pid %u, ppid %u, tid %u, ptid %u time 0x%llx\n", data_.pid, data_.ppid, data_.tid, data_.ptid, data_.time); } PerfRecordThrottle::PerfRecordThrottle(uint8_t *p) : PerfEventRecord(p, "throttle") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordThrottle retren failed !!!"); } } bool PerfRecordThrottle::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); auto pDest = reinterpret_cast(p); *pDest = data_; return true; } void PerfRecordThrottle::DumpData(int indent) const { PRINT_INDENT(indent, "time 0x%llx, id %llx, stream_id %llx\n", data_.time, data_.id, data_.stream_id); } PerfRecordUnthrottle::PerfRecordUnthrottle(uint8_t *p) : PerfEventRecord(p, "unthrottle") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordUnthrottle retren failed !!!"); } } bool PerfRecordUnthrottle::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); auto pDest = reinterpret_cast(p); *pDest = data_; return true; } void PerfRecordUnthrottle::DumpData(int indent) const { PRINT_INDENT(indent, "time 0x%llx, id %llx, stream_id %llx\n", data_.time, data_.id, data_.stream_id); } PerfRecordFork::PerfRecordFork(uint8_t *p) : PerfEventRecord(p, "fork") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordFork retren failed !!!"); } } bool PerfRecordFork::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); auto pDest = reinterpret_cast(p); *pDest = data_; return true; } void PerfRecordFork::DumpData(int indent) const { PRINT_INDENT(indent, "pid %u, ppid %u, tid %u, ptid %u\n", data_.pid, data_.ppid, data_.tid, data_.ptid); } PerfRecordRead::PerfRecordRead(uint8_t *p) : PerfEventRecord(p, "read") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordRead retren failed !!!"); } } bool PerfRecordRead::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); auto pDest = reinterpret_cast(p); *pDest = data_; return true; } void PerfRecordRead::DumpData(int indent) const { PRINT_INDENT(indent, "pid %u, tid %u\n", data_.pid, data_.tid); PRINT_INDENT(indent, "values: value %llx, timeEnabled %llx, timeRunning %llx, id %llx\n", data_.values.value, data_.values.timeEnabled, data_.values.timeRunning, data_.values.id); } PerfRecordAux::PerfRecordAux(uint8_t *p) : PerfEventRecord(p, "aux") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordAux retren failed !!!"); } } bool PerfRecordAux::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); PushToBinary(true, p, data_.aux_offset); PushToBinary(true, p, data_.aux_size); PushToBinary(true, p, data_.flags); PushToBinary2(sampleType_ & PERF_SAMPLE_TID, p, data_.sample_id.pid, data_.sample_id.tid); PushToBinary(sampleType_ & PERF_SAMPLE_TIME, p, data_.sample_id.time); PushToBinary(sampleType_ & PERF_SAMPLE_ID, p, data_.sample_id.id); PushToBinary(sampleType_ & PERF_SAMPLE_STREAM_ID, p, data_.sample_id.stream_id); PushToBinary2(sampleType_ & PERF_SAMPLE_CPU, p, data_.sample_id.cpu, data_.sample_id.res); PushToBinary(sampleType_ & PERF_SAMPLE_IDENTIFIER, p, data_.sample_id.id2); return true; } void PerfRecordAux::DumpData(int indent) const { PRINT_INDENT(indent, "aux_offset 0x%llx aux_size 0x%llx flags 0x%llx pid %u tid %u time %llu", data_.aux_offset, data_.aux_size, data_.flags, data_.sample_id.pid, data_.sample_id.tid, data_.sample_id.time); } PerfRecordItraceStart::PerfRecordItraceStart(uint8_t *p) : PerfEventRecord(p, "itraceStart") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordItraceStart retren failed !!!"); } } bool PerfRecordItraceStart::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); auto pDest = reinterpret_cast(p); *pDest = data_; return true; } void PerfRecordItraceStart::DumpData(int indent) const { PRINT_INDENT(indent, "pid %u, tid %u\n", data_.pid, data_.tid); } PerfRecordLostSamples::PerfRecordLostSamples(uint8_t *p) : PerfEventRecord(p, "lostSamples") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordLostSamples retren failed !!!"); } } bool PerfRecordLostSamples::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); auto pDest = reinterpret_cast(p); *pDest = data_; return true; } void PerfRecordLostSamples::DumpData(int indent) const { PRINT_INDENT(indent, "lost %llu\n", data_.lost); } PerfRecordSwitch::PerfRecordSwitch(uint8_t *p) : PerfEventRecord(p, "switch") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordSwitch retren failed !!!"); } } bool PerfRecordSwitch::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); auto pDest = reinterpret_cast(p); *pDest = data_; return true; } PerfRecordSwitchCpuWide::PerfRecordSwitchCpuWide(uint8_t *p) : PerfEventRecord(p, "switchCpuWide") { size_t dataSize = GetSize(); if (dataSize >= sizeof(header)) { size_t copySize = dataSize - sizeof(header); if (memcpy_s(reinterpret_cast(&data_), sizeof(data_), p + sizeof(header), copySize) != 0) { HLOGE("memcpy_s retren failed !!!"); } } else { HLOGE("PerfRecordSwitchCpuWide retren failed !!!"); } } bool PerfRecordSwitchCpuWide::GetBinary(std::vector &buf) const { if (buf.size() < GetSize()) { buf.resize(GetSize()); } GetHeaderBinary(buf); uint8_t *p = buf.data() + GetHeaderSize(); auto pDest = reinterpret_cast(p); *pDest = data_; return true; } void PerfRecordSwitchCpuWide::DumpData(int indent) const { PRINT_INDENT(indent, "next_prev_pid %u, next_prev_tid %u\n", data_.next_prev_pid, data_.next_prev_tid); } pid_t PerfRecordSample::GetUstackServerPid() { if (!data_.server_nr) { return data_.pid; } size_t currServer = 0; // ipNr == 1...nr: server_pid of data_.ips[nr] for (size_t i = 0; i < data_.nr; i++) { // context change, use next server pid if (data_.ips[i] >= PERF_CONTEXT_MAX) { currServer++; } } // ipNr == nr + 1: server_pid of ustack if (currServer > 0) { currServer++; } if (currServer >= data_.server_nr) { HLOGE("ustack server pid nr %zu out of range", currServer); return data_.pid; } // return server pid return data_.server_pids[currServer]; } pid_t PerfRecordSample::GetServerPidof(unsigned int ipNr) { if (!data_.server_nr) { return data_.pid; } // init serverPidMap_ if (!serverPidMap_.size()) { size_t currServer = 0; // ipNr == 0: server_pid of data_.ip serverPidMap_.emplace_back(data_.server_pids[currServer]); // ipNr == 1...nr: server_pid of data_.ips[nr] for (size_t i = 1; i < data_.nr; i++) { // context change, use next server pid if (data_.ips[i] >= PERF_CONTEXT_MAX) { currServer++; } if (currServer >= data_.server_nr) { HLOGE("callchain server pid nr %zu out of range", currServer); break; } serverPidMap_.emplace_back(data_.server_pids[currServer]); } } // return server pid if (ipNr >= serverPidMap_.size()) { return data_.pid; } else { return serverPidMap_[ipNr]; } } } // namespace HiPerf } // namespace Developtools } // namespace OHOS