FILENAME 3/4 - delete the old get_fun_path etc.
[hiphop-php.git] / hphp / doc / debugger.devdocs
blob7fff38b075eff5186ff1f27a3cace3cd38d03619
1 This document is intended to help developers understand how HHVM
2 debugging is implemented. For user documentation, see
3 docs/debugger.start.
5 1. Overview
6 -----------
8 HHVM provides a rich set of debugging services as well as a
9 command-line debugger client. The client and server (VM) can be on the
10 same or different machines. The client and server may also be in the
11 same process when debugging a script instead of a web server.
13 For simplicity, much of this document will assume the client and
14 server are in different processes. The operation of the various
15 components below is mostly unchanged when they are in the same
16 process, though.
18 A HHVM server can be configured to allow remote debugging with option
19 Eval.Debugger.EnableDebuggerServer. This creates a new debug-only
20 endpoint to which debugger clients may connect on the port specified
21 by Eval.Debugger.Port (default 8089). The class DebuggerServer is
22 responsible for setting up and listening for connections on this endpoint.
25 1.1 The Proxy
26 -------------
28 When a debugger client connects to a VM, whether that VM is a remote
29 server or a local instance running a script, a "debugger proxy" is
30 created by the server. This proxy owns a connection, via a socket
31 wrapped within a Thrift buffer, to the client which is doing the
32 debugging. All interaction with the client is performed through this
33 proxy, and any time the VM does something the debugger might need to
34 know about it informs the proxy. The proxy is implemented in the
35 DebuggerProxy class.
37 The proxy has two important states: interrupted, or not
38 interrupted. When a proxy is interrupted it listens for commands from
39 the client and responds to them. When it is not interrupted, the proxy
40 does nothing and simply sits around waiting to be interrupted. A proxy
41 gets interrupted in one of two ways: interesting events from the VM,
42 or by a dedicated signal polling thread in response to a signal from
43 the client.
45 The proxy will listen for commands from the client, and create
46 instances of subclasses of DebuggerCommand to execute those
47 commands. A command may respond to the client with results, or it may
48 cause the proxy to allow the interrupted thread to run again.
51 1.2 The Client
52 --------------
54 Anyone can build a client so long as they speak the protocol described
55 below. HHVM provides a command line client, usually called
56 "hphpd". The client is invoked by passing "--mode debug" on the
57 command line. This causes HHVM to create a DebuggerClient object,
58 which creates a new thread which will run the command processing
59 loop. The client may attach to a server, or it may attach to the VM in
60 its own process and debug a script running on the main thread. If
61 there is no script to run, the main thread simply waits for the client
62 to finish.
64 Somewhat confusingly, the client also creates a proxy to represent the
65 VM in its own process. Thus, the proxy is not only a server-side
66 object. This proxy works just like the proxy on a server, and is
67 connected to in the same way. This proxy is created even if the client
68 is connecting to a server, though in that case it will not really be
69 used. If the user disconnects from their server this proxy will be
70 used to debug local scripts.
73 2.0 Communication Protocol
74 --------------------------
76 The communication protocol between the client and server is fairly
77 simple. It is based on Thrift, and the bulk of the implementation is
78 held in DebuggerCommand and its subclasses. All communication is based
79 on sending a Command, and in most cases receiving a response of the
80 same Command back, sometimes updated with more information. User
81 actions like print, where, and breakpoint translate into CmdPrint,
82 CmdWhere, and CmdBreak being sent to the server, and received back
83 with data like the result of the print or where operation, or status
84 about the breakpoints being set. Some commands cause the server to
85 resume execution of the program. User actions like continue, next, and
86 step translate into CmdContinue, CmdNext, and CmdStep being sent to
87 the server, which does not respond immediately but continues execution
88 until the program reaches, say, a breakpoint. The server then responds
89 with CmdInterrupt(BreakPointReached) to signal that it is now in the
90 "interrupted state" and is ready to receive more commands.
93 2.1 Initialization
94 ------------------
96 When a new connection is made to the debugger port a proxy is created
97 to own that connection. This proxy is held in a global map keyed by a
98 sandbox ID. The proxy starts a "dummy sandbox" so it can accept
99 commands when there is no active request, and it starts up a signal
100 thread to poll the client for "signals", i.e., Ctrl-C. The dummy
101 sandbox is always started, and should not be confused with a real
102 sandbox. It will never serve a request and is really just there to
103 provide a place to execute code and interact with the server when
104 there are no requests.
106 The proxy is now ready to use, and is not interrupted. The client,
107 after establishing the connection on the debugger port now waits for a
108 command from the proxy. Note that the proxy really doesn't have it's
109 own thread. From now on, it runs on whatever thread interrupts
110 it. That may be the dummy sandbox thread, or it may be a normal
111 request thread.
113 So long as the proxy is not interrupted, the signal thread will poll
114 the client once per second with CmdSignal. The client responds with
115 CmdSignal, updated with whether or not Ctrl-C was pressed. If it was,
116 the signal thread asks each thread registered with the proxy to
117 interrupt, then goes back to polling. If the proxy remains
118 un-interrupted on the next poll, the signal thread will ask the dummy
119 sandbox thread to interrupt.
121 The dummy sandbox creates a thread which first interrupts the proxy
122 with "session started", and then waits to see if it needs to respond
123 to a signal from the client. If there is a signal from the client, the
124 dummy sandbox thread simply loops and interrupts the proxy with
125 "session started" again, and waits again.
127 The proxy, having been interrupted with "session started" from the
128 dummy sandbox, sends a CmdInterrupt(SessionStarted) to the client. The
129 proxy is now interrupted, so it enters a loop listening for commands
130 from the client. It also blocks the signal thread from sending
131 CmdSignal to the client. The proxy will remain interrupted, processing
132 commands requested by the client, until one of those commands causes
133 the proxy to leave the interrupted state and let the thread which
134 interrupted it continue. In the case of SessionStarted, that lets the
135 dummy sandbox thread continue. In the case of more interesting
136 interrupts from the VM, on threads executing requests or other user
137 code, it lets those threads run.
139 When the client receives the SessionStarted interrupt after making the
140 initial connection, it sends CmdMachine to attach to the user's
141 sandbox. The proxy "attaches" to the sandbox by registering itself as
142 the proxy for that sandbox id in the global proxy map. It then signals
143 the dummy sandbox thread, responds with CmdMachine, and returns to the
144 un-interrupted state. The client again waits for a command from the
145 proxy. The dummy sandbox receives the signal, loops, and interrupts
146 the proxy again with "session started", which sends a second
147 CmdInterrupt with type SessionStarted to the client. At this point the
148 client has received CmdInterrupt(SessionStarted) and the proxy is
149 interrupted in the dummy sandbox. The initial connection is complete,
150 and the client can issue whatever commands it wishes.
152 Graphically, the initial connection protocol is:
154 Server threads:
155 DL  -- Debugger Server Listening Thread
156 SP  -- Signal Polling Thread
157 DS  -- Dummy Sandbox Thread
158 RTx -- Request Threads
160    Client                           Server
161 -------------   --------------------------------------------------
162                     DL            SP            DS           RTx
164    |            Listen for
165    |            connections
166    |                |
167 Connect on  ------> |
168 debugger port     Create Proxy
169    |              Create SP ----> |
170    |              Create DS ------------------> |
171    |                |             |             |
172    |                |             |             |
173    | <----------------------- CmdSignal         |
174 CmdSignal ----------------------> |             |
175    |                |             |             |
176    | <------------------------------------ CmdInterrupt(SS)
177 CmdMachine(attach) ---------------------------> |
178    |                |             |        Switch sandbox
179    |                |             |        Notify DS
180    | <------------------------------------ CmdMachine
181    |                |             |        Loop due to notify
182    | <------------------------------------ CmdInterrupt(SS)
183 Ready to go         |             |             |
184    |                |             |             |
185    v                v             v             v
188 2.2 Steady State
189 ----------------
191 Once the client and server are connected, the most common flow is that
192 the client waits for a CmdInterrupt from the proxy while the
193 application runs. When a request thread hits a breakpoint, throws an
194 exception, etc. it will interrupt the proxy. The proxy may decide to
195 ignore the interrupt (perhaps it is not configured to care about
196 thrown exceptions, for instance), in which case the request thread
197 will keep running and the client will never know about the event. If
198 the proxy does decide to take the interrupt it will send CmdInterrupt
199 to the client, then wait for commands from the client. The client will
200 send commands and get responses from the proxy until it sends a
201 command that causes the proxy to let the interrupted thread continue
202 execution.
204 Signal polling continues so long as the proxy is not interrupted.
206    Client                           Server
207 -------------   --------------------------------------------------
208                     DL            SP            DS           RTx
210 Listen for          |             |             |            |
211 commands            |             |             |       IP is at a
212    |                |             |             |       breakpoint.
213    | <----------------------------------------------- CmdInterrupt(BPR)
214 CmdWhere --------------------------------------------------> |
215    | <-------------------------------------------------- CmdWhere
216    |                |             |             |            |
217 CmdPrint --------------------------------------------------> |
218    | <-------------------------------------------------- CmdPrint
219    |                |             |             |            |
220 CmdContinue -----------------------------------------------> |
221    |                |             |             |       Continue request
222    | <----------------------- CmdSignal         |            |
223 CmdSignal ----------------------> |             |            |
224    |                |             |             |            |
225    v                v             v             v            v
228 2.3 Ctrl-C
229 ----------
231 When the client wants to interrupt the server while it is executing
232 code, it responds to CmdSignal with a flag indicating it wants to
233 stop. In the command line client, pressing Ctrl-C will cause the next
234 response to CmdSignal to set the flag. The proxy's signal polling
235 thread will then ask all current request threads to interrupt. When
236 one does, it will send a CmdInterrupt to the client.
238    Client                           Server
239 -------------   --------------------------------------------------
240                     DL            SP            DS           RTx
242    | <----------------------- CmdSignal         |            |
243 CmdSignal(stop) ----------------> |             |            |
244    |                |      Set flag on each     |            |
245    |                |      RTx thread to cause  |            |
246    |                |      it to interrupt      |            |
247    |                |             |             |    Interrupt flag seen.
248    | <----------------------------------------------- CmdInterrupt(BPR)
249    |                |             |             |            |
250    v                v             v             v            v
253 2.4 Quitting
254 ------------
256 CmdQuit is just like any other command, except that after the proxy
257 responds it will remove itself from the global proxy map, close the
258 connection, turn off the signal polling and dummy sandbox threads, and
259 destroy itself. The same actions will occur if the connection with the
260 client is lost for any other reason, even if no CmdQuit was
261 received. An error reading from the socket, a closed socket, etc.
263 2.4.1 Cleaning the proxy
264 ------------------------
266 There are many cases where the proxy will notice that a client has
267 terminated the connection. The easiest one is when a quit command is
268 received, but the client may exit for any number of reasons, and in
269 any state. At a minimum, the proxy's signal polling thread will, after
270 one second, notice that the connection has been dropped and initiate
271 cleanup. However, neither the signal polling thread nor the dummy
272 sandbox thread can completely perform the cleanup because they are
273 owned by the proxy, and destroying the proxy would destroy those
274 threads before they have completed.
276 Thus, proxy cleanup may be initiated by any thread with Proxy::stop(),
277 but the final cleanup is performed by another thread doing
278 housekeeping work. The cleanup work waits for both the signal polling
279 and dummy sandbox threads to exit before completing. Server-side this
280 housekeeping work is done by the server thread, which is also
281 listening for new debugger connections. Note that this cleanup work
282 may complete while a request thread is still using the proxy. The last
283 reference to the proxy will finally destroy it, and the cleanup work
284 ensures that the proxy is still usable (and communicates that it is
285 stopped) by any outstanding request threads.
289 3.0 Client Implementation
290 -------------------------
292 The debugger client provided by HHVM is not a separate program, but a
293 special mode passed when executing HHVM. When "--mode debug" is
294 passed, a DebuggerClient object is created and a new thread is started
295 to execute DebuggerClient::run(). This "client thread" will execute
296 the main loop of the client, presenting a command prompt at times, and
297 running a communication loop with the server at other times.
299 A "local proxy" is also created, which is a normal DebuggerProxy for
300 the VM within the process. The client connects to this proxy normally,
301 with a socket and a thrift buffer. The proxy will create a signal
302 polling thread as usual, but it will not setup a dummy sandbox. The
303 lack of a dummy sandbox is really the only difference between a normal
304 proxy and this local proxy.
306 The main thread of the process will run a script specified on the
307 command line, just like HHVM normally would. The client will, by
308 default, attempt to debug that script. The main thread's execution is
309 slightly modified to allow the client to restart the script in
310 response to the 'run' command, and to give control back to the client
311 when the script is complete instead of exiting the process.
313 If the client is asked to connect to a remote server (either via "-h"
314 on the command line or via the 'machine connect' command) then it does
315 so as described above, and the main thread of the process will simply
316 idle and wait for the client to exit, at which time the process will
317 exit.
319 3.1 Console and communication loops
320 -----------------------------------
322 The debugger client has a top-level "event loop" which waits to
323 receive commands from the proxy to which it is attached. It responds
324 to CmdSignal, and when it receives a CmdInterrupt it enters a "console
325 loop" which presents a prompt to the user and processes user
326 commands. Each user command is recognized and an instance of a
327 subclass of DebuggerCommand is created, then executed.
329 The client will remain in the top-level event loop until an interrupt
330 is received, and it will remain in the console loop until a command
331 causes execution on the server to continue. When such a command is
332 executed (e.g. 'continue'), it sends the request to the server and
333 then throws DebuggerConsoleExitException. This is the notification to
334 exit the console loop and return to the event loop. The use of an
335 exception for this is a bit odd, as it is typically simply the last
336 thing done from the command's onClientImpl() method, which is called
337 directly from the console loop. The use of an exception here is
338 similar to the use of DebuggerClientExitException, discussed below,
339 but is now a vestige that will likely be removed soon.
341 Some commands can cause the client to exit, like 'quit'. The client
342 may also exit due to various error conditions, like loss of
343 communication with the server. In these cases a
344 DebuggerClientExitException is thrown. This causes execution to unwind
345 out of both the console and event loops, back to
346 DebuggerClient::run(), which eventually causes the client to exit. The
347 use of an exception here is more interesting as we will see below when
348 discussing nested event loops, as it allows the client to exit out of
349 multiple nested event loops with ease.
351 Somewhat confusingly, DebuggerClientExitException is also thrown by
352 the proxy when it detects the client is exiting. This signals the
353 termination of the request which was being debugged, which is
354 reasonable. But you'd imagine that a different exception could serve
355 that purpose. This is a subtle cheat in the system, and is more
356 meaningful when the proxy is local: it is a signal back to the
357 modified main thread that the debugger is quitting and the main thread
358 should now quit. In the local case, the main thread is much like a web
359 server request thread in that it calls into the proxy to interrupt it.
362 4.0 Nested execution
363 --------------------
365 Some commands allow a user to recursively execute more PHP code while
366 stopped at, say, a breakpoint. More breakpoints may be hit in the
367 newly executed function, and more code may be executed while stopped
368 at those breakpoints. The best example of this is CmdEval, which is
369 used to evaluate arbitrary functions and code (e.g., '@foo(42)').
371 Both the client and proxy are designed to handle this.
373 On the proxy, execution is paused waiting for a command from the
374 client. When an eval command is received, the proxy is put back into
375 the running state and the code is executed directly from the command
376 processing loop. If another interrupt is encountered, a new command
377 processing loop is entered and the interrupt is communicated to the
378 client just like normal. When then code completes, the response to the
379 eval command is sent and control is returned to the command processing
380 loop still on the stack. Thus we may recurse arbitrarily deep on the
381 server down multiple levels of proxy command loops, depending on how
382 deeply the user wishes to go. In practice this depth is quite shallow,
383 and most often involves no recursion.
385 On the client the story is much the same. When an eval command is
386 entered by the user, the client sends CmdEval to the proxy then enters
387 a nested event loop to wait for either the eval command to be sent
388 back, indication completion of the command, or for new CmdInterrupts
389 to be received, indicating breakpoints and other interesting events
390 while the code was being executed. Interrupts are handled normally,
391 and a new console loop is entered. Again, like the proxy these loops
392 may nest to arbitrary depths.
395 5.0 Control Flow
396 ----------------
398 This section will discuss how the control flow commands 'next',
399 'step', 'out', and 'continue' work in the proxy and the VM. Operation
400 of these commands on the client isn't very interesting and is covered
401 well enough elsewhere.
403 Flow control commands are treated specially by the proxy. A single
404 instance of a subclass of CmdFlowControl is held in the m_flow member
405 variable of DebuggerProxy so long as it remains active, and having an
406 active flow command typically means that the proxy will deliver all
407 interrupts to the flow command for processing first. The flow command
408 will have the opportunity to examine the interrupt and determine if
409 the proxy should really stop at the interrupt, or continue
410 execution. A flow command will mark itself as completed when it
411 decides it's time to stop execution, and the proxy will remove it.
413 The only thing that can get the proxy to stop at an interrupt when a
414 flow command has said not to is an active breakpoint.
416 When the proxy does finally have an interrupt to stop at and send to
417 the client it removes and deletes the flow command. The flow command
418 is either complete, in which case it doesn't need to remain active
419 anyway, or it has been trumped by a breakpoint, in which case the flow
420 command is essentially forced to be complete.
422 A flow command is set as active on the proxy when it is received from
423 the client. Execution continues at that time.
426 5.1 Continue
427 ------------
429 'continue' is the simplest control flow command. It simply marks
430 itself completed and returns. The proxy will remove the CmdContinue
431 and continue execution.
434 5.2 Step
435 --------
437 'step' is the next simplest flow command. It operates on the very
438 simple theory that to step to the next line executed you simply need
439 to interpret the program until the current source location
440 changes. This is, by definition, the next source line to be executed
441 no matter how execution flows in the program: function call,
442 exception, a simple add, etc.
444 First, CmdStep sets a "VM interrupt" flag which is eventually
445 installed on the VM's execution context for the current thread. The
446 interpreter's execution loop is instrumented, only when a debugger
447 client is actually attached to the VM, with a "hook" to the debugger
448 infrastructure called phpDebuggerOpcodeHook(). This hook is given the
449 PC of the opcode which is about to be executed. Setting the VM
450 interrupt flag ensures the VM will only interpret code, and thus call
451 the debugger opcode hook. This flag remains set only while the step
452 command is active; the VM will go back to executing translated code
453 once the command completes.
455 Next, CmdStep sets up a "location filter" for the current source
456 line. This is a very simple set which contains all PC's for bytecodes
457 implementing the current source line.  It first consults the location
458 filter to determine if this is a location which might be interesting
459 to a debugger. If it gets a hit in the location filter, it simply
460 returns to the interpreter and executes the opcode as usual. The
461 location filter, then, is a simple mechanism which flow commands can
462 use to avoid being interrupted when a set of bytecodes is executed.
464 By setting up a location filter for the current source line and
465 turning on interrupts the step command ensures it will be interrupted
466 as soon as a bytecode not belonging to the current source line is
467 encountered. When that occurs it marks itself completed, and the proxy
468 will remove the CmdStep and destroy it. When any flow command is
469 destroyed the location filter is cleared, and the VM interrupt flag is
470 turned off.
473 5.3 Out
474 -------
476 'out' works very differently from 'step'. It predicts the next
477 execution location and sets up an "internal breakpoint" at that
478 location. This internal breakpoint works just like a normal breakpoint
479 set by a user, but it is not visible to the user. The breakpoint will
480 be automatically removed when CmdOut is destroyed. CmdOut sets this
481 breakpoint, then continues execution like normal (no location filter,
482 no VM interrupt flag).
484 The breakpoint is placed at the return address of the current
485 function. When it is reached, there are two possibilities: we have
486 returned from the function in question, in which case the command is
487 marked as complete, or we have hit the breakpoint during a recursive
488 call before exiting the original function.
490 To determine which case it is, CmdOut remembers the original stack
491 depth when the command was activated, and checks it when the
492 breakpoint is hit. If the stack depth is the same or lower, execution
493 is continued. Otherwise, the command is complete.
496 5.3.1 Determining the location to step out to
497 ---------------------------------------------
499 Finding the location a function will return to is not straightforward
500 in all cases. For a function called from FCALL, the location is
501 obvious and unambiguous. But for functions called from, say, ITERNEXT
502 or a destructor called from a simple SETL the return offset stored in
503 the VM's activation record is not what we would expect. A complex
504 instruction may have multiple points at which execution could
505 continue. ITERNEXT, for instance, will branch to the top of the loop
506 if the iterator is not done, or fall through if the iterator is
507 done. Thus the step out location could be multiple places.
509 Also, functions with no source information are ignored for a step out
510 operation, so the location could be multiple frames up, not just one.
512 All of this is accounted for in CmdFlowControl::setupStepOuts(). See
513 that function for the details.
516 5.4 Next
517 --------
519 'next' is the most complex of the flow control commands, and builds on
520 the primitives of the others. It starts just like 'step' by trying to
521 use the interpreter to get off the current source line. But the goal
522 is not to just get off the line, it is to get to the next line. If
523 execution ends up deeper on the stack, a call has been made. CmdNext
524 will re-use the step out facility needed for 'out' and, in essence,
525 internally execute a step out to get back to the original source line,
526 then continue try to interpret off of it. If execution ends up
527 shallower on the stack, then we have stepped over a return and are
528 done as well.
530 There is extra logic to support stepping within generators, so that
531 a 'next' executed on a source line with a 'yield' on it will step over
532 the yield. Thus CmdNext looks at the opcodes it is stepping over, and
533 upon recognizing CONTEXIT or CONTRETC will setup its own internal
534 breakpoints at destinations indicated by those opcodes.
536 For the details, see CmdNext.
539 5.5 Exceptions
540 --------------
542 Exceptions are non-local control flow that throw a monkey wrench into
543 most flow control commands. The VM is further instrumented, again only
544 while a debugger is attached, to call a hook whenever it is about to
545 transfer control to a catch block. This gives all of the flow commands
546 a chance to determine if the new location satisfies the completion
547 criteria for their respective operations. In most cases, the flow
548 command will force the first opcode of the catch clause to be
549 interpreted and let it's normal logic run its course.
551 6. Breakpoints
552 --------------
554 Breakpoints are set via commands processed by the client, which keeps a list
555 of all breakpoints. This list is sent to the proxy whenever an element is added,
556 modified or deleted. The proxy keeps of a copy of the list and updates each
557 breakpoint with a flag that indicates if the breakpoint is bound. An unbound
558 breakpoint is either invalid because it refers to a non existent source line
559 or function, or it refers to a location that has not yet been loaded into the
560 VM. The flag distinguishes these cases.
562 6.1 Checking if breakpoints are hit.
564 When a breakpoint becomes bound, it is mapped to a range of program counters
565 (PCs) in an execution unit. Each of these PCs is then added to a set of
566 breakable PCs (called the Breakpoint Filter) kept in the VM's execution context
567 object. If a debugger is attached to the VM, the interpreter calls
568 phpDebuggerOpcodeHook before executing each operation. This routine checks if
569 the PC of the operation is a member of the Breakpoint Filter. If so, it calls
570 the Debugger::InterruptVMHook method, which among other things, checks the
571 proxy's list of breakpoints to see if any of them apply to the source location
572 of the current PC. This involves a check if the breakpoint is enabled by the
573 user, a check if the source location is the same, a check if the breakpoint is
574 not already active and a check if the breakpoint is conditional on the value
575 of an expression.
577 6.2 Active breakpoints
579 A breakpoint can be already active when InterruptVMHook is called because it
580 can be set on a source line that maps to several interpreter operations.
581 (InterruptVMHook will be called for each such operation). It is not safe to
582 simply disable the breakpoint until the last of these operations are completed,
583 since one or more of those operations may be function calls that recurse back
584 to the active breakpoint.
586 In order to deal with this, each breakpoint keeps track of the depth of the
587 execution stack where it was activated. When InterruptVM finds that a breakpoint
588 will be hit, it checks that the height of the execution stack is greater than
589 the height recorded in the breakpoint before it proceeds with the steps that
590 are taken when a breakpoint is first hit. When control leaves the site of a
591 breakpoint at the right stack depth, the breakpoint is updated its previous
592 active stack depth (it has a stack of stack depths and it pops the top entry).
594 This pop operation cannot happen before the last operation corresponding to a
595 breakpoint has completed. However, it is not convenient for this to happen on
596 the very next operation. Instead, InterruptVM hook will update any breakpoints
597 that do not correspond to the current operation to ensure they are active if a
598 subsequent call to InterruptVMHook matches their location at the current stack
599 level.