• 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 "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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) { 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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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