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 "breakpoint_hooks.h"
17 #include "error.h"
18 #include "inspector.h"
19 #include "json_property.h"
20 #include "server.h"
21 #include "source_file.h"
22
23 #include "debug_info_extractor.h"
24 #include "utils/json_builder.h"
25 #include "utils/json_parser.h"
26 #include "utils/logger.h"
27 #include "utils/string_helpers.h"
28
29 #include <algorithm>
30 #include <cstddef>
31 #include <cstring>
32 #include <functional>
33 #include <list>
34 #include <regex>
35 #include <set>
36 #include <string>
37
38 using namespace std::placeholders; // NOLINT(google-build-using-namespace)
39 using panda::helpers::string::ParseInt;
40
41 namespace panda::tooling::inspector {
BreakpointHooks(Inspector & inspector)42 BreakpointHooks::BreakpointHooks(Inspector &inspector) : inspector_(inspector)
43 {
44 inspector.GetServer().OnCall("Debugger.getPossibleBreakpoints",
45 std::bind(&BreakpointHooks::GetPossibleBreakpoints, this, _1, _2));
46 inspector.GetServer().OnCall("Debugger.removeBreakpoint", std::bind(&BreakpointHooks::RemoveBreakpoint, this, _2));
47 inspector.GetServer().OnCall("Debugger.setBreakpoint",
48 std::bind(&BreakpointHooks::SetBreakpointAtLocation, this, _1, _2));
49 inspector.GetServer().OnCall("Debugger.setBreakpointByUrl",
50 std::bind(&BreakpointHooks::SetBreakpointByUrl, this, _1, _2));
51 inspector.GetServer().OnCall("Debugger.setBreakpointsActive",
52 std::bind(&BreakpointHooks::SetBreakpointsActive, this, _2));
53 }
54
Breakpoint(PtThread thread,Method *,const PtLocation &)55 void BreakpointHooks::Breakpoint(PtThread thread, Method * /* method */, const PtLocation & /* location */)
56 {
57 if (active_) {
58 inspector_.SetPause(thread, "Breakpoint");
59 }
60 }
61
GetPossibleBreakpoints(JsonObjectBuilder & result,const JsonObject & params)62 void BreakpointHooks::GetPossibleBreakpoints(JsonObjectBuilder &result, const JsonObject ¶ms)
63 {
64 const std::string *scriptIdString;
65 size_t startLine;
66 size_t endLine = ~0U;
67 bool restrictToFunction = false;
68
69 if (!GetPropertyOrLog<JsonObject::StringT>(scriptIdString, params, "start", "scriptId") ||
70 !GetPropertyOrLog<JsonObject::NumT>(startLine, params, "start", "lineNumber")) {
71 return;
72 }
73
74 int scriptId;
75 if (!ParseInt(*scriptIdString, &scriptId, 0)) {
76 LOG(INFO, DEBUGGER) << "Invalid script id: " << *scriptIdString;
77 return;
78 }
79
80 GetProperty<JsonObject::NumT>(endLine, params, "end", "lineNumber");
81 GetProperty<JsonObject::BoolT>(restrictToFunction, params, "restrictToFunction");
82
83 const panda_file::DebugInfoExtractor *debugInfo;
84
85 if (auto sourceFile = inspector_.GetSourceManager().GetSourceFile(scriptId)) {
86 debugInfo = &sourceFile->GetDebugInfo();
87 } else {
88 return;
89 }
90
91 std::set<size_t> lineNumbers;
92
93 for (auto methodId : debugInfo->GetMethodIdList()) {
94 auto &table = debugInfo->GetLineNumberTable(methodId);
95
96 if (restrictToFunction &&
97 (table.empty() || startLine < table.front().line - 1 || table.back().line - 1 < startLine)) {
98 continue;
99 }
100
101 for (auto &entry : table) {
102 size_t lineNumber = entry.line - 1;
103
104 if (startLine <= lineNumber && lineNumber < endLine) {
105 lineNumbers.insert(lineNumber);
106 }
107 }
108 }
109
110 result.AddProperty("array", [&](JsonArrayBuilder &array) {
111 for (auto lineNumber : lineNumbers) {
112 array.Add([&](JsonObjectBuilder &breakLocation) {
113 breakLocation.AddProperty("scriptId", std::to_string(scriptId));
114 breakLocation.AddProperty("lineNumber", lineNumber);
115 });
116 }
117 });
118 }
119
RemoveBreakpoint(const JsonObject & params)120 void BreakpointHooks::RemoveBreakpoint(const JsonObject ¶ms)
121 {
122 const std::string *breakpointIdString;
123 if (!GetPropertyOrLog<JsonObject::StringT>(breakpointIdString, params, "breakpointId")) {
124 return;
125 }
126
127 int breakpointId;
128 if (!ParseInt(*breakpointIdString, &breakpointId, 0)) {
129 LOG(INFO, DEBUGGER) << "Invalid breakpoint id: " << *breakpointIdString;
130 return;
131 }
132
133 auto [begin, end] = locations_.equal_range(breakpointId);
134 std::for_each(begin, end,
135 [&](auto &entry) { HandleError(inspector_.GetDebugger().RemoveBreakpoint(entry.second)); });
136 locations_.erase(begin, end);
137 }
138
SetBreakpoint(size_t breakpointId,const SourceFile & sourceFile,size_t lineNumber)139 bool BreakpointHooks::SetBreakpoint(size_t breakpointId, const SourceFile &sourceFile, size_t lineNumber)
140 {
141 bool breakpointSet = false;
142
143 sourceFile.EnumerateLocations(lineNumber, [&](auto location) {
144 if (!HandleError(inspector_.GetDebugger().SetBreakpoint(location))) {
145 return true;
146 }
147
148 locations_.emplace(breakpointId, location);
149 breakpointSet = true;
150 return false;
151 });
152
153 return breakpointSet;
154 }
155
SetBreakpointAtLocation(JsonObjectBuilder & result,const JsonObject & params)156 void BreakpointHooks::SetBreakpointAtLocation(JsonObjectBuilder &result, const JsonObject ¶ms)
157 {
158 const std::string *scriptIdString;
159 if (!GetPropertyOrLog<JsonObject::StringT>(scriptIdString, params, "location", "scriptId")) {
160 return;
161 }
162
163 int scriptId;
164 if (!ParseInt(*scriptIdString, &scriptId, 0)) {
165 LOG(INFO, DEBUGGER) << "Invalid script id: " << *scriptIdString;
166 return;
167 }
168
169 size_t lineNumber;
170 if (!GetPropertyOrLog<JsonObject::NumT>(lineNumber, params, "location", "lineNumber")) {
171 return;
172 }
173
174 auto sourceFile = inspector_.GetSourceManager().GetSourceFile(scriptId);
175 if (sourceFile == nullptr) {
176 return;
177 }
178
179 auto breakpointId = GenerateBreakpointId();
180 SetBreakpoint(breakpointId, *sourceFile, lineNumber);
181
182 result.AddProperty("breakpointId", std::to_string(breakpointId));
183 result.AddProperty("actualLocation", [&](JsonObjectBuilder &location) {
184 location.AddProperty("scriptId", std::to_string(scriptId));
185 location.AddProperty("lineNumber", lineNumber);
186 });
187 }
188
SetBreakpointByUrl(JsonObjectBuilder & result,const JsonObject & params)189 void BreakpointHooks::SetBreakpointByUrl(JsonObjectBuilder &result, const JsonObject ¶ms)
190 {
191 size_t lineNumber;
192 if (!GetPropertyOrLog<JsonObject::NumT>(lineNumber, params, "lineNumber")) {
193 return;
194 }
195
196 const std::string *url;
197 std::list<const SourceFile *> sourceFiles;
198
199 if (GetPropertyOrLog<JsonObject::StringT>(url, params, "url")) {
200 if (url->find("file:///") != 0) {
201 LOG(INFO, DEBUGGER) << "URL not supported: " << *url;
202 return;
203 }
204
205 if (auto sourceFile = inspector_.GetSourceManager().GetSourceFile(url->substr(std::strlen("file:///")))) {
206 sourceFiles.push_back(sourceFile);
207 } else {
208 return;
209 }
210 } else if (GetPropertyOrLog<JsonObject::StringT>(url, params, "urlRegex")) {
211 std::regex regex(*url);
212
213 inspector_.GetSourceManager().EnumerateSourceFiles([&](auto &sourceFile) {
214 if (std::regex_match(sourceFile.GetFileName(), regex)) {
215 sourceFiles.push_back(&sourceFile);
216 }
217 return true;
218 });
219 } else {
220 return;
221 }
222
223 auto breakpointId = GenerateBreakpointId();
224 result.AddProperty("breakpointId", std::to_string(breakpointId));
225
226 for (auto sourceFile = sourceFiles.begin(); sourceFile != sourceFiles.end();) {
227 if (SetBreakpoint(breakpointId, **sourceFile, lineNumber)) {
228 ++sourceFile;
229 } else {
230 sourceFile = sourceFiles.erase(sourceFile);
231 }
232 }
233
234 result.AddProperty("locations", [&](JsonArrayBuilder &locations) {
235 for (auto sourceFile : sourceFiles) {
236 locations.Add([&](JsonObjectBuilder &location) {
237 location.AddProperty("scriptId", std::to_string(sourceFile->GetScriptId()));
238 location.AddProperty("lineNumber", lineNumber);
239 });
240 }
241 });
242 }
243
SetBreakpointsActive(const JsonObject & params)244 void BreakpointHooks::SetBreakpointsActive(const JsonObject ¶ms)
245 {
246 GetPropertyOrLog<JsonObject::BoolT>(active_, params, "active");
247 }
248 } // namespace panda::tooling::inspector
249