• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Main parts description
2
3Two big parts, the `Inspector` and `InspectorServer` classes, isolate debugger and server logic respectively. Their brief description follows.
4
5## Inspector
6
7The main class, it is responsible for interoperability with the runtime and managing other modules. It starts a separate server thread to listen to client requests.
8
9The `Inspector` class implements `PtHooks` interface and manages debugger events correspondingly.
10
11`Inspector` uses the following modules.
12
13### DebuggableThread
14
15Represents an application thread being debugged. It encapsulates a `ThreadState` and processes logic of thread suspensions. Threads have the following three levels of suspension:
16
17#### Paused
18
19The thread is paused (`state_.IsPaused()` is `true`) means that a breakpoint was hit, or a step was performed, etc., and the thread should not execute the following instructions until it will be unpaused.
20
21#### Suspended
22
23The thread is suspended (`suspended_` is `true`) means that the thread actually is going to sleep now, but it still can process asynchronous requests to the object repository ([see below](./inspector.md#objectrepository)).
24
25Before and after the suspension (i.e. setting `suspended_` to `true`) the `pre_suspend_` and `post_suspend_` callbacks are called respectively. The latter sends a `Debugger.paused` event in the current implementation of `Inspector`.
26
27The thread could be resumed when still remaining paused. In this case it suspends again. This opportunity is used by the `Touch()` call during new connection processing to notify the client about the thread state (when suspending, the corresponding `Debugger.paused` event is sent).
28
29#### Wait for suspension
30
31When the thread is suspended, the `thread_->Suspend()` call is performed. It increases managed thread's suspension counter. Then, when `thread_->WaitSuspension()` is called, it waits until a resume call will decreases the counter and it becomes zero.
32
33Before and after waiting for suspension `pre_wait_suspension_` and `post_wait_suspension_` callbacks are called respectively. They release and take a read-lock on debugger events processing (in the current implementation of `Inspector`) to allow / disallow new connections processing.
34
35If thread's suspension counter becomes zero, but `suspended_` is still `true`, this means that a request to the object repository was sent. The request is executed, then the counter is increased and the thread is waiting for suspension again.
36
37### ThreadState
38
39An FSM-like state of an application thread. It processes events from the runtime and client requests and decides (due to breakpoints, stepping state etc.) when to pause or unpause the thread.
40
41### ObjectRepository
42
43Makes remote objects corresponding to managed objects (maps managed objects to IDs) and primitives. Also manages fake frame objects.
44
45To store managed objects in a GC-safe manner, `HandleScope` is used. Due to limitation of handle scopes, an object repository could be used only from the corresponding application thread. That's why asynchronous requests to object repositories ([see above](./inspector.md#wait-for-suspension)) were implemented.
46
47An object repository is valid during a pause only. It is destroyed when the thread is resumed, and remote object mapping become invalid.
48
49### DebugInfoCache
50
51The service provides necessary debug info for the application. It caches the info during module loads.
52
53The debug info could be backed by disassembled code to allow debugging when source code is not available. The module tracks fake disassembled source file names to substitute their contents when corresponding source code requests occur.
54
55### Debugger
56
57Inspector utilizes the runtime instrumentation capabilities with the help of a `DebugInterface` object.
58
59### Breakpoints
60
61Represents common and conditional breakpoints.
62For common breakpoint multiple locations are allowed in case of breakpoint location set by regex. For conditional breakpoint only one location is allowed and condition is necessary that is evaluated on location hit.
63
64Breakpoints can be lazily resolved. Resolution is a process of mapping client's breakpoint source code location to bytecode location in binary file executed in the VM. First resolution attempt is done during breakpoint setting and it is succeed if corresponding binary file is loaded. Other resolution attempts are called during load of new binary file, find `LoadModule` in `inspector.cpp` for clarification.
65Conditional breakpoints are resolved once, since they allow only one location. Common breakpoints can be resolved again and new location will be added.
66
67## InspectorServer
68
69The class is responsible for communication with client. It wraps the `Server` class and gives a convenient Inspector protocol based API. It hides all Inspector protocol related details inside and translates them to the language of runtime (e.g. session and source IDs). The implementation is pretty straightforward. It uses the following helper modules.
70
71### Server
72
73This provides a low-level JSON-based interface to a connection.
74
75### SessionManager
76
77The module is responsible for translation between runtime threads and Inspector session IDs.
78
79### SourceManager
80
81The class is responsible for translation between source file names and Inspector source file IDs. Also, it keeps track of which session was informed about the ID (so the `InspectorServer` could send the corresponding "Debugger.scriptParsed" message).
82
83# Threading model overview
84
85In the multi-thread version, the two main kinds of threads are considered:
86- The server thread, it is a single thread responsible for processing client requests;
87- Application threads, these are the execution threads of the debuggable application.
88
89The main idea is that the user requests are processed by the server thread, while the debugger events are processed by application threads. This fact is used to simplify provision of thread safety in several places.
90
91For example, in `InspectorServer::CallTargetDetachedFromTarget`, when the server is paused, we are sure that we could safely remove the corresponding thread from the session and source managers. It is because:
92- the server thread sleeps;
93- and we are executing on the corresponding application thread.
94
95So, we do not consider the case, when someone else in Inspector could access the corresponding `PtThread` objects.
96
97Another example is processing of a new connection in `Inspector`. On validation, a write-lock on debugger events processing is held. Then, during the connection open hook, we safely reset debuggable thread states because:
98- we are executing on the server thread;
99- and application threads could not process events due to the aforementioned write-lock.
100
101We do not consider that someone else in Inspector could access the debuggable thread states.
102
103`DebuggableThread`'s safety also actively relies on these assumptions. For example, we do not consider the case of simultaneous calls to `OnSingleStep` and `OnMethodEntry`. Follow the inline comments for more information.
104
105# Diagrams
106
107The diagrams below are split in two parts (`Inspector` and `InspectorServer` perspectives) for simplification.
108
109A single step event and a "Continue to" request are chosen as the most interesting cases.
110
111## Single step event (***an application thread***)
112
113### Inspector perspective
114
115```mermaid
116sequenceDiagram
117    autonumber
118
119    participant InspectorServer
120    participant Inspector
121    participant DebuggableThread
122    participant Debugger
123    participant DebugInfoCache
124    participant ObjectRepository
125
126    Inspector->>+DebuggableThread: OnSingleStep()
127        loop while paused
128            DebuggableThread->>+Inspector: Post-suspend callback
129                Inspector->>+InspectorServer: CallDebuggerPaused()
130                    InspectorServer->>+Inspector: Enumerate frames callback
131                        Inspector->>+Debugger: EnumerateFrames()
132                            loop for each frame
133                                Debugger->>+Inspector: Handle frame callback
134                                    Inspector->>+DebugInfoCache: GetSourceLocation() for frame
135                                    DebugInfoCache-->>-Inspector:
136
137                                    Inspector->>+DebugInfoCache: GetLocals() for frame
138                                    DebugInfoCache-->>-Inspector:
139
140                                    Inspector->>+ObjectRepository: CreateFrameObject() for frame
141                                    ObjectRepository-->>-Inspector:
142
143                                    Inspector->>+InspectorServer: Handle frame callback
144                                    InspectorServer-->>-Inspector:
145                                Inspector-->>-Debugger:
146                            end
147                        Debugger-->>-Inspector:
148                    Inspector-->>-InspectorServer:
149                InspectorServer-->>-Inspector:
150            Inspector-->>-DebuggableThread:
151
152            loop while suspended
153                DebuggableThread->>+Inspector: Pre-wait suspension callback
154                    Inspector->>Inspector: Release debugger events lock
155                Inspector-->>-DebuggableThread:
156
157                DebuggableThread->>DebuggableThread: Wait for suspension
158
159                DebuggableThread->>+Inspector: Post-wait suspension callback
160                    Inspector->>Inspector: Read-lock debugger events lock
161                Inspector-->>-DebuggableThread:
162
163                DebuggableThread->>DebuggableThread: Process an object repository request, if any
164            end
165        end
166    DebuggableThread-->>-Inspector:
167```
168
169### InspectorServer perspective
170
171```mermaid
172sequenceDiagram
173    autonumber
174
175    participant Inspector
176    participant InspectorServer
177    participant Server
178    participant SessionManager
179    participant SourceManager
180
181    Inspector->>+InspectorServer: CallDebuggerPaused()
182        InspectorServer->>+SessionManager: GetSessionIdByThread()
183        SessionManager-->>-InspectorServer:
184
185        InspectorServer->>+Server: Call("Debugger.paused")
186            Server->>+InspectorServer: Fill parameters callback
187                InspectorServer->>+Inspector: Enumerate frames callback
188                    loop for each frame
189                        Inspector->>+InspectorServer: Handle frame callback
190                            InspectorServer->>+SourceManager: GetSourceFileId()
191                            SourceManager-->>-InspectorServer:
192                        InspectorServer-->>-Inspector:
193                    end
194                Inspector-->>-InspectorServer:
195            InspectorServer-->>-Server:
196        Server-->>-InspectorServer:
197    InspectorServer-->>-Inspector:
198```
199
200## "Continue to" request (***the server thread***)
201
202### InspectorServer perspective
203
204```mermaid
205sequenceDiagram
206    autonumber
207
208    participant Inspector
209    participant InspectorServer
210    participant Server
211    participant SessionManager
212    participant SourceManager
213
214    Server->>+InspectorServer: "Debugger.continueToLocation" callback
215        InspectorServer->>+SessionManager: GetThreadBySessionId()
216        SessionManager-->>-InspectorServer:
217
218        InspectorServer->>+SourceManager: GetSourceFileName()
219        SourceManager-->>-InspectorServer:
220
221        InspectorServer->>+Inspector: ContinueToLocation()
222            Inspector->>+InspectorServer: CallDebuggerResumed()
223                InspectorServer->>+SessionManager: GetSessionIdByThread()
224                SessionManager-->>-InspectorServer:
225
226                InspectorServer->>+Server: Call("Debugger.resumed")
227                Server-->>-InspectorServer:
228            InspectorServer-->>-Inspector:
229        Inspector-->>-InspectorServer:
230    InspectorServer-->>-Server:
231```
232
233### Inspector perspective
234
235```mermaid
236sequenceDiagram
237    autonumber
238
239    participant InspectorServer
240    participant Inspector
241    participant DebuggableThread
242    participant DebugInfoCache
243
244    InspectorServer->>+Inspector: ContinueToLocation()
245        Inspector->>+DebugInfoCache: GetContinueToLocations()
246        DebugInfoCache-->>-Inspector:
247
248        Inspector->>+DebuggableThread: ContinueTo()
249            DebuggableThread->>DebuggableThread: Resume managed thread
250
251            DebuggableThread->>+Inspector: Post-resume callback
252                Inspector->>+InspectorServer: CallDebuggerResumed()
253                InspectorServer-->>-Inspector:
254            Inspector-->>-DebuggableThread:
255        DebuggableThread-->>-Inspector:
256    Inspector-->>-InspectorServer:
257```
258
259## Runtime perspective
260
261The diagram below represents high-level scheme of communication between ArkTS Runtime and Inspector debugger.
262
263![](./images/inspector-debugger-sequence.svg)
264