1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
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
16 #include "lite_perf.h"
17
18 #include <dlfcn.h>
19 #include <fcntl.h>
20 #include <map>
21 #include <mutex>
22 #include <poll.h>
23 #include <securec.h>
24 #include <string_ex.h>
25 #include <sys/syscall.h>
26 #include <sys/types.h>
27 #include <sys/uio.h>
28 #include <sys/wait.h>
29 #include <unistd.h>
30 #include "dfx_define.h"
31 #include "dfx_dump_res.h"
32 #include "dfx_dumprequest.h"
33 #include "dfx_log.h"
34 #include "dfx_lperf.h"
35 #include "dfx_util.h"
36 #include "faultloggerd_client.h"
37 #include "procinfo.h"
38 #include "smart_fd.h"
39 #include "stack_printer.h"
40 #include "unwinder.h"
41 #include "unwinder_config.h"
42
43 namespace OHOS {
44 namespace HiviewDFX {
45 namespace {
46 #undef LOG_DOMAIN
47 #define LOG_DOMAIN 0xD002D11
48 #undef LOG_TAG
49 #define LOG_TAG "DfxLitePerf"
50 }
51
52 class LitePerf::Impl {
53 public:
54 int StartProcessStackSampling(const std::vector<int>& tids, int freq, int durationMs, bool parseMiniDebugInfo);
55 int CollectSampleStackByTid(int tid, std::string& stack);
56 int FinishProcessStackSampling();
57
58 private:
59 bool IsValidParam(const std::vector<int>& tids, int freq, int durationMs);
60 int ExecDump(const std::vector<int>& tids, int freq, int durationMs);
61 bool InitDumpParam(const std::vector<int>& tids, int freq, int durationMs, LitePerfParam& lperf);
62 bool ExecDumpPipe(const int (&pipefd)[2], const LitePerfParam& lperf);
63 void FinishDump();
64
65 int DumpPoll(const int (&pipeFds)[2], const int timeout);
66 bool HandlePollEvents(const struct pollfd (&readFds)[2], const int (&pipeFds)[2], bool& bPipeConnect, int& pollRet);
67 bool DoReadBuf(int fd);
68 bool DoReadRes(int fd, int& pollRet);
69 bool ParseSampleStacks(const std::string& datas);
70
71 std::string bufMsg_;
72 std::string resMsg_;
73 std::map<int, std::string> stackMaps_{};
74 std::mutex mutex_;
75 std::atomic<bool> isRunning_{false};
76
77 bool defaultEnableDebugInfo_ {false};
78 bool enableDebugInfoSymbolic_ {false};
79 };
80
LitePerf()81 LitePerf::LitePerf() : impl_(std::make_shared<Impl>())
82 {}
83
StartProcessStackSampling(const std::vector<int> & tids,int freq,int durationMs,bool parseMiniDebugInfo)84 int LitePerf::StartProcessStackSampling(const std::vector<int>& tids, int freq, int durationMs, bool parseMiniDebugInfo)
85 {
86 return impl_->StartProcessStackSampling(tids, freq, durationMs, parseMiniDebugInfo);
87 }
88
CollectSampleStackByTid(int tid,std::string & stack)89 int LitePerf::CollectSampleStackByTid(int tid, std::string& stack)
90 {
91 return impl_->CollectSampleStackByTid(tid, stack);
92 }
93
FinishProcessStackSampling()94 int LitePerf::FinishProcessStackSampling()
95 {
96 return impl_->FinishProcessStackSampling();
97 }
98
StartProcessStackSampling(const std::vector<int> & tids,int freq,int durationMs,bool parseMiniDebugInfo)99 int LitePerf::Impl::StartProcessStackSampling(const std::vector<int>& tids, int freq, int durationMs,
100 bool parseMiniDebugInfo)
101 {
102 DFXLOGI("StartProcessStackSampling.");
103 int res = 0;
104 if (!IsValidParam(tids, freq, durationMs)) {
105 DFXLOGE("Invalid stack sampling param.");
106 return -1;
107 }
108 bool expected = false;
109 if (!isRunning_.compare_exchange_strong(expected, true)) {
110 DFXLOGW("Process is being sampling.");
111 return -1;
112 }
113 enableDebugInfoSymbolic_ = parseMiniDebugInfo;
114 int timeout = durationMs + DUMP_LITEPERF_TIMEOUT;
115
116 int pipeReadFd[] = {-1, -1};
117 int req = RequestLitePerfPipeFd(FaultLoggerPipeType::PIPE_FD_READ, pipeReadFd,
118 static_cast<int>(timeout / 1000));
119 if (req != ResponseCode::REQUEST_SUCCESS) {
120 DFXLOGE("Failed to request liteperf pipe read.");
121 isRunning_.store(false);
122 return -1;
123 }
124
125 do {
126 if (ExecDump(tids, freq, durationMs) < 0) {
127 res = -1;
128 break;
129 }
130
131 if (DumpPoll(pipeReadFd, timeout) < 0) {
132 res = -1;
133 break;
134 }
135 } while (false);
136 FinishDump();
137 return res;
138 }
139
IsValidParam(const std::vector<int> & tids,int freq,int durationMs)140 bool LitePerf::Impl::IsValidParam(const std::vector<int>& tids, int freq, int durationMs)
141 {
142 if (tids.size() < MIN_SAMPLE_TIDS || tids.size() > MAX_SAMPLE_TIDS ||
143 freq < MIN_SAMPLE_FREQUENCY || freq > MAX_SAMPLE_FREQUENCY ||
144 durationMs < MIN_STOP_SECONDS || durationMs > MAX_STOP_SECONDS) {
145 return false;
146 }
147 return true;
148 }
149
FinishDump()150 void LitePerf::Impl::FinishDump()
151 {
152 DFX_RestoreDumpableState();
153 int req = RequestLitePerfDelPipeFd();
154 if (req != ResponseCode::REQUEST_SUCCESS) {
155 DFXLOGE("Failed to request liteperf pipe delete.");
156 }
157 isRunning_.store(false);
158 }
159
DumpPoll(const int (& pipeFds)[2],const int timeout)160 int LitePerf::Impl::DumpPoll(const int (&pipeFds)[2], const int timeout)
161 {
162 MAYBE_UNUSED int pollRet = DUMP_POLL_INIT;
163 struct pollfd readFds[2] = {};
164 readFds[0].fd = pipeFds[PIPE_BUF_INDEX];
165 readFds[0].events = POLLIN;
166 readFds[1].fd = pipeFds[PIPE_RES_INDEX];
167 readFds[1].events = POLLIN;
168 int fdsSize = sizeof(readFds) / sizeof(readFds[0]);
169 bool bPipeConnect = false;
170 uint64_t endTime = GetAbsTimeMilliSeconds() + static_cast<uint64_t>(timeout);
171 bool isContinue = true;
172 do {
173 uint64_t now = GetAbsTimeMilliSeconds();
174 if (now >= endTime) {
175 pollRet = DUMP_POLL_TIMEOUT;
176 resMsg_.append("Result: poll timeout.\n");
177 isContinue = false;
178 break;
179 }
180 int remainTime = static_cast<int>(endTime - now);
181
182 int pRet = poll(readFds, fdsSize, remainTime);
183 if (pRet < 0) {
184 if (errno == EINTR) {
185 continue;
186 }
187 pollRet = DUMP_POLL_FAILED;
188 resMsg_.append("Result: poll error, errno(" + std::to_string(errno) + ")\n");
189 isContinue = false;
190 break;
191 } else if (pRet == 0) {
192 pollRet = DUMP_POLL_TIMEOUT;
193 resMsg_.append("Result: poll timeout.\n");
194 isContinue = false;
195 break;
196 }
197
198 if (!HandlePollEvents(readFds, pipeFds, bPipeConnect, pollRet)) {
199 isContinue = false;
200 break;
201 }
202 } while (isContinue);
203 DFXLOGI("%{public}s :: %{public}s", __func__, resMsg_.c_str());
204 return pollRet == DUMP_POLL_OK ? 0 : -1;
205 }
206
HandlePollEvents(const struct pollfd (& readFds)[2],const int (& pipeFds)[2],bool & bPipeConnect,int & pollRet)207 bool LitePerf::Impl::HandlePollEvents(const struct pollfd (&readFds)[2], const int (&pipeFds)[2],
208 bool& bPipeConnect, int& pollRet)
209 {
210 bool bufRet = true;
211 bool resRet = false;
212 bool eventRet = true;
213 for (auto& readFd : readFds) {
214 if (!bPipeConnect && (static_cast<uint32_t>(readFd.revents) & POLLIN)) {
215 bPipeConnect = true;
216 }
217
218 if (bPipeConnect && ((static_cast<uint32_t>(readFd.revents) & POLLERR))) {
219 resMsg_.append("Result: poll events error.\n");
220 eventRet = false;
221 break;
222 }
223
224 if ((static_cast<uint32_t>(readFd.revents) & POLLIN) != POLLIN) {
225 continue;
226 }
227
228 if (readFd.fd == pipeFds[PIPE_BUF_INDEX]) {
229 bufRet = DoReadBuf(pipeFds[PIPE_BUF_INDEX]);
230 } else if (readFd.fd == pipeFds[PIPE_RES_INDEX]) {
231 resRet = DoReadRes(pipeFds[PIPE_RES_INDEX], pollRet);
232 }
233 }
234 if ((eventRet == false) || (bufRet == false) || (resRet == true)) {
235 DFXLOGI("eventRet:%{public}d bufRet:%{public}d resRet:%{public}d", eventRet, bufRet, resRet);
236 return false;
237 }
238 return true;
239 }
240
DoReadBuf(int fd)241 bool LitePerf::Impl::DoReadBuf(int fd)
242 {
243 std::vector<char> buffer(MAX_PIPE_SIZE, 0);
244 ssize_t nread = OHOS_TEMP_FAILURE_RETRY(read(fd, buffer.data(), MAX_PIPE_SIZE));
245 if (nread <= 0) {
246 DFXLOGW("%{public}s :: read error", __func__);
247 return false;
248 }
249 DFXLOGI("%{public}s :: nread: %{public}zu", __func__, nread);
250 bufMsg_.append(buffer.data(), static_cast<size_t>(nread));
251 return true;
252 }
253
DoReadRes(int fd,int & pollRet)254 bool LitePerf::Impl::DoReadRes(int fd, int& pollRet)
255 {
256 int32_t res = DumpErrorCode::DUMP_ESUCCESS;
257 ssize_t nread = OHOS_TEMP_FAILURE_RETRY(read(fd, &res, sizeof(res)));
258 if (nread <= 0 || nread != sizeof(res)) {
259 DFXLOGW("%{public}s :: read error", __func__);
260 return false;
261 }
262 pollRet = (res == DUMP_ESUCCESS) ? DUMP_POLL_OK : DUMP_POLL_FAILED;
263 resMsg_.append("Result: " + DfxDumpRes::ToString(res) + "\n");
264 return true;
265 }
266
InitDumpParam(const std::vector<int> & tids,int freq,int durationMs,LitePerfParam & lperf)267 bool LitePerf::Impl::InitDumpParam(const std::vector<int>& tids, int freq, int durationMs, LitePerfParam& lperf)
268 {
269 (void)memset_s(&lperf, sizeof(LitePerfParam), 0, sizeof(LitePerfParam));
270 int tidSize = 0;
271 lperf.pid = getpid();
272 for (size_t i = 0; i < tids.size() && i < MAX_SAMPLE_TIDS; ++i) {
273 if (tids[i] <= 0 || !IsThreadInPid(lperf.pid, tids[i])) {
274 DFXLOGW("%{public}s :: tid(%{public}d) error", __func__, tids[i]);
275 continue;
276 }
277 lperf.tids[i] = tids[i];
278 tidSize++;
279 }
280
281 if (tidSize <= 0) {
282 DFXLOGE("%{public}s :: all tids error", __func__);
283 return false;
284 }
285 lperf.freq = freq;
286 lperf.durationMs = durationMs;
287
288 if (DFX_SetDumpableState() == false) {
289 DFXLOGE("%{public}s :: Failed to set dumpable.", __func__);
290 return false;
291 }
292 return true;
293 }
294
ExecDump(const std::vector<int> & tids,int freq,int durationMs)295 int LitePerf::Impl::ExecDump(const std::vector<int>& tids, int freq, int durationMs)
296 {
297 static LitePerfParam lperf;
298 if (!InitDumpParam(tids, freq, durationMs, lperf)) {
299 return -1;
300 }
301
302 pid_t pid = 0;
303 pid = fork();
304 if (pid < 0) {
305 DFXLOGE("Failed to fork.");
306 return -1;
307 }
308 if (pid == 0) {
309 int pipefd[PIPE_NUM_SZ] = {-1, -1};
310 if (pipe2(pipefd, O_NONBLOCK) != 0) {
311 DFXLOGE("Failed to create pipe, errno: %{public}d.", errno);
312 _exit(-1);
313 }
314
315 pid_t dumpPid = fork();
316 if (dumpPid < 0) {
317 DFXLOGE("Failed to fork.");
318 _exit(-1);
319 }
320 if (dumpPid == 0) {
321 ExecDumpPipe(pipefd, lperf);
322 _exit(0);
323 } else {
324 _exit(0);
325 }
326 }
327 int res = waitpid(pid, nullptr, 0);
328 if (res < 0) {
329 DFXLOGE("Failed to wait pid(%{public}d), errno(%{public}d)", pid, errno);
330 } else {
331 DFXLOGI("wait pid(%{public}d) exit", pid);
332 }
333 return 0;
334 }
335
ExecDumpPipe(const int (& pipefd)[2],const LitePerfParam & lperf)336 bool LitePerf::Impl::ExecDumpPipe(const int (&pipefd)[2], const LitePerfParam& lperf)
337 {
338 ssize_t writeLen = sizeof(LitePerfParam);
339 if (fcntl(pipefd[1], F_SETPIPE_SZ, writeLen) < writeLen) {
340 DFXLOGE("Failed to set pipe buffer size, errno(%{public}d).", errno);
341 return false;
342 }
343
344 struct iovec iovs[1] = {
345 {
346 .iov_base = (void *)(&lperf),
347 .iov_len = sizeof(struct LitePerfParam)
348 },
349 };
350
351 if (OHOS_TEMP_FAILURE_RETRY(writev(pipefd[PIPE_WRITE], iovs, 1)) != writeLen) {
352 DFXLOGE("Failed to write pipe, errno(%{public}d)", errno);
353 return false;
354 }
355 OHOS_TEMP_FAILURE_RETRY(dup2(pipefd[PIPE_READ], STDOUT_FILENO));
356 if (pipefd[PIPE_READ] != STDOUT_FILENO) {
357 close(pipefd[PIPE_READ]);
358 }
359 close(pipefd[PIPE_WRITE]);
360
361 if (DFX_InheritCapabilities() != 0) {
362 DFXLOGE("Failed to inherit Capabilities from parent.");
363 return false;
364 }
365
366 DFXLOGI("execl processdump -liteperf.");
367 execl(PROCESSDUMP_PATH, "processdump", "-liteperf", nullptr);
368 DFXLOGE("Failed to execl processdump -liteperf, errno(%{public}d)", errno);
369 return false;
370 }
371
ParseSampleStacks(const std::string & datas)372 bool LitePerf::Impl::ParseSampleStacks(const std::string& datas)
373 {
374 if (datas.empty()) {
375 return false;
376 }
377
378 std::unique_lock<std::mutex> lck(mutex_);
379 if (stackMaps_.empty()) {
380 auto unwinder = std::make_shared<Unwinder>(false);
381 auto maps = DfxMaps::Create();
382 defaultEnableDebugInfo_ = UnwinderConfig::GetEnableMiniDebugInfo();
383 UnwinderConfig::SetEnableMiniDebugInfo(enableDebugInfoSymbolic_);
384 std::istringstream iss(datas);
385 auto frameMap = StackPrinter::DeserializeSampledFrameMap(iss);
386 for (const auto& pair : frameMap) {
387 auto stack = StackPrinter::PrintTreeStackBySampledStack(pair.second, false, unwinder, maps);
388 stackMaps_.emplace(pair.first, std::move(stack));
389 }
390 }
391 return (stackMaps_.size() > 0);
392 }
393
CollectSampleStackByTid(int tid,std::string & stack)394 int LitePerf::Impl::CollectSampleStackByTid(int tid, std::string& stack)
395 {
396 DFXLOGI("CollectSampleStackByTid, tid:%{public}d.", tid);
397 if (!ParseSampleStacks(bufMsg_)) {
398 return -1;
399 }
400
401 std::unique_lock<std::mutex> lck(mutex_);
402 if (stackMaps_.find(tid) != stackMaps_.end()) {
403 stack = stackMaps_[tid];
404 if (stack.size() > 0) {
405 return 0;
406 }
407 }
408 return -1;
409 }
410
FinishProcessStackSampling()411 int LitePerf::Impl::FinishProcessStackSampling()
412 {
413 DFXLOGI("FinishProcessStackSampling.");
414 std::unique_lock<std::mutex> lck(mutex_);
415 UnwinderConfig::SetEnableMiniDebugInfo(defaultEnableDebugInfo_);
416 stackMaps_.clear();
417 bufMsg_.clear();
418 resMsg_.clear();
419 return 0;
420 }
421 } // namespace HiviewDFX
422 } // namespace OHOS