From 61146c1f4682810f460f6a18aab51b4a764d783e Mon Sep 17 00:00:00 2001 From: jiangwei Date: Wed, 21 May 2025 20:42:43 +0800 Subject: [PATCH] =?UTF-8?q?fix:1.record=E6=94=AF=E6=8C=81--control=20stop?= =?UTF-8?q?=E7=BB=93=E6=9D=9Fprepare;2.stat=E5=92=8Crecord=E9=80=9A?= =?UTF-8?q?=E4=BF=A1=E4=BB=A3=E7=A0=81=E5=A4=8D=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: jiangwei --- BUILD.gn | 1 + include/command_reporter.h | 3 +- include/perf_pipe.h | 61 ++ include/subcommand_record.h | 29 +- include/subcommand_stat.h | 18 +- include/utilities.h | 1 + src/perf_pipe.cpp | 236 +++++++ src/subcommand_record.cpp | 593 +++++++++++------- src/subcommand_stat.cpp | 207 +----- src/utilities.cpp | 29 +- test/BUILD.gn | 2 + .../common/native/include/perf_pipe_test.h | 23 + .../common/native/include/test_utilities.h | 3 + .../unittest/common/native/perf_pipe_test.cpp | 93 +++ .../common/native/subcommand_record_test.cpp | 59 +- .../common/native/subcommand_stat_test.cpp | 52 +- .../unittest/common/native/test_utilities.cpp | 48 ++ 17 files changed, 970 insertions(+), 488 deletions(-) create mode 100644 include/perf_pipe.h create mode 100644 src/perf_pipe.cpp create mode 100644 test/unittest/common/native/include/perf_pipe_test.h create mode 100644 test/unittest/common/native/perf_pipe_test.cpp diff --git a/BUILD.gn b/BUILD.gn index 2310e0e..6b777c9 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -160,6 +160,7 @@ sources_platform_linux = [ "./src/subcommand_record.cpp", "./src/subcommand_list.cpp", "./src/spe_decoder.cpp", + "./src/perf_pipe.cpp", ] common_deps = [ diff --git a/include/command_reporter.h b/include/command_reporter.h index ba857c7..d548eef 100644 --- a/include/command_reporter.h +++ b/include/command_reporter.h @@ -50,7 +50,8 @@ namespace OHOS::Developtools::HiPerf { GEN_ITEM(OPTION_NOT_SUPPORT), /* 28 */ \ GEN_ITEM(SUBCOMMAND_OPTIONS_ERROR), /* 29 */ \ GEN_ITEM(CHECK_OUT_PUT_ERROR), /* 30 */ \ - GEN_ITEM(WRONG_CONTROL_CMD) /* 31 */ + GEN_ITEM(WRONG_CONTROL_CMD), /* 31 */ \ + GEN_ITEM(CHECK_DEBUG_APP_FAIL) /* 32 */ #define FOR_ERROR_ENUM(x) x diff --git a/include/perf_pipe.h b/include/perf_pipe.h new file mode 100644 index 0000000..6c9764b --- /dev/null +++ b/include/perf_pipe.h @@ -0,0 +1,61 @@ +/* + * 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 HIPERF_PERF_PIPE +#define HIPERF_PERF_PIPE + +#include +#include +#include "utilities.h" +#include "perf_events.h" + +namespace OHOS { +namespace Developtools { +namespace HiPerf { +enum class CommandType { + RECORD = 0, + STAT, +}; +const std::string CONTROL_CMD_PREPARE = "prepare"; +const std::string CONTROL_CMD_START = "start"; +const std::string CONTROL_CMD_PAUSE = "pause"; +const std::string CONTROL_CMD_RESUME = "resume"; +const std::string CONTROL_CMD_OUTPUT = "output"; +const std::string CONTROL_CMD_STOP = "stop"; +class PerfPipe { +private: + std::string fifoFileC2S_; + std::string fifoFileS2C_; + std::string controlCmd_; + std::string perfCmd_; + bool outputEnd_ = false; + +public: + void SetFifoFileName(const CommandType& commandType, std::string& controlCmd, + std::string& fifoFileC2S, std::string& fifoFileS2C); + void RemoveFifoFile(); + bool CreateFifoFile(); + bool SendFifoAndWaitReply(const std::string &cmd, const std::chrono::milliseconds &timeOut); + bool WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut); + void WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut, std::string& reply); + void ProcessStopCommand(bool ret); + void ProcessOutputCommand(bool ret); + bool ProcessControlCmd(); + void SetOutPutEnd(bool outputEnd); +}; +} // namespace HiPerf +} // namespace Developtools +} // namespace OHOS +#endif // HIPERF_PERF_PIPE diff --git a/include/subcommand_record.h b/include/subcommand_record.h index ec04145..5f265f9 100644 --- a/include/subcommand_record.h +++ b/include/subcommand_record.h @@ -33,6 +33,7 @@ #include "perf_event_record.h" #include "perf_events.h" #include "perf_file_writer.h" +#include "perf_pipe.h" #include "subcommand.h" #include "virtual_runtime.h" @@ -224,6 +225,7 @@ public: private: PerfEvents perfEvents_; + PerfPipe perfPipe_; bool targetSystemWide_ = false; bool compressData_ = false; @@ -251,6 +253,8 @@ private: int checkAppMs_ = DEFAULT_CHECK_APP_MS; std::string clockId_ = {}; std::string strLimit_ = {}; + std::string fifoFileC2S_; + std::string fifoFileS2C_; std::vector selectCpus_ = {}; std::vector selectPids_ = {}; std::vector selectTids_ = {}; @@ -307,9 +311,13 @@ private: // for client int clientPipeInput_ = -1; int clientPipeOutput_ = -1; + int readFd_ = -1; + int writeFd_ = -1; int nullFd_ = -1; - std::thread clientCommandHanle_; + std::thread clientCommandHandle_; + std::thread replyCommandHandle_; bool clientRunning_ = true; + bool isHiperfClient_ = false; struct ControlCommandHandler { std::function preProcess = []() -> bool { return false; @@ -318,11 +326,15 @@ private: }; std::unordered_map controlCommandHandlerMap_ = {}; inline void CreateClientThread(); + inline void CreateReplyThread(); void ClientCommandHandle(); + void ReplyCommandHandle(); void InitControlCommandHandlerMap(); void DispatchControlCommand(const std::string& command); bool ClientCommandResponse(bool response); bool ClientCommandResponse(const std::string& str); + bool ChildResponseToMain(bool response); + bool ChildResponseToMain(const std::string& str); bool IsSamplingRunning(); // for cmdline client @@ -333,14 +345,10 @@ private: bool dedupStack_ = false; std::map> mapPids_; bool ProcessControl(); - void ProcessStopCommand(bool ret); - void ProcessOutputCommand(bool ret); bool CreateFifoServer(); - bool SendFifoAndWaitReply(const std::string &cmd, const std::chrono::milliseconds &timeOut); - bool WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut); - void WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut, std::string& reply); + bool MainRecvFromChild(int fd, std::string& reply); void CloseClientThread(); - std::string HandleAppInfo(); + void CloseReplyThread(); bool PreparePerfEvent(); bool PrepareSysKernel(); @@ -397,8 +405,15 @@ private: bool CheckReportOption(); bool CheckBacktrackOption(); bool CheckSpeOption(); + bool IsAppRestarted(); + bool CheckAppRestart(); + pid_t GetPidFromAppPackage(const pid_t oldPid, const uint64_t waitAppTimeOut); + bool IsAppRunning(); + bool IsPidAndTidExist(); + void MsgPrintAndTrans(bool isTrans, const std::string& msg); void WriteCommEventBeforeSampling(); void RemoveVdsoTmpFile(); + void RemoveFifoFile(); VirtualRuntime virtualRuntime_; #if USE_COLLECT_SYMBOLIC diff --git a/include/subcommand_stat.h b/include/subcommand_stat.h index 62da0c3..b4c0c27 100644 --- a/include/subcommand_stat.h +++ b/include/subcommand_stat.h @@ -17,6 +17,7 @@ #include "option.h" #include "perf_events.h" +#include "perf_pipe.h" #include "subcommand.h" namespace OHOS { @@ -27,6 +28,7 @@ public: static constexpr int DEFAULT_CHECK_APP_MS = 10; static constexpr int MIN_CHECK_APP_MS = 1; static constexpr int MAX_CHECK_APP_MS = 200; + ~SubCommandStat(); SubCommandStat() : SubCommand("stat", "Collect performance counter information", // clang-format off @@ -95,7 +97,6 @@ public: targetSystemWide_(false) { } - ~SubCommandStat(); HiperfError OnSubCommand(std::vector& args) override; bool ParseOption(std::vector &args) override; bool ParseSpecialOption(std::vector &args); @@ -108,6 +109,7 @@ public: private: PerfEvents perfEvents_; + PerfPipe perfPipe_; bool targetSystemWide_ {false}; std::vector selectCpus_ = {}; float timeStopSec_ = PerfEvents::DEFAULT_TIMEOUT; @@ -118,6 +120,8 @@ private: bool noCreateNew_ {false}; std::string appPackage_ = {}; std::string outputFilename_ = ""; + std::string fifoFileC2S_; + std::string fifoFileS2C_; int checkAppMs_ = DEFAULT_CHECK_APP_MS; std::vector selectPids_; std::vector selectTids_; @@ -143,7 +147,8 @@ private: static std::string GetCommentConfigName( const std::unique_ptr &countEvent, std::string eventName); - static void Report(const std::map> &countEvents, FILE* filePtr); + static void Report(const std::map> &countEvents, FILE* filePtr); static void PrintPerHead(FILE* filePtr); static void GetPerKey(std::string &perKey, const PerfEvents::Summary &summary); static void MakeComments(const std::unique_ptr &reportSum, std::string &commentStr); @@ -179,7 +184,7 @@ private: int clientPipeOutput_ = -1; int nullFd_ = -1; FILE* filePtr_ = nullptr; - std::thread clientCommandHanle_; + std::thread clientCommandHandle_; bool clientRunning_ = true; struct ControlCommandHandler { std::function preProcess = []() -> bool { @@ -202,14 +207,9 @@ private: bool isFifoServer_ = false; bool isFifoClient_ = false; bool ProcessControl(); - void ProcessStopCommand(bool ret); bool CreateFifoServer(); - bool SendFifoAndWaitReply(const std::string &cmd, const std::chrono::milliseconds &timeOut); - bool WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut); - void WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut, std::string& reply); void CloseClientThread(); - std::string HandleAppInfo(); - bool ParseControlCmd(const std::string cmd); + bool ParseControlCmd(const std::string& cmd); }; bool RegisterSubCommandStat(void); diff --git a/include/utilities.h b/include/utilities.h index a59584c..b9ab4fd 100644 --- a/include/utilities.h +++ b/include/utilities.h @@ -394,6 +394,7 @@ const std::string GetUserType(); bool GetDeveloperMode(); bool IsArkJsFile(const std::string& filepath); std::string GetProcessName(int pid); +std::string HandleAppInfo(const std::string& appPackage, const std::vector &inputPidTidArgs); bool NeedAdaptSandboxPath(char *filename, int pid, u16 &headerSize); bool NeedAdaptHMBundlePath(std::string& filename, const std::string& threadname); diff --git a/src/perf_pipe.cpp b/src/perf_pipe.cpp new file mode 100644 index 0000000..3825c56 --- /dev/null +++ b/src/perf_pipe.cpp @@ -0,0 +1,236 @@ +/* + * 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 "perf_pipe.h" +#include "hiperf_client.h" +#include "ipc_utilities.h" +#include "utilities.h" +#if defined(is_ohos) && is_ohos +#include "hiperf_hilog.h" +#endif + +using namespace std::chrono; +namespace OHOS { +namespace Developtools { +namespace HiPerf { +const std::string RECORD_CONTROL_FIFO_FILE_C2S = "/data/log/hiperflog/.hiperf_record_control_c2s"; +const std::string RECORD_CONTROL_FIFO_FILE_S2C = "/data/log/hiperflog/.hiperf_record_control_s2c"; +const std::string STAT_CONTROL_FIFO_FILE_C2S = "/data/log/hiperflog/.hiperf_stat_control_c2s"; +const std::string STAT_CONTROL_FIFO_FILE_S2C = "/data/log/hiperflog/.hiperf_stat_control_s2c"; +const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT = 2000ms; +const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT_CHECK = 1000ms; +static constexpr uint64_t CHECK_WAIT_TIME_MS = 200; +static constexpr uint32_t MAX_CLIENT_OUTPUT_WAIT_COUNT = 240; + +void PerfPipe::SetFifoFileName(const CommandType& commandType, std::string& controlCmd, + std::string& fifoFileC2S, std::string& fifoFileS2C) +{ + if (commandType == CommandType::RECORD) { + fifoFileC2S_ = RECORD_CONTROL_FIFO_FILE_C2S; + fifoFileS2C_ = RECORD_CONTROL_FIFO_FILE_S2C; + perfCmd_ = "sampling"; + } else if (commandType == CommandType::STAT) { + fifoFileC2S_ = STAT_CONTROL_FIFO_FILE_C2S; + fifoFileS2C_ = STAT_CONTROL_FIFO_FILE_S2C; + perfCmd_ = "counting"; + } + fifoFileC2S = fifoFileC2S_; + fifoFileS2C = fifoFileS2C_; + controlCmd_ = controlCmd; + HLOGD("C2S:%s, S2C:%s", fifoFileC2S.c_str(), fifoFileS2C.c_str()); +} + +void PerfPipe::RemoveFifoFile() +{ + char errInfo[ERRINFOLEN] = { 0 }; + if (remove(fifoFileC2S_.c_str()) != 0) { + strerror_r(errno, errInfo, ERRINFOLEN); + HLOGE("remove %s failed, errno:(%d:%s)", fifoFileC2S_.c_str(), errno, errInfo); + HIPERF_HILOGE(MODULE_DEFAULT, "remove %{public}s failed, errno:(%{public}d:%{public}s)", + fifoFileC2S_.c_str(), errno, errInfo); + } + if (remove(fifoFileS2C_.c_str()) != 0) { + strerror_r(errno, errInfo, ERRINFOLEN); + HLOGE("remove %s failed, errno:(%d:%s)", fifoFileS2C_.c_str(), errno, errInfo); + HIPERF_HILOGE(MODULE_DEFAULT, "remove %{public}s failed, errno:(%{public}d:%{public}s)", + fifoFileS2C_.c_str(), errno, errInfo); + } +} + +bool PerfPipe::CreateFifoFile() +{ + char errInfo[ERRINFOLEN] = { 0 }; + const mode_t fifoMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + std::string tempPath("/data/log/hiperflog/"); + if (!IsDirectoryExists(tempPath)) { + HIPERF_HILOGI(MODULE_DEFAULT, "%{public}s not exist.", tempPath.c_str()); + if (!CreateDirectory(tempPath, HIPERF_FILE_PERM_770)) { + HIPERF_HILOGI(MODULE_DEFAULT, "create %{public}s failed.", tempPath.c_str()); + } + } + if (mkfifo(fifoFileS2C_.c_str(), fifoMode) != 0 || + mkfifo(fifoFileC2S_.c_str(), fifoMode) != 0) { + if (errno == EEXIST) { + printf("another %s service is running.\n", perfCmd_.c_str()); + } else { + RemoveFifoFile(); + } + strerror_r(errno, errInfo, ERRINFOLEN); + HLOGE("create fifo file failed. %d:%s", errno, errInfo); + return false; + } + return true; +} + +bool PerfPipe::SendFifoAndWaitReply(const std::string &cmd, const std::chrono::milliseconds &timeOut) +{ + // need open for read first, because server maybe send reply before client wait to read + int fdRead = open(fifoFileS2C_.c_str(), O_RDONLY | O_NONBLOCK); + if (fdRead == -1) { + HLOGE("can not open fifo file(%s)", fifoFileS2C_.c_str()); + HIPERF_HILOGE(MODULE_DEFAULT, "can not open fifo file: %{public}s.", fifoFileS2C_.c_str()); + return false; + } + int fdWrite = open(fifoFileC2S_.c_str(), O_WRONLY | O_NONBLOCK); + if (fdWrite == -1) { + HLOGE("can not open fifo file(%s)", fifoFileC2S_.c_str()); + HIPERF_HILOGE(MODULE_DEFAULT, "can not open fifo file: %{public}s.", fifoFileC2S_.c_str()); + close(fdRead); + return false; + } + size_t size = write(fdWrite, cmd.c_str(), cmd.size()); + if (size != cmd.size()) { + HLOGE("failed to write fifo file(%s) command(%s)", fifoFileC2S_.c_str(), cmd.c_str()); + HIPERF_HILOGE(MODULE_DEFAULT, "failed to write fifo file(%{public}s) command(%{public}s).", + fifoFileC2S_.c_str(), cmd.c_str()); + close(fdWrite); + close(fdRead); + return false; + } + close(fdWrite); + + bool ret = WaitFifoReply(fdRead, timeOut); + close(fdRead); + return ret; +} + +bool PerfPipe::WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut) +{ + std::string reply; + WaitFifoReply(fd, timeOut, reply); + return reply == HiperfClient::ReplyOK; +} + +void PerfPipe::WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut, std::string& reply) +{ + struct pollfd pollFd { + fd, POLLIN, 0 + }; + int polled = poll(&pollFd, 1, timeOut.count()); + reply.clear(); + if (polled > 0) { + bool exitLoop = false; + while (!exitLoop) { + char c; + ssize_t result = TEMP_FAILURE_RETRY(read(fd, &c, 1)); + if (result <= 0) { + HLOGE("read from fifo file(%s) failed", fifoFileS2C_.c_str()); + HIPERF_HILOGE(MODULE_DEFAULT, "read from fifo file(%{public}s) failed", fifoFileS2C_.c_str()); + exitLoop = true; + } + reply.push_back(c); + if (c == '\n') { + exitLoop = true; + } + } + } else if (polled == 0) { + HLOGD("wait fifo file(%s) timeout", fifoFileS2C_.c_str()); + HIPERF_HILOGD(MODULE_DEFAULT, "wait fifo file(%{public}s) timeout", fifoFileS2C_.c_str()); + } else { + HLOGD("wait fifo file(%s) failed", fifoFileS2C_.c_str()); + HIPERF_HILOGD(MODULE_DEFAULT, "wait fifo file(%{public}s) failed", fifoFileS2C_.c_str()); + } +} + +void PerfPipe::SetOutPutEnd(bool outputEnd) +{ + outputEnd_ = outputEnd; +} + +void PerfPipe::ProcessStopCommand(bool ret) +{ + if (ret) { + // wait sampling process exit really + static constexpr uint64_t waitCheckSleepMs = 200; + std::this_thread::sleep_for(milliseconds(waitCheckSleepMs)); + while (SendFifoAndWaitReply(HiperfClient::ReplyCheck, CONTROL_WAITREPY_TIMEOUT_CHECK)) { + std::this_thread::sleep_for(milliseconds(waitCheckSleepMs)); + } + HLOGI("wait reply check end."); + } + + RemoveFifoFile(); +} + +void PerfPipe::ProcessOutputCommand(bool ret) +{ + if (!ret) { + HLOGI("send fifo and wait repoy fail"); + HIPERF_HILOGI(MODULE_DEFAULT, "send fifo and wait repoy fail"); + return; + } + + std::this_thread::sleep_for(milliseconds(CHECK_WAIT_TIME_MS)); + uint32_t outputFailCount = 0; + while (!outputEnd_) { + ret = SendFifoAndWaitReply(HiperfClient::ReplyOutputCheck, CONTROL_WAITREPY_TIMEOUT_CHECK); + if (outputFailCount++ > MAX_CLIENT_OUTPUT_WAIT_COUNT || ret) { + break; + } + std::this_thread::sleep_for(milliseconds(CHECK_WAIT_TIME_MS)); + } +} + +bool PerfPipe::ProcessControlCmd() +{ + bool ret = false; + if (controlCmd_ == CONTROL_CMD_START) { + ret = SendFifoAndWaitReply(HiperfClient::ReplyStart, CONTROL_WAITREPY_TIMEOUT); + } else if (controlCmd_ == CONTROL_CMD_RESUME) { + ret = SendFifoAndWaitReply(HiperfClient::ReplyResume, CONTROL_WAITREPY_TIMEOUT); + } else if (controlCmd_ == CONTROL_CMD_PAUSE) { + ret = SendFifoAndWaitReply(HiperfClient::ReplyPause, CONTROL_WAITREPY_TIMEOUT); + } else if (controlCmd_ == CONTROL_CMD_STOP) { + ret = SendFifoAndWaitReply(HiperfClient::ReplyStop, CONTROL_WAITREPY_TIMEOUT); + if (!ret) { + ret = SendFifoAndWaitReply(HiperfClient::ReplyStop, CONTROL_WAITREPY_TIMEOUT); + } + ProcessStopCommand(ret); + } else if (controlCmd_ == CONTROL_CMD_OUTPUT) { + ret = SendFifoAndWaitReply(HiperfClient::ReplyOutput, CONTROL_WAITREPY_TIMEOUT); + ProcessOutputCommand(ret); + } + if (ret) { + printf("%s %s success.\n", controlCmd_.c_str(), perfCmd_.c_str()); + HIPERF_HILOGI(MODULE_DEFAULT, "%{public}s %{public}s success.", controlCmd_.c_str(), perfCmd_.c_str()); + } else { + printf("%s %s failed.\n", controlCmd_.c_str(), perfCmd_.c_str()); + HIPERF_HILOGI(MODULE_DEFAULT, "%{public}s %{public}s failed.", controlCmd_.c_str(), perfCmd_.c_str()); + } + return ret; +} +} // namespace HiPerf +} // namespace Developtools +} // namespace OHOS diff --git a/src/subcommand_record.cpp b/src/subcommand_record.cpp index 367d1ac..5b71fab 100644 --- a/src/subcommand_record.cpp +++ b/src/subcommand_record.cpp @@ -45,15 +45,6 @@ using namespace std::chrono; namespace OHOS { namespace Developtools { namespace HiPerf { -const std::string CONTROL_CMD_PREPARE = "prepare"; -const std::string CONTROL_CMD_START = "start"; -const std::string CONTROL_CMD_PAUSE = "pause"; -const std::string CONTROL_CMD_RESUME = "resume"; -const std::string CONTROL_CMD_OUTPUT = "output"; -const std::string CONTROL_CMD_STOP = "stop"; -const std::string CONTROL_FIFO_FILE_C2S = "/data/log/hiperflog/.hiperf_record_control_c2s"; -const std::string CONTROL_FIFO_FILE_S2C = "/data/log/hiperflog/.hiperf_record_control_s2c"; - const std::string PERF_CPU_TIME_MAX_PERCENT = "/proc/sys/kernel/perf_cpu_time_max_percent"; const std::string PERF_EVENT_MAX_SAMPLE_RATE = "/proc/sys/kernel/perf_event_max_sample_rate"; const std::string PERF_EVENT_MLOCK_KB = "/proc/sys/kernel/perf_event_mlock_kb"; @@ -64,7 +55,8 @@ const std::string SAVED_CMDLINES_SIZE = "/sys/kernel/tracing/saved_cmdlines_size // when there are many events, start record will take more time. const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT = 2000ms; -const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT_CHECK = 1000ms; +const int CONTROL_WAIT_RESPONSE_TIMEOUT = 35000; // 35s: Timeout for main process wait app restart finish +constexpr const uint8_t WAIT_TIMEOUT = 40; // 40s: Timeout for the main process exit constexpr uint64_t MASK_ALIGNED_8 = 7; constexpr size_t MAX_DWARF_CALL_CHAIN = 2; @@ -72,9 +64,11 @@ constexpr uint64_t TYPE_PERF_SAMPLE_BRANCH = PERF_SAMPLE_BRANCH_ANY | PERF_SAMPL PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_JUMP | PERF_SAMPLE_BRANCH_IND_CALL | PERF_SAMPLE_BRANCH_COND | PERF_SAMPLE_BRANCH_CALL; +static constexpr uint64_t PIPE_READ = 0; +static constexpr uint64_t PIPE_WRITE = 1; static constexpr uint64_t CHECK_WAIT_TIME_MS = 200; static constexpr uint32_t MAX_SERVER_OUTPUT_WAIT_COUNT = 600; -static constexpr uint32_t MAX_CLIENT_OUTPUT_WAIT_COUNT = 240; +static std::atomic_bool g_callStop(false); int GetClockId(const std::string &name) { @@ -116,7 +110,16 @@ uint64_t GetBranchSampleType(const std::string &name) SubCommandRecord::~SubCommandRecord() { + CloseReplyThread(); CloseClientThread(); + if (readFd_ != -1) { + close(readFd_); + readFd_ = -1; + } + if (writeFd_ != -1) { + close(writeFd_); + writeFd_ = -1; + } } void SubCommandRecord::DumpOptions() const @@ -349,6 +352,9 @@ bool SubCommandRecord::GetOptions(std::vector &args) if (!Option::GetOptionValue(args, "--pipe_output", clientPipeOutput_)) { return false; } + if (clientPipeInput_ != -1 && clientPipeOutput_ != -1) { + isHiperfClient_ = true; + } if (!Option::GetOptionValue(args, "--dedup_stack", dedupStack_)) { return false; } @@ -630,26 +636,14 @@ bool SubCommandRecord::CheckTargetProcessOptions() return false; } if (controlCmd_ == CONTROL_CMD_PREPARE) { - CHECK_TRUE(!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_), false, 0, ""); + CHECK_TRUE(!CheckAppRestart(), false, 0, ""); } - return CheckTargetPids(); + return IsPidAndTidExist(); } bool SubCommandRecord::CheckTargetPids() { - for (auto pid : selectPids_) { - if (!IsDir("/proc/" + std::to_string(pid))) { - printf("not exist pid %d\n", pid); - return false; - } - } - for (auto tid : selectTids_) { - if (!IsDir("/proc/" + std::to_string(tid))) { - printf("not exist tid %d\n", tid); - return false; - } - } - if (!CheckAppIsRunning(selectPids_, appPackage_, checkAppMs_)) { + if (!IsAppRunning()) { return false; } if (!selectPids_.empty()) { @@ -671,6 +665,23 @@ bool SubCommandRecord::CheckTargetPids() return true; } +bool SubCommandRecord::IsPidAndTidExist() +{ + for (auto pid : selectPids_) { + if (!IsDir("/proc/" + std::to_string(pid))) { + printf("not exist pid %d\n", pid); + return false; + } + } + for (auto tid : selectTids_) { + if (!IsDir("/proc/" + std::to_string(tid))) { + printf("not exist tid %d\n", tid); + return false; + } + } + return true; +} + bool SubCommandRecord::CheckReportOption() { if (targetSystemWide_ && report_) { @@ -713,6 +724,96 @@ bool SubCommandRecord::CheckSpeOption() return true; } +void SubCommandRecord::MsgPrintAndTrans(bool isTrans, const std::string& msg) +{ + if (isTrans && controlCmd_ == CONTROL_CMD_PREPARE) { + ChildResponseToMain(msg); + } + printf("%s", msg.c_str()); +} + +bool SubCommandRecord::IsAppRestarted() +{ + std::string info = "please restart " + appPackage_ + " for profiling within 30 seconds\n"; + MsgPrintAndTrans(true, info); + std::set oldPids {}; + std::set newPids {}; + std::vector intersection; + const auto startTime = steady_clock::now(); + const auto endTime = startTime + std::chrono::seconds(CHECK_TIMEOUT); + CollectPidsByAppname(oldPids, appPackage_); + do { + CollectPidsByAppname(newPids, appPackage_); + std::set_intersection(oldPids.begin(), oldPids.end(), + newPids.begin(), newPids.end(), std::back_insert_iterator(intersection)); + // app names are same, no intersection, means app restarted + CHECK_TRUE(intersection.empty(), true, 0, ""); + intersection.clear(); + newPids.clear(); + std::this_thread::sleep_for(milliseconds(CHECK_FREQUENCY)); + } while (steady_clock::now() < endTime && !g_callStop.load()); + std::string err = "app " + appPackage_ + " was not stopped within 30 seconds\n"; + MsgPrintAndTrans(!g_callStop.load(), err); + return false; +} + +bool SubCommandRecord::CheckAppRestart() +{ + if (!restart_) { + return true; + } + if (appPackage_.empty()) { + printf("to detect the performance of application startup, --app option must be given\n"); + return false; + } + if (targetSystemWide_ || !selectPids_.empty()) { + printf("option --restart and -p/-a is conflict, please check usage\n"); + return false; + } + return true; +} + +pid_t SubCommandRecord::GetPidFromAppPackage(const pid_t oldPid, const uint64_t waitAppTimeOut) +{ + pid_t res {-1}; + const std::string basePath {"/proc/"}; + const std::string cmdline {"/cmdline"}; + const auto startTime = steady_clock::now(); + const auto endTime = startTime + std::chrono::seconds(waitAppTimeOut); + do { + std::vector subDirs = GetSubDirs(basePath); + for (const auto &subDir : subDirs) { + if (!IsDigits(subDir)) { + continue; + } + std::string fileName {basePath + subDir + cmdline}; + if (IsSameCommand(ReadFileToString(fileName), appPackage_)) { + res = std::stoul(subDir, nullptr); + HLOGD("[GetAppPackagePid]: get appid for %s is %d", appPackage_.c_str(), res); + return res; + } + } + std::this_thread::sleep_for(milliseconds(checkAppMs_)); + } while (steady_clock::now() < endTime && !g_callStop.load()); + + return res; +} + +bool SubCommandRecord::IsAppRunning() +{ + if (!appPackage_.empty()) { + pid_t appPid = GetPidFromAppPackage(-1, waitAppRunCheckTimeOut); + if (appPid <= 0) { + std::string err = "app " + appPackage_ + " not running\n"; + MsgPrintAndTrans(!g_callStop.load(), err); + return false; + } + HLOGD("[CheckAppIsRunning] get appPid %d for app %s", appPid, appPackage_.c_str()); + selectPids_.push_back(appPid); + } + return true; +} + bool SubCommandRecord::ParseDataLimitOption(const std::string &str) { uint unit = 1; @@ -1091,11 +1192,79 @@ bool SubCommandRecord::ClientCommandResponse(bool response) bool SubCommandRecord::ClientCommandResponse(const std::string& str) { + if (!isHiperfClient_) { + clientPipeOutput_ = open(fifoFileS2C_.c_str(), O_WRONLY); + if (clientPipeOutput_ == -1) { + char errInfo[ERRINFOLEN] = { 0 }; + strerror_r(errno, errInfo, ERRINFOLEN); + HLOGE("open fifo file(%s) failed, errno:%d:%s", fifoFileS2C_.c_str(), errno, errInfo); + HIPERF_HILOGE(MODULE_DEFAULT, "open fifo file(%{public}s) failed, errno:%{public}d:%{public}s", + fifoFileS2C_.c_str(), errno, errInfo); + return false; + } + } size_t size = write(clientPipeOutput_, str.c_str(), str.size()); if (size != str.size()) { char errInfo[ERRINFOLEN] = { 0 }; strerror_r(errno, errInfo, ERRINFOLEN); HLOGD("Server:%s -> %d : %zd %d:%s", str.c_str(), clientPipeOutput_, size, errno, errInfo); + HIPERF_HILOGE(MODULE_DEFAULT, "Server:%{public}s -> %{public}d : %{public}zd %{public}d:%{public}s", + str.c_str(), clientPipeOutput_, size, errno, errInfo); + return false; + } + return true; +} + +bool SubCommandRecord::ChildResponseToMain(bool response) +{ + return ChildResponseToMain(response ? HiperfClient::ReplyOK : HiperfClient::ReplyFAIL); +} + +bool SubCommandRecord::ChildResponseToMain(const std::string& str) +{ + int tempFd = isHiperfClient_ ? clientPipeOutput_ : writeFd_; + size_t size = write(tempFd, str.c_str(), str.size()); + if (size != str.size()) { + char errInfo[ERRINFOLEN] = { 0 }; + strerror_r(errno, errInfo, ERRINFOLEN); + HLOGE("write pipe failed. str:%s, size:%zd, errno:%d:%s", str.c_str(), size, errno, errInfo); + HIPERF_HILOGE(MODULE_DEFAULT, + "write pipe failed. str:%{public}s, size:%{public}zd, errno:%{public}d:%{public}s", + str.c_str(), size, errno, errInfo); + return false; + } + return true; +} + +bool SubCommandRecord::MainRecvFromChild(int fd, std::string& reply) +{ + struct pollfd pollFd { + fd, POLLIN, 0 + }; + int polled = poll(&pollFd, 1, CONTROL_WAIT_RESPONSE_TIMEOUT); + reply.clear(); + if (polled > 0) { + bool exitLoop = false; + while (!exitLoop) { + char c; + ssize_t result = TEMP_FAILURE_RETRY(read(fd, &c, 1)); + if (result <= 0) { + HLOGD("read from pipeFd failed"); + HIPERF_HILOGE(MODULE_DEFAULT, "read from pipeFd failed"); + exitLoop = true; + } + reply.push_back(c); + if (c == '\n') { + exitLoop = true; + } + } + } else if (polled == 0) { + HLOGD("wait pipeFd timeout"); + HIPERF_HILOGE(MODULE_DEFAULT, "wait pipeFd timeout"); + return false; + } else { + HLOGD("wait pipeFd failed"); + HIPERF_HILOGE(MODULE_DEFAULT, "wait pipeFd failed"); return false; } return true; @@ -1136,6 +1305,7 @@ bool SubCommandRecord::PreOutputRecordFile() return false; } outputEnd_ = false; + perfPipe_.SetOutPutEnd(outputEnd_); return true; } @@ -1165,6 +1335,7 @@ bool SubCommandRecord::PostOutputRecordFile(bool output) fileWriter_ = nullptr; outputEnd_ = true; + perfPipe_.SetOutPutEnd(outputEnd_); StringViewHold::Get().Clean(); return true; } @@ -1202,20 +1373,32 @@ void SubCommandRecord::InitControlCommandHandlerMap() }); } +inline void SubCommandRecord::CreateReplyThread() +{ + replyCommandHandle_ = std::thread(&SubCommandRecord::ReplyCommandHandle, this); +} + +void SubCommandRecord::ReplyCommandHandle() +{ + if (!IsSamplingRunning()) { + HLOGI("IsSamplingRunning() return false"); + HIPERF_HILOGI(MODULE_DEFAULT, "IsSamplingRunning() return false"); + ChildResponseToMain(false); + isHiperfClient_ = false; + return; + } + ChildResponseToMain(true); +} + inline void SubCommandRecord::CreateClientThread() { // make a thread wait the other command - if (clientPipeOutput_ != -1) { - clientCommandHanle_ = std::thread(&SubCommandRecord::ClientCommandHandle, this); - } + clientCommandHandle_ = std::thread(&SubCommandRecord::ClientCommandHandle, this); } void SubCommandRecord::ClientCommandHandle() { using namespace HiperfClient; - CHECK_TRUE(!IsSamplingRunning(), NO_RETVAL, 0, ""); - // tell the caller if Exist - ClientCommandResponse(true); InitControlCommandHandlerMap(); bool hasRead = true; @@ -1225,7 +1408,7 @@ void SubCommandRecord::ClientCommandHandle() // after read(), block is disabled, the poll will be waked neven if no data close(clientPipeInput_); } - clientPipeInput_ = open(CONTROL_FIFO_FILE_C2S.c_str(), O_RDONLY | O_NONBLOCK); + clientPipeInput_ = open(fifoFileC2S_.c_str(), O_RDONLY | O_NONBLOCK); } struct pollfd pollFd { clientPipeInput_, POLLIN, 0 @@ -1251,6 +1434,11 @@ void SubCommandRecord::ClientCommandHandle() } HLOGD("server:new command %s", command.c_str()); HIPERF_HILOGI(MODULE_DEFAULT, "server:new command : %{public}s", command.c_str()); + if (command.find("STOP") != std::string::npos) { + HLOGD("receive sop command, set g_callStop to true"); + HIPERF_HILOGI(MODULE_DEFAULT, "receive sop command, set g_callStop to true"); + g_callStop.store(true); + } DispatchControlCommand(command); } } @@ -1274,99 +1462,46 @@ bool SubCommandRecord::ProcessControl() return true; } HIPERF_HILOGI(MODULE_DEFAULT, "control cmd : %{public}s", controlCmd_.c_str()); + perfPipe_.SetFifoFileName(CommandType::RECORD, controlCmd_, fifoFileC2S_, fifoFileS2C_); if (controlCmd_ == CONTROL_CMD_PREPARE) { CHECK_TRUE(!CreateFifoServer(), false, 0, ""); return true; } isFifoClient_ = true; - bool ret = false; - if (controlCmd_ == CONTROL_CMD_START) { - ret = SendFifoAndWaitReply(HiperfClient::ReplyStart, CONTROL_WAITREPY_TIMEOUT); - } else if (controlCmd_ == CONTROL_CMD_RESUME) { - ret = SendFifoAndWaitReply(HiperfClient::ReplyResume, CONTROL_WAITREPY_TIMEOUT); - } else if (controlCmd_ == CONTROL_CMD_PAUSE) { - ret = SendFifoAndWaitReply(HiperfClient::ReplyPause, CONTROL_WAITREPY_TIMEOUT); - } else if (controlCmd_ == CONTROL_CMD_STOP) { - ret = SendFifoAndWaitReply(HiperfClient::ReplyStop, CONTROL_WAITREPY_TIMEOUT); - if (!ret) { - ret = SendFifoAndWaitReply(HiperfClient::ReplyStop, CONTROL_WAITREPY_TIMEOUT); - } - ProcessStopCommand(ret); - } else if (controlCmd_ == CONTROL_CMD_OUTPUT) { - ret = SendFifoAndWaitReply(HiperfClient::ReplyOutput, CONTROL_WAITREPY_TIMEOUT); - ProcessOutputCommand(ret); - } - - if (ret) { - printf("%s sampling success.\n", controlCmd_.c_str()); - HIPERF_HILOGI(MODULE_DEFAULT, "%{public}s sampling success.", controlCmd_.c_str()); - } else { - printf("%s sampling failed.\n", controlCmd_.c_str()); - HIPERF_HILOGI(MODULE_DEFAULT, "%{public}s sampling failed.", controlCmd_.c_str()); - } - return ret; -} - -void SubCommandRecord::ProcessStopCommand(bool ret) -{ - if (ret) { - // wait sampling process exit really - static constexpr uint64_t waitCheckSleepMs = 200; - std::this_thread::sleep_for(milliseconds(waitCheckSleepMs)); - while (SendFifoAndWaitReply(HiperfClient::ReplyCheck, CONTROL_WAITREPY_TIMEOUT_CHECK)) { - std::this_thread::sleep_for(milliseconds(waitCheckSleepMs)); - } - HLOGI("wait reply check end."); - } - - if (remove(CONTROL_FIFO_FILE_C2S.c_str()) != 0) { - HLOGE("remove fifo file %s failed", CONTROL_FIFO_FILE_C2S.c_str()); - } - if (remove(CONTROL_FIFO_FILE_S2C.c_str()) != 0) { - HLOGE("remove fifo file %s failed", CONTROL_FIFO_FILE_S2C.c_str()); - } + return perfPipe_.ProcessControlCmd(); } -void SubCommandRecord::ProcessOutputCommand(bool ret) +void SubCommandRecord::RemoveFifoFile() { - if (!ret) { - HLOGI("send fifo and wait repoy fail"); - return; + char errInfo[ERRINFOLEN] = { 0 }; + if (remove(fifoFileC2S_.c_str()) != 0) { + strerror_r(errno, errInfo, ERRINFOLEN); + HLOGE("remove %s failed, errno:(%d:%s)", fifoFileC2S_.c_str(), errno, errInfo); + HIPERF_HILOGE(MODULE_DEFAULT, "remove %{public}s failed, errno:(%{public}d:%{public}s)", + fifoFileC2S_.c_str(), errno, errInfo); } - - std::this_thread::sleep_for(milliseconds(CHECK_WAIT_TIME_MS)); - uint32_t outputFailCount = 0; - while (!outputEnd_) { - ret = SendFifoAndWaitReply(HiperfClient::ReplyOutputCheck, CONTROL_WAITREPY_TIMEOUT_CHECK); - if (outputFailCount++ > MAX_CLIENT_OUTPUT_WAIT_COUNT || ret) { - break; - } - std::this_thread::sleep_for(milliseconds(CHECK_WAIT_TIME_MS)); + if (remove(fifoFileS2C_.c_str()) != 0) { + strerror_r(errno, errInfo, ERRINFOLEN); + HLOGE("remove %s failed, errno:(%d:%s)", fifoFileS2C_.c_str(), errno, errInfo); + HIPERF_HILOGE(MODULE_DEFAULT, "remove %{public}s failed, errno:(%{public}d:%{public}s)", + fifoFileS2C_.c_str(), errno, errInfo); } } bool SubCommandRecord::CreateFifoServer() { char errInfo[ERRINFOLEN] = { 0 }; - const mode_t fifoMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; - std::string tempPath("/data/log/hiperflog/"); - if (!IsDirectoryExists(tempPath)) { - HIPERF_HILOGI(MODULE_DEFAULT, "%{public}s not exist.", tempPath.c_str()); - if (!CreateDirectory(tempPath, HIPERF_FILE_PERM_770)) { - HIPERF_HILOGI(MODULE_DEFAULT, "create %{public}s failed.", tempPath.c_str()); - } - } - if (mkfifo(CONTROL_FIFO_FILE_S2C.c_str(), fifoMode) != 0 || - mkfifo(CONTROL_FIFO_FILE_C2S.c_str(), fifoMode) != 0) { - if (errno == EEXIST) { - printf("another sampling service is running.\n"); - } else { - remove(CONTROL_FIFO_FILE_S2C.c_str()); - remove(CONTROL_FIFO_FILE_C2S.c_str()); - } + if (!perfPipe_.CreateFifoFile()) { + return false; + } + + int pipeFd[2]; + if (pipe(pipeFd) == -1) { strerror_r(errno, errInfo, ERRINFOLEN); - HLOGE("create fifo file failed. %d:%s", errno, errInfo); + HLOGE("pipe creation error, errno:(%d:%s)", errno, errInfo); + HIPERF_HILOGE(MODULE_DEFAULT, "pipe creation error, errno:(%{public}d:%{public}s)", errno, errInfo); + RemoveFifoFile(); return false; } @@ -1377,113 +1512,61 @@ bool SubCommandRecord::CreateFifoServer() if (pid == -1) { strerror_r(errno, errInfo, ERRINFOLEN); HLOGE("fork failed. %d:%s", errno, errInfo); + close(pipeFd[PIPE_READ]); + close(pipeFd[PIPE_WRITE]); return false; } else if (pid == 0) { // child process close(STDIN_FILENO); close(STDERR_FILENO); + close(pipeFd[PIPE_READ]); + writeFd_ = pipeFd[PIPE_WRITE]; isFifoServer_ = true; - clientPipeOutput_ = open(CONTROL_FIFO_FILE_S2C.c_str(), O_WRONLY); - if (clientPipeOutput_ == -1) { - strerror_r(errno, errInfo, ERRINFOLEN); - HLOGE("open fifo file(%s) failed. %d:%s", CONTROL_FIFO_FILE_S2C.c_str(), errno, errInfo); - return false; - } nullFd_ = open("/dev/null", O_WRONLY); (void)dup2(nullFd_, STDOUT_FILENO); // redirect stdout to /dev/null - std::string err = HandleAppInfo(); - if (!err.empty()) { - ClientCommandResponse(err); - return false; - } } else { // parent process + close(pipeFd[PIPE_WRITE]); + readFd_ = pipeFd[PIPE_READ]; isFifoClient_ = true; - int fd = open(CONTROL_FIFO_FILE_S2C.c_str(), O_RDONLY | O_NONBLOCK); - std::string reply = ""; - if (fd != -1) { - WaitFifoReply(fd, CONTROL_WAITREPY_TIMEOUT, reply); - } - if (fd == -1 || reply != HiperfClient::ReplyOK) { - if (reply != HiperfClient::ReplyOK) { - printf("%s\n", reply.c_str()); + bool isSuccess = false; + bool isPrint = false; + const auto startTime = steady_clock::now(); + const auto endTime = startTime + std::chrono::seconds(WAIT_TIMEOUT); + do { + std::string reply = ""; + bool ret = MainRecvFromChild(readFd_, reply); + if (ret && reply.find("OK") != std::string::npos) { + printf("%s control hiperf sampling success.\n", restart_ ? "start" : "create"); + isSuccess = true; + break; } - close(fd); - kill(pid, SIGKILL); - remove(CONTROL_FIFO_FILE_C2S.c_str()); - remove(CONTROL_FIFO_FILE_S2C.c_str()); - strerror_r(errno, errInfo, ERRINFOLEN); - printf("create control hiperf sampling failed. %d:%s\n", errno, errInfo); - return false; - } - close(fd); - printf("%s control hiperf sampling success.\n", restart_ ? "start" : "create"); - } - return true; -} - -bool SubCommandRecord::SendFifoAndWaitReply(const std::string &cmd, const std::chrono::milliseconds &timeOut) -{ - // need open for read first, because server maybe send reply before client wait to read - int fdRead = open(CONTROL_FIFO_FILE_S2C.c_str(), O_RDONLY | O_NONBLOCK); - if (fdRead == -1) { - HLOGE("can not open fifo file(%s)", CONTROL_FIFO_FILE_S2C.c_str()); - HIPERF_HILOGI(MODULE_DEFAULT, "can not open fifo file: %{public}s.", CONTROL_FIFO_FILE_S2C.c_str()); - return false; - } - int fdWrite = open(CONTROL_FIFO_FILE_C2S.c_str(), O_WRONLY | O_NONBLOCK); - if (fdWrite == -1) { - HLOGE("can not open fifo file(%s)", CONTROL_FIFO_FILE_C2S.c_str()); - HIPERF_HILOGI(MODULE_DEFAULT, "can not open fifo file: %{public}s.", CONTROL_FIFO_FILE_C2S.c_str()); - close(fdRead); - return false; - } - size_t size = write(fdWrite, cmd.c_str(), cmd.size()); - if (size != cmd.size()) { - HLOGE("failed to write fifo file(%s) command(%s)", CONTROL_FIFO_FILE_C2S.c_str(), - cmd.c_str()); - HIPERF_HILOGI(MODULE_DEFAULT, "failed to write fifo file: %{public}s.", CONTROL_FIFO_FILE_C2S.c_str()); - close(fdWrite); - close(fdRead); - return false; - } - close(fdWrite); - - bool ret = WaitFifoReply(fdRead, timeOut); - close(fdRead); - return ret; -} - -bool SubCommandRecord::WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut) -{ - std::string reply; - WaitFifoReply(fd, timeOut, reply); - return reply == HiperfClient::ReplyOK; -} - -void SubCommandRecord::WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut, std::string& reply) -{ - struct pollfd pollFd { - fd, POLLIN, 0 - }; - int polled = poll(&pollFd, 1, timeOut.count()); - reply.clear(); - if (polled > 0) { - while (true) { - char c; - ssize_t result = TEMP_FAILURE_RETRY(read(fd, &c, 1)); - if (result <= 0) { - HLOGD("read from fifo file(%s) failed", CONTROL_FIFO_FILE_S2C.c_str()); + HLOGE("reply is (%s)", reply.c_str()); + HIPERF_HILOGE(MODULE_DEFAULT, "reply is (%s)", reply.c_str()); + if (ret && reply.find("FAIL") == std::string::npos) { + printf("%s", reply.c_str()); + if (reply.find("debug application") != std::string::npos) { + isPrint = true; + } + continue; + } + if (ret && reply.find("FAIL") != std::string::npos) { break; } - reply.push_back(c); - if (c == '\n') { + if (!ret) { + isPrint = true; break; } + } while (steady_clock::now() < endTime); + if (!isSuccess) { + kill(pid, SIGKILL); + RemoveFifoFile(); + if (isPrint) { + strerror_r(errno, errInfo, ERRINFOLEN); + printf("create control hiperf sampling failed. %d:%s\n", errno, errInfo); + return false; + } } - } else if (polled == 0) { - HLOGD("wait fifo file(%s) timeout", CONTROL_FIFO_FILE_S2C.c_str()); - } else { - HLOGD("wait fifo file(%s) failed", CONTROL_FIFO_FILE_S2C.c_str()); } + return true; } HiperfError SubCommandRecord::OnSubCommand(std::vector& args) @@ -1495,27 +1578,93 @@ HiperfError SubCommandRecord::OnSubCommand(std::vector& args) return HiperfError::NO_ERR; } + if (controlCmd_ == CONTROL_CMD_PREPARE) { + CreateClientThread(); + if (!appPackage_.empty() && restart_) { + if (!IsAppRestarted()) { + ChildResponseToMain(false); + CloseClientThread(); + return HiperfError::CHECK_RESTART_OPTION_FAIL; + } + } + } + + if (!CheckTargetPids()) { + if (controlCmd_ == CONTROL_CMD_PREPARE) { + ChildResponseToMain(false); + CloseClientThread(); + } + if (controlCmd_.empty()) { + return HiperfError::NO_ERR; + } else { + return HiperfError::CHECK_OPTION_PID_FAIL; + } + } + std::string err = OHOS::Developtools::HiPerf::HandleAppInfo(appPackage_, inputPidTidArgs_); + if (!err.empty()) { + ChildResponseToMain(err); + ChildResponseToMain(false); + CloseClientThread(); + return HiperfError::CHECK_DEBUG_APP_FAIL; + } // prepare PerfEvents - RETURN_IF(!PrepareSysKernel(), HiperfError::PREPARE_SYS_KERNEL_FAIL); - RETURN_IF(!PreparePerfEvent(), HiperfError::PREPARE_PERF_EVENT_FAIL); + if (!PrepareSysKernel()) { + if (controlCmd_ == CONTROL_CMD_PREPARE) { + ChildResponseToMain(false); + CloseClientThread(); + } + return HiperfError::PREPARE_SYS_KERNEL_FAIL; + } + + if (!PreparePerfEvent()) { + if (controlCmd_ == CONTROL_CMD_PREPARE) { + ChildResponseToMain(false); + CloseClientThread(); + } + return HiperfError::PREPARE_PERF_EVENT_FAIL; + } // prepar some attr before CreateInitRecordFile - CHECK_TRUE(!perfEvents_.PrepareTracking(), HiperfError::PREPARE_TACKING_FAIL, - LOG_TYPE_WITH_HILOG, "Fail to prepare tracking "); + if (!perfEvents_.PrepareTracking()) { + if (controlCmd_ == CONTROL_CMD_PREPARE) { + ChildResponseToMain(false); + CloseClientThread(); + } + HIPERF_HILOGE(MODULE_DEFAULT, "Fail to prepare tracking"); + HLOGE("Fail to prepare tracking"); + return HiperfError::PREPARE_TACKING_FAIL; + } HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord perfEvents prepared"); if (!backtrack_ && !CreateInitRecordFile(delayUnwind_ ? false : compressData_)) { + if (controlCmd_ == CONTROL_CMD_PREPARE) { + ChildResponseToMain(false); + CloseClientThread(); + } HLOGE("Fail to create record file %s", outputFilename_.c_str()); HIPERF_HILOGE(MODULE_DEFAULT, "Fail to create record file %{public}s", outputFilename_.c_str()); return HiperfError::CREATE_OUTPUT_FILE_FAIL; } - RETURN_IF(!PrepareVirtualRuntime(), HiperfError::PREPARE_VIRTUAL_RUNTIME_FAIL); + if (!PrepareVirtualRuntime()) { + if (controlCmd_ == CONTROL_CMD_PREPARE) { + ChildResponseToMain(false); + CloseClientThread(); + } + HLOGE("Fail to prepare virtualRuntime"); + HIPERF_HILOGE(MODULE_DEFAULT, "Fail to prepare virtualRuntime"); + return HiperfError::PREPARE_VIRTUAL_RUNTIME_FAIL; + } HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord virtualRuntime prepared"); - CreateClientThread(); + if (controlCmd_ == CONTROL_CMD_PREPARE || isHiperfClient_) { + CreateReplyThread(); + } + if (isHiperfClient_) { + CreateClientThread(); + } //write comm event WriteCommEventBeforeSampling(); SetExcludeHiperf(); @@ -1553,6 +1702,7 @@ HiperfError SubCommandRecord::OnSubCommand(std::vector& args) // finial report RecoverSavedCmdlinesSize(); OnlineReportData(); + CloseReplyThread(); CloseClientThread(); RemoveVdsoTmpFile(); AgeHiperflogFiles(); @@ -1562,22 +1712,30 @@ HiperfError SubCommandRecord::OnSubCommand(std::vector& args) void SubCommandRecord::CloseClientThread() { - if (clientCommandHanle_.joinable()) { + if (clientCommandHandle_.joinable()) { clientRunning_ = false; HLOGI("CloseClientThread"); if (nullFd_ != -1) { close(nullFd_); } - clientCommandHanle_.join(); + clientCommandHandle_.join(); close(clientPipeInput_); close(clientPipeOutput_); if (isFifoServer_) { - remove(CONTROL_FIFO_FILE_C2S.c_str()); - remove(CONTROL_FIFO_FILE_S2C.c_str()); + RemoveFifoFile(); } } } +void SubCommandRecord::CloseReplyThread() +{ + if (replyCommandHandle_.joinable()) { + clientRunning_ = false; + HLOGI("CloseReplyThread"); + replyCommandHandle_.join(); + } +} + void SubCommandRecord::RemoveVdsoTmpFile() { std::vector fileName = {"/data/log/hiperflog/[shmm]", "/data/log/hiperflog/[vdso]"}; @@ -2225,21 +2383,6 @@ bool SubCommandRecord::OnlineReportData() return ret; } -std::string SubCommandRecord::HandleAppInfo() -{ - std::string err = ""; - if (!appPackage_.empty()) { - if (!IsExistDebugByApp(appPackage_, err)) { - return err; - } - } else { - if (!IsExistDebugByPid(inputPidTidArgs_, err)) { - return err; - } - } - return err; -} - void SubCommandRecord::AddReportArgs(CommandReporter& reporter) { if (targetSystemWide_) { diff --git a/src/subcommand_stat.cpp b/src/subcommand_stat.cpp index 055e695..7ac4c30 100644 --- a/src/subcommand_stat.cpp +++ b/src/subcommand_stat.cpp @@ -37,15 +37,9 @@ const uint16_t THOUSANDS_SEPARATOR = 3; namespace OHOS { namespace Developtools { namespace HiPerf { -const std::string CONTROL_CMD_PREPARE = "prepare"; -const std::string CONTROL_CMD_START = "start"; -const std::string CONTROL_CMD_STOP = "stop"; -const std::string CONTROL_FIFO_FILE_C2S = "/data/log/hiperflog/.hiperf_stat_control_c2s"; -const std::string CONTROL_FIFO_FILE_S2C = "/data/log/hiperflog/.hiperf_stat_control_s2c"; const std::string DEFAULT_STAT_FILE = "/data/local/tmp/perf_stat.txt"; // when there are many events, start record will take more time. const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT = 2000ms; -const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT_CHECK = 1000ms; static std::map thread_map_; static bool g_reportCpuFlag = false; static bool g_reportThreadFlag = false; @@ -218,8 +212,8 @@ void SubCommandStat::PrintPerHead(FILE* filePtr) printf(" %24s %-30s | %-30s %10s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name", "pid", "tid", "coreid", "comment", "coverage"); } else { - fprintf(filePtr, " %24s %-30s | %-30s %10s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name", - "pid", "tid", "coreid", "comment", "coverage"); + fprintf(filePtr, " %24s %-30s | %-30s %10s %10s %10s | %-32s | %s\n", + "count", "event_name", "thread_name", "pid", "tid", "coreid", "comment", "coverage"); } return; } @@ -227,7 +221,8 @@ void SubCommandStat::PrintPerHead(FILE* filePtr) if (filePtr == nullptr) { printf(" %24s %-30s | %10s | %-32s | %s\n", "count", "event_name", "coreid", "comment", "coverage"); } else { - fprintf(filePtr, " %24s %-30s | %10s | %-32s | %s\n", "count", "event_name", "coreid", "comment", "coverage"); + fprintf(filePtr, " %24s %-30s | %10s | %-32s | %s\n", + "count", "event_name", "coreid", "comment", "coverage"); } return; } @@ -235,8 +230,8 @@ void SubCommandStat::PrintPerHead(FILE* filePtr) printf(" %24s %-30s | %-30s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name", "pid", "tid", "comment", "coverage"); } else { - fprintf(filePtr, " %24s %-30s | %-30s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name", "pid", - "tid", "comment", "coverage"); + fprintf(filePtr, " %24s %-30s | %-30s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name", + "pid", "tid", "comment", "coverage"); } return; } @@ -266,8 +261,8 @@ void SubCommandStat::PrintPerValue(const std::unique_ptr commentStr.c_str(), reportSum->scaleSum * ratio); } else { fprintf(filePtr, " %24s %-30s | %-30s %10d %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), - configName.c_str(), reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, - reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio); + configName.c_str(), reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, + reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio); } } else if (g_reportCpuFlag) { if (filePtr == nullptr) { @@ -275,7 +270,7 @@ void SubCommandStat::PrintPerValue(const std::unique_ptr reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio); } else { fprintf(filePtr, " %24s %-30s | %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(), - reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio); + reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio); } } else { if (filePtr == nullptr) { @@ -284,8 +279,8 @@ void SubCommandStat::PrintPerValue(const std::unique_ptr reportSum->scaleSum * ratio); } else { fprintf(filePtr, " %24s %-30s | %-30s %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), - configName.c_str(), reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, - commentStr.c_str(), reportSum->scaleSum * ratio); + configName.c_str(), reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, + commentStr.c_str(), reportSum->scaleSum * ratio); } } fflush(stdout); @@ -387,7 +382,7 @@ void SubCommandStat::ReportNormal( comment.c_str(), scale * ratio); } else { fprintf(filePtr, " %24s %-30s | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(), - comment.c_str(), scale * ratio); + comment.c_str(), scale * ratio); } fflush(stdout); } @@ -706,65 +701,12 @@ void SubCommandStat::SetPerfEvent() perfEvents_.SetStatReportFd(filePtr_); } -std::string SubCommandStat::HandleAppInfo() -{ - std::string err = ""; - if (!appPackage_.empty()) { - if (!IsExistDebugByApp(appPackage_, err)) { - return err; - } - } else { - if (!IsExistDebugByPid(inputPidTidArgs_, err)) { - return err; - } - } - return err; -} - -void SubCommandStat::ProcessStopCommand(bool ret) -{ - if (ret) { - // wait counting process exit really - static constexpr uint64_t waitCheckSleepMs = 200; - std::this_thread::sleep_for(milliseconds(waitCheckSleepMs)); - while (SendFifoAndWaitReply(HiperfClient::ReplyCheck, CONTROL_WAITREPY_TIMEOUT_CHECK)) { - std::this_thread::sleep_for(milliseconds(waitCheckSleepMs)); - } - HLOGI("wait reply check end."); - } - - if (remove(CONTROL_FIFO_FILE_C2S.c_str()) != 0) { - HLOGE("remove fifo file %s failed", CONTROL_FIFO_FILE_C2S.c_str()); - } - if (remove(CONTROL_FIFO_FILE_S2C.c_str()) != 0) { - HLOGE("remove fifo file %s failed", CONTROL_FIFO_FILE_S2C.c_str()); - } -} - bool SubCommandStat::CreateFifoServer() { char errInfo[ERRINFOLEN] = { 0 }; - const mode_t fifoMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; - std::string tempPath("/data/log/hiperflog/"); - if (!IsDirectoryExists(tempPath)) { - HIPERF_HILOGI(MODULE_DEFAULT, "%{public}s not exist.", tempPath.c_str()); - if (!CreateDirectory(tempPath, HIPERF_FILE_PERM_770)) { - HIPERF_HILOGI(MODULE_DEFAULT, "create %{public}s failed.", tempPath.c_str()); - } - } - if (mkfifo(CONTROL_FIFO_FILE_S2C.c_str(), fifoMode) != 0 || - mkfifo(CONTROL_FIFO_FILE_C2S.c_str(), fifoMode) != 0) { - if (errno == EEXIST) { - printf("another counting service is running.\n"); - } else { - remove(CONTROL_FIFO_FILE_S2C.c_str()); - remove(CONTROL_FIFO_FILE_C2S.c_str()); - } - strerror_r(errno, errInfo, ERRINFOLEN); - HLOGE("create fifo file failed. %d:%s", errno, errInfo); + if (!perfPipe_.CreateFifoFile()) { return false; } - CheckIpcBeforeFork(); pid_t pid = fork(); allowIpc_ = true; @@ -777,31 +719,31 @@ bool SubCommandStat::CreateFifoServer() close(STDIN_FILENO); close(STDERR_FILENO); isFifoServer_ = true; - clientPipeOutput_ = open(CONTROL_FIFO_FILE_S2C.c_str(), O_WRONLY); + clientPipeOutput_ = open(fifoFileS2C_.c_str(), O_WRONLY); if (clientPipeOutput_ == -1) { strerror_r(errno, errInfo, ERRINFOLEN); - HLOGE("open fifo file(%s) failed. %d:%s", CONTROL_FIFO_FILE_S2C.c_str(), errno, errInfo); + HLOGE("open fifo file(%s) failed. %d:%s", fifoFileS2C_.c_str(), errno, errInfo); HIPERF_HILOGE(MODULE_DEFAULT, "open fifo file(%{public}s) failed. %d:%s", - CONTROL_FIFO_FILE_S2C.c_str(), errno, errInfo); + fifoFileS2C_.c_str(), errno, errInfo); return false; } nullFd_ = open("/dev/null", O_WRONLY); (void)dup2(nullFd_, STDOUT_FILENO); // redirect stdout to /dev/null - std::string err = HandleAppInfo(); + std::string err = OHOS::Developtools::HiPerf::HandleAppInfo(appPackage_, inputPidTidArgs_); if (!err.empty()) { ClientCommandResponse(err); return false; } } else { // parent process isFifoClient_ = true; - int fd = open(CONTROL_FIFO_FILE_S2C.c_str(), O_RDONLY | O_NONBLOCK); + int fd = open(fifoFileS2C_.c_str(), O_RDONLY | O_NONBLOCK); std::string reply = ""; if (fd != -1) { - WaitFifoReply(fd, CONTROL_WAITREPY_TIMEOUT, reply); + perfPipe_.WaitFifoReply(fd, CONTROL_WAITREPY_TIMEOUT, reply); } if (fd == -1 || reply != HiperfClient::ReplyOK) { if (reply != HiperfClient::ReplyOK) { - printf("%s\n", reply.c_str()); + printf("%s", reply.c_str()); HLOGE("reply is %s", reply.c_str()); HIPERF_HILOGE(MODULE_DEFAULT, "reply is %{public}s", reply.c_str()); } @@ -817,8 +759,8 @@ bool SubCommandStat::CreateFifoServer() HLOGE("Failed to wait for pid: %d", pid); HIPERF_HILOGE(MODULE_DEFAULT, "Failed to wait for pid: %{public}d", pid); } - remove(CONTROL_FIFO_FILE_C2S.c_str()); - remove(CONTROL_FIFO_FILE_S2C.c_str()); + remove(fifoFileC2S_.c_str()); + remove(fifoFileS2C_.c_str()); strerror_r(errno, errInfo, ERRINFOLEN); printf("create control hiperf counting failed.\n"); HLOGI("errno is %d:%s", errno, errInfo); @@ -832,76 +774,6 @@ bool SubCommandStat::CreateFifoServer() return true; } -bool SubCommandStat::SendFifoAndWaitReply(const std::string &cmd, const std::chrono::milliseconds &timeOut) -{ - // need open for read first, because server maybe send reply before client wait to read - int fdRead = open(CONTROL_FIFO_FILE_S2C.c_str(), O_RDONLY | O_NONBLOCK); - if (fdRead == -1) { - HLOGE("can not open fifo file(%s)", CONTROL_FIFO_FILE_S2C.c_str()); - HIPERF_HILOGI(MODULE_DEFAULT, "can not open fifo file: %{public}s.", CONTROL_FIFO_FILE_S2C.c_str()); - return false; - } - int fdWrite = open(CONTROL_FIFO_FILE_C2S.c_str(), O_WRONLY | O_NONBLOCK); - if (fdWrite == -1) { - HLOGE("can not open fifo file(%s)", CONTROL_FIFO_FILE_C2S.c_str()); - HIPERF_HILOGI(MODULE_DEFAULT, "can not open fifo file: %{public}s.", CONTROL_FIFO_FILE_C2S.c_str()); - close(fdRead); - return false; - } - size_t size = write(fdWrite, cmd.c_str(), cmd.size()); - if (size != cmd.size()) { - HLOGE("failed to write fifo file(%s) command(%s)", CONTROL_FIFO_FILE_C2S.c_str(), - cmd.c_str()); - HIPERF_HILOGI(MODULE_DEFAULT, "failed to write fifo file: %{public}s.", CONTROL_FIFO_FILE_C2S.c_str()); - close(fdWrite); - close(fdRead); - return false; - } - close(fdWrite); - - bool ret = WaitFifoReply(fdRead, timeOut); - close(fdRead); - return ret; -} - -bool SubCommandStat::WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut) -{ - std::string reply; - WaitFifoReply(fd, timeOut, reply); - return reply == HiperfClient::ReplyOK; -} - -void SubCommandStat::WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut, std::string& reply) -{ - struct pollfd pollFd { - fd, POLLIN, 0 - }; - int polled = poll(&pollFd, 1, timeOut.count()); - reply.clear(); - if (polled > 0) { - bool exitLoop = false; - while (!exitLoop) { - char c; - ssize_t result = TEMP_FAILURE_RETRY(read(fd, &c, 1)); - if (result <= 0) { - HLOGD("read from fifo file(%s) failed", CONTROL_FIFO_FILE_S2C.c_str()); - HIPERF_HILOGD(MODULE_DEFAULT, "read from fifo file(%{public}s) failed.", CONTROL_FIFO_FILE_S2C.c_str()); - exitLoop = true; - } - reply.push_back(c); - if (c == '\n') { - exitLoop = true; - } - } - } else if (polled == 0) { - HLOGD("wait fifo file(%s) timeout", CONTROL_FIFO_FILE_S2C.c_str()); - HIPERF_HILOGD(MODULE_DEFAULT, "wait fifo file(%{public}s) timeout.", CONTROL_FIFO_FILE_S2C.c_str()); - } else { - HLOGD("wait fifo file(%s) failed", CONTROL_FIFO_FILE_S2C.c_str()); - HIPERF_HILOGD(MODULE_DEFAULT, "wait fifo file(%{public}s) failed.", CONTROL_FIFO_FILE_S2C.c_str()); - } -} - bool SubCommandStat::ClientCommandResponse(bool response) { return ClientCommandResponse(response ? HiperfClient::ReplyOK : HiperfClient::ReplyFAIL); @@ -954,7 +826,7 @@ inline void SubCommandStat::CreateClientThread() { // make a thread wait the other command if (clientPipeOutput_ != -1) { - clientCommandHanle_ = std::thread(&SubCommandStat::ClientCommandHandle, this); + clientCommandHandle_ = std::thread(&SubCommandStat::ClientCommandHandle, this); } } @@ -973,7 +845,7 @@ void SubCommandStat::ClientCommandHandle() // after read(), block is disabled, the poll will be waked neven if no data close(clientPipeInput_); } - clientPipeInput_ = open(CONTROL_FIFO_FILE_C2S.c_str(), O_RDONLY | O_NONBLOCK); + clientPipeInput_ = open(fifoFileC2S_.c_str(), O_RDONLY | O_NONBLOCK); } struct pollfd pollFd { clientPipeInput_, POLLIN, 0 @@ -1024,31 +896,14 @@ bool SubCommandStat::ProcessControl() return true; } HIPERF_HILOGI(MODULE_DEFAULT, "control cmd : %{public}s", controlCmd_.c_str()); + perfPipe_.SetFifoFileName(CommandType::STAT, controlCmd_, fifoFileC2S_, fifoFileS2C_); if (controlCmd_ == CONTROL_CMD_PREPARE) { CHECK_TRUE(!CreateFifoServer(), false, 0, ""); return true; } isFifoClient_ = true; - bool ret = false; - if (controlCmd_ == CONTROL_CMD_START) { - ret = SendFifoAndWaitReply(HiperfClient::ReplyStart, CONTROL_WAITREPY_TIMEOUT); - } else if (controlCmd_ == CONTROL_CMD_STOP) { - ret = SendFifoAndWaitReply(HiperfClient::ReplyStop, CONTROL_WAITREPY_TIMEOUT); - if (!ret) { - ret = SendFifoAndWaitReply(HiperfClient::ReplyStop, CONTROL_WAITREPY_TIMEOUT); - } - ProcessStopCommand(ret); - } - - if (ret) { - printf("%s counting success.\n", controlCmd_.c_str()); - HIPERF_HILOGI(MODULE_DEFAULT, "%{public}s counting success.", controlCmd_.c_str()); - } else { - printf("%s counting failed.\n", controlCmd_.c_str()); - HIPERF_HILOGI(MODULE_DEFAULT, "%{public}s counting failed.", controlCmd_.c_str()); - } - return ret; + return perfPipe_.ProcessControlCmd(); } HiperfError SubCommandStat::CheckStatOption() @@ -1140,23 +995,23 @@ HiperfError SubCommandStat::OnSubCommand(std::vector& args) void SubCommandStat::CloseClientThread() { - if (clientCommandHanle_.joinable()) { + if (clientCommandHandle_.joinable()) { clientRunning_ = false; HLOGI("CloseClientThread"); if (nullFd_ != -1) { close(nullFd_); } - clientCommandHanle_.join(); + clientCommandHandle_.join(); close(clientPipeInput_); close(clientPipeOutput_); if (isFifoServer_) { - remove(CONTROL_FIFO_FILE_C2S.c_str()); - remove(CONTROL_FIFO_FILE_S2C.c_str()); + remove(fifoFileC2S_.c_str()); + remove(fifoFileS2C_.c_str()); } } } -bool SubCommandStat::ParseControlCmd(const std::string cmd) +bool SubCommandStat::ParseControlCmd(const std::string& cmd) { if (cmd.empty() || cmd == CONTROL_CMD_PREPARE || cmd == CONTROL_CMD_START || cmd == CONTROL_CMD_STOP) { return true; diff --git a/src/utilities.cpp b/src/utilities.cpp index a08fd3d..6dc1e7e 100644 --- a/src/utilities.cpp +++ b/src/utilities.cpp @@ -699,7 +699,7 @@ pid_t GetAppPackagePid(const std::string &appPackage, const pid_t oldPid, const return res; } -bool CheckAppIsRunning(std::vector &selectPids, const std::string &appPackage, int checkAppMs) +bool CheckAppIsRunning (std::vector &selectPids, const std::string &appPackage, int checkAppMs) { if (!appPackage.empty()) { pid_t appPid = GetAppPackagePid(appPackage, -1, checkAppMs, waitAppRunCheckTimeOut); @@ -722,8 +722,8 @@ bool IsExistDebugByApp(const std::string& bundleName, std::string& err) } if (!IsSupportNonDebuggableApp() && !bundleNameTmp.empty() && !IsDebugableApp(bundleNameTmp)) { HLOGE("--app option only support debug application."); - err = "--app option only support debug application"; - printf("%s\n", err.c_str()); + err = "--app option only support debug application\n"; + printf("%s", err.c_str()); return false; } return true; @@ -734,8 +734,8 @@ bool IsExistDebugByPid(const std::vector &pids, std::string& err) CHECK_TRUE(pids.empty(), true, 1, "IsExistDebugByPid: pids is empty."); for (auto pid : pids) { if (pid <= 0) { - err = "Invalid -p value '" + std::to_string(pid) + "', the pid should be larger than 0"; - printf("%s\n", err.c_str()); + err = "Invalid -p value '" + std::to_string(pid) + "', the pid should be larger than 0\n"; + printf("%s", err.c_str()); return false; } std::string bundleName = GetProcessName(pid); @@ -745,14 +745,29 @@ bool IsExistDebugByPid(const std::vector &pids, std::string& err) } if (!IsSupportNonDebuggableApp() && !IsDebugableApp(bundleName)) { HLOGE("-p option only support debug application for %s", bundleName.c_str()); - err = "-p option only support debug application"; - printf("%s\n", err.c_str()); + err = "-p option only support debug application\n"; + printf("%s", err.c_str()); return false; } } return true; } +std::string HandleAppInfo(const std::string& appPackage, const std::vector &inputPidTidArgs) +{ + std::string err = ""; + if (!appPackage.empty()) { + if (!IsExistDebugByApp(appPackage, err)) { + return err; + } + } else { + if (!IsExistDebugByPid(inputPidTidArgs, err)) { + return err; + } + } + return err; +} + bool IsSupportNonDebuggableApp() { // root first diff --git a/test/BUILD.gn b/test/BUILD.gn index 22bc8a4..2aa5b44 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -45,6 +45,7 @@ sources_base = [ "unittest/common/native/unique_stack_table_test.cpp", "unittest/common/native/spe_decoder_test.cpp", "unittest/common/native/test_utilities.cpp", + "unittest/common/native/perf_pipe_test.cpp", ] if (hiperf_debug) { @@ -166,6 +167,7 @@ ohos_unittest("hiperf_unittest") { "./../src/utilities.cpp", "./../src/virtual_runtime.cpp", "./../src/virtual_thread.cpp", + "./../src/perf_pipe.cpp", ] if (is_ohos) { diff --git a/test/unittest/common/native/include/perf_pipe_test.h b/test/unittest/common/native/include/perf_pipe_test.h new file mode 100644 index 0000000..c8deb5f --- /dev/null +++ b/test/unittest/common/native/include/perf_pipe_test.h @@ -0,0 +1,23 @@ +/* + * 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 HIPERF_PERF_PIPE_TEST_H +#define HIPERF_PERF_PIPE_TEST_H + +#include +#include + +#include "perf_pipe.h" + +#endif // HIPERF_PERF_PIPE_TEST_H \ No newline at end of file diff --git a/test/unittest/common/native/include/test_utilities.h b/test/unittest/common/native/include/test_utilities.h index 6313ead..09945d9 100644 --- a/test/unittest/common/native/include/test_utilities.h +++ b/test/unittest/common/native/include/test_utilities.h @@ -15,6 +15,7 @@ #ifndef HIPERF_TEST_UTILITIES_H_ #define HIPERF_TEST_UTILITIES_H_ +#include #include "dfx_map.h" namespace OHOS { @@ -23,6 +24,8 @@ namespace HiPerf { bool CheckTestApp(const std::string& appName); bool GetMemMapOffset(pid_t devhostPid, uint64_t &mapOffset, std::vector> &memMaps, std::string &line); +bool RunCmd(const std::string& cmdstr); +bool CheckTraceCommandOutput(const std::string& cmd, const std::vector& keywords); } // namespace HiPerf } // namespace Developtools } // namespace OHOS diff --git a/test/unittest/common/native/perf_pipe_test.cpp b/test/unittest/common/native/perf_pipe_test.cpp new file mode 100644 index 0000000..2871275 --- /dev/null +++ b/test/unittest/common/native/perf_pipe_test.cpp @@ -0,0 +1,93 @@ +/* + * 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 "hiperf_client.h" +#include "subcommand_record_test.h" +#include "perf_pipe_test.h" + +#include +#include + +using namespace testing::ext; +using namespace std::chrono; +namespace OHOS { +namespace Developtools { +namespace HiPerf { +const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT = 2000ms; +class PerfPipeTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void PerfPipeTest::SetUpTestCase() {} + +void PerfPipeTest::TearDownTestCase() {} + +void PerfPipeTest::SetUp() {} + +void PerfPipeTest::TearDown() {} + +/** + * @tc.name: CreateFifoFile + * @tc.desc: + * @tc.type: FUNC + */ +HWTEST_F(PerfPipeTest, CreateRecordFifoFile, TestSize.Level0) +{ + PerfPipe perfPipe; + std::string controlCmd = "prepare"; + std::string fifoFileC2S; + std::string fifoFileS2C; + perfPipe.SetFifoFileName(CommandType::RECORD, controlCmd, fifoFileC2S, fifoFileS2C); + EXPECT_EQ(perfPipe.CreateFifoFile(), true); + EXPECT_EQ(perfPipe.CreateFifoFile(), false); + EXPECT_EQ(remove(fifoFileC2S.c_str()), 0); + EXPECT_EQ(remove(fifoFileS2C.c_str()), 0); +} + +/** + * @tc.name: SendFifoAndWaitReply + * @tc.desc: Test send Fifo and wait reply + * @tc.type: FUNC + */ +HWTEST_F(PerfPipeTest, SendFifoAndWaitReply, TestSize.Level1) +{ + PerfPipe perfPipe; + std::string controlCmd = "prepare"; + std::string fifoFileC2S; + std::string fifoFileS2C; + perfPipe.SetFifoFileName(CommandType::RECORD, controlCmd, fifoFileC2S, fifoFileS2C); + EXPECT_EQ(perfPipe.SendFifoAndWaitReply(HiperfClient::ReplyStart, CONTROL_WAITREPY_TIMEOUT), false); +} + +/** + * @tc.name: ProcessControlCmd + * @tc.desc: Test send Fifo and wait reply + * @tc.type: FUNC + */ +HWTEST_F(PerfPipeTest, ProcessControlCmd, TestSize.Level1) +{ + PerfPipe perfPipe; + std::string controlCmd = "prepare"; + std::string fifoFileC2S; + std::string fifoFileS2C; + perfPipe.SetFifoFileName(CommandType::RECORD, controlCmd, fifoFileC2S, fifoFileS2C); + EXPECT_EQ(perfPipe.ProcessControlCmd(), false); +} +} // namespace HiPerf +} // namespace Developtools +} // namespace OHOS diff --git a/test/unittest/common/native/subcommand_record_test.cpp b/test/unittest/common/native/subcommand_record_test.cpp index c1d2484..25a5f80 100644 --- a/test/unittest/common/native/subcommand_record_test.cpp +++ b/test/unittest/common/native/subcommand_record_test.cpp @@ -29,6 +29,7 @@ #include "command.h" #include "debug_logger.h" #include "hisysevent_manager.h" +#include "perf_pipe.h" #include "subcommand_dump.h" #include "subcommand_report.h" #include "subcommand_test.h" @@ -243,11 +244,6 @@ HWTEST_F(SubCommandRecordTest, PackageName, TestSize.Level0) ForkAndRunTest("-d 2 ", true, true); } -HWTEST_F(SubCommandRecordTest, PackageNameErr, TestSize.Level3) -{ - TestRecordCommand("-d 2 --app package_name ", false, false); -} - // check app milliseconds /** * @tc.name: CheckAppMsMin @@ -1449,12 +1445,6 @@ HWTEST_F(SubCommandRecordTest, ReStart, TestSize.Level0) TestRecordCommand("--restart ", false, true); } -// --exclude-tid -HWTEST_F(SubCommandRecordTest, ExcludeTidConflict, TestSize.Level2) -{ - TestRecordCommand("--exclude-tid 5 --exclude-thread test ", false, true); -} - /** * @tc.name: CmdLinesSizeSucess * @tc.desc: Test --cmdline-size option @@ -1595,7 +1585,7 @@ HWTEST_F(SubCommandRecordTest, SendFifoAndWaitReply, TestSize.Level1) { SubCommandRecord cmd; std::string test = "test"; - EXPECT_EQ(cmd.SendFifoAndWaitReply(test, CONTROL_WAITREPY_TOMEOUT), false); + EXPECT_EQ(cmd.perfPipe_.SendFifoAndWaitReply(test, CONTROL_WAITREPY_TOMEOUT), false); } /** @@ -2229,6 +2219,51 @@ HWTEST_F(SubCommandRecordTest, CheckProductCfg, TestSize.Level1) cJSON_Delete(root); } } + +/** + * @tc.name: TestOnSubCommand_control01 + * @tc.desc: prepare, start, stop + * @tc.type: FUNC + */ +HWTEST_F(SubCommandRecordTest, TestOnSubCommand_control01, TestSize.Level1) +{ + ASSERT_TRUE(RunCmd("hiperf record --control stop")); + EXPECT_EQ(CheckTraceCommandOutput("hiperf record --control prepare -a", + {"create control hiperf sampling success"}), + true); + EXPECT_EQ(CheckTraceCommandOutput("hiperf record --control start", {"start sampling success"}), + true); + EXPECT_EQ(CheckTraceCommandOutput("hiperf record --control stop", {"stop sampling success"}), + true); +} + +/** + * @tc.name: TestOnSubCommand_control02 + * @tc.desc: prepare, prepare + * @tc.type: FUNC + */ +HWTEST_F(SubCommandRecordTest, TestOnSubCommand_control02, TestSize.Level1) +{ + ASSERT_TRUE(RunCmd("hiperf record --control stop")); + ASSERT_TRUE(RunCmd("hiperf record --control prepare -a")); + EXPECT_EQ(CheckTraceCommandOutput("hiperf record --control prepare -a", + {"another sampling service is running"}), + true); +} + +/** + * @tc.name: TestOnSubCommand_control03 + * @tc.desc: start, stop + * @tc.type: FUNC + */ +HWTEST_F(SubCommandRecordTest, TestOnSubCommand_control03, TestSize.Level1) +{ + ASSERT_TRUE(RunCmd("hiperf record --control stop")); + EXPECT_EQ(CheckTraceCommandOutput("hiperf record --control start", {"start sampling failed"}), + true); + EXPECT_EQ(CheckTraceCommandOutput("hiperf record --control stop", {"stop sampling failed"}), + true); +} } // namespace HiPerf } // namespace Developtools } // namespace OHOS diff --git a/test/unittest/common/native/subcommand_stat_test.cpp b/test/unittest/common/native/subcommand_stat_test.cpp index 35ee791..ea0e6c6 100644 --- a/test/unittest/common/native/subcommand_stat_test.cpp +++ b/test/unittest/common/native/subcommand_stat_test.cpp @@ -32,13 +32,13 @@ #include #include "perf_events.h" +#include "test_utilities.h" #include "tracked_command.h" using namespace testing::ext; namespace OHOS { namespace Developtools { namespace HiPerf { -const int CMD_OUTPUT_BUF = 1024; static std::atomic g_wait = false; class SubCommandStatTest : public testing::Test { public: @@ -57,9 +57,6 @@ public: int CounterValue(const std::string &stringOut, const std::string &configName) const; void CheckGroupCoverage(const std::string &stringOut, const std::string &groupCounterName) const; - bool RunCmd(const string& cmdstr) const; - bool CheckTraceCommandOutput(const std::string& cmd, - const std::vector& keywords) const; const std::vector defaultConfigNames_ = { "hw-branch-misses", @@ -234,53 +231,6 @@ void SubCommandStatTest::CheckGroupCoverage(const std::string &stringOut, } } -bool SubCommandStatTest::RunCmd(const string& cmdstr) const -{ - if (cmdstr.empty()) { - return false; - } - FILE *fp = popen(cmdstr.c_str(), "r"); - if (fp == nullptr) { - return false; - } - char res[CMD_OUTPUT_BUF] = { '\0' }; - while (fgets(res, sizeof(res), fp) != nullptr) { - std::cout << res; - } - pclose(fp); - return true; -} - -bool SubCommandStatTest::CheckTraceCommandOutput(const std::string& cmd, - const std::vector& keywords) const -{ - if (cmd.empty()) { - return false; - } - FILE* fp = popen(cmd.c_str(), "r"); - if (fp == nullptr) { - return false; - } - - char buffer[CMD_OUTPUT_BUF]; - int checkIdx = 0; - while (fgets(buffer, sizeof(buffer), fp) != nullptr) { - while (checkIdx < keywords.size() && strstr(buffer, keywords[checkIdx].c_str()) != nullptr) { - GTEST_LOG_(INFO) << "match keyword :" << keywords[checkIdx]; - checkIdx++; - if (checkIdx == keywords.size()) { - break; - } - } - } - - pclose(fp); - if (checkIdx < keywords.size()) { - GTEST_LOG_(ERROR) << "Failed to match keyword : " << keywords[checkIdx]; - } - return checkIdx == keywords.size(); -} - /** * @tc.name: TestOnSubCommand_a * @tc.desc: -a diff --git a/test/unittest/common/native/test_utilities.cpp b/test/unittest/common/native/test_utilities.cpp index 620f424..93a1ae8 100644 --- a/test/unittest/common/native/test_utilities.cpp +++ b/test/unittest/common/native/test_utilities.cpp @@ -22,6 +22,8 @@ namespace OHOS { namespace Developtools { namespace HiPerf { +const int CMD_OUTPUT_BUF = 1024; + bool CheckTestApp(const std::string& appName) { FILE *fp = nullptr; @@ -62,6 +64,52 @@ bool GetMemMapOffset(pid_t devhostPid, uint64_t &mapOffset, } return false; } + +bool RunCmd(const std::string& cmdstr) +{ + if (cmdstr.empty()) { + return false; + } + FILE *fp = popen(cmdstr.c_str(), "r"); + if (fp == nullptr) { + return false; + } + char res[CMD_OUTPUT_BUF] = { '\0' }; + while (fgets(res, sizeof(res), fp) != nullptr) { + std::cout << res; + } + pclose(fp); + return true; +} + +bool CheckTraceCommandOutput(const std::string& cmd, const std::vector& keywords) +{ + if (cmd.empty()) { + return false; + } + FILE* fp = popen(cmd.c_str(), "r"); + if (fp == nullptr) { + return false; + } + + char buffer[CMD_OUTPUT_BUF]; + int checkIdx = 0; + while (fgets(buffer, sizeof(buffer), fp) != nullptr) { + while (checkIdx < keywords.size() && strstr(buffer, keywords[checkIdx].c_str()) != nullptr) { + GTEST_LOG_(INFO) << "match keyword :" << keywords[checkIdx]; + checkIdx++; + if (checkIdx == keywords.size()) { + break; + } + } + } + + pclose(fp); + if (checkIdx < keywords.size()) { + GTEST_LOG_(ERROR) << "Failed to match keyword : " << keywords[checkIdx]; + } + return checkIdx == keywords.size(); +} } // namespace HiPerf } // namespace Developtools } // namespace OHOS \ No newline at end of file -- Gitee