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 264