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 "inspector_server.h"
17
18 #include "server.h"
19 #include "types/location.h"
20 #include "types/numeric_id.h"
21
22 #include "console_call_type.h"
23 #include "macros.h"
24 #include "tooling/pt_thread.h"
25 #include "utils/json_builder.h"
26 #include "utils/json_parser.h"
27 #include "utils/logger.h"
28
29 #include <functional>
30 #include <regex>
31 #include <string>
32 #include <utility>
33
34 namespace panda::tooling::inspector {
InspectorServer(Server & server)35 InspectorServer::InspectorServer(Server &server) : server_(server)
36 {
37 server_.OnCall("Debugger.enable", [](auto, auto &result, auto &) { result.AddProperty("debuggerId", "debugger"); });
38 }
39
Kill()40 void InspectorServer::Kill()
41 {
42 server_.Kill();
43 }
44
Run()45 void InspectorServer::Run()
46 {
47 server_.Run();
48 }
49
OnValidate(std::function<void ()> && handler)50 void InspectorServer::OnValidate(std::function<void()> &&handler)
51 {
52 server_.OnValidate([handler = std::move(handler)]() {
53 // Pause debugger events processing
54 handler();
55
56 // At this point, the whole messaging is stopped due to:
57 // - Debugger events are not processed by the inspector after the call above;
58 // - Client messages are not processed as we are executing on the server thread.
59 });
60 }
61
OnOpen(std::function<void ()> && handler)62 void InspectorServer::OnOpen(std::function<void()> &&handler)
63 {
64 server_.OnOpen([this, handler = std::move(handler)]() {
65 // A new connection is open, reinitialize the state
66 sessionManager_.EnumerateSessions([this](auto &id, auto thread) {
67 sourceManager_.RemoveThread(thread);
68 if (!id.empty()) {
69 SendTargetAttachedToTarget(id);
70 }
71 });
72
73 // Resume debugger events processing
74 handler();
75 });
76 }
77
OnFail(std::function<void ()> && handler)78 void InspectorServer::OnFail(std::function<void()> &&handler)
79 {
80 server_.OnFail([handler = std::move(handler)]() {
81 // Resume debugger events processing
82 handler();
83 });
84 }
85
CallDebuggerPaused(PtThread thread,const std::vector<BreakpointId> & hitBreakpoints,const std::optional<RemoteObject> & exception,const std::function<void (const FrameInfoHandler &)> & enumerateFrames)86 void InspectorServer::CallDebuggerPaused(PtThread thread, const std::vector<BreakpointId> &hitBreakpoints,
87 const std::optional<RemoteObject> &exception,
88 const std::function<void(const FrameInfoHandler &)> &enumerateFrames)
89 {
90 auto sessionId = sessionManager_.GetSessionIdByThread(thread);
91
92 server_.Call(sessionId, "Debugger.paused", [&](auto ¶ms) {
93 params.AddProperty("callFrames", [this, thread, &enumerateFrames](JsonArrayBuilder &callFrames) {
94 EnumerateCallFrames(callFrames, thread, enumerateFrames);
95 });
96
97 params.AddProperty("hitBreakpoints", [&hitBreakpoints](JsonArrayBuilder &hitBreakpointsBuilder) {
98 AddHitBreakpoints(hitBreakpointsBuilder, hitBreakpoints);
99 });
100
101 if (exception) {
102 params.AddProperty("data", exception->ToJson());
103 }
104
105 params.AddProperty("reason", exception.has_value() ? "exception" : "other");
106 });
107 }
108
CallDebuggerResumed(PtThread thread)109 void InspectorServer::CallDebuggerResumed(PtThread thread)
110 {
111 server_.Call(sessionManager_.GetSessionIdByThread(thread), "Debugger.resumed");
112 }
113
CallDebuggerScriptParsed(PtThread thread,ScriptId scriptId,std::string_view sourceFile)114 void InspectorServer::CallDebuggerScriptParsed(PtThread thread, ScriptId scriptId, std::string_view sourceFile)
115 {
116 auto sessionId = sessionManager_.GetSessionIdByThread(thread);
117 server_.Call(sessionId, "Debugger.scriptParsed", [&sourceFile, &thread, &scriptId](auto ¶ms) {
118 params.AddProperty("executionContextId", thread.GetId());
119 params.AddProperty("scriptId", std::to_string(scriptId));
120 params.AddProperty("url", sourceFile.data());
121 params.AddProperty("startLine", 0);
122 params.AddProperty("startColumn", 0);
123 params.AddProperty("endLine", std::numeric_limits<int>::max());
124 params.AddProperty("endColumn", std::numeric_limits<int>::max());
125 params.AddProperty("hash", "");
126 });
127 }
128
CallRuntimeConsoleApiCalled(PtThread thread,ConsoleCallType type,uint64_t timestamp,const std::vector<RemoteObject> & arguments)129 void InspectorServer::CallRuntimeConsoleApiCalled(PtThread thread, ConsoleCallType type, uint64_t timestamp,
130 const std::vector<RemoteObject> &arguments)
131 {
132 auto sessionId = sessionManager_.GetSessionIdByThread(thread);
133
134 server_.Call(sessionId, "Runtime.consoleAPICalled", [&](auto ¶ms) {
135 params.AddProperty("executionContextId", thread.GetId());
136 params.AddProperty("timestamp", timestamp);
137
138 switch (type) {
139 case ConsoleCallType::LOG:
140 params.AddProperty("type", "log");
141 break;
142 case ConsoleCallType::DEBUG:
143 params.AddProperty("type", "debug");
144 break;
145 case ConsoleCallType::INFO:
146 params.AddProperty("type", "info");
147 break;
148 case ConsoleCallType::ERROR:
149 params.AddProperty("type", "error");
150 break;
151 case ConsoleCallType::WARNING:
152 params.AddProperty("type", "warning");
153 break;
154 default:
155 UNREACHABLE();
156 }
157
158 params.AddProperty("args", [&](JsonArrayBuilder &argsBuilder) {
159 for (const auto &argument : arguments) {
160 argsBuilder.Add(argument.ToJson());
161 }
162 });
163 });
164 }
165
CallRuntimeExecutionContextCreated(PtThread thread)166 void InspectorServer::CallRuntimeExecutionContextCreated(PtThread thread)
167 {
168 auto sessionId = sessionManager_.GetSessionIdByThread(thread);
169
170 std::string name;
171 if (thread != PtThread::NONE) {
172 name = "Thread #" + std::to_string(thread.GetId());
173 }
174
175 server_.Call(sessionId, "Runtime.executionContextCreated", [&](auto ¶ms) {
176 params.AddProperty("context", [&](JsonObjectBuilder &context) {
177 context.AddProperty("id", thread.GetId());
178 context.AddProperty("origin", "");
179 context.AddProperty("name", name);
180 });
181 });
182 }
183
CallTargetAttachedToTarget(PtThread thread)184 void InspectorServer::CallTargetAttachedToTarget(PtThread thread)
185 {
186 auto &sessionId = sessionManager_.AddSession(thread);
187 if (!sessionId.empty()) {
188 SendTargetAttachedToTarget(sessionId);
189 }
190 }
191
CallTargetDetachedFromTarget(PtThread thread)192 void InspectorServer::CallTargetDetachedFromTarget(PtThread thread)
193 {
194 auto sessionId = sessionManager_.GetSessionIdByThread(thread);
195
196 // Pause the server thread to ensure that there will be no dangling PtThreads
197 server_.Pause();
198
199 sessionManager_.RemoveSession(sessionId);
200 sourceManager_.RemoveThread(thread);
201
202 // Now no one will retrieve the detached thread from the sessions manager
203 server_.Continue();
204
205 if (!sessionId.empty()) {
206 server_.Call("Target.detachedFromTarget",
207 [&sessionId](auto ¶ms) { params.AddProperty("session_id", sessionId); });
208 }
209 }
210
OnCallDebuggerContinueToLocation(std::function<void (PtThread,std::string_view,size_t)> && handler)211 void InspectorServer::OnCallDebuggerContinueToLocation(
212 std::function<void(PtThread, std::string_view, size_t)> &&handler)
213 {
214 server_.OnCall(
215 "Debugger.continueToLocation", [this, handler = std::move(handler)](auto &sessionId, auto &, auto ¶ms) {
216 auto location = Location::FromJsonProperty(params, "location");
217 if (!location) {
218 LOG(INFO, DEBUGGER) << location.Error();
219 return;
220 }
221
222 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
223
224 handler(thread, sourceManager_.GetSourceFileName(location->GetScriptId()), location->GetLineNumber());
225 });
226 }
227
OnCallDebuggerGetPossibleBreakpoints(std::function<std::set<size_t> (std::string_view,size_t,size_t,bool)> && handler)228 void InspectorServer::OnCallDebuggerGetPossibleBreakpoints(
229 std::function<std::set<size_t>(std::string_view, size_t, size_t, bool)> &&handler)
230 {
231 // clang-format off
232 server_.OnCall("Debugger.getPossibleBreakpoints",
233 [this, handler = std::move(handler)](auto &, auto &result, const JsonObject ¶ms) {
234 auto start = Location::FromJsonProperty(params, "start");
235 if (!start) {
236 LOG(INFO, DEBUGGER) << start.Error();
237 return;
238 }
239
240 auto scriptId = start->GetScriptId();
241
242 size_t endLine = ~0U;
243 if (auto end = Location::FromJsonProperty(params, "end")) {
244 if (end->GetScriptId() != scriptId) {
245 LOG(INFO, DEBUGGER) << "Script ids don't match";
246 return;
247 }
248
249 endLine = end->GetLineNumber();
250 }
251
252 bool restrictToFunction = false;
253 if (auto prop = params.GetValue<JsonObject::BoolT>("restrictToFunction")) {
254 restrictToFunction = *prop;
255 }
256
257 auto lineNumbers = handler(sourceManager_.GetSourceFileName(scriptId), start->GetLineNumber(),
258 endLine, restrictToFunction);
259
260 result.AddProperty("locations", [scriptId, &lineNumbers](JsonArrayBuilder &array) {
261 for (auto lineNumber : lineNumbers) {
262 array.Add(Location(scriptId, lineNumber).ToJson());
263 }
264 });
265 });
266 // clang-format on
267 }
268
OnCallDebuggerGetScriptSource(std::function<std::string (std::string_view)> && handler)269 void InspectorServer::OnCallDebuggerGetScriptSource(std::function<std::string(std::string_view)> &&handler)
270 {
271 server_.OnCall("Debugger.getScriptSource",
272 [this, handler = std::move(handler)](auto &, auto &result, auto ¶ms) {
273 if (auto scriptId = ParseNumericId<ScriptId>(params, "scriptId")) {
274 auto sourceFile = sourceManager_.GetSourceFileName(*scriptId);
275 result.AddProperty("scriptSource", handler(sourceFile));
276 } else {
277 LOG(INFO, DEBUGGER) << scriptId.Error();
278 }
279 });
280 }
281
OnCallDebuggerPause(std::function<void (PtThread)> && handler)282 void InspectorServer::OnCallDebuggerPause(std::function<void(PtThread)> &&handler)
283 {
284 server_.OnCall("Debugger.pause", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
285 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
286 handler(thread);
287 });
288 }
289
OnCallDebuggerRemoveBreakpoint(std::function<void (PtThread,BreakpointId)> && handler)290 void InspectorServer::OnCallDebuggerRemoveBreakpoint(std::function<void(PtThread, BreakpointId)> &&handler)
291 {
292 server_.OnCall("Debugger.removeBreakpoint",
293 [this, handler = std::move(handler)](auto &sessionId, auto &, auto ¶ms) {
294 if (auto breakpointId = ParseNumericId<BreakpointId>(params, "breakpointId")) {
295 handler(sessionManager_.GetThreadBySessionId(sessionId), *breakpointId);
296 } else {
297 LOG(INFO, DEBUGGER) << breakpointId.Error();
298 }
299 });
300 }
301
OnCallDebuggerRestartFrame(std::function<void (PtThread,FrameId)> && handler)302 void InspectorServer::OnCallDebuggerRestartFrame(std::function<void(PtThread, FrameId)> &&handler)
303 {
304 // clang-format off
305 server_.OnCall("Debugger.restartFrame",
306 [this, handler = std::move(handler)](auto &sessionId, auto &result, auto ¶ms) {
307 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
308
309 auto frameId = ParseNumericId<FrameId>(params, "callFrameId");
310 if (!frameId) {
311 LOG(INFO, DEBUGGER) << frameId.Error();
312 return;
313 }
314
315 handler(thread, *frameId);
316
317 result.AddProperty("callFrames", [](JsonArrayBuilder &) {});
318 });
319 // clang-format on
320 }
321
OnCallDebuggerResume(std::function<void (PtThread)> && handler)322 void InspectorServer::OnCallDebuggerResume(std::function<void(PtThread)> &&handler)
323 {
324 server_.OnCall("Debugger.resume", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
325 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
326 handler(thread);
327 });
328 }
329
OnCallDebuggerSetBreakpoint(std::function<std::optional<BreakpointId> (PtThread,const std::function<bool (std::string_view)> &,size_t,std::set<std::string_view> &)> && handler)330 void InspectorServer::OnCallDebuggerSetBreakpoint(
331 std::function<std::optional<BreakpointId>(PtThread, const std::function<bool(std::string_view)> &, size_t,
332 std::set<std::string_view> &)> &&handler)
333 {
334 // clang-format off
335 server_.OnCall("Debugger.setBreakpoint",
336 [this, handler = std::move(handler)](auto &sessionId, auto &result, auto ¶ms) {
337 auto location = Location::FromJsonProperty(params, "location");
338 if (!location) {
339 LOG(INFO, DEBUGGER) << location.Error();
340 return;
341 }
342
343 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
344
345 auto sourceFile = sourceManager_.GetSourceFileName(location->GetScriptId());
346 std::set<std::string_view> sourceFiles;
347
348 auto id = handler(
349 thread, [sourceFile](auto fileName) { return fileName == sourceFile; },
350 location->GetLineNumber(), sourceFiles);
351 if (!id) {
352 LOG(INFO, DEBUGGER) << "Failed to set breakpoint";
353 return;
354 }
355
356 result.AddProperty("breakpointId", std::to_string(*id));
357 result.AddProperty("actualLocation", location->ToJson());
358 });
359 // clang-format on
360 }
361
OnCallDebuggerSetBreakpointByUrl(std::function<std::optional<BreakpointId> (PtThread,const std::function<bool (std::string_view)> &,size_t,std::set<std::string_view> &)> && handler)362 void InspectorServer::OnCallDebuggerSetBreakpointByUrl(
363 std::function<std::optional<BreakpointId>(PtThread, const std::function<bool(std::string_view)> &, size_t,
364 std::set<std::string_view> &)> &&handler)
365 {
366 server_.OnCall("Debugger.setBreakpointByUrl", [this, handler = std::move(handler)](auto &sessionId, auto &result,
367 const JsonObject ¶ms) {
368 size_t lineNumber;
369 if (auto prop = params.GetValue<JsonObject::NumT>("lineNumber")) {
370 lineNumber = *prop + 1;
371 } else {
372 LOG(INFO, DEBUGGER) << "No 'lineNumber' property";
373 return;
374 }
375
376 std::function<bool(std::string_view)> sourceFileFilter;
377 if (auto url = params.GetValue<JsonObject::StringT>("url")) {
378 sourceFileFilter = [sourceFile = url->find("file://") == 0 ? url->substr(std::strlen("file://")) : *url](
379 auto fileName) { return fileName == sourceFile; };
380 } else if (auto urlRegex = params.GetValue<JsonObject::StringT>("urlRegex")) {
381 sourceFileFilter = [regex = std::regex(*urlRegex)](auto fileName) {
382 return std::regex_match(fileName.data(), regex);
383 };
384 } else {
385 LOG(INFO, DEBUGGER) << "No 'url' or 'urlRegex' properties";
386 return;
387 }
388
389 std::set<std::string_view> sourceFiles;
390 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
391
392 auto id = handler(thread, sourceFileFilter, lineNumber, sourceFiles);
393 if (!id) {
394 LOG(INFO, DEBUGGER) << "Failed to set breakpoint";
395 return;
396 }
397
398 result.AddProperty("breakpointId", std::to_string(*id));
399 result.AddProperty("locations", [this, lineNumber, &sourceFiles, thread](JsonArrayBuilder &locations) {
400 AddBreakpointByUrlLocations(locations, sourceFiles, lineNumber, thread);
401 });
402 });
403 }
404
OnCallDebuggerSetBreakpointsActive(std::function<void (PtThread,bool)> && handler)405 void InspectorServer::OnCallDebuggerSetBreakpointsActive(std::function<void(PtThread, bool)> &&handler)
406 {
407 // clang-format off
408 server_.OnCall("Debugger.setBreakpointsActive",
409 [this, handler = std::move(handler)](auto &sessionId, auto &, const JsonObject ¶ms) {
410 bool active;
411 if (auto prop = params.GetValue<JsonObject::BoolT>("active")) {
412 active = *prop;
413 } else {
414 LOG(INFO, DEBUGGER) << "No 'active' property";
415 return;
416 }
417
418 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
419 handler(thread, active);
420 });
421 // clang-format on
422 }
423
OnCallDebuggerSetPauseOnExceptions(std::function<void (PtThread,PauseOnExceptionsState)> && handler)424 void InspectorServer::OnCallDebuggerSetPauseOnExceptions(
425 std::function<void(PtThread, PauseOnExceptionsState)> &&handler)
426 {
427 // clang-format off
428 server_.OnCall("Debugger.setPauseOnExceptions",
429 [this, handler = std::move(handler)](auto &sessionId, auto &, const JsonObject ¶ms) {
430 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
431
432 PauseOnExceptionsState state;
433 auto stateStr = params.GetValue<JsonObject::StringT>("state");
434 if (stateStr == nullptr) {
435 LOG(INFO, DEBUGGER) << "No 'state' property";
436 return;
437 }
438
439 if (*stateStr == "none") {
440 state = PauseOnExceptionsState::NONE;
441 } else if (*stateStr == "caught") {
442 state = PauseOnExceptionsState::CAUGHT;
443 } else if (*stateStr == "uncaught") {
444 state = PauseOnExceptionsState::UNCAUGHT;
445 } else if (*stateStr == "all") {
446 state = PauseOnExceptionsState::ALL;
447 } else {
448 LOG(INFO, DEBUGGER) << "Invalid 'state' value: " << *stateStr;
449 return;
450 }
451
452 handler(thread, state);
453 });
454 // clang-format on
455 }
456
OnCallDebuggerStepInto(std::function<void (PtThread)> && handler)457 void InspectorServer::OnCallDebuggerStepInto(std::function<void(PtThread)> &&handler)
458 {
459 server_.OnCall("Debugger.stepInto", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
460 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
461 handler(thread);
462 });
463 }
464
OnCallDebuggerStepOut(std::function<void (PtThread)> && handler)465 void InspectorServer::OnCallDebuggerStepOut(std::function<void(PtThread)> &&handler)
466 {
467 server_.OnCall("Debugger.stepOut", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
468 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
469 handler(thread);
470 });
471 }
472
OnCallDebuggerStepOver(std::function<void (PtThread)> && handler)473 void InspectorServer::OnCallDebuggerStepOver(std::function<void(PtThread)> &&handler)
474 {
475 server_.OnCall("Debugger.stepOver", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
476 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
477 handler(thread);
478 });
479 }
480
OnCallRuntimeEnable(std::function<void (PtThread)> && handler)481 void InspectorServer::OnCallRuntimeEnable(std::function<void(PtThread)> &&handler)
482 {
483 server_.OnCall("Runtime.enable", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
484 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
485 handler(thread);
486 });
487 }
488
OnCallRuntimeGetProperties(std::function<std::vector<PropertyDescriptor> (PtThread,RemoteObjectId,bool)> && handler)489 void InspectorServer::OnCallRuntimeGetProperties(
490 std::function<std::vector<PropertyDescriptor>(PtThread, RemoteObjectId, bool)> &&handler)
491 {
492 // clang-format off
493 server_.OnCall("Runtime.getProperties",
494 [this, handler = std::move(handler)](auto &sessionId, auto &result, const JsonObject ¶ms) {
495 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
496
497 auto objectId = ParseNumericId<RemoteObjectId>(params, "objectId");
498 if (!objectId) {
499 LOG(INFO, DEBUGGER) << objectId.Error();
500 return;
501 }
502
503 auto generatePreview = false;
504 if (auto prop = params.GetValue<JsonObject::BoolT>("generatePreview")) {
505 generatePreview = *prop;
506 }
507
508 result.AddProperty("result", [&](JsonArrayBuilder &array) {
509 for (auto &descriptor : handler(thread, *objectId, generatePreview)) {
510 array.Add(descriptor.ToJson());
511 }
512 });
513 });
514 // clang-format on
515 }
516
OnCallRuntimeRunIfWaitingForDebugger(std::function<void (PtThread)> && handler)517 void InspectorServer::OnCallRuntimeRunIfWaitingForDebugger(std::function<void(PtThread)> &&handler)
518 {
519 server_.OnCall("Runtime.runIfWaitingForDebugger",
520 [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
521 auto thread = sessionManager_.GetThreadBySessionId(sessionId);
522 handler(thread);
523 });
524 }
525
SendTargetAttachedToTarget(const std::string & sessionId)526 void InspectorServer::SendTargetAttachedToTarget(const std::string &sessionId)
527 {
528 server_.Call("Target.attachedToTarget", [&sessionId](auto ¶ms) {
529 params.AddProperty("sessionId", sessionId);
530 params.AddProperty("targetInfo", [&sessionId](JsonObjectBuilder &targetInfo) {
531 targetInfo.AddProperty("targetId", sessionId);
532 targetInfo.AddProperty("type", "worker");
533 targetInfo.AddProperty("title", sessionId);
534 targetInfo.AddProperty("url", "");
535 targetInfo.AddProperty("attached", true);
536 targetInfo.AddProperty("canAccessOpener", false);
537 });
538 params.AddProperty("waitingForDebugger", true);
539 });
540 }
541
EnumerateCallFrames(JsonArrayBuilder & callFrames,PtThread thread,const std::function<void (const FrameInfoHandler &)> & enumerateFrames)542 void InspectorServer::EnumerateCallFrames(JsonArrayBuilder &callFrames, PtThread thread,
543 const std::function<void(const FrameInfoHandler &)> &enumerateFrames)
544 {
545 enumerateFrames(
546 [this, thread, &callFrames](auto frameId, auto methodName, auto sourceFile, auto lineNumber, auto &scopeChain) {
547 CallFrameInfo callFrameInfo {frameId, sourceFile, methodName, lineNumber};
548 AddCallFrameInfo(callFrames, callFrameInfo, scopeChain, thread);
549 });
550 }
551
AddCallFrameInfo(JsonArrayBuilder & callFrames,const CallFrameInfo & callFrameInfo,const std::vector<Scope> & scopeChain,PtThread thread)552 void InspectorServer::AddCallFrameInfo(JsonArrayBuilder &callFrames, const CallFrameInfo &callFrameInfo,
553 const std::vector<Scope> &scopeChain, PtThread thread)
554 {
555 callFrames.Add([&](JsonObjectBuilder &callFrame) {
556 auto [scriptId, isNew] = sourceManager_.GetScriptId(thread, callFrameInfo.sourceFile);
557
558 if (isNew) {
559 CallDebuggerScriptParsed(thread, scriptId, callFrameInfo.sourceFile);
560 }
561
562 callFrame.AddProperty("callFrameId", std::to_string(callFrameInfo.frameId));
563 callFrame.AddProperty("functionName", callFrameInfo.methodName.data());
564 callFrame.AddProperty("location", Location(scriptId, callFrameInfo.lineNumber).ToJson());
565 callFrame.AddProperty("url", callFrameInfo.sourceFile.data());
566
567 callFrame.AddProperty("scopeChain", [&](JsonArrayBuilder &scopeChainBuilder) {
568 for (auto &scope : scopeChain) {
569 scopeChainBuilder.Add(scope.ToJson());
570 }
571 });
572
573 callFrame.AddProperty("canBeRestarted", true);
574 });
575 }
576
577 /* static */
AddHitBreakpoints(JsonArrayBuilder & hitBreakpointsBuilder,const std::vector<BreakpointId> & hitBreakpoints)578 void InspectorServer::AddHitBreakpoints(JsonArrayBuilder &hitBreakpointsBuilder,
579 const std::vector<BreakpointId> &hitBreakpoints)
580 {
581 for (auto id : hitBreakpoints) {
582 hitBreakpointsBuilder.Add(std::to_string(id));
583 }
584 }
585
AddBreakpointByUrlLocations(JsonArrayBuilder & locations,const std::set<std::string_view> & sourceFiles,size_t lineNumber,PtThread thread)586 void InspectorServer::AddBreakpointByUrlLocations(JsonArrayBuilder &locations,
587 const std::set<std::string_view> &sourceFiles, size_t lineNumber,
588 PtThread thread)
589 {
590 for (auto sourceFile : sourceFiles) {
591 locations.Add([this, lineNumber, thread, sourceFile](JsonObjectBuilder &location) {
592 auto [scriptId, isNew] = sourceManager_.GetScriptId(thread, sourceFile);
593
594 if (isNew) {
595 CallDebuggerScriptParsed(thread, scriptId, sourceFile);
596 }
597
598 Location(scriptId, lineNumber).ToJson()(location);
599 });
600 }
601 }
602 } // namespace panda::tooling::inspector
603