• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "step_hooks.h"
17 #include "inspector.h"
18 #include "error.h"
19 #include "json_property.h"
20 #include "server.h"
21 
22 #include "bytecode_instruction-inl.h"
23 #include "optimizer/ir_builder/inst_builder.h"
24 #include "tooling/debug_interface.h"
25 #include "tooling/pt_location.h"
26 #include "utils/json_parser.h"
27 #include "utils/logger.h"
28 #include "utils/string_helpers.h"
29 
30 #include <algorithm>
31 #include <cstddef>
32 #include <cstdint>
33 #include <functional>
34 #include <string>
35 #include <utility>
36 
37 using namespace std::placeholders;  // NOLINT(google-build-using-namespace)
38 using panda::helpers::string::ParseInt;
39 
40 namespace panda::tooling::inspector {
StepHooks(Inspector & inspector)41 StepHooks::StepHooks(Inspector &inspector) : inspector_(inspector)
42 {
43     inspector.GetServer().OnCall("Debugger.continueToLocation", std::bind(&StepHooks::ContinueToLocation, this, _2));
44     inspector.GetServer().OnCall("Debugger.stepInto", std::bind(&StepHooks::SetStep, this, STEP_INTO));
45     inspector.GetServer().OnCall("Debugger.stepOut", std::bind(&StepHooks::SetStep, this, STEP_OUT));
46     inspector.GetServer().OnCall("Debugger.stepOver", std::bind(&StepHooks::SetStep, this, STEP_OVER));
47     inspector.GetServer().OnCall("Runtime.runIfWaitingForDebugger", std::bind(&StepHooks::SetBreakOnStart, this));
48 
49     inspector.OnPause(std::bind(&StepHooks::OnPause, this));
50 }
51 
AddLocation(const PtLocation & location)52 void StepHooks::AddLocation(const PtLocation &location)
53 {
54     if (state_->locations.count(location) != 0) {
55         return;
56     }
57 
58     if (auto error = inspector_.GetDebugger().SetBreakpoint(location)) {
59         if (error->GetType() == Error::Type::BREAKPOINT_ALREADY_EXISTS) {
60             state_->locations[location] = true;
61         } else {
62             HandleError(std::move(error));
63         }
64     } else {
65         state_->locations[location] = false;
66     }
67 }
68 
Breakpoint(PtThread thread,Method *,const PtLocation & location)69 void StepHooks::Breakpoint(PtThread thread, Method * /* method */, const PtLocation &location)
70 {
71     if (state_ && state_->thread == thread && state_->locations.count(location) != 0) {
72         inspector_.SetPause(thread, "Step");
73     }
74 }
75 
ContinueToLocation(const JsonObject & params)76 void StepHooks::ContinueToLocation(const JsonObject &params)
77 {
78     const std::string *scriptIdString;
79     if (!GetPropertyOrLog<JsonObject::StringT>(scriptIdString, params, "location", "scriptId")) {
80         return;
81     }
82 
83     int scriptId;
84     if (!ParseInt(*scriptIdString, &scriptId, 0)) {
85         LOG(INFO, DEBUGGER) << "Invalid script id: " << *scriptIdString;
86         return;
87     }
88 
89     size_t lineNumber;
90     if (!GetPropertyOrLog<JsonObject::NumT>(lineNumber, params, "location", "lineNumber")) {
91         return;
92     }
93 
94     auto sourceFile = inspector_.GetSourceManager().GetSourceFile(scriptId);
95     if (sourceFile == nullptr) {
96         return;
97     }
98 
99     state_ = State {};
100     state_->stepKind = CONTINUE_TO;
101     state_->thread = inspector_.Resume();
102 
103     sourceFile->EnumerateLocations(lineNumber, [&](auto location) {
104         AddLocation(location);
105         return true;
106     });
107 }
108 
FramePop(PtThread thread,Method *,bool)109 void StepHooks::FramePop(PtThread thread, Method * /* method */, bool /* wasPoppedByException */)
110 {
111     if (state_ && state_->thread == thread) {
112         state_->pauseOnStep = true;
113     }
114 }
115 
MethodEntry(PtThread thread,Method *)116 void StepHooks::MethodEntry(PtThread thread, Method * /* method */)
117 {
118     if (!state_ || state_->thread != thread || std::exchange(state_->methodEntered, true)) {
119         return;
120     }
121 
122     switch (state_->stepKind) {
123         case BREAK_ON_START:
124         case CONTINUE_TO:
125         case STEP_OUT:
126             break;
127 
128         case STEP_INTO:
129             state_->pauseOnStep = true;
130             break;
131 
132         case STEP_OVER:
133             HandleError(inspector_.GetDebugger().NotifyFramePop(thread, 0));
134             break;
135     }
136 }
137 
OnPause()138 void StepHooks::OnPause()
139 {
140     if (!state_) {
141         return;
142     }
143 
144     for (auto &[location, existingBreakpoint] : state_->locations) {
145         if (!existingBreakpoint) {
146             HandleError(inspector_.GetDebugger().RemoveBreakpoint(location));
147         }
148     }
149 
150     state_.reset();
151 }
152 
SetBreakOnStart()153 void StepHooks::SetBreakOnStart()
154 {
155     inspector_.Resume();
156 
157     state_ = State {};
158     state_->stepKind = BREAK_ON_START;
159 }
160 
SetStep(StepKind stepKind)161 void StepHooks::SetStep(StepKind stepKind)
162 {
163     auto thread = inspector_.Resume();
164 
165     state_ = State {};
166     state_->stepKind = stepKind;
167     state_->thread = thread;
168 
169     if (stepKind == STEP_OUT) {
170         HandleError(inspector_.GetDebugger().NotifyFramePop(thread, 0));
171         return;
172     }
173 
174     auto frame = inspector_.GetDebugger().GetCurrentFrame(thread);
175     if (!frame) {
176         HandleError(frame.Error());
177         return;
178     }
179 
180     auto method = frame.Value()->GetMethod();
181     uint32_t bytecodeOffset = frame.Value()->GetBytecodeOffset();
182 
183     auto &table =
184         inspector_.GetSourceManager().GetDebugInfo(method->GetPandaFile()).GetLineNumberTable(method->GetFileId());
185     auto nextLineIter = std::upper_bound(table.begin(), table.end(), bytecodeOffset,
186                                          [](auto offset, auto &entry) { return offset < entry.offset; });
187     uint32_t nextLineBytecodeOffset = nextLineIter != table.end() ? nextLineIter->offset : ~0U;
188 
189     while (bytecodeOffset < std::min(nextLineBytecodeOffset, method->GetCodeSize())) {
190         BytecodeInstruction inst(method->GetInstructions() + bytecodeOffset);
191 
192         if (inst.HasFlag(BytecodeInstruction::Flags::JUMP)) {
193             AddLocation(PtLocation(method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(),
194                                    bytecodeOffset + compiler::InstBuilder::GetInstructionJumpOffset(&inst)));
195 
196             if (!inst.HasFlag(BytecodeInstruction::Flags::CONDITIONAL)) {
197                 break;
198             }
199         }
200 
201         bytecodeOffset += inst.GetSize();
202     }
203 
204     if (bytecodeOffset == nextLineBytecodeOffset) {
205         AddLocation(
206             PtLocation(method->GetPandaFile()->GetFilename().c_str(), method->GetFileId(), nextLineBytecodeOffset));
207     } else {
208         HandleError(inspector_.GetDebugger().NotifyFramePop(thread, 0));
209     }
210 }
211 
SingleStep(PtThread thread,Method *,const PtLocation &)212 void StepHooks::SingleStep(PtThread thread, Method * /* method */, const PtLocation & /* location */)
213 {
214     if (!state_) {
215         return;
216     }
217 
218     if (state_->stepKind == BREAK_ON_START) {
219         inspector_.SetPause(thread, "Break on start");
220     } else if (state_->thread == thread && state_->pauseOnStep) {
221         inspector_.SetPause(thread, "Step");
222     }
223 }
224 
VmInitialization(PtThread thread)225 void StepHooks::VmInitialization(PtThread thread)
226 {
227     // Wait for a debugger to attach.
228     inspector_.SetPause(thread, {});
229 }
230 }  // namespace panda::tooling::inspector
231