2009-10-09 01:17:38 +04:00
/*
* probe - finder . c : C expression to kprobe event converter
*
* Written by Masami Hiramatsu < mhiramat @ redhat . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*
*/
# include <sys/utsname.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <errno.h>
# include <stdio.h>
# include <unistd.h>
# include <getopt.h>
# include <stdlib.h>
# include <string.h>
# include <stdarg.h>
# include <ctype.h>
2009-10-17 04:08:01 +04:00
2009-10-17 04:08:10 +04:00
# include "event.h"
# include "debug.h"
2009-10-17 04:08:01 +04:00
# include "util.h"
2009-10-09 01:17:38 +04:00
# include "probe-finder.h"
/* Dwarf_Die Linkage to parent Die */
struct die_link {
struct die_link * parent ; /* Parent die */
Dwarf_Die die ; /* Current die */
} ;
static Dwarf_Debug __dw_debug ;
static Dwarf_Error __dw_error ;
/*
* Generic dwarf analysis helpers
*/
# define X86_32_MAX_REGS 8
const char * x86_32_regs_table [ X86_32_MAX_REGS ] = {
" %ax " ,
" %cx " ,
" %dx " ,
" %bx " ,
" $stack " , /* Stack address instead of %sp */
" %bp " ,
" %si " ,
" %di " ,
} ;
# define X86_64_MAX_REGS 16
const char * x86_64_regs_table [ X86_64_MAX_REGS ] = {
" %ax " ,
" %dx " ,
" %cx " ,
" %bx " ,
" %si " ,
" %di " ,
" %bp " ,
" %sp " ,
" %r8 " ,
" %r9 " ,
" %r10 " ,
" %r11 " ,
" %r12 " ,
" %r13 " ,
" %r14 " ,
" %r15 " ,
} ;
/* TODO: switching by dwarf address size */
# ifdef __x86_64__
# define ARCH_MAX_REGS X86_64_MAX_REGS
# define arch_regs_table x86_64_regs_table
# else
# define ARCH_MAX_REGS X86_32_MAX_REGS
# define arch_regs_table x86_32_regs_table
# endif
/* Return architecture dependent register string (for kprobe-tracer) */
static const char * get_arch_regstr ( unsigned int n )
{
return ( n < = ARCH_MAX_REGS ) ? arch_regs_table [ n ] : NULL ;
}
/*
* Compare the tail of two strings .
* Return 0 if whole of either string is same as another ' s tail part .
*/
static int strtailcmp ( const char * s1 , const char * s2 )
{
int i1 = strlen ( s1 ) ;
int i2 = strlen ( s2 ) ;
2009-12-07 20:00:40 +03:00
while ( - - i1 > = 0 & & - - i2 > = 0 ) {
2009-10-09 01:17:38 +04:00
if ( s1 [ i1 ] ! = s2 [ i2 ] )
return s1 [ i1 ] - s2 [ i2 ] ;
}
return 0 ;
}
/* Find the fileno of the target file. */
2009-10-27 23:43:19 +03:00
static Dwarf_Unsigned cu_find_fileno ( Dwarf_Die cu_die , const char * fname )
2009-10-09 01:17:38 +04:00
{
Dwarf_Signed cnt , i ;
Dwarf_Unsigned found = 0 ;
char * * srcs ;
int ret ;
if ( ! fname )
return 0 ;
ret = dwarf_srcfiles ( cu_die , & srcs , & cnt , & __dw_error ) ;
if ( ret = = DW_DLV_OK ) {
for ( i = 0 ; i < cnt & & ! found ; i + + ) {
if ( strtailcmp ( srcs [ i ] , fname ) = = 0 )
found = i + 1 ;
dwarf_dealloc ( __dw_debug , srcs [ i ] , DW_DLA_STRING ) ;
}
for ( ; i < cnt ; i + + )
dwarf_dealloc ( __dw_debug , srcs [ i ] , DW_DLA_STRING ) ;
dwarf_dealloc ( __dw_debug , srcs , DW_DLA_LIST ) ;
}
if ( found )
2009-10-21 23:34:06 +04:00
pr_debug ( " found fno: %d \n " , ( int ) found ) ;
2009-10-09 01:17:38 +04:00
return found ;
}
/* Compare diename and tname */
2009-10-17 04:08:01 +04:00
static int die_compare_name ( Dwarf_Die dw_die , const char * tname )
2009-10-09 01:17:38 +04:00
{
char * name ;
int ret ;
2009-10-17 04:08:01 +04:00
ret = dwarf_diename ( dw_die , & name , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( ret = = DW_DLV_OK ) {
ret = strcmp ( tname , name ) ;
dwarf_dealloc ( __dw_debug , name , DW_DLA_STRING ) ;
} else
ret = - 1 ;
return ret ;
}
/* Check the address is in the subprogram(function). */
static int die_within_subprogram ( Dwarf_Die sp_die , Dwarf_Addr addr ,
Dwarf_Signed * offs )
{
Dwarf_Addr lopc , hipc ;
int ret ;
/* TODO: check ranges */
ret = dwarf_lowpc ( sp_die , & lopc , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( ret = = DW_DLV_NO_ENTRY )
return 0 ;
ret = dwarf_highpc ( sp_die , & hipc , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
if ( lopc < = addr & & addr < hipc ) {
* offs = addr - lopc ;
return 1 ;
} else
return 0 ;
}
/* Check the die is inlined function */
2009-10-17 04:08:01 +04:00
static Dwarf_Bool die_inlined_subprogram ( Dwarf_Die dw_die )
2009-10-09 01:17:38 +04:00
{
/* TODO: check strictly */
Dwarf_Bool inl ;
int ret ;
2009-10-17 04:08:01 +04:00
ret = dwarf_hasattr ( dw_die , DW_AT_inline , & inl , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
return inl ;
}
/* Get the offset of abstruct_origin */
2009-10-17 04:08:01 +04:00
static Dwarf_Off die_get_abstract_origin ( Dwarf_Die dw_die )
2009-10-09 01:17:38 +04:00
{
Dwarf_Attribute attr ;
Dwarf_Off cu_offs ;
int ret ;
2009-10-17 04:08:01 +04:00
ret = dwarf_attr ( dw_die , DW_AT_abstract_origin , & attr , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
ret = dwarf_formref ( attr , & cu_offs , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
dwarf_dealloc ( __dw_debug , attr , DW_DLA_ATTR ) ;
return cu_offs ;
}
/* Get entry pc(or low pc, 1st entry of ranges) of the die */
2009-10-17 04:08:01 +04:00
static Dwarf_Addr die_get_entrypc ( Dwarf_Die dw_die )
2009-10-09 01:17:38 +04:00
{
Dwarf_Attribute attr ;
Dwarf_Addr addr ;
Dwarf_Off offs ;
Dwarf_Ranges * ranges ;
Dwarf_Signed cnt ;
int ret ;
/* Try to get entry pc */
2009-10-17 04:08:01 +04:00
ret = dwarf_attr ( dw_die , DW_AT_entry_pc , & attr , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( ret = = DW_DLV_OK ) {
ret = dwarf_formaddr ( attr , & addr , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
dwarf_dealloc ( __dw_debug , attr , DW_DLA_ATTR ) ;
return addr ;
}
/* Try to get low pc */
2009-10-17 04:08:01 +04:00
ret = dwarf_lowpc ( dw_die , & addr , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( ret = = DW_DLV_OK )
return addr ;
/* Try to get ranges */
2009-10-17 04:08:01 +04:00
ret = dwarf_attr ( dw_die , DW_AT_ranges , & attr , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
ret = dwarf_formref ( attr , & offs , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
ret = dwarf_get_ranges ( __dw_debug , offs , & ranges , & cnt , NULL ,
& __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
addr = ranges [ 0 ] . dwr_addr1 ;
dwarf_ranges_dealloc ( __dw_debug , ranges , cnt ) ;
return addr ;
}
/*
* Search a Die from Die tree .
* Note : cur_link - > die should be deallocated in this function .
*/
static int __search_die_tree ( struct die_link * cur_link ,
int ( * die_cb ) ( struct die_link * , void * ) ,
void * data )
{
Dwarf_Die new_die ;
struct die_link new_link ;
int ret ;
if ( ! die_cb )
return 0 ;
/* Check current die */
while ( ! ( ret = die_cb ( cur_link , data ) ) ) {
/* Check child die */
ret = dwarf_child ( cur_link - > die , & new_die , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( ret = = DW_DLV_OK ) {
new_link . parent = cur_link ;
new_link . die = new_die ;
ret = __search_die_tree ( & new_link , die_cb , data ) ;
if ( ret )
break ;
}
/* Move to next sibling */
ret = dwarf_siblingof ( __dw_debug , cur_link - > die , & new_die ,
& __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
dwarf_dealloc ( __dw_debug , cur_link - > die , DW_DLA_DIE ) ;
cur_link - > die = new_die ;
if ( ret = = DW_DLV_NO_ENTRY )
return 0 ;
}
dwarf_dealloc ( __dw_debug , cur_link - > die , DW_DLA_DIE ) ;
return ret ;
}
/* Search a die in its children's die tree */
static int search_die_from_children ( Dwarf_Die parent_die ,
int ( * die_cb ) ( struct die_link * , void * ) ,
void * data )
{
struct die_link new_link ;
int ret ;
new_link . parent = NULL ;
ret = dwarf_child ( parent_die , & new_link . die , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( ret = = DW_DLV_OK )
return __search_die_tree ( & new_link , die_cb , data ) ;
else
return 0 ;
}
/* Find a locdesc corresponding to the address */
static int attr_get_locdesc ( Dwarf_Attribute attr , Dwarf_Locdesc * desc ,
Dwarf_Addr addr )
{
Dwarf_Signed lcnt ;
Dwarf_Locdesc * * llbuf ;
int ret , i ;
ret = dwarf_loclist_n ( attr , & llbuf , & lcnt , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
ret = DW_DLV_NO_ENTRY ;
for ( i = 0 ; i < lcnt ; + + i ) {
if ( llbuf [ i ] - > ld_lopc < = addr & &
llbuf [ i ] - > ld_hipc > addr ) {
memcpy ( desc , llbuf [ i ] , sizeof ( Dwarf_Locdesc ) ) ;
desc - > ld_s =
malloc ( sizeof ( Dwarf_Loc ) * llbuf [ i ] - > ld_cents ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( desc - > ld_s = = NULL ) ;
2009-10-09 01:17:38 +04:00
memcpy ( desc - > ld_s , llbuf [ i ] - > ld_s ,
sizeof ( Dwarf_Loc ) * llbuf [ i ] - > ld_cents ) ;
ret = DW_DLV_OK ;
break ;
}
dwarf_dealloc ( __dw_debug , llbuf [ i ] - > ld_s , DW_DLA_LOC_BLOCK ) ;
dwarf_dealloc ( __dw_debug , llbuf [ i ] , DW_DLA_LOCDESC ) ;
}
/* Releasing loop */
for ( ; i < lcnt ; + + i ) {
dwarf_dealloc ( __dw_debug , llbuf [ i ] - > ld_s , DW_DLA_LOC_BLOCK ) ;
dwarf_dealloc ( __dw_debug , llbuf [ i ] , DW_DLA_LOCDESC ) ;
}
dwarf_dealloc ( __dw_debug , llbuf , DW_DLA_LIST ) ;
return ret ;
}
2009-10-27 23:43:19 +03:00
/* Get decl_file attribute value (file number) */
static Dwarf_Unsigned die_get_decl_file ( Dwarf_Die sp_die )
{
Dwarf_Attribute attr ;
Dwarf_Unsigned fno ;
int ret ;
ret = dwarf_attr ( sp_die , DW_AT_decl_file , & attr , & __dw_error ) ;
DIE_IF ( ret ! = DW_DLV_OK ) ;
dwarf_formudata ( attr , & fno , & __dw_error ) ;
DIE_IF ( ret ! = DW_DLV_OK ) ;
dwarf_dealloc ( __dw_debug , attr , DW_DLA_ATTR ) ;
return fno ;
}
/* Get decl_line attribute value (line number) */
static Dwarf_Unsigned die_get_decl_line ( Dwarf_Die sp_die )
{
Dwarf_Attribute attr ;
Dwarf_Unsigned lno ;
int ret ;
ret = dwarf_attr ( sp_die , DW_AT_decl_line , & attr , & __dw_error ) ;
DIE_IF ( ret ! = DW_DLV_OK ) ;
dwarf_formudata ( attr , & lno , & __dw_error ) ;
DIE_IF ( ret ! = DW_DLV_OK ) ;
dwarf_dealloc ( __dw_debug , attr , DW_DLA_ATTR ) ;
return lno ;
}
2009-10-09 01:17:38 +04:00
/*
* Probe finder related functions
*/
/* Show a location */
static void show_location ( Dwarf_Loc * loc , struct probe_finder * pf )
{
Dwarf_Small op ;
Dwarf_Unsigned regn ;
Dwarf_Signed offs ;
int deref = 0 , ret ;
const char * regs ;
op = loc - > lr_atom ;
/* If this is based on frame buffer, set the offset */
if ( op = = DW_OP_fbreg ) {
deref = 1 ;
offs = ( Dwarf_Signed ) loc - > lr_number ;
op = pf - > fbloc . ld_s [ 0 ] . lr_atom ;
loc = & pf - > fbloc . ld_s [ 0 ] ;
} else
offs = 0 ;
if ( op > = DW_OP_breg0 & & op < = DW_OP_breg31 ) {
regn = op - DW_OP_breg0 ;
offs + = ( Dwarf_Signed ) loc - > lr_number ;
deref = 1 ;
} else if ( op > = DW_OP_reg0 & & op < = DW_OP_reg31 ) {
regn = op - DW_OP_reg0 ;
} else if ( op = = DW_OP_bregx ) {
regn = loc - > lr_number ;
offs + = ( Dwarf_Signed ) loc - > lr_number2 ;
deref = 1 ;
} else if ( op = = DW_OP_regx ) {
regn = loc - > lr_number ;
} else
2010-01-06 01:47:03 +03:00
die ( " Dwarf_OP %d is not supported. " , op ) ;
2009-10-09 01:17:38 +04:00
regs = get_arch_regstr ( regn ) ;
if ( ! regs )
2010-01-06 01:47:03 +03:00
die ( " %lld exceeds max register number. " , regn ) ;
2009-10-09 01:17:38 +04:00
if ( deref )
ret = snprintf ( pf - > buf , pf - > len ,
" %s=%+lld(%s) " , pf - > var , offs , regs ) ;
else
ret = snprintf ( pf - > buf , pf - > len , " %s=%s " , pf - > var , regs ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret < 0 ) ;
DIE_IF ( ret > = pf - > len ) ;
2009-10-09 01:17:38 +04:00
}
/* Show a variables in kprobe event format */
static void show_variable ( Dwarf_Die vr_die , struct probe_finder * pf )
{
Dwarf_Attribute attr ;
Dwarf_Locdesc ld ;
int ret ;
ret = dwarf_attr ( vr_die , DW_AT_location , & attr , & __dw_error ) ;
if ( ret ! = DW_DLV_OK )
goto error ;
ret = attr_get_locdesc ( attr , & ld , ( pf - > addr - pf - > cu_base ) ) ;
if ( ret ! = DW_DLV_OK )
goto error ;
/* TODO? */
2009-10-17 04:08:18 +04:00
DIE_IF ( ld . ld_cents ! = 1 ) ;
2009-10-09 01:17:38 +04:00
show_location ( & ld . ld_s [ 0 ] , pf ) ;
free ( ld . ld_s ) ;
dwarf_dealloc ( __dw_debug , attr , DW_DLA_ATTR ) ;
return ;
error :
2009-10-17 04:08:01 +04:00
die ( " Failed to find the location of %s at this address. \n "
2010-01-06 01:47:03 +03:00
" Perhaps, it has been optimized out. " , pf - > var ) ;
2009-10-09 01:17:38 +04:00
}
static int variable_callback ( struct die_link * dlink , void * data )
{
struct probe_finder * pf = ( struct probe_finder * ) data ;
Dwarf_Half tag ;
int ret ;
ret = dwarf_tag ( dlink - > die , & tag , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( ( tag = = DW_TAG_formal_parameter | |
tag = = DW_TAG_variable ) & &
( die_compare_name ( dlink - > die , pf - > var ) = = 0 ) ) {
show_variable ( dlink - > die , pf ) ;
return 1 ;
}
/* TODO: Support struct members and arrays */
return 0 ;
}
/* Find a variable in a subprogram die */
static void find_variable ( Dwarf_Die sp_die , struct probe_finder * pf )
{
int ret ;
if ( ! is_c_varname ( pf - > var ) ) {
/* Output raw parameters */
ret = snprintf ( pf - > buf , pf - > len , " %s " , pf - > var ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret < 0 ) ;
DIE_IF ( ret > = pf - > len ) ;
2009-10-09 01:17:38 +04:00
return ;
}
2009-10-21 23:34:06 +04:00
pr_debug ( " Searching '%s' variable in context. \n " , pf - > var ) ;
2009-10-09 01:17:38 +04:00
/* Search child die for local variables and parameters. */
ret = search_die_from_children ( sp_die , variable_callback , pf ) ;
if ( ! ret )
2010-01-06 01:47:03 +03:00
die ( " Failed to find '%s' in this function. " , pf - > var ) ;
2009-10-09 01:17:38 +04:00
}
/* Get a frame base on the address */
static void get_current_frame_base ( Dwarf_Die sp_die , struct probe_finder * pf )
{
Dwarf_Attribute attr ;
int ret ;
ret = dwarf_attr ( sp_die , DW_AT_frame_base , & attr , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
ret = attr_get_locdesc ( attr , & pf - > fbloc , ( pf - > addr - pf - > cu_base ) ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
dwarf_dealloc ( __dw_debug , attr , DW_DLA_ATTR ) ;
}
static void free_current_frame_base ( struct probe_finder * pf )
{
free ( pf - > fbloc . ld_s ) ;
memset ( & pf - > fbloc , 0 , sizeof ( Dwarf_Locdesc ) ) ;
}
/* Show a probe point to output buffer */
static void show_probepoint ( Dwarf_Die sp_die , Dwarf_Signed offs ,
struct probe_finder * pf )
{
struct probe_point * pp = pf - > pp ;
char * name ;
char tmp [ MAX_PROBE_BUFFER ] ;
int ret , i , len ;
/* Output name of probe point */
ret = dwarf_diename ( sp_die , & name , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( ret = = DW_DLV_OK ) {
ret = snprintf ( tmp , MAX_PROBE_BUFFER , " %s+%u " , name ,
( unsigned int ) offs ) ;
2009-10-27 23:43:10 +03:00
/* Copy the function name if possible */
if ( ! pp - > function ) {
pp - > function = strdup ( name ) ;
pp - > offset = offs ;
}
2009-10-09 01:17:38 +04:00
dwarf_dealloc ( __dw_debug , name , DW_DLA_STRING ) ;
} else {
/* This function has no name. */
ret = snprintf ( tmp , MAX_PROBE_BUFFER , " 0x%llx " , pf - > addr ) ;
2009-10-27 23:43:10 +03:00
if ( ! pp - > function ) {
/* TODO: Use _stext */
pp - > function = strdup ( " " ) ;
pp - > offset = ( int ) pf - > addr ;
}
2009-10-09 01:17:38 +04:00
}
2009-10-17 04:08:18 +04:00
DIE_IF ( ret < 0 ) ;
DIE_IF ( ret > = MAX_PROBE_BUFFER ) ;
2009-10-09 01:17:38 +04:00
len = ret ;
2009-10-27 23:43:19 +03:00
pr_debug ( " Probe point found: %s \n " , tmp ) ;
2009-10-09 01:17:38 +04:00
/* Find each argument */
get_current_frame_base ( sp_die , pf ) ;
for ( i = 0 ; i < pp - > nr_args ; i + + ) {
pf - > var = pp - > args [ i ] ;
pf - > buf = & tmp [ len ] ;
pf - > len = MAX_PROBE_BUFFER - len ;
find_variable ( sp_die , pf ) ;
len + = strlen ( pf - > buf ) ;
}
free_current_frame_base ( pf ) ;
pp - > probes [ pp - > found ] = strdup ( tmp ) ;
pp - > found + + ;
}
static int probeaddr_callback ( struct die_link * dlink , void * data )
{
struct probe_finder * pf = ( struct probe_finder * ) data ;
Dwarf_Half tag ;
Dwarf_Signed offs ;
int ret ;
ret = dwarf_tag ( dlink - > die , & tag , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
/* Check the address is in this subprogram */
if ( tag = = DW_TAG_subprogram & &
die_within_subprogram ( dlink - > die , pf - > addr , & offs ) ) {
show_probepoint ( dlink - > die , offs , pf ) ;
return 1 ;
}
return 0 ;
}
/* Find probe point from its line number */
2009-10-27 23:43:19 +03:00
static void find_by_line ( struct probe_finder * pf )
2009-10-09 01:17:38 +04:00
{
2009-10-27 23:43:19 +03:00
Dwarf_Signed cnt , i , clm ;
2009-10-09 01:17:38 +04:00
Dwarf_Line * lines ;
Dwarf_Unsigned lineno = 0 ;
Dwarf_Addr addr ;
Dwarf_Unsigned fno ;
int ret ;
2009-10-27 23:43:19 +03:00
ret = dwarf_srclines ( pf - > cu_die , & lines , & cnt , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
for ( i = 0 ; i < cnt ; i + + ) {
ret = dwarf_line_srcfileno ( lines [ i ] , & fno , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
if ( fno ! = pf - > fno )
continue ;
ret = dwarf_lineno ( lines [ i ] , & lineno , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-27 23:43:19 +03:00
if ( lineno ! = pf - > lno )
2009-10-09 01:17:38 +04:00
continue ;
2009-10-27 23:43:19 +03:00
ret = dwarf_lineoff ( lines [ i ] , & clm , & __dw_error ) ;
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
ret = dwarf_lineaddr ( lines [ i ] , & addr , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-27 23:43:19 +03:00
pr_debug ( " Probe line found: line[%d]:%u,%d addr:0x%llx \n " ,
( int ) i , ( unsigned ) lineno , ( int ) clm , addr ) ;
2009-10-09 01:17:38 +04:00
pf - > addr = addr ;
/* Search a real subprogram including this line, */
2009-10-27 23:43:19 +03:00
ret = search_die_from_children ( pf - > cu_die ,
probeaddr_callback , pf ) ;
2009-10-09 01:17:38 +04:00
if ( ret = = 0 )
2010-01-06 01:47:03 +03:00
die ( " Probe point is not found in subprograms. " ) ;
2009-10-09 01:17:38 +04:00
/* Continuing, because target line might be inlined. */
}
dwarf_srclines_dealloc ( __dw_debug , lines , cnt ) ;
}
/* Search function from function name */
static int probefunc_callback ( struct die_link * dlink , void * data )
{
struct probe_finder * pf = ( struct probe_finder * ) data ;
struct probe_point * pp = pf - > pp ;
struct die_link * lk ;
Dwarf_Signed offs ;
Dwarf_Half tag ;
int ret ;
ret = dwarf_tag ( dlink - > die , & tag , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( tag = = DW_TAG_subprogram ) {
if ( die_compare_name ( dlink - > die , pp - > function ) = = 0 ) {
2009-10-27 23:43:19 +03:00
if ( pp - > line ) { /* Function relative line */
pf - > fno = die_get_decl_file ( dlink - > die ) ;
pf - > lno = die_get_decl_line ( dlink - > die )
+ pp - > line ;
find_by_line ( pf ) ;
return 1 ;
}
2009-10-09 01:17:38 +04:00
if ( die_inlined_subprogram ( dlink - > die ) ) {
/* Inlined function, save it. */
ret = dwarf_die_CU_offset ( dlink - > die ,
& pf - > inl_offs ,
& __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-21 23:34:06 +04:00
pr_debug ( " inline definition offset %lld \n " ,
pf - > inl_offs ) ;
2009-10-27 23:42:53 +03:00
return 0 ; /* Continue to search */
2009-10-09 01:17:38 +04:00
}
/* Get probe address */
pf - > addr = die_get_entrypc ( dlink - > die ) ;
pf - > addr + = pp - > offset ;
/* TODO: Check the address in this function */
show_probepoint ( dlink - > die , pp - > offset , pf ) ;
2009-10-27 23:42:53 +03:00
return 1 ; /* Exit; no same symbol in this CU. */
2009-10-09 01:17:38 +04:00
}
} else if ( tag = = DW_TAG_inlined_subroutine & & pf - > inl_offs ) {
if ( die_get_abstract_origin ( dlink - > die ) = = pf - > inl_offs ) {
/* Get probe address */
pf - > addr = die_get_entrypc ( dlink - > die ) ;
pf - > addr + = pp - > offset ;
2009-10-21 23:34:06 +04:00
pr_debug ( " found inline addr: 0x%llx \n " , pf - > addr ) ;
2009-10-09 01:17:38 +04:00
/* Inlined function. Get a real subprogram */
for ( lk = dlink - > parent ; lk ! = NULL ; lk = lk - > parent ) {
tag = 0 ;
dwarf_tag ( lk - > die , & tag , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( tag = = DW_TAG_subprogram & &
! die_inlined_subprogram ( lk - > die ) )
goto found ;
}
2010-01-06 01:47:03 +03:00
die ( " Failed to find real subprogram. " ) ;
2009-10-09 01:17:38 +04:00
found :
/* Get offset from subprogram */
ret = die_within_subprogram ( lk - > die , pf - > addr , & offs ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ! ret ) ;
2009-10-09 01:17:38 +04:00
show_probepoint ( lk - > die , offs , pf ) ;
/* Continue to search */
}
}
return 0 ;
}
2009-10-27 23:43:19 +03:00
static void find_by_func ( struct probe_finder * pf )
2009-10-09 01:17:38 +04:00
{
2009-10-27 23:43:19 +03:00
search_die_from_children ( pf - > cu_die , probefunc_callback , pf ) ;
2009-10-09 01:17:38 +04:00
}
/* Find a probe point */
int find_probepoint ( int fd , struct probe_point * pp )
{
Dwarf_Half addr_size = 0 ;
Dwarf_Unsigned next_cuh = 0 ;
int cu_number = 0 , ret ;
struct probe_finder pf = { . pp = pp } ;
ret = dwarf_init ( fd , DW_DLC_READ , 0 , 0 , & __dw_debug , & __dw_error ) ;
2009-12-15 18:31:35 +03:00
if ( ret ! = DW_DLV_OK )
2009-11-04 03:12:30 +03:00
return - ENOENT ;
2009-10-09 01:17:38 +04:00
pp - > found = 0 ;
while ( + + cu_number ) {
/* Search CU (Compilation Unit) */
ret = dwarf_next_cu_header ( __dw_debug , NULL , NULL , NULL ,
& addr_size , & next_cuh , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( ret = = DW_DLV_NO_ENTRY )
break ;
/* Get the DIE(Debugging Information Entry) of this CU */
2009-10-27 23:43:19 +03:00
ret = dwarf_siblingof ( __dw_debug , 0 , & pf . cu_die , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
/* Check if target file is included. */
if ( pp - > file )
2009-10-27 23:43:19 +03:00
pf . fno = cu_find_fileno ( pf . cu_die , pp - > file ) ;
2009-10-09 01:17:38 +04:00
if ( ! pp - > file | | pf . fno ) {
/* Save CU base address (for frame_base) */
2009-10-27 23:43:19 +03:00
ret = dwarf_lowpc ( pf . cu_die , & pf . cu_base , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret = = DW_DLV_ERROR ) ;
2009-10-09 01:17:38 +04:00
if ( ret = = DW_DLV_NO_ENTRY )
pf . cu_base = 0 ;
if ( pp - > function )
2009-10-27 23:43:19 +03:00
find_by_func ( & pf ) ;
else {
pf . lno = pp - > line ;
find_by_line ( & pf ) ;
}
2009-10-09 01:17:38 +04:00
}
2009-10-27 23:43:19 +03:00
dwarf_dealloc ( __dw_debug , pf . cu_die , DW_DLA_DIE ) ;
2009-10-09 01:17:38 +04:00
}
ret = dwarf_finish ( __dw_debug , & __dw_error ) ;
2009-10-17 04:08:18 +04:00
DIE_IF ( ret ! = DW_DLV_OK ) ;
2009-10-09 01:17:38 +04:00
return pp - > found ;
}