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