ctlmux is a Cinquecento library for tracing the execution of running programs. ctlmux defines two abstractions: * A ctl represents a traced execution. An execution corresponds to a single-threaded process or one thread of a multi-threaded process. In this manual the term "process" generally refers to either form of execution. Events in the traced execution --- death, a signal, a system call, creation of a new process, execution of a new program, arrival at a breakpoint --- are reflected as ctl events. Ctlmux users can register handlers for ctl events. Every ctl has two associated Cinquecento address spaces that reflect the current state of the process memory and registers. Ctls of a threaded process usually share a memory address space. * A mux is a multiplexor of ctl events over a set of concurrently executing ctls. To use ctlmux, you need to build prctl (available separately) and install the prctl executable in your path. Getting started --------------- Include the cltmux library: @include Create a fresh mux: mux = mkctlmux(); This mux can control threads and processes on the local machine, but not (for now) those on other machines. Operations on muxes ------------------- mux.launch(args, flags) Launch a new process. Returns a ctl. ctl = mux.launch([cmd, arg, ...], flags); [cmd, arg, ...] is the list of arguments used to exec the program; i.e., argv[0] = cmd, argv[1] = arg, etc. flags is any combination of the following flags. ctlmux`Fstdin - redirect target stdin ctlmux`Fstdout - redirect target stdout ctlmux`Fstderr - redirect target stderr These redirections allow the stdio of the target to be read or written through the ctl. If flags is 0 or nil, the target process inherits the stdio of the calling process. The resulting process is stopped at the return of the (successful) call to exec. mux.attach(pid) Attach to an existing process. Returns a ctl. ctl = mux.attach(pid); pid is the process id of the target thread. The target process is left running. mux.run() Run the mux until all ctls under its control have exited. Returns nil. mux.abort() Abort the execution of the mux. Valid only while a call to mux.run is active. All ctls until the control of mux are terminated, and the mux is no longer usable. mux.looksym(nsid, sym) Look up symbol sym (string) in name space nsid. @record elfsym { id, // symbol name bind, // value of binding field in ELF symtab type, // see below val, // absolute address of symbol sz, // size of object }; type is a bitfield with the following structure: bit 0: if set, the symbol is undefined in the namespace (listed as an undefined symbol) bit 1-2: specifies the type of the symbol: 0 - function 1 - data 2 - read-only data Return nil if there is no symbol by that name. Operations on Ctls ------------------ ctl.id Return process identifier. (Note that this is not a function call.) ctl.xstop() Pause execution. ctl.xcont() Resume execution. ctl.xstep() Single step. ctl.detach() Discontinue tracing. ctl.kill() Terminate target process. ctl.mem() Return memory address space. ctl.reg() Return register address space. ctl.trace(event, handler) trace registers a handler to be called when certain events occurs on the ctl. The arguments passed to the handler depend on the type of the event. See the table below. The process is stopped during the call to the handler. If the handler returns nil, the ctl is resumed upon return of the handler; otherwise the ctl remains stopped. Passing a nil handler ends the tracing of the specified event. Event Identifier Handler Args Event --------------------------------------------------------------- ctlmux`Esyscall (ctl) enter/exit system call ctlmux`Eexec (ctl) successful exec return ctlmux`Esignal (ctl) signal (UNIMPLEMENTED) ctlmux`Eexit (ctl) exit ctlmux`Efork (ctl, newctl) successful fork return ctlmux`Eclone (ctl, newctl) successful clone return ctl.traces() (UNIMPLEMENTED) traces returns a list of active (non-nil) traces on the ctl. Each element of the list is a list of two elements: the event identifier (e.g., ctlmux`Esyscall), followed by the handler. ctl.xtrap(addr, handler) xtrap registers a handler to be called when execution of ctl arrives at the specified address. The argument to the handler is the trapped ctl. The process is stopped during the call to the handler. If the handler returns nil, the ctl is resumed upon return of the handler; otherwise the ctl remains stopped. Passing a nil handler ends the trapping of the specified address. ctl.xtraps() (UNIMPLEMENTED) xtraps returns a list of active traps on the ctl. Each element of the list is a list of two elements: the address of the trap, followed by the handler. ctl.ns() return a name space for the executable associated with ctl. ctl.locals(ctx) return a list of symbols that are local variables at the execution context CTX. ctl.localns(ctx) return a new name space that shadows the name space returned by NS with the symbols returned by LOCALS. Inheritance ----------- Every ctl has an associated a set of traced events and breakpoints. Fork and clone events, when traced, instantiate a new ctl representing the new process. This ctl is passed as the second argument to the event handler. The new process is stopped. The new ctl inherits the set of traced events and handlers associated with its parent. Similarly, a newly spawned ctl inherits the set of traps associated with its parent, except the set of traps is cleared upon an exec event.