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>
2010-04-20 10:58:32 +04:00
# include <dwarf-regs.h>
2009-10-17 04:08:01 +04:00
2010-02-25 16:36:12 +03:00
# include "string.h"
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"
2010-04-12 21:17:15 +04:00
/* Kprobe tracer basic type is up to u64 */
# define MAX_BASIC_TYPE_BITS 64
2009-10-09 01:17:38 +04:00
/*
* 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 ;
}
2010-02-25 16:36:12 +03:00
/* Line number list operations */
/* Add a line to line number list */
2010-04-15 02:39:42 +04:00
static int line_list__add_line ( struct list_head * head , int line )
2010-02-25 16:36:12 +03:00
{
struct line_node * ln ;
struct list_head * p ;
/* Reverse search, because new line will be the last one */
list_for_each_entry_reverse ( ln , head , list ) {
if ( ln - > line < line ) {
p = & ln - > list ;
goto found ;
} else if ( ln - > line = = line ) /* Already exist */
2010-04-12 21:17:49 +04:00
return 1 ;
2010-02-25 16:36:12 +03:00
}
/* List is empty, or the smallest entry */
p = head ;
found :
pr_debug ( " line list: add a line %u \n " , line ) ;
2010-04-12 21:17:49 +04:00
ln = zalloc ( sizeof ( struct line_node ) ) ;
if ( ln = = NULL )
return - ENOMEM ;
2010-02-25 16:36:12 +03:00
ln - > line = line ;
INIT_LIST_HEAD ( & ln - > list ) ;
list_add ( & ln - > list , p ) ;
2010-04-12 21:17:49 +04:00
return 0 ;
2010-02-25 16:36:12 +03:00
}
/* Check if the line in line number list */
2010-04-15 02:39:42 +04:00
static int line_list__has_line ( struct list_head * head , int line )
2010-02-25 16:36:12 +03:00
{
struct line_node * ln ;
/* Reverse search, because new line will be the last one */
list_for_each_entry ( ln , head , list )
if ( ln - > line = = line )
return 1 ;
return 0 ;
}
/* Init line number list */
static void line_list__init ( struct list_head * head )
{
INIT_LIST_HEAD ( head ) ;
}
/* Free line number list */
static void line_list__free ( struct list_head * head )
{
struct line_node * ln ;
while ( ! list_empty ( head ) ) {
ln = list_first_entry ( head , struct line_node , list ) ;
list_del ( & ln - > list ) ;
free ( ln ) ;
}
}
/* Dwarf wrappers */
/* Find the realpath of the target file. */
static const char * cu_find_realpath ( Dwarf_Die * cu_die , const char * fname )
2009-10-09 01:17:38 +04:00
{
2010-02-25 16:35:42 +03:00
Dwarf_Files * files ;
size_t nfiles , i ;
2010-03-05 18:51:04 +03:00
const char * src = NULL ;
2009-10-09 01:17:38 +04:00
int ret ;
if ( ! fname )
2010-02-25 16:36:12 +03:00
return NULL ;
2009-10-09 01:17:38 +04:00
2010-02-25 16:35:42 +03:00
ret = dwarf_getsrcfiles ( cu_die , & files , & nfiles ) ;
2010-02-25 16:36:12 +03:00
if ( ret ! = 0 )
return NULL ;
for ( i = 0 ; i < nfiles ; i + + ) {
src = dwarf_filesrc ( files , i , NULL , NULL ) ;
if ( strtailcmp ( src , fname ) = = 0 )
break ;
2009-10-09 01:17:38 +04:00
}
2010-04-02 20:50:45 +04:00
if ( i = = nfiles )
return NULL ;
2010-02-25 16:36:12 +03:00
return src ;
2009-10-09 01:17:38 +04:00
}
2010-03-17 01:05:58 +03:00
/* Compare diename and tname */
static bool die_compare_name ( Dwarf_Die * dw_die , const char * tname )
{
const char * name ;
name = dwarf_diename ( dw_die ) ;
2010-04-12 21:17:35 +04:00
return name ? strcmp ( tname , name ) : - 1 ;
2010-03-17 01:05:58 +03:00
}
2010-03-17 01:06:26 +03:00
/* Get type die, but skip qualifiers and typedef */
static Dwarf_Die * die_get_real_type ( Dwarf_Die * vr_die , Dwarf_Die * die_mem )
{
Dwarf_Attribute attr ;
int tag ;
do {
if ( dwarf_attr ( vr_die , DW_AT_type , & attr ) = = NULL | |
dwarf_formref_die ( & attr , die_mem ) = = NULL )
return NULL ;
tag = dwarf_tag ( die_mem ) ;
vr_die = die_mem ;
} while ( tag = = DW_TAG_const_type | |
tag = = DW_TAG_restrict_type | |
tag = = DW_TAG_volatile_type | |
tag = = DW_TAG_shared_type | |
tag = = DW_TAG_typedef ) ;
return die_mem ;
}
2010-04-12 21:17:15 +04:00
static bool die_is_signed_type ( Dwarf_Die * tp_die )
{
Dwarf_Attribute attr ;
Dwarf_Word ret ;
if ( dwarf_attr ( tp_die , DW_AT_encoding , & attr ) = = NULL | |
dwarf_formudata ( & attr , & ret ) ! = 0 )
return false ;
return ( ret = = DW_ATE_signed_char | | ret = = DW_ATE_signed | |
ret = = DW_ATE_signed_fixed ) ;
}
static int die_get_byte_size ( Dwarf_Die * tp_die )
{
Dwarf_Attribute attr ;
Dwarf_Word ret ;
if ( dwarf_attr ( tp_die , DW_AT_byte_size , & attr ) = = NULL | |
dwarf_formudata ( & attr , & ret ) ! = 0 )
return 0 ;
return ( int ) ret ;
}
2010-04-15 00:44:00 +04:00
/* Get data_member_location offset */
static int die_get_data_member_location ( Dwarf_Die * mb_die , Dwarf_Word * offs )
{
Dwarf_Attribute attr ;
Dwarf_Op * expr ;
size_t nexpr ;
int ret ;
if ( dwarf_attr ( mb_die , DW_AT_data_member_location , & attr ) = = NULL )
return - ENOENT ;
if ( dwarf_formudata ( & attr , offs ) ! = 0 ) {
/* DW_AT_data_member_location should be DW_OP_plus_uconst */
ret = dwarf_getlocation ( & attr , & expr , & nexpr ) ;
if ( ret < 0 | | nexpr = = 0 )
return - ENOENT ;
if ( expr [ 0 ] . atom ! = DW_OP_plus_uconst | | nexpr ! = 1 ) {
pr_debug ( " Unable to get offset:Unexpected OP %x (%zd) \n " ,
expr [ 0 ] . atom , nexpr ) ;
return - ENOTSUP ;
}
* offs = ( Dwarf_Word ) expr [ 0 ] . number ;
}
return 0 ;
}
2010-03-17 01:05:58 +03:00
/* Return values for die_find callbacks */
enum {
DIE_FIND_CB_FOUND = 0 , /* End of Search */
DIE_FIND_CB_CHILD = 1 , /* Search only children */
DIE_FIND_CB_SIBLING = 2 , /* Search only siblings */
DIE_FIND_CB_CONTINUE = 3 , /* Search children and siblings */
} ;
/* Search a child die */
static Dwarf_Die * die_find_child ( Dwarf_Die * rt_die ,
int ( * callback ) ( Dwarf_Die * , void * ) ,
void * data , Dwarf_Die * die_mem )
{
Dwarf_Die child_die ;
int ret ;
ret = dwarf_child ( rt_die , die_mem ) ;
if ( ret ! = 0 )
return NULL ;
do {
ret = callback ( die_mem , data ) ;
if ( ret = = DIE_FIND_CB_FOUND )
return die_mem ;
if ( ( ret & DIE_FIND_CB_CHILD ) & &
die_find_child ( die_mem , callback , data , & child_die ) ) {
memcpy ( die_mem , & child_die , sizeof ( Dwarf_Die ) ) ;
return die_mem ;
}
} while ( ( ret & DIE_FIND_CB_SIBLING ) & &
dwarf_siblingof ( die_mem , die_mem ) = = 0 ) ;
return NULL ;
}
2010-02-25 16:35:42 +03:00
struct __addr_die_search_param {
Dwarf_Addr addr ;
Dwarf_Die * die_mem ;
} ;
static int __die_search_func_cb ( Dwarf_Die * fn_die , void * data )
2010-01-06 17:45:34 +03:00
{
2010-02-25 16:35:42 +03:00
struct __addr_die_search_param * ad = data ;
2010-01-06 17:45:34 +03:00
2010-02-25 16:35:42 +03:00
if ( dwarf_tag ( fn_die ) = = DW_TAG_subprogram & &
dwarf_haspc ( fn_die , ad - > addr ) ) {
memcpy ( ad - > die_mem , fn_die , sizeof ( Dwarf_Die ) ) ;
return DWARF_CB_ABORT ;
}
return DWARF_CB_OK ;
}
2010-01-06 17:45:34 +03:00
2010-02-25 16:35:42 +03:00
/* Search a real subprogram including this line, */
2010-03-17 01:05:51 +03:00
static Dwarf_Die * die_find_real_subprogram ( Dwarf_Die * cu_die , Dwarf_Addr addr ,
Dwarf_Die * die_mem )
2010-02-25 16:35:42 +03:00
{
struct __addr_die_search_param ad ;
ad . addr = addr ;
ad . die_mem = die_mem ;
/* dwarf_getscopes can't find subprogram. */
if ( ! dwarf_getfuncs ( cu_die , __die_search_func_cb , & ad , 0 ) )
return NULL ;
else
return die_mem ;
2010-01-06 17:45:34 +03:00
}
2010-03-17 01:05:58 +03:00
/* die_find callback for inline function search */
static int __die_find_inline_cb ( Dwarf_Die * die_mem , void * data )
2010-02-25 16:35:57 +03:00
{
2010-03-17 01:05:58 +03:00
Dwarf_Addr * addr = data ;
2010-02-25 16:35:57 +03:00
2010-03-17 01:05:58 +03:00
if ( dwarf_tag ( die_mem ) = = DW_TAG_inlined_subroutine & &
dwarf_haspc ( die_mem , * addr ) )
return DIE_FIND_CB_FOUND ;
2010-02-25 16:35:57 +03:00
2010-03-17 01:05:58 +03:00
return DIE_FIND_CB_CONTINUE ;
2010-02-25 16:35:57 +03:00
}
2010-03-17 01:05:58 +03:00
/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */
static Dwarf_Die * die_find_inlinefunc ( Dwarf_Die * sp_die , Dwarf_Addr addr ,
Dwarf_Die * die_mem )
2009-10-09 01:17:38 +04:00
{
2010-03-17 01:05:58 +03:00
return die_find_child ( sp_die , __die_find_inline_cb , & addr , die_mem ) ;
2009-10-09 01:17:38 +04:00
}
2010-03-17 01:05:58 +03:00
static int __die_find_variable_cb ( Dwarf_Die * die_mem , void * data )
2009-10-09 01:17:38 +04:00
{
2010-03-17 01:05:58 +03:00
const char * name = data ;
int tag ;
2009-10-09 01:17:38 +04:00
2010-03-17 01:05:58 +03:00
tag = dwarf_tag ( die_mem ) ;
if ( ( tag = = DW_TAG_formal_parameter | |
tag = = DW_TAG_variable ) & &
( die_compare_name ( die_mem , name ) = = 0 ) )
return DIE_FIND_CB_FOUND ;
return DIE_FIND_CB_CONTINUE ;
2009-10-09 01:17:38 +04:00
}
2010-03-17 01:05:58 +03:00
/* Find a variable called 'name' */
2010-02-25 16:35:50 +03:00
static Dwarf_Die * die_find_variable ( Dwarf_Die * sp_die , const char * name ,
Dwarf_Die * die_mem )
2009-10-09 01:17:38 +04:00
{
2010-03-17 01:05:58 +03:00
return die_find_child ( sp_die , __die_find_variable_cb , ( void * ) name ,
die_mem ) ;
2009-10-09 01:17:38 +04:00
}
2010-03-17 01:06:26 +03:00
static int __die_find_member_cb ( Dwarf_Die * die_mem , void * data )
{
const char * name = data ;
if ( ( dwarf_tag ( die_mem ) = = DW_TAG_member ) & &
( die_compare_name ( die_mem , name ) = = 0 ) )
return DIE_FIND_CB_FOUND ;
return DIE_FIND_CB_SIBLING ;
}
/* Find a member called 'name' */
static Dwarf_Die * die_find_member ( Dwarf_Die * st_die , const char * name ,
Dwarf_Die * die_mem )
{
return die_find_child ( st_die , __die_find_member_cb , ( void * ) name ,
die_mem ) ;
}
2009-10-09 01:17:38 +04:00
/*
* Probe finder related functions
*/
/* Show a location */
2010-04-12 21:17:35 +04:00
static int convert_location ( Dwarf_Op * op , struct probe_finder * pf )
2009-10-09 01:17:38 +04:00
{
2010-02-25 16:35:42 +03:00
unsigned int regn ;
Dwarf_Word offs = 0 ;
2010-03-17 01:06:12 +03:00
bool ref = false ;
2009-10-09 01:17:38 +04:00
const char * regs ;
2010-03-17 01:06:12 +03:00
struct kprobe_trace_arg * tvar = pf - > tvar ;
2009-10-09 01:17:38 +04:00
/* If this is based on frame buffer, set the offset */
2010-02-25 16:35:42 +03:00
if ( op - > atom = = DW_OP_fbreg ) {
2010-04-12 21:17:35 +04:00
if ( pf - > fb_ops = = NULL ) {
pr_warning ( " The attribute of frame base is not "
" supported. \n " ) ;
return - ENOTSUP ;
}
2010-03-17 01:06:12 +03:00
ref = true ;
2010-02-25 16:35:42 +03:00
offs = op - > number ;
op = & pf - > fb_ops [ 0 ] ;
}
2009-10-09 01:17:38 +04:00
2010-02-25 16:35:42 +03:00
if ( op - > atom > = DW_OP_breg0 & & op - > atom < = DW_OP_breg31 ) {
regn = op - > atom - DW_OP_breg0 ;
offs + = op - > number ;
2010-03-17 01:06:12 +03:00
ref = true ;
2010-02-25 16:35:42 +03:00
} else if ( op - > atom > = DW_OP_reg0 & & op - > atom < = DW_OP_reg31 ) {
regn = op - > atom - DW_OP_reg0 ;
} else if ( op - > atom = = DW_OP_bregx ) {
regn = op - > number ;
offs + = op - > number2 ;
2010-03-17 01:06:12 +03:00
ref = true ;
2010-02-25 16:35:42 +03:00
} else if ( op - > atom = = DW_OP_regx ) {
regn = op - > number ;
2010-04-12 21:17:35 +04:00
} else {
pr_warning ( " DW_OP %x is not supported. \n " , op - > atom ) ;
return - ENOTSUP ;
}
2009-10-09 01:17:38 +04:00
regs = get_arch_regstr ( regn ) ;
2010-04-12 21:17:35 +04:00
if ( ! regs ) {
2010-04-20 10:58:32 +04:00
pr_warning ( " Mapping for DWARF register number %u missing on this architecture. " , regn ) ;
2010-04-12 21:17:35 +04:00
return - ERANGE ;
}
2009-10-09 01:17:38 +04:00
2010-04-12 21:17:56 +04:00
tvar - > value = strdup ( regs ) ;
if ( tvar - > value = = NULL )
return - ENOMEM ;
2010-03-17 01:06:12 +03:00
if ( ref ) {
2010-04-12 21:17:49 +04:00
tvar - > ref = zalloc ( sizeof ( struct kprobe_trace_arg_ref ) ) ;
if ( tvar - > ref = = NULL )
return - ENOMEM ;
2010-03-17 01:06:12 +03:00
tvar - > ref - > offset = ( long ) offs ;
}
2010-04-12 21:17:35 +04:00
return 0 ;
2009-10-09 01:17:38 +04:00
}
2010-04-12 21:17:35 +04:00
static int convert_variable_type ( Dwarf_Die * vr_die ,
struct kprobe_trace_arg * targ )
2010-04-12 21:17:15 +04:00
{
Dwarf_Die type ;
char buf [ 16 ] ;
int ret ;
2010-04-12 21:17:35 +04:00
if ( die_get_real_type ( vr_die , & type ) = = NULL ) {
pr_warning ( " Failed to get a type information of %s. \n " ,
dwarf_diename ( vr_die ) ) ;
return - ENOENT ;
}
2010-04-12 21:17:15 +04:00
ret = die_get_byte_size ( & type ) * 8 ;
if ( ret ) {
/* Check the bitwidth */
if ( ret > MAX_BASIC_TYPE_BITS ) {
2010-04-12 21:17:35 +04:00
pr_info ( " %s exceeds max-bitwidth. "
" Cut down to %d bits. \n " ,
dwarf_diename ( & type ) , MAX_BASIC_TYPE_BITS ) ;
2010-04-12 21:17:15 +04:00
ret = MAX_BASIC_TYPE_BITS ;
}
ret = snprintf ( buf , 16 , " %c%d " ,
die_is_signed_type ( & type ) ? ' s ' : ' u ' , ret ) ;
2010-04-12 21:17:35 +04:00
if ( ret < 0 | | ret > = 16 ) {
if ( ret > = 16 )
ret = - E2BIG ;
pr_warning ( " Failed to convert variable type: %s \n " ,
strerror ( - ret ) ) ;
return ret ;
}
2010-04-12 21:17:56 +04:00
targ - > type = strdup ( buf ) ;
if ( targ - > type = = NULL )
return - ENOMEM ;
2010-04-12 21:17:15 +04:00
}
2010-04-12 21:17:35 +04:00
return 0 ;
2010-04-12 21:17:15 +04:00
}
2010-04-12 21:17:35 +04:00
static int convert_variable_fields ( Dwarf_Die * vr_die , const char * varname ,
2010-03-17 01:06:26 +03:00
struct perf_probe_arg_field * field ,
2010-04-12 21:17:15 +04:00
struct kprobe_trace_arg_ref * * ref_ptr ,
Dwarf_Die * die_mem )
2010-03-17 01:06:26 +03:00
{
struct kprobe_trace_arg_ref * ref = * ref_ptr ;
Dwarf_Die type ;
Dwarf_Word offs ;
2010-04-15 00:44:00 +04:00
int ret ;
2010-03-17 01:06:26 +03:00
pr_debug ( " converting %s in %s \n " , field - > name , varname ) ;
2010-04-12 21:17:35 +04:00
if ( die_get_real_type ( vr_die , & type ) = = NULL ) {
pr_warning ( " Failed to get the type of %s. \n " , varname ) ;
return - ENOENT ;
}
2010-03-17 01:06:26 +03:00
/* Check the pointer and dereference */
if ( dwarf_tag ( & type ) = = DW_TAG_pointer_type ) {
2010-04-12 21:17:35 +04:00
if ( ! field - > ref ) {
pr_err ( " Semantic error: %s must be referred by '->' \n " ,
field - > name ) ;
return - EINVAL ;
}
2010-03-17 01:06:26 +03:00
/* Get the type pointed by this pointer */
2010-04-12 21:17:35 +04:00
if ( die_get_real_type ( & type , & type ) = = NULL ) {
pr_warning ( " Failed to get the type of %s. \n " , varname ) ;
return - ENOENT ;
}
2010-04-02 20:50:53 +04:00
/* Verify it is a data structure */
2010-04-12 21:17:35 +04:00
if ( dwarf_tag ( & type ) ! = DW_TAG_structure_type ) {
pr_warning ( " %s is not a data structure. \n " , varname ) ;
return - EINVAL ;
}
2010-04-02 20:50:53 +04:00
2010-04-12 21:17:49 +04:00
ref = zalloc ( sizeof ( struct kprobe_trace_arg_ref ) ) ;
if ( ref = = NULL )
return - ENOMEM ;
2010-03-17 01:06:26 +03:00
if ( * ref_ptr )
( * ref_ptr ) - > next = ref ;
else
* ref_ptr = ref ;
} else {
2010-04-02 20:50:53 +04:00
/* Verify it is a data structure */
2010-04-12 21:17:35 +04:00
if ( dwarf_tag ( & type ) ! = DW_TAG_structure_type ) {
pr_warning ( " %s is not a data structure. \n " , varname ) ;
return - EINVAL ;
}
if ( field - > ref ) {
pr_err ( " Semantic error: %s must be referred by '.' \n " ,
field - > name ) ;
return - EINVAL ;
}
if ( ! ref ) {
pr_warning ( " Structure on a register is not "
" supported yet. \n " ) ;
return - ENOTSUP ;
}
2010-03-17 01:06:26 +03:00
}
2010-04-12 21:17:35 +04:00
if ( die_find_member ( & type , field - > name , die_mem ) = = NULL ) {
pr_warning ( " %s(tyep:%s) has no member %s. \n " , varname ,
dwarf_diename ( & type ) , field - > name ) ;
return - EINVAL ;
}
2010-03-17 01:06:26 +03:00
/* Get the offset of the field */
2010-04-15 00:44:00 +04:00
ret = die_get_data_member_location ( die_mem , & offs ) ;
if ( ret < 0 ) {
2010-04-12 21:17:35 +04:00
pr_warning ( " Failed to get the offset of %s. \n " , field - > name ) ;
2010-04-15 00:44:00 +04:00
return ret ;
2010-04-12 21:17:35 +04:00
}
2010-03-17 01:06:26 +03:00
ref - > offset + = ( long ) offs ;
/* Converting next field */
if ( field - > next )
2010-04-12 21:17:35 +04:00
return convert_variable_fields ( die_mem , field - > name ,
2010-04-15 00:44:00 +04:00
field - > next , & ref , die_mem ) ;
2010-04-12 21:17:35 +04:00
else
return 0 ;
2010-03-17 01:06:26 +03:00
}
2009-10-09 01:17:38 +04:00
/* Show a variables in kprobe event format */
2010-04-12 21:17:35 +04:00
static int convert_variable ( Dwarf_Die * vr_die , struct probe_finder * pf )
2009-10-09 01:17:38 +04:00
{
Dwarf_Attribute attr ;
2010-04-12 21:17:15 +04:00
Dwarf_Die die_mem ;
2010-02-25 16:35:42 +03:00
Dwarf_Op * expr ;
size_t nexpr ;
2009-10-09 01:17:38 +04:00
int ret ;
2010-02-25 16:35:42 +03:00
if ( dwarf_attr ( vr_die , DW_AT_location , & attr ) = = NULL )
2009-10-09 01:17:38 +04:00
goto error ;
2010-02-25 16:35:42 +03:00
/* TODO: handle more than 1 exprs */
2010-03-15 20:02:35 +03:00
ret = dwarf_getlocation_addr ( & attr , pf - > addr , & expr , & nexpr , 1 ) ;
2010-02-25 16:35:42 +03:00
if ( ret < = 0 | | nexpr = = 0 )
2009-10-09 01:17:38 +04:00
goto error ;
2010-02-25 16:35:42 +03:00
2010-04-12 21:17:35 +04:00
ret = convert_location ( expr , pf ) ;
if ( ret = = 0 & & pf - > pvar - > field ) {
ret = convert_variable_fields ( vr_die , pf - > pvar - > var ,
pf - > pvar - > field , & pf - > tvar - > ref ,
& die_mem ) ;
2010-04-12 21:17:15 +04:00
vr_die = & die_mem ;
}
2010-04-12 21:17:35 +04:00
if ( ret = = 0 ) {
2010-04-12 21:17:56 +04:00
if ( pf - > pvar - > type ) {
pf - > tvar - > type = strdup ( pf - > pvar - > type ) ;
if ( pf - > tvar - > type = = NULL )
ret = - ENOMEM ;
} else
2010-04-12 21:17:35 +04:00
ret = convert_variable_type ( vr_die , pf - > tvar ) ;
}
2010-02-25 16:35:42 +03:00
/* *expr will be cached in libdw. Don't free it. */
2010-04-12 21:17:35 +04:00
return ret ;
2009-10-09 01:17:38 +04:00
error :
2010-02-25 16:35:42 +03:00
/* TODO: Support const_value */
2010-04-12 21:17:35 +04:00
pr_err ( " Failed to find the location of %s at this address. \n "
" Perhaps, it has been optimized out. \n " , pf - > pvar - > var ) ;
return - ENOENT ;
2009-10-09 01:17:38 +04:00
}
/* Find a variable in a subprogram die */
2010-04-12 21:17:35 +04:00
static int find_variable ( Dwarf_Die * sp_die , struct probe_finder * pf )
2009-10-09 01:17:38 +04:00
{
2010-02-25 16:35:50 +03:00
Dwarf_Die vr_die ;
2010-04-12 21:17:22 +04:00
char buf [ 32 ] , * ptr ;
2010-04-12 21:17:56 +04:00
int ret ;
2009-10-09 01:17:38 +04:00
2010-04-12 21:16:53 +04:00
/* TODO: Support arrays */
if ( pf - > pvar - > name )
2010-04-12 21:17:56 +04:00
pf - > tvar - > name = strdup ( pf - > pvar - > name ) ;
2010-04-12 21:16:53 +04:00
else {
2010-04-12 21:17:56 +04:00
ret = synthesize_perf_probe_arg ( pf - > pvar , buf , 32 ) ;
if ( ret < 0 )
return ret ;
2010-04-12 21:17:22 +04:00
ptr = strchr ( buf , ' : ' ) ; /* Change type separator to _ */
if ( ptr )
* ptr = ' _ ' ;
2010-04-12 21:17:56 +04:00
pf - > tvar - > name = strdup ( buf ) ;
2010-04-12 21:16:53 +04:00
}
2010-04-12 21:17:56 +04:00
if ( pf - > tvar - > name = = NULL )
return - ENOMEM ;
2010-04-12 21:16:53 +04:00
if ( ! is_c_varname ( pf - > pvar - > var ) ) {
2010-03-17 01:06:12 +03:00
/* Copy raw parameters */
2010-04-12 21:17:56 +04:00
pf - > tvar - > value = strdup ( pf - > pvar - > var ) ;
if ( pf - > tvar - > value = = NULL )
return - ENOMEM ;
else
return 0 ;
2009-10-09 01:17:38 +04:00
}
2010-04-12 21:17:35 +04:00
pr_debug ( " Searching '%s' variable in context. \n " ,
pf - > pvar - > var ) ;
/* Search child die for local variables and parameters. */
if ( ! die_find_variable ( sp_die , pf - > pvar - > var , & vr_die ) ) {
pr_warning ( " Failed to find '%s' in this function. \n " ,
pf - > pvar - > var ) ;
return - ENOENT ;
}
return convert_variable ( & vr_die , pf ) ;
2009-10-09 01:17:38 +04:00
}
/* Show a probe point to output buffer */
2010-04-12 21:17:35 +04:00
static int convert_probe_point ( Dwarf_Die * sp_die , struct probe_finder * pf )
2009-10-09 01:17:38 +04:00
{
2010-03-17 01:06:12 +03:00
struct kprobe_trace_event * tev ;
2010-02-25 16:35:50 +03:00
Dwarf_Addr eaddr ;
Dwarf_Die die_mem ;
2010-02-25 16:35:42 +03:00
const char * name ;
2010-03-17 01:06:12 +03:00
int ret , i ;
2010-02-25 16:35:42 +03:00
Dwarf_Attribute fb_attr ;
size_t nops ;
2009-10-09 01:17:38 +04:00
2010-04-21 23:56:40 +04:00
if ( pf - > ntevs = = pf - > max_tevs ) {
pr_warning ( " Too many( > %d) probe point found. \n " ,
pf - > max_tevs ) ;
2010-04-12 21:17:35 +04:00
return - ERANGE ;
}
2010-03-17 01:06:12 +03:00
tev = & pf - > tevs [ pf - > ntevs + + ] ;
2010-02-25 16:35:50 +03:00
/* If no real subprogram, find a real one */
if ( ! sp_die | | dwarf_tag ( sp_die ) ! = DW_TAG_subprogram ) {
2010-03-17 01:05:51 +03:00
sp_die = die_find_real_subprogram ( & pf - > cu_die ,
2010-02-25 16:35:50 +03:00
pf - > addr , & die_mem ) ;
2010-04-12 21:17:35 +04:00
if ( ! sp_die ) {
pr_warning ( " Failed to find probe point in any "
" functions. \n " ) ;
return - ENOENT ;
}
2010-02-25 16:35:50 +03:00
}
2010-03-17 01:06:12 +03:00
/* Copy the name of probe point */
2010-02-25 16:35:42 +03:00
name = dwarf_diename ( sp_die ) ;
if ( name ) {
2010-04-12 21:17:35 +04:00
if ( dwarf_entrypc ( sp_die , & eaddr ) ! = 0 ) {
pr_warning ( " Failed to get entry pc of %s \n " ,
dwarf_diename ( sp_die ) ) ;
return - ENOENT ;
}
2010-04-12 21:17:56 +04:00
tev - > point . symbol = strdup ( name ) ;
if ( tev - > point . symbol = = NULL )
return - ENOMEM ;
2010-03-17 01:06:12 +03:00
tev - > point . offset = ( unsigned long ) ( pf - > addr - eaddr ) ;
} else
2009-10-09 01:17:38 +04:00
/* This function has no name. */
2010-03-17 01:06:12 +03:00
tev - > point . offset = ( unsigned long ) pf - > addr ;
pr_debug ( " Probe point found: %s+%lu \n " , tev - > point . symbol ,
tev - > point . offset ) ;
2009-10-09 01:17:38 +04:00
2010-02-25 16:35:42 +03:00
/* Get the frame base attribute/ops */
dwarf_attr ( sp_die , DW_AT_frame_base , & fb_attr ) ;
2010-03-15 20:02:35 +03:00
ret = dwarf_getlocation_addr ( & fb_attr , pf - > addr , & pf - > fb_ops , & nops , 1 ) ;
2010-04-12 21:17:29 +04:00
if ( ret < = 0 | | nops = = 0 ) {
2010-02-25 16:35:42 +03:00
pf - > fb_ops = NULL ;
2010-05-10 21:12:07 +04:00
# if _ELFUTILS_PREREQ(0, 142)
2010-04-12 21:17:29 +04:00
} else if ( nops = = 1 & & pf - > fb_ops [ 0 ] . atom = = DW_OP_call_frame_cfa & &
pf - > cfi ! = NULL ) {
Dwarf_Frame * frame ;
2010-04-12 21:17:35 +04:00
if ( dwarf_cfi_addrframe ( pf - > cfi , pf - > addr , & frame ) ! = 0 | |
dwarf_frame_cfa ( frame , & pf - > fb_ops , & nops ) ! = 0 ) {
pr_warning ( " Failed to get CFA on 0x%jx \n " ,
( uintmax_t ) pf - > addr ) ;
return - ENOENT ;
}
2010-05-10 21:12:07 +04:00
# endif
2010-04-12 21:17:29 +04:00
}
2010-02-25 16:35:42 +03:00
2009-10-09 01:17:38 +04:00
/* Find each argument */
2010-03-17 01:06:12 +03:00
tev - > nargs = pf - > pev - > nargs ;
2010-04-12 21:17:49 +04:00
tev - > args = zalloc ( sizeof ( struct kprobe_trace_arg ) * tev - > nargs ) ;
if ( tev - > args = = NULL )
return - ENOMEM ;
2010-03-17 01:06:12 +03:00
for ( i = 0 ; i < pf - > pev - > nargs ; i + + ) {
pf - > pvar = & pf - > pev - > args [ i ] ;
pf - > tvar = & tev - > args [ i ] ;
2010-04-12 21:17:35 +04:00
ret = find_variable ( sp_die , pf ) ;
if ( ret ! = 0 )
return ret ;
2009-10-09 01:17:38 +04:00
}
2010-02-25 16:35:42 +03:00
/* *pf->fb_ops will be cached in libdw. Don't free it. */
pf - > fb_ops = NULL ;
2010-04-12 21:17:35 +04:00
return 0 ;
2009-10-09 01:17:38 +04:00
}
/* Find probe point from its line number */
2010-04-12 21:17:35 +04:00
static int find_probe_point_by_line ( struct probe_finder * pf )
2009-10-09 01:17:38 +04:00
{
2010-02-25 16:35:42 +03:00
Dwarf_Lines * lines ;
Dwarf_Line * line ;
size_t nlines , i ;
2010-02-25 16:35:50 +03:00
Dwarf_Addr addr ;
2010-02-25 16:35:42 +03:00
int lineno ;
2010-04-12 21:17:35 +04:00
int ret = 0 ;
2009-10-09 01:17:38 +04:00
2010-04-12 21:17:35 +04:00
if ( dwarf_getsrclines ( & pf - > cu_die , & lines , & nlines ) ! = 0 ) {
pr_warning ( " No source lines found in this CU. \n " ) ;
return - ENOENT ;
}
2009-10-09 01:17:38 +04:00
2010-04-12 21:17:35 +04:00
for ( i = 0 ; i < nlines & & ret = = 0 ; i + + ) {
2010-02-25 16:35:42 +03:00
line = dwarf_onesrcline ( lines , i ) ;
2010-04-12 21:17:35 +04:00
if ( dwarf_lineno ( line , & lineno ) ! = 0 | |
lineno ! = pf - > lno )
2009-10-09 01:17:38 +04:00
continue ;
2010-02-25 16:35:42 +03:00
/* TODO: Get fileno from line, but how? */
if ( strtailcmp ( dwarf_linesrc ( line , NULL , NULL ) , pf - > fname ) ! = 0 )
continue ;
2009-10-27 23:43:19 +03:00
2010-04-12 21:17:35 +04:00
if ( dwarf_lineaddr ( line , & addr ) ! = 0 ) {
pr_warning ( " Failed to get the address of the line. \n " ) ;
return - ENOENT ;
}
2010-02-25 16:35:42 +03:00
pr_debug ( " Probe line found: line[%d]:%d addr:0x%jx \n " ,
( int ) i , lineno , ( uintmax_t ) addr ) ;
2009-10-09 01:17:38 +04:00
pf - > addr = addr ;
2010-02-25 16:35:42 +03:00
2010-04-12 21:17:35 +04:00
ret = convert_probe_point ( NULL , pf ) ;
2009-10-09 01:17:38 +04:00
/* Continuing, because target line might be inlined. */
}
2010-04-12 21:17:35 +04:00
return ret ;
2009-10-09 01:17:38 +04:00
}
2010-02-25 16:36:12 +03:00
/* Find lines which match lazy pattern */
static int find_lazy_match_lines ( struct list_head * head ,
const char * fname , const char * pat )
{
char * fbuf , * p1 , * p2 ;
2010-05-19 06:04:28 +04:00
int fd , line , nlines = - 1 ;
2010-02-25 16:36:12 +03:00
struct stat st ;
fd = open ( fname , O_RDONLY ) ;
2010-04-12 21:17:35 +04:00
if ( fd < 0 ) {
pr_warning ( " Failed to open %s: %s \n " , fname , strerror ( - fd ) ) ;
2010-05-19 06:04:28 +04:00
return - errno ;
2010-04-12 21:17:35 +04:00
}
2010-05-19 06:04:28 +04:00
if ( fstat ( fd , & st ) < 0 ) {
2010-04-12 21:17:35 +04:00
pr_warning ( " Failed to get the size of %s: %s \n " ,
fname , strerror ( errno ) ) ;
2010-05-19 06:04:28 +04:00
nlines = - errno ;
goto out_close ;
2010-04-12 21:17:35 +04:00
}
2010-05-19 06:04:28 +04:00
nlines = - ENOMEM ;
fbuf = malloc ( st . st_size + 2 ) ;
if ( fbuf = = NULL )
goto out_close ;
if ( read ( fd , fbuf , st . st_size ) < 0 ) {
2010-04-12 21:17:35 +04:00
pr_warning ( " Failed to read %s: %s \n " , fname , strerror ( errno ) ) ;
2010-05-19 06:04:28 +04:00
nlines = - errno ;
goto out_free_fbuf ;
2010-04-12 21:17:35 +04:00
}
2010-02-25 16:36:12 +03:00
fbuf [ st . st_size ] = ' \n ' ; /* Dummy line */
fbuf [ st . st_size + 1 ] = ' \0 ' ;
p1 = fbuf ;
line = 1 ;
2010-05-19 06:04:28 +04:00
nlines = 0 ;
2010-02-25 16:36:12 +03:00
while ( ( p2 = strchr ( p1 , ' \n ' ) ) ! = NULL ) {
* p2 = ' \0 ' ;
if ( strlazymatch ( p1 , pat ) ) {
line_list__add_line ( head , line ) ;
nlines + + ;
}
line + + ;
p1 = p2 + 1 ;
}
2010-05-19 06:04:28 +04:00
out_free_fbuf :
2010-02-25 16:36:12 +03:00
free ( fbuf ) ;
2010-05-19 06:04:28 +04:00
out_close :
close ( fd ) ;
2010-02-25 16:36:12 +03:00
return nlines ;
}
/* Find probe points from lazy pattern */
2010-04-12 21:17:35 +04:00
static int find_probe_point_lazy ( Dwarf_Die * sp_die , struct probe_finder * pf )
2010-02-25 16:36:12 +03:00
{
Dwarf_Lines * lines ;
Dwarf_Line * line ;
size_t nlines , i ;
Dwarf_Addr addr ;
Dwarf_Die die_mem ;
int lineno ;
2010-04-12 21:17:35 +04:00
int ret = 0 ;
2010-02-25 16:36:12 +03:00
if ( list_empty ( & pf - > lcache ) ) {
/* Matching lazy line pattern */
ret = find_lazy_match_lines ( & pf - > lcache , pf - > fname ,
2010-03-17 01:06:12 +03:00
pf - > pev - > point . lazy_line ) ;
2010-04-12 21:17:35 +04:00
if ( ret = = 0 ) {
pr_debug ( " No matched lines found in %s. \n " , pf - > fname ) ;
return 0 ;
} else if ( ret < 0 )
return ret ;
2010-02-25 16:36:12 +03:00
}
2010-04-12 21:17:35 +04:00
if ( dwarf_getsrclines ( & pf - > cu_die , & lines , & nlines ) ! = 0 ) {
pr_warning ( " No source lines found in this CU. \n " ) ;
return - ENOENT ;
}
for ( i = 0 ; i < nlines & & ret > = 0 ; i + + ) {
2010-02-25 16:36:12 +03:00
line = dwarf_onesrcline ( lines , i ) ;
2010-04-12 21:17:35 +04:00
if ( dwarf_lineno ( line , & lineno ) ! = 0 | |
! line_list__has_line ( & pf - > lcache , lineno ) )
2010-02-25 16:36:12 +03:00
continue ;
/* TODO: Get fileno from line, but how? */
if ( strtailcmp ( dwarf_linesrc ( line , NULL , NULL ) , pf - > fname ) ! = 0 )
continue ;
2010-04-12 21:17:35 +04:00
if ( dwarf_lineaddr ( line , & addr ) ! = 0 ) {
pr_debug ( " Failed to get the address of line %d. \n " ,
lineno ) ;
continue ;
}
2010-02-25 16:36:12 +03:00
if ( sp_die ) {
/* Address filtering 1: does sp_die include addr? */
if ( ! dwarf_haspc ( sp_die , addr ) )
continue ;
/* Address filtering 2: No child include addr? */
2010-03-17 01:05:51 +03:00
if ( die_find_inlinefunc ( sp_die , addr , & die_mem ) )
2010-02-25 16:36:12 +03:00
continue ;
}
pr_debug ( " Probe line found: line[%d]:%d addr:0x%llx \n " ,
( int ) i , lineno , ( unsigned long long ) addr ) ;
pf - > addr = addr ;
2010-04-12 21:17:35 +04:00
ret = convert_probe_point ( sp_die , pf ) ;
2010-02-25 16:36:12 +03:00
/* Continuing, because target line might be inlined. */
}
/* TODO: deallocate lines, but how? */
2010-04-12 21:17:35 +04:00
return ret ;
2010-02-25 16:36:12 +03:00
}
2010-04-12 21:17:35 +04:00
/* Callback parameter with return value */
struct dwarf_callback_param {
void * data ;
int retval ;
} ;
2010-02-25 16:35:50 +03:00
static int probe_point_inline_cb ( Dwarf_Die * in_die , void * data )
{
2010-04-12 21:17:35 +04:00
struct dwarf_callback_param * param = data ;
struct probe_finder * pf = param - > data ;
2010-03-17 01:06:12 +03:00
struct perf_probe_point * pp = & pf - > pev - > point ;
2010-04-12 21:17:35 +04:00
Dwarf_Addr addr ;
2010-02-25 16:35:50 +03:00
2010-02-25 16:36:12 +03:00
if ( pp - > lazy_line )
2010-04-12 21:17:35 +04:00
param - > retval = find_probe_point_lazy ( in_die , pf ) ;
2010-02-25 16:36:12 +03:00
else {
/* Get probe address */
2010-04-12 21:17:35 +04:00
if ( dwarf_entrypc ( in_die , & addr ) ! = 0 ) {
pr_warning ( " Failed to get entry pc of %s. \n " ,
dwarf_diename ( in_die ) ) ;
param - > retval = - ENOENT ;
return DWARF_CB_ABORT ;
}
pf - > addr = addr ;
2010-02-25 16:36:12 +03:00
pf - > addr + = pp - > offset ;
pr_debug ( " found inline addr: 0x%jx \n " ,
( uintmax_t ) pf - > addr ) ;
2010-04-12 21:17:35 +04:00
param - > retval = convert_probe_point ( in_die , pf ) ;
2010-04-21 23:56:32 +04:00
if ( param - > retval < 0 )
return DWARF_CB_ABORT ;
2010-02-25 16:36:12 +03:00
}
2010-02-25 16:35:50 +03:00
return DWARF_CB_OK ;
}
2010-02-25 16:35:42 +03:00
2009-10-09 01:17:38 +04:00
/* Search function from function name */
2010-02-25 16:35:50 +03:00
static int probe_point_search_cb ( Dwarf_Die * sp_die , void * data )
2009-10-09 01:17:38 +04:00
{
2010-04-12 21:17:35 +04:00
struct dwarf_callback_param * param = data ;
struct probe_finder * pf = param - > data ;
2010-03-17 01:06:12 +03:00
struct perf_probe_point * pp = & pf - > pev - > point ;
2009-10-09 01:17:38 +04:00
2010-02-25 16:35:50 +03:00
/* Check tag and diename */
if ( dwarf_tag ( sp_die ) ! = DW_TAG_subprogram | |
die_compare_name ( sp_die , pp - > function ) ! = 0 )
2010-04-12 21:17:35 +04:00
return DWARF_CB_OK ;
2010-02-25 16:35:50 +03:00
2010-02-25 16:36:12 +03:00
pf - > fname = dwarf_decl_file ( sp_die ) ;
2010-02-25 16:35:50 +03:00
if ( pp - > line ) { /* Function relative line */
dwarf_decl_line ( sp_die , & pf - > lno ) ;
pf - > lno + = pp - > line ;
2010-04-12 21:17:35 +04:00
param - > retval = find_probe_point_by_line ( pf ) ;
2010-02-25 16:35:50 +03:00
} else if ( ! dwarf_func_inline ( sp_die ) ) {
/* Real function */
2010-02-25 16:36:12 +03:00
if ( pp - > lazy_line )
2010-04-12 21:17:35 +04:00
param - > retval = find_probe_point_lazy ( sp_die , pf ) ;
2010-02-25 16:36:12 +03:00
else {
2010-04-12 21:17:35 +04:00
if ( dwarf_entrypc ( sp_die , & pf - > addr ) ! = 0 ) {
pr_warning ( " Failed to get entry pc of %s. \n " ,
dwarf_diename ( sp_die ) ) ;
param - > retval = - ENOENT ;
return DWARF_CB_ABORT ;
}
2010-02-25 16:36:12 +03:00
pf - > addr + = pp - > offset ;
/* TODO: Check the address in this function */
2010-04-12 21:17:35 +04:00
param - > retval = convert_probe_point ( sp_die , pf ) ;
2010-02-25 16:36:12 +03:00
}
2010-04-12 21:17:35 +04:00
} else {
struct dwarf_callback_param _param = { . data = ( void * ) pf ,
. retval = 0 } ;
2010-02-25 16:35:50 +03:00
/* Inlined function: search instances */
2010-04-12 21:17:35 +04:00
dwarf_func_inline_instances ( sp_die , probe_point_inline_cb ,
& _param ) ;
param - > retval = _param . retval ;
}
2010-02-25 16:35:50 +03:00
2010-04-12 21:17:35 +04:00
return DWARF_CB_ABORT ; /* Exit; no same symbol in this CU. */
2009-10-09 01:17:38 +04:00
}
2010-04-12 21:17:35 +04:00
static int find_probe_point_by_func ( struct probe_finder * pf )
2009-10-09 01:17:38 +04:00
{
2010-04-12 21:17:35 +04:00
struct dwarf_callback_param _param = { . data = ( void * ) pf ,
. retval = 0 } ;
dwarf_getfuncs ( & pf - > cu_die , probe_point_search_cb , & _param , 0 ) ;
return _param . retval ;
2009-10-09 01:17:38 +04:00
}
2010-03-17 01:06:12 +03:00
/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */
int find_kprobe_trace_events ( int fd , struct perf_probe_event * pev ,
2010-04-21 23:56:40 +04:00
struct kprobe_trace_event * * tevs , int max_tevs )
2009-10-09 01:17:38 +04:00
{
2010-04-21 23:56:40 +04:00
struct probe_finder pf = { . pev = pev , . max_tevs = max_tevs } ;
2010-03-17 01:06:12 +03:00
struct perf_probe_point * pp = & pev - > point ;
2010-02-25 16:35:42 +03:00
Dwarf_Off off , noff ;
size_t cuhl ;
Dwarf_Die * diep ;
Dwarf * dbg ;
2010-04-12 21:17:35 +04:00
int ret = 0 ;
2010-02-25 16:35:42 +03:00
2010-04-21 23:56:40 +04:00
pf . tevs = zalloc ( sizeof ( struct kprobe_trace_event ) * max_tevs ) ;
2010-04-12 21:17:49 +04:00
if ( pf . tevs = = NULL )
return - ENOMEM ;
2010-03-17 01:06:12 +03:00
* tevs = pf . tevs ;
pf . ntevs = 0 ;
2010-02-25 16:35:42 +03:00
dbg = dwarf_begin ( fd , DWARF_C_READ ) ;
2010-04-12 21:17:35 +04:00
if ( ! dbg ) {
pr_warning ( " No dwarf info found in the vmlinux - "
" please rebuild with CONFIG_DEBUG_INFO=y. \n " ) ;
2010-05-19 06:04:28 +04:00
free ( pf . tevs ) ;
* tevs = NULL ;
2010-04-12 21:17:35 +04:00
return - EBADF ;
}
2009-10-09 01:17:38 +04:00
2010-05-10 21:12:07 +04:00
# if _ELFUTILS_PREREQ(0, 142)
2010-04-12 21:17:29 +04:00
/* Get the call frame information from this dwarf */
pf . cfi = dwarf_getcfi ( dbg ) ;
2010-05-10 21:12:07 +04:00
# endif
2010-04-12 21:17:29 +04:00
2010-02-25 16:35:42 +03:00
off = 0 ;
2010-02-25 16:36:12 +03:00
line_list__init ( & pf . lcache ) ;
2010-02-25 16:35:42 +03:00
/* Loop on CUs (Compilation Unit) */
2010-04-12 21:17:35 +04:00
while ( ! dwarf_nextcu ( dbg , off , & noff , & cuhl , NULL , NULL , NULL ) & &
ret > = 0 ) {
2009-10-09 01:17:38 +04:00
/* Get the DIE(Debugging Information Entry) of this CU */
2010-02-25 16:35:42 +03:00
diep = dwarf_offdie ( dbg , off + cuhl , & pf . cu_die ) ;
if ( ! diep )
continue ;
2009-10-09 01:17:38 +04:00
/* Check if target file is included. */
if ( pp - > file )
2010-02-25 16:36:12 +03:00
pf . fname = cu_find_realpath ( & pf . cu_die , pp - > file ) ;
2010-02-25 16:35:42 +03:00
else
2010-02-25 16:36:12 +03:00
pf . fname = NULL ;
2009-10-09 01:17:38 +04:00
2010-02-25 16:36:12 +03:00
if ( ! pp - > file | | pf . fname ) {
2009-10-09 01:17:38 +04:00
if ( pp - > function )
2010-04-12 21:17:35 +04:00
ret = find_probe_point_by_func ( & pf ) ;
2010-02-25 16:36:12 +03:00
else if ( pp - > lazy_line )
2010-04-12 21:17:35 +04:00
ret = find_probe_point_lazy ( NULL , & pf ) ;
2009-10-27 23:43:19 +03:00
else {
pf . lno = pp - > line ;
2010-04-12 21:17:35 +04:00
ret = find_probe_point_by_line ( & pf ) ;
2009-10-27 23:43:19 +03:00
}
2009-10-09 01:17:38 +04:00
}
2010-02-25 16:35:42 +03:00
off = noff ;
2009-10-09 01:17:38 +04:00
}
2010-02-25 16:36:12 +03:00
line_list__free ( & pf . lcache ) ;
2010-02-25 16:35:42 +03:00
dwarf_end ( dbg ) ;
2009-10-09 01:17:38 +04:00
2010-04-12 21:17:35 +04:00
return ( ret < 0 ) ? ret : pf . ntevs ;
2009-10-09 01:17:38 +04:00
}
2010-03-17 01:06:19 +03:00
/* Reverse search */
int find_perf_probe_point ( int fd , unsigned long addr ,
struct perf_probe_point * ppt )
{
Dwarf_Die cudie , spdie , indie ;
Dwarf * dbg ;
Dwarf_Line * line ;
Dwarf_Addr laddr , eaddr ;
const char * tmp ;
int lineno , ret = 0 ;
2010-04-12 21:17:35 +04:00
bool found = false ;
2010-03-17 01:06:19 +03:00
dbg = dwarf_begin ( fd , DWARF_C_READ ) ;
if ( ! dbg )
2010-04-12 21:17:35 +04:00
return - EBADF ;
2010-03-17 01:06:19 +03:00
/* Find cu die */
2010-04-02 20:50:59 +04:00
if ( ! dwarf_addrdie ( dbg , ( Dwarf_Addr ) addr , & cudie ) ) {
ret = - EINVAL ;
goto end ;
}
2010-03-17 01:06:19 +03:00
/* Find a corresponding line */
line = dwarf_getsrc_die ( & cudie , ( Dwarf_Addr ) addr ) ;
if ( line ) {
2010-04-12 21:17:35 +04:00
if ( dwarf_lineaddr ( line , & laddr ) = = 0 & &
( Dwarf_Addr ) addr = = laddr & &
dwarf_lineno ( line , & lineno ) = = 0 ) {
2010-03-17 01:06:19 +03:00
tmp = dwarf_linesrc ( line , NULL , NULL ) ;
2010-04-12 21:17:35 +04:00
if ( tmp ) {
ppt - > line = lineno ;
2010-04-12 21:17:56 +04:00
ppt - > file = strdup ( tmp ) ;
if ( ppt - > file = = NULL ) {
ret = - ENOMEM ;
goto end ;
}
2010-04-12 21:17:35 +04:00
found = true ;
}
2010-03-17 01:06:19 +03:00
}
}
/* Find a corresponding function */
if ( die_find_real_subprogram ( & cudie , ( Dwarf_Addr ) addr , & spdie ) ) {
tmp = dwarf_diename ( & spdie ) ;
2010-04-12 21:17:35 +04:00
if ( ! tmp | | dwarf_entrypc ( & spdie , & eaddr ) ! = 0 )
2010-03-17 01:06:19 +03:00
goto end ;
2010-04-12 21:17:35 +04:00
if ( ppt - > line ) {
if ( die_find_inlinefunc ( & spdie , ( Dwarf_Addr ) addr ,
& indie ) ) {
/* addr in an inline function */
tmp = dwarf_diename ( & indie ) ;
if ( ! tmp )
goto end ;
ret = dwarf_decl_line ( & indie , & lineno ) ;
} else {
if ( eaddr = = addr ) { /* Function entry */
lineno = ppt - > line ;
ret = 0 ;
} else
ret = dwarf_decl_line ( & spdie , & lineno ) ;
}
if ( ret = = 0 ) {
/* Make a relative line number */
ppt - > line - = lineno ;
goto found ;
}
2010-03-17 01:06:19 +03:00
}
2010-04-12 21:17:35 +04:00
/* We don't have a line number, let's use offset */
ppt - > offset = addr - ( unsigned long ) eaddr ;
found :
2010-04-12 21:17:56 +04:00
ppt - > function = strdup ( tmp ) ;
if ( ppt - > function = = NULL ) {
ret = - ENOMEM ;
goto end ;
}
2010-04-12 21:17:35 +04:00
found = true ;
2010-03-17 01:06:19 +03:00
}
end :
dwarf_end ( dbg ) ;
2010-04-12 21:17:35 +04:00
if ( ret > = 0 )
ret = found ? 1 : 0 ;
2010-03-17 01:06:19 +03:00
return ret ;
}
2010-04-15 02:40:07 +04:00
/* Add a line and store the src path */
static int line_range_add_line ( const char * src , unsigned int lineno ,
struct line_range * lr )
{
/* Copy real path */
if ( ! lr - > path ) {
lr - > path = strdup ( src ) ;
if ( lr - > path = = NULL )
return - ENOMEM ;
}
return line_list__add_line ( & lr - > line_list , lineno ) ;
}
/* Search function declaration lines */
static int line_range_funcdecl_cb ( Dwarf_Die * sp_die , void * data )
{
struct dwarf_callback_param * param = data ;
struct line_finder * lf = param - > data ;
const char * src ;
int lineno ;
src = dwarf_decl_file ( sp_die ) ;
if ( src & & strtailcmp ( src , lf - > fname ) ! = 0 )
return DWARF_CB_OK ;
if ( dwarf_decl_line ( sp_die , & lineno ) ! = 0 | |
( lf - > lno_s > lineno | | lf - > lno_e < lineno ) )
return DWARF_CB_OK ;
param - > retval = line_range_add_line ( src , lineno , lf - > lr ) ;
2010-04-21 23:56:32 +04:00
if ( param - > retval < 0 )
return DWARF_CB_ABORT ;
2010-04-15 02:40:07 +04:00
return DWARF_CB_OK ;
}
static int find_line_range_func_decl_lines ( struct line_finder * lf )
{
struct dwarf_callback_param param = { . data = ( void * ) lf , . retval = 0 } ;
dwarf_getfuncs ( & lf - > cu_die , line_range_funcdecl_cb , & param , 0 ) ;
return param . retval ;
}
2010-03-17 01:06:19 +03:00
2010-01-06 17:45:34 +03:00
/* Find line range from its line number */
2010-04-12 21:17:35 +04:00
static int find_line_range_by_line ( Dwarf_Die * sp_die , struct line_finder * lf )
2010-01-06 17:45:34 +03:00
{
2010-02-25 16:35:42 +03:00
Dwarf_Lines * lines ;
Dwarf_Line * line ;
size_t nlines , i ;
2010-01-06 17:45:34 +03:00
Dwarf_Addr addr ;
2010-04-15 02:40:07 +04:00
int lineno , ret = 0 ;
2010-02-25 16:35:42 +03:00
const char * src ;
2010-02-25 16:35:57 +03:00
Dwarf_Die die_mem ;
2010-01-06 17:45:34 +03:00
2010-02-25 16:36:12 +03:00
line_list__init ( & lf - > lr - > line_list ) ;
2010-04-12 21:17:35 +04:00
if ( dwarf_getsrclines ( & lf - > cu_die , & lines , & nlines ) ! = 0 ) {
pr_warning ( " No source lines found in this CU. \n " ) ;
return - ENOENT ;
}
2010-01-06 17:45:34 +03:00
2010-04-15 02:40:07 +04:00
/* Search probable lines on lines list */
2010-02-25 16:35:42 +03:00
for ( i = 0 ; i < nlines ; i + + ) {
line = dwarf_onesrcline ( lines , i ) ;
2010-04-12 21:17:35 +04:00
if ( dwarf_lineno ( line , & lineno ) ! = 0 | |
( lf - > lno_s > lineno | | lf - > lno_e < lineno ) )
2010-01-06 17:45:34 +03:00
continue ;
2010-02-25 16:35:57 +03:00
if ( sp_die ) {
/* Address filtering 1: does sp_die include addr? */
2010-04-12 21:17:35 +04:00
if ( dwarf_lineaddr ( line , & addr ) ! = 0 | |
! dwarf_haspc ( sp_die , addr ) )
2010-02-25 16:35:57 +03:00
continue ;
/* Address filtering 2: No child include addr? */
2010-03-17 01:05:51 +03:00
if ( die_find_inlinefunc ( sp_die , addr , & die_mem ) )
2010-02-25 16:35:57 +03:00
continue ;
}
2010-02-25 16:35:42 +03:00
/* TODO: Get fileno from line, but how? */
src = dwarf_linesrc ( line , NULL , NULL ) ;
if ( strtailcmp ( src , lf - > fname ) ! = 0 )
2010-01-06 17:45:34 +03:00
continue ;
2010-04-15 02:40:07 +04:00
ret = line_range_add_line ( src , lineno , lf - > lr ) ;
if ( ret < 0 )
return ret ;
2010-01-06 17:45:34 +03:00
}
2010-04-15 02:40:07 +04:00
/*
* Dwarf lines doesn ' t include function declarations . We have to
* check functions list or given function .
*/
if ( sp_die ) {
src = dwarf_decl_file ( sp_die ) ;
if ( src & & dwarf_decl_line ( sp_die , & lineno ) = = 0 & &
( lf - > lno_s < = lineno & & lf - > lno_e > = lineno ) )
ret = line_range_add_line ( src , lineno , lf - > lr ) ;
} else
ret = find_line_range_func_decl_lines ( lf ) ;
2010-02-25 16:35:42 +03:00
/* Update status */
2010-04-15 02:40:07 +04:00
if ( ret > = 0 )
if ( ! list_empty ( & lf - > lr - > line_list ) )
ret = lf - > found = 1 ;
else
ret = 0 ; /* Lines are not found */
2010-02-25 16:35:42 +03:00
else {
free ( lf - > lr - > path ) ;
lf - > lr - > path = NULL ;
}
2010-04-15 02:40:07 +04:00
return ret ;
2010-01-06 17:45:34 +03:00
}
2010-02-25 16:35:57 +03:00
static int line_range_inline_cb ( Dwarf_Die * in_die , void * data )
{
2010-04-12 21:17:35 +04:00
struct dwarf_callback_param * param = data ;
param - > retval = find_line_range_by_line ( in_die , param - > data ) ;
2010-02-25 16:35:57 +03:00
return DWARF_CB_ABORT ; /* No need to find other instances */
}
2010-01-06 17:45:34 +03:00
/* Search function from function name */
2010-02-25 16:35:50 +03:00
static int line_range_search_cb ( Dwarf_Die * sp_die , void * data )
2010-01-06 17:45:34 +03:00
{
2010-04-12 21:17:35 +04:00
struct dwarf_callback_param * param = data ;
struct line_finder * lf = param - > data ;
2010-01-06 17:45:34 +03:00
struct line_range * lr = lf - > lr ;
2010-02-25 16:35:50 +03:00
if ( dwarf_tag ( sp_die ) = = DW_TAG_subprogram & &
die_compare_name ( sp_die , lr - > function ) = = 0 ) {
lf - > fname = dwarf_decl_file ( sp_die ) ;
dwarf_decl_line ( sp_die , & lr - > offset ) ;
2010-02-25 16:35:42 +03:00
pr_debug ( " fname: %s, lineno:%d \n " , lf - > fname , lr - > offset ) ;
2010-01-06 17:45:34 +03:00
lf - > lno_s = lr - > offset + lr - > start ;
2010-04-15 02:39:42 +04:00
if ( lf - > lno_s < 0 ) /* Overflow */
lf - > lno_s = INT_MAX ;
lf - > lno_e = lr - > offset + lr - > end ;
if ( lf - > lno_e < 0 ) /* Overflow */
2010-02-25 16:35:42 +03:00
lf - > lno_e = INT_MAX ;
2010-04-15 02:39:42 +04:00
pr_debug ( " New line range: %d to %d \n " , lf - > lno_s , lf - > lno_e ) ;
2010-01-06 17:45:34 +03:00
lr - > start = lf - > lno_s ;
lr - > end = lf - > lno_e ;
2010-04-12 21:17:35 +04:00
if ( dwarf_func_inline ( sp_die ) ) {
struct dwarf_callback_param _param ;
_param . data = ( void * ) lf ;
_param . retval = 0 ;
2010-02-25 16:35:57 +03:00
dwarf_func_inline_instances ( sp_die ,
2010-04-12 21:17:35 +04:00
line_range_inline_cb ,
& _param ) ;
param - > retval = _param . retval ;
} else
param - > retval = find_line_range_by_line ( sp_die , lf ) ;
return DWARF_CB_ABORT ;
2010-01-06 17:45:34 +03:00
}
2010-04-12 21:17:35 +04:00
return DWARF_CB_OK ;
2010-01-06 17:45:34 +03:00
}
2010-04-12 21:17:35 +04:00
static int find_line_range_by_func ( struct line_finder * lf )
2010-01-06 17:45:34 +03:00
{
2010-04-12 21:17:35 +04:00
struct dwarf_callback_param param = { . data = ( void * ) lf , . retval = 0 } ;
dwarf_getfuncs ( & lf - > cu_die , line_range_search_cb , & param , 0 ) ;
return param . retval ;
2010-01-06 17:45:34 +03:00
}
int find_line_range ( int fd , struct line_range * lr )
{
2010-02-25 16:35:42 +03:00
struct line_finder lf = { . lr = lr , . found = 0 } ;
2010-04-12 21:17:35 +04:00
int ret = 0 ;
2010-02-25 16:35:42 +03:00
Dwarf_Off off = 0 , noff ;
size_t cuhl ;
Dwarf_Die * diep ;
Dwarf * dbg ;
dbg = dwarf_begin ( fd , DWARF_C_READ ) ;
2010-04-12 21:17:35 +04:00
if ( ! dbg ) {
pr_warning ( " No dwarf info found in the vmlinux - "
" please rebuild with CONFIG_DEBUG_INFO=y. \n " ) ;
return - EBADF ;
}
2010-01-06 17:45:34 +03:00
2010-02-25 16:35:42 +03:00
/* Loop on CUs (Compilation Unit) */
2010-04-12 21:17:35 +04:00
while ( ! lf . found & & ret > = 0 ) {
if ( dwarf_nextcu ( dbg , off , & noff , & cuhl , NULL , NULL , NULL ) ! = 0 )
2010-01-06 17:45:34 +03:00
break ;
/* Get the DIE(Debugging Information Entry) of this CU */
2010-02-25 16:35:42 +03:00
diep = dwarf_offdie ( dbg , off + cuhl , & lf . cu_die ) ;
if ( ! diep )
continue ;
2010-01-06 17:45:34 +03:00
/* Check if target file is included. */
if ( lr - > file )
2010-02-25 16:36:12 +03:00
lf . fname = cu_find_realpath ( & lf . cu_die , lr - > file ) ;
2010-02-25 16:35:42 +03:00
else
2010-02-25 16:36:12 +03:00
lf . fname = 0 ;
2010-01-06 17:45:34 +03:00
2010-02-25 16:36:12 +03:00
if ( ! lr - > file | | lf . fname ) {
2010-01-06 17:45:34 +03:00
if ( lr - > function )
2010-04-12 21:17:35 +04:00
ret = find_line_range_by_func ( & lf ) ;
2010-01-06 17:45:34 +03:00
else {
lf . lno_s = lr - > start ;
2010-04-15 02:39:42 +04:00
lf . lno_e = lr - > end ;
2010-04-12 21:17:35 +04:00
ret = find_line_range_by_line ( NULL , & lf ) ;
2010-01-06 17:45:34 +03:00
}
}
2010-02-25 16:35:42 +03:00
off = noff ;
2010-01-06 17:45:34 +03:00
}
2010-02-25 16:35:42 +03:00
pr_debug ( " path: %lx \n " , ( unsigned long ) lr - > path ) ;
dwarf_end ( dbg ) ;
2010-04-12 21:17:35 +04:00
return ( ret < 0 ) ? ret : lf . found ;
2010-01-06 17:45:34 +03:00
}