* 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());
HIPERF_HILOGD(MODULE_DEFAULT, "[SetFifoFileName] C2S:%{public}s, S2C:%{public}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());
HIPERF_HILOGE(MODULE_DEFAULT, "another %{public}s service is running.", 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)
{
char errInfo[ERRINFOLEN] = { 0 };
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,
"[SendFifoAndWaitReply] can not open fifo file: %{public}s, errno:(%{public}d:%{public}s)",
fifoFileS2C_.c_str(), errno, errInfo);
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,
"[SendFifoAndWaitReply] can not open fifo file: %{public}s, errno:(%{public}d:%{public}s)",
fifoFileC2S_.c_str(), errno, errInfo);
close(fdRead);
return false;
}
ssize_t size = write(fdWrite, cmd.c_str(), cmd.size());
if (size != static_cast<ssize_t>(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(const int fd, const std::chrono::milliseconds &timeOut)
{
std::string reply;
WaitFifoReply(fd, timeOut, reply);
HIPERF_HILOGI(MODULE_DEFAULT, "[WaitFifoReply] WaitFifoReply reply:(%{public}s)", reply.c_str());
return reply == HiperfClient::REPLY_OK;
}
void PerfPipe::WaitFifoReply(const 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("[WaitFifoReply] read from fifo file(%s) failed", fifoFileS2C_.c_str());
HIPERF_HILOGE(MODULE_DEFAULT, "[WaitFifoReply] 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("[WaitFifoReply] wait fifo file(%s) timeout", fifoFileS2C_.c_str());
HIPERF_HILOGD(MODULE_DEFAULT, "[WaitFifoReply] wait fifo file(%{public}s) timeout", fifoFileS2C_.c_str());
} else {
HLOGD("[WaitFifoReply] wait fifo file(%s) failed", fifoFileS2C_.c_str());
HIPERF_HILOGD(MODULE_DEFAULT, "[WaitFifoReply] wait fifo file(%{public}s) failed", fifoFileS2C_.c_str());
}
}
void PerfPipe::SetOutPutEnd(const bool outputEnd)
{
outputEnd_ = outputEnd;
}
void PerfPipe::ProcessStopCommand(const bool ret)
{
if (ret) {
static constexpr uint64_t waitCheckSleepMs = 200;
std::this_thread::sleep_for(milliseconds(waitCheckSleepMs));
while (SendFifoAndWaitReply(HiperfClient::REPLY_CHECK, 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::REPLY_OUTPUT_CHECK, 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::REPLY_START, CONTROL_WAITREPY_TIMEOUT);
} else if (controlCmd_ == CONTROL_CMD_RESUME) {
ret = SendFifoAndWaitReply(HiperfClient::REPLY_RESUME, CONTROL_WAITREPY_TIMEOUT);
} else if (controlCmd_ == CONTROL_CMD_PAUSE) {
ret = SendFifoAndWaitReply(HiperfClient::REPLY_PAUSE, CONTROL_WAITREPY_TIMEOUT);
} else if (controlCmd_ == CONTROL_CMD_STOP) {
ret = SendFifoAndWaitReply(HiperfClient::REPLY_STOP, CONTROL_WAITREPY_TIMEOUT);
if (!ret) {
ret = SendFifoAndWaitReply(HiperfClient::REPLY_STOP, CONTROL_WAITREPY_TIMEOUT);
}
ProcessStopCommand(ret);
} else if (controlCmd_ == CONTROL_CMD_OUTPUT) {
ret = SendFifoAndWaitReply(HiperfClient::REPLY_OUTPUT, CONTROL_WAITREPY_TIMEOUT);
ProcessOutputCommand(ret);
}
if (ret) {
printf("%s %s success.\n", controlCmd_.c_str(), perfCmd_.c_str());
HIPERF_HILOGI(MODULE_DEFAULT, "[ProcessControlCmd] %{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, "[ProcessControlCmd] %{public}s %{public}s failed.",
controlCmd_.c_str(), perfCmd_.c_str());
}
return ret;
}
}
}
}