On Thu, 2013-06-13 at 00:03 +0200, Mark Wielaard wrote:
What do you think? I am struggling a bit with this because I can see
the
user might want both (full freeze on begin or minimal thread freeze at
use only).
What about this interface? Besides the dwarf_report_begin point, there
are explicit actions for, attaching "state", iterating through threads,
starting and ending a frame unwind. That seems to give the most
flexibility. You can attach/freeze at any point, when starting to report
Dwfl modules, when attaching the "state", when iterating through the
threads or only when starting an unwind.
We can then provide an implementation that does attach on report
dwfl_linux_proc_report_state (Dwfl *dwfl, pid_t pid) or one that only
attaches on the start of a frame unwind (and detaches after the unwind
finishes). That one could just be the existing dwfl_linux_proc_report
(Dwfl *dwfl, pid_t pid) because it would only differ to the user when
the other new interfaces are used. For core files we could just make
dwfl_core_file_report (Dwfl *dwfl, Elf *elf) do the right thing.
So on the "backend" side the interface could be:
typedef struct Dwfl_Thread;
typedef struct
{
/* Called to iterate through threads. Starting with previous *ptid
set to -1. Returns next thread id on success, a negative number on
failure and zero if there are no more threads. Should call
dwfl_thread_state () on nthread if thread can be unwound with
initial register and memory accessors. */
pid_t (*next_thread) (Dwfl *dwfl, pid_t pid, pid_t *ptid,
Dwfl_Thread *nthread, void *arg);
/* Called by dwfl_end (). */
void (*detach) (Dwfl *dwfl, pid_t pid, void *arg);
} Dwfl_State_Callbacks;
/* pid is the process id associated with the Dwfl state, arg is the
callback backend state. Both will be provided to the callbacks. */
dwfl_attach_state (Dwfl *dwfl, pid_t pid,
Dwfl_State_Callbacks *state, void *arg);
/* probably needed... */
Dwfl *dwfl_thread_dwfl (Dwfl_Thread *thread);
pid_t dwfl_thread_pid (Dwfl_Thread *thread);
typedef struct
{
/* Called on initial unwind to get the initial register state of the
first frame. Should call dwfl_thread_state_registers (), possibly
multiple times for different ranges, to fill in initial (DWARF)
register values. After this call, till at least thread_detach is
called, the thread is assumed to be frozen, so that it is safe to
unwind. Returns true on success or false and sets dwfl_err() on
failure. */
bool (*set_initial_registers) (Dwfl_Thread *thread, pid_t tid,
void *arg);
/* Called during unwinding to access memory (stack) state. */
bool (*memory_read) (Dwfl_Thread *thread, pid_t tid,
Dwarf_Addr addr, Dwarf_Word *result,
void *arg);
/* Called when unwinding is done. No callback will be called after
this has been called. */
void (*thread_detach) (Dwfl_Thread *thread, pid_t tid, void *arg);
} Dwfl_Thread_Callbacks;
dwfl_thread_state (Dwfl_Thread *thread, Dwfl_Thread_Callbacks *state);
pid_t dwfl_thread_tid (Dwfl_Thread *thread);
dwfl_thread_state_registers (Dwfl_Thread *thread,
const int firstreg,
unsigned nregs,
const Dwarf_Word *regs);
Then (the existing?) dwfl_linux_proc_report and dwfl_core_file_report
could setup the above callbacks. Or they could be setup by someone by
hand if they can provide registers and memory accessors in some other
way.
For the user/usage side we could then have:
/* Gets the next known thread, if any. To get the initial thread
provide NULL as previous thread. */
Dwfl_Thread *dwfl_next_thread (Dwfl *dwfl, Dwfl_Thread *pthread);
/* Iterate through the frames for a thread. Returns zero if all frames
have been processed by the callback, returns -1 on error, or the
value of the callback when not DWARF_CB_OK. Keeps calling the
callback with the next frame while the callback returns
DWARF_CB_OK, till there are no more frames. On start will call the
set_initial_registers callback and on return will call the
detach_thread callback of the Dwfl_Thread. */
int dwfl_getdwarf (Dwfl_Thread *thread,
int (*callback) (Dwfl_Frame *, void *),
void *arg);
bool dwfl_frame_state_pc (Dwfl_Frame_State *state, Dwarf_Addr *pc,
bool *isactivation)
Note the above is just a first suggestion, not even tried to compile
yet. But unless I forgot something I think this is flexible enough to
support different backends and policies for "freezing" a process.
Cheers,
Mark