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