1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "hiperf_module.h"
16
17 #include <array>
18 #include <poll.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 #include <vector>
24
25 #include "hiperf_plugin_config.pb.h"
26 #include "logging.h"
27 #include "securec.h"
28 #include "common.h"
29 #include "trace_file_writer.h"
30
31 namespace {
32 constexpr uint32_t MAX_BUFFER_SIZE = 4 * 1024 * 1024;
33 constexpr uint32_t SLEEP_TIME = 250000;
34 const std::string SU_ROOT = "su root";
35 const std::string HIPERF_CMD = " hiperf";
36 const std::string HIPERF_RECORD_CMD = " record";
37 const std::string HIPERF_RECORD_PREPARE = " --control prepare";
38 const std::string HIPERF_RECORD_START = " --control start";
39 const std::string HIPERF_RECORD_STOP = " --control stop";
40 const std::string HIPERF_RECORD_OK = "sampling success";
41 const int WAIT_HIPERF_TIME = 10;
42 const std::string HIPERF_BIN_PATH = "/system/bin/hiperf";
43
44 std::mutex g_taskMutex;
45 bool g_isRoot = false;
46 std::string g_logLevel = "";
47 HiperfPluginConfig g_config;
48 std::shared_ptr<TraceFileWriter> g_splitTraceWriter {nullptr};
49
ParseConfigToCmd(const HiperfPluginConfig& config, std::vector<std::string>& cmds)50 bool ParseConfigToCmd(const HiperfPluginConfig& config, std::vector<std::string>& cmds)
51 {
52 g_isRoot = config.is_root();
53 auto logLevel = config.log_level();
54 if (logLevel == HiperfPluginConfig_LogLevel_MUCH) {
55 g_logLevel = " --hilog --much";
56 } else if (logLevel == HiperfPluginConfig_LogLevel_VERBOSE) {
57 g_logLevel = " --hilog --verbose";
58 } else if (logLevel == HiperfPluginConfig_LogLevel_DEBUG) {
59 g_logLevel = " --hilog --debug";
60 } else {
61 g_logLevel = " --nodebug";
62 }
63
64 // command of prepare
65 std::string traceCmd;
66 auto &prepareCmd = cmds.emplace_back();
67 prepareCmd = g_isRoot ? SU_ROOT : "";
68 prepareCmd += HIPERF_CMD + g_logLevel + HIPERF_RECORD_CMD + HIPERF_RECORD_PREPARE;
69 if (!config.outfile_name().empty()) {
70 prepareCmd += " -o " + config.outfile_name();
71 size_t fileSize = sizeof(g_pluginModule.outFileName);
72 int ret = strncpy_s(g_pluginModule.outFileName, fileSize, config.outfile_name().c_str(), fileSize - 1);
73 CHECK_TRUE(ret == EOK, false, "strncpy_s error! outfile is %s", config.outfile_name().c_str());
74 }
75 if (!config.record_args().empty()) {
76 prepareCmd += " " + config.record_args();
77 }
78
79 // command of start
80 auto &startCmd = cmds.emplace_back();
81 startCmd = g_isRoot ? SU_ROOT : "";
82 startCmd += HIPERF_CMD + g_logLevel + HIPERF_RECORD_CMD + HIPERF_RECORD_START;
83 return true;
84 }
85
RunCommand(const std::string& cmd)86 bool RunCommand(const std::string& cmd)
87 {
88 PROFILER_LOG_INFO(LOG_CORE, "run command: %s", cmd.c_str());
89 bool res = false;
90 std::vector<std::string> cmdArg;
91 COMMON::SplitString(cmd, " ", cmdArg);
92 cmdArg.emplace(cmdArg.begin(), HIPERF_BIN_PATH);
93
94 volatile pid_t childPid = -1;
95 int pipeFds[2] = {-1, -1};
96 FILE* fp = COMMON::CustomPopen(cmdArg, "r", pipeFds, childPid);
97 CHECK_NOTNULL(fp, false, "HiperfPlugin::RunCommand CustomPopen FAILED!r");
98 constexpr uint32_t readBufferSize = 4096;
99 std::array<char, readBufferSize> buffer;
100 std::string result;
101 usleep(WAIT_HIPERF_TIME);
102 while (fgets(buffer.data(), buffer.size(), fp) != nullptr) {
103 result += buffer.data();
104 res = result.find(HIPERF_RECORD_OK) != std::string::npos;
105 if (res) {
106 break;
107 }
108 }
109 COMMON::CustomPclose(fp, pipeFds, childPid);
110 PROFILER_LOG_INFO(LOG_CORE, "run command result: %s", result.c_str());
111 CHECK_TRUE(res, false, "HiperfPlugin::RunCommand: execute command FAILED!");
112 return true;
113 }
114 } // namespace
115
HiperfPluginSessionStart(const uint8_t* configData, const uint32_t configSize)116 int HiperfPluginSessionStart(const uint8_t* configData, const uint32_t configSize)
117 {
118 if (configData == nullptr) {
119 return -1;
120 }
121 std::lock_guard<std::mutex> guard(g_taskMutex);
122 (void)remove("/data/local/tmp/perf.data");
123 bool res = g_config.ParseFromArray(configData, configSize);
124 CHECK_TRUE(res, -1, "HiperfPluginSessionStart, parse config from array FAILED! configSize: %u", configSize);
125
126 if (!g_config.split_outfile_name().empty()) {
127 g_splitTraceWriter = std::make_shared<TraceFileWriter>(g_config.split_outfile_name());
128 g_splitTraceWriter->WriteStandalonePluginData(
129 std::string(g_pluginModule.name) + "_config",
130 std::string(reinterpret_cast<const char *>(configData),
131 configSize));
132 g_splitTraceWriter->SetTimeSource();
133 }
134
135 std::vector<std::string> cmds;
136 res = ParseConfigToCmd(g_config, cmds);
137 CHECK_TRUE(res, -1, "HiperfPluginSessionStart, parse config FAILED!");
138
139 for (const auto &cmd : cmds) {
140 res = RunCommand(cmd);
141 CHECK_TRUE(res, -1, "HiperfPluginSessionStart, RunCommand(%s) FAILED!", cmd.c_str());
142 }
143
144 return 0;
145 }
146
HiperfPluginSessionStop(void)147 int HiperfPluginSessionStop(void)
148 {
149 std::lock_guard<std::mutex> guard(g_taskMutex);
150 if (!g_config.split_outfile_name().empty()) {
151 CHECK_NOTNULL(g_splitTraceWriter, -1, "%s: writer is nullptr, SetDurationTime failed", __func__);
152 g_splitTraceWriter->SetDurationTime();
153 }
154
155 std::string cmd;
156 if (g_isRoot) {
157 cmd = SU_ROOT;
158 }
159 cmd += HIPERF_CMD + g_logLevel + HIPERF_RECORD_CMD;
160 cmd += HIPERF_RECORD_STOP;
161 RunCommand(cmd);
162 usleep(SLEEP_TIME); // 250000: wait for perf.data
163
164 if (!g_config.split_outfile_name().empty()) { // write split file.
165 CHECK_NOTNULL(g_splitTraceWriter, -1, "%s: writer is nullptr, WriteStandaloneFile failed", __func__);
166 g_splitTraceWriter->WriteStandalonePluginFile(std::string(g_pluginModule.outFileName),
167 std::string(g_pluginModule.name), std::string(g_pluginModule.version), DataType::HIPERF_DATA);
168 g_splitTraceWriter->Finish();
169 g_splitTraceWriter.reset();
170 g_splitTraceWriter = nullptr;
171 }
172 return 0;
173 }
174
HiperfRegisterWriterStruct(const WriterStruct* writer)175 int HiperfRegisterWriterStruct(const WriterStruct* writer)
176 {
177 PROFILER_LOG_INFO(LOG_CORE, "%s:writer", __func__);
178 return 0;
179 }
180
181 static PluginModuleCallbacks g_callbacks = {
182 .onPluginSessionStart = HiperfPluginSessionStart,
183 .onPluginReportResult = 0,
184 .onPluginSessionStop = HiperfPluginSessionStop,
185 .onRegisterWriterStruct = HiperfRegisterWriterStruct,
186 };
187
188 EXPORT_API PluginModuleStruct g_pluginModule = {
189 .callbacks = &g_callbacks,
190 .name = "hiperf-plugin",
191 .version = "1.02",
192 .resultBufferSizeHint = MAX_BUFFER_SIZE,
193 .isStandaloneFileData = true,
194 .outFileName = "/data/local/tmp/perf.data",
195 };