1 /*
2 * Copyright (c) 2022 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
16 #include "ecmascript/dfx/stackinfo/js_stackinfo.h"
17 #include "ecmascript/base/builtins_base.h"
18 #include "ecmascript/ecma_vm.h"
19 #include "ecmascript/interpreter/frame_handler.h"
20 #include "ecmascript/interpreter/interpreter.h"
21 #include "ecmascript/jspandafile/js_pandafile_manager.h"
22 #if defined(ENABLE_EXCEPTION_BACKTRACE)
23 #include "ecmascript/platform/backtrace.h"
24 #endif
25
26 namespace panda::ecmascript {
BuildMethodTrace(Method * method,uint32_t pcOffset)27 std::string JsStackInfo::BuildMethodTrace(Method *method, uint32_t pcOffset)
28 {
29 std::string data;
30 data.append(" at ");
31 std::string name = method->ParseFunctionName();
32 if (name.empty()) {
33 name = "anonymous";
34 }
35 data += name;
36 data.append(" (");
37 // source file
38 DebugInfoExtractor *debugExtractor =
39 JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
40 const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId());
41 if (sourceFile.empty()) {
42 data.push_back('?');
43 } else {
44 data += sourceFile;
45 }
46 data.push_back(':');
47 // line number and column number
48 auto callbackLineFunc = [&data](int32_t line) -> bool {
49 data += std::to_string(line + 1);
50 data.push_back(':');
51 return true;
52 };
53 auto callbackColumnFunc = [&data](int32_t column) -> bool {
54 data += std::to_string(column + 1);
55 return true;
56 };
57 panda_file::File::EntityId methodId = method->GetMethodId();
58 if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, pcOffset) ||
59 !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, pcOffset)) {
60 data.push_back('?');
61 }
62 data.push_back(')');
63 data.push_back('\n');
64 return data;
65 }
66
BuildJsStackTrace(JSThread * thread,bool needNative)67 std::string JsStackInfo::BuildJsStackTrace(JSThread *thread, bool needNative)
68 {
69 std::string data;
70 FrameHandler frameHandler(thread);
71 for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) {
72 if (!frameHandler.IsJSFrame()) {
73 continue;
74 }
75 auto method = frameHandler.CheckAndGetMethod();
76 if (method == nullptr) {
77 continue;
78 }
79 if (!method->IsNativeWithCallField()) {
80 auto pcOffset = frameHandler.GetBytecodeOffset();
81 data += BuildMethodTrace(method, pcOffset);
82 } else if (needNative) {
83 auto addr = method->GetNativePointer();
84 std::stringstream strm;
85 strm << addr;
86 data.append(" at native method (").append(strm.str()).append(")\n");
87 }
88 }
89 if (data.empty()) {
90 #if defined(ENABLE_EXCEPTION_BACKTRACE)
91 std::ostringstream stack;
92 Backtrace(stack);
93 data = stack.str();
94 #endif
95 }
96 return data;
97 }
98
BuildJsStackInfo(JSThread * thread)99 std::vector<struct JsFrameInfo> JsStackInfo::BuildJsStackInfo(JSThread *thread)
100 {
101 FrameHandler frameHandler(thread);
102 std::vector<struct JsFrameInfo> jsframe;
103 uintptr_t *native = nullptr;
104 for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) {
105 if (!frameHandler.IsJSFrame()) {
106 continue;
107 }
108 auto method = frameHandler.CheckAndGetMethod();
109 if (method == nullptr) {
110 continue;
111 }
112 struct JsFrameInfo frameInfo;
113 if (native != nullptr) {
114 frameInfo.nativePointer = native;
115 native = nullptr;
116 }
117 if (!method->IsNativeWithCallField()) {
118 std::string name = method->ParseFunctionName();
119 if (name.empty()) {
120 frameInfo.functionName = "anonymous";
121 } else {
122 frameInfo.functionName = name;
123 }
124 // source file
125 DebugInfoExtractor *debugExtractor =
126 JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
127 const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId());
128 if (sourceFile.empty()) {
129 frameInfo.fileName = "?";
130 } else {
131 frameInfo.fileName = sourceFile;
132 }
133 // line number and column number
134 int lineNumber = 0;
135 auto callbackLineFunc = [&frameInfo, &lineNumber](int32_t line) -> bool {
136 lineNumber = line + 1;
137 frameInfo.pos = std::to_string(lineNumber) + ":";
138 return true;
139 };
140 auto callbackColumnFunc = [&frameInfo](int32_t column) -> bool {
141 frameInfo.pos += std::to_string(column + 1);
142 return true;
143 };
144 panda_file::File::EntityId methodId = method->GetMethodId();
145 uint32_t offset = frameHandler.GetBytecodeOffset();
146 if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) ||
147 !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
148 frameInfo.pos = "?";
149 }
150 jsframe.push_back(frameInfo);
151 } else {
152 JSTaggedValue function = frameHandler.GetFunction();
153 JSHandle<JSTaggedValue> extraInfoValue(
154 thread, JSFunction::Cast(function.GetTaggedObject())->GetFunctionExtraInfo());
155 if (extraInfoValue->IsJSNativePointer()) {
156 JSHandle<JSNativePointer> extraInfo(extraInfoValue);
157 native = reinterpret_cast<uintptr_t *>(extraInfo->GetExternalPointer());
158 }
159 }
160 }
161 return jsframe;
162 }
163 } // namespace panda::ecmascript
164