1 /*
2 * Copyright (c) 2023 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 #ifdef FFRT_CO_BACKTRACE_OH_ENABLE
16 #include <dlfcn.h>
17 #include <sstream>
18 #include "unwinder.h"
19 #include "backtrace_local.h"
20 #endif
21 #include <securec.h>
22 #include "c/ffrt_dump.h"
23 #include "dfx/bbox/bbox.h"
24 #include "internal_inc/osal.h"
25 #include "dfx/log/ffrt_log_api.h"
26 #include "dfx/trace_record/ffrt_trace_record.h"
27 #include "dump.h"
28
29 #ifdef FFRT_CO_BACKTRACE_OH_ENABLE
30 using namespace OHOS::HiviewDFX;
31 #endif
32
33 namespace ffrt {
34 constexpr uint32_t DEFAULT_TIMEOUT_MS = 30000;
35 struct TimeoutCfg {
Instanceffrt::TimeoutCfg36 static inline TimeoutCfg* Instance()
37 {
38 static TimeoutCfg inst;
39 return &inst;
40 }
41
42 uint32_t timeout = DEFAULT_TIMEOUT_MS;
43 ffrt_task_timeout_cb callback = nullptr;
44 };
45
46 #ifdef FFRT_CO_BACKTRACE_OH_ENABLE
DumpTask(CPUEUTask * task,std::string & stackInfo,uint8_t flag)47 void DumpTask(CPUEUTask* task, std::string& stackInfo, uint8_t flag)
48 {
49 ucontext_t ctx;
50
51 if (ExecuteCtx::Cur()->task == task || task == nullptr) {
52 if (flag == 0) {
53 OHOS::HiviewDFX::PrintTrace(-1);
54 } else {
55 OHOS::HiviewDFX::GetBacktrace(stackInfo, false);
56 }
57 return;
58 } else {
59 memset_s(&ctx, sizeof(ctx), 0, sizeof(ctx));
60 #if defined(__aarch64__)
61 ctx.uc_mcontext.regs[REG_AARCH64_X29] = task->coRoutine->ctx.regs[10];
62 ctx.uc_mcontext.sp = task->coRoutine->ctx.regs[13];
63 ctx.uc_mcontext.pc = task->coRoutine->ctx.regs[11];
64 #elif defined(__x86_64__)
65 ctx.uc_mcontext.gregs[REG_RBX] = task->coRoutine->ctx.regs[0];
66 ctx.uc_mcontext.gregs[REG_RBP] = task->coRoutine->ctx.regs[1];
67 ctx.uc_mcontext.gregs[REG_RSP] = task->coRoutine->ctx.regs[6];
68 ctx.uc_mcontext.gregs[REG_RIP] = *(reinterpret_cast<greg_t *>(ctx.uc_mcontext.gregs[REG_RSP] - 8));
69 #elif defined(__arm__)
70 ctx.uc_mcontext.arm_sp = task->coRoutine->ctx.regs[0]; /* sp */
71 ctx.uc_mcontext.arm_pc = task->coRoutine->ctx.regs[1]; /* pc */
72 ctx.uc_mcontext.arm_lr = task->coRoutine->ctx.regs[1]; /* lr */
73 ctx.uc_mcontext.arm_fp = task->coRoutine->ctx.regs[10]; /* fp */
74 #endif
75 }
76
77 auto co = task->coRoutine;
78 uintptr_t stackBottom = reinterpret_cast<uintptr_t>(reinterpret_cast<char*>(co) + sizeof(CoRoutine) - 8);
79 uintptr_t stackTop = static_cast<uintptr_t>(stackBottom + co->stkMem.size);
80 auto unwinder = std::make_shared<Unwinder>();
81 auto regs = DfxRegs::CreateFromUcontext(ctx);
82 unwinder->SetRegs(regs);
83 UnwindContext context;
84 context.pid = UNWIND_TYPE_LOCAL;
85 context.regs = regs;
86 context.maps = unwinder->GetMaps();
87 context.stackCheck = false;
88 context.stackBottom = stackBottom;
89 context.stackTop = stackTop;
90 bool resFlag = unwinder->Unwind(&context);
91 if (!resFlag) {
92 FFRT_LOGE("Call Unwind failed");
93 return;
94 }
95 std::ostringstream ss;
96 auto frames = unwinder->GetFrames();
97 if (flag != 0) {
98 ss << Unwinder::GetFramesStr(frames);
99 ss << std::endl;
100 stackInfo = ss.str();
101 return;
102 }
103 FFRT_LOGE("%s", Unwinder::GetFramesStr(frames).c_str());
104 }
105 #endif
106 }
107
108 #ifdef __cplusplus
109 extern "C" {
110 #endif //__cplusplus
111
112 API_ATTRIBUTE((visibility("default")))
113 int dump_info_all(char *buf, uint32_t len)
114 {
115 #ifdef FFRT_CO_BACKTRACE_OH_ENABLE
116 if (FFRTIsWork()) {
117 std::string dumpInfo;
118 dumpInfo += "|-> Launcher proc ffrt, pid:" + std::to_string(GetPid()) + "\n";
119 #if (FFRT_TRACE_RECORD_LEVEL >= FFRT_TRACE_RECORD_LEVEL_2)
120 dumpInfo += SaveTaskCounterInfo();
121 #endif
122 dumpInfo += SaveKeyInfo();
123 dumpInfo += SaveWorkerStatusInfo();
124 dumpInfo += SaveNormalTaskStatusInfo();
125 dumpInfo += SaveQueueTaskStatusInfo();
126 if (dumpInfo.length() > (len - 1)) {
127 FFRT_LOGW("dumpInfo exceeds the buffer length, length:%d", dumpInfo.length());
128 }
129 return snprintf_s(buf, len, len - 1, "%s", dumpInfo.c_str());
130 } else {
131 return snprintf_s(buf, len, len - 1, "|-> FFRT has done all tasks, pid: %u \n", GetPid());
132 }
133 #else
134 return -1;
135 #endif
136 }
137
138 API_ATTRIBUTE((visibility("default")))
139 int ffrt_dump(ffrt_dump_cmd_t cmd, char *buf, uint32_t len)
140 {
141 FFRT_COND_RETURN_ERROR(buf == nullptr || len < 1, -1, "buf is nullptr or len is less than 1, len %u", len);
142 switch (cmd) {
143 case DUMP_INFO_ALL: {
144 return dump_info_all(buf, len);
145 }
146 case DUMP_TASK_STATISTIC_INFO: {
147 #if (FFRT_TRACE_RECORD_LEVEL >= FFRT_TRACE_RECORD_LEVEL_2)
148 return ffrt::FFRTTraceRecord::StatisticInfoDump(buf, len);
149 #else
150 return -1;
151 #endif
152 }
153 default: {
154 FFRT_LOGE("ffrt_dump unsupport cmd[%d]", cmd);
155 }
156 }
157 return -1;
158 }
159
160 API_ATTRIBUTE((visibility("default")))
161 ffrt_task_timeout_cb ffrt_task_timeout_get_cb(void)
162 {
163 return ffrt::TimeoutCfg::Instance()->callback;
164 }
165
166 API_ATTRIBUTE((visibility("default")))
167 void ffrt_task_timeout_set_cb(ffrt_task_timeout_cb cb)
168 {
169 ffrt::TimeoutCfg::Instance()->callback = cb;
170 }
171
172 API_ATTRIBUTE((visibility("default")))
173 uint32_t ffrt_task_timeout_get_threshold(void)
174 {
175 return ffrt::TimeoutCfg::Instance()->timeout;
176 }
177
178 API_ATTRIBUTE((visibility("default")))
179 void ffrt_task_timeout_set_threshold(uint32_t threshold_ms)
180 {
181 ffrt::TimeoutCfg::Instance()->timeout = threshold_ms;
182 }
183 #ifdef __cplusplus
184 }
185 #endif
186