• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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/debugger/js_debugger.h"
17 
18 #include "ecmascript/base/builtins_base.h"
19 #include "ecmascript/ecma_macros.h"
20 #include "ecmascript/global_env.h"
21 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
22 #include "ecmascript/interpreter/frame_handler.h"
23 #include "ecmascript/jspandafile/js_pandafile_manager.h"
24 
25 namespace panda::ecmascript::tooling {
26 using panda::ecmascript::base::BuiltinsBase;
27 
SetBreakpoint(const JSPtLocation & location,Local<FunctionRef> condFuncRef)28 bool JSDebugger::SetBreakpoint(const JSPtLocation &location, Local<FunctionRef> condFuncRef)
29 {
30     std::unique_ptr<PtMethod> ptMethod = FindMethod(location);
31     if (ptMethod == nullptr) {
32         LOG_DEBUGGER(ERROR) << "SetBreakpoint: Cannot find MethodLiteral";
33         return false;
34     }
35 
36     if (location.GetBytecodeOffset() >= ptMethod->GetCodeSize()) {
37         LOG_DEBUGGER(ERROR) << "SetBreakpoint: Invalid breakpoint location";
38         return false;
39     }
40 
41     auto [_, success] = breakpoints_.emplace(location.GetSourceFile(), ptMethod.release(),
42         location.GetBytecodeOffset(), Global<FunctionRef>(ecmaVm_, condFuncRef));
43     if (!success) {
44         // also return true
45         LOG_DEBUGGER(WARN) << "SetBreakpoint: Breakpoint already exists";
46     }
47 
48     DumpBreakpoints();
49     return true;
50 }
51 
RemoveBreakpoint(const JSPtLocation & location)52 bool JSDebugger::RemoveBreakpoint(const JSPtLocation &location)
53 {
54     std::unique_ptr<PtMethod> ptMethod = FindMethod(location);
55     if (ptMethod == nullptr) {
56         LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: Cannot find MethodLiteral";
57         return false;
58     }
59 
60     if (!RemoveBreakpoint(ptMethod, location.GetBytecodeOffset())) {
61         LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: Breakpoint not found";
62         return false;
63     }
64 
65     DumpBreakpoints();
66     return true;
67 }
68 
RemoveAllBreakpoints()69 void JSDebugger::RemoveAllBreakpoints()
70 {
71     breakpoints_.clear();
72 }
73 
BytecodePcChanged(JSThread * thread,JSHandle<Method> method,uint32_t bcOffset)74 void JSDebugger::BytecodePcChanged(JSThread *thread, JSHandle<Method> method, uint32_t bcOffset)
75 {
76     ASSERT(bcOffset < method->GetCodeSize() && "code size of current Method less then bcOffset");
77     HandleExceptionThrowEvent(thread, method, bcOffset);
78 
79     // Step event is reported before breakpoint, according to the spec.
80     if (!HandleStep(method, bcOffset)) {
81         HandleBreakpoint(method, bcOffset);
82     }
83 }
84 
HandleBreakpoint(JSHandle<Method> method,uint32_t bcOffset)85 bool JSDebugger::HandleBreakpoint(JSHandle<Method> method, uint32_t bcOffset)
86 {
87     auto breakpoint = FindBreakpoint(method, bcOffset);
88     if (hooks_ == nullptr || !breakpoint.has_value()) {
89         return false;
90     }
91 
92     JSThread *thread = ecmaVm_->GetJSThread();
93     auto condFuncRef = breakpoint.value().GetConditionFunction();
94     if (condFuncRef->IsFunction()) {
95         LOG_DEBUGGER(INFO) << "HandleBreakpoint: begin evaluate condition";
96         auto handlerPtr = std::make_shared<FrameHandler>(ecmaVm_->GetJSThread());
97         auto res = DebuggerApi::EvaluateViaFuncCall(const_cast<EcmaVM *>(ecmaVm_),
98             condFuncRef.ToLocal(ecmaVm_), handlerPtr);
99         if (thread->HasPendingException()) {
100             LOG_DEBUGGER(ERROR) << "HandleBreakpoint: has pending exception";
101             thread->ClearException();
102             return false;
103         }
104         bool isMeet = res->ToBoolean(ecmaVm_)->Value();
105         if (!isMeet) {
106             LOG_DEBUGGER(ERROR) << "HandleBreakpoint: condition not meet";
107             return false;
108         }
109     }
110 
111     JSPtLocation location {method->GetJSPandaFile(), method->GetMethodId(), bcOffset,
112         breakpoint.value().GetSourceFile()};
113 
114     hooks_->Breakpoint(location);
115     return true;
116 }
117 
HandleExceptionThrowEvent(const JSThread * thread,JSHandle<Method> method,uint32_t bcOffset)118 void JSDebugger::HandleExceptionThrowEvent(const JSThread *thread, JSHandle<Method> method, uint32_t bcOffset)
119 {
120     if (hooks_ == nullptr || !thread->HasPendingException()) {
121         return;
122     }
123 
124     JSPtLocation throwLocation {method->GetJSPandaFile(), method->GetMethodId(), bcOffset};
125 
126     hooks_->Exception(throwLocation);
127 }
128 
HandleStep(JSHandle<Method> method,uint32_t bcOffset)129 bool JSDebugger::HandleStep(JSHandle<Method> method, uint32_t bcOffset)
130 {
131     if (hooks_ == nullptr) {
132         return false;
133     }
134 
135     JSPtLocation location {method->GetJSPandaFile(), method->GetMethodId(), bcOffset};
136 
137     return hooks_->SingleStep(location);
138 }
139 
FindBreakpoint(JSHandle<Method> method,uint32_t bcOffset) const140 std::optional<JSBreakpoint> JSDebugger::FindBreakpoint(JSHandle<Method> method, uint32_t bcOffset) const
141 {
142     for (const auto &bp : breakpoints_) {
143         if ((bp.GetBytecodeOffset() == bcOffset) &&
144             (bp.GetPtMethod()->GetJSPandaFile() == method->GetJSPandaFile()) &&
145             (bp.GetPtMethod()->GetMethodId() == method->GetMethodId())) {
146             return bp;
147         }
148     }
149     return {};
150 }
151 
RemoveBreakpoint(const std::unique_ptr<PtMethod> & ptMethod,uint32_t bcOffset)152 bool JSDebugger::RemoveBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, uint32_t bcOffset)
153 {
154     for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) {
155         const auto &bp = *it;
156         if ((bp.GetBytecodeOffset() == bcOffset) &&
157             (bp.GetPtMethod()->GetJSPandaFile() == ptMethod->GetJSPandaFile()) &&
158             (bp.GetPtMethod()->GetMethodId() == ptMethod->GetMethodId())) {
159             it = breakpoints_.erase(it);
160             return true;
161         }
162     }
163 
164     return false;
165 }
166 
FindMethod(const JSPtLocation & location) const167 std::unique_ptr<PtMethod> JSDebugger::FindMethod(const JSPtLocation &location) const
168 {
169     std::unique_ptr<PtMethod> ptMethod {nullptr};
170     ::panda::ecmascript::JSPandaFileManager::GetInstance()->EnumerateJSPandaFiles([&ptMethod, location](
171         const panda::ecmascript::JSPandaFile *jsPandaFile) {
172         if (jsPandaFile->GetJSPandaFileDesc() == location.GetJsPandaFile()->GetJSPandaFileDesc()) {
173             MethodLiteral *methodsData = jsPandaFile->GetMethodLiterals();
174             uint32_t numberMethods = jsPandaFile->GetNumMethods();
175             for (uint32_t i = 0; i < numberMethods; ++i) {
176                 if (methodsData[i].GetMethodId() == location.GetMethodId()) {
177                     MethodLiteral *methodLiteral = methodsData + i;
178                     ptMethod = std::make_unique<PtMethod>(jsPandaFile,
179                         methodLiteral->GetMethodId(), methodLiteral->IsNativeWithCallField());
180                     return false;
181                 }
182             }
183         }
184         return true;
185     });
186     return ptMethod;
187 }
188 
DumpBreakpoints()189 void JSDebugger::DumpBreakpoints()
190 {
191     LOG_DEBUGGER(INFO) << "dump breakpoints with size " << breakpoints_.size();
192     for (const auto &bp : breakpoints_) {
193         LOG_DEBUGGER(DEBUG) << bp.ToString();
194     }
195 }
196 }  // namespace panda::tooling::ecmascript
197