From fc7ff1346d6d76d7294cbf13cfce12817cd255ca Mon Sep 17 00:00:00 2001 From: jiangwei Date: Wed, 16 Apr 2025 10:00:33 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20control=E6=A8=A1=E5=BC=8F=E6=8A=BD?= =?UTF-8?q?=E5=87=BA=E4=B8=BA=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: jiangwei --- BUILD.gn | 1 + include/perf_pipe.h | 107 ++++++++ include/subcommand_stat.h | 10 +- include/utilities.h | 1 + src/perf_events.cpp | 6 +- src/perf_pipe.cpp | 522 ++++++++++++++++++++++++++++++++++++++ src/subcommand_stat.cpp | 95 +++---- 7 files changed, 695 insertions(+), 47 deletions(-) create mode 100644 include/perf_pipe.h create mode 100644 src/perf_pipe.cpp diff --git a/BUILD.gn b/BUILD.gn index fc23c69..3fc3e4b 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/perf_pipe.h b/include/perf_pipe.h new file mode 100644 index 0000000..f0c741d --- /dev/null +++ b/include/perf_pipe.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HIPERF_PERF_PIPE +#define HIPERF_PERF_PIPE + +#include +#include +#include "utilities.h" +#include "perf_events.h" + +namespace OHOS { +namespace Developtools { +namespace HiPerf { +enum 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 { +public: + std::string controlCmd_ = {}; + std::string appPackage_ = {}; + std::vector inputPidTidArgs_ = {}; + std::vector selectCpus_ = {}; + std::string CONTROL_FIFO_FILE_C2S; + std::string CONTROL_FIFO_FILE_S2C; + std::string outputFilename_; + bool allowIpc_ = true; + bool isFifoServer_ = false; + bool isFifoClient_ = false; + int clientPipeInput_ = -1; + int clientPipeOutput_ = -1; + int nullFd_ = -1; + bool restart_ = false; + bool outputEnd_ = false; + std::thread clientCommandHanle_; + bool clientRunning_ = true; + CommandType commandType_; + struct ControlCommandHandler { + std::function preProcess = []() -> bool { + return false; + }; + std::function postProcess = [](bool) {}; + }; + std::unordered_map controlCommandHandlerMap_ = {}; + PerfEvents perfEvents_; + std::vector> selectEvents_; + std::vector> selectGroups_; + +public: + // PerfPipe(); + ~PerfPipe(); + void SetControlCmd(const std::string controlCmd); + void SetFifoFileName(const CommandType& commandType); + void SetAppAndPidTid(std::string appPackage, std::vector inputPidTidArgs); + void SetRestart(bool restart); + void SetCpu(std::vector cpus); + void SetPid(std::vector pids); + void SetPerfEvent(bool systemTarget, float timeOut, int timeReport, bool perCpu, bool perThread, bool verboseReport, + bool inherit, const std::vector &trackedCommand, PerfEvents::StatCallBack reportCallBack); + void SetSelectEvents(std::vector> selectEvents); + void SetSelectGroups(std::vector> selectGroups); + void SetOutPutFileName(std::string outputFilename); + bool PrepairEvents(); + bool CreateFifoServer(); + +public: + void CreateClientThread(); + void ClientCommandHandle(); + bool ProcessControl(); + std::string HandleAppInfo(); + void InitControlCommandHandlerMap(); + void DispatchControlCommand(const std::string& command); + bool ClientCommandResponse(bool response); + bool ClientCommandResponse(const std::string& str); + bool IsSamplingRunning(); + void ProcessStopCommand(bool ret); + void ProcessOutputCommand(bool ret); + // bool PerfPipe::PreOutputRecordFile() + 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(); + // bool PrepairEvents(); +}; +} // namespace HiPerf +} // namespace Developtools +} // namespace OHOS +#endif // HIPERF_PERF_PIPE \ No newline at end of file diff --git a/include/subcommand_stat.h b/include/subcommand_stat.h index c961291..b60cc72 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 { @@ -100,6 +101,7 @@ public: private: PerfEvents perfEvents_; + PerfPipe perfPipe_; bool targetSystemWide_ {false}; std::vector selectCpus_ = {}; float timeStopSec_ = PerfEvents::DEFAULT_TIMEOUT; @@ -109,6 +111,8 @@ private: bool restart_ {false}; bool noCreateNew_ {false}; std::string appPackage_ = {}; + std::string outputFilename_ = "/data/local/tmp/perf_stat.txt"; + std::string controlCmd_ = {}; int checkAppMs_ = DEFAULT_CHECK_APP_MS; std::vector selectPids_; std::vector selectTids_; @@ -118,6 +122,7 @@ private: bool verboseReport_ {false}; std::vector trackedCommand_ {}; bool helpOption_ {false}; + bool ParseControlCmd(const std::string& cmd); bool CheckOptionPidAndApp(std::vector pids); bool CheckOptionPid(std::vector pids); static bool FindEventCount( @@ -155,11 +160,12 @@ private: { return helpOption_; } - bool PrepairEvents(); + // bool PrepairEvents(); bool CheckOptions(const std::vector &pids); bool CheckSelectCpuPidOption(); void SetReportFlags(bool cpuFlag, bool threadFlag); - void SetPerfEvent(); + // void SetPerfEvent(); + void SetPerfPipe(); }; bool RegisterSubCommandStat(void); diff --git a/include/utilities.h b/include/utilities.h index b892334..3a96b2d 100644 --- a/include/utilities.h +++ b/include/utilities.h @@ -79,6 +79,7 @@ constexpr const uint16_t CHECK_FREQUENCY = 100; // constexpr const uint8_t CHECK_TIMEOUT = 30; constexpr const int INDENT_TWO = 2; constexpr const float ALMOST_ZERO = 0.001; + #if !is_mingw #ifndef O_BINARY #define O_BINARY 0 diff --git a/src/perf_events.cpp b/src/perf_events.cpp index 6c98ef9..f53b0e4 100644 --- a/src/perf_events.cpp +++ b/src/perf_events.cpp @@ -44,6 +44,7 @@ namespace HiPerf { bool PerfEvents::updateTimeThreadRunning_ = true; std::atomic PerfEvents::currentTimeSecond_ = 0; static std::atomic_bool g_trackRunning = false; +static std::atomic_bool g_captureSig = false; static constexpr int32_t UPDATE_TIME_INTERVAL = 10; // 10ms static constexpr uint64_t NANO_SECONDS_PER_SECOND = 1000000000; static constexpr uint32_t POLL_FAIL_COUNT_THRESHOLD = 10; @@ -567,6 +568,7 @@ static bool CaptureSig() sig.sa_handler = [](int sig) { printf("\n Ctrl + C detected.\n"); g_trackRunning = false; + g_captureSig = true; }; sig.sa_flags = 0; @@ -1811,7 +1813,7 @@ void PerfEvents::RecordLoop() } } - if (!g_trackRunning) { + if (!g_trackRunning && g_captureSig) { // for user interrupt situation, print time statistic usedTimeMsTick = duration_cast(steady_clock::now() - startTime); printf("User interrupt exit (total %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count()); @@ -1872,7 +1874,7 @@ void PerfEvents::StatLoop() std::this_thread::sleep_for(microseconds(defaultSleepUs)); } - if (!g_trackRunning) { + if (!g_trackRunning && g_captureSig) { // for user interrupt situation, print time statistic usedTimeMsTick = duration_cast(steady_clock::now() - startTime); printf("User interrupt exit (total %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count()); diff --git a/src/perf_pipe.cpp b/src/perf_pipe.cpp new file mode 100644 index 0000000..5d3602d --- /dev/null +++ b/src/perf_pipe.cpp @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#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/local/tmp/.hiperf_record_control_c2s"; +const std::string RECORD_CONTROL_FIFO_FILE_S2C = "/data/local/tmp/.hiperf_record_control_s2c"; +const std::string STAT_CONTROL_FIFO_FILE_C2S = "/data/local/tmp/.hiperf_stat_control_c2s"; +const std::string STAT_CONTROL_FIFO_FILE_S2C = "/data/local/tmp/.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_SERVER_OUTPUT_WAIT_COUNT = 600; +static constexpr uint32_t MAX_CLIENT_OUTPUT_WAIT_COUNT = 240; + +PerfPipe::~PerfPipe() +{ + CloseClientThread(); +} + +void PerfPipe::SetControlCmd(const std::string controlCmd) +{ + controlCmd_ = controlCmd; + allowIpc_ = controlCmd_ != CONTROL_CMD_PREPARE; +} + +void PerfPipe::SetFifoFileName(const CommandType& commandType) +{ + if (commandType == CommandType::RECORD) { + CONTROL_FIFO_FILE_C2S = RECORD_CONTROL_FIFO_FILE_C2S; + CONTROL_FIFO_FILE_S2C = RECORD_CONTROL_FIFO_FILE_S2C; + } else if (commandType == CommandType::STAT) { + CONTROL_FIFO_FILE_C2S = STAT_CONTROL_FIFO_FILE_C2S; + CONTROL_FIFO_FILE_S2C = STAT_CONTROL_FIFO_FILE_S2C; + } + HLOGD("C2S:%s, S2C:%s", CONTROL_FIFO_FILE_C2S.c_str(), CONTROL_FIFO_FILE_S2C.c_str()); + commandType_ = commandType; +} + +void PerfPipe::SetRestart(bool restart) +{ + restart_ = restart; +} + +void PerfPipe::SetOutPutFileName(std::string outputFilename) +{ + outputFilename_ = outputFilename; +} + +void PerfPipe::SetAppAndPidTid(std::string appPackage, std::vector inputPidTidArgs) +{ + appPackage_ = appPackage; + inputPidTidArgs_ = inputPidTidArgs; +} + +void PerfPipe::SetPerfEvent(bool systemTarget, float timeOut, int timeReport, bool perCpu, bool perThread, + bool verboseReport, bool inherit, const std::vector &trackedCommand, PerfEvents::StatCallBack reportCallBack) +{ + perfEvents_.SetSystemTarget(systemTarget); + perfEvents_.SetTimeOut(timeOut); + perfEvents_.SetTimeReport(timeReport); + perfEvents_.SetPerCpu(perCpu); + perfEvents_.SetPerThread(perThread); + perfEvents_.SetVerboseReport(verboseReport); + perfEvents_.SetInherit(!inherit); + perfEvents_.SetTrackedCommand(trackedCommand); + // set report handle + perfEvents_.SetStatCallBack(reportCallBack); +} + +bool PerfPipe::PrepairEvents() +{ + if (selectEvents_.empty() && selectGroups_.empty()) { + perfEvents_.AddDefaultEvent(PERF_TYPE_HARDWARE); + perfEvents_.AddDefaultEvent(PERF_TYPE_SOFTWARE); + } else { + for (auto events : selectEvents_) { + if (!perfEvents_.AddEvents(events)) { + HLOGV("add events failed"); + return false; + } + } + for (auto events : selectGroups_) { + if (!perfEvents_.AddEvents(events, true)) { + HLOGV("add groups failed"); + return false; + } + } + } + return true; +} + +void PerfPipe::SetCpu(std::vector cpus) +{ + perfEvents_.SetCpu(cpus); +} + +void PerfPipe::SetPid(std::vector pids) +{ + perfEvents_.SetPid(pids); +} + +void PerfPipe::SetSelectEvents(std::vector> selectEvents) +{ + selectEvents_ = selectEvents; + +} + +void PerfPipe::SetSelectGroups(std::vector> selectGroups) +{ + selectGroups_ = selectGroups; +} + +bool PerfPipe::ClientCommandResponse(bool response) +{ + return ClientCommandResponse(response ? HiperfClient::ReplyOK : HiperfClient::ReplyFAIL); +} + +bool PerfPipe::ClientCommandResponse(const std::string& str) +{ + 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); + return false; + } + return true; +} + +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."); + } + + 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()); + } +} + +void PerfPipe::ProcessOutputCommand(bool ret) +{ + if (!ret) { + HLOGI("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)); + } +} + +void PerfPipe::InitControlCommandHandlerMap() +{ + controlCommandHandlerMap_.clear(); + controlCommandHandlerMap_.emplace(HiperfClient::ReplyStart, ControlCommandHandler{ + std::bind(&PerfEvents::EnableTracking, &perfEvents_) + }); + + // controlCommandHandlerMap_.emplace(HiperfClient::ReplyCheck, ControlCommandHandler{ + // std::bind(&SubCommandRecord::clientRunning_, this) + // }); + + controlCommandHandlerMap_.emplace(HiperfClient::ReplyStop, ControlCommandHandler{ + std::bind(&PerfEvents::StopTracking, &perfEvents_) + }); + + controlCommandHandlerMap_.emplace(HiperfClient::ReplyPause, ControlCommandHandler{ + std::bind(&PerfEvents::PauseTracking, &perfEvents_) + }); + + controlCommandHandlerMap_.emplace(HiperfClient::ReplyResume, ControlCommandHandler{ + std::bind(&PerfEvents::ResumeTracking, &perfEvents_) + }); + + // controlCommandHandlerMap_.emplace(HiperfClient::ReplyOutput, ControlCommandHandler{ + // std::bind(&SubCommandRecord::PreOutputRecordFile, this), + // std::bind(&SubCommandRecord::PostOutputRecordFile, this, std::placeholders::_1) + // }); + + // controlCommandHandlerMap_.emplace(HiperfClient::ReplyOutputCheck, ControlCommandHandler{ + // std::bind(&SubCommandRecord::outputEnd_, this) + // }); +} + +void PerfPipe::CreateClientThread() +{ + // make a thread wait the other command + if (clientPipeOutput_ != -1) { + clientCommandHanle_ = std::thread(&PerfPipe::ClientCommandHandle, this); + } +} + +void PerfPipe::ClientCommandHandle() +{ + using namespace HiperfClient; + CHECK_TRUE(!IsSamplingRunning(), NO_RETVAL, 0, ""); + // tell the caller if Exist + ClientCommandResponse(true); + InitControlCommandHandlerMap(); + + bool hasRead = true; + while (clientRunning_) { + if (isFifoServer_ && hasRead) { + if (clientPipeInput_ != -1) { + // 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); + } + struct pollfd pollFd { + clientPipeInput_, POLLIN, 0 + }; + int polled = poll(&pollFd, 1, CONTROL_WAITREPY_TIMEOUT.count()); + if (polled <= 0) { + hasRead = false; + continue; + } + hasRead = true; + std::string command; + while (true) { + char c; + ssize_t result = TEMP_FAILURE_RETRY(read(clientPipeInput_, &c, 1)); + if (result <= 0) { + HLOGD("server :read from pipe file failed"); + break; + } + command.push_back(c); + if (c == '\n') { + break; + } + } + HLOGD("server:new command %s", command.c_str()); + HIPERF_HILOGI(MODULE_DEFAULT, "server:new command : %{public}s", command.c_str()); + DispatchControlCommand(command); + } +} + +bool PerfPipe::IsSamplingRunning() +{ + constexpr int maxWaitTrackingCount = 3000 / 100; // wait 3 second + int waitTrackingCount = maxWaitTrackingCount; + while (!perfEvents_.IsTrackRunning()) { + waitTrackingCount--; + if (waitTrackingCount <= 0) { + return false; + } + constexpr uint64_t waitTrackingSleepMs = 100; + std::this_thread::sleep_for(milliseconds(waitTrackingSleepMs)); + } + return true; +} + +void PerfPipe::DispatchControlCommand(const std::string& command) +{ + auto it = controlCommandHandlerMap_.find(command); + if (it == controlCommandHandlerMap_.end()) { + return; + } + + ControlCommandHandler& handler = it->second; + bool ret = handler.preProcess(); + ClientCommandResponse(ret); + handler.postProcess(ret); +} + +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(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 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) { + 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()); + break; + } + reply.push_back(c); + if (c == '\n') { + break; + } + } + } 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()); + } +} + +bool PerfPipe::ProcessControl() +{ + if (controlCmd_.empty()) { + return true; + } + HIPERF_HILOGI(MODULE_DEFAULT, "control cmd : %{public}s", controlCmd_.c_str()); + 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; +} + +bool PerfPipe::CreateFifoServer() +{ + char errInfo[ERRINFOLEN] = { 0 }; + const mode_t fifoMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + 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()); + } + strerror_r(errno, errInfo, ERRINFOLEN); + HLOGE("create fifo file failed. %d:%s", errno, errInfo); + return false; + } + + CheckIpcBeforeFork(); + pid_t pid = fork(); + allowIpc_ = true; + + if (pid == -1) { + strerror_r(errno, errInfo, ERRINFOLEN); + HLOGE("fork failed. %d:%s", errno, errInfo); + return false; + } else if (pid == 0) { // child process + close(STDIN_FILENO); + close(STDERR_FILENO); + 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; + } + FILE* fileFd = fopen(outputFilename_.c_str(), "w"); + if (fileFd == nullptr) { + perror("file open failed.\n"); + } else { + (void)dup2(fileno(fileFd), STDOUT_FILENO); + fclose(fileFd); + } + // 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 + 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()); + } + 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"); + if (commandType_ == STAT) { + printf("stat file will saved in %s.", outputFilename_.c_str()); + } + } + return true; +} + +std::string PerfPipe::HandleAppInfo() +{ + std::string err = ""; + if (!appPackage_.empty()) { + if (!IsExistDebugByApp(appPackage_, err)) { + return err; + } + } else { + if (!IsExistDebugByPid(inputPidTidArgs_, err)) { + return err; + } + } + return err; +} + +void PerfPipe::CloseClientThread() +{ + if (clientCommandHanle_.joinable()) { + clientRunning_ = false; + HLOGI("CloseClientThread"); + if (nullFd_ != -1) { + close(nullFd_); + } + clientCommandHanle_.join(); + close(clientPipeInput_); + close(clientPipeOutput_); + if (isFifoServer_) { + remove(CONTROL_FIFO_FILE_C2S.c_str()); + remove(CONTROL_FIFO_FILE_S2C.c_str()); + } + } +} +} // namespace HiPerf +} // namespace Developtools +} // namespace OHOS \ No newline at end of file diff --git a/src/subcommand_stat.cpp b/src/subcommand_stat.cpp index 00fd3df..8de9474 100644 --- a/src/subcommand_stat.cpp +++ b/src/subcommand_stat.cpp @@ -24,6 +24,7 @@ #include "debug_logger.h" #include "hiperf_hilog.h" #include "utilities.h" +#include "perf_pipe.h" const uint16_t ONE_HUNDRED = 100; const uint16_t THOUSANDS_SEPARATOR = 3; @@ -132,6 +133,12 @@ bool SubCommandStat::ParseOption(std::vector &args) HLOGD("get option --verbose failed"); return false; } + if (!Option::GetOptionValue(args, "--control", controlCmd_)) { + return false; + } + if (!Option::GetOptionValue(args, "-o", outputFilename_)) { + return false; + } return ParseSpecialOption(args); } @@ -617,19 +624,12 @@ bool SubCommandStat::CheckOptionPid(std::vector pids) return true; } -void SubCommandStat::SetPerfEvent() +void SubCommandStat::SetPerfPipe() { - SetReportFlags(perCpus_, perThreads_); - perfEvents_.SetSystemTarget(targetSystemWide_); - perfEvents_.SetTimeOut(timeStopSec_); - perfEvents_.SetTimeReport(timeReportMs_); - perfEvents_.SetPerCpu(perCpus_); - perfEvents_.SetPerThread(perThreads_); - perfEvents_.SetVerboseReport(verboseReport_); - perfEvents_.SetInherit(!noCreateNew_); - perfEvents_.SetTrackedCommand(trackedCommand_); - // set report handle - perfEvents_.SetStatCallBack(Report); + perfPipe_.SetControlCmd(controlCmd_); + perfPipe_.SetFifoFileName(CommandType::STAT); + perfPipe_.SetAppAndPidTid(appPackage_, inputPidTidArgs_); + perfPipe_.SetOutPutFileName(outputFilename_); } HiperfError SubCommandStat::OnSubCommand(std::vector& args) @@ -655,7 +655,7 @@ HiperfError SubCommandStat::OnSubCommand(std::vector& args) return HiperfError::CHECK_OPTION_PID_FAIL; } - perfEvents_.SetCpu(selectCpus_); + perfPipe_.SetCpu(selectCpus_); std::vector pids; for (auto selectPid : selectPids_) { HLOGD("[OnSubCommand] selectPid %d\n", selectPid); @@ -667,7 +667,7 @@ HiperfError SubCommandStat::OnSubCommand(std::vector& args) } } pids.insert(pids.end(), selectTids_.begin(), selectTids_.end()); - perfEvents_.SetPid(pids); + perfPipe_.SetPid(pids); if (!CheckOptionPidAndApp(pids)) { HLOGV("CheckOptionPidAndApp() failed"); return HiperfError::CHECK_OPTION_PID_APP_FAIL; @@ -676,18 +676,35 @@ HiperfError SubCommandStat::OnSubCommand(std::vector& args) if (!IsExistDebugByPid(inputPidTidArgs_, err)) { return HiperfError::CHECK_OPTION_PID_APP_FAIL; } - SetPerfEvent(); - if (!PrepairEvents()) { + perfPipe_.SetRestart(restart_); + SetPerfPipe(); + if (!perfPipe_.ProcessControl()) { + return HiperfError::PROCESS_CONTROL_FAIL; + } else if (perfPipe_.isFifoClient_) { + return HiperfError::NO_ERR; + } + // SetPerfEvent(); + SetReportFlags(perCpus_, perThreads_); + perfPipe_.SetPerfEvent(targetSystemWide_, timeStopSec_, timeReportMs_, + perCpus_, perThreads_, verboseReport_, !noCreateNew_, trackedCommand_, Report); + perfPipe_.SetSelectEvents(selectEvents_); + perfPipe_.SetSelectGroups(selectGroups_); + if (!perfPipe_.PrepairEvents()) { HLOGV("PrepairEvents() failed"); return HiperfError::PREPAIR_EVENTS_FAIL; } // preapare fd - perfEvents_.PrepareTracking(); - + perfPipe_.perfEvents_.PrepareTracking(); + perfPipe_.CreateClientThread(); // start tracking - perfEvents_.StartTracking(); - + if (restart_ && controlCmd_ == CONTROL_CMD_PREPARE) { + RETURN_IF(!perfPipe_.perfEvents_.StartTracking(perfPipe_.isFifoServer_), HiperfError::PREPARE_START_TRACKING_FAIL); + } else { + RETURN_IF(!perfPipe_.perfEvents_.StartTracking((!perfPipe_.isFifoServer_) && (perfPipe_.clientPipeInput_ == -1)), + HiperfError::START_TRACKING_FAIL); + } + perfPipe_.CloseClientThread(); return HiperfError::NO_ERR; } @@ -696,28 +713,6 @@ bool RegisterSubCommandStat() return SubCommand::RegisterSubCommand("stat", SubCommandStat::GetInstance); } -bool SubCommandStat::PrepairEvents() -{ - if (selectEvents_.empty() && selectGroups_.empty()) { - perfEvents_.AddDefaultEvent(PERF_TYPE_HARDWARE); - perfEvents_.AddDefaultEvent(PERF_TYPE_SOFTWARE); - } else { - for (auto events : selectEvents_) { - if (!perfEvents_.AddEvents(events)) { - HLOGV("add events failed"); - return false; - } - } - for (auto events : selectGroups_) { - if (!perfEvents_.AddEvents(events, true)) { - HLOGV("add groups failed"); - return false; - } - } - } - return true; -} - bool SubCommandStat::CheckSelectCpuPidOption() { if (!selectCpus_.empty()) { @@ -757,6 +752,17 @@ bool SubCommandStat::CheckSelectCpuPidOption() return true; } +bool SubCommandStat::ParseControlCmd(const std::string &cmd) +{ + if (cmd.empty() || cmd == CONTROL_CMD_PREPARE || cmd == CONTROL_CMD_START || cmd == CONTROL_CMD_STOP) { + return true; + } + + printf("Invalid --control %s option, command should be: prepare, start, stop.\n", + cmd.c_str()); + return false; +} + bool SubCommandStat::CheckOptions(const std::vector &pids) { if (targetSystemWide_) { @@ -773,8 +779,11 @@ bool SubCommandStat::CheckOptions(const std::vector &pids) printf("You cannot specify --app and -t/-p at the same time\n"); return false; } + if (!ParseControlCmd(controlCmd_)) { + return false; + } if (!targetSystemWide_ && trackedCommand_.empty() && pids.empty() && appPackage_.empty() - && selectTids_.empty()) { + && selectTids_.empty() && (controlCmd_.empty() || controlCmd_ == CONTROL_CMD_PREPARE)) { printf("You need to set the -p option or --app option.\n"); return false; } -- Gitee