diff --git a/faultloggerd.gni b/faultloggerd.gni index ac10b33739ce7b29d14d5eded7f2233d12fc73ca..85c87e68bdd4368baeda150ad396dd120b6f1466 100644 --- a/faultloggerd.gni +++ b/faultloggerd.gni @@ -22,7 +22,7 @@ declare_args() { has_libunwindstack = false processdump_minidebuginfo_enable = true faultloggerd_hisysevent_enable = false - faultloggerd_liteperf_enable = false + faultloggerd_liteperf_enable = true processdump_parse_lock_owner_enable = true faultloggerd_enable_build_targets = true if (defined(global_parts_info)) { diff --git a/interfaces/common/dfx_lperf.h b/interfaces/common/dfx_lperf.h index 76d0f13a0d075ca4b4381088d64ec0e95ef67d8b..539080316e0cc1abdbe7487ca26cd48591217a67 100644 --- a/interfaces/common/dfx_lperf.h +++ b/interfaces/common/dfx_lperf.h @@ -20,8 +20,13 @@ namespace OHOS { namespace HiviewDFX { namespace { -constexpr int MAX_PERF_TIDS_SIZE = 5; -const char* const LITE_PERF_SPLIT = "tid: "; +constexpr int MIN_SAMPLE_TIDS = 1; +constexpr int MAX_SAMPLE_TIDS = 10; +constexpr int MIN_SAMPLE_FREQUENCY = 1; +constexpr int MAX_SAMPLE_FREQUENCY = 100; +constexpr int MIN_STOP_SECONDS = 1; +constexpr int MAX_STOP_SECONDS = 10000; +constexpr int DUMP_LITEPERF_TIMEOUT = 5000; } /** @@ -30,10 +35,9 @@ const char* const LITE_PERF_SPLIT = "tid: "; */ struct LitePerfParam { int pid; - int tids[MAX_PERF_TIDS_SIZE]; + int tids[MAX_SAMPLE_TIDS]; int freq = 0; int durationMs = 0; - bool parseMiniDebugInfo = false; }; } // namespace HiviewDFX } // namespace OHOS diff --git a/interfaces/common/dfx_socket_request.h b/interfaces/common/dfx_socket_request.h index 918dc7f5ed7e23a118f5857a928b5070069a8572..a57c76985470ac76ff3db5d2ca91ac66e980e6aa 100644 --- a/interfaces/common/dfx_socket_request.h +++ b/interfaces/common/dfx_socket_request.h @@ -18,6 +18,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -182,6 +183,19 @@ typedef struct PipFdRequestData { int8_t pipeType; } __attribute__((packed)) PipFdRequestData; +typedef struct LitePerfFdRequestData { + /** request data head **/ + RequestDataHead head; + /** process id */ + int32_t pid; + /** user id */ + uid_t uid; + /** type of pipe */ + int8_t pipeType; + /** timeout of request */ + int32_t timeout; +} __attribute__((packed)) LitePerfFdRequestData; + /** * @brief request information */ diff --git a/interfaces/innerkits/dump_catcher/BUILD.gn b/interfaces/innerkits/dump_catcher/BUILD.gn index bdf127e7eb748804dcd166e9d46a857a2889e967..c54758615072474ac77ee731d98947434e449eb0 100644 --- a/interfaces/innerkits/dump_catcher/BUILD.gn +++ b/interfaces/innerkits/dump_catcher/BUILD.gn @@ -81,6 +81,9 @@ if (defined(ohos_lite)) { "$faultloggerd_interfaces_path/innerkits/backtrace:libbacktrace_local", "$faultloggerd_interfaces_path/innerkits/faultloggerd_client:libfaultloggerd", "$faultloggerd_interfaces_path/innerkits/procinfo:libdfx_procinfo", + "$faultloggerd_path/interfaces/innerkits/signal_handler:dfx_signalhandler", + "$faultloggerd_interfaces_path/innerkits/stack_printer:libstack_printer", + "$faultloggerd_interfaces_path/innerkits/unwinder:libunwinder", ] external_deps = [ @@ -97,5 +100,6 @@ if (defined(ohos_lite)) { ] part_name = "faultloggerd" subsystem_name = "hiviewdfx" + kernel_permission_path = "./encaps.json" } } diff --git a/interfaces/innerkits/dump_catcher/encaps.json b/interfaces/innerkits/dump_catcher/encaps.json new file mode 100644 index 0000000000000000000000000000000000000000..a859cec83abf2b563d8af32fcad7b70dfdb7bb92 --- /dev/null +++ b/interfaces/innerkits/dump_catcher/encaps.json @@ -0,0 +1,6 @@ +{ + "encaps": { + "ohos.encaps.count": 1, + "ohos.encaps.fork.source": 2 + } +} \ No newline at end of file diff --git a/interfaces/innerkits/dump_catcher/lite_perf.cpp b/interfaces/innerkits/dump_catcher/lite_perf.cpp index 50e9012218c7c0bb75596cda7612f6b29840a8ca..c704066b27b0fbee08ccc80537abcc75829e9e0b 100644 --- a/interfaces/innerkits/dump_catcher/lite_perf.cpp +++ b/interfaces/innerkits/dump_catcher/lite_perf.cpp @@ -29,11 +29,16 @@ #include #include "dfx_define.h" #include "dfx_dump_res.h" +#include "dfx_dumprequest.h" #include "dfx_log.h" #include "dfx_lperf.h" #include "dfx_util.h" #include "faultloggerd_client.h" +#include "procinfo.h" #include "smart_fd.h" +#include "stack_printer.h" +#include "unwinder.h" +#include "unwinder_config.h" namespace OHOS { namespace HiviewDFX { @@ -42,8 +47,6 @@ namespace { #define LOG_DOMAIN 0xD002D11 #undef LOG_TAG #define LOG_TAG "DfxLitePerf" - -static constexpr int DUMP_LITEPERF_TIMEOUT_DELAY = 3000; } class LitePerf::Impl { @@ -53,20 +56,26 @@ public: int FinishProcessStackSampling(); private: - int ExecDump(const std::vector& tids, int freq, int durationMs, bool parseMiniDebugInfo); + bool IsValidParam(const std::vector& tids, int freq, int durationMs); + int ExecDump(const std::vector& tids, int freq, int durationMs); + bool InitDumpParam(const std::vector& tids, int freq, int durationMs, LitePerfParam& lperf); bool ExecDumpPipe(const int (&pipefd)[2], const LitePerfParam& lperf); + void FinishDump(); int DumpPoll(const int (&pipeFds)[2], const int timeout); bool HandlePollEvents(const struct pollfd (&readFds)[2], const int (&pipeFds)[2], bool& bPipeConnect, int& pollRet); bool DoReadBuf(int fd); bool DoReadRes(int fd, int& pollRet); - bool ParseSampleStacks(const std::string& source); + bool ParseSampleStacks(const std::string& datas); std::string bufMsg_; std::string resMsg_; std::map stackMaps_{}; std::mutex mutex_; std::atomic isRunning_{false}; + + bool defaultEnableDebugInfo_ {false}; + bool enableDebugInfoSymbolic_ {false}; }; LitePerf::LitePerf() : impl_(std::make_shared()) @@ -92,8 +101,8 @@ int LitePerf::Impl::StartProcessStackSampling(const std::vector& tids, int { DFXLOGI("StartProcessStackSampling."); int res = 0; - if (tids.size() > MAX_PERF_TIDS_SIZE || durationMs < 1) { - DFXLOGE("Failed to tids size."); + if (!IsValidParam(tids, freq, durationMs)) { + DFXLOGE("Invalid stack sampling param."); return -1; } bool expected = false; @@ -101,35 +110,51 @@ int LitePerf::Impl::StartProcessStackSampling(const std::vector& tids, int DFXLOGW("Process is being sampling."); return -1; } + enableDebugInfoSymbolic_ = parseMiniDebugInfo; + int timeout = durationMs + DUMP_LITEPERF_TIMEOUT; int pipeReadFd[] = {-1, -1}; - int req = RequestLitePerfPipeFd(FaultLoggerPipeType::PIPE_FD_READ, pipeReadFd); + int req = RequestLitePerfPipeFd(FaultLoggerPipeType::PIPE_FD_READ, pipeReadFd, + static_cast(timeout / 1000)); if (req != ResponseCode::REQUEST_SUCCESS) { DFXLOGE("Failed to request liteperf pipe read."); isRunning_.store(false); - res = -1; - return res; + return -1; } do { - if (ExecDump(tids, freq, durationMs, parseMiniDebugInfo) < 0) { + if (ExecDump(tids, freq, durationMs) < 0) { res = -1; break; } - int timeout = durationMs + DUMP_LITEPERF_TIMEOUT_DELAY; if (DumpPoll(pipeReadFd, timeout) < 0) { res = -1; break; } } while (false); + FinishDump(); + return res; +} - req = RequestLitePerfDelPipeFd(); +bool LitePerf::Impl::IsValidParam(const std::vector& tids, int freq, int durationMs) +{ + if (tids.size() < MIN_SAMPLE_TIDS || tids.size() > MAX_SAMPLE_TIDS || + freq < MIN_SAMPLE_FREQUENCY || freq > MAX_SAMPLE_FREQUENCY || + durationMs < MIN_STOP_SECONDS || durationMs > MAX_STOP_SECONDS) { + return false; + } + return true; +} + +void LitePerf::Impl::FinishDump() +{ + DFX_RestoreDumpableState(); + int req = RequestLitePerfDelPipeFd(); if (req != ResponseCode::REQUEST_SUCCESS) { DFXLOGE("Failed to request liteperf pipe delete."); } isRunning_.store(false); - return res; } int LitePerf::Impl::DumpPoll(const int (&pipeFds)[2], const int timeout) @@ -175,7 +200,8 @@ int LitePerf::Impl::DumpPoll(const int (&pipeFds)[2], const int timeout) break; } } while (isContinue); - return pollRet; + DFXLOGI("%{public}s :: %{public}s", __func__, resMsg_.c_str()); + return pollRet == DUMP_POLL_OK ? 0 : -1; } bool LitePerf::Impl::HandlePollEvents(const struct pollfd (&readFds)[2], const int (&pipeFds)[2], @@ -239,16 +265,32 @@ bool LitePerf::Impl::DoReadRes(int fd, int& pollRet) return true; } -int LitePerf::Impl::ExecDump(const std::vector& tids, int freq, int durationMs, bool parseMiniDebugInfo) +bool LitePerf::Impl::InitDumpParam(const std::vector& tids, int freq, int durationMs, LitePerfParam& lperf) { - LitePerfParam lperf; + if (DFX_SetDumpableState() == false) { + DFXLOGE("%{public}s :: Failed to set dumpable.", __func__); + return false; + } + lperf.pid = getpid(); - for (size_t i = 0; i < tids.size() && i < MAX_PERF_TIDS_SIZE; ++i) { + for (size_t i = 0; i < tids.size() && i < MAX_SAMPLE_TIDS; ++i) { + if (tids[i] <= 0 || !IsThreadInPid(lperf.pid, tids[i])) { + DFXLOGW("%{public}s :: tid(%{public}d) error", __func__, tids[i]); + continue; + } lperf.tids[i] = tids[i]; } lperf.freq = freq; lperf.durationMs = durationMs; - lperf.parseMiniDebugInfo = parseMiniDebugInfo; + return true; +} + +int LitePerf::Impl::ExecDump(const std::vector& tids, int freq, int durationMs) +{ + static LitePerfParam lperf; + if (!InitDumpParam(tids, freq, durationMs, lperf)) { + return -1; + } pid_t pid = 0; pid = vfork(); @@ -315,33 +357,23 @@ bool LitePerf::Impl::ExecDumpPipe(const int (&pipefd)[2], const LitePerfParam& l return false; } -bool LitePerf::Impl::ParseSampleStacks(const std::string& source) +bool LitePerf::Impl::ParseSampleStacks(const std::string& datas) { - if (source.empty()) { + if (datas.empty()) { return false; } std::unique_lock lck(mutex_); if (stackMaps_.empty()) { - std::vector stacks; - OHOS::SplitStr(source, LITE_PERF_SPLIT, stacks); - for (auto& str : stacks) { - size_t pos = str.find("\n"); - if (pos == std::string::npos) { - DFXLOGW("error str: %s", str.c_str()); - continue; - } - - std::string tidStr = str.substr(0, pos); - int tmpTid; - int ret = sscanf_s(tidStr.c_str(), "%d", &tmpTid); - if (ret != 1) { - DFXLOGE("sscanf %{public}s failed.", tidStr.c_str()); - continue; - } - std::string stack = str.substr(pos + 1); - DFXLOGD("emplace tid:%{public}d, str: %s", tmpTid, stack.c_str()); - stackMaps_.emplace(tmpTid, std::move(stack)); + auto unwinder = std::make_shared(false); + auto maps = DfxMaps::Create(); + defaultEnableDebugInfo_ = UnwinderConfig::GetEnableMiniDebugInfo(); + UnwinderConfig::SetEnableMiniDebugInfo(enableDebugInfoSymbolic_); + std::istringstream iss(datas); + auto frameMap = StackPrinter::DeserializeSampledFrameMap(iss); + for (const auto& pair : frameMap) { + auto stack = StackPrinter::PrintTreeStackBySampledStack(pair.second, false, unwinder, maps); + stackMaps_.emplace(pair.first, std::move(stack)); } } return (stackMaps_.size() > 0); @@ -368,6 +400,7 @@ int LitePerf::Impl::FinishProcessStackSampling() { DFXLOGI("FinishProcessStackSampling."); std::unique_lock lck(mutex_); + UnwinderConfig::SetEnableMiniDebugInfo(defaultEnableDebugInfo_); stackMaps_.clear(); bufMsg_.clear(); resMsg_.clear(); diff --git a/interfaces/innerkits/faultloggerd_client/faultloggerd_client.cpp b/interfaces/innerkits/faultloggerd_client/faultloggerd_client.cpp index be4436e4fd6dc0a30c554a5cb235ef8b30dc4d26..c342b323409db68e29739419b9c0a682de14906b 100644 --- a/interfaces/innerkits/faultloggerd_client/faultloggerd_client.cpp +++ b/interfaces/innerkits/faultloggerd_client/faultloggerd_client.cpp @@ -131,7 +131,7 @@ int32_t RequestSdkDump(int32_t pid, int32_t tid, int (&pipeReadFd)[2], bool isJs #endif } -int32_t RequestLitePerfPipeFd(int32_t pipeType, int (&pipeFd)[2]) +int32_t RequestLitePerfPipeFd(int32_t pipeType, int (&pipeFd)[2], int timeout) { #ifndef is_ohos_lite if (pipeType < FaultLoggerPipeType::PIPE_FD_READ || pipeType > FaultLoggerPipeType::PIPE_FD_DELETE) { @@ -139,10 +139,12 @@ int32_t RequestLitePerfPipeFd(int32_t pipeType, int (&pipeFd)[2]) return ResponseCode::DEFAULT_ERROR_CODE; } DFXLOGI("%{public}s.%{public}s :: pipeType: %{public}d.", FAULTLOGGERD_CLIENT_TAG, __func__, pipeType); - PipFdRequestData request{}; - request.pipeType = static_cast(pipeType); + LitePerfFdRequestData request{}; FillRequestHeadData(request.head, FaultLoggerClientType::PIPE_FD_LITEPERF_CLIENT); + request.pipeType = static_cast(pipeType); request.pid = getpid(); + request.uid = getuid(); + request.timeout = timeout; SocketRequestData socketRequestData = {&request, sizeof(request)}; SocketFdData socketFdData = {pipeFd, PIPE_NUM_SZ}; return SendRequestToServer(GetSocketName().c_str(), socketRequestData, CRASHDUMP_SOCKET_TIMEOUT, &socketFdData); @@ -154,10 +156,11 @@ int32_t RequestLitePerfPipeFd(int32_t pipeType, int (&pipeFd)[2]) int32_t RequestLitePerfDelPipeFd() { #ifndef is_ohos_lite - PipFdRequestData request{}; + LitePerfFdRequestData request{}; FillRequestHeadData(request.head, FaultLoggerClientType::PIPE_FD_LITEPERF_CLIENT); request.pipeType = FaultLoggerPipeType::PIPE_FD_DELETE; request.pid = getpid(); + request.uid = getuid(); return SendRequestToServer(GetSocketName().c_str(), {&request, sizeof(request)}, SDKDUMP_SOCKET_TIMEOUT); #else return ResponseCode::DEFAULT_ERROR_CODE; diff --git a/interfaces/innerkits/faultloggerd_client/include/faultloggerd_client.h b/interfaces/innerkits/faultloggerd_client/include/faultloggerd_client.h index 4c26535306424324695442fa784b898769197942..5cc0862db7fac84919bad9e0ad098b6fe9efd612 100644 --- a/interfaces/innerkits/faultloggerd_client/include/faultloggerd_client.h +++ b/interfaces/innerkits/faultloggerd_client/include/faultloggerd_client.h @@ -62,11 +62,10 @@ int32_t RequestFileDescriptorEx(struct FaultLoggerdRequest* request); * pipeFd[1] to transfer backtrace result * @return if succeed return 0, otherwise return -1 */ -int32_t RequestLitePerfPipeFd(int32_t pipeType, int (&pipeFd)[2]); +int32_t RequestLitePerfPipeFd(int32_t pipeType, int (&pipeFd)[2], int timeout); /** * @brief request delete lite perf file descriptor - * @param pid process id of request pipe * @return if succeed return 0, otherwise return the error code */ int32_t RequestLitePerfDelPipeFd(); diff --git a/interfaces/innerkits/signal_handler/dfx_dumprequest.c b/interfaces/innerkits/signal_handler/dfx_dumprequest.c index 798823dfdb8a6699a6631e220a3c58256829b21a..79683162ffd320d0065a6930e2732950caa51183 100644 --- a/interfaces/innerkits/signal_handler/dfx_dumprequest.c +++ b/interfaces/innerkits/signal_handler/dfx_dumprequest.c @@ -72,6 +72,10 @@ static struct ProcessDumpRequest *g_request = NULL; static long g_blockExit = 0; static long g_vmRealPid = 0; static long g_unwindResult = 0; +static int g_dumpCount = 0; +static int g_dumpState = 0; +static pthread_mutex_t g_dumpMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutexattr_t g_dumpAttr; enum PIPE_FD_TYPE { WRITE_TO_DUMP, @@ -105,6 +109,19 @@ static bool ReadProcessDumpGetRegsMsg(void); DumpHiTraceIdStruct HiTraceChainGetId() __attribute__((weak)); #endif +void __attribute__((constructor)) InitMutex(void) +{ + pthread_mutexattr_init(&g_dumpAttr); + pthread_mutexattr_settype(&g_dumpAttr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&g_dumpMutex, &g_dumpAttr); +} + +void __attribute__((destructor)) DeinitMutex(void) +{ + pthread_mutexattr_destroy(&g_dumpAttr); + pthread_mutex_destroy(&g_dumpMutex); +} + static void ResetFlags(void) { g_unwindResult = 0; @@ -275,13 +292,39 @@ static pid_t ForkBySyscall(void) #endif } +bool DFX_SetDumpableState(void) +{ + pthread_mutex_lock(&g_dumpMutex); + if (g_dumpCount == 0) { + g_dumpState = prctl(PR_GET_DUMPABLE); + if (prctl(PR_SET_DUMPABLE, 1) != 0) { + DFXLOGE("Failed to set dumpable, errno(%{public}d).", errno); + pthread_mutex_unlock(&g_dumpMutex); + return false; + } + } + ++g_dumpCount; + pthread_mutex_unlock(&g_dumpMutex); + return true; +} + +void DFX_RestoreDumpableState(void) +{ + pthread_mutex_lock(&g_dumpMutex); + if (g_dumpCount > 0) { + --g_dumpCount; + if (g_dumpCount == 0) { + prctl(PR_SET_DUMPABLE, g_dumpState); + } + } + pthread_mutex_unlock(&g_dumpMutex); +} + static bool SetDumpState(void) { - if (prctl(PR_SET_DUMPABLE, 1) != 0) { - DFXLOGE("Failed to set dumpable, errno(%{public}d).", errno); + if (DFX_SetDumpableState() == false) { return false; } - if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) != 0) { if (errno != EINVAL) { DFXLOGE("Failed to set ptracer, errno(%{public}d).", errno); @@ -291,9 +334,9 @@ static bool SetDumpState(void) return true; } -static void RestoreDumpState(int prevState, bool isTracerStatusModified) +static void RestoreDumpState(bool isTracerStatusModified) { - prctl(PR_SET_DUMPABLE, prevState); + DFX_RestoreDumpableState(); if (isTracerStatusModified == true) { prctl(PR_SET_PTRACER, 0); } @@ -563,7 +606,6 @@ static void ReadUnwindFinishMsg(int signo) static int ProcessDump(int signo) { - int prevDumpableStatus = prctl(PR_GET_DUMPABLE); bool isTracerStatusModified = SetDumpState(); if (!IsDumpSignal(signo)) { ResetFlags(); @@ -600,7 +642,7 @@ static int ProcessDump(int signo) ReadUnwindFinishMsg(signo); } while (false); - RestoreDumpState(prevDumpableStatus, isTracerStatusModified); + RestoreDumpState(isTracerStatusModified); return 0; } diff --git a/interfaces/innerkits/signal_handler/include/dfx_dumprequest.h b/interfaces/innerkits/signal_handler/include/dfx_dumprequest.h index 026506e628d4d4e1b77aa1295f23b85692ce32c4..0e19989f0948333f65a4ae9ba98c21a37046333c 100644 --- a/interfaces/innerkits/signal_handler/include/dfx_dumprequest.h +++ b/interfaces/innerkits/signal_handler/include/dfx_dumprequest.h @@ -23,6 +23,9 @@ extern "C" { void DfxDumpRequest(int signo, struct ProcessDumpRequest *request); +bool DFX_SetDumpableState(void); +void DFX_RestoreDumpableState(void); + #ifdef __cplusplus } #endif diff --git a/interfaces/innerkits/signal_handler/libdfx_signalhandler.map b/interfaces/innerkits/signal_handler/libdfx_signalhandler.map index 2b00ea5b99a0638244e635919bb0340d19e32b16..9d8a0e24b20a0de321986d07c3f4ed59a5e09d8b 100644 --- a/interfaces/innerkits/signal_handler/libdfx_signalhandler.map +++ b/interfaces/innerkits/signal_handler/libdfx_signalhandler.map @@ -8,6 +8,8 @@ DFX_SetCrashObj; DFX_ResetCrashObj; DFX_SetCrashLogConfig; + DFX_SetDumpableState; + DFX_RestoreDumpableState; }; local: *; diff --git a/interfaces/innerkits/stack_printer/include/stack_printer.h b/interfaces/innerkits/stack_printer/include/stack_printer.h index 9bbed6abd2da016b10042f21dd21f53b94a1de3c..8ef75ecca38ba52e920b030f10ca60cfadd180a8 100644 --- a/interfaces/innerkits/stack_printer/include/stack_printer.h +++ b/interfaces/innerkits/stack_printer/include/stack_printer.h @@ -1,17 +1,17 @@ /* -* Copyright (c) 2025 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. -*/ + * Copyright (c) 2025 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. + */ #ifndef RELIABILITY_STACK_PRINTER_H #define RELIABILITY_STACK_PRINTER_H @@ -27,24 +27,126 @@ struct TimeStampedPcs { std::vector pcVec; }; +struct SampledFrame { + int indent {0}; // the indent of this frame. + int count {0}; // the count of this frame in the serial stacks. + int level {0}; // the level of this frame in its stack call chain. + uintptr_t pc {0}; // pc of this stack frame. + bool isLeaf {false}; // whether is leaf node in the tree format stack string. + std::vector timestamps; // timestamps of this stack sampled. + + friend std::ostream& operator<<(std::ostream& os, const SampledFrame& frame); + friend std::istream& operator>>(std::istream& is, SampledFrame& frame); +}; + class StackPrinter final { public: StackPrinter(); StackPrinter(const StackPrinter& other) = delete; StackPrinter& operator=(const StackPrinter& other) = delete; + /** + * @brief Initialize the unique_stack_table, which used to aggregation the stack pcs. + * + * @param pid the pid of the process. + * @param size the size of unique_stack_table to be initialized. + * @param name the name of unique_stack_table to be initialized, default unique_stack_table. + * @return return true if success, otherwise return false. + */ bool InitUniqueTable(pid_t pid, uint32_t size, std::string name = "unique_stack_table"); - bool PutPcsInTable(const std::vector& pcs, uint64_t snapshotTime); + /** + * @brief Put the pcs into unique_stack_table to generate stackId. + * + * @param pcs the vector of the sampled stack pcs. + * @param tid the tid of the sampled thread. + * @param snapshotTime the timestamp of the stack sampled. + * @return return true if success, otherwise return false. + */ + bool PutPcsInTable(const std::vector& pcs, int tid, uint64_t snapshotTime); + + /** + * @brief Set the unwind info to the StackPrinter. + * + * @param unwinder the shared_ptr of unwinder. + * @param maps the shared_ptr of process maps. + * @return void. + */ void SetUnwindInfo(const std::shared_ptr& unwinder, const std::shared_ptr& maps); + + /** + * @brief Get the sampled stack string listed by time order. + * + * @param timeStampedPcsVec the vector of sampled stack pcs with its timestamp. + * @return the string of the sampled stack, listed by time order. + */ std::string GetFullStack(const std::vector& timeStampedPcsVec); - std::string GetTreeStack(bool printTimes = false, uint64_t beginTime = 0, uint64_t endTime = 0); - std::string GetHeaviestStack(uint64_t beginTime = 0, uint64_t endTime = 0); + + /** + * @brief Get the SampledFrames with tids into map, which can be serialize to bytes and deserialize back to map. + * + * @param beginTime the begin time of the time interval to filter the sampled stack, default 0. + * @param endTime the end time of the time interval to filter sampled stack, default 0. + * @return the map of the SampledFrame vector of each tid. + */ + std::map> GetThreadSampledFrames(uint64_t beginTime = 0, uint64_t endTime = 0); + + /** + * @brief the stack string of the sampled stack in tree format. + * + * @param tid the tid of sampled thread. + * @param printTimes whether to print the timestamps of the stack, default false. + * @param beginTime the begin time of the time interval to filter the sampled stack, default 0. + * @param endTime the end time of the time interval to filter sampled stack, default 0. + * @return the string of the sampled stack, formated in tree style. + */ + std::string GetTreeStack(int tid, bool printTimes = false, uint64_t beginTime = 0, uint64_t endTime = 0); + + /** + * @brief Get the heaviest stack string of the tree format stack. + * + * @param tid the tid of the sampled thread. + * @param beginTime the begin time of the time interval to filter the sampled stack, default 0. + * @param endTime the end time of the time interval to filter sampled stack, default 0. + * @return the string of the heaviest sampled stack. + */ + std::string GetHeaviestStack(int tid, uint64_t beginTime = 0, uint64_t endTime = 0); + + /** + * @brief Print the sampled stack to tree format string from the vector of struct SampledFrame. + * + * @param sampledFrameVec the SampledFrame vector of sampled stack. + * @param printTimes whether to print the timestamps of the stack, default false. + * @param unwinder the unwinder to unwind stacks. + * @param maps the maps of the process. + * @return the string of the sampled stack, formated in tree style. + */ + static std::string PrintTreeStackBySampledStack(const std::vector& sampledFrameVec, bool printTimes, + const std::shared_ptr& unwinder, + const std::shared_ptr& maps); + + /** + * @brief Serialize the sampled stack frames of each tid map by ostream. + * + * @param sampledFrameMap the map of the SampledFrame vector of each tid. + * @param os the ostream to do serialize. + * @return void. + */ + static void SerializeSampledFrameMap(const std::map>& sampledFrameMap, + std::ostream& os); + + /** + * @brief Deserialize the sampled stack frames of tid to map by istream. + * + * @param is the istream to do deserialize + * @return the map of the SampledFrame vector of each tid. + */ + static std::map> DeserializeSampledFrameMap(std::istream& is); private: class Impl; std::shared_ptr impl_; }; -} // end of namespace HiviewDFX -} // end of namespace OHOS +} // end of namespace HiviewDFX +} // end of namespace OHOS #endif diff --git a/interfaces/innerkits/stack_printer/libstack_printer.map b/interfaces/innerkits/stack_printer/libstack_printer.map index ea49add06c8e32b1b3adf969709f3d4f0e15ce9a..9f8fab2f9bc943ee621cc21b0c835d960bce9c59 100644 --- a/interfaces/innerkits/stack_printer/libstack_printer.map +++ b/interfaces/innerkits/stack_printer/libstack_printer.map @@ -1,13 +1,17 @@ { global: extern "C++" { - OHOS::HiviewDFX::StackPrinter::SetUnwindInfo*; OHOS::HiviewDFX::StackPrinter::StackPrinter*; + OHOS::HiviewDFX::StackPrinter::InitUniqueTable*; OHOS::HiviewDFX::StackPrinter::PutPcsInTable*; + OHOS::HiviewDFX::StackPrinter::SetUnwindInfo*; OHOS::HiviewDFX::StackPrinter::GetFullStack*; + OHOS::HiviewDFX::StackPrinter::GetThreadSampledFrames*; OHOS::HiviewDFX::StackPrinter::GetTreeStack*; OHOS::HiviewDFX::StackPrinter::GetHeaviestStack*; - OHOS::HiviewDFX::StackPrinter::InitUniqueTable*; + OHOS::HiviewDFX::StackPrinter::PrintTreeStackBySampledStack*; + OHOS::HiviewDFX::StackPrinter::SerializeSampledFrameMap*; + OHOS::HiviewDFX::StackPrinter::DeserializeSampledFrameMap*; }; local: *; diff --git a/interfaces/innerkits/stack_printer/src/stack_printer.cpp b/interfaces/innerkits/stack_printer/src/stack_printer.cpp index 017f575c110b627004dec13b18d2df45e7c1cbb1..707aa197f1bd739dca5e17693e7ce0db27c56051 100644 --- a/interfaces/innerkits/stack_printer/src/stack_printer.cpp +++ b/interfaces/innerkits/stack_printer/src/stack_printer.cpp @@ -1,22 +1,24 @@ /* -* Copyright (c) 2025 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. -*/ + * Copyright (c) 2025 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. + */ #include "stack_printer.h" #include +#include #include + #include #include "dfx_frame.h" @@ -31,7 +33,7 @@ constexpr uint64_t SEC_TO_NANOSEC = 1000000000; constexpr uint64_t MICROSEC_TO_NANOSEC = 1000; constexpr int FORMAT_TIME_LEN = 20; constexpr int MICROSEC_LEN = 6; -} +} // namespace struct StackRecord { uint64_t stackId {0}; @@ -42,12 +44,35 @@ struct StackItem { uintptr_t pc {0}; int32_t pcCount {0}; uint64_t level {0}; - uint64_t stackId {0}; // Only leaf node update this. + uint64_t stackId {0}; // Only leaf node update this. std::shared_ptr current {nullptr}; std::shared_ptr child {nullptr}; std::shared_ptr siblings {nullptr}; }; +std::ostream& operator<<(std::ostream& os, const SampledFrame& frame) +{ + os << frame.indent << " " << frame.count << " " << frame.level << " " << frame.pc << " " << frame.isLeaf << " " << + frame.timestamps.size(); + for (auto timestamp : frame.timestamps) { + os << " " << timestamp; + } + return os; +} + +std::istream& operator>>(std::istream& is, SampledFrame& frame) +{ + size_t timestampsSize; + is >> frame.indent >> frame.count >> frame.level >> frame.pc >> frame.isLeaf >> timestampsSize; + if (timestampsSize > 0) { + frame.timestamps.resize(timestampsSize); + for (size_t i = 0; i < timestampsSize; i++) { + is >> frame.timestamps[i]; + } + } + return is; +} + static std::string TimeFormat(uint64_t time) { uint64_t nsec = time % SEC_TO_NANOSEC; @@ -71,8 +96,8 @@ static std::string TimeFormat(uint64_t time) return s; } -static std::vector TimeFilter(const std::vector& stackRecordVec, - uint64_t beginTime, uint64_t endTime) +static std::vector TimeFilter(const std::vector& stackRecordVec, uint64_t beginTime, + uint64_t endTime) { if ((beginTime == 0 && endTime == 0) || (beginTime > endTime)) { return stackRecordVec; @@ -97,7 +122,7 @@ class StackPrinter::Impl { public: Impl() : root_(nullptr) {} - + inline void SetUnwindInfo(const std::shared_ptr& unwinder, const std::shared_ptr& maps) { unwinder_ = unwinder; @@ -105,26 +130,39 @@ public: } bool InitUniqueTable(pid_t pid, uint32_t size, std::string name = "unique_stack_table"); - bool PutPcsInTable(const std::vector& pcs, uint64_t snapshotTime); + bool PutPcsInTable(const std::vector& pcs, int tid, uint64_t snapshotTime); std::string GetFullStack(const std::vector& timeStampedPcsVec); - std::string GetTreeStack(bool printTimes = false, uint64_t beginTime = 0, uint64_t endTime = 0); - std::string GetHeaviestStack(uint64_t beginTime = 0, uint64_t endTime = 0); + std::map> GetThreadSampledFrames(uint64_t beginTime = 0, uint64_t endTime = 0); + std::string GetTreeStack(int tid, bool printTimes = false, uint64_t beginTime = 0, uint64_t endTime = 0); + std::string GetHeaviestStack(int tid, uint64_t beginTime = 0, uint64_t endTime = 0); + + static std::string PrintTreeStackBySampledStack(const std::vector& sampledFrameVec, bool printTimes, + const std::shared_ptr& unwinder, + const std::shared_ptr& maps); + static void SerializeSampledFrameMap(const std::map>& sampledFrameMap, + std::ostream& os); + static std::map> DeserializeSampledFrameMap(std::istream& is); private: void Insert(const std::vector& pcs, int32_t count, StackId stackId); - std::shared_ptr InsertImpl(std::shared_ptr curNode, - uintptr_t pc, int32_t count, uint64_t level, std::shared_ptr acientNode); - std::shared_ptr AdjustSiblings(std::shared_ptr acient, - std::shared_ptr cur, std::shared_ptr node); - std::string PrintTreeStack(bool printTimes); - std::string PrintStackByPcs(const std::vector& pcs); - void PrintTimesStr(std::string& frameStr, const std::vector& snapshotTimes); + std::shared_ptr InsertImpl(std::shared_ptr curNode, uintptr_t pc, int32_t count, + uint64_t level, std::shared_ptr acientNode); + std::shared_ptr AdjustSiblings(std::shared_ptr acient, std::shared_ptr cur, + std::shared_ptr node); + void BuildStackTree(std::vector& stackRecordVec); + std::vector GenerateTreeStackFrames(const std::vector& stackRecordVec); + std::vector GetSampledFramesByTid(int tid, uint64_t beginTime, uint64_t endTime); + + static void PrintTimesStr(std::string& frameStr, const std::vector& snapshotTimes); + static std::string PrintStackByPcs(const std::vector& pcs, const std::shared_ptr& unwinder, + const std::shared_ptr& maps); std::shared_ptr root_; - std::shared_ptr unwinder_; - std::shared_ptr maps_; - std::unique_ptr uniqueStackTable_; - std::vector stackRecordVec_; + std::shared_ptr unwinder_ {nullptr}; + std::shared_ptr maps_ {nullptr}; + std::unique_ptr uniqueStackTable_ {nullptr}; + std::map> tidStackRecordMap_; + std::mutex mutex_; }; StackPrinter::StackPrinter() : impl_(std::make_shared()) @@ -135,9 +173,9 @@ void StackPrinter::SetUnwindInfo(const std::shared_ptr& unwinder, cons impl_->SetUnwindInfo(unwinder, maps); } -bool StackPrinter::PutPcsInTable(const std::vector& pcs, uint64_t snapshotTime) +bool StackPrinter::PutPcsInTable(const std::vector& pcs, int tid, uint64_t snapshotTime) { - return impl_->PutPcsInTable(pcs, snapshotTime); + return impl_->PutPcsInTable(pcs, tid, snapshotTime); } std::string StackPrinter::GetFullStack(const std::vector& timeStampedPcsVec) @@ -145,14 +183,14 @@ std::string StackPrinter::GetFullStack(const std::vector& timeSt return impl_->GetFullStack(timeStampedPcsVec); } -std::string StackPrinter::GetTreeStack(bool printTimes, uint64_t beginTime, uint64_t endTime) +std::map> StackPrinter::GetThreadSampledFrames(uint64_t beginTime, uint64_t endTime) { - return impl_->GetTreeStack(printTimes, beginTime, endTime); + return impl_->GetThreadSampledFrames(beginTime, endTime); } -std::string StackPrinter::GetHeaviestStack(uint64_t beginTime, uint64_t endTime) +std::string StackPrinter::GetHeaviestStack(int tid, uint64_t beginTime, uint64_t endTime) { - return impl_->GetHeaviestStack(beginTime, endTime); + return impl_->GetHeaviestStack(tid, beginTime, endTime); } bool StackPrinter::InitUniqueTable(pid_t pid, uint32_t size, std::string name) @@ -160,8 +198,32 @@ bool StackPrinter::InitUniqueTable(pid_t pid, uint32_t size, std::string name) return impl_->InitUniqueTable(pid, size, name); } -std::shared_ptr StackPrinter::Impl::InsertImpl(std::shared_ptr curNode, - uintptr_t pc, int32_t pcCount, uint64_t level, std::shared_ptr acientNode) +std::string StackPrinter::PrintTreeStackBySampledStack(const std::vector& sampledFrameVec, + bool printTimes, const std::shared_ptr& unwinder, + const std::shared_ptr& maps) +{ + return Impl::PrintTreeStackBySampledStack(sampledFrameVec, printTimes, unwinder, maps); +} + +std::string StackPrinter::GetTreeStack(int tid, bool printTimes, uint64_t beginTime, uint64_t endTime) +{ + return impl_->GetTreeStack(tid, printTimes, beginTime, endTime); +} + +void StackPrinter::SerializeSampledFrameMap(const std::map>& sampledFrameMap, + std::ostream& os) +{ + Impl::SerializeSampledFrameMap(sampledFrameMap, os); +} + +std::map> StackPrinter::DeserializeSampledFrameMap(std::istream& is) +{ + return Impl::DeserializeSampledFrameMap(is); +} + +std::shared_ptr StackPrinter::Impl::InsertImpl(std::shared_ptr curNode, uintptr_t pc, + int32_t pcCount, uint64_t level, + std::shared_ptr acientNode) { if (curNode == nullptr) { return nullptr; @@ -232,7 +294,8 @@ void StackPrinter::Impl::Insert(const std::vector& pcs, int32_t pcCou } std::shared_ptr StackPrinter::Impl::AdjustSiblings(std::shared_ptr acient, - std::shared_ptr cur, std::shared_ptr node) + std::shared_ptr cur, + std::shared_ptr node) { std::shared_ptr dummy = std::make_shared(); dummy->siblings = acient->child; @@ -251,7 +314,7 @@ std::shared_ptr StackPrinter::Impl::AdjustSiblings(std::shared_ptr& pcs, uint64_t snapshotTime) +bool StackPrinter::Impl::PutPcsInTable(const std::vector& pcs, int tid, uint64_t snapshotTime) { if (uniqueStackTable_ == nullptr) { return false; @@ -260,14 +323,16 @@ bool StackPrinter::Impl::PutPcsInTable(const std::vector& pcs, uint64 auto stackIdPtr = reinterpret_cast(&stackId); uniqueStackTable_->PutPcsInTable(stackIdPtr, pcs.data(), pcs.size()); - auto it = std::find_if(stackRecordVec_.begin(), stackRecordVec_.end(), [&stackId](const auto& stackRecord) { + std::lock_guard lock(mutex_); + auto& stackRecordVec = tidStackRecordMap_[tid]; + auto it = std::find_if(stackRecordVec.begin(), stackRecordVec.end(), [&stackId](const auto& stackRecord) { return stackRecord.stackId == stackId; }); - if (it == stackRecordVec_.end()) { + if (it == stackRecordVec.end()) { StackRecord record; record.stackId = stackId; record.snapshotTimes.emplace_back(snapshotTime); - stackRecordVec_.emplace_back(record); + stackRecordVec.emplace_back(record); } else { it->snapshotTimes.emplace_back(snapshotTime); } @@ -276,56 +341,52 @@ bool StackPrinter::Impl::PutPcsInTable(const std::vector& pcs, uint64 std::string StackPrinter::Impl::GetFullStack(const std::vector& timeStampedPcsVec) { - if (unwinder_ == nullptr || maps_ == nullptr) { - return std::string(""); - } - std::stringstream stack; for (const auto& timeStampedPcs : timeStampedPcsVec) { std::string snapshotTimeStr = TimeFormat(timeStampedPcs.snapshotTime); stack << "SnapshotTime:" << snapshotTimeStr << "\n"; std::vector pcs = timeStampedPcs.pcVec; - stack << PrintStackByPcs(pcs); + stack << PrintStackByPcs(pcs, unwinder_, maps_); stack << "\n"; } return stack.str(); } -std::string StackPrinter::Impl::GetTreeStack(bool printTimes, uint64_t beginTime, uint64_t endTime) +std::vector StackPrinter::Impl::GetSampledFramesByTid(int tid, uint64_t beginTime, uint64_t endTime) { - std::vector vec = TimeFilter(stackRecordVec_, beginTime, endTime); - std::sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) { - return a.snapshotTimes.size() > b.snapshotTimes.size(); - }); - for (const auto& record : vec) { - std::vector pcs; - StackId stackId; - stackId.value = record.stackId; - if (uniqueStackTable_->GetPcsByStackId(stackId, pcs)) { - Insert(pcs, record.snapshotTimes.size(), stackId); - } - } - return PrintTreeStack(printTimes); + std::lock_guard lock(mutex_); + auto& stackRecordVec = tidStackRecordMap_[tid]; + std::vector vec = TimeFilter(stackRecordVec, beginTime, endTime); + BuildStackTree(vec); + return GenerateTreeStackFrames(vec); +} + +std::string StackPrinter::Impl::GetTreeStack(int tid, bool printTimes, uint64_t beginTime, uint64_t endTime) +{ + std::vector sampledFrameVec = GetSampledFramesByTid(tid, beginTime, endTime); + return PrintTreeStackBySampledStack(sampledFrameVec, printTimes, unwinder_, maps_); } -std::string StackPrinter::Impl::GetHeaviestStack(uint64_t beginTime, uint64_t endTime) +std::string StackPrinter::Impl::GetHeaviestStack(int tid, uint64_t beginTime, uint64_t endTime) { if (unwinder_ == nullptr || maps_ == nullptr) { return std::string(""); } - - std::vector vec = TimeFilter(stackRecordVec_, beginTime, endTime); + std::lock_guard lock(mutex_); + auto& stackRecordVec = tidStackRecordMap_[tid]; + std::vector vec = TimeFilter(stackRecordVec, beginTime, endTime); std::sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) { return a.snapshotTimes.size() > b.snapshotTimes.size(); }); - std::stringstream heaviestStack; auto it = vec.begin(); std::vector pcs; StackId stackId; stackId.value = it->stackId; uniqueStackTable_->GetPcsByStackId(stackId, pcs); + + std::stringstream heaviestStack; heaviestStack << "heaviest stack: \nstack counts: " << std::to_string(it->snapshotTimes.size()) << "\n"; - heaviestStack << PrintStackByPcs(pcs); + heaviestStack << PrintStackByPcs(pcs, unwinder_, maps_); return heaviestStack.str(); } @@ -342,6 +403,21 @@ bool StackPrinter::Impl::InitUniqueTable(pid_t pid, uint32_t size, std::string n return true; } +void StackPrinter::Impl::BuildStackTree(std::vector& stackRecordVec) +{ + std::sort(stackRecordVec.begin(), stackRecordVec.end(), [](const auto& a, const auto& b) { + return a.snapshotTimes.size() > b.snapshotTimes.size(); + }); + for (const auto& record : stackRecordVec) { + std::vector pcs; + StackId stackId; + stackId.value = record.stackId; + if (uniqueStackTable_->GetPcsByStackId(stackId, pcs)) { + Insert(pcs, record.snapshotTimes.size(), stackId); + } + } +} + void StackPrinter::Impl::PrintTimesStr(std::string& frameStr, const std::vector& snapshotTimes) { // rm last '\n' @@ -349,7 +425,7 @@ void StackPrinter::Impl::PrintTimesStr(std::string& frameStr, const std::vector< if (pos != std::string::npos) { frameStr.replace(pos, 1, ""); } - + for (auto t : snapshotTimes) { frameStr.append(" "); frameStr.append(TimeFormat(t)); @@ -357,58 +433,126 @@ void StackPrinter::Impl::PrintTimesStr(std::string& frameStr, const std::vector< frameStr.append("\n"); } -std::string StackPrinter::Impl::PrintTreeStack(bool printTimes) +std::string StackPrinter::Impl::PrintTreeStackBySampledStack(const std::vector& sampledFrameVec, + bool printTimes, const std::shared_ptr& unwinder, + const std::shared_ptr& maps) { - if (root_ == nullptr || unwinder_ == nullptr || maps_ == nullptr) { + if (unwinder == nullptr || maps == nullptr) { return std::string(""); } std::stringstream ss; + for (const auto& sampledFrame : sampledFrameVec) { + std::string space(sampledFrame.indent, ' '); + DfxFrame frame; + frame.index = sampledFrame.level; + unwinder->GetFrameByPc(sampledFrame.pc, maps, frame); + std::string frameStr = DfxFrameFormatter::GetFrameStr(frame); + + if (sampledFrame.isLeaf && printTimes) { + PrintTimesStr(frameStr, sampledFrame.timestamps); + } + ss << space << sampledFrame.count << " " << frameStr; + } + return ss.str(); +} + +std::vector StackPrinter::Impl::GenerateTreeStackFrames(const std::vector& stackRecordVec) +{ + std::vector sampledFrameVec; std::vector> nodes; - nodes.push_back(root_); + if (root_ != nullptr) { + nodes.emplace_back(root_); + } const int indent = 2; while (!nodes.empty()) { std::shared_ptr back = nodes.back(); - back->current = std::make_shared(); - back->current->index = back->level; - unwinder_->GetFrameByPc(back->pc, maps_, *(back->current)); - std::shared_ptr sibling = back->siblings; - std::shared_ptr child = back->child; - std::string space(indent * back->level, ' '); + SampledFrame sampledFrame; + sampledFrame.indent = indent * back->level; + sampledFrame.count = back->pcCount; + sampledFrame.level = back->level; + sampledFrame.pc = back->pc; nodes.pop_back(); - std::string frameStr = DfxFrameFormatter::GetFrameStr(back->current); - if (sibling != nullptr) { - nodes.push_back(sibling); + if (back->siblings != nullptr) { + nodes.emplace_back(back->siblings); } - if (child != nullptr) { - nodes.push_back(child); - } else if (printTimes) { + if (back->child != nullptr) { + nodes.emplace_back(back->child); + sampledFrame.isLeaf = false; + } else { + sampledFrame.isLeaf = true; uint64_t stackId = back->stackId; - auto it = std::find_if(stackRecordVec_.begin(), stackRecordVec_.end(), - [&stackId](const auto& stackIdTimes) { - return stackIdTimes.stackId == stackId; + auto it = std::find_if(stackRecordVec.begin(), stackRecordVec.end(), [&stackId](const auto& record) { + return record.stackId == stackId; }); - PrintTimesStr(frameStr, it->snapshotTimes); + sampledFrame.timestamps = it->snapshotTimes; } - ss << space << back->pcCount << " " << frameStr; + sampledFrameVec.emplace_back(sampledFrame); } root_ = nullptr; - return ss.str(); + return sampledFrameVec; } -std::string StackPrinter::Impl::PrintStackByPcs(const std::vector& pcs) +std::string StackPrinter::Impl::PrintStackByPcs(const std::vector& pcs, + const std::shared_ptr& unwinder, + const std::shared_ptr& maps) { std::stringstream ss; for (size_t i = 0; i < pcs.size(); i++) { DfxFrame frame; - unwinder_->GetFrameByPc(pcs[i], maps_, frame); + unwinder->GetFrameByPc(pcs[i], maps, frame); frame.index = i; auto frameStr = DfxFrameFormatter::GetFrameStr(frame); ss << frameStr; } return ss.str(); } -} // end of namespace HiviewDFX -} // end of namespace OHOS + +std::map> StackPrinter::Impl::GetThreadSampledFrames(uint64_t beginTime, + uint64_t endTime) +{ + std::map> sampledFrameMap; + for (const auto& [tid, _] : tidStackRecordMap_) { + sampledFrameMap[tid] = GetSampledFramesByTid(tid, beginTime, endTime); + } + return sampledFrameMap; +} + +void StackPrinter::Impl::SerializeSampledFrameMap(const std::map>& sampledFrameMap, + std::ostream& os) +{ + os << sampledFrameMap.size() << "\n"; + for (const auto& [tid, sampledFrameVec] : sampledFrameMap) { + os << tid << " " << sampledFrameVec.size(); + for (const auto& frame : sampledFrameVec) { + os << " " << frame; + } + os << "\n"; + } +} + +std::map> StackPrinter::Impl::DeserializeSampledFrameMap(std::istream& is) +{ + std::map> sampledFrameMap; + size_t mapSize; + is >> mapSize; + is.ignore(1); + for (size_t i = 0; i < mapSize; i++) { + int tid; + size_t vecSize; + std::vector sampledFrameVec; + is >> tid >> vecSize; + for (size_t j = 0; j < vecSize; j++) { + SampledFrame frame; + is >> frame; + sampledFrameVec.emplace_back(frame); + } + is.ignore(1); + sampledFrameMap[tid] = sampledFrameVec; + } + return sampledFrameMap; +} +} // end of namespace HiviewDFX +} // end of namespace OHOS diff --git a/services/fault_logger_pipe.cpp b/services/fault_logger_pipe.cpp index 3214a888c557d4cac24dc3a9b12a4aea23cd1a18..331189a0b204f404e49a5f4103a6c5bec62d856d 100644 --- a/services/fault_logger_pipe.cpp +++ b/services/fault_logger_pipe.cpp @@ -35,6 +35,9 @@ namespace OHOS { namespace HiviewDFX { +namespace { +constexpr size_t MAX_LITE_PERF_PIPE_SIZE = 100; +} FaultLoggerPipe::FaultLoggerPipe() { @@ -155,6 +158,14 @@ LitePerfPipePair& LitePerfPipePair::CreatePipePair(int uid) return pipes_.emplace_back(uid); } +bool LitePerfPipePair::CheckDumpMax() +{ + if (pipes_.size() > MAX_LITE_PERF_PIPE_SIZE) { + return true; + } + return false; +} + bool LitePerfPipePair::CheckDumpRecord(int uid) { auto iter = std::find_if(pipes_.begin(), pipes_.end(), diff --git a/services/fault_logger_pipe.h b/services/fault_logger_pipe.h index 8e72ede69bf8eb0cd68fabfd22df7c9520ef6b9d..af905789b25919d3bef2ee7083889bfb19f0518a 100644 --- a/services/fault_logger_pipe.h +++ b/services/fault_logger_pipe.h @@ -76,6 +76,7 @@ public: static LitePerfPipePair& CreatePipePair(int uid); static bool CheckDumpRecord(int uid); + static bool CheckDumpMax(); static LitePerfPipePair* GetPipePair(int uid); static void DelPipePair(int uid); private: diff --git a/services/fault_logger_service.cpp b/services/fault_logger_service.cpp index 46c5ae0e46768d2bfbd664fad51ecfa14637dc6c..eaf1c60fa831cf706091eaee61a32363340509ec 100644 --- a/services/fault_logger_service.cpp +++ b/services/fault_logger_service.cpp @@ -22,6 +22,7 @@ #include #include "dfx_define.h" #include "dfx_log.h" +#include "dfx_lperf.h" #include "dfx_trace.h" #include "dfx_util.h" #include "fault_logger_daemon.h" @@ -396,7 +397,7 @@ int32_t PipeService::OnRequest(const std::string& socketName, int32_t connection } bool LitePerfPipeService::Filter(const std::string &socketName, int32_t connectionFd, - const PipFdRequestData &requestData, int& uid) + const LitePerfFdRequestData &requestData) { if (requestData.pipeType > FaultLoggerPipeType::PIPE_FD_DELETE || requestData.pipeType < FaultLoggerPipeType::PIPE_FD_READ) { @@ -407,23 +408,22 @@ bool LitePerfPipeService::Filter(const std::string &socketName, int32_t connecti if (!FaultCommonUtil::GetUcredByPeerCred(creds, connectionFd)) { return false; } - if (creds.pid != requestData.pid) { + if (creds.uid != requestData.uid) { DFXLOGW("Failed to check request credential request:%{public}d, cred:%{public}d, fd:%{public}d", - requestData.pid, creds.pid, connectionFd); + requestData.uid, creds.uid, connectionFd); return false; } - uid = creds.uid; return true; } int32_t LitePerfPipeService::OnRequest(const std::string& socketName, int32_t connectionFd, - const PipFdRequestData& requestData) + const LitePerfFdRequestData& requestData) { DFX_TRACE_SCOPED("LitePerfPipeServiceOnRequest"); - int uid; - if (!Filter(socketName, connectionFd, requestData, uid)) { + if (!Filter(socketName, connectionFd, requestData)) { return ResponseCode::REQUEST_REJECT; } + int uid = static_cast(requestData.uid); int32_t responseData = ResponseCode::REQUEST_SUCCESS; if (requestData.pipeType == FaultLoggerPipeType::PIPE_FD_DELETE) { LitePerfPipePair::DelPipePair(uid); @@ -433,13 +433,20 @@ int32_t LitePerfPipeService::OnRequest(const std::string& socketName, int32_t co int32_t fds[PIPE_NUM_SZ] = {0}; if (requestData.pipeType == FaultLoggerPipeType::PIPE_FD_READ) { - if (LitePerfPipePair::CheckDumpRecord(uid)) { + if (LitePerfPipePair::CheckDumpMax() || LitePerfPipePair::CheckDumpRecord(uid)) { DFXLOGE("%{public}s :: uid(%{public}d) is dumping.", FAULTLOGGERD_SERVICE_TAG, uid); return ResponseCode::SDK_DUMP_REPEAT; } auto& pipePair = LitePerfPipePair::CreatePipePair(uid); fds[PIPE_BUF_INDEX] = pipePair.GetPipeFd(PipeFdUsage::BUFFER_FD, FaultLoggerPipeType::PIPE_FD_READ); fds[PIPE_RES_INDEX] = pipePair.GetPipeFd(PipeFdUsage::RESULT_FD, FaultLoggerPipeType::PIPE_FD_READ); + + constexpr int maxPerfTimeout = (MAX_STOP_SECONDS + DUMP_LITEPERF_TIMEOUT) / 1000; + int32_t delayTime = std::min(requestData.timeout, maxPerfTimeout); + auto task = [uid, this] { + LitePerfPipePair::DelPipePair(uid); + }; + StartDelayTask(task, delayTime); } else if (requestData.pipeType == FaultLoggerPipeType::PIPE_FD_WRITE) { LitePerfPipePair* pipePair = LitePerfPipePair::GetPipePair(uid); if (pipePair == nullptr) { @@ -459,6 +466,12 @@ int32_t LitePerfPipeService::OnRequest(const std::string& socketName, int32_t co SendFileDescriptorToSocket(connectionFd, fds, PIPE_NUM_SZ); return responseData; } + +void LitePerfPipeService::StartDelayTask(std::function workFunc, int32_t delayTime) +{ + auto delayTask = DelayTask::CreateInstance(workFunc, delayTime); + FaultLoggerDaemon::GetEpollManager(EpollManagerType::MAIN_SERVER).AddListener(std::move(delayTask)); +} #endif } } \ No newline at end of file diff --git a/services/fault_logger_service.h b/services/fault_logger_service.h index ca9f4c27803428eed7948b2f56ea0d42e8550480..33cc9b9fb5895b76213eb8e13aae7de9b81d9dc7 100644 --- a/services/fault_logger_service.h +++ b/services/fault_logger_service.h @@ -104,13 +104,13 @@ private: static int32_t Filter(const std::string& socketName, const SdkDumpRequestData& requestData, uint32_t uid); }; -class LitePerfPipeService : public FaultLoggerService { +class LitePerfPipeService : public FaultLoggerService { public: int32_t OnRequest(const std::string& socketName, int32_t connectionFd, - const PipFdRequestData& requestData) override; + const LitePerfFdRequestData& requestData) override; private: - static bool Filter(const std::string& socketName, int32_t connectionFd, - const PipFdRequestData& requestData, int& uid); + static bool Filter(const std::string& socketName, int32_t connectionFd, const LitePerfFdRequestData& requestData); + void StartDelayTask(std::function workFunc, int32_t delayTime); }; class PipeService : public FaultLoggerService { diff --git a/test/unittest/dump_catcher/lite_perf_test.cpp b/test/unittest/dump_catcher/lite_perf_test.cpp index b1a8e65bcf31eaf3dc5646451f1667f8547d0dd9..bf9166dcb7d67521766e2caaf6865a208a3f131a 100644 --- a/test/unittest/dump_catcher/lite_perf_test.cpp +++ b/test/unittest/dump_catcher/lite_perf_test.cpp @@ -19,6 +19,7 @@ #include #include #include "dfx_test_util.h" +#include "dfx_dump_catcher.h" #include "file_ex.h" #include "lite_perf.h" @@ -49,13 +50,62 @@ void LitePerfTest::TearDown() {} /** - * @tc.name: LitePerfTestTest002 - * @tc.desc: test LitePerf invalid tids + * @tc.name: LitePerfTest001 + * @tc.desc: test LitePerf normal * @tc.type: FUNC */ -HWTEST_F(LitePerfTest, LitePerfTestTest002, TestSize.Level2) +HWTEST_F(LitePerfTest, LitePerfTest001, TestSize.Level2) { - GTEST_LOG_(INFO) << "LitePerfTestTest002: start."; + GTEST_LOG_(INFO) << "LitePerfTest001: start."; + if (IsLinuxKernel()) { + return; + } + + static bool threadExit = false; + const int testTimes = 5000; + auto testThread = [&testTimes] { + std::vector tids; + int tid = getpid(); + tids.emplace_back(tid); + tids.emplace_back(gettid()); + int freq = 100; + int durationMs = testTimes; + bool parseMiniDebugInfo = false; + LitePerf litePerf; + int ret = litePerf.StartProcessStackSampling(tids, freq, durationMs, parseMiniDebugInfo); + EXPECT_EQ(ret, 0); + std::string sampleStack; + ret = litePerf.CollectSampleStackByTid(tid, sampleStack); + EXPECT_EQ(ret, 0); + ASSERT_TRUE(sampleStack.size() != 0); + ret = litePerf.FinishProcessStackSampling(); + EXPECT_EQ(ret, 0); + threadExit = true; + }; + std::thread th(testThread); + + int times = 0; + int sleepTime = 100; + while (!threadExit) { + if (times > testTimes || sleepTime <= 0) { + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); + times += sleepTime; + sleepTime--; + } + th.join(); + GTEST_LOG_(INFO) << "LitePerfTest001: end."; +} + +/** + * @tc.name: LitePerfTest002 + * @tc.desc: test LitePerf tids empty + * @tc.type: FUNC + */ +HWTEST_F(LitePerfTest, LitePerfTest002, TestSize.Level2) +{ + GTEST_LOG_(INFO) << "LitePerfTest002: start."; if (IsLinuxKernel()) { return; } @@ -77,13 +127,13 @@ HWTEST_F(LitePerfTest, LitePerfTestTest002, TestSize.Level2) } /** - * @tc.name: LitePerfTestTest003 + * @tc.name: LitePerfTest003 * @tc.desc: test LitePerf invalid freq * @tc.type: FUNC */ -HWTEST_F(LitePerfTest, LitePerfTestTest003, TestSize.Level2) +HWTEST_F(LitePerfTest, LitePerfTest003, TestSize.Level2) { - GTEST_LOG_(INFO) << "LitePerfTestTest003: start."; + GTEST_LOG_(INFO) << "LitePerfTest003: start."; if (IsLinuxKernel()) { return; } @@ -106,13 +156,13 @@ HWTEST_F(LitePerfTest, LitePerfTestTest003, TestSize.Level2) } /** - * @tc.name: LitePerfTestTest004 + * @tc.name: LitePerfTest004 * @tc.desc: test LitePerf invalid freq -1 * @tc.type: FUNC */ -HWTEST_F(LitePerfTest, LitePerfTestTest004, TestSize.Level2) +HWTEST_F(LitePerfTest, LitePerfTest004, TestSize.Level2) { - GTEST_LOG_(INFO) << "LitePerfTestTest004: start."; + GTEST_LOG_(INFO) << "LitePerfTest004: start."; if (IsLinuxKernel()) { return; } @@ -135,13 +185,13 @@ HWTEST_F(LitePerfTest, LitePerfTestTest004, TestSize.Level2) } /** - * @tc.name: LitePerfTestTest005 + * @tc.name: LitePerfTest005 * @tc.desc: test LitePerf invalid time * @tc.type: FUNC */ -HWTEST_F(LitePerfTest, LitePerfTestTest005, TestSize.Level2) +HWTEST_F(LitePerfTest, LitePerfTest005, TestSize.Level2) { - GTEST_LOG_(INFO) << "LitePerfTestTest005: start."; + GTEST_LOG_(INFO) << "LitePerfTest005: start."; if (IsLinuxKernel()) { return; } @@ -164,13 +214,13 @@ HWTEST_F(LitePerfTest, LitePerfTestTest005, TestSize.Level2) } /** - * @tc.name: LitePerfTestTest006 + * @tc.name: LitePerfTest006 * @tc.desc: test LitePerf invalid time -1 * @tc.type: FUNC */ -HWTEST_F(LitePerfTest, LitePerfTestTest006, TestSize.Level2) +HWTEST_F(LitePerfTest, LitePerfTest006, TestSize.Level2) { - GTEST_LOG_(INFO) << "LitePerfTestTest006: start."; + GTEST_LOG_(INFO) << "LitePerfTest006: start."; if (IsLinuxKernel()) { return; } @@ -189,7 +239,86 @@ HWTEST_F(LitePerfTest, LitePerfTestTest006, TestSize.Level2) ASSERT_TRUE(sampleStack.size() == 0); ret = litePerf.FinishProcessStackSampling(); EXPECT_EQ(ret, 0); - GTEST_LOG_(INFO) << "LitePerfTestTest006: end."; + GTEST_LOG_(INFO) << "LitePerfTest006: end."; +} + +/** + * @tc.name: LitePerfTest007 + * @tc.desc: test LitePerf invalid tids + * @tc.type: FUNC + */ +HWTEST_F(LitePerfTest, LitePerfTest007, TestSize.Level2) +{ + GTEST_LOG_(INFO) << "LitePerfTest007: start."; + if (IsLinuxKernel()) { + return; + } + std::vector tids {1, 2, 3, 4, 5}; + int freq = 100; + int durationMs = 5000; + bool parseMiniDebugInfo = false; + LitePerf litePerf; + int ret = litePerf.StartProcessStackSampling(tids, freq, durationMs, parseMiniDebugInfo); + EXPECT_EQ(ret, -1); + for (auto tid : tids) { + std::string sampleStack; + sampleStack.clear(); + ret = litePerf.CollectSampleStackByTid(tid, sampleStack); + EXPECT_EQ(ret, -1); + } + ret = litePerf.FinishProcessStackSampling(); + EXPECT_EQ(ret, 0); + GTEST_LOG_(INFO) << "LitePerfTest007: end."; +} + +/** + * @tc.name: LitePerfTest008 + * @tc.desc: test LitePerf and DumpCatcher as the same time + * @tc.type: FUNC + */ +HWTEST_F(LitePerfTest, LitePerfTest008, TestSize.Level2) +{ + GTEST_LOG_(INFO) << "LitePerfTest008: start."; + if (IsLinuxKernel()) { + return; + } + + int fd[2]; + EXPECT_TRUE(CreatePipeFd(fd)); + + pid_t pid = 0; + pid = fork(); + if (pid < 0) { + GTEST_LOG_(INFO) << "LitePerfTest008: Failed to vfork."; + return; + } + if (pid == 0) { + NotifyProcStart(fd); + std::this_thread::sleep_for(std::chrono::seconds(1)); + pid_t parentPid = getppid(); + GTEST_LOG_(INFO) << "LitePerfTest008: parentPid: " << parentPid; + DfxDumpCatcher dumplog; + string msg = ""; + bool ret = dumplog.DumpCatch(parentPid, 0, msg); + EXPECT_TRUE(ret) << "LitePerfTest008: DumpCatch0 msg Failed."; + } else { + WaitProcStart(fd); + std::vector tids; + int tid = getpid(); + GTEST_LOG_(INFO) << "pid: " << tid; + tids.emplace_back(tid); + int freq = 100; + int durationMs = 5000; + bool parseMiniDebugInfo = false; + LitePerf litePerf; + int ret = litePerf.StartProcessStackSampling(tids, freq, durationMs, parseMiniDebugInfo); + EXPECT_EQ(ret, 0) << "LitePerfTest008: StartProcessStackSampling Failed."; + std::string sampleStack; + litePerf.CollectSampleStackByTid(tid, sampleStack); + ret = litePerf.FinishProcessStackSampling(); + EXPECT_EQ(ret, 0); + } + GTEST_LOG_(INFO) << "LitePerfTest008: end."; } } // namespace HiviewDFX } // namespace OHOS diff --git a/test/unittest/faultloggerd/faultlogger_server_test.cpp b/test/unittest/faultloggerd/faultlogger_server_test.cpp index 87adf24a214fb0532f4a56f3c3a0e9140cb10a63..436e189785af5e87bc12dc92b127c45f86364941 100644 --- a/test/unittest/faultloggerd/faultlogger_server_test.cpp +++ b/test/unittest/faultloggerd/faultlogger_server_test.cpp @@ -346,7 +346,7 @@ HWTEST_F(FaultLoggerdServiceTest, PipeFdClientTest03, TestSize.Level2) */ HWTEST_F(FaultLoggerdServiceTest, LitePerfPipeFdClientTest01, TestSize.Level2) { - PipFdRequestData requestData; + LitePerfFdRequestData requestData; FillRequestHeadData(requestData.head, FaultLoggerClientType::PIPE_FD_LITEPERF_CLIENT); requestData.pipeType = FaultLoggerPipeType::PIPE_FD_READ; @@ -390,7 +390,7 @@ HWTEST_F(FaultLoggerdServiceTest, LitePerfPipeFdClientTest01, TestSize.Level2) */ HWTEST_F(FaultLoggerdServiceTest, LitePerfPipeFdClientTest02, TestSize.Level2) { - PipFdRequestData requestData; + LitePerfFdRequestData requestData; FillRequestHeadData(requestData.head, FaultLoggerClientType::PIPE_FD_LITEPERF_CLIENT); requestData.pipeType = FaultLoggerPipeType::PIPE_FD_READ; @@ -413,7 +413,7 @@ HWTEST_F(FaultLoggerdServiceTest, LitePerfPipeFdClientTest02, TestSize.Level2) */ HWTEST_F(FaultLoggerdServiceTest, LitePerfPipeFdClientTest03, TestSize.Level2) { - PipFdRequestData requestData; + LitePerfFdRequestData requestData; FillRequestHeadData(requestData.head, FaultLoggerClientType::PIPE_FD_LITEPERF_CLIENT); requestData.pid = requestData.head.clientPid; diff --git a/test/unittest/stack_printer/stack_printer_test.cpp b/test/unittest/stack_printer/stack_printer_test.cpp index 69829888a12293aa318d9410ea633bb3a6c405c0..6b3ef5d6448418328fd14c7860eee8f63b3a4f35 100644 --- a/test/unittest/stack_printer/stack_printer_test.cpp +++ b/test/unittest/stack_printer/stack_printer_test.cpp @@ -608,13 +608,13 @@ HWTEST_F(StackPrinterTest, StackPrinterTest_002, TestSize.Level2) { GTEST_LOG_(INFO) << "StackPrinterTest_002: start."; std::unique_ptr stackPrinter = std::make_unique(); - bool flag = stackPrinter->PutPcsInTable(pcsVec[0], timestamps[0]); + bool flag = stackPrinter->PutPcsInTable(pcsVec[0], gettid(), timestamps[0]); ASSERT_FALSE(flag); uint32_t uniqueStableSize = 128 * 1024; stackPrinter->InitUniqueTable(getpid(), uniqueStableSize); for (size_t i = 0; i < pcsVec.size(); i++) { - flag = stackPrinter->PutPcsInTable(pcsVec[i], timestamps[i]); + flag = stackPrinter->PutPcsInTable(pcsVec[i], gettid(), timestamps[i]); ASSERT_TRUE(flag); } GTEST_LOG_(INFO) << "StackPrinterTest_002: end."; @@ -659,23 +659,23 @@ HWTEST_F(StackPrinterTest, StackPrinterTest_004, TestSize.Level2) uint32_t uniqueStableSize = 128 * 1024; stackPrinter->InitUniqueTable(getpid(), uniqueStableSize); for (size_t i = 0; i < pcsVec.size(); i++) { - stackPrinter->PutPcsInTable(pcsVec[i], timestamps[i]); + stackPrinter->PutPcsInTable(pcsVec[i], gettid(), timestamps[i]); } std::shared_ptr unwinder = std::make_shared(false); std::shared_ptr maps = DfxMaps::Create(getpid(), MAPS_PATH); stackPrinter->SetUnwindInfo(unwinder, maps); - std::string stack = stackPrinter->GetTreeStack(); + std::string stack = stackPrinter->GetTreeStack(gettid()); ASSERT_NE(stack, ""); GTEST_LOG_(INFO) << "stack:\n" << stack.c_str() << "\n"; GTEST_LOG_(INFO) << "\n================================================\n"; - stack = stackPrinter->GetTreeStack(true); + stack = stackPrinter->GetTreeStack(gettid(), true); ASSERT_NE(stack, ""); GTEST_LOG_(INFO) << "stack:\n" << stack.c_str() << "\n"; GTEST_LOG_(INFO) << "\n================================================\n"; - stack = stackPrinter->GetTreeStack(true, timestamps[2], timestamps[6]); + stack = stackPrinter->GetTreeStack(gettid(), true, timestamps[2], timestamps[6]); ASSERT_NE(stack, ""); GTEST_LOG_(INFO) << "stack:\n" << stack.c_str() << "\n"; GTEST_LOG_(INFO) << "StackPrinterTest_004: end."; @@ -693,21 +693,128 @@ HWTEST_F(StackPrinterTest, StackPrinterTest_005, TestSize.Level2) uint32_t uniqueStableSize = 128 * 1024; stackPrinter->InitUniqueTable(getpid(), uniqueStableSize); for (size_t i = 0; i < pcsVec.size(); i++) { - stackPrinter->PutPcsInTable(pcsVec[i], timestamps[i]); + stackPrinter->PutPcsInTable(pcsVec[i], gettid(), timestamps[i]); } std::shared_ptr unwinder = std::make_shared(false); std::shared_ptr maps = DfxMaps::Create(getpid(), MAPS_PATH); stackPrinter->SetUnwindInfo(unwinder, maps); - std::string stack = stackPrinter->GetHeaviestStack(); + std::string stack = stackPrinter->GetHeaviestStack(gettid()); ASSERT_NE(stack, ""); GTEST_LOG_(INFO) << "stack:\n" << stack.c_str() << "\n"; GTEST_LOG_(INFO) << "\n================================================\n"; - stack = stackPrinter->GetHeaviestStack(timestamps[2], timestamps[6]); + stack = stackPrinter->GetHeaviestStack(gettid(), timestamps[2], timestamps[6]); ASSERT_NE(stack, ""); GTEST_LOG_(INFO) << "stack:\n" << stack.c_str() << "\n"; GTEST_LOG_(INFO) << "StackPrinterTest_005: end."; } -} // namespace HiviewDFX -} // namespace OHOS \ No newline at end of file + +/** + * @tc.name: StackPrinterTest006 + * @tc.desc: test StackPrinter functions + * @tc.type: FUNC + */ +HWTEST_F(StackPrinterTest, StackPrinterTest_006, TestSize.Level2) +{ + GTEST_LOG_(INFO) << "StackPrinterTest_006: start."; + std::unique_ptr stackPrinter = std::make_unique(); + uint32_t uniqueStableSize = 128 * 1024; + stackPrinter->InitUniqueTable(getpid(), uniqueStableSize); + for (size_t i = 0; i < pcsVec.size(); i++) { + stackPrinter->PutPcsInTable(pcsVec[i], gettid(), timestamps[i]); + } + + std::shared_ptr unwinder = std::make_shared(false); + std::shared_ptr maps = DfxMaps::Create(getpid(), MAPS_PATH); + stackPrinter->SetUnwindInfo(unwinder, maps); + std::map> threadSampledFrames = stackPrinter->GetThreadSampledFrames(); + ASSERT_FALSE(threadSampledFrames.empty()); + std::vector frames = threadSampledFrames[getpid()]; + ASSERT_FALSE(frames.empty()); + + const int indent = 2; + for (const auto& frame : frames) { + ASSERT_EQ(frame.indent, indent * frame.level); + } + GTEST_LOG_(INFO) << "StackPrinterTest_006: end."; +} + +/** + * @tc.name: StackPrinterTest007 + * @tc.desc: test StackPrinter functions + * @tc.type: FUNC + */ +HWTEST_F(StackPrinterTest, StackPrinterTest_007, TestSize.Level2) +{ + GTEST_LOG_(INFO) << "StackPrinterTest_007: start."; + std::unique_ptr stackPrinter = std::make_unique(); + uint32_t uniqueStableSize = 128 * 1024; + stackPrinter->InitUniqueTable(getpid(), uniqueStableSize); + for (size_t i = 0; i < pcsVec.size(); i++) { + stackPrinter->PutPcsInTable(pcsVec[i], gettid(), timestamps[i]); + } + + std::shared_ptr unwinder = std::make_shared(false); + std::shared_ptr maps = DfxMaps::Create(getpid(), MAPS_PATH); + stackPrinter->SetUnwindInfo(unwinder, maps); + std::map> threadSampledFrames = stackPrinter->GetThreadSampledFrames(); + ASSERT_FALSE(threadSampledFrames.empty()); + + std::stringstream ss; + StackPrinter::SerializeSampledFrameMap(threadSampledFrames, ss); + GTEST_LOG_(INFO) << "serialized map: " << ss.str().c_str(); + + std::map> deserializeMap = StackPrinter::DeserializeSampledFrameMap(ss); + ASSERT_EQ(threadSampledFrames.size(), deserializeMap.size()); + auto it1 = threadSampledFrames.cbegin(); + auto it2 = deserializeMap.cbegin(); + while (it1 != threadSampledFrames.cend() && it2 != deserializeMap.cend()) { + ASSERT_EQ(it1->first, it2->first); + ASSERT_EQ(it1->second.size(), it2->second.size()); + for (size_t i = 0; i < it1->second.size(); i++) { + ASSERT_EQ(it1->second[i].indent, it2->second[i].indent); + ASSERT_EQ(it1->second[i].count, it2->second[i].count); + ASSERT_EQ(it1->second[i].level, it2->second[i].level); + ASSERT_EQ(it1->second[i].pc, it2->second[i].pc); + ASSERT_EQ(it1->second[i].isLeaf, it2->second[i].isLeaf); + ASSERT_EQ(it1->second[i].timestamps, it2->second[i].timestamps); + } + it1++; + it2++; + } + GTEST_LOG_(INFO) << "StackPrinterTest_007: end."; +} + +/** + * @tc.name: StackPrinterTest008 + * @tc.desc: test StackPrinter functions + * @tc.type: FUNC + */ +HWTEST_F(StackPrinterTest, StackPrinterTest_008, TestSize.Level2) +{ + GTEST_LOG_(INFO) << "StackPrinterTest_008: start."; + std::unique_ptr stackPrinter = std::make_unique(); + uint32_t uniqueStableSize = 128 * 1024; + stackPrinter->InitUniqueTable(getpid(), uniqueStableSize); + for (size_t i = 0; i < pcsVec.size(); i++) { + stackPrinter->PutPcsInTable(pcsVec[i], gettid(), timestamps[i]); + } + + std::shared_ptr unwinder = std::make_shared(false); + std::shared_ptr maps = DfxMaps::Create(getpid(), MAPS_PATH); + stackPrinter->SetUnwindInfo(unwinder, maps); + std::map> threadSampledFrames = stackPrinter->GetThreadSampledFrames(); + ASSERT_FALSE(threadSampledFrames.empty()); + + std::vector sampledFrameVec = threadSampledFrames[gettid()]; + ASSERT_FALSE(sampledFrameVec.empty()); + + std::string stack = StackPrinter::PrintTreeStackBySampledStack(sampledFrameVec, true, unwinder, maps); + ASSERT_NE(stack, ""); + GTEST_LOG_(INFO) << "stack:\n" << stack.c_str() << "\n"; + + GTEST_LOG_(INFO) << "StackPrinterTest_008: end."; +} +} // namespace HiviewDFX +} // namespace OHOS \ No newline at end of file diff --git a/tools/process_dump/BUILD.gn b/tools/process_dump/BUILD.gn index 087ab3951c2cf2b046c4e04da82914262dec1cf9..862c4636cbfae5fb9d5bca2b4bbb041199fe4ef1 100644 --- a/tools/process_dump/BUILD.gn +++ b/tools/process_dump/BUILD.gn @@ -180,6 +180,7 @@ if (defined(ohos_lite)) { include_dirs = [ "lperf" ] sources = processdump_sources sources += [ + "lite_perf_dumper.cpp", "lperf/lperf_event_record.cpp", "lperf/lperf_events.cpp", "lperf/lperf_record.cpp", diff --git a/tools/process_dump/lite_perf_dumper.cpp b/tools/process_dump/lite_perf_dumper.cpp index e6442c8b0c664f8986919b03f325fc0954a1c8d9..5f068c8e5328e7c8d413e1eddd74bae5fa223ba5 100644 --- a/tools/process_dump/lite_perf_dumper.cpp +++ b/tools/process_dump/lite_perf_dumper.cpp @@ -24,6 +24,7 @@ #include "dfx_define.h" #include "dfx_dump_request.h" #include "dfx_dump_res.h" +#include "dfx_trace.h" #include "faultloggerd_client.h" #include "lperf_event_record.h" #include "lperf_events.h" @@ -59,7 +60,7 @@ int LitePerfDumper::PerfProcess(LitePerfParam& lperf) } int pipeWriteFd[PIPE_NUM_SZ] = { -1, -1}; - if (RequestLitePerfPipeFd(FaultLoggerPipeType::PIPE_FD_WRITE, pipeWriteFd) == -1) { + if (RequestLitePerfPipeFd(FaultLoggerPipeType::PIPE_FD_WRITE, pipeWriteFd, 0) == -1) { DFXLOGE("%{public}s request pipe failed, err:%{public}d", __func__, errno); return -1; } @@ -73,29 +74,20 @@ int LitePerfDumper::PerfRecord(int (&pipeWriteFd)[2], LitePerfParam& lperf) SmartFd resFd(pipeWriteFd[PIPE_RES_INDEX]); std::vector lperfTids; - for (auto i = 0; i < MAX_PERF_TIDS_SIZE; ++i) { - if (lperf.tids[i] <= 0 || !IsThreadInPid(lperf.pid, lperf.tids[i])) { - DFXLOGW("tid(%{public}d) is not in curr pid(%{public}d).", lperf.tids[i], lperf.pid); + for (auto i = 0; i < MAX_SAMPLE_TIDS; ++i) { + if (lperf.tids[i] <= 0) { continue; } + DFXLOGI("perf tid(%{public}d).", lperf.tids[i]); lperfTids.emplace_back(lperf.tids[i]); } LperfRecord record; - auto accessor = std::make_shared(); - auto unwinder = std::make_shared(accessor, false); - auto maps = DfxMaps::Create(lperf.pid); - record.SetUnwindInfo(unwinder, maps); - int res = record.StartProcessSampling(lperf.pid, lperfTids, lperf.freq, lperf.durationMs, lperf.parseMiniDebugInfo); + int res = record.StartProcessSampling(lperf.pid, lperfTids, lperf.freq, lperf.durationMs); if (res == 0) { - for (const auto& tid : lperfTids) { - std::string stack; - if (record.CollectSampleStack(tid, stack) == 0) { - WriteSampleStackByTid(tid, bufFd.GetFd()); - } else { - DFXLOGW("%{public}s CollectSampleStack tid(%{public}d) fail", __func__, tid); - continue; - } + std::string data; + if (record.CollectSampleStack(data) == 0) { + WriteSampleData(bufFd.GetFd(), data); } } ssize_t nresWrite = OHOS_TEMP_FAILURE_RETRY(write(resFd.GetFd(), &res, sizeof(res))); @@ -107,21 +99,23 @@ int LitePerfDumper::PerfRecord(int (&pipeWriteFd)[2], LitePerfParam& lperf) return 0; } -void LitePerfDumper::WriteSampleStackByTid(int tid, int bufFd) +void LitePerfDumper::WriteSampleData(int bufFd, const std::string& data) { - std::string writeStr = LITE_PERF_SPLIT + std::to_string(tid) + "\n"; constexpr size_t step = MAX_PIPE_SIZE; - writeStr += stack; - for (size_t i = 0; i < writeStr.size(); i += step) { - size_t length = (i + step) < writeStr.size() ? step : writeStr.size() - i; - OHOS_TEMP_FAILURE_RETRY(write(bufFd, writeStr.substr(i, length).c_str(), length)); + for (size_t i = 0; i < data.size(); i += step) { + size_t length = (i + step) < data.size() ? step : data.size() - i; + DFXLOGI("%{public}s write length: %{public}zu", __func__, length); + ssize_t nwrite = OHOS_TEMP_FAILURE_RETRY(write(bufFd, data.substr(i, length).c_str(), length)); + if (nwrite != static_cast(length)) { + DFXLOGE("%{public}s write fail, err:%{public}d", __func__, errno); + return; + } } } int32_t LitePerfDumper::ReadLperfAndCheck(LitePerfParam& lperf) { DFX_TRACE_SCOPED("ReadRequestAndCheck"); - ElapsedTime counter("ReadRequestAndCheck", 20); // 20 : limit cost time 20 ms ssize_t readCount = OHOS_TEMP_FAILURE_RETRY(read(STDOUT_FILENO, &lperf, sizeof(LitePerfParam))); if (readCount != static_cast(sizeof(LitePerfParam))) { DFXLOGE("Failed to read LitePerfParam(%{public}d), readCount(%{public}zd).", errno, readCount); diff --git a/tools/process_dump/lite_perf_dumper.h b/tools/process_dump/lite_perf_dumper.h index d9c277cbf486d1e4dc6ff0a820ef403baad90e0f..bef5698bae05d89b0cea3328014a7fa454a4dac6 100644 --- a/tools/process_dump/lite_perf_dumper.h +++ b/tools/process_dump/lite_perf_dumper.h @@ -37,7 +37,7 @@ private: int PerfProcess(LitePerfParam& lperf); int32_t ReadLperfAndCheck(LitePerfParam& lperf); int PerfRecord(int (&pipeWriteFd)[2], LitePerfParam& lperf); - void WriteSampleStackByTid(int tid, int bufFd); + void WriteSampleData(int bufFd, const std::string& data); }; } // namespace HiviewDFX } // namespace OHOS diff --git a/tools/process_dump/lperf/lperf_events.cpp b/tools/process_dump/lperf/lperf_events.cpp index 0b2c59a7ef498ef0bc3675ccb3beef622ee7ef5f..6b914b55c5961c9654732c3b9933123cbf2c6dd9 100644 --- a/tools/process_dump/lperf/lperf_events.cpp +++ b/tools/process_dump/lperf/lperf_events.cpp @@ -21,6 +21,7 @@ #include #include #include +#include "dfx_lperf.h" namespace OHOS { namespace HiviewDFX { @@ -35,7 +36,6 @@ constexpr unsigned long LPERF_IOCTL_INIT = 1075866625; constexpr unsigned long LPERF_IOCTL_PROFILE = 2148035586; constexpr unsigned long LPERF_IOCTL_ADD_THREADS = 1074031620; constexpr unsigned int DEFAULT_WATER_MARK = 5000; -constexpr int MAX_PERF_TIDS_COUNT = 10; #define HIPERF_BUF_ALIGN alignas(64) @@ -50,7 +50,7 @@ struct LperfInitArg { struct LperfThreadInputArg { unsigned int tidCount; - unsigned int tids[MAX_PERF_TIDS_COUNT]; + unsigned int tids[MAX_SAMPLE_TIDS]; }; } @@ -176,7 +176,7 @@ void LperfEvents::GetRecordFieldFromMmap(MmapFd& mmap, void* dest, size_t destSi size_t tailSize = mmap.bufSize - pos; size_t copySize = std::min(size, tailSize); if (memcpy_s(dest, destSize, mmap.buf + pos, copySize) != 0) { - DFXLOGE("memcpy_s %{public}p to %{public}p failed. size %{public}zd", mmap.buf + pos, dest, copySize); + DFXLOGE("memcpy_s failed. size %{public}zd", copySize); } if (copySize < size) { size -= copySize; diff --git a/tools/process_dump/lperf/lperf_record.cpp b/tools/process_dump/lperf/lperf_record.cpp index 79df77bf57985b157c19e0deb9a4fb60a125f882..760d76f1950cdd48f35421fa4388ef5687c1d1b7 100644 --- a/tools/process_dump/lperf/lperf_record.cpp +++ b/tools/process_dump/lperf/lperf_record.cpp @@ -14,7 +14,8 @@ */ #include "lperf_record.h" -#include "unwinder_config.h" + +#include "dfx_lperf.h" namespace OHOS { namespace HiviewDFX { @@ -24,27 +25,14 @@ namespace { #define LOG_DOMAIN 0xD002D11 #define LOG_TAG "DfxLperfRecord" -constexpr int MIN_SAMPLE_COUNT = 1; -constexpr int MAX_SAMPLE_COUNT = 10; -constexpr int MIN_SAMPLE_FREQUENCY = 1; -constexpr int MAX_SAMPLE_FREQUENCY = 100; -constexpr int MIN_STOP_SECONDS = 1; -constexpr int MAX_STOP_SECONDS = 10000; const char *const LPERF_UNIQUE_TABLE = "lperf_unique_table"; constexpr uint32_t UNIQUE_STABLE_SIZE = 1024 * 1024; } -void LperfRecord::SetUnwindInfo(const std::shared_ptr& unwinder, const std::shared_ptr& maps) +int LperfRecord::StartProcessSampling(int pid, const std::vector& tids, int freq, int duration) { - unwinder_ = unwinder; - maps_ = maps; -} - -int LperfRecord::StartProcessSampling(int pid, const std::vector& tids, - int freq, int duration, bool parseMiniDebugInfo) -{ - CHECK_TRUE_AND_RET(!CheckOutOfRange(tids.size(), MIN_SAMPLE_COUNT, MAX_SAMPLE_COUNT), -1, - "invalid tids count: %d", tids.size()); + CHECK_TRUE_AND_RET(!CheckOutOfRange(tids.size(), MIN_SAMPLE_TIDS, MAX_SAMPLE_TIDS), -1, + "invalid tids size: %d", tids.size()); CHECK_TRUE_AND_RET(!CheckOutOfRange(freq, MIN_SAMPLE_FREQUENCY, MAX_SAMPLE_FREQUENCY), -1, "invalid frequency value: %d", freq); CHECK_TRUE_AND_RET(!CheckOutOfRange(duration, MIN_STOP_SECONDS, MAX_STOP_SECONDS), -1, @@ -57,31 +45,30 @@ int LperfRecord::StartProcessSampling(int pid, const std::vector& tids, tids_ = tids; frequency_ = static_cast(freq); timeStopSec_ = static_cast(duration); - enableDebugInfoSymbolic_ = parseMiniDebugInfo; int ret = OnSubCommand(); lperfEvents_.Clear(); return ret; } -int LperfRecord::CollectSampleStack(int tid, std::string& stack) +int LperfRecord::CollectSampleStack(std::string& datas) { - CHECK_TRUE_AND_RET(tid > 0, -1, "invalid tid: %d", tid); - unsigned int uintTid = static_cast(tid); - if (tidStackMaps_.find(uintTid) != tidStackMaps_.end()) { - tidStackMaps_[uintTid]->SetUnwindInfo(unwinder_, maps_); - stack = tidStackMaps_[uintTid]->GetTreeStack(); - if (stack.size() > 0) { - return 0; - } + if (stackPrinter_ == nullptr) { + return -1; } - return -1; + auto frames = stackPrinter_->GetThreadSampledFrames(); + if (frames.empty()) { + return -1; + } + std::ostringstream oss; + StackPrinter::SerializeSampledFrameMap(frames, oss); + datas = oss.str(); + return datas.empty() ? -1 : 0; } void LperfRecord::FinishProcessSampling() { - UnwinderConfig::SetEnableMiniDebugInfo(defaultEnableDebugInfo_); - tidStackMaps_.clear(); + stackPrinter_ = nullptr; } int LperfRecord::OnSubCommand() @@ -94,8 +81,6 @@ int LperfRecord::OnSubCommand() void LperfRecord::PrepareLperfEvent() { - defaultEnableDebugInfo_ = UnwinderConfig::GetEnableMiniDebugInfo(); - UnwinderConfig::SetEnableMiniDebugInfo(enableDebugInfoSymbolic_); lperfEvents_.SetTid(tids_); lperfEvents_.SetTimeOut(timeStopSec_); lperfEvents_.SetSampleFrequency(frequency_); @@ -108,18 +93,18 @@ void LperfRecord::PrepareLperfEvent() void LperfRecord::SymbolicRecord(LperfRecordSample& record) { CHECK_TRUE_AND_RET(record.data_.tid > 0, NO_RETVAL, "Symbolic invalid Record, tid: %d", record.data_.tid); - unsigned int tid = static_cast(record.data_.tid); - if (tidStackMaps_.find(tid) == tidStackMaps_.end()) { - tidStackMaps_.emplace(tid, std::make_unique()); - tidStackMaps_[tid]->InitUniqueTable(record.data_.pid, UNIQUE_STABLE_SIZE, LPERF_UNIQUE_TABLE); - } std::vector pcs; - for (unsigned int i = 0; i < record.data_.nr; i++) { + for (uint64_t i = 0; i < record.data_.nr; i++) { if (record.data_.ips[i] != PERF_CONTEXT_USER) { pcs.emplace_back(static_cast(record.data_.ips[i])); } } - tidStackMaps_[tid]->PutPcsInTable(pcs, record.data_.time); + + if (stackPrinter_ == nullptr) { + stackPrinter_ = std::make_unique(); + stackPrinter_->InitUniqueTable(static_cast(record.data_.pid), UNIQUE_STABLE_SIZE, LPERF_UNIQUE_TABLE); + } + stackPrinter_->PutPcsInTable(pcs, static_cast(record.data_.tid), record.data_.time); } } // namespace HiviewDFX } // namespace OHOS \ No newline at end of file diff --git a/tools/process_dump/lperf/lperf_record.h b/tools/process_dump/lperf/lperf_record.h index 45578379f542fcd5c522b9f9ff03757150e436ac..d5f05db28814e3eb7a89b9bbee6ceefbac2ccb31 100644 --- a/tools/process_dump/lperf/lperf_record.h +++ b/tools/process_dump/lperf/lperf_record.h @@ -15,21 +15,18 @@ #ifndef LPERF_RECORD_H #define LPERF_RECORD_H -#include #include +#include #include "lperf_events.h" #include "stack_printer.h" -#include "unwinder.h" namespace OHOS { namespace HiviewDFX { class LperfRecord { public: - void SetUnwindInfo(const std::shared_ptr& unwinder, const std::shared_ptr& maps); - - int StartProcessSampling(int pid, const std::vector& tids, int freq, int duration, bool parseMiniDebugInfo); - int CollectSampleStack(int tid, std::string& stack); + int StartProcessSampling(int pid, const std::vector& tids, int freq, int duration); + int CollectSampleStack(std::string& datas); void FinishProcessSampling(); private: @@ -38,16 +35,12 @@ private: void SymbolicRecord(LperfRecordSample& record); LperfEvents lperfEvents_; - std::shared_ptr unwinder_; - std::shared_ptr maps_; - std::map> tidStackMaps_; + std::unique_ptr stackPrinter_ = nullptr; - unsigned int timeStopSec_ = 5; + unsigned int timeStopSec_ = 0; unsigned int frequency_ = 0; int pid_ = 0; std::vector tids_ = {}; - bool defaultEnableDebugInfo_ = false; - bool enableDebugInfoSymbolic_ = false; }; template