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 #include "uncaught_exception_callback.h"
16
17 #include <charconv>
18 #include <dlfcn.h>
19 #include <string>
20 #include <sstream>
21
22 #include "dfx_symbols.h"
23 #include "elf_factory.h"
24 #include "hilog_tag_wrapper.h"
25 #include "string_printf.h"
26 #ifdef SUPPORT_GRAPHICS
27 #include "ui_content.h"
28 #endif // SUPPORT_GRAPHICS
29 #include "unwinder.h"
30 #include "unwinder_config.h"
31
32 namespace OHOS {
33 namespace JsEnv {
34 constexpr char BACKTRACE[] = "=====================Backtrace========================";
35 constexpr size_t FLAG_SPLIT_POS = 16;
36 constexpr size_t FLAG_PC_POS = 4;
37 constexpr char LIB_AYNC_STACK_SO_NAME[] = "libasync_stack.z.so";
38
39 typedef int (*SubmitterStackFunc)(char*, size_t);
40
GetNativeStrFromJsTaggedObj(napi_value obj,const char * key)41 std::string NapiUncaughtExceptionCallback::GetNativeStrFromJsTaggedObj(napi_value obj, const char* key)
42 {
43 if (obj == nullptr) {
44 TAG_LOGE(AAFwkTag::JSENV, "Failed to get value from key");
45 return "";
46 }
47
48 napi_value valueStr = nullptr;
49 napi_get_named_property(env_, obj, key, &valueStr);
50 napi_valuetype valueType = napi_undefined;
51 napi_typeof(env_, valueStr, &valueType);
52 if (valueType == napi_string) {
53 size_t valueStrBufLength = 0;
54 napi_get_value_string_utf8(env_, valueStr, nullptr, 0, &valueStrBufLength);
55 auto valueCStr = std::make_unique<char[]>(valueStrBufLength + 1);
56 size_t valueStrLength = 0;
57 napi_get_value_string_utf8(env_, valueStr, valueCStr.get(), valueStrBufLength + 1, &valueStrLength);
58 std::string ret(valueCStr.get(), valueStrLength);
59 TAG_LOGD(AAFwkTag::JSENV, "GetNativeStrFromJsTaggedObj Success as string");
60 return ret;
61 }
62 if (valueType == napi_number) {
63 int64_t valueInt;
64 napi_get_value_int64(env_, valueStr, &valueInt);
65 TAG_LOGD(AAFwkTag::JSENV, "GetNativeStrFromJsTaggedObj Success as int");
66 return std::to_string(valueInt);
67 }
68 TAG_LOGD(AAFwkTag::JSENV, "Failed to convert value from key");
69 return "";
70 }
71
operator ()(napi_value obj)72 void NapiUncaughtExceptionCallback::operator()(napi_value obj)
73 {
74 CallbackTask(obj);
75 }
76
operator ()(panda::TryCatch & trycatch)77 void NapiUncaughtExceptionCallback::operator()(panda::TryCatch& trycatch)
78 {
79 panda::Local<panda::ObjectRef> exception = trycatch.GetAndClearException();
80 if (!exception.IsEmpty() && !exception->IsHole()) {
81 napi_value obj = ArkNativeEngine::ArkValueToNapiValue(env_, exception);
82 CallbackTask(obj);
83 }
84 }
85
CallbackTask(napi_value & obj)86 void NapiUncaughtExceptionCallback::CallbackTask(napi_value& obj)
87 {
88 std::string errorMsg = GetNativeStrFromJsTaggedObj(obj, "message");
89 std::string errorName = GetNativeStrFromJsTaggedObj(obj, "name");
90 std::string errorStack = GetNativeStrFromJsTaggedObj(obj, "stack");
91 std::string summary = "Error name:" + errorName + "\n";
92 summary += "Error message:" + errorMsg + "\n";
93 const JsEnv::ErrorObject errorObj = {
94 .name = errorName,
95 .message = errorMsg,
96 .stack = errorStack
97 };
98 bool hasProperty = false;
99 napi_has_named_property(env_, obj, "code", &hasProperty);
100 if (hasProperty) {
101 std::string errorCode = GetNativeStrFromJsTaggedObj(obj, "code");
102 summary += "Error code:" + errorCode + "\n";
103 }
104 if (errorStack.empty()) {
105 TAG_LOGE(AAFwkTag::JSENV, "errorStack is empty");
106 return;
107 }
108 if (errorStack.find(BACKTRACE) != std::string::npos) {
109 summary += "Stacktrace:\n" + GetFuncNameAndBuildId(errorStack);
110 std::string submitterStack = GetSubmitterStackLocal();
111 if (!submitterStack.empty()) {
112 summary.append("========SubmitterStacktrace========\n");
113 summary.append(submitterStack);
114 }
115 } else {
116 summary += "Stacktrace:\n" + errorStack;
117 }
118 #ifdef SUPPORT_GRAPHICS
119 std::string str = Ace::UIContent::GetCurrentUIStackInfo();
120 if (!str.empty()) {
121 summary.append(str);
122 }
123 #endif // SUPPORT_GRAPHICS
124 if (uncaughtTask_) {
125 uncaughtTask_(summary, errorObj);
126 }
127 }
128
GetFuncNameAndBuildId(std::string nativeStack)129 std::string NapiUncaughtExceptionCallback::GetFuncNameAndBuildId(std::string nativeStack)
130 {
131 std::stringstream ss(nativeStack);
132 std::string tempStr;
133 std::string appendInfo;
134 // Filter two lines
135 std::getline(ss, tempStr); // Cannot get SourceMap info, dump raw stack:
136 std::getline(ss, tempStr); // =====================Backtrace========================
137 HiviewDFX::UnwinderConfig::SetEnableMiniDebugInfo(true);
138 HiviewDFX::UnwinderConfig::SetEnableLoadSymbolLazily(true);
139 while (std::getline(ss, tempStr)) {
140 auto splitPos = tempStr.rfind(" ");
141 if (splitPos == std::string::npos) {
142 return nativeStack;
143 }
144 HiviewDFX::RegularElfFactory elfFactory(tempStr.substr(splitPos + 1));
145 auto elfFile = elfFactory.Create();
146 std::string pc;
147 size_t pcPos = tempStr.find(" pc ");
148 if (pcPos == std::string::npos) {
149 return nativeStack;
150 }
151 pc = tempStr.substr(pcPos += FLAG_PC_POS, FLAG_SPLIT_POS);
152 uint64_t value;
153 auto res = std::from_chars(pc.data(), pc.data() + pc.size(), value, FLAG_SPLIT_POS);
154 if (res.ec != std::errc()) {
155 return nativeStack;
156 }
157 std::string funcName;
158 uint64_t funcOffset;
159 HiviewDFX::DfxSymbols::GetFuncNameAndOffsetByPc(value, elfFile, funcName, funcOffset);
160 std::string buildId = elfFile->GetBuildId();
161 if (!funcName.empty()) {
162 appendInfo += tempStr + "(" + funcName;
163 appendInfo += HiviewDFX::StringPrintf("+%" PRId64, funcOffset) + ")";
164 if (!buildId.empty()) {
165 appendInfo += "(" + buildId + ")" + "\n";
166 } else {
167 appendInfo += "\n";
168 }
169 } else {
170 if (!buildId.empty()) {
171 appendInfo += tempStr + "(" + buildId + ")" + "\n";
172 } else {
173 appendInfo += tempStr + "\n";
174 }
175 }
176 }
177 return appendInfo;
178 }
179
GetSubmitterStackLocal()180 std::string NapiUncaughtExceptionCallback::GetSubmitterStackLocal()
181 {
182 static SubmitterStackFunc sbmitterStack = nullptr;
183 void *handle = dlopen(LIB_AYNC_STACK_SO_NAME, RTLD_NOW);
184 if (!handle) {
185 TAG_LOGE(AAFwkTag::JSENV, "Failed to dlopen libasync_stack, %{public}s", dlerror());
186 return "";
187 }
188 sbmitterStack = reinterpret_cast<SubmitterStackFunc>(dlsym(handle, "DfxGetSubmitterStackLocal"));
189 if (sbmitterStack == nullptr) {
190 TAG_LOGE(AAFwkTag::JSENV, "dlsym libasync_stack failed, %{public}s", dlerror());
191 dlclose(handle);
192 return "";
193 }
194 const size_t bufferSize = 64 * 1024;
195 char stackTrace[bufferSize] = {0};
196 int result = sbmitterStack(stackTrace, bufferSize);
197 if (result == 0) {
198 dlclose(handle);
199 return stackTrace;
200 } else {
201 TAG_LOGE(AAFwkTag::JSENV, "submitterStack interface failed, result: %{public}d", result);
202 dlclose(handle);
203 return "";
204 }
205 }
206 } // namespace JsEnv
207 } // namespace OHOS
208