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
2011-02-04 15:52:11 +03:00
# include <linux/bitops.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"
2010-06-14 23:26:30 +04:00
# include "symbol.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 ) ;
}
}
2010-10-21 14:13:41 +04:00
/* Dwarf FL wrappers */
static char * debuginfo_path ; /* Currently dummy */
static const Dwfl_Callbacks offline_callbacks = {
. find_debuginfo = dwfl_standard_find_debuginfo ,
. debuginfo_path = & debuginfo_path ,
. section_address = dwfl_offline_section_address ,
/* We use this table for core files too. */
. find_elf = dwfl_build_id_find_elf ,
} ;
/* Get a Dwarf from offline image */
static Dwarf * dwfl_init_offline_dwarf ( int fd , Dwfl * * dwflp , Dwarf_Addr * bias )
{
Dwfl_Module * mod ;
Dwarf * dbg = NULL ;
if ( ! dwflp )
return NULL ;
* dwflp = dwfl_begin ( & offline_callbacks ) ;
if ( ! * dwflp )
return NULL ;
mod = dwfl_report_offline ( * dwflp , " " , " " , fd ) ;
if ( ! mod )
goto error ;
dbg = dwfl_module_getdwarf ( mod , bias ) ;
if ( ! dbg ) {
error :
dwfl_end ( * dwflp ) ;
* dwflp = NULL ;
}
return dbg ;
}
2010-12-17 16:12:18 +03:00
# if _ELFUTILS_PREREQ(0, 148)
/* This method is buggy if elfutils is older than 0.148 */
static int __linux_kernel_find_elf ( Dwfl_Module * mod ,
void * * userdata ,
const char * module_name ,
Dwarf_Addr base ,
char * * file_name , Elf * * elfp )
{
int fd ;
const char * path = kernel_get_module_path ( module_name ) ;
pr_debug2 ( " Use file %s for %s \n " , path , module_name ) ;
if ( path ) {
fd = open ( path , O_RDONLY ) ;
if ( fd > = 0 ) {
* file_name = strdup ( path ) ;
return fd ;
}
}
/* If failed, try to call standard method */
return dwfl_linux_kernel_find_elf ( mod , userdata , module_name , base ,
file_name , elfp ) ;
}
static const Dwfl_Callbacks kernel_callbacks = {
. find_debuginfo = dwfl_standard_find_debuginfo ,
. debuginfo_path = & debuginfo_path ,
. find_elf = __linux_kernel_find_elf ,
. section_address = dwfl_linux_kernel_module_section_address ,
} ;
2010-10-21 14:13:41 +04:00
/* Get a Dwarf from live kernel image */
static Dwarf * dwfl_init_live_kernel_dwarf ( Dwarf_Addr addr , Dwfl * * dwflp ,
Dwarf_Addr * bias )
{
Dwarf * dbg ;
if ( ! dwflp )
return NULL ;
* dwflp = dwfl_begin ( & kernel_callbacks ) ;
if ( ! * dwflp )
return NULL ;
/* Load the kernel dwarves: Don't care the result here */
dwfl_linux_kernel_report_kernel ( * dwflp ) ;
dwfl_linux_kernel_report_modules ( * dwflp ) ;
dbg = dwfl_addrdwarf ( * dwflp , addr , bias ) ;
/* Here, check whether we could get a real dwarf */
if ( ! dbg ) {
2010-12-17 16:12:18 +03:00
pr_debug ( " Failed to find kernel dwarf at %lx \n " ,
( unsigned long ) addr ) ;
2010-10-21 14:13:41 +04:00
dwfl_end ( * dwflp ) ;
* dwflp = NULL ;
}
return dbg ;
}
2010-12-17 16:12:18 +03:00
# else
/* With older elfutils, this just support kernel module... */
static Dwarf * dwfl_init_live_kernel_dwarf ( Dwarf_Addr addr __used , Dwfl * * dwflp ,
Dwarf_Addr * bias )
{
int fd ;
const char * path = kernel_get_module_path ( " kernel " ) ;
if ( ! path ) {
pr_err ( " Failed to find vmlinux path \n " ) ;
return NULL ;
}
pr_debug2 ( " Use file %s for debuginfo \n " , path ) ;
fd = open ( path , O_RDONLY ) ;
if ( fd < 0 )
return NULL ;
return dwfl_init_offline_dwarf ( fd , dwflp , bias ) ;
}
# endif
2010-10-21 14:13:41 +04:00
2010-02-25 16:36:12 +03:00
/* 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-07-09 13:29:11 +04:00
/* Get DW_AT_comp_dir (should be NULL with older gcc) */
static const char * cu_get_comp_dir ( Dwarf_Die * cu_die )
{
Dwarf_Attribute attr ;
if ( dwarf_attr ( cu_die , DW_AT_comp_dir , & attr ) = = NULL )
return NULL ;
return dwarf_formstring ( & attr ) ;
}
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-07-09 13:29:17 +04:00
return name ? ( strcmp ( tname , name ) = = 0 ) : false ;
2010-03-17 01:05:58 +03:00
}
perf probe: Enable to put probe inline function call site
Enable to put probe inline function call site. This will increase line-based
probe-ability.
<Without this patch>
$ ./perf probe -L schedule:48
<schedule:48>
pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);
put_prev_task(rq, prev);
next = pick_next_task(rq);
56 if (likely(prev != next)) {
sched_info_switch(prev, next);
trace_sched_switch_out(prev, next);
perf_event_task_sched_out(prev, next);
<With this patch>
$ ./perf probe -L schedule:48
<schedule:48>
48 pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
51 idle_balance(cpu, rq);
53 put_prev_task(rq, prev);
54 next = pick_next_task(rq);
56 if (likely(prev != next)) {
57 sched_info_switch(prev, next);
58 trace_sched_switch_out(prev, next);
59 perf_event_task_sched_out(prev, next);
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Franck Bui-Huu <fbuihuu@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20110113124604.22426.48873.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-01-13 15:46:05 +03:00
/* Get callsite line number of inline-function instance */
static int die_get_call_lineno ( Dwarf_Die * in_die )
{
Dwarf_Attribute attr ;
Dwarf_Word ret ;
if ( ! dwarf_attr ( in_die , DW_AT_call_line , & attr ) )
return - ENOENT ;
dwarf_formudata ( & attr , & ret ) ;
return ( int ) ret ;
}
2010-10-21 14:13:02 +04:00
/* Get type die */
static Dwarf_Die * die_get_type ( Dwarf_Die * vr_die , Dwarf_Die * die_mem )
{
Dwarf_Attribute attr ;
if ( dwarf_attr_integrate ( vr_die , DW_AT_type , & attr ) & &
dwarf_formref_die ( & attr , die_mem ) )
return die_mem ;
else
return NULL ;
}
2010-10-21 14:13:23 +04:00
/* Get a type die, but skip qualifiers */
static Dwarf_Die * __die_get_real_type ( Dwarf_Die * vr_die , Dwarf_Die * die_mem )
2010-03-17 01:06:26 +03:00
{
int tag ;
do {
2010-10-21 14:13:02 +04:00
vr_die = die_get_type ( vr_die , die_mem ) ;
if ( ! vr_die )
break ;
tag = dwarf_tag ( vr_die ) ;
2010-03-17 01:06:26 +03:00
} while ( tag = = DW_TAG_const_type | |
tag = = DW_TAG_restrict_type | |
tag = = DW_TAG_volatile_type | |
2010-10-21 14:13:23 +04:00
tag = = DW_TAG_shared_type ) ;
return vr_die ;
}
/* Get a type die, but skip qualifiers and typedef */
static Dwarf_Die * die_get_real_type ( Dwarf_Die * vr_die , Dwarf_Die * die_mem )
{
do {
vr_die = __die_get_real_type ( vr_die , die_mem ) ;
} while ( vr_die & & dwarf_tag ( vr_die ) = = DW_TAG_typedef ) ;
2010-03-17 01:06:26 +03:00
2010-10-21 14:13:02 +04:00
return vr_die ;
2010-03-17 01:06:26 +03:00
}
2011-02-04 15:52:11 +03:00
static int die_get_attr_udata ( Dwarf_Die * tp_die , unsigned int attr_name ,
Dwarf_Word * result )
2010-04-12 21:17:15 +04:00
{
Dwarf_Attribute attr ;
2011-02-04 15:52:11 +03:00
if ( dwarf_attr ( tp_die , attr_name , & attr ) = = NULL | |
dwarf_formudata ( & attr , result ) ! = 0 )
return - ENOENT ;
return 0 ;
}
static bool die_is_signed_type ( Dwarf_Die * tp_die )
{
2010-04-12 21:17:15 +04:00
Dwarf_Word ret ;
2011-02-04 15:52:11 +03:00
if ( die_get_attr_udata ( tp_die , DW_AT_encoding , & ret ) )
2010-04-12 21:17:15 +04:00
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_Word ret ;
2011-02-04 15:52:11 +03:00
if ( die_get_attr_udata ( tp_die , DW_AT_byte_size , & ret ) )
return 0 ;
return ( int ) ret ;
}
static int die_get_bit_size ( Dwarf_Die * tp_die )
{
Dwarf_Word ret ;
if ( die_get_attr_udata ( tp_die , DW_AT_bit_size , & ret ) )
return 0 ;
return ( int ) ret ;
}
static int die_get_bit_offset ( Dwarf_Die * tp_die )
{
Dwarf_Word ret ;
if ( die_get_attr_udata ( tp_die , DW_AT_bit_offset , & ret ) )
2010-04-12 21:17:15 +04:00
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
}
2011-01-13 15:45:58 +03:00
/* Walker on lines (Note: line number will not be sorted) */
typedef int ( * line_walk_handler_t ) ( const char * fname , int lineno ,
Dwarf_Addr addr , void * data ) ;
struct __line_walk_param {
perf probe: Enable to put probe inline function call site
Enable to put probe inline function call site. This will increase line-based
probe-ability.
<Without this patch>
$ ./perf probe -L schedule:48
<schedule:48>
pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);
put_prev_task(rq, prev);
next = pick_next_task(rq);
56 if (likely(prev != next)) {
sched_info_switch(prev, next);
trace_sched_switch_out(prev, next);
perf_event_task_sched_out(prev, next);
<With this patch>
$ ./perf probe -L schedule:48
<schedule:48>
48 pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
51 idle_balance(cpu, rq);
53 put_prev_task(rq, prev);
54 next = pick_next_task(rq);
56 if (likely(prev != next)) {
57 sched_info_switch(prev, next);
58 trace_sched_switch_out(prev, next);
59 perf_event_task_sched_out(prev, next);
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Franck Bui-Huu <fbuihuu@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20110113124604.22426.48873.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-01-13 15:46:05 +03:00
const char * fname ;
2011-01-13 15:45:58 +03:00
line_walk_handler_t handler ;
void * data ;
int retval ;
} ;
perf probe: Enable to put probe inline function call site
Enable to put probe inline function call site. This will increase line-based
probe-ability.
<Without this patch>
$ ./perf probe -L schedule:48
<schedule:48>
pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);
put_prev_task(rq, prev);
next = pick_next_task(rq);
56 if (likely(prev != next)) {
sched_info_switch(prev, next);
trace_sched_switch_out(prev, next);
perf_event_task_sched_out(prev, next);
<With this patch>
$ ./perf probe -L schedule:48
<schedule:48>
48 pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
51 idle_balance(cpu, rq);
53 put_prev_task(rq, prev);
54 next = pick_next_task(rq);
56 if (likely(prev != next)) {
57 sched_info_switch(prev, next);
58 trace_sched_switch_out(prev, next);
59 perf_event_task_sched_out(prev, next);
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Franck Bui-Huu <fbuihuu@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20110113124604.22426.48873.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-01-13 15:46:05 +03:00
static int __die_walk_funclines_cb ( Dwarf_Die * in_die , void * data )
{
struct __line_walk_param * lw = data ;
Dwarf_Addr addr ;
int lineno ;
if ( dwarf_tag ( in_die ) = = DW_TAG_inlined_subroutine ) {
lineno = die_get_call_lineno ( in_die ) ;
if ( lineno > 0 & & dwarf_entrypc ( in_die , & addr ) = = 0 ) {
lw - > retval = lw - > handler ( lw - > fname , lineno , addr ,
lw - > data ) ;
if ( lw - > retval ! = 0 )
return DIE_FIND_CB_FOUND ;
}
}
return DIE_FIND_CB_SIBLING ;
}
/* Walk on lines of blocks included in given DIE */
2011-01-13 15:45:58 +03:00
static int __die_walk_funclines ( Dwarf_Die * sp_die ,
line_walk_handler_t handler , void * data )
{
perf probe: Enable to put probe inline function call site
Enable to put probe inline function call site. This will increase line-based
probe-ability.
<Without this patch>
$ ./perf probe -L schedule:48
<schedule:48>
pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);
put_prev_task(rq, prev);
next = pick_next_task(rq);
56 if (likely(prev != next)) {
sched_info_switch(prev, next);
trace_sched_switch_out(prev, next);
perf_event_task_sched_out(prev, next);
<With this patch>
$ ./perf probe -L schedule:48
<schedule:48>
48 pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
51 idle_balance(cpu, rq);
53 put_prev_task(rq, prev);
54 next = pick_next_task(rq);
56 if (likely(prev != next)) {
57 sched_info_switch(prev, next);
58 trace_sched_switch_out(prev, next);
59 perf_event_task_sched_out(prev, next);
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Franck Bui-Huu <fbuihuu@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20110113124604.22426.48873.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-01-13 15:46:05 +03:00
struct __line_walk_param lw = {
. handler = handler ,
. data = data ,
. retval = 0 ,
} ;
Dwarf_Die die_mem ;
2011-01-13 15:45:58 +03:00
Dwarf_Addr addr ;
perf probe: Enable to put probe inline function call site
Enable to put probe inline function call site. This will increase line-based
probe-ability.
<Without this patch>
$ ./perf probe -L schedule:48
<schedule:48>
pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);
put_prev_task(rq, prev);
next = pick_next_task(rq);
56 if (likely(prev != next)) {
sched_info_switch(prev, next);
trace_sched_switch_out(prev, next);
perf_event_task_sched_out(prev, next);
<With this patch>
$ ./perf probe -L schedule:48
<schedule:48>
48 pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
51 idle_balance(cpu, rq);
53 put_prev_task(rq, prev);
54 next = pick_next_task(rq);
56 if (likely(prev != next)) {
57 sched_info_switch(prev, next);
58 trace_sched_switch_out(prev, next);
59 perf_event_task_sched_out(prev, next);
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Franck Bui-Huu <fbuihuu@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20110113124604.22426.48873.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-01-13 15:46:05 +03:00
int lineno ;
2011-01-13 15:45:58 +03:00
/* Handle function declaration line */
perf probe: Enable to put probe inline function call site
Enable to put probe inline function call site. This will increase line-based
probe-ability.
<Without this patch>
$ ./perf probe -L schedule:48
<schedule:48>
pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);
put_prev_task(rq, prev);
next = pick_next_task(rq);
56 if (likely(prev != next)) {
sched_info_switch(prev, next);
trace_sched_switch_out(prev, next);
perf_event_task_sched_out(prev, next);
<With this patch>
$ ./perf probe -L schedule:48
<schedule:48>
48 pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
51 idle_balance(cpu, rq);
53 put_prev_task(rq, prev);
54 next = pick_next_task(rq);
56 if (likely(prev != next)) {
57 sched_info_switch(prev, next);
58 trace_sched_switch_out(prev, next);
59 perf_event_task_sched_out(prev, next);
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Franck Bui-Huu <fbuihuu@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20110113124604.22426.48873.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-01-13 15:46:05 +03:00
lw . fname = dwarf_decl_file ( sp_die ) ;
if ( lw . fname & & dwarf_decl_line ( sp_die , & lineno ) = = 0 & &
2011-01-13 15:45:58 +03:00
dwarf_entrypc ( sp_die , & addr ) = = 0 ) {
perf probe: Enable to put probe inline function call site
Enable to put probe inline function call site. This will increase line-based
probe-ability.
<Without this patch>
$ ./perf probe -L schedule:48
<schedule:48>
pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);
put_prev_task(rq, prev);
next = pick_next_task(rq);
56 if (likely(prev != next)) {
sched_info_switch(prev, next);
trace_sched_switch_out(prev, next);
perf_event_task_sched_out(prev, next);
<With this patch>
$ ./perf probe -L schedule:48
<schedule:48>
48 pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
51 idle_balance(cpu, rq);
53 put_prev_task(rq, prev);
54 next = pick_next_task(rq);
56 if (likely(prev != next)) {
57 sched_info_switch(prev, next);
58 trace_sched_switch_out(prev, next);
59 perf_event_task_sched_out(prev, next);
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Franck Bui-Huu <fbuihuu@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20110113124604.22426.48873.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-01-13 15:46:05 +03:00
lw . retval = handler ( lw . fname , lineno , addr , data ) ;
if ( lw . retval ! = 0 )
goto done ;
2011-01-13 15:45:58 +03:00
}
perf probe: Enable to put probe inline function call site
Enable to put probe inline function call site. This will increase line-based
probe-ability.
<Without this patch>
$ ./perf probe -L schedule:48
<schedule:48>
pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
idle_balance(cpu, rq);
put_prev_task(rq, prev);
next = pick_next_task(rq);
56 if (likely(prev != next)) {
sched_info_switch(prev, next);
trace_sched_switch_out(prev, next);
perf_event_task_sched_out(prev, next);
<With this patch>
$ ./perf probe -L schedule:48
<schedule:48>
48 pre_schedule(rq, prev);
50 if (unlikely(!rq->nr_running))
51 idle_balance(cpu, rq);
53 put_prev_task(rq, prev);
54 next = pick_next_task(rq);
56 if (likely(prev != next)) {
57 sched_info_switch(prev, next);
58 trace_sched_switch_out(prev, next);
59 perf_event_task_sched_out(prev, next);
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Franck Bui-Huu <fbuihuu@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20110113124604.22426.48873.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-01-13 15:46:05 +03:00
die_find_child ( sp_die , __die_walk_funclines_cb , & lw , & die_mem ) ;
done :
return lw . retval ;
2011-01-13 15:45:58 +03:00
}
static int __die_walk_culines_cb ( Dwarf_Die * sp_die , void * data )
{
struct __line_walk_param * lw = data ;
lw - > retval = __die_walk_funclines ( sp_die , lw - > handler , lw - > data ) ;
if ( lw - > retval ! = 0 )
return DWARF_CB_ABORT ;
return DWARF_CB_OK ;
}
/*
* Walk on lines inside given PDIE . If the PDIE is subprogram , walk only on
* the lines inside the subprogram , otherwise PDIE must be a CU DIE .
*/
static int die_walk_lines ( Dwarf_Die * pdie , line_walk_handler_t handler ,
void * data )
{
Dwarf_Lines * lines ;
Dwarf_Line * line ;
Dwarf_Addr addr ;
const char * fname ;
int lineno , ret = 0 ;
Dwarf_Die die_mem , * cu_die ;
size_t nlines , i ;
/* Get the CU die */
if ( dwarf_tag ( pdie ) = = DW_TAG_subprogram )
cu_die = dwarf_diecu ( pdie , & die_mem , NULL , NULL ) ;
else
cu_die = pdie ;
if ( ! cu_die ) {
pr_debug2 ( " Failed to get CU from subprogram \n " ) ;
return - EINVAL ;
}
/* Get lines list in the CU */
if ( dwarf_getsrclines ( cu_die , & lines , & nlines ) ! = 0 ) {
pr_debug2 ( " Failed to get source lines on this CU. \n " ) ;
return - ENOENT ;
}
pr_debug2 ( " Get %zd lines from this CU \n " , nlines ) ;
/* Walk on the lines on lines list */
for ( i = 0 ; i < nlines ; i + + ) {
line = dwarf_onesrcline ( lines , i ) ;
if ( line = = NULL | |
dwarf_lineno ( line , & lineno ) ! = 0 | |
dwarf_lineaddr ( line , & addr ) ! = 0 ) {
pr_debug2 ( " Failed to get line info. "
" Possible error in debuginfo. \n " ) ;
continue ;
}
/* Filter lines based on address */
if ( pdie ! = cu_die )
/*
* Address filtering
* The line is included in given function , and
* no inline block includes it .
*/
if ( ! dwarf_haspc ( pdie , addr ) | |
die_find_inlinefunc ( pdie , addr , & die_mem ) )
continue ;
/* Get source line */
fname = dwarf_linesrc ( line , NULL , NULL ) ;
ret = handler ( fname , lineno , addr , data ) ;
if ( ret ! = 0 )
return ret ;
}
/*
* Dwarf lines doesn ' t include function declarations and inlined
* subroutines . We have to check functions list or given function .
*/
if ( pdie ! = cu_die )
ret = __die_walk_funclines ( pdie , handler , data ) ;
else {
struct __line_walk_param param = {
. handler = handler ,
. data = data ,
. retval = 0 ,
} ;
dwarf_getfuncs ( cu_die , __die_walk_culines_cb , & param , 0 ) ;
ret = param . retval ;
}
return ret ;
}
2010-10-21 14:13:09 +04:00
struct __find_variable_param {
const char * name ;
Dwarf_Addr addr ;
} ;
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-10-21 14:13:09 +04:00
struct __find_variable_param * fvp = data ;
2010-03-17 01:05:58 +03:00
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 ) & &
2010-10-21 14:13:09 +04:00
die_compare_name ( die_mem , fvp - > name ) )
2010-03-17 01:05:58 +03:00
return DIE_FIND_CB_FOUND ;
2010-10-21 14:13:09 +04:00
if ( dwarf_haspc ( die_mem , fvp - > addr ) )
return DIE_FIND_CB_CONTINUE ;
else
return DIE_FIND_CB_SIBLING ;
2009-10-09 01:17:38 +04:00
}
2010-10-21 14:13:09 +04:00
/* Find a variable called 'name' at given address */
static Dwarf_Die * die_find_variable_at ( Dwarf_Die * sp_die , const char * name ,
Dwarf_Addr addr , Dwarf_Die * die_mem )
2009-10-09 01:17:38 +04:00
{
2010-10-21 14:13:09 +04:00
struct __find_variable_param fvp = { . name = name , . addr = addr } ;
return die_find_child ( sp_die , __die_find_variable_cb , ( void * ) & fvp ,
2010-03-17 01:05:58 +03:00
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 ) & &
2010-07-09 13:29:17 +04:00
die_compare_name ( die_mem , name ) )
2010-03-17 01:06:26 +03:00
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 ) ;
}
2010-10-21 14:13:23 +04:00
/* Get the name of given variable DIE */
static int die_get_typename ( Dwarf_Die * vr_die , char * buf , int len )
{
Dwarf_Die type ;
int tag , ret , ret2 ;
const char * tmp = " " ;
if ( __die_get_real_type ( vr_die , & type ) = = NULL )
return - ENOENT ;
tag = dwarf_tag ( & type ) ;
if ( tag = = DW_TAG_array_type | | tag = = DW_TAG_pointer_type )
tmp = " * " ;
else if ( tag = = DW_TAG_subroutine_type ) {
/* Function pointer */
ret = snprintf ( buf , len , " (function_type) " ) ;
return ( ret > = len ) ? - E2BIG : ret ;
} else {
if ( ! dwarf_diename ( & type ) )
return - ENOENT ;
if ( tag = = DW_TAG_union_type )
tmp = " union " ;
else if ( tag = = DW_TAG_structure_type )
tmp = " struct " ;
/* Write a base name */
ret = snprintf ( buf , len , " %s%s " , tmp , dwarf_diename ( & type ) ) ;
return ( ret > = len ) ? - E2BIG : ret ;
}
ret = die_get_typename ( & type , buf , len ) ;
if ( ret > 0 ) {
ret2 = snprintf ( buf + ret , len - ret , " %s " , tmp ) ;
ret = ( ret2 > = len - ret ) ? - E2BIG : ret2 + ret ;
}
return ret ;
}
/* Get the name and type of given variable DIE, stored as "type\tname" */
static int die_get_varname ( Dwarf_Die * vr_die , char * buf , int len )
{
int ret , ret2 ;
ret = die_get_typename ( vr_die , buf , len ) ;
if ( ret < 0 ) {
pr_debug ( " Failed to get type, make it unknown. \n " ) ;
ret = snprintf ( buf , len , " (unknown_type) " ) ;
}
if ( ret > 0 ) {
ret2 = snprintf ( buf + ret , len - ret , " \t %s " ,
dwarf_diename ( vr_die ) ) ;
ret = ( ret2 > = len - ret ) ? - E2BIG : ret2 + ret ;
}
return ret ;
}
2009-10-09 01:17:38 +04:00
/*
* Probe finder related functions
*/
2010-07-29 18:13:51 +04:00
static struct probe_trace_arg_ref * alloc_trace_arg_ref ( long offs )
2010-05-19 23:57:49 +04:00
{
2010-07-29 18:13:51 +04:00
struct probe_trace_arg_ref * ref ;
ref = zalloc ( sizeof ( struct probe_trace_arg_ref ) ) ;
2010-05-19 23:57:49 +04:00
if ( ref ! = NULL )
ref - > offset = offs ;
return ref ;
}
2010-10-21 14:13:23 +04:00
/*
* Convert a location into trace_arg .
* If tvar = = NULL , this just checks variable can be converted .
*/
static int convert_variable_location ( Dwarf_Die * vr_die , Dwarf_Addr addr ,
Dwarf_Op * fb_ops ,
struct probe_trace_arg * tvar )
2009-10-09 01:17:38 +04:00
{
2010-05-19 23:57:49 +04:00
Dwarf_Attribute attr ;
Dwarf_Op * op ;
size_t nops ;
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-05-19 23:57:49 +04:00
int ret ;
2010-10-21 14:13:16 +04:00
if ( dwarf_attr ( vr_die , DW_AT_external , & attr ) ! = NULL )
goto static_var ;
2010-05-19 23:57:49 +04:00
/* TODO: handle more than 1 exprs */
if ( dwarf_attr ( vr_die , DW_AT_location , & attr ) = = NULL | |
2010-10-21 14:13:23 +04:00
dwarf_getlocation_addr ( & attr , addr , & op , & nops , 1 ) < = 0 | |
2010-05-19 23:57:49 +04:00
nops = = 0 ) {
/* TODO: Support const_value */
return - ENOENT ;
}
if ( op - > atom = = DW_OP_addr ) {
2010-10-21 14:13:16 +04:00
static_var :
2010-10-21 14:13:23 +04:00
if ( ! tvar )
return 0 ;
2010-05-19 23:57:49 +04:00
/* Static variables on memory (not stack), make @varname */
ret = strlen ( dwarf_diename ( vr_die ) ) ;
tvar - > value = zalloc ( ret + 2 ) ;
if ( tvar - > value = = NULL )
return - ENOMEM ;
snprintf ( tvar - > value , ret + 2 , " @%s " , dwarf_diename ( vr_die ) ) ;
tvar - > ref = alloc_trace_arg_ref ( ( long ) offs ) ;
if ( tvar - > ref = = NULL )
return - ENOMEM ;
return 0 ;
}
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-10-21 14:13:23 +04:00
if ( fb_ops = = NULL )
2010-04-12 21:17:35 +04:00
return - ENOTSUP ;
2010-03-17 01:06:12 +03:00
ref = true ;
2010-02-25 16:35:42 +03:00
offs = op - > number ;
2010-10-21 14:13:23 +04:00
op = & fb_ops [ 0 ] ;
2010-02-25 16:35:42 +03:00
}
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 {
2010-10-21 14:13:23 +04:00
pr_debug ( " DW_OP %x is not supported. \n " , op - > atom ) ;
2010-04-12 21:17:35 +04:00
return - ENOTSUP ;
}
2009-10-09 01:17:38 +04:00
2010-10-21 14:13:23 +04:00
if ( ! tvar )
return 0 ;
2009-10-09 01:17:38 +04:00
regs = get_arch_regstr ( regn ) ;
2010-04-12 21:17:35 +04:00
if ( ! regs ) {
2010-10-21 14:13:23 +04:00
/* This should be a bug in DWARF or this tool */
2010-12-17 16:12:11 +03:00
pr_warning ( " Mapping for the register number %u "
" missing on this architecture. \n " , 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-05-19 23:57:49 +04:00
tvar - > ref = alloc_trace_arg_ref ( ( long ) offs ) ;
2010-04-12 21:17:49 +04:00
if ( tvar - > ref = = NULL )
return - ENOMEM ;
2010-03-17 01:06:12 +03:00
}
2010-04-12 21:17:35 +04:00
return 0 ;
2009-10-09 01:17:38 +04:00
}
2011-02-04 15:52:11 +03:00
# define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long))
2010-04-12 21:17:35 +04:00
static int convert_variable_type ( Dwarf_Die * vr_die ,
2010-07-29 18:13:51 +04:00
struct probe_trace_arg * tvar ,
2010-05-19 23:57:35 +04:00
const char * cast )
2010-04-12 21:17:15 +04:00
{
2010-07-29 18:13:51 +04:00
struct probe_trace_arg_ref * * ref_ptr = & tvar - > ref ;
2010-04-12 21:17:15 +04:00
Dwarf_Die type ;
char buf [ 16 ] ;
int ret ;
2010-05-19 23:57:35 +04:00
/* TODO: check all types */
if ( cast & & strcmp ( cast , " string " ) ! = 0 ) {
/* Non string type is OK */
tvar - > type = strdup ( cast ) ;
return ( tvar - > type = = NULL ) ? - ENOMEM : 0 ;
}
2011-02-04 15:52:11 +03:00
if ( die_get_bit_size ( vr_die ) ! = 0 ) {
/* This is a bitfield */
ret = snprintf ( buf , 16 , " b%d@%d/%zd " , die_get_bit_size ( vr_die ) ,
die_get_bit_offset ( vr_die ) ,
BYTES_TO_BITS ( die_get_byte_size ( vr_die ) ) ) ;
goto formatted ;
}
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
2010-05-19 23:57:42 +04:00
pr_debug ( " %s type is %s. \n " ,
dwarf_diename ( vr_die ) , dwarf_diename ( & type ) ) ;
2010-05-19 23:57:35 +04:00
if ( cast & & strcmp ( cast , " string " ) = = 0 ) { /* String type */
ret = dwarf_tag ( & type ) ;
if ( ret ! = DW_TAG_pointer_type & &
ret ! = DW_TAG_array_type ) {
pr_warning ( " Failed to cast into string: "
2010-12-17 16:12:11 +03:00
" %s(%s) is not a pointer nor array. \n " ,
2010-05-19 23:57:35 +04:00
dwarf_diename ( vr_die ) , dwarf_diename ( & type ) ) ;
return - EINVAL ;
}
if ( ret = = DW_TAG_pointer_type ) {
if ( die_get_real_type ( & type , & type ) = = NULL ) {
2010-12-17 16:12:11 +03:00
pr_warning ( " Failed to get a type "
" information. \n " ) ;
2010-05-19 23:57:35 +04:00
return - ENOENT ;
}
while ( * ref_ptr )
ref_ptr = & ( * ref_ptr ) - > next ;
/* Add new reference with offset +0 */
2010-07-29 18:13:51 +04:00
* ref_ptr = zalloc ( sizeof ( struct probe_trace_arg_ref ) ) ;
2010-05-19 23:57:35 +04:00
if ( * ref_ptr = = NULL ) {
pr_warning ( " Out of memory error \n " ) ;
return - ENOMEM ;
}
}
2010-07-09 13:29:17 +04:00
if ( ! die_compare_name ( & type , " char " ) & &
! die_compare_name ( & type , " unsigned char " ) ) {
2010-05-19 23:57:35 +04:00
pr_warning ( " Failed to cast into string: "
2010-12-17 16:12:11 +03:00
" %s is not (unsigned) char *. \n " ,
2010-05-19 23:57:35 +04:00
dwarf_diename ( vr_die ) ) ;
return - EINVAL ;
}
tvar - > type = strdup ( cast ) ;
return ( tvar - > type = = NULL ) ? - ENOMEM : 0 ;
}
2011-02-04 15:52:11 +03:00
ret = BYTES_TO_BITS ( die_get_byte_size ( & type ) ) ;
if ( ! ret )
/* No size ... try to use default type */
return 0 ;
2010-04-12 21:17:15 +04:00
2011-02-04 15:52:11 +03:00
/* Check the bitwidth */
if ( ret > MAX_BASIC_TYPE_BITS ) {
pr_info ( " %s exceeds max-bitwidth. Cut down to %d bits. \n " ,
dwarf_diename ( & type ) , MAX_BASIC_TYPE_BITS ) ;
ret = MAX_BASIC_TYPE_BITS ;
2010-04-12 21:17:15 +04:00
}
2011-02-04 15:52:11 +03:00
ret = snprintf ( buf , 16 , " %c%d " ,
die_is_signed_type ( & type ) ? ' s ' : ' u ' , ret ) ;
formatted :
if ( ret < 0 | | ret > = 16 ) {
if ( ret > = 16 )
ret = - E2BIG ;
pr_warning ( " Failed to convert variable type: %s \n " ,
strerror ( - ret ) ) ;
return ret ;
}
tvar - > type = strdup ( buf ) ;
if ( tvar - > type = = NULL )
return - ENOMEM ;
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-07-29 18:13:51 +04:00
struct probe_trace_arg_ref * * ref_ptr ,
2010-04-12 21:17:15 +04:00
Dwarf_Die * die_mem )
2010-03-17 01:06:26 +03:00
{
2010-07-29 18:13:51 +04:00
struct probe_trace_arg_ref * ref = * ref_ptr ;
2010-03-17 01:06:26 +03:00
Dwarf_Die type ;
Dwarf_Word offs ;
2010-05-19 23:57:42 +04:00
int ret , tag ;
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-05-19 23:57:42 +04:00
pr_debug2 ( " Var real type: (%x) \n " , ( unsigned ) dwarf_dieoffset ( & type ) ) ;
tag = dwarf_tag ( & type ) ;
if ( field - > name [ 0 ] = = ' [ ' & &
( tag = = DW_TAG_array_type | | tag = = DW_TAG_pointer_type ) ) {
if ( field - > next )
/* Save original type for next field */
memcpy ( die_mem , & type , sizeof ( * die_mem ) ) ;
/* Get the type of this array */
if ( die_get_real_type ( & type , & type ) = = NULL ) {
pr_warning ( " Failed to get the type of %s. \n " , varname ) ;
return - ENOENT ;
}
pr_debug2 ( " Array real type: (%x) \n " ,
( unsigned ) dwarf_dieoffset ( & type ) ) ;
if ( tag = = DW_TAG_pointer_type ) {
2010-07-29 18:13:51 +04:00
ref = zalloc ( sizeof ( struct probe_trace_arg_ref ) ) ;
2010-05-19 23:57:42 +04:00
if ( ref = = NULL )
return - ENOMEM ;
if ( * ref_ptr )
( * ref_ptr ) - > next = ref ;
else
* ref_ptr = ref ;
}
ref - > offset + = die_get_byte_size ( & type ) * field - > index ;
if ( ! field - > next )
/* Save vr_die for converting types */
memcpy ( die_mem , vr_die , sizeof ( * die_mem ) ) ;
goto next ;
} else if ( tag = = DW_TAG_pointer_type ) {
/* Check the pointer and dereference */
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-07-29 18:13:51 +04:00
ref = zalloc ( sizeof ( struct probe_trace_arg_ref ) ) ;
2010-04-12 21:17:49 +04:00
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-05-19 23:57:42 +04:00
if ( tag ! = DW_TAG_structure_type ) {
2010-04-12 21:17:35 +04:00
pr_warning ( " %s is not a data structure. \n " , varname ) ;
return - EINVAL ;
}
2010-05-19 23:57:42 +04:00
if ( field - > name [ 0 ] = = ' [ ' ) {
2010-12-17 16:12:11 +03:00
pr_err ( " Semantic error: %s is not a pointor "
" nor array. \n " , varname ) ;
2010-05-19 23:57:42 +04:00
return - EINVAL ;
}
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 ;
}
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 ;
2010-05-19 23:57:42 +04:00
next :
2010-03-17 01:06:26 +03:00
/* 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
{
2010-04-12 21:17:15 +04:00
Dwarf_Die die_mem ;
2009-10-09 01:17:38 +04:00
int ret ;
2010-05-19 23:57:49 +04:00
pr_debug ( " Converting variable %s into trace event. \n " ,
dwarf_diename ( vr_die ) ) ;
2010-02-25 16:35:42 +03:00
2010-10-21 14:13:23 +04:00
ret = convert_variable_location ( vr_die , pf - > addr , pf - > fb_ops ,
pf - > tvar ) ;
if ( ret = = - ENOENT )
pr_err ( " Failed to find the location of %s at this address. \n "
" Perhaps, it has been optimized out. \n " , pf - > pvar - > var ) ;
else if ( ret = = - ENOTSUP )
pr_err ( " Sorry, we don't support this variable location yet. \n " ) ;
else if ( pf - > pvar - > field ) {
2010-04-12 21:17:35 +04:00
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-05-19 23:57:35 +04:00
if ( ret = = 0 )
ret = convert_variable_type ( vr_die , pf - > tvar , pf - > pvar - > type ) ;
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
}
/* 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-05-19 23:57:49 +04:00
Dwarf_Die vr_die , * scopes ;
2010-04-12 21:17:22 +04:00
char buf [ 32 ] , * ptr ;
2010-05-19 23:57:49 +04:00
int ret , nscopes ;
2009-10-09 01:17:38 +04:00
2010-08-27 15:38:59 +04:00
if ( ! is_c_varname ( pf - > pvar - > var ) ) {
/* Copy raw parameters */
pf - > tvar - > value = strdup ( pf - > pvar - > var ) ;
if ( pf - > tvar - > value = = NULL )
return - ENOMEM ;
if ( pf - > pvar - > type ) {
pf - > tvar - > type = strdup ( pf - > pvar - > type ) ;
if ( pf - > tvar - > type = = NULL )
return - ENOMEM ;
}
if ( pf - > pvar - > name ) {
pf - > tvar - > name = strdup ( pf - > pvar - > name ) ;
if ( pf - > tvar - > name = = NULL )
return - ENOMEM ;
} else
pf - > tvar - > name = NULL ;
return 0 ;
}
2010-04-12 21:16:53 +04:00
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
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. */
2010-10-21 14:13:09 +04:00
if ( die_find_variable_at ( sp_die , pf - > pvar - > var , pf - > addr , & vr_die ) )
2010-05-19 23:57:49 +04:00
ret = convert_variable ( & vr_die , pf ) ;
else {
/* Search upper class */
nscopes = dwarf_getscopes_die ( sp_die , & scopes ) ;
2010-10-21 14:13:16 +04:00
while ( nscopes - - > 1 ) {
pr_debug ( " Searching variables in %s \n " ,
dwarf_diename ( & scopes [ nscopes ] ) ) ;
/* We should check this scope, so give dummy address */
if ( die_find_variable_at ( & scopes [ nscopes ] ,
pf - > pvar - > var , 0 ,
& vr_die ) ) {
2010-05-19 23:57:49 +04:00
ret = convert_variable ( & vr_die , pf ) ;
2010-10-21 14:13:16 +04:00
goto found ;
}
}
if ( scopes )
2010-05-19 23:57:49 +04:00
free ( scopes ) ;
2010-10-21 14:13:16 +04:00
ret = - ENOENT ;
2010-05-19 23:57:49 +04:00
}
2010-10-21 14:13:16 +04:00
found :
2010-05-19 23:57:49 +04:00
if ( ret < 0 )
2010-04-12 21:17:35 +04:00
pr_warning ( " Failed to find '%s' in this function. \n " ,
pf - > pvar - > var ) ;
2010-05-19 23:57:49 +04:00
return ret ;
2009-10-09 01:17:38 +04:00
}
2010-10-21 14:13:23 +04:00
/* Convert subprogram DIE to trace point */
static int convert_to_trace_point ( Dwarf_Die * sp_die , Dwarf_Addr paddr ,
bool retprobe , struct probe_trace_point * tp )
2009-10-09 01:17:38 +04:00
{
2010-02-25 16:35:50 +03:00
Dwarf_Addr eaddr ;
2010-02-25 16:35:42 +03:00
const char * name ;
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 ) {
2010-12-17 16:12:11 +03:00
pr_warning ( " Failed to get entry address of %s \n " ,
2010-04-12 21:17:35 +04:00
dwarf_diename ( sp_die ) ) ;
return - ENOENT ;
}
2010-10-21 14:13:23 +04:00
tp - > symbol = strdup ( name ) ;
if ( tp - > symbol = = NULL )
2010-04-12 21:17:56 +04:00
return - ENOMEM ;
2010-10-21 14:13:23 +04:00
tp - > offset = ( unsigned long ) ( paddr - eaddr ) ;
2010-03-17 01:06:12 +03:00
} else
2009-10-09 01:17:38 +04:00
/* This function has no name. */
2010-10-21 14:13:23 +04:00
tp - > offset = ( unsigned long ) paddr ;
2010-03-17 01:06:12 +03:00
2010-08-27 15:38:53 +04:00
/* Return probe must be on the head of a subprogram */
2010-10-21 14:13:23 +04:00
if ( retprobe ) {
if ( eaddr ! = paddr ) {
2010-08-27 15:38:53 +04:00
pr_warning ( " Return probe must be on the head of "
2010-12-17 16:12:11 +03:00
" a real function. \n " ) ;
2010-08-27 15:38:53 +04:00
return - EINVAL ;
}
2010-10-21 14:13:23 +04:00
tp - > retprobe = true ;
2010-08-27 15:38:53 +04:00
}
2010-10-21 14:13:23 +04:00
return 0 ;
}
/* Call probe_finder callback with real subprogram DIE */
static int call_probe_finder ( Dwarf_Die * sp_die , struct probe_finder * pf )
{
Dwarf_Die die_mem ;
Dwarf_Attribute fb_attr ;
size_t nops ;
int ret ;
/* If no real subprogram, find a real one */
if ( ! sp_die | | dwarf_tag ( sp_die ) ! = DW_TAG_subprogram ) {
sp_die = die_find_real_subprogram ( & pf - > cu_die ,
pf - > addr , & die_mem ) ;
if ( ! sp_die ) {
pr_warning ( " Failed to find probe point in any "
" functions. \n " ) ;
return - ENOENT ;
}
}
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 ) {
2010-12-17 16:12:11 +03:00
pr_warning ( " Failed to get call frame on 0x%jx \n " ,
2010-04-12 21:17:35 +04:00
( 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
2010-10-21 14:13:23 +04:00
/* Call finder's callback handler */
ret = pf - > callback ( sp_die , pf ) ;
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-10-21 14:13:23 +04:00
return ret ;
2009-10-09 01:17:38 +04:00
}
2011-01-13 15:45:58 +03:00
static int probe_point_line_walker ( const char * fname , int lineno ,
Dwarf_Addr addr , void * data )
2009-10-09 01:17:38 +04:00
{
2011-01-13 15:45:58 +03:00
struct probe_finder * pf = data ;
int ret ;
2009-10-09 01:17:38 +04:00
2011-01-13 15:45:58 +03:00
if ( lineno ! = pf - > lno | | strtailcmp ( fname , pf - > fname ) ! = 0 )
return 0 ;
2009-10-09 01:17:38 +04:00
2011-01-13 15:45:58 +03:00
pf - > addr = addr ;
ret = call_probe_finder ( NULL , pf ) ;
2009-10-27 23:43:19 +03:00
2011-01-13 15:45:58 +03:00
/* Continue if no error, because the line will be in inline function */
2011-02-21 19:23:57 +03:00
return ret < 0 ? ret : 0 ;
2011-01-13 15:45:58 +03:00
}
2010-02-25 16:35:42 +03:00
2011-01-13 15:45:58 +03:00
/* Find probe point from its line number */
static int find_probe_point_by_line ( struct probe_finder * pf )
{
return die_walk_lines ( & pf - > cu_die , probe_point_line_walker , pf ) ;
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 )
{
2011-01-13 13:18:30 +03:00
FILE * fp ;
char * line = NULL ;
size_t line_len ;
ssize_t len ;
int count = 0 , linenum = 1 ;
fp = fopen ( fname , " r " ) ;
if ( ! fp ) {
pr_warning ( " Failed to open %s: %s \n " , fname , strerror ( errno ) ) ;
2010-05-19 06:04:28 +04:00
return - errno ;
2010-04-12 21:17:35 +04:00
}
2011-01-13 13:18:30 +03:00
while ( ( len = getline ( & line , & line_len , fp ) ) > 0 ) {
2010-05-19 06:04:28 +04:00
2011-01-13 13:18:30 +03:00
if ( line [ len - 1 ] = = ' \n ' )
line [ len - 1 ] = ' \0 ' ;
if ( strlazymatch ( line , pat ) ) {
line_list__add_line ( head , linenum ) ;
count + + ;
2010-02-25 16:36:12 +03:00
}
2011-01-13 13:18:30 +03:00
linenum + + ;
2010-02-25 16:36:12 +03:00
}
2011-01-13 13:18:30 +03:00
if ( ferror ( fp ) )
count = - errno ;
free ( line ) ;
fclose ( fp ) ;
if ( count = = 0 )
pr_debug ( " No matched lines found in %s. \n " , fname ) ;
return count ;
2010-02-25 16:36:12 +03:00
}
2011-01-13 15:45:58 +03:00
static int probe_point_lazy_walker ( const char * fname , int lineno ,
Dwarf_Addr addr , void * data )
{
struct probe_finder * pf = data ;
int ret ;
if ( ! line_list__has_line ( & pf - > lcache , lineno ) | |
strtailcmp ( fname , pf - > fname ) ! = 0 )
return 0 ;
pr_debug ( " Probe line found: line:%d addr:0x%llx \n " ,
lineno , ( unsigned long long ) addr ) ;
pf - > addr = addr ;
ret = call_probe_finder ( NULL , pf ) ;
/*
* Continue if no error , because the lazy pattern will match
* to other lines
*/
return ret < 0 ? : 0 ;
}
2010-02-25 16:36:12 +03:00
/* 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
{
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 ) ;
2011-01-13 13:18:30 +03:00
if ( ret < = 0 )
2010-04-12 21:17:35 +04:00
return ret ;
2010-02-25 16:36:12 +03:00
}
2011-01-13 15:45:58 +03:00
return die_walk_lines ( sp_die , probe_point_lazy_walker , pf ) ;
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 ) {
2010-12-17 16:12:11 +03:00
pr_warning ( " Failed to get entry address of %s. \n " ,
2010-04-12 21:17:35 +04:00
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-10-21 14:13:23 +04:00
param - > retval = call_probe_finder ( 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 | |
2010-07-09 13:29:17 +04:00
! die_compare_name ( sp_die , pp - > function ) )
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 ) {
2010-12-17 16:12:11 +03:00
pr_warning ( " Failed to get entry address of "
" %s. \n " , dwarf_diename ( sp_die ) ) ;
2010-04-12 21:17:35 +04:00
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-10-21 14:13:23 +04:00
param - > retval = call_probe_finder ( 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-10-21 14:13:23 +04:00
/* Find probe points from debuginfo */
static int find_probes ( int fd , struct probe_finder * pf )
2009-10-09 01:17:38 +04:00
{
2010-10-21 14:13:23 +04:00
struct perf_probe_point * pp = & pf - > pev - > point ;
2010-02-25 16:35:42 +03:00
Dwarf_Off off , noff ;
size_t cuhl ;
Dwarf_Die * diep ;
2010-10-21 14:13:41 +04:00
Dwarf * dbg = NULL ;
Dwfl * dwfl ;
Dwarf_Addr bias ; /* Currently ignored */
2010-04-12 21:17:35 +04:00
int ret = 0 ;
2010-02-25 16:35:42 +03:00
2010-10-21 14:13:41 +04:00
dbg = dwfl_init_offline_dwarf ( fd , & dwfl , & bias ) ;
2010-04-12 21:17:35 +04:00
if ( ! dbg ) {
2010-12-17 16:12:11 +03:00
pr_warning ( " No debug information found in the vmlinux - "
2010-04-12 21:17:35 +04:00
" please rebuild with CONFIG_DEBUG_INFO=y. \n " ) ;
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 */
2010-10-21 14:13:23 +04:00
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-10-21 14:13:23 +04: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-10-21 14:13:23 +04:00
diep = dwarf_offdie ( dbg , off + cuhl , & pf - > cu_die ) ;
2010-02-25 16:35:42 +03:00
if ( ! diep )
continue ;
2009-10-09 01:17:38 +04:00
/* Check if target file is included. */
if ( pp - > file )
2010-10-21 14:13:23 +04:00
pf - > fname = cu_find_realpath ( & pf - > cu_die , pp - > file ) ;
2010-02-25 16:35:42 +03:00
else
2010-10-21 14:13:23 +04:00
pf - > fname = NULL ;
2009-10-09 01:17:38 +04:00
2010-10-21 14:13:23 +04:00
if ( ! pp - > file | | pf - > fname ) {
2009-10-09 01:17:38 +04:00
if ( pp - > function )
2010-10-21 14:13:23 +04:00
ret = find_probe_point_by_func ( pf ) ;
2010-02-25 16:36:12 +03:00
else if ( pp - > lazy_line )
2010-10-21 14:13:23 +04:00
ret = find_probe_point_lazy ( NULL , pf ) ;
2009-10-27 23:43:19 +03:00
else {
2010-10-21 14:13:23 +04:00
pf - > lno = pp - > line ;
ret = find_probe_point_by_line ( pf ) ;
2009-10-27 23:43:19 +03:00
}
2011-02-21 19:23:57 +03:00
if ( ret ! = DWARF_CB_OK )
break ;
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-10-21 14:13:23 +04:00
line_list__free ( & pf - > lcache ) ;
2010-10-21 14:13:41 +04:00
if ( dwfl )
dwfl_end ( dwfl ) ;
2009-10-09 01:17:38 +04:00
2010-10-21 14:13:23 +04:00
return ret ;
}
/* Add a found probe point into trace event list */
static int add_probe_trace_event ( Dwarf_Die * sp_die , struct probe_finder * pf )
{
struct trace_event_finder * tf =
container_of ( pf , struct trace_event_finder , pf ) ;
struct probe_trace_event * tev ;
int ret , i ;
/* Check number of tevs */
if ( tf - > ntevs = = tf - > max_tevs ) {
pr_warning ( " Too many( > %d) probe point found. \n " ,
tf - > max_tevs ) ;
return - ERANGE ;
}
tev = & tf - > tevs [ tf - > ntevs + + ] ;
ret = convert_to_trace_point ( sp_die , pf - > addr , pf - > pev - > point . retprobe ,
& tev - > point ) ;
if ( ret < 0 )
return ret ;
pr_debug ( " Probe point found: %s+%lu \n " , tev - > point . symbol ,
tev - > point . offset ) ;
/* Find each argument */
tev - > nargs = pf - > pev - > nargs ;
tev - > args = zalloc ( sizeof ( struct probe_trace_arg ) * tev - > nargs ) ;
if ( tev - > args = = NULL )
return - ENOMEM ;
for ( i = 0 ; i < pf - > pev - > nargs ; i + + ) {
pf - > pvar = & pf - > pev - > args [ i ] ;
pf - > tvar = & tev - > args [ i ] ;
ret = find_variable ( sp_die , pf ) ;
if ( ret ! = 0 )
return ret ;
}
return 0 ;
}
/* Find probe_trace_events specified by perf_probe_event from debuginfo */
int find_probe_trace_events ( int fd , struct perf_probe_event * pev ,
struct probe_trace_event * * tevs , int max_tevs )
{
struct trace_event_finder tf = {
. pf = { . pev = pev , . callback = add_probe_trace_event } ,
. max_tevs = max_tevs } ;
int ret ;
/* Allocate result tevs array */
* tevs = zalloc ( sizeof ( struct probe_trace_event ) * max_tevs ) ;
if ( * tevs = = NULL )
return - ENOMEM ;
tf . tevs = * tevs ;
tf . ntevs = 0 ;
ret = find_probes ( fd , & tf . pf ) ;
if ( ret < 0 ) {
free ( * tevs ) ;
* tevs = NULL ;
return ret ;
}
return ( ret < 0 ) ? ret : tf . ntevs ;
}
# define MAX_VAR_LEN 64
/* Collect available variables in this scope */
static int collect_variables_cb ( Dwarf_Die * die_mem , void * data )
{
struct available_var_finder * af = data ;
struct variable_list * vl ;
char buf [ MAX_VAR_LEN ] ;
int tag , ret ;
vl = & af - > vls [ af - > nvls - 1 ] ;
tag = dwarf_tag ( die_mem ) ;
if ( tag = = DW_TAG_formal_parameter | |
tag = = DW_TAG_variable ) {
ret = convert_variable_location ( die_mem , af - > pf . addr ,
af - > pf . fb_ops , NULL ) ;
if ( ret = = 0 ) {
ret = die_get_varname ( die_mem , buf , MAX_VAR_LEN ) ;
2010-10-21 14:13:35 +04:00
pr_debug2 ( " Add new var: %s \n " , buf ) ;
2010-10-21 14:13:23 +04:00
if ( ret > 0 )
strlist__add ( vl - > vars , buf ) ;
}
}
2010-10-21 14:13:35 +04:00
if ( af - > child & & dwarf_haspc ( die_mem , af - > pf . addr ) )
2010-10-21 14:13:23 +04:00
return DIE_FIND_CB_CONTINUE ;
else
return DIE_FIND_CB_SIBLING ;
}
/* Add a found vars into available variables list */
static int add_available_vars ( Dwarf_Die * sp_die , struct probe_finder * pf )
{
struct available_var_finder * af =
container_of ( pf , struct available_var_finder , pf ) ;
struct variable_list * vl ;
2010-10-21 14:13:35 +04:00
Dwarf_Die die_mem , * scopes = NULL ;
int ret , nscopes ;
2010-10-21 14:13:23 +04:00
/* Check number of tevs */
if ( af - > nvls = = af - > max_vls ) {
pr_warning ( " Too many( > %d) probe point found. \n " , af - > max_vls ) ;
return - ERANGE ;
}
vl = & af - > vls [ af - > nvls + + ] ;
ret = convert_to_trace_point ( sp_die , pf - > addr , pf - > pev - > point . retprobe ,
& vl - > point ) ;
if ( ret < 0 )
return ret ;
pr_debug ( " Probe point found: %s+%lu \n " , vl - > point . symbol ,
vl - > point . offset ) ;
/* Find local variables */
vl - > vars = strlist__new ( true , NULL ) ;
if ( vl - > vars = = NULL )
return - ENOMEM ;
2010-10-21 14:13:35 +04:00
af - > child = true ;
2010-10-21 14:13:23 +04:00
die_find_child ( sp_die , collect_variables_cb , ( void * ) af , & die_mem ) ;
2010-10-21 14:13:35 +04:00
/* Find external variables */
if ( ! af - > externs )
goto out ;
/* Don't need to search child DIE for externs. */
af - > child = false ;
nscopes = dwarf_getscopes_die ( sp_die , & scopes ) ;
while ( nscopes - - > 1 )
die_find_child ( & scopes [ nscopes ] , collect_variables_cb ,
( void * ) af , & die_mem ) ;
if ( scopes )
free ( scopes ) ;
out :
2010-10-21 14:13:23 +04:00
if ( strlist__empty ( vl - > vars ) ) {
strlist__delete ( vl - > vars ) ;
vl - > vars = NULL ;
}
return ret ;
}
/* Find available variables at given probe point */
int find_available_vars_at ( int fd , struct perf_probe_event * pev ,
2010-10-21 14:13:35 +04:00
struct variable_list * * vls , int max_vls ,
bool externs )
2010-10-21 14:13:23 +04:00
{
struct available_var_finder af = {
. pf = { . pev = pev , . callback = add_available_vars } ,
2010-10-21 14:13:35 +04:00
. max_vls = max_vls , . externs = externs } ;
2010-10-21 14:13:23 +04:00
int ret ;
/* Allocate result vls array */
* vls = zalloc ( sizeof ( struct variable_list ) * max_vls ) ;
if ( * vls = = NULL )
return - ENOMEM ;
af . vls = * vls ;
af . nvls = 0 ;
ret = find_probes ( fd , & af . pf ) ;
if ( ret < 0 ) {
/* Free vlist for error */
while ( af . nvls - - ) {
if ( af . vls [ af . nvls ] . point . symbol )
free ( af . vls [ af . nvls ] . point . symbol ) ;
if ( af . vls [ af . nvls ] . vars )
strlist__delete ( af . vls [ af . nvls ] . vars ) ;
}
free ( af . vls ) ;
* vls = NULL ;
return ret ;
}
return ( ret < 0 ) ? ret : af . nvls ;
2009-10-09 01:17:38 +04:00
}
2010-03-17 01:06:19 +03:00
/* Reverse search */
2010-10-21 14:13:41 +04:00
int find_perf_probe_point ( unsigned long addr , struct perf_probe_point * ppt )
2010-03-17 01:06:19 +03:00
{
Dwarf_Die cudie , spdie , indie ;
2010-10-21 14:13:41 +04:00
Dwarf * dbg = NULL ;
Dwfl * dwfl = NULL ;
2010-03-17 01:06:19 +03:00
Dwarf_Line * line ;
2010-10-21 14:13:41 +04:00
Dwarf_Addr laddr , eaddr , bias = 0 ;
2010-03-17 01:06:19 +03:00
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
2010-10-21 14:13:41 +04:00
/* Open the live linux kernel */
dbg = dwfl_init_live_kernel_dwarf ( addr , & dwfl , & bias ) ;
if ( ! dbg ) {
2010-12-17 16:12:11 +03:00
pr_warning ( " No debug information found in the vmlinux - "
2010-10-21 14:13:41 +04:00
" please rebuild with CONFIG_DEBUG_INFO=y. \n " ) ;
ret = - EINVAL ;
goto end ;
}
2010-03-17 01:06:19 +03:00
2010-10-21 14:13:41 +04:00
/* Adjust address with bias */
addr + = bias ;
2010-03-17 01:06:19 +03:00
/* Find cu die */
2010-10-21 14:13:41 +04:00
if ( ! dwarf_addrdie ( dbg , ( Dwarf_Addr ) addr - bias , & cudie ) ) {
2010-12-17 16:12:11 +03:00
pr_warning ( " Failed to find debug information for address %lx \n " ,
addr ) ;
2010-04-02 20:50:59 +04:00
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 :
2010-10-21 14:13:41 +04:00
if ( dwfl )
dwfl_end ( dwfl ) ;
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 )
{
2010-07-09 13:28:59 +04:00
/* Copy source path */
2010-04-15 02:40:07 +04:00
if ( ! lr - > path ) {
2010-07-09 13:28:59 +04:00
lr - > path = strdup ( src ) ;
if ( lr - > path = = NULL )
return - ENOMEM ;
2010-04-15 02:40:07 +04:00
}
return line_list__add_line ( & lr - > line_list , lineno ) ;
}
2011-01-13 15:45:58 +03:00
static int line_range_walk_cb ( const char * fname , int lineno ,
Dwarf_Addr addr __used ,
void * data )
2010-04-15 02:40:07 +04:00
{
2011-01-13 15:45:58 +03:00
struct line_finder * lf = data ;
2010-04-15 02:40:07 +04:00
2011-01-13 15:45:58 +03:00
if ( ( strtailcmp ( fname , lf - > fname ) ! = 0 ) | |
2010-04-15 02:40:07 +04:00
( lf - > lno_s > lineno | | lf - > lno_e < lineno ) )
2011-01-13 15:45:58 +03:00
return 0 ;
2010-04-15 02:40:07 +04:00
2011-01-13 15:45:58 +03:00
if ( line_range_add_line ( fname , lineno , lf - > lr ) < 0 )
return - EINVAL ;
2010-04-15 02:40:07 +04:00
2011-01-13 15:45:58 +03:00
return 0 ;
2010-04-15 02:40:07 +04:00
}
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
{
2011-01-13 15:45:58 +03:00
int ret ;
2010-04-15 02:40:07 +04:00
2011-01-13 15:45:58 +03:00
ret = die_walk_lines ( sp_die ? : & lf - > cu_die , line_range_walk_cb , lf ) ;
2010-04-15 02:40:07 +04:00
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 & &
2010-07-09 13:29:17 +04:00
die_compare_name ( sp_die , lr - > function ) ) {
2010-02-25 16:35:50 +03:00
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 ;
2010-10-21 14:13:41 +04:00
Dwarf * dbg = NULL ;
Dwfl * dwfl ;
Dwarf_Addr bias ; /* Currently ignored */
2010-07-09 13:29:11 +04:00
const char * comp_dir ;
2010-02-25 16:35:42 +03:00
2010-10-21 14:13:41 +04:00
dbg = dwfl_init_offline_dwarf ( fd , & dwfl , & bias ) ;
2010-04-12 21:17:35 +04:00
if ( ! dbg ) {
2010-12-17 16:12:11 +03:00
pr_warning ( " No debug information found in the vmlinux - "
2010-04-12 21:17:35 +04:00
" 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-07-09 13:29:11 +04:00
/* Store comp_dir */
if ( lf . found ) {
comp_dir = cu_get_comp_dir ( & lf . cu_die ) ;
if ( comp_dir ) {
lr - > comp_dir = strdup ( comp_dir ) ;
if ( ! lr - > comp_dir )
ret = - ENOMEM ;
}
}
2010-07-09 13:28:59 +04:00
pr_debug ( " path: %s \n " , lr - > path ) ;
2010-10-21 14:13:41 +04:00
dwfl_end ( dwfl ) ;
2010-04-12 21:17:35 +04:00
return ( ret < 0 ) ? ret : lf . found ;
2010-01-06 17:45:34 +03:00
}