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 "kernel_stack_async_collector.h"
17 #include <chrono>
18 #include <future>
19 #include <thread>
20 #include <vector>
21 #include <unistd.h>
22
23 #include "dfx_kernel_stack.h"
24 #include "dfx_log.h"
25 #include "elapsed_time.h"
26 #include "procinfo.h"
27
28 #ifdef LOG_DOMAIN
29 #undef LOG_DOMAIN
30 #define LOG_DOMAIN 0xD002D11
31 #endif
32
33 #ifdef LOG_TAG
34 #undef LOG_TAG
35 #define LOG_TAG "AsyncKernelStack"
36 #endif
37
38 namespace OHOS {
39 namespace HiviewDFX {
40
41 std::atomic<int> KernelStackAsyncCollector::asyncCount_{0};
42 struct KerrCodeToErrCode {
43 KernelStackErrorCode kErrCode;
44 KernelStackAsyncCollector::ErrorCode errCode;
45 };
46 const struct KerrCodeToErrCode ERR_CODE_CONVERT_TABLE[] = {
47 { KERNELSTACK_ESUCCESS, KernelStackAsyncCollector::STACK_SUCCESS },
48 { KERNELSTACK_ECREATE, KernelStackAsyncCollector::STACK_ECREATE },
49 { KERNELSTACK_EOPEN, KernelStackAsyncCollector::STACK_EOPEN },
50 { KERNELSTACK_EIOCTL, KernelStackAsyncCollector::STACK_EIOCTL },
51 };
52
GetProcessStackWithTimeout(int pid,uint32_t timeoutMs) const53 KernelStackAsyncCollector::KernelResult KernelStackAsyncCollector::GetProcessStackWithTimeout(int pid,
54 uint32_t timeoutMs) const
55 {
56 if (asyncCount_ > maxAsyncTaskNum_) {
57 DFXLOGE("GetProcessStackWithTimeout fail, overlimit pid:%{public}d count:%{public}d",
58 pid, static_cast<int>(asyncCount_));
59 return KernelResult {STACK_OVER_LIMIT};
60 }
61 std::promise<KernelResult> result;
62 auto f = result.get_future();
63 // kernel may take much time
64 std::thread {CollectKernelStackTask, pid, std::move(result)}.detach();
65 auto st = f.wait_for(std::chrono::milliseconds(timeoutMs));
66 if (st == std::future_status::timeout) {
67 DFXLOGE("GetStackWithTimeout task timeout pid:%{public}d", pid);
68 return KernelResult {STACK_TIMEOUT};
69 } else if (st == std::future_status::deferred) {
70 DFXLOGE("GetStackWithTimeout task deferred pid:%{public}d", pid);
71 return KernelResult {STACK_DEFERRED};
72 }
73 return f.get();
74 }
75
NotifyStartCollect(int pid)76 bool KernelStackAsyncCollector::NotifyStartCollect(int pid)
77 {
78 if (asyncCount_ > maxAsyncTaskNum_) {
79 DFXLOGE("NotifyStartCollect fail, overlimit pid:%{public}d count:%{public}d",
80 pid, static_cast<int>(asyncCount_));
81 return false;
82 }
83 std::promise<KernelResult> result;
84 stackFuture_ = result.get_future();
85 // kernel may take much time
86 std::thread {CollectKernelStackTask, pid, std::move(result)}.detach();
87 return true;
88 }
89
GetCollectedStackResult()90 KernelStackAsyncCollector::KernelResult KernelStackAsyncCollector::GetCollectedStackResult()
91 {
92 if (!stackFuture_.valid()) {
93 DFXLOGE("GetCollectStackResult fail, overlimit count:%{public}d", static_cast<int>(asyncCount_));
94 return KernelResult {STACK_OVER_LIMIT};
95 }
96 auto st = stackFuture_.wait_for(std::chrono::milliseconds(0));
97 if (st == std::future_status::timeout) {
98 DFXLOGE("GetCollectStackResult task timeout");
99 return KernelResult {STACK_TIMEOUT};
100 } else if (st == std::future_status::deferred) {
101 DFXLOGE("GetCollectStackResult task deferred");
102 return KernelResult {STACK_DEFERRED};
103 }
104 return stackFuture_.get();
105 }
106
107 class AutoCounter {
108 public:
AutoCounter(std::atomic<int> & count)109 explicit AutoCounter(std::atomic<int> &count) : count_(count)
110 {
111 count_++;
112 }
113 AutoCounter(const AutoCounter&) = delete;
114 AutoCounter& operator=(const AutoCounter&) = delete;
115
~AutoCounter()116 ~AutoCounter()
117 {
118 count_--;
119 }
120 private:
121 std::atomic<int> &count_;
122 };
123
CollectKernelStackTask(int pid,std::promise<KernelResult> result)124 void KernelStackAsyncCollector::CollectKernelStackTask(int pid, std::promise<KernelResult> result)
125 {
126 AutoCounter autoCounter(asyncCount_);
127 ElapsedTime timer;
128 if (!CheckProcessValid(pid)) {
129 DFXLOGW("No process(%{public}d) status file exist!", pid);
130 result.set_value(KernelResult {STACK_NO_PROCESS});
131 return;
132 }
133 std::string kernelStackInfo;
134 int kernelRet = 0;
135 std::function<bool(int)> stackTask = [&kernelStackInfo, &kernelRet](int tid) {
136 if (tid <= 0) {
137 return false;
138 }
139 std::string tidKernelStackInfo;
140 int32_t ret = DfxGetKernelStack(tid, tidKernelStackInfo);
141 if (ret == 0) {
142 kernelStackInfo.append(tidKernelStackInfo);
143 } else if (kernelRet == 0) {
144 kernelRet = ret;
145 }
146 return true;
147 };
148 std::vector<int> tids;
149 (void)GetTidsByPidWithFunc(pid, tids, stackTask);
150 uint32_t threadCount = tids.size();
151 if (kernelStackInfo.empty()) {
152 DFXLOGE("Process(%{public}d) collect kernel stack fail!", pid);
153 result.set_value({ToErrCode(kernelRet), threadCount});
154 return;
155 }
156 result.set_value({STACK_SUCCESS, std::move(kernelStackInfo), threadCount});
157
158 DFXLOGI("finish collect all tid info for pid(%{public}d) time(%{public}" PRId64 ")ms", pid,
159 timer.Elapsed<std::chrono::milliseconds>());
160 }
161
CheckProcessValid(int pid)162 bool KernelStackAsyncCollector::CheckProcessValid(int pid)
163 {
164 std::string statusPath = std::string {"/proc/"} + std::to_string(pid) + "/status";
165 if (access(statusPath.c_str(), F_OK) != 0) {
166 DFXLOGW("No process(%{public}d) status file exist!", pid);
167 return false;
168 }
169 return true;
170 }
171
ToErrCode(int kernelErr)172 KernelStackAsyncCollector::ErrorCode KernelStackAsyncCollector::ToErrCode(int kernelErr)
173 {
174 auto iter = std::find_if(std::begin(ERR_CODE_CONVERT_TABLE), std::end(ERR_CODE_CONVERT_TABLE),
175 [kernelErr] (const KerrCodeToErrCode &kerrCodeToErrCode) { return kerrCodeToErrCode.kErrCode == kernelErr; });
176 return iter != std::end(ERR_CODE_CONVERT_TABLE) ? iter->errCode : STACK_UNKNOWN;
177 }
178 } // namespace HiviewDFX
179 } // namespace OHOS
180