Line data Source code
1 : /**
2 : * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3 : * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 : * CANN Open Software License Agreement Version 2.0 (the "License").
5 : * Please refer to the License for details. You may not use this file except in compliance with the License.
6 : * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 : * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 : * See LICENSE in the root of the software repository for the full text of the License.
9 : */
10 :
11 : /* !
12 : * \file stub_backtrace.cpp
13 : * \brief
14 : */
15 : #include <csignal>
16 : #include <sstream>
17 : #include <string>
18 : #include <cstring>
19 : #include <map>
20 : #include <unistd.h>
21 : #include <sys/mman.h>
22 : #include <dlfcn.h>
23 : #include <link.h>
24 : #include <unwind.h>
25 : #include "securec.h"
26 : #include "stub_def.h"
27 :
28 : namespace {
29 0 : std::map<std::string, uint64_t>& BinaryBaseMap()
30 : {
31 0 : static std::map<std::string, uint64_t> instance;
32 0 : return instance;
33 : }
34 :
35 : } // namespace
36 :
37 : namespace AscendC {
38 : constexpr int32_t BT_MAX = 24 * 1024;
39 :
40 0 : const std::map<int, std::string>& GetCoreTypeMap()
41 : {
42 : static const std::map<int, std::string> coreTypeMap = {
43 : {AscendC::AIC_TYPE, "AIC_"},
44 : {AscendC::AIV_TYPE, "AIV_"},
45 : {AscendC::MIX_TYPE, "CORE_"},
46 0 : };
47 0 : return coreTypeMap;
48 0 : }
49 :
50 0 : std::map<int, std::string>& GetSignalMessage()
51 : {
52 : static std::map<int, std::string> g_signalMessage = {
53 0 : {SIGILL, "SIGILL Signal (Illegal instruction) catched"},
54 0 : {SIGBUS, "SIGBUS Signal (Bus error) catched"},
55 0 : {SIGFPE, "SIGFPE Signal (Floating point exception) catched"},
56 0 : {SIGSEGV, "SIGSEGV Signal (Invalid memory reference) catched"},
57 0 : {SIGPIPE, "SIGPIPE Signal (Broken pipe: write to pipe with no readers) catched"},
58 0 : {SIGABRT, "SIGABRT Signal (Abort Signal from abort) catched"},
59 0 : };
60 0 : return g_signalMessage;
61 0 : }
62 :
63 : struct BacktraceData {
64 : std::string exeObjName;
65 : std::ostringstream* outStream;
66 : };
67 :
68 : // parse the input msg and get the exec file name,
69 : // input msg is like this: output/ascend/host/ubuntu20.04/x86_64/lib/xxxxxx
70 : // the last / means the executed file name
71 0 : std::string GetExecName(const std::string& msg)
72 : {
73 0 : size_t lastSlashPos = msg.find_last_of('/');
74 0 : if (lastSlashPos != std::string::npos) {
75 0 : return msg.substr(lastSlashPos + 1);
76 : }
77 :
78 0 : return msg;
79 : }
80 :
81 : // execute addr2line cmd and get the output
82 0 : std::string ExecCmd(const char* cmd)
83 : {
84 0 : FILE* fp = popen(cmd, "r");
85 0 : if (fp) {
86 0 : char buffer[BT_MAX] = "";
87 0 : if (fgets(buffer, BT_MAX, fp) == nullptr) {
88 0 : return std::string("addr2line excuted failed.");
89 : }
90 0 : (void)pclose(fp);
91 0 : return std::string(buffer);
92 : }
93 0 : return std::string("?");
94 : }
95 :
96 : std::string GetExecPath();
97 : std::string StackTrace();
98 : bool IsValidBinary(const std::string& exceutePath);
99 :
100 : namespace {
101 : // callback function to process each frame
102 0 : _Unwind_Reason_Code UnwindCallback(struct _Unwind_Context* context, void* arg)
103 : {
104 : // get pc with unwind function
105 0 : int ipBeforeInsn = 0;
106 : static int32_t frameId = 0;
107 0 : uintptr_t pc = _Unwind_GetIPInfo(context, &ipBeforeInsn);
108 0 : BacktraceData* btData = reinterpret_cast<BacktraceData*>(arg);
109 0 : if (ipBeforeInsn == 0) {
110 0 : --pc;
111 : }
112 :
113 : Dl_info info;
114 0 : int32_t ret = dladdr(reinterpret_cast<void*>(pc), &info);
115 0 : if (ret == 0) {
116 0 : return _URC_NO_REASON;
117 : }
118 :
119 0 : std::string objName = GetExecName(info.dli_fname);
120 0 : if (BinaryBaseMap().count(objName) == 0) {
121 0 : return _URC_NO_REASON;
122 : }
123 :
124 0 : uint64_t baseAddr = BinaryBaseMap()[objName];
125 0 : uint64_t errorPC = static_cast<uint64_t>(pc) - baseAddr;
126 0 : std::vector<char> btCmd(BT_MAX);
127 0 : if (!IsValidBinary(info.dli_fname)) {
128 0 : return _URC_NO_REASON;
129 : }
130 :
131 0 : errno_t err = sprintf_s(btCmd.data(), BT_MAX, "addr2line -e %s -f -p -a -i -C 0x%lx", info.dli_fname, errorPC);
132 0 : if (err < 0) {
133 0 : return _URC_NO_REASON;
134 : }
135 0 : std::string result = ExecCmd(btCmd.data());
136 0 : *(btData->outStream) << "[#" << frameId << "] " << result;
137 0 : frameId++;
138 0 : return _URC_NO_REASON;
139 0 : }
140 :
141 : // translate cpu debug index to core name in v220
142 0 : std::string ConvertCpuIdxToCoreName(int idx)
143 : {
144 0 : const std::map<int, std::string>& coreTypeMap = GetCoreTypeMap();
145 0 : constexpr int subCoreAic = 0;
146 0 : constexpr int defaultTaskRation = 2;
147 0 : std::string coreName;
148 : // mix mode
149 0 : if (g_kernelMode == KernelMode::MIX_MODE) {
150 0 : int idxRes = idx % (defaultTaskRation + 1);
151 0 : int blockIdx = idx / (defaultTaskRation + 1);
152 0 : int subBlockIdx = 0;
153 0 : int flatIdx = 0;
154 0 : switch (idxRes) {
155 0 : case subCoreAic:
156 0 : flatIdx = blockIdx;
157 0 : coreName = coreTypeMap.at(AscendC::AIC_TYPE) + std::to_string(flatIdx);
158 0 : break;
159 0 : default:
160 0 : subBlockIdx = idxRes - 1;
161 0 : flatIdx = blockIdx * defaultTaskRation + subBlockIdx;
162 0 : coreName = coreTypeMap.at(AscendC::AIV_TYPE) + std::to_string(flatIdx);
163 0 : break;
164 : }
165 0 : return coreName;
166 : }
167 :
168 : // unmix mode
169 0 : coreName = coreTypeMap.at(AscendC::MIX_TYPE) + std::to_string(idx);
170 0 : return coreName;
171 0 : }
172 :
173 0 : std::string GetMainExecName()
174 : {
175 0 : std::string currentObj = GetExecPath();
176 0 : std::string exeName = GetExecName(currentObj);
177 0 : return exeName;
178 0 : }
179 :
180 : // record all binary name and base addr
181 0 : int32_t DlCallback(struct dl_phdr_info* info, size_t size, void* data)
182 : {
183 : (void)data;
184 : (void)size;
185 : static int32_t count = 0;
186 0 : if (std::strlen(info->dlpi_name) == 0) {
187 : // first frame dlpi name is empty, means the main execut obj self
188 0 : if (count == 0) {
189 0 : BinaryBaseMap().insert(std::make_pair(GetMainExecName(), info->dlpi_addr));
190 : }
191 : } else {
192 0 : BinaryBaseMap().insert(std::make_pair(GetExecName(info->dlpi_name), info->dlpi_addr));
193 : }
194 0 : count++;
195 0 : return 0;
196 : }
197 : } // namespace
198 : // reduce the print content and avoid some mistakes
199 0 : bool IsValidBinary(const std::string& exceutePath)
200 : {
201 0 : std::vector<std::string> skipPath = {"/lib", "/usr/lib", "/usr/local/lib"};
202 0 : for (const std::string& path : skipPath) {
203 0 : size_t n = exceutePath.find(path);
204 : // remove system files debug info
205 0 : if (n == 0) {
206 0 : return false;
207 : }
208 : }
209 :
210 0 : std::vector<std::string> skipFile = {"linux-vdso", "libcpudebug_stubreg.so"};
211 0 : std::string objName = GetExecName(exceutePath);
212 0 : for (const std::string& file : skipFile) {
213 0 : size_t n = objName.find(file);
214 : // remove system files debug info
215 0 : if (n == 0) {
216 0 : return false;
217 : }
218 : }
219 :
220 0 : return true;
221 0 : }
222 :
223 : // generate error message
224 0 : std::string GetExecPath()
225 : {
226 : char result[BT_MAX];
227 0 : ssize_t count = readlink("/proc/self/exe", result, BT_MAX);
228 0 : return std::string(result, (count > 0) ? count : 0);
229 : }
230 :
231 0 : std::string StackTrace()
232 : {
233 0 : dl_iterate_phdr(DlCallback, nullptr);
234 0 : std::ostringstream stacktraceStream;
235 0 : std::string exeName = GetMainExecName();
236 0 : BacktraceData btData = {exeName, &stacktraceStream};
237 0 : _Unwind_Backtrace(UnwindCallback, &btData);
238 : // return the backtrace info str
239 0 : std::string stackTrace = stacktraceStream.str();
240 0 : return stackTrace;
241 0 : }
242 :
243 : // generate core name from cpu debug index for all soc version
244 0 : std::string GetCoreName(int idx)
245 : {
246 0 : int coreIdx = idx;
247 0 : std::string coreName = "CORE_" + std::to_string(coreIdx);
248 0 : if (g_socVersion == SocVersion::VER_220 || g_socVersion == SocVersion::VER_310 ||
249 0 : g_socVersion == SocVersion::VER_510) {
250 0 : coreName = ConvertCpuIdxToCoreName(idx);
251 : }
252 0 : return coreName;
253 0 : }
254 :
255 0 : void BacktracePrint(int sig)
256 : {
257 0 : int flatIdx = block_idx;
258 0 : const std::map<int, std::string>& coreTypeMap = GetCoreTypeMap();
259 0 : std::string coreName = coreTypeMap.at(AscendC::MIX_TYPE);
260 0 : if (g_socVersion == SocVersion::VER_220 || g_socVersion == SocVersion::VER_310 ||
261 0 : g_socVersion == SocVersion::VER_510) {
262 0 : if (g_kernelMode == KernelMode::MIX_MODE) {
263 0 : coreName = coreTypeMap.at(g_coreType);
264 0 : flatIdx = (g_coreType == AscendC::AIC_TYPE) ? block_idx : (block_idx * g_taskRation + sub_block_idx);
265 : }
266 : }
267 0 : coreName += std::to_string(flatIdx);
268 : // samples: [ERROR][AIV_0][pid 12] error happened! =========
269 0 : std::string ret = "[ERROR][" + coreName + "][pid ";
270 0 : ret += std::to_string(getpid());
271 0 : ret += "] error happened! ========= \n";
272 : // samples: SIGSEGV Signal (Invalid memory reference) catched, backtrace info:
273 0 : ret += GetSignalMessage()[sig] + ", backtrace info:\n";
274 0 : ret += StackTrace();
275 : // using process lock to keep content clear
276 0 : AscendC::KernelPrintLock::GetLock()->Lock();
277 0 : std::cout << ret << std::endl;
278 0 : AscendC::KernelPrintLock::GetLock()->Unlock();
279 0 : }
280 : } // namespace AscendC
|