2018-03-13 20:28:12 +03:00
/*
* This file is based on a patch submitted by Mark Wielaard < mjw @ redhat . com >
* to ltrace project :
* https : //anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=dfefa9f057857735a073ea655f5cb34351032c8e
*
* It was re - licensed for strace by the original author :
* https : //lists.strace.io/pipermail/strace-devel/2018-March/008063.html
*
* Copyright ( c ) 2014 - 2018 Mark Wielaard < mjw @ redhat . com >
* Copyright ( c ) 2018 Masatake YAMATO < yamato @ redhat . com >
* Copyright ( c ) 2018 The strace developers .
* All rights reserved .
*
2018-12-10 03:00:00 +03:00
* SPDX - License - Identifier : LGPL - 2.1 - or - later
2018-03-13 20:28:12 +03:00
*/
# include "defs.h"
# include "unwind.h"
2018-04-30 00:45:40 +03:00
# include "mmap_notify.h"
2018-03-13 20:28:12 +03:00
# include <elfutils/libdwfl.h>
2018-04-30 00:45:39 +03:00
struct ctx {
Dwfl * dwfl ;
2018-04-30 00:45:40 +03:00
unsigned int last_proc_updating ;
2018-04-30 00:45:39 +03:00
} ;
2018-04-30 00:45:40 +03:00
static unsigned int mapping_generation ;
static void
update_mapping_generation ( struct tcb * tcp , void * unused )
{
mapping_generation + + ;
}
static void
init ( void )
{
mmap_notify_register_client ( update_mapping_generation , NULL ) ;
}
2018-03-13 20:28:12 +03:00
static void *
tcb_init ( struct tcb * tcp )
{
static const Dwfl_Callbacks proc_callbacks = {
. find_elf = dwfl_linux_proc_find_elf ,
. find_debuginfo = dwfl_standard_find_debuginfo
} ;
Dwfl * dwfl = dwfl_begin ( & proc_callbacks ) ;
if ( dwfl = = NULL ) {
2018-04-19 21:03:58 +03:00
error_msg ( " dwfl_begin: %s " , dwfl_errmsg ( - 1 ) ) ;
2018-03-13 20:28:12 +03:00
return NULL ;
}
int r = dwfl_linux_proc_attach ( dwfl , tcp - > pid , true ) ;
if ( r ) {
const char * msg = NULL ;
if ( r < 0 )
msg = dwfl_errmsg ( - 1 ) ;
else if ( r > 0 )
msg = strerror ( r ) ;
error_msg ( " dwfl_linux_proc_attach returned an error "
2018-04-19 21:03:58 +03:00
" for process %d: %s " , tcp - > pid , msg ) ;
2018-03-13 20:28:12 +03:00
dwfl_end ( dwfl ) ;
return NULL ;
}
2018-04-30 00:45:39 +03:00
struct ctx * ctx = xmalloc ( sizeof ( * ctx ) ) ;
ctx - > dwfl = dwfl ;
2018-04-30 00:45:40 +03:00
ctx - > last_proc_updating = 0 ;
2018-04-30 00:45:39 +03:00
return ctx ;
2018-03-13 20:28:12 +03:00
}
static void
tcb_fin ( struct tcb * tcp )
{
2018-04-30 00:45:39 +03:00
struct ctx * ctx = tcp - > unwind_ctx ;
if ( ctx ) {
dwfl_end ( ctx - > dwfl ) ;
free ( ctx ) ;
}
2018-03-13 20:28:12 +03:00
}
2018-04-30 00:45:40 +03:00
static void
flush_cache_maybe ( struct tcb * tcp )
{
struct ctx * ctx = tcp - > unwind_ctx ;
if ( ! ctx )
return ;
if ( ctx - > last_proc_updating = = mapping_generation )
return ;
int r = dwfl_linux_proc_report ( ctx - > dwfl , tcp - > pid ) ;
if ( r < 0 )
error_msg ( " dwfl_linux_proc_report returned an error "
" for pid %d: %s " , tcp - > pid , dwfl_errmsg ( - 1 ) ) ;
else if ( r > 0 )
error_msg ( " dwfl_linux_proc_report returned an error "
" for pid %d " , tcp - > pid ) ;
else if ( dwfl_report_end ( ctx - > dwfl , NULL , NULL ) ! = 0 )
error_msg ( " dwfl_report_end returned an error "
" for pid %d: %s " , tcp - > pid , dwfl_errmsg ( - 1 ) ) ;
ctx - > last_proc_updating = mapping_generation ;
}
2018-03-13 20:28:12 +03:00
struct frame_user_data {
unwind_call_action_fn call_action ;
unwind_error_action_fn error_action ;
void * data ;
int stack_depth ;
} ;
static int
frame_callback ( Dwfl_Frame * state , void * arg )
{
struct frame_user_data * user_data = arg ;
Dwarf_Addr pc ;
bool isactivation ;
if ( ! dwfl_frame_pc ( state , & pc , & isactivation ) ) {
/* Propagate the error to the caller. */
return - 1 ;
}
if ( ! isactivation )
pc - - ;
Dwfl * dwfl = dwfl_thread_dwfl ( dwfl_frame_thread ( state ) ) ;
Dwfl_Module * mod = dwfl_addrmodule ( dwfl , pc ) ;
GElf_Off off = 0 ;
if ( mod ! = NULL ) {
const char * modname = NULL ;
const char * symname = NULL ;
GElf_Sym sym ;
Dwarf_Addr true_offset = pc ;
modname = dwfl_module_info ( mod , NULL , NULL , NULL , NULL ,
NULL , NULL , NULL ) ;
symname = dwfl_module_addrinfo ( mod , pc , & off , & sym ,
NULL , NULL , NULL ) ;
dwfl_module_relocate_address ( mod , & true_offset ) ;
user_data - > call_action ( user_data - > data , modname , symname ,
off , true_offset ) ;
}
/* Max number of frames to print reached? */
if ( user_data - > stack_depth - - = = 0 )
return DWARF_CB_ABORT ;
return DWARF_CB_OK ;
}
static void
tcb_walk ( struct tcb * tcp ,
unwind_call_action_fn call_action ,
unwind_error_action_fn error_action ,
void * data )
{
2018-04-30 00:45:39 +03:00
struct ctx * ctx = tcp - > unwind_ctx ;
if ( ! ctx )
2018-03-13 20:28:12 +03:00
return ;
struct frame_user_data user_data = {
. call_action = call_action ,
. error_action = error_action ,
. data = data ,
. stack_depth = 256 ,
} ;
2018-04-30 00:45:40 +03:00
flush_cache_maybe ( tcp ) ;
2018-04-30 00:45:39 +03:00
int r = dwfl_getthread_frames ( ctx - > dwfl , tcp - > pid , frame_callback ,
2018-03-13 20:28:12 +03:00
& user_data ) ;
if ( r )
error_action ( data ,
r < 0 ? dwfl_errmsg ( - 1 ) : " too many stack frames " ,
0 ) ;
}
const struct unwind_unwinder_t unwinder = {
. name = " libdw " ,
2018-04-30 00:45:40 +03:00
. init = init ,
2018-03-13 20:28:12 +03:00
. tcb_init = tcb_init ,
. tcb_fin = tcb_fin ,
. tcb_walk = tcb_walk ,
} ;