From 6cf4194a3eff6682d50a7302179fe580ce8aaf45 Mon Sep 17 00:00:00 2001 From: buzhenwang Date: Mon, 8 Sep 2025 20:28:54 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B6=85=E5=A4=A7=E5=87=BD=E6=95=B0=E6=95=B4?= =?UTF-8?q?=E6=94=B9=20Signed-off-by:leiguangyu=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: buzhenwang --- include/debug_logger.h | 1 + include/option.h | 80 ++-- include/perf_event_record.h | 1 + include/perf_events.h | 7 + include/report_protobuf_file.h | 1 + include/subcommand_dump.h | 1 + src/debug_logger.cpp | 39 +- src/perf_event_record.cpp | 78 ++-- src/perf_events.cpp | 370 +++++++++--------- src/report_protobuf_file.cpp | 63 +-- src/subcommand_dump.cpp | 89 +++-- .../common/native/debug_logger_test.cpp | 19 + test/unittest/common/native/option_test.cpp | 15 + .../common/native/perf_events_test.cpp | 35 ++ .../native/report_protobuf_file_test.cpp | 18 + 15 files changed, 478 insertions(+), 339 deletions(-) diff --git a/include/debug_logger.h b/include/debug_logger.h index d52b391..d2aa21d 100644 --- a/include/debug_logger.h +++ b/include/debug_logger.h @@ -122,6 +122,7 @@ private: DebugLevel GetLogLevelByName(const std::string &) const; DebugLevel GetLogLevelByTag(const std::string &) const; const std::string GetLogLevelName(const DebugLevel) const; + void PrintHilog(std::string &buffer, const std::chrono::steady_clock::time_point startTime, int& ret) const; int HiLog(std::string &buffer) const; diff --git a/include/option.h b/include/option.h index 80937d6..c494067 100644 --- a/include/option.h +++ b/include/option.h @@ -62,6 +62,48 @@ The program should exit with an error. Return true, indicating that the parameter is legal (but the user does not necessarily enter the parameter) */ +template +bool GetValue(argsVector &args, const std::string &optionName, T &value, T &localValues) +{ + if (!CheckOptionFormat(optionName)) { + if (optionName.empty()) { + printf("unable to use empty option name!\n"); + } else { + printf("format error. must use '-' at the begin of option '%s'!\n", optionName.c_str()); + } + return false; // something wrong + } + auto it = FindOption(args, optionName); + if (it == args.end()) { + HLOGV("not found option, return default value"); + return true; // not found but also not error + } else { + it = args.erase(it); + // some special case + if constexpr (std::is_same::value) { + // for bool we don't need get value. + // this always return true + GetValueFromString(optionName, optionName, value); + return true; + } else if (it == args.end()) { + // no value means failed + printf("option %s value missed\n", optionName.c_str()); + return false; + } else if (GetValueFromString(*it, optionName, localValues)) { + // got some value + value = localValues; + args.erase(it); + return true; + } else { + // have value but convert failed. + printf("incorrect option value '%s' for option '%s'. View the usage with the --help option.\n", + (*it).c_str(), optionName.c_str()); + return false; + } + } + return true; +} + template bool GetOptionValue(argsVector &args, const std::string optionName, T &value) { @@ -87,43 +129,7 @@ bool GetOptionValue(argsVector &args, const std::string optionName, T &value) } return true; } else { - if (!CheckOptionFormat(optionName)) { - if (optionName.empty()) { - printf("unable to use empty option name!\n"); - } else { - printf("format error. must use '-' at the begin of option '%s'!\n", - optionName.c_str()); - } - return false; // something wrong - } - auto it = FindOption(args, optionName); - if (it == args.end()) { - HLOGV("not found option, return default value"); - return true; // not found but also not error - } else { - it = args.erase(it); - // some special case - if constexpr (std::is_same::value) { - // for bool we don't need get value. - // this always return true - GetValueFromString(optionName, optionName, value); - return true; - } else if (it == args.end()) { - // no value means failed - printf("option %s value missed\n", optionName.c_str()); - return false; - } else if (GetValueFromString(*it, optionName, localValues)) { - // got some value - value = localValues; - args.erase(it); - return true; - } else { - // have value but convert failed. - printf("incorrect option value '%s' for option '%s'. View the usage with the --help option.\n", - (*it).c_str(), optionName.c_str()); - return false; - } - } + return GetValue(args, optionName, value, localValues); } } diff --git a/include/perf_event_record.h b/include/perf_event_record.h index 11da0cc..2841917 100644 --- a/include/perf_event_record.h +++ b/include/perf_event_record.h @@ -322,6 +322,7 @@ public: pid_t GetServerPidof(unsigned int ipNr); private: static bool dumpRemoveStack_; + void ParseRecordData(uint8_t *p, const perf_event_attr &attr, u64 dataSize); }; class PerfRecordExit : public PerfEventRecordTemplate { diff --git a/include/perf_events.h b/include/perf_events.h index 98b5e36..8d52adb 100644 --- a/include/perf_events.h +++ b/include/perf_events.h @@ -563,6 +563,7 @@ private: void ReadRecordsFromSpeMmaps(MmapFd& mmapFd, const u64 auxOffset, u64 auxSize, const u32 pid, const u32 tid); void SpeReadData(void *dataPage, u64 *dataTail, uint8_t *buf, const u32 size); bool GetRecordFromMmap(MmapFd &mmap); + void GetRecords(bool &enableFlag); void GetRecordFieldFromMmap(MmapFd &mmap, void *dest, size_t pos, size_t size); void MoveRecordToBuf(MmapFd &mmap, bool &isAuxEvent, u64 &auxOffset, u64 &auxSize, u32 &pid, u32 &tid); size_t GetCallChainPosInSampleRecord(const perf_event_attr &attr); @@ -576,6 +577,9 @@ private: void WaitRecordThread(); bool HaveTargetsExit(const std::chrono::steady_clock::time_point &startTime); void ExitReadRecordBufThread(); + bool GetStat(const std::chrono::steady_clock::time_point &startTime, + std::chrono::steady_clock::time_point &nextReportTime, std::chrono::milliseconds &usedTimeMsTick, + __u64 &durationInSec, int64_t thresholdTimeInMs); #ifdef CONFIG_HAS_CCM static constexpr char PRODUCT_CONFIG_PATH[] = "etc/hiperf/hiperf_cfg.json"; @@ -676,6 +680,7 @@ private: bool AddEvent(const perf_type_id type, const __u64 config, const bool excludeUser = false, const bool excludeKernel = false, const bool followGroup = false); bool AddEvent(const std::string &eventString, const bool followGroup = false); + void SetEventAttr(EventItem &eventItem, const perf_type_id type, const bool followGroup); bool AddSpeEvent(const u32 type, const bool followGroup = false); bool IsEventSupport(const perf_type_id type, const __u64 config); bool IsEventAttrSupport(perf_event_attr &attr); @@ -689,6 +694,8 @@ private: void PutAllCpus(); bool PrepareFdEvents(); bool CreateFdEvents(); + int CreateFdEventsForEachPid(EventItem &eventItem, const size_t icpu, const size_t ipid, + uint &fdNumber, int &groupFdCache); bool StatReport(const __u64 &durationInSec); bool CreateMmap(const FdItem &item, const perf_event_attr &attr); bool CreateSpeMmap(const FdItem &item, const perf_event_attr &attr); diff --git a/include/report_protobuf_file.h b/include/report_protobuf_file.h index 06ce84c..4bd1ada 100644 --- a/include/report_protobuf_file.h +++ b/include/report_protobuf_file.h @@ -86,6 +86,7 @@ private: bool Dump(const Proto::SymbolTableFile &message, const int indent = 0); bool Dump(const Proto::VirtualThreadInfo &message, const int indent = 0); bool Dump(const Proto::ReportInfo &message, const int indent = 0); + int Dump(uint32_t &recordLength, ProtobufReadBack readBack); }; } // namespace HiPerf } // namespace Developtools diff --git a/include/subcommand_dump.h b/include/subcommand_dump.h index b5e0d03..9682064 100644 --- a/include/subcommand_dump.h +++ b/include/subcommand_dump.h @@ -111,6 +111,7 @@ private: void DumpDataPortion(const int indent = 0); void DumpCallChain(int indent, const PerfRecordSample& sample); void DumpFeaturePortion(const int indent = 0); + void DumpFeatureSection(const int indent, const std::unique_ptr &featureSection); void DumpUniqueStackTableNode(const int indent, const PerfFileSectionUniStackTable &uniStackTable); void ExportUserData(PerfEventRecord& record); void ExportUserStack(const PerfRecordSample &recordSample); diff --git a/src/debug_logger.cpp b/src/debug_logger.cpp index 9e3362d..94e7fc8 100644 --- a/src/debug_logger.cpp +++ b/src/debug_logger.cpp @@ -80,6 +80,26 @@ int DebugLogger::HiLog(std::string &buffer) const #endif #endif +void DebugLogger::PrintHilog(std::string &buffer, const std::chrono::steady_clock::time_point startTime, int& ret) const +{ + if (enableHilog_) { +#if is_ohos && !defined(CONFIG_NO_HILOG) + std::lock_guard lock(logMutex_); + ret = HiLog(buffer); // to the hilog +#endif + } else if (file_ != nullptr) { + std::lock_guard lock(logMutex_); +#ifdef HIPERF_DEBUG_TIME + const auto startWriteTime = std::chrono::steady_clock::now(); +#endif + auto timeStamp = startTime - timeStamp_; + fprintf(file_, "%05" PRId64 "ms %s", (int64_t)timeStamp.count(), buffer.data()); // to the file +#ifdef HIPERF_DEBUG_TIME + logWriteTimes_ += duration_cast(std::chrono::steady_clock::now() - startWriteTime); +#endif + } +} + int DebugLogger::Log(const DebugLevel level, const std::string &logTag, const char *fmt, ...) const { constexpr const int DEFAULT_STRING_BUF_SIZE = 4096; @@ -106,24 +126,7 @@ int DebugLogger::Log(const DebugLevel level, const std::string &logTag, const ch if ((mixLogOutput_ && level < LEVEL_FATAL) || level == LEVEL_FATAL) { ret = fprintf(stdout, "%s", buffer.data()); // to the stdout } - - if (enableHilog_) { -#if is_ohos && !defined(CONFIG_NO_HILOG) - std::lock_guard lock(logMutex_); - ret = HiLog(buffer); // to the hilog -#endif - } else if (file_ != nullptr) { - std::lock_guard lock(logMutex_); -#ifdef HIPERF_DEBUG_TIME - const auto startWriteTime = std::chrono::steady_clock::now(); -#endif - auto timeStamp = startTime - timeStamp_; - fprintf(file_, "%05" PRId64 "ms %s", (int64_t)timeStamp.count(), buffer.data()); // to the file -#ifdef HIPERF_DEBUG_TIME - logWriteTimes_ += duration_cast(std::chrono::steady_clock::now() - startWriteTime); -#endif - } - + PrintHilog(buffer, startTime, ret); #ifdef HIPERF_DEBUG_TIME logTimes_ += duration_cast(std::chrono::steady_clock::now() - startTime); logCount_++; diff --git a/src/perf_event_record.cpp b/src/perf_event_record.cpp index 232796c..a68c386 100644 --- a/src/perf_event_record.cpp +++ b/src/perf_event_record.cpp @@ -121,6 +121,18 @@ inline void PopFromBinary2(const bool condition, uint8_t*& p, T1& v1, T2& v2, u6 } } +template +void PopFromBinary(const bool condition, uint8_t*& p, Addr* addr, Count& count, u64& dataSize) +{ + PopFromBinary(condition, p, count, dataSize); + if (count > 0) { + // the pointer is from input(p), require caller keep input(p) with *this together + // think it in next time + addr = reinterpret_cast(p); + SetPointerOffset(p, count * sizeof(Count), dataSize); + } +} + inline void SetPointerOffset(uint8_t*& p, u64 offset, u64& size) { HIPERF_ASSERT(offset <= size && offset <= RECORD_SIZE_LIMIT, "SetPointerOffset error\n"); @@ -515,23 +527,9 @@ PerfRecordSample::PerfRecordSample(const PerfRecordSample& sample) removeStack_ = sample.removeStack_; } -void PerfRecordSample::Init(uint8_t *p, const perf_event_attr &attr) -{ - PerfEventRecord::InitHeader(p); - - HLOG_ASSERT(p != nullptr); - // clear the vector data - Clean(); - sampleType_ = attr.sample_type; - skipKernel_ = 0; - skipPid_ = 0; - stackId_ = {0}; - removeStack_ = false; - data_ = {}; - uint8_t *start = p; - u64 dataSize = static_cast(RECORD_SIZE_LIMIT); - SetPointerOffset(p, sizeof(header_), dataSize); +void PerfRecordSample::ParseRecordData(uint8_t *p, const perf_event_attr &attr, u64 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); @@ -542,23 +540,9 @@ void PerfRecordSample::Init(uint8_t *p, const perf_event_attr &attr) 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_CALLCHAIN, p, data_.ips, data_.nr, dataSize); + PopFromBinary(sampleType_ & PERF_SAMPLE_RAW, p, data_.raw_data, data_.raw_size, dataSize); + PopFromBinary(sampleType_ & PERF_SAMPLE_BRANCH_STACK, p, data_.lbr, data_.bnr, dataSize); PopFromBinary(sampleType_ & PERF_SAMPLE_REGS_USER, p, data_.user_abi, dataSize); if (data_.user_abi > 0) { data_.reg_mask = attr.sample_regs_user; @@ -566,17 +550,33 @@ void PerfRecordSample::Init(uint8_t *p, const perf_event_attr &attr) 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_SERVER_PID, p, data_.server_pids, data_.server_nr, 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); } +} + +void PerfRecordSample::Init(uint8_t *p, const perf_event_attr &attr) +{ + PerfEventRecord::InitHeader(p); + + HLOG_ASSERT(p != nullptr); + // clear the vector data + Clean(); + sampleType_ = attr.sample_type; + skipKernel_ = 0; + skipPid_ = 0; + stackId_ = {0}; + removeStack_ = false; + data_ = {}; + uint8_t *start = p; + u64 dataSize = static_cast(RECORD_SIZE_LIMIT); + SetPointerOffset(p, sizeof(header_), dataSize); + + ParseRecordData(p, attr, dataSize); uint32_t remain = header_.size - (p - start); if (data_.nr == 0 && dumpRemoveStack_ && remain == sizeof(stackId_)) { PopFromBinary(true, p, stackId_.value, dataSize); @@ -598,10 +598,8 @@ 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); diff --git a/src/perf_events.cpp b/src/perf_events.cpp index 5f47bd5..f2bf804 100644 --- a/src/perf_events.cpp +++ b/src/perf_events.cpp @@ -47,6 +47,7 @@ static std::atomic_bool g_trackRunning = false; static constexpr int32_t UPDATE_TIME_INTERVAL = 10; // 10ms static constexpr uint64_t NANO_SECONDS_PER_SECOND = 1000000000; static constexpr uint32_t POLL_FAIL_COUNT_THRESHOLD = 10; +static constexpr unsigned int MAX_WAKEUP_MARK = 1024 * 1024; OHOS::UniqueFd PerfEvents::Open(perf_event_attr &attr, const pid_t pid, const int cpu, const int groupFd, const unsigned long flags) @@ -443,6 +444,56 @@ void PerfEvents::SetConfig(std::map &speOptMaps) config2_ |= speOptMaps["min_latency"] & 0xfff; } +void PerfEvents::SetEventAttr(EventItem &eventItem, const perf_type_id type, const bool followGroup) +{ + if (samplePeriod_ > 0) { + eventItem.attr.freq = 0; + eventItem.attr.sample_freq = 0; + eventItem.attr.sample_period = samplePeriod_; + } else if (sampleFreq_ > 0) { + eventItem.attr.freq = 1; + eventItem.attr.sample_freq = sampleFreq_; + } else { + if (type == PERF_TYPE_TRACEPOINT) { + eventItem.attr.freq = 0; + eventItem.attr.sample_period = DEFAULT_SAMPLE_PERIOD; + } else { + eventItem.attr.freq = 1; + eventItem.attr.sample_freq = DEFAULT_SAMPLE_FREQUNCY; + } + } + + eventItem.attr.watermark = 1; + eventItem.attr.wakeup_watermark = (mmapPages_ * pageSize_) >> 1; + if (eventItem.attr.wakeup_watermark > MAX_WAKEUP_MARK) { + eventItem.attr.wakeup_watermark = MAX_WAKEUP_MARK; + } + + // for a group of events, only enable comm/mmap on the first event + if (!followGroup) { + eventItem.attr.comm = 1; + eventItem.attr.mmap = 1; + eventItem.attr.mmap2 = 1; + eventItem.attr.mmap_data = 1; + } + + if (sampleStackType_ == SampleStackType::DWARF) { + eventItem.attr.sample_type = SAMPLE_TYPE | PERF_SAMPLE_CALLCHAIN | + PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER; + eventItem.attr.exclude_callchain_user = 1; + eventItem.attr.sample_regs_user = GetSupportedRegMask(GetDeviceArch()); + eventItem.attr.sample_stack_user = dwarfSampleStackSize_; + } else if (sampleStackType_ == SampleStackType::FP) { + eventItem.attr.sample_type = SAMPLE_TYPE | PERF_SAMPLE_CALLCHAIN; + } else { + eventItem.attr.sample_type = SAMPLE_TYPE; + } + + if (isHM_) { + eventItem.attr.sample_type |= PERF_SAMPLE_SERVER_PID; + } +} + bool PerfEvents::AddEvent(const perf_type_id type, const __u64 config, const bool excludeUser, const bool excludeKernel, const bool followGroup) { @@ -452,20 +503,17 @@ bool PerfEvents::AddEvent(const perf_type_id type, const __u64 config, const boo CHECK_TRUE(IsEventSupport(type, config), false, 0, ""); HLOGV("type %d config %llu excludeUser %d excludeKernel %d followGroup %d", type, config, excludeUser, excludeKernel, followGroup); - // if use follow ? EventGroupItem &eventGroupItem = followGroup ? eventGroupItem_.back() : eventGroupItem_.emplace_back(); // always new item EventItem &eventItem = eventGroupItem.eventItems.emplace_back(); - eventItem.typeName = GetTypeName(type); if (type == PERF_TYPE_TRACEPOINT) { eventItem.configName = GetTraceConfigName(config); } else { eventItem.configName = GetStaticConfigName(type, config); } - // attr if (memset_s(&eventItem.attr, sizeof(perf_event_attr), 0, sizeof(perf_event_attr)) != EOK) { HLOGE("memset_s failed in PerfEvents::AddEvent"); @@ -477,62 +525,13 @@ bool PerfEvents::AddEvent(const perf_type_id type, const __u64 config, const boo eventItem.attr.disabled = 1; eventItem.attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID; - eventItem.attr.inherit = (inherit_ ? 1 : 0); eventItem.attr.exclude_kernel = excludeKernel; eventItem.attr.exclude_user = excludeUser; - // we also need mmap for record if (recordCallBack_) { - if (samplePeriod_ > 0) { - eventItem.attr.freq = 0; - eventItem.attr.sample_freq = 0; - eventItem.attr.sample_period = samplePeriod_; - } else if (sampleFreq_ > 0) { - eventItem.attr.freq = 1; - eventItem.attr.sample_freq = sampleFreq_; - } else { - if (type == PERF_TYPE_TRACEPOINT) { - eventItem.attr.freq = 0; - eventItem.attr.sample_period = DEFAULT_SAMPLE_PERIOD; - } else { - eventItem.attr.freq = 1; - eventItem.attr.sample_freq = DEFAULT_SAMPLE_FREQUNCY; - } - } - - eventItem.attr.watermark = 1; - eventItem.attr.wakeup_watermark = (mmapPages_ * pageSize_) >> 1; - static constexpr unsigned int maxWakeupMark = 1024 * 1024; - if (eventItem.attr.wakeup_watermark > maxWakeupMark) { - eventItem.attr.wakeup_watermark = maxWakeupMark; - } - - // for a group of events, only enable comm/mmap on the first event - if (!followGroup) { - eventItem.attr.comm = 1; - eventItem.attr.mmap = 1; - eventItem.attr.mmap2 = 1; - eventItem.attr.mmap_data = 1; - } - - if (sampleStackType_ == SampleStackType::DWARF) { - eventItem.attr.sample_type = SAMPLE_TYPE | PERF_SAMPLE_CALLCHAIN | - PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER; - eventItem.attr.exclude_callchain_user = 1; - eventItem.attr.sample_regs_user = GetSupportedRegMask(GetDeviceArch()); - eventItem.attr.sample_stack_user = dwarfSampleStackSize_; - } else if (sampleStackType_ == SampleStackType::FP) { - eventItem.attr.sample_type = SAMPLE_TYPE | PERF_SAMPLE_CALLCHAIN; - } else { - eventItem.attr.sample_type = SAMPLE_TYPE; - } - - if (isHM_) { - eventItem.attr.sample_type |= PERF_SAMPLE_SERVER_PID; - } + PerfEvents::SetEventAttr(eventItem, type, followGroup); } - // set clock id if (clockId_ != -1) { eventItem.attr.use_clockid = 1; @@ -542,11 +541,9 @@ bool PerfEvents::AddEvent(const perf_type_id type, const __u64 config, const boo eventItem.attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; eventItem.attr.branch_sample_type = branchSampleType_; } - HLOGV("Add Event: '%s':'%s' %s %s %s", eventItem.typeName.c_str(), eventItem.configName.c_str(), excludeUser ? "excludeUser" : "", excludeKernel ? "excludeKernel" : "", followGroup ? "" : "group leader"); - return true; } @@ -1104,6 +1101,64 @@ bool PerfEvents::PrepareFdEvents(void) return true; } +int PerfEvents::CreateFdEventsForEachPid(EventItem &eventItem, const size_t icpu, const size_t ipid, + uint &fdNumber, int &groupFdCache) +{ + UniqueFd fd = Open(eventItem.attr, pids_[ipid], cpus_[icpu], + groupFdCache, 0); + // clang-format on + if (fd < 0) { + if (errno == ESRCH) { + if (verboseReport_) { + printf("pid %d does not exist.\n", pids_[ipid]); + } + HLOGE("pid %d does not exist.\n", pids_[ipid]); + HIPERF_HILOGE(MODULE_DEFAULT, "[CreateFdEvents] pid %{public}d does not exist.", + pids_[ipid]); + return 1; + } else { + // clang-format off + if (verboseReport_) { + char errInfo[ERRINFOLEN] = { 0 }; + strerror_r(errno, errInfo, ERRINFOLEN); + printf("%s event is not supported by the kernel on cpu %d. reason: %d:%s\n", + eventItem.configName.c_str(), cpus_[icpu], errno, errInfo); + } + char errInfo[ERRINFOLEN] = { 0 }; + strerror_r(errno, errInfo, ERRINFOLEN); + HLOGE("%s event is not supported by the kernel on cpu %d. reason: %d:%s\n", + eventItem.configName.c_str(), cpus_[icpu], errno, errInfo); + // clang-format on + return 0; // jump to next cpu + } + } + // after open successed , fill the result + // make a new FdItem + FdItem &fdItem = eventItem.fdItems.emplace_back(); + fdItem.fd = std::move(fd); + fdItem.cpu = cpus_[icpu]; + fdItem.pid = pids_[ipid]; + fdNumber++; + + // if sampling, mmap ring buffer + bool createMmapSucc = true; + if (recordCallBack_) { + createMmapSucc = isSpe_ ? + CreateSpeMmap(fdItem, eventItem.attr) : CreateMmap(fdItem, eventItem.attr); + } + if (!createMmapSucc) { + printf("create mmap fail\n"); + HIPERF_HILOGI(MODULE_DEFAULT, "create mmap fail"); + return -1; + } + // update group leader + int groupFdCacheNum = groupFdCache; + if (groupFdCacheNum == -1) { + groupFdCache = fdItem.fd.Get(); + } + return 1; +} + bool PerfEvents::CreateFdEvents(void) { // must be some events , or will failed @@ -1167,57 +1222,12 @@ bool PerfEvents::CreateFdEvents(void) // one fd event group must match same cpu and same pid config (event can be // different) // clang-format off - UniqueFd fd = Open(eventItem.attr, pids_[ipid], cpus_[icpu], - groupFdCache[icpu][ipid], 0); - // clang-format on - if (fd < 0) { - if (errno == ESRCH) { - if (verboseReport_) { - printf("pid %d does not exist.\n", pids_[ipid]); - } - HLOGE("pid %d does not exist.\n", pids_[ipid]); - HIPERF_HILOGE(MODULE_DEFAULT, "[CreateFdEvents] pid %{public}d does not exist.", - pids_[ipid]); - continue; - } else { - // clang-format off - if (verboseReport_) { - char errInfo[ERRINFOLEN] = { 0 }; - strerror_r(errno, errInfo, ERRINFOLEN); - printf("%s event is not supported by the kernel on cpu %d. reason: %d:%s\n", - eventItem.configName.c_str(), cpus_[icpu], errno, errInfo); - } - char errInfo[ERRINFOLEN] = { 0 }; - strerror_r(errno, errInfo, ERRINFOLEN); - HLOGE("%s event is not supported by the kernel on cpu %d. reason: %d:%s\n", - eventItem.configName.c_str(), cpus_[icpu], errno, errInfo); - // clang-format on - break; // jump to next cpu - } - } - // after open successed , fill the result - // make a new FdItem - FdItem &fdItem = eventItem.fdItems.emplace_back(); - fdItem.fd = std::move(fd); - fdItem.cpu = cpus_[icpu]; - fdItem.pid = pids_[ipid]; - fdNumber++; - - // if sampling, mmap ring buffer - bool createMmapSucc = true; - if (recordCallBack_) { - createMmapSucc = isSpe_ ? - CreateSpeMmap(fdItem, eventItem.attr) : CreateMmap(fdItem, eventItem.attr); - } - if (!createMmapSucc) { - printf("create mmap fail\n"); - HIPERF_HILOGI(MODULE_DEFAULT, "create mmap fail"); + int ret = CreateFdEventsForEachPid(eventItem, icpu, ipid, + fdNumber, groupFdCache[icpu][ipid]); + if (ret == -1) { return false; - } - // update group leader - int groupFdCacheNum = groupFdCache[icpu][ipid]; - if (groupFdCacheNum == -1) { - groupFdCache[icpu][ipid] = fdItem.fd.Get(); + } else if (ret == 0) { + break; } } } @@ -1465,25 +1475,8 @@ static bool CompareRecordTime(const PerfEvents::MmapFd *left, const PerfEvents:: return left->timestamp > right->timestamp; } -void PerfEvents::ReadRecordsFromMmaps() +void PerfEvents::GetRecords(bool &enableFlag) { -#ifdef HIPERF_DEBUG_TIME - const auto readKenelStartTime = steady_clock::now(); -#endif - // get readable mmap at this time - for (auto &it : cpuMmap_) { - ssize_t dataSize = it.second.mmapPage->data_head - it.second.mmapPage->data_tail; - __sync_synchronize(); // this same as rmb in gcc, after reading mmapPage->data_head - if (dataSize <= 0) { - continue; - } - it.second.dataSize = dataSize; - MmapRecordHeap_.push_back(&(it.second)); - } - if (MmapRecordHeap_.empty()) { - return; - } - bool enableFlag = false; if (MmapRecordHeap_.size() > 1) { for (const auto &it : MmapRecordHeap_) { GetRecordFromMmap(*it); @@ -1512,7 +1505,6 @@ void PerfEvents::ReadRecordsFromMmaps() } } } - while (GetRecordFromMmap(*MmapRecordHeap_.front())) { bool auxEvent = false; u32 pid = 0; @@ -1525,6 +1517,28 @@ void PerfEvents::ReadRecordsFromMmaps() enableFlag = true; } } +} + +void PerfEvents::ReadRecordsFromMmaps() +{ +#ifdef HIPERF_DEBUG_TIME + const auto readKenelStartTime = steady_clock::now(); +#endif + // get readable mmap at this time + for (auto &it : cpuMmap_) { + ssize_t dataSize = it.second.mmapPage->data_head - it.second.mmapPage->data_tail; + __sync_synchronize(); // this same as rmb in gcc, after reading mmapPage->data_head + if (dataSize <= 0) { + continue; + } + it.second.dataSize = dataSize; + MmapRecordHeap_.push_back(&(it.second)); + } + if (MmapRecordHeap_.empty()) { + return; + } + bool enableFlag = false; + GetRecords(enableFlag); if (isSpe_ && enableFlag) { PerfEventsEnable(false); PerfEventsEnable(true); @@ -1865,70 +1879,74 @@ void PerfEvents::RecordLoop() } } +bool PerfEvents::GetStat(const steady_clock::time_point &startTime, steady_clock::time_point &nextReportTime, + milliseconds &usedTimeMsTick, __u64 &durationInSec, int64_t thresholdTimeInMs) +{ + const auto endTime = startTime + timeOut_; + // time check point + const auto thisTime = steady_clock::now(); + if (timeReport_ != milliseconds::zero()) { + // stat cmd + if (thisTime >= nextReportTime) { + usedTimeMsTick = duration_cast(thisTime - startTime); + durationInSec = usedTimeMsTick.count(); + auto lefTimeMsTick = duration_cast(endTime - thisTime); + if (reportPtr_ == nullptr) { + printf("\nReport at %" PRIu64 " ms (%" PRIu64 " ms left):\n", + static_cast(usedTimeMsTick.count()), + static_cast(lefTimeMsTick.count())); + } else { + fprintf(reportPtr_, "\nReport at %" PRIu64 " ms (%" PRIu64 " ms left):\n", + static_cast(usedTimeMsTick.count()), + static_cast(lefTimeMsTick.count())); + } + // end of comments + nextReportTime += timeReport_; + StatReport(durationInSec); + } + } + if (HaveTargetsExit(startTime)) { + return true; + } + if (thisTime >= endTime) { + usedTimeMsTick = duration_cast(thisTime - startTime); + durationInSec = usedTimeMsTick.count(); + if (reportPtr_ == nullptr) { + printf("Timeout exit (total %" PRIu64 " ms)\n", static_cast(usedTimeMsTick.count())); + } else { + fprintf(reportPtr_, "Timeout exit (total %" PRIu64 " ms)\n", + static_cast(usedTimeMsTick.count())); + } + if (trackedCommand_) { + trackedCommand_->Stop(); + } + return true; + } + // lefttime > 200ms sleep 100ms, else sleep 200us + uint64_t defaultSleepUs = 2 * HUNDREDS; // 200us + if (timeReport_ == milliseconds::zero() && (timeOut_.count() * THOUSANDS) > thresholdTimeInMs) { + milliseconds leftTimeMsTmp = duration_cast(endTime - thisTime); + if (leftTimeMsTmp.count() > thresholdTimeInMs) { + defaultSleepUs = HUNDREDS * THOUSANDS; // 100ms + } + } + std::this_thread::sleep_for(microseconds(defaultSleepUs)); + return false; +} + void PerfEvents::StatLoop() { // calc the time const auto startTime = steady_clock::now(); - const auto endTime = startTime + timeOut_; auto nextReportTime = startTime + timeReport_; milliseconds usedTimeMsTick {}; __u64 durationInSec = 0; int64_t thresholdTimeInMs = 2 * HUNDREDS; while (g_trackRunning) { - // time check point - const auto thisTime = steady_clock::now(); - if (timeReport_ != milliseconds::zero()) { - // stat cmd - if (thisTime >= nextReportTime) { - // only for log or debug? - usedTimeMsTick = duration_cast(thisTime - startTime); - durationInSec = usedTimeMsTick.count(); - auto lefTimeMsTick = duration_cast(endTime - thisTime); - if (reportPtr_ == nullptr) { - printf("\nReport at %" PRIu64 " ms (%" PRIu64 " ms left):\n", - static_cast(usedTimeMsTick.count()), - static_cast(lefTimeMsTick.count())); - } else { - fprintf(reportPtr_, "\nReport at %" PRIu64 " ms (%" PRIu64 " ms left):\n", - static_cast(usedTimeMsTick.count()), - static_cast(lefTimeMsTick.count())); - } - // end of comments - nextReportTime += timeReport_; - StatReport(durationInSec); - } - } - - if (HaveTargetsExit(startTime)) { + if (GetStat(startTime, endTime, nextReportTime, usedTimeMsTick, durationInSec, thresholdTimeInMs)) { break; } - - if (thisTime >= endTime) { - usedTimeMsTick = duration_cast(thisTime - startTime); - durationInSec = usedTimeMsTick.count(); - if (reportPtr_ == nullptr) { - printf("Timeout exit (total %" PRIu64 " ms)\n", static_cast(usedTimeMsTick.count())); - } else { - fprintf(reportPtr_, "Timeout exit (total %" PRIu64 " ms)\n", - static_cast(usedTimeMsTick.count())); - } - if (trackedCommand_) { - trackedCommand_->Stop(); - } - break; - } - - // lefttime > 200ms sleep 100ms, else sleep 200us - uint64_t defaultSleepUs = 2 * HUNDREDS; // 200us - if (timeReport_ == milliseconds::zero() - && (timeOut_.count() * THOUSANDS) > thresholdTimeInMs) { - milliseconds leftTimeMsTmp = duration_cast(endTime - thisTime); - if (leftTimeMsTmp.count() > thresholdTimeInMs) { - defaultSleepUs = HUNDREDS * THOUSANDS; // 100ms - } - } - std::this_thread::sleep_for(microseconds(defaultSleepUs)); } if (!g_trackRunning) { diff --git a/src/report_protobuf_file.cpp b/src/report_protobuf_file.cpp index 6ca1a69..c8a05df 100644 --- a/src/report_protobuf_file.cpp +++ b/src/report_protobuf_file.cpp @@ -254,9 +254,41 @@ bool ReportProtobufFileReader::CheckFileMagic() return true; } -bool ReportProtobufFileReader::Dump(const std::string fileName, ProtobufReadBack readBack) +int ReportProtobufFileReader::Dump(uint32_t &recordLength, ProtobufReadBack readBack) { const int defaultIndent = 0; + protpbufCodedInputStream_->ReadLittleEndian32(&recordLength); + if (recordLength != 0) { + PRINT_INDENT(defaultIndent, "record length:%u (%x)\n", recordLength, recordLength); + HiperfRecord record; + std::string recordBuf; + recordBuf.resize(recordLength); + if (!protpbufCodedInputStream_->ReadString(&recordBuf, recordLength)) { + printf("read record error\n"); + return -1; + } + if (!record.ParseFromString(recordBuf)) { + printf("parse format error\n"); + return -1; + } else { + if (readBack == nullptr) { + PRINT_INDENT(defaultIndent, "\n"); + Dump(record, defaultIndent); + } else { + readBack(record); + } + } + } else { + if (readBack == nullptr) { + printf("no more record\n"); + } + return 1; + } + return 0; +} + +bool ReportProtobufFileReader::Dump(const std::string fileName, ProtobufReadBack readBack) +{ fileName_ = fileName; try { protobufFileStream_->exceptions(std::ifstream::failbit | std::ifstream::badbit); @@ -271,31 +303,10 @@ bool ReportProtobufFileReader::Dump(const std::string fileName, ProtobufReadBack std::make_unique(protpbufInputStream_.get()); uint32_t recordLength = 0; do { - protpbufCodedInputStream_->ReadLittleEndian32(&recordLength); - if (recordLength != 0) { - PRINT_INDENT(defaultIndent, "record length:%u (%x)\n", recordLength, recordLength); - HiperfRecord record; - std::string recordBuf; - recordBuf.resize(recordLength); - if (!protpbufCodedInputStream_->ReadString(&recordBuf, recordLength)) { - printf("read record error\n"); - return false; - } - if (!record.ParseFromString(recordBuf)) { - printf("parse format error\n"); - return false; - } else { - if (readBack == nullptr) { - PRINT_INDENT(defaultIndent, "\n"); - Dump(record, defaultIndent); - } else { - readBack(record); - } - } - } else { - if (readBack == nullptr) { - printf("no more record\n"); - } + int ret = Dump(recordLength, readBack); + if (ret == -1) { + return false; + } else if (ret == 1) { break; } } while (recordLength != 0); diff --git a/src/subcommand_dump.cpp b/src/subcommand_dump.cpp index c2e6cc9..76c4391 100644 --- a/src/subcommand_dump.cpp +++ b/src/subcommand_dump.cpp @@ -519,6 +519,52 @@ void SubCommandDump::PrintFeatureEventdesc(const int indent, PRINT_INDENT(indent + INDENT_TWO, "\n"); } +void SubCommandDump::DumpFeatureSection(const int indent, const std::unique_ptr &featureSection) +{ + PRINT_INDENT(indent + 1, "feature %d:%s content: \n", featureSection.get()->featureId_, + PerfFileSection::GetFeatureName(featureSection.get()->featureId_).c_str()); + if (reader_->IsFeatrureStringSection(featureSection.get()->featureId_)) { + const PerfFileSectionString *sectionString = + static_cast(featureSection.get()); + PRINT_INDENT(indent + INDENT_TWO, "%s\n", sectionString->ToString().c_str()); + return; + } else if (featureSection.get()->featureId_ == FEATURE::EVENT_DESC) { + PrintFeatureEventdesc( + indent, *static_cast(featureSection.get())); + return; + } else if (featureSection.get()->featureId_ == FEATURE::HIPERF_FILES_SYMBOL) { + const PerfFileSectionSymbolsFiles *sectionSymbolsFiles = + static_cast(featureSection.get()); + if (sectionSymbolsFiles != nullptr) { + PRINT_INDENT(indent + INDENT_TWO, "SymbolFiles:%zu\n", + sectionSymbolsFiles->symbolFileStructs_.size()); + + int fileid = 0; + for (auto &symbolFileStruct : sectionSymbolsFiles->symbolFileStructs_) { + PRINT_INDENT(indent + INDENT_TWO, "\n"); + PRINT_INDENT(indent + INDENT_TWO, "fileid:%d\n", fileid); + fileid++; + // symbol file info + PrintSymbolFile(indent, symbolFileStruct); + } + } else { + PRINT_INDENT(indent + INDENT_TWO, "get SymbolFiles failed\n"); + } + return; + } else if (featureSection.get()->featureId_ == FEATURE::HIPERF_FILES_UNISTACK_TABLE) { + const PerfFileSectionUniStackTable *sectioniStackTable = + static_cast(const_cast(featureSection.get())); + if (sectioniStackTable != nullptr) { + DumpUniqueStackTableNode(indent + 1, *sectioniStackTable); + } else { + PRINT_INDENT(indent + INDENT_TWO, "get StackTable failed\n"); + } + return; + } else { + PRINT_INDENT(indent + INDENT_TWO, "not support dump this feature(%d).\n", featureSection.get()->featureId_); + } +} + void SubCommandDump::DumpFeaturePortion(const int indent) { PRINT_INDENT(indent, "\n ==== features ====\n"); @@ -534,48 +580,7 @@ void SubCommandDump::DumpFeaturePortion(const int indent) PRINT_INDENT(indent, "\n ==== feature sections ====\n"); for (auto &featureSection : featureSections) { - PRINT_INDENT(indent + 1, "feature %d:%s content: \n", featureSection.get()->featureId_, - PerfFileSection::GetFeatureName(featureSection.get()->featureId_).c_str()); - if (reader_->IsFeatrureStringSection(featureSection.get()->featureId_)) { - const PerfFileSectionString *sectionString = - static_cast(featureSection.get()); - PRINT_INDENT(indent + INDENT_TWO, "%s\n", sectionString->ToString().c_str()); - continue; - } else if (featureSection.get()->featureId_ == FEATURE::EVENT_DESC) { - PrintFeatureEventdesc( - indent, *static_cast(featureSection.get())); - continue; - } else if (featureSection.get()->featureId_ == FEATURE::HIPERF_FILES_SYMBOL) { - const PerfFileSectionSymbolsFiles *sectionSymbolsFiles = - static_cast(featureSection.get()); - if (sectionSymbolsFiles != nullptr) { - PRINT_INDENT(indent + INDENT_TWO, "SymbolFiles:%zu\n", - sectionSymbolsFiles->symbolFileStructs_.size()); - - int fileid = 0; - for (auto &symbolFileStruct : sectionSymbolsFiles->symbolFileStructs_) { - PRINT_INDENT(indent + INDENT_TWO, "\n"); - PRINT_INDENT(indent + INDENT_TWO, "fileid:%d\n", fileid); - fileid++; - // symbol file info - PrintSymbolFile(indent, symbolFileStruct); - } - } else { - PRINT_INDENT(indent + INDENT_TWO, "get SymbolFiles failed\n"); - } - continue; - } else if (featureSection.get()->featureId_ == FEATURE::HIPERF_FILES_UNISTACK_TABLE) { - const PerfFileSectionUniStackTable *sectioniStackTable = - static_cast(const_cast(featureSection.get())); - if (sectioniStackTable != nullptr) { - DumpUniqueStackTableNode(indent + 1, *sectioniStackTable); - } else { - PRINT_INDENT(indent + INDENT_TWO, "get StackTable failed\n"); - } - continue; - } else { - PRINT_INDENT(indent + INDENT_TWO, "not support dump this feature(%d).\n", featureSection.get()->featureId_); - } + DumpFeatureSection(indent, featureSection); } } diff --git a/test/unittest/common/native/debug_logger_test.cpp b/test/unittest/common/native/debug_logger_test.cpp index ce0294b..6de5a44 100644 --- a/test/unittest/common/native/debug_logger_test.cpp +++ b/test/unittest/common/native/debug_logger_test.cpp @@ -288,6 +288,25 @@ HWTEST_F(DebugLoggerTest, EnableHiLog, TestSize.Level1) DebugLogger::GetInstance()->EnableHiLog(false); #endif } + +/** + * @tc.name: PrintHilog + * @tc.desc: + * @tc.type: FUNC + */ +HWTEST_F(DebugLoggerTest, PrintHilog, TestSize.Level1) +{ +#if is_ohos + DebugLogger::GetInstance()->EnableHiLog(true); + + LogLevelTest(static_cast(LEVEL_MUCH)); + std::string test = "test string"; + int ret = 0; + const auto startTime = std::chrono::steady_clock::now(); + DebugLogger::GetInstance()->PrintHilog(test, startTime, ret); + EXPECT_EQ(ret >= 47, true); // 47: return from hilog, msg length +#endif +} } // namespace HiPerf } // namespace Developtools } // namespace OHOS diff --git a/test/unittest/common/native/option_test.cpp b/test/unittest/common/native/option_test.cpp index f0f4385..bf52836 100644 --- a/test/unittest/common/native/option_test.cpp +++ b/test/unittest/common/native/option_test.cpp @@ -640,6 +640,21 @@ HWTEST_F(OptionTest, TestGetValueFromStringUINT64_T03, TestSize.Level3) // 18446744073709551616: UINT64_T_MAX +1 EXPECT_EQ(Option::GetValueFromString("18446744073709551616", OPTION_NAME, value), false); } + +/** + * @tc.name: GetValue + * @tc.desc: + * @tc.type: FUNC + */ +HWTEST_F(OptionTest, GetValue, TestSize.Level1) +{ + std::string stringValue; + std::vector args = {OPTION_NAME, OPTION_STRING_VALUE}; + std::string localValues = OPTION_STRING_VALUE; + // one arg + EXPECT_EQ(Option::GetValue(args = ONE_ARGS_WITH_VALUE, OPTION_NAME, stringValue, localValues), true); + EXPECT_EQ(stringValue, OPTION_STRING_VALUE); +} } // namespace HiPerf } // namespace Developtools } // namespace OHOS diff --git a/test/unittest/common/native/perf_events_test.cpp b/test/unittest/common/native/perf_events_test.cpp index 0b3df9e..ac36638 100644 --- a/test/unittest/common/native/perf_events_test.cpp +++ b/test/unittest/common/native/perf_events_test.cpp @@ -564,6 +564,41 @@ HWTEST_F(PerfEventsTest, SetConfig1, TestSize.Level2) event.SetConfig(speOptMap); EXPECT_EQ(event.config_, config); } + +HWTEST_F(PerfEventsTest, GetStat, TestSize.Level2) +{ + PerfEvents event; + // prepare + event.SetMmapPages(DEFAULT_SAMPLE_MMAPAGE); + event.SetRecordCallBack(nullptr); + event.SetStatCallBack(StatCount); + std::vector selectCpus_; + event.SetCpu(selectCpus_); + std::vector pids; + event.SetPid(pids); + const unsigned int frequency = 1000; + event.SetSampleFrequency(frequency); + event.SetSystemTarget(true); + event.SetTimeOut(DEFAULT_TRACKING_TIME); + event.SetInherit(false); + std::vector trackedCommand_ {"ls"}; + event.SetTrackedCommand(trackedCommand_); + event.AddDefaultEvent(PERF_TYPE_SOFTWARE); + event.AddDefaultEvent(PERF_TYPE_HARDWARE); + + ASSERT_EQ(event.PrepareTracking(), true); + + std::thread runThread(RunTrack, std::ref(event)); + std::vector testThreads; + RunTestThreads(testThreads); + + std::this_thread::sleep_for(TEST_TIME); // wait for clearing mmap buffer + EXPECT_EQ(event.StopTracking(), true); + runThread.join(); + for (std::thread &t : testThreads) { + t.join(); + } +} } // namespace HiPerf } // namespace Developtools } // namespace OHOS diff --git a/test/unittest/common/native/report_protobuf_file_test.cpp b/test/unittest/common/native/report_protobuf_file_test.cpp index 197bbab..24377bc 100644 --- a/test/unittest/common/native/report_protobuf_file_test.cpp +++ b/test/unittest/common/native/report_protobuf_file_test.cpp @@ -353,6 +353,24 @@ HWTEST_F(ReportProtobufFileTest, ProcessSampleRecord, TestSize.Level1) EXPECT_EQ(expectRecord, 1); } + +/** + * @tc.name: ReadCallBackWithNull + * @tc.desc: + * @tc.type: FUNC + */ +HWTEST_F(ReportProtobufFileTest, ReadCallBackWithNull, TestSize.Level2) +{ + std::string fileName = "perf.proto"; + std::vector configNames = {"config1", "config2", "config3"}; + std::string workloadCmd = "workcommand"; + + ASSERT_EQ(protobufOutputFileWriter_->Create(fileName), true); + protobufOutputFileWriter_->ProcessReportInfo(configNames, workloadCmd); + protobufOutputFileWriter_->Close(); + + EXPECT_EQ(protobufInputFileReader_->Dump(fileName, nullptr), true); +} } // namespace HiPerf } // namespace Developtools } // namespace OHOS -- Gitee