• 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 "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 &params)
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 &params)
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 &params)
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 &params)
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 &params)
245 {
246     GetPropertyOrLog<JsonObject::BoolT>(active_, params, "active");
247 }
248 }  // namespace panda::tooling::inspector
249