diff options
Diffstat (limited to 'doc/rgdb_specs/daemon.t')
-rw-r--r-- | doc/rgdb_specs/daemon.t | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/doc/rgdb_specs/daemon.t b/doc/rgdb_specs/daemon.t new file mode 100644 index 0000000000..fb505a8d07 --- /dev/null +++ b/doc/rgdb_specs/daemon.t @@ -0,0 +1,420 @@ +@c +@c RTEMS Remote Debugger Server Specifications +@c +@c Written by: Eric Valette <valette@crf.canon.fr> +@c Emmanuel Raguet <raguet@crf.canon.fr> +@c +@c +@c $Id$ +@c + +@chapter RTEMS Debugger Server Daemon + +We will describe in this section how this debugger server will be +implemented on RTEMS environment. Our initial target is based on Intel Pentium +and we will use an Ethernet link to communicate between the host and the target. + +The RTEMS remote debugger will be composed by several tasks and exception +handlers : + +@itemize @b +@item an initialization task which opens the sockets and runs the SUN RPC +server. This task will also connect the interrupt handlers and launch the communication +task +@item a communication task which receives the SUN RPC commands, executes +them and sends the result to the GDB client, +@item A debuggee event management task which waits for events. We need a different +task than the command management task in order to be able to still accept commands +while no event has yet occurred for the debuggee. An example could be a continue +command from GDB and then hitting to DEL key to see what is currently going +on on the target side because an expected breakpoint is not caught... +@item a debug exception handler which manages the hardware breakpoint and +single step exceptions (INT 1 on Intel x86), +@item a breakpoint exception handler which manages the software breakpoints +exceptions (INT 3 on Intel x86), +@item a default exception handler used to catch every possible errors make on the +target system, +@end itemize + +@c XXX figure reference +@c XXX references to other sections +Figure @b{remote debugger tasks and handlers} represents these +different tasks and handlers. The synchronization between the different task +and exception handlers will be described below in the section +@b{Synchronization Among Tasks and Exception Handlers}. +Some open issues we have faced for a prototype implementation are described +in the section @b{Open Issues}. The temporary workaround we chose are described +in chapter @b{Workarounds for Open Issues in Prototype}. + + +@subsection The INITIALIZATION task + +This is the task that must be executed at the boot phase of RTEMS. +It initializes the debug context. It must : + +@itemize @b +@item open the UDP sockets, +@item run the SUN RPC server main loop, +@item create the COMMAND MANAGEMENT task, +@item connect the DEBUG EXCEPTION handler, +@item connect the SOFTWARE BREAKPOINT handler, +@item delete itself. +@end itemize +If an error occurs at any step of the execution, the connections established +before the error will be closed, before the initialization task deletes itself. + + +@subsection The COMMAND_MNGT task + +This task is in charge of receiving the SUN RPC messages and executing +the associated commands. This task must have an important priority because it +must be executed each time a command message comes from the debugger. It must +be executed even if one or both exception handlers are executed. But the COMMAND +MANAGEMENT task must not block the TCP/IP module without which no message can +be received. + +When not executing a command, this task is waiting for a SUN RPC message +on the primary port. This idle state blocks the task, so the other active tasks +can run. Once a message comes from Ethernet via the primary port, the COMMAND +MANAGEMENT task wakes up and receives the message which is a request from GDB. +This request is sent to the SUN RPC server code which extracts the command and +its arguments, executes it and, if needed, sends a result to GDB. After having +performed these actions, the task sleeps, waiting for another message. + +A particular case is the reception of the ATTACH command : in this +case the COMMAND_MNGT task creates the EVENT_MNGT task described below before +going to wait on UDP socket again. + + +@subsection The EVENT_MNGT task + +This task is in charge of managing events happening on the debuggee such as +breakpoint, exceptions. This task does a basic simple loop waiting for event +on a synchronization variable. It is waken up by exception handlers code. It +then signals GDB that an event occurred and then go sleeping again as further +requests will be processed by the COMMAND_MNGT task. + + +@subsection The DEBUG EXCEPTION handler + +This handler is connected to the DEBUG exception (INT 1 on Intel ix86). +This exception is entered when : + +@itemize @b +@item executing a single-step instruction, +@item hardware breakpoint condition is true, +@end itemize +These events will be treated by the debugger because they are the +primary event used when debugging a software for instruction stepping. In both +cases, the DEBUG EXCEPTION handler code is executed. Please note that the execution +context of the exception handler is the supervisor stack of the task that generated +the exception . This implies : + +@itemize @b +@item We may sleep in this context, +@item We have as many possible execution context for the DEBUG EXCEPTION handler as +we need to, +@item When we enter the high level exception handler code, a normalized exception +context has been pushed on the system stack and a pointer to this context is +available as the first argument (cf c/src/exec/score/cpu/i386/cpu.c for more +details), +@end itemize +First the exception handler wakeup the EVENT_MNGT task. Then it will +cause the faulting thread to sleep on a synchronization object. As soon as GDB +receives the event notifying that the debuggee status has changed, it will start +sending requests to get the debuggee status (registers set, faulty task id, +...). These requests are handled by the COMMAND MANAGEMENT task. When this task +receive a PTRACE_CONT command it will resume the execution of the task that +caused the exception by doing a V on the synchronization object. + + +@subsection The BREAKPOINT EXCEPTION handler + +This handler is connected to the BREAKPOINT exception (INT3 on Intel +Ix86). Each time the debugger wants to place a software breakpoint in the debuggee, +a debuggee opcode is temporarily replaced by an instruction causing BREAKPOINT +exception (the ``INT 3'' instruction on Intel ix86). When ``INT 3'' is executed, +the BREAKPOINT handler is executed. Otherwise, the exception processing is the +same than the one described in previous section. + + +@subsection Synchronization Among Tasks and Exception Handlers + +The previous chapters have presented a simplified and static view of the various +tasks and exceptions handlers. This chapter is more focussed on synchronization +requirements about the various pieces of code executed when RGDBSD is operating. + + +@subsubsection Implicit Synchronization Using Task Priorities + +This chapter is relevant on Uniprocessor System (UP) only. However, it will +also list the requirements for explicit synchronization on Multi-processor Systems +(MP). Below are the task priorities sorted by high priority. They are not supposed +to be equal : + +@enumerate +@item Network Input Task. This is the highest priority task. This can be regarded +as a software interrupt task for FreeBSD code, +@item RGDBSD command task. As this task waits on UDP sockets, it shall not prevent +the previous task from running. As the main debug entry point, it should preempt +any other task in the system, +@item RGDBSD event task. This task should preempt any task but the two mentionned +before to signal a debug event to GDB. The command task shall be able to preempt +this task for emergency command such as DEL, or REBOOT, +@item Applications tasks (task we are able to debug), +@end enumerate +Using theses priorities eliminates the need for adding more synchronization +objects in the next section. My belief is that symmetric MP support will require +more important change in the RTEMS than RGDBSD itself like multiple scheduler +queues, task to processor binding for non symmetric IO, use a different implementation +for @emph{task_disable_preemption}, ... + + +@subsubsection Explicit Synchronization + +This chapter will describe the synchronization variables that need to be implemented +in order to sequence debug events in a way that is compatible with what GDB +code expects. The root of the problem is that GDB code mainly expects that once +a debug event has occurred on the debuggee, the entire debuggee is frozen and +no other event will occur before the CONTINUE command is issued. This behavior +is hard to achieve in our case as once we hit a breakpoint, only the task that +hits the breakpoint will be asleep on a synchronization object. Other tasks +may hit other breakpoints while we are waiting commands from GDB generating +potential unexpected events. There is a solutions if RGDBSD itself use RTEMS +threads to fix this problem by creating a task that loops forever at a priority +superior to any debugged task but below RGDBSD task priorities. Unfortunately +this will not work for the case we use the nano-kernel implementation and we +think it is better to study synchronization problems now. We also expects that +multi-thread debug support hardening in GDB will remove some event serializations +requirements. Here is the list of synchronization variables we plan to use and +their usage. They are all regular semaphores. They are not binary semaphores +because the task that does V is not the task that has done the P. + +@itemize @b +@item @emph{WakeUpEventTask} : used by exception handler code to wake up the EVENT_MNGT +task by doing a V operation on this object. When target code is running normally +the EVENT_MNGT task sleeps due to a P operation on this semaphore, +@item @emph{SerializeDebugEvent} : used to serialize events in a way compatible to +what GDB expects. Before doing a V operation on @emph{WakeUpEventTask,} the +exception handler does a P on this semaphore to be sure processing of another +exception is not in progress. Upon reception of a CONTINUE command, the COMMAND_MNGT +task will issue a V operation so that the exception code can wake up EVENT_MNGT +task using the mechanism described above, +@item @emph{RestartFromException} : (in fact one semaphore per task) used by exception +handling code to put a faulty task to sleep once it has generated an exception +by doing a P operation on this semaphore. In the case the exception was generated +due to a breakpoint, GDB command will modify back the BREAKPOINT opcode to the +original value before doing the CONTINUE command. This command will perform +a V on this semaphore. In the case it is a real non restartable exception (faulty +memory reference via invalid pointer for example), GDB will not allow to restart +the program avoiding any loop. So not special analysis of cause of exception +is foreseen as far as RGDBSD code is concerned, +@end itemize + +@subsection Open Issues + +Here are some problems we have faced while implementing our prototype : + +@table @b +@item [Protected ReadMem/WriteMem (I1)]: +A GDB user can request to see the content +of a corrupted pointer. The request PEEK_DATA will be performed by the COMMAND_MNGT +task. It shall not enter the default exception handler set by RGDBSD or it will +cause a dead lock in the RGDBSD code. Replacing the default exception vector +before calling @b{readMem/writeMem} can be temporarily sufficient but : + +@itemize @b +@item It will never work on MP system as it will rely on task priorities to insure +that other task will not cause exceptions while we have removed the default +exception handler, + +@item This feature should not be usable in RGDBSD only but also by an embedded debugger +that may run without any task. It is also unavoidable in case of protected memory +and in this case no priority mechanism can be used, + +@item In the case of using RGDBSD code on a dedicated nano kernel, this code will +be called from interrupt level and we need a way to be sure we can debug other +interrupts that may also cause exceptions, +@end itemize + +@item [ATTACH Command Implementation (I2)]: +After the @emph{target rtems symbolic_ip_target_name} +command, the normal operation is to issue an @emph{attach lid} command where +@emph{lid} represents a valid execution context. For Unix this is a process +id, for other multi-tasking system this is the id of a thread. After the attach +command, GDB expects to be waken up in the same manner as it is for normal events. +Once waken up it expects to have a complete register context available and also +that the target task is in a stopped state and that it can restart it using +the regular CONTINUE command. In RTEMS there is a way to get force a thread +to become inactive via @emph{rtems_task_suspend} but no way to get the full +registers set for the thread. A partial context can be retrieved from the task +@emph{Registers} data structure. On the other hand, relying on @emph{rtems_task_suspend +will be a problem for the nano-kernel implementation. + +@item [Stopping Target System (I3)]: +Allthough it might not be obvious, most of the +actions made by a GDB user assume the target is not running. If you modify a +variable via the @emph{set variable = value} command you expect that the value +is the one you have put when restarting. If a still running task modifies the +same value in the mean time, this may be false. On the other hand, stopping +all the tasks on the target system impose to have a very deep knowledge of the +system. Using an interrupt driven RGDBSD, may facilitate the implementation +on the nano-kernel. + +@item [Getting Tasks Contexts (I4)]: +As previously mentionned there is no way to get +tasks execution contexts via the RTEMS API. This is needed when debugging for +example via this classical sequence : + +@enumerate + +@item @emph{(gdb) target rtems symbolic_ip_target_name} + +@item @emph{(gdb) info threads <=} get a thread list on screen + +@item @emph{(gdb)} @emph{attach thread_id} <= thread_id is one of the thread in +the list + +@item @emph{(gdb) b a_function_of_interest } + +@item @emph{(gdb) continue} + +@item @emph{(gdb)} @emph{backtrace} <= print the call stack on the screen once we +have hit the breakpoint + +@item @emph{(gdb) thread target another_thread_li <=} change implicit current thread +value for gdb commands + +@item @emph{(gdb)} @emph{backtrace <=} should print the backtrace for the chosen thread +@end enumerate +In our execution model, we have a valid context only for the threads that hits +the breakpoint as it has been pushed by the exception handler code. The other +thread is still running and during the various RPC requesting memory access, +it even changes as the COMMAND_MNGT thread is going to sleep. So the backtrace +command will fail. We must find a way to make this work as it is very usefull +when debugging multi-threaded programs, + + +@item [Backtrace Stop convention (I5)]: +The backtrace command on RTEMS task does not +gracefully terminate as GDB does not find some backtrace termination condition +it expects. +@end table + +@subsection Workarounds for Open Issues in Prototype + +@table @b + +@item [(I1)]: +Not implemented.We would rather like to work on the formalization of +per thread flags and global flags that are much more general than any kludge +we could implement, + +@item [(I2)]: +We have tried two solutions in our prototype. The first one was to use +the @emph{idle} thread context contained in the @emph{Registers} task control +block field. The drawback of this solution was that we had to implement specific +code for the continue operation immediately following the attach command. We +then decided to create a dedicated task that will only exist during the attach +phase. This task will call the ``ENTER_RGDB'' exception. This call will execute +the Exception Handler that saves a valid context and that notifies a change +to GDB. After the first CONTINUE command from GDB, this task will continue its +execution and delete itself, + +@item [(I3)]: +As explained above in the synchronization chapter, we choose to serialize +events in a way that makes GDB think the system is frozen, + +@item [(I4)]: +As a temporary fix, we have called @emph{rtems_task_suspend} and used +the context switch contex for tasks that are unknown to RGDBSD, + +@item [(I5)]: +Not Implemented yet. If I remember correctly, setting the frame pointer +to 0 at task initialization for CISC processor solves this problem (ebp = 0x0 +on Intel or a6 = 0x0 on 680x0). This should be done in rtems_task_create function +in the path to really starts the task for the first time. The processor/system +specific stop condition can be found as macros in the GDB source tree. +@end table + +@subsection Output of a Debug Session with the Prototype + +@example +GNU gdb 4.17 +Copyright 1998 Free Software Foundation, Inc. +GDB is free software, covered by the GNU General Public License, and you are +welcome to change it and/or distribute copies of it under certain conditions. +Type "show copying" to see the conditions. +There is absolutely no warranty for GDB. Type "show warranty" for details. +This GDB was configured as --host=i686-pc-linux-gnu --target=i386-rtems". +Attaching remote machine across net... +Connected to net-test. +Now the "run" command will start a remote process. +Setting up the environment for debugging gdb. +(gdb) attach 1 +Attaching program: /build-rtems/pc386/tests/debug.exe pid 1 +0x230715 in enterRdbg () +(gdb) info threads +There are 8 threads: +Id. Name Detached Suspended +134283273 Rini No No <= current target thread +0x230715 in enterRdbg () +134283272 Evnt No No +_Thread_Dispatch () at /rtems/c/src/exec/score/src/thread.c:315 +134283271 SPE2 No No +_Thread_Dispatch () at /rtems/c/src/exec/score/src/thread.c:315 +134283270 SPE1 No No +_Thread_Handler () at /rtems/c/src/exec/score/src/thread.c:1107 +134283269 RDBG No No +0x230715 in enterRdbg () +134283268 SCrx No No +_Thread_Dispatch () at /rtems/c/src/exec/score/src/thread.c:315 +134283267 SCtx No No +_Thread_Dispatch () at /rtems/c/src/exec/score/src/thread.c:315 +134283266 ntwk No No +_Thread_Dispatch () at /rtems/c/src/exec/score/src/thread.c:315 +(gdb) b init.c:89 +Breakpoint 1 at 0x200180: file /rtems/c/src/tests/samples/debug/init.c, line 89. +(gdb) c +Continuing. +Thread 134283273 (Rini) has been deleted. +[Switching to Rtems thread 134283271 (Not suspended) ( <= current target thread )] +Breakpoint 1, example2 (argument=4) at /rtems/c/src/tests/samples/debug/init.c:89 +89 tuto += tuti; +(gdb) s +90 if (print_enable2) +(gdb) c +Continuing. +Breakpoint 1, example2 (argument=4) at /rtems/c/src/tests/samples/debug/init.c:89 +89 tuto += tuti; +(gdb) b init.c:66 +Breakpoint 2 at 0x200128: file /rtems/c/src/tests/samples/debug/init.c, line 66. +(gdb) c +Continuing. +{Switching to Rtems thread 134283270 (Not suspended) ( <= current target thread )] +Breakpoint 2, example1 (argument=4) at /rtems/c/src/tests/samples/debug/init.c:66 +66 toto += titi; +(gdb) c +Continuing. +[Switching to Rtems thread 134283271 (Not suspended) ( <= current target thread )] +Breakpoint 1, example2 (argument=4) at /rtems/c/src/tests/samples/debug/init.c:89 +89 tuto += tuti; +(gdb) bt +#0 example2 (argument=4) + at /rtems/c/src/tests/samples/debug/init.c:89 +#1 0xf0009bd0 in ?? () +(gdb) thread target 134283270 +thread 134283270 [SPE1], _Thread_Dispatch () at /rtems/c/src/exec/score/src/thread.c:315 +315 executing = _Thread_Executing; +(gdb) c +Continuing. +Breakpoint 2, example1 (argument=4) at /rtems/c/src/tests/samples/debug/init.c:66 +66 toto += titi; +(gdb) detach +Detaching program: /build-rtems/pc386/tests/debug.exe pid 1 +Warning: the next command will be done localy! If you want to restart another remote +program, reuse the target command +(gdb) +@end example + + |