• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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