• 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 "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