• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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