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.h"
17 #include "breakpoint_hooks.h"
18 #include "error.h"
19 #include "server.h"
20 #include "step_hooks.h"
21
22 #include "macros.h"
23 #include "tooling/debug_interface.h"
24 #include "utils/json_builder.h"
25 #include "utils/utf.h"
26
27 #include <algorithm>
28 #include <functional>
29 #include <iterator>
30 #include <memory>
31 #include <string>
32
33 using namespace std::literals::string_literals; // NOLINT(google-build-using-namespace)
34 using namespace std::placeholders; // NOLINT(google-build-using-namespace)
35
36 namespace panda::tooling::inspector {
Inspector(Server & server,DebugInterface * debugger)37 Inspector::Inspector(Server &server, DebugInterface *debugger)
38 : server_(server),
39 debugger_(debugger),
40 sourceManager_(server),
41 hooks_(*this, std::make_unique<StepHooks>(*this), std::make_unique<BreakpointHooks>(*this))
42 {
43 if (!HandleError(debugger_->RegisterHooks(&hooks_))) {
44 return;
45 }
46
47 server_.OnCall("Debugger.enable", std::bind(&Inspector::EnableDebugger, this, _1));
48 server_.OnCall("Debugger.resume", std::bind(&Inspector::Resume, this));
49 server_.OnCall("Runtime.enable", std::bind(&Inspector::EnableRuntime, this));
50 }
51
~Inspector()52 Inspector::~Inspector()
53 {
54 HandleError(debugger_->UnregisterHooks());
55 }
56
EnableDebugger(JsonObjectBuilder & result)57 void Inspector::EnableDebugger(JsonObjectBuilder &result)
58 {
59 result.AddProperty("debuggerId", "debugger");
60 }
61
EnableRuntime()62 void Inspector::EnableRuntime()
63 {
64 server_.Call("Runtime.executionContextCreated", [](auto ¶ms) {
65 params.AddProperty("context", [](JsonObjectBuilder &context) {
66 context.AddProperty("id", 0);
67 context.AddProperty("origin", "");
68 context.AddProperty("name", "context");
69 });
70 });
71 }
72
HandlePendingPause()73 bool Inspector::HandlePendingPause()
74 {
75 if (!pendingPause_) {
76 return false;
77 }
78
79 std::for_each(pauseHandlers_.begin(), pauseHandlers_.end(), [](auto &handler) { handler(); });
80
81 auto addCallFrame = [&this](JsonArrayBuilder &callFrames, const PtFrame &frame) {
82 auto &sourceFile = sourceManager_.GetOrLoadSourceFile(frame.GetMethod());
83 auto &table = sourceFile.GetDebugInfo().GetLineNumberTable(frame.GetMethod()->GetFileId());
84
85 // Line number entry corresponding to the bytecode location is
86 // the last such entry for which the bytecode offset is not greater than
87 // the given offset. Use `std::upper_bound` to find the first entry
88 // for which the condition is not true, and then step back.
89 auto lineNumberIter = std::upper_bound(table.begin(), table.end(), frame.GetBytecodeOffset(),
90 [](auto offset, auto &entry) { return offset < entry.offset; });
91 ASSERT(lineNumberIter != table.begin());
92 auto lineNumber = std::prev(lineNumberIter)->line - 1;
93
94 callFrames.Add([&](JsonObjectBuilder &callFrame) {
95 callFrame.AddProperty("callFrameId", std::to_string(frame.GetFrameId()));
96 callFrame.AddProperty("functionName", utf::Mutf8AsCString(frame.GetMethod()->GetName().data));
97 callFrame.AddProperty("location", [&](JsonObjectBuilder &location) {
98 location.AddProperty("scriptId", std::to_string(sourceFile.GetScriptId()));
99 location.AddProperty("lineNumber", lineNumber);
100 });
101 callFrame.AddProperty("url", "file:///"s + sourceFile.GetFileName());
102 callFrame.AddProperty("scopeChain", [](JsonArrayBuilder & /* scopeChain */) {});
103 callFrame.AddProperty("this", [](JsonObjectBuilder &thisObj) { thisObj.AddProperty("type", "undefined"); });
104 });
105
106 return true;
107 };
108
109 if (pendingPause_->reason) {
110 server_.Call("Debugger.paused", [&](auto ¶ms) {
111 params.AddProperty("callFrames", [&](JsonArrayBuilder &array) {
112 HandleError(
113 debugger_->EnumerateFrames(pendingPause_->thread, std::bind(addCallFrame, std::ref(array), _1)));
114 });
115 params.AddProperty("reason", *pendingPause_->reason);
116 });
117 }
118
119 pausedThread_.emplace(pendingPause_->thread);
120 pendingPause_.reset();
121
122 server_.Run();
123 pausedThread_.reset();
124 return true;
125 }
126
Resume()127 PtThread Inspector::Resume()
128 {
129 server_.Call("Debugger.resumed");
130 server_.Pause();
131 return *pausedThread_;
132 }
133 } // namespace panda::tooling::inspector
134