2012-04-06 02:47:53 +04:00
/*
* Copyright ( C ) 2009 , 2010 Red Hat Inc , Steven Rostedt < srostedt @ redhat . com >
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ;
* version 2.1 of the License ( not later ! )
*
* 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 Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
2012-09-08 00:32:46 +04:00
* License along with this program ; if not , see < http : //www.gnu.org/licenses>
2012-04-06 02:47:53 +04:00
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* The parts for function graph printing was taken and modified from the
* Linux Kernel that were written by
* - Copyright ( C ) 2009 Frederic Weisbecker ,
* Frederic Weisbecker gave his permission to relicense the code to
* the Lesser General Public License .
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <stdarg.h>
# include <ctype.h>
# include <errno.h>
2012-08-07 21:43:14 +04:00
# include <stdint.h>
2012-09-13 00:30:50 +04:00
# include <limits.h>
2012-04-06 02:47:53 +04:00
2014-12-19 05:11:11 +03:00
# include <netinet/ip6.h>
2012-04-06 02:47:53 +04:00
# include "event-parse.h"
2012-04-06 02:47:55 +04:00
# include "event-utils.h"
2012-04-06 02:47:53 +04:00
static const char * input_buf ;
static unsigned long long input_buf_ptr ;
static unsigned long long input_buf_siz ;
2012-04-06 02:47:58 +04:00
static int is_flag_field ;
static int is_symbolic_field ;
2012-04-06 02:47:53 +04:00
static int show_warning = 1 ;
# define do_warning(fmt, ...) \
do { \
if ( show_warning ) \
warning ( fmt , # # __VA_ARGS__ ) ; \
} while ( 0 )
2014-03-19 05:22:53 +04:00
# define do_warning_event(event, fmt, ...) \
do { \
if ( ! show_warning ) \
continue ; \
\
if ( event ) \
warning ( " [%s:%s] " fmt , event - > system , \
event - > name , # # __VA_ARGS__ ) ; \
else \
warning ( fmt , # # __VA_ARGS__ ) ; \
} while ( 0 )
2012-04-06 02:47:53 +04:00
static void init_input_buf ( const char * buf , unsigned long long size )
{
input_buf = buf ;
input_buf_siz = size ;
input_buf_ptr = 0 ;
}
const char * pevent_get_input_buf ( void )
{
return input_buf ;
}
unsigned long long pevent_get_input_buf_ptr ( void )
{
return input_buf_ptr ;
}
struct event_handler {
struct event_handler * next ;
int id ;
const char * sys_name ;
const char * event_name ;
pevent_event_handler_func func ;
void * context ;
} ;
struct pevent_func_params {
struct pevent_func_params * next ;
enum pevent_func_arg_type type ;
} ;
struct pevent_function_handler {
struct pevent_function_handler * next ;
enum pevent_func_arg_type ret_type ;
char * name ;
pevent_func_handler func ;
struct pevent_func_params * params ;
int nr_args ;
} ;
static unsigned long long
process_defined_func ( struct trace_seq * s , void * data , int size ,
struct event_format * event , struct print_arg * arg ) ;
static void free_func_handle ( struct pevent_function_handler * func ) ;
/**
* pevent_buffer_init - init buffer for parsing
* @ buf : buffer to parse
* @ size : the size of the buffer
*
* For use with pevent_read_token ( ) , this initializes the internal
* buffer that pevent_read_token ( ) will parse .
*/
void pevent_buffer_init ( const char * buf , unsigned long long size )
{
init_input_buf ( buf , size ) ;
}
void breakpoint ( void )
{
static int x ;
x + + ;
}
struct print_arg * alloc_arg ( void )
{
2012-09-12 22:39:59 +04:00
return calloc ( 1 , sizeof ( struct print_arg ) ) ;
2012-04-06 02:47:53 +04:00
}
struct cmdline {
char * comm ;
int pid ;
} ;
static int cmdline_cmp ( const void * a , const void * b )
{
const struct cmdline * ca = a ;
const struct cmdline * cb = b ;
if ( ca - > pid < cb - > pid )
return - 1 ;
if ( ca - > pid > cb - > pid )
return 1 ;
return 0 ;
}
struct cmdline_list {
struct cmdline_list * next ;
char * comm ;
int pid ;
} ;
static int cmdline_init ( struct pevent * pevent )
{
struct cmdline_list * cmdlist = pevent - > cmdlist ;
struct cmdline_list * item ;
struct cmdline * cmdlines ;
int i ;
2012-09-13 00:30:50 +04:00
cmdlines = malloc ( sizeof ( * cmdlines ) * pevent - > cmdline_count ) ;
if ( ! cmdlines )
return - 1 ;
2012-04-06 02:47:53 +04:00
i = 0 ;
while ( cmdlist ) {
cmdlines [ i ] . pid = cmdlist - > pid ;
cmdlines [ i ] . comm = cmdlist - > comm ;
i + + ;
item = cmdlist ;
cmdlist = cmdlist - > next ;
free ( item ) ;
}
qsort ( cmdlines , pevent - > cmdline_count , sizeof ( * cmdlines ) , cmdline_cmp ) ;
pevent - > cmdlines = cmdlines ;
pevent - > cmdlist = NULL ;
return 0 ;
}
2012-11-10 00:40:47 +04:00
static const char * find_cmdline ( struct pevent * pevent , int pid )
2012-04-06 02:47:53 +04:00
{
const struct cmdline * comm ;
struct cmdline key ;
if ( ! pid )
return " <idle> " ;
2012-09-13 00:30:50 +04:00
if ( ! pevent - > cmdlines & & cmdline_init ( pevent ) )
return " <not enough memory for cmdlines!> " ;
2012-04-06 02:47:53 +04:00
key . pid = pid ;
comm = bsearch ( & key , pevent - > cmdlines , pevent - > cmdline_count ,
sizeof ( * pevent - > cmdlines ) , cmdline_cmp ) ;
if ( comm )
return comm - > comm ;
return " <...> " ;
}
/**
* pevent_pid_is_registered - return if a pid has a cmdline registered
* @ pevent : handle for the pevent
* @ pid : The pid to check if it has a cmdline registered with .
*
* Returns 1 if the pid has a cmdline mapped to it
* 0 otherwise .
*/
int pevent_pid_is_registered ( struct pevent * pevent , int pid )
{
const struct cmdline * comm ;
struct cmdline key ;
if ( ! pid )
return 1 ;
2012-09-13 00:30:50 +04:00
if ( ! pevent - > cmdlines & & cmdline_init ( pevent ) )
return 0 ;
2012-04-06 02:47:53 +04:00
key . pid = pid ;
comm = bsearch ( & key , pevent - > cmdlines , pevent - > cmdline_count ,
sizeof ( * pevent - > cmdlines ) , cmdline_cmp ) ;
if ( comm )
return 1 ;
return 0 ;
}
/*
* If the command lines have been converted to an array , then
* we must add this pid . This is much slower than when cmdlines
* are added before the array is initialized .
*/
static int add_new_comm ( struct pevent * pevent , const char * comm , int pid )
{
struct cmdline * cmdlines = pevent - > cmdlines ;
const struct cmdline * cmdline ;
struct cmdline key ;
if ( ! pid )
return 0 ;
/* avoid duplicates */
key . pid = pid ;
cmdline = bsearch ( & key , pevent - > cmdlines , pevent - > cmdline_count ,
sizeof ( * pevent - > cmdlines ) , cmdline_cmp ) ;
if ( cmdline ) {
errno = EEXIST ;
return - 1 ;
}
cmdlines = realloc ( cmdlines , sizeof ( * cmdlines ) * ( pevent - > cmdline_count + 1 ) ) ;
if ( ! cmdlines ) {
errno = ENOMEM ;
return - 1 ;
}
cmdlines [ pevent - > cmdline_count ] . comm = strdup ( comm ) ;
2012-09-13 00:30:50 +04:00
if ( ! cmdlines [ pevent - > cmdline_count ] . comm ) {
free ( cmdlines ) ;
errno = ENOMEM ;
return - 1 ;
}
cmdlines [ pevent - > cmdline_count ] . pid = pid ;
2012-04-06 02:47:53 +04:00
if ( cmdlines [ pevent - > cmdline_count ] . comm )
pevent - > cmdline_count + + ;
qsort ( cmdlines , pevent - > cmdline_count , sizeof ( * cmdlines ) , cmdline_cmp ) ;
pevent - > cmdlines = cmdlines ;
return 0 ;
}
/**
* pevent_register_comm - register a pid / comm mapping
* @ pevent : handle for the pevent
* @ comm : the command line to register
* @ pid : the pid to map the command line to
*
* This adds a mapping to search for command line names with
* a given pid . The comm is duplicated .
*/
int pevent_register_comm ( struct pevent * pevent , const char * comm , int pid )
{
struct cmdline_list * item ;
if ( pevent - > cmdlines )
return add_new_comm ( pevent , comm , pid ) ;
2012-09-13 00:30:50 +04:00
item = malloc ( sizeof ( * item ) ) ;
if ( ! item )
return - 1 ;
2015-03-24 16:57:49 +03:00
if ( comm )
item - > comm = strdup ( comm ) ;
else
item - > comm = strdup ( " <...> " ) ;
2012-09-13 00:30:50 +04:00
if ( ! item - > comm ) {
free ( item ) ;
return - 1 ;
}
2012-04-06 02:47:53 +04:00
item - > pid = pid ;
item - > next = pevent - > cmdlist ;
pevent - > cmdlist = item ;
pevent - > cmdline_count + + ;
return 0 ;
}
2015-03-24 16:57:50 +03:00
int pevent_register_trace_clock ( struct pevent * pevent , const char * trace_clock )
2013-11-02 01:53:53 +04:00
{
2015-03-24 16:57:50 +03:00
pevent - > trace_clock = strdup ( trace_clock ) ;
if ( ! pevent - > trace_clock ) {
errno = ENOMEM ;
return - 1 ;
}
return 0 ;
2013-11-02 01:53:53 +04:00
}
2012-04-06 02:47:53 +04:00
struct func_map {
unsigned long long addr ;
char * func ;
char * mod ;
} ;
struct func_list {
struct func_list * next ;
unsigned long long addr ;
char * func ;
char * mod ;
} ;
static int func_cmp ( const void * a , const void * b )
{
const struct func_map * fa = a ;
const struct func_map * fb = b ;
if ( fa - > addr < fb - > addr )
return - 1 ;
if ( fa - > addr > fb - > addr )
return 1 ;
return 0 ;
}
/*
* We are searching for a record in between , not an exact
* match .
*/
static int func_bcmp ( const void * a , const void * b )
{
const struct func_map * fa = a ;
const struct func_map * fb = b ;
if ( ( fa - > addr = = fb - > addr ) | |
( fa - > addr > fb - > addr & &
fa - > addr < ( fb + 1 ) - > addr ) )
return 0 ;
if ( fa - > addr < fb - > addr )
return - 1 ;
return 1 ;
}
static int func_map_init ( struct pevent * pevent )
{
struct func_list * funclist ;
struct func_list * item ;
struct func_map * func_map ;
int i ;
2012-09-13 00:30:50 +04:00
func_map = malloc ( sizeof ( * func_map ) * ( pevent - > func_count + 1 ) ) ;
if ( ! func_map )
return - 1 ;
2012-04-06 02:47:53 +04:00
funclist = pevent - > funclist ;
i = 0 ;
while ( funclist ) {
func_map [ i ] . func = funclist - > func ;
func_map [ i ] . addr = funclist - > addr ;
func_map [ i ] . mod = funclist - > mod ;
i + + ;
item = funclist ;
funclist = funclist - > next ;
free ( item ) ;
}
qsort ( func_map , pevent - > func_count , sizeof ( * func_map ) , func_cmp ) ;
/*
* Add a special record at the end .
*/
func_map [ pevent - > func_count ] . func = NULL ;
func_map [ pevent - > func_count ] . addr = 0 ;
func_map [ pevent - > func_count ] . mod = NULL ;
pevent - > func_map = func_map ;
pevent - > funclist = NULL ;
return 0 ;
}
static struct func_map *
2015-07-22 18:36:55 +03:00
__find_func ( struct pevent * pevent , unsigned long long addr )
2012-04-06 02:47:53 +04:00
{
struct func_map * func ;
struct func_map key ;
if ( ! pevent - > func_map )
func_map_init ( pevent ) ;
key . addr = addr ;
func = bsearch ( & key , pevent - > func_map , pevent - > func_count ,
sizeof ( * pevent - > func_map ) , func_bcmp ) ;
return func ;
}
2015-07-22 18:36:55 +03:00
struct func_resolver {
pevent_func_resolver_t * func ;
void * priv ;
struct func_map map ;
} ;
/**
* pevent_set_function_resolver - set an alternative function resolver
* @ pevent : handle for the pevent
* @ resolver : function to be used
* @ priv : resolver function private state .
*
* Some tools may have already a way to resolve kernel functions , allow them to
* keep using it instead of duplicating all the entries inside
* pevent - > funclist .
*/
int pevent_set_function_resolver ( struct pevent * pevent ,
pevent_func_resolver_t * func , void * priv )
{
struct func_resolver * resolver = malloc ( sizeof ( * resolver ) ) ;
if ( resolver = = NULL )
return - 1 ;
resolver - > func = func ;
resolver - > priv = priv ;
free ( pevent - > func_resolver ) ;
pevent - > func_resolver = resolver ;
return 0 ;
}
/**
* pevent_reset_function_resolver - reset alternative function resolver
* @ pevent : handle for the pevent
*
* Stop using whatever alternative resolver was set , use the default
* one instead .
*/
void pevent_reset_function_resolver ( struct pevent * pevent )
{
free ( pevent - > func_resolver ) ;
pevent - > func_resolver = NULL ;
}
static struct func_map *
find_func ( struct pevent * pevent , unsigned long long addr )
{
struct func_map * map ;
if ( ! pevent - > func_resolver )
return __find_func ( pevent , addr ) ;
map = & pevent - > func_resolver - > map ;
map - > mod = NULL ;
map - > addr = addr ;
map - > func = pevent - > func_resolver - > func ( pevent - > func_resolver - > priv ,
& map - > addr , & map - > mod ) ;
if ( map - > func = = NULL )
return NULL ;
return map ;
}
2012-04-06 02:47:53 +04:00
/**
* pevent_find_function - find a function by a given address
* @ pevent : handle for the pevent
* @ addr : the address to find the function with
*
* Returns a pointer to the function stored that has the given
* address . Note , the address does not have to be exact , it
* will select the function that would contain the address .
*/
const char * pevent_find_function ( struct pevent * pevent , unsigned long long addr )
{
struct func_map * map ;
map = find_func ( pevent , addr ) ;
if ( ! map )
return NULL ;
return map - > func ;
}
/**
* pevent_find_function_address - find a function address by a given address
* @ pevent : handle for the pevent
* @ addr : the address to find the function with
*
* Returns the address the function starts at . This can be used in
* conjunction with pevent_find_function to print both the function
* name and the function offset .
*/
unsigned long long
pevent_find_function_address ( struct pevent * pevent , unsigned long long addr )
{
struct func_map * map ;
map = find_func ( pevent , addr ) ;
if ( ! map )
return 0 ;
return map - > addr ;
}
/**
* pevent_register_function - register a function with a given address
* @ pevent : handle for the pevent
* @ function : the function name to register
* @ addr : the address the function starts at
* @ mod : the kernel module the function may be in ( NULL for none )
*
* This registers a function name with an address and module .
* The @ func passed in is duplicated .
*/
int pevent_register_function ( struct pevent * pevent , char * func ,
unsigned long long addr , char * mod )
{
2012-09-13 00:30:50 +04:00
struct func_list * item = malloc ( sizeof ( * item ) ) ;
2012-04-06 02:47:53 +04:00
2012-09-13 00:30:50 +04:00
if ( ! item )
return - 1 ;
2012-04-06 02:47:53 +04:00
item - > next = pevent - > funclist ;
item - > func = strdup ( func ) ;
2012-09-13 00:30:50 +04:00
if ( ! item - > func )
goto out_free ;
if ( mod ) {
2012-04-06 02:47:53 +04:00
item - > mod = strdup ( mod ) ;
2012-09-13 00:30:50 +04:00
if ( ! item - > mod )
goto out_free_func ;
} else
2012-04-06 02:47:53 +04:00
item - > mod = NULL ;
item - > addr = addr ;
2012-04-09 06:54:31 +04:00
pevent - > funclist = item ;
2012-04-06 02:47:53 +04:00
pevent - > func_count + + ;
return 0 ;
2012-09-13 00:30:50 +04:00
out_free_func :
free ( item - > func ) ;
item - > func = NULL ;
out_free :
free ( item ) ;
errno = ENOMEM ;
return - 1 ;
2012-04-06 02:47:53 +04:00
}
/**
* pevent_print_funcs - print out the stored functions
* @ pevent : handle for the pevent
*
* This prints out the stored functions .
*/
void pevent_print_funcs ( struct pevent * pevent )
{
int i ;
if ( ! pevent - > func_map )
func_map_init ( pevent ) ;
for ( i = 0 ; i < ( int ) pevent - > func_count ; i + + ) {
printf ( " %016llx %s " ,
pevent - > func_map [ i ] . addr ,
pevent - > func_map [ i ] . func ) ;
if ( pevent - > func_map [ i ] . mod )
printf ( " [%s] \n " , pevent - > func_map [ i ] . mod ) ;
else
printf ( " \n " ) ;
}
}
struct printk_map {
unsigned long long addr ;
char * printk ;
} ;
struct printk_list {
struct printk_list * next ;
unsigned long long addr ;
char * printk ;
} ;
static int printk_cmp ( const void * a , const void * b )
{
2012-04-09 06:54:29 +04:00
const struct printk_map * pa = a ;
const struct printk_map * pb = b ;
2012-04-06 02:47:53 +04:00
2012-04-09 06:54:29 +04:00
if ( pa - > addr < pb - > addr )
2012-04-06 02:47:53 +04:00
return - 1 ;
2012-04-09 06:54:29 +04:00
if ( pa - > addr > pb - > addr )
2012-04-06 02:47:53 +04:00
return 1 ;
return 0 ;
}
2012-09-13 00:30:50 +04:00
static int printk_map_init ( struct pevent * pevent )
2012-04-06 02:47:53 +04:00
{
struct printk_list * printklist ;
struct printk_list * item ;
struct printk_map * printk_map ;
int i ;
2012-09-13 00:30:50 +04:00
printk_map = malloc ( sizeof ( * printk_map ) * ( pevent - > printk_count + 1 ) ) ;
if ( ! printk_map )
return - 1 ;
2012-04-06 02:47:53 +04:00
printklist = pevent - > printklist ;
i = 0 ;
while ( printklist ) {
printk_map [ i ] . printk = printklist - > printk ;
printk_map [ i ] . addr = printklist - > addr ;
i + + ;
item = printklist ;
printklist = printklist - > next ;
free ( item ) ;
}
qsort ( printk_map , pevent - > printk_count , sizeof ( * printk_map ) , printk_cmp ) ;
pevent - > printk_map = printk_map ;
pevent - > printklist = NULL ;
2012-09-13 00:30:50 +04:00
return 0 ;
2012-04-06 02:47:53 +04:00
}
static struct printk_map *
find_printk ( struct pevent * pevent , unsigned long long addr )
{
struct printk_map * printk ;
struct printk_map key ;
2012-09-13 00:30:50 +04:00
if ( ! pevent - > printk_map & & printk_map_init ( pevent ) )
return NULL ;
2012-04-06 02:47:53 +04:00
key . addr = addr ;
printk = bsearch ( & key , pevent - > printk_map , pevent - > printk_count ,
sizeof ( * pevent - > printk_map ) , printk_cmp ) ;
return printk ;
}
/**
* pevent_register_print_string - register a string by its address
* @ pevent : handle for the pevent
* @ fmt : the string format to register
* @ addr : the address the string was located at
*
* This registers a string by the address it was stored in the kernel .
* The @ fmt passed in is duplicated .
*/
2013-11-02 01:53:54 +04:00
int pevent_register_print_string ( struct pevent * pevent , const char * fmt ,
2012-04-06 02:47:53 +04:00
unsigned long long addr )
{
2012-09-13 00:30:50 +04:00
struct printk_list * item = malloc ( sizeof ( * item ) ) ;
2013-11-02 01:53:54 +04:00
char * p ;
2012-04-06 02:47:53 +04:00
2012-09-13 00:30:50 +04:00
if ( ! item )
return - 1 ;
2012-04-06 02:47:53 +04:00
item - > next = pevent - > printklist ;
item - > addr = addr ;
2013-11-02 01:53:54 +04:00
/* Strip off quotes and '\n' from the end */
if ( fmt [ 0 ] = = ' " ' )
fmt + + ;
2012-09-13 00:30:50 +04:00
item - > printk = strdup ( fmt ) ;
2012-04-09 06:54:31 +04:00
if ( ! item - > printk )
2012-09-13 00:30:50 +04:00
goto out_free ;
2012-04-09 06:54:31 +04:00
2013-11-02 01:53:54 +04:00
p = item - > printk + strlen ( item - > printk ) - 1 ;
if ( * p = = ' " ' )
* p = 0 ;
p - = 2 ;
if ( strcmp ( p , " \\ n " ) = = 0 )
* p = 0 ;
2012-04-09 06:54:31 +04:00
pevent - > printklist = item ;
2012-04-06 02:47:53 +04:00
pevent - > printk_count + + ;
return 0 ;
2012-09-13 00:30:50 +04:00
out_free :
free ( item ) ;
errno = ENOMEM ;
return - 1 ;
2012-04-06 02:47:53 +04:00
}
/**
* pevent_print_printk - print out the stored strings
* @ pevent : handle for the pevent
*
* This prints the string formats that were stored .
*/
void pevent_print_printk ( struct pevent * pevent )
{
int i ;
if ( ! pevent - > printk_map )
printk_map_init ( pevent ) ;
for ( i = 0 ; i < ( int ) pevent - > printk_count ; i + + ) {
printf ( " %016llx %s \n " ,
pevent - > printk_map [ i ] . addr ,
pevent - > printk_map [ i ] . printk ) ;
}
}
static struct event_format * alloc_event ( void )
{
2012-09-12 22:39:59 +04:00
return calloc ( 1 , sizeof ( struct event_format ) ) ;
2012-04-06 02:47:53 +04:00
}
2012-09-13 00:30:50 +04:00
static int add_event ( struct pevent * pevent , struct event_format * event )
2012-04-06 02:47:53 +04:00
{
int i ;
2012-09-13 00:30:50 +04:00
struct event_format * * events = realloc ( pevent - > events , sizeof ( event ) *
( pevent - > nr_events + 1 ) ) ;
if ( ! events )
return - 1 ;
2012-04-06 02:47:53 +04:00
2012-09-13 00:30:50 +04:00
pevent - > events = events ;
2012-04-06 02:47:53 +04:00
for ( i = 0 ; i < pevent - > nr_events ; i + + ) {
if ( pevent - > events [ i ] - > id > event - > id )
break ;
}
if ( i < pevent - > nr_events )
memmove ( & pevent - > events [ i + 1 ] ,
& pevent - > events [ i ] ,
sizeof ( event ) * ( pevent - > nr_events - i ) ) ;
pevent - > events [ i ] = event ;
pevent - > nr_events + + ;
event - > pevent = pevent ;
2012-09-13 00:30:50 +04:00
return 0 ;
2012-04-06 02:47:53 +04:00
}
static int event_item_type ( enum event_type type )
{
switch ( type ) {
case EVENT_ITEM . . . EVENT_SQUOTE :
return 1 ;
case EVENT_ERROR . . . EVENT_DELIM :
default :
return 0 ;
}
}
static void free_flag_sym ( struct print_flag_sym * fsym )
{
struct print_flag_sym * next ;
while ( fsym ) {
next = fsym - > next ;
free ( fsym - > value ) ;
free ( fsym - > str ) ;
free ( fsym ) ;
fsym = next ;
}
}
static void free_arg ( struct print_arg * arg )
{
struct print_arg * farg ;
if ( ! arg )
return ;
switch ( arg - > type ) {
case PRINT_ATOM :
free ( arg - > atom . atom ) ;
break ;
case PRINT_FIELD :
free ( arg - > field . name ) ;
break ;
case PRINT_FLAGS :
free_arg ( arg - > flags . field ) ;
free ( arg - > flags . delim ) ;
free_flag_sym ( arg - > flags . flags ) ;
break ;
case PRINT_SYMBOL :
free_arg ( arg - > symbol . field ) ;
free_flag_sym ( arg - > symbol . symbols ) ;
break ;
2012-06-27 04:41:41 +04:00
case PRINT_HEX :
free_arg ( arg - > hex . field ) ;
free_arg ( arg - > hex . size ) ;
break ;
2015-03-24 14:07:19 +03:00
case PRINT_INT_ARRAY :
free_arg ( arg - > int_array . field ) ;
free_arg ( arg - > int_array . count ) ;
free_arg ( arg - > int_array . el_size ) ;
break ;
2012-04-06 02:47:53 +04:00
case PRINT_TYPE :
free ( arg - > typecast . type ) ;
free_arg ( arg - > typecast . item ) ;
break ;
case PRINT_STRING :
case PRINT_BSTRING :
free ( arg - > string . string ) ;
break ;
2014-06-03 07:20:16 +04:00
case PRINT_BITMASK :
free ( arg - > bitmask . bitmask ) ;
break ;
2012-04-06 02:47:53 +04:00
case PRINT_DYNAMIC_ARRAY :
free ( arg - > dynarray . index ) ;
break ;
case PRINT_OP :
free ( arg - > op . op ) ;
free_arg ( arg - > op . left ) ;
free_arg ( arg - > op . right ) ;
break ;
case PRINT_FUNC :
while ( arg - > func . args ) {
farg = arg - > func . args ;
arg - > func . args = farg - > next ;
free_arg ( farg ) ;
}
break ;
case PRINT_NULL :
default :
break ;
}
free ( arg ) ;
}
static enum event_type get_type ( int ch )
{
if ( ch = = ' \n ' )
return EVENT_NEWLINE ;
if ( isspace ( ch ) )
return EVENT_SPACE ;
if ( isalnum ( ch ) | | ch = = ' _ ' )
return EVENT_ITEM ;
if ( ch = = ' \' ' )
return EVENT_SQUOTE ;
if ( ch = = ' " ' )
return EVENT_DQUOTE ;
if ( ! isprint ( ch ) )
return EVENT_NONE ;
if ( ch = = ' ( ' | | ch = = ' ) ' | | ch = = ' , ' )
return EVENT_DELIM ;
return EVENT_OP ;
}
static int __read_char ( void )
{
if ( input_buf_ptr > = input_buf_siz )
return - 1 ;
return input_buf [ input_buf_ptr + + ] ;
}
static int __peek_char ( void )
{
if ( input_buf_ptr > = input_buf_siz )
return - 1 ;
return input_buf [ input_buf_ptr ] ;
}
/**
* pevent_peek_char - peek at the next character that will be read
*
* Returns the next character read , or - 1 if end of buffer .
*/
int pevent_peek_char ( void )
{
return __peek_char ( ) ;
}
2012-04-09 06:54:30 +04:00
static int extend_token ( char * * tok , char * buf , int size )
{
char * newtok = realloc ( * tok , size ) ;
if ( ! newtok ) {
free ( * tok ) ;
* tok = NULL ;
return - 1 ;
}
if ( ! * tok )
strcpy ( newtok , buf ) ;
else
strcat ( newtok , buf ) ;
* tok = newtok ;
return 0 ;
}
2012-04-06 02:47:53 +04:00
static enum event_type force_token ( const char * str , char * * tok ) ;
static enum event_type __read_token ( char * * tok )
{
char buf [ BUFSIZ ] ;
int ch , last_ch , quote_ch , next_ch ;
int i = 0 ;
int tok_size = 0 ;
enum event_type type ;
* tok = NULL ;
ch = __read_char ( ) ;
if ( ch < 0 )
return EVENT_NONE ;
type = get_type ( ch ) ;
if ( type = = EVENT_NONE )
return type ;
buf [ i + + ] = ch ;
switch ( type ) {
case EVENT_NEWLINE :
case EVENT_DELIM :
2012-09-12 22:39:59 +04:00
if ( asprintf ( tok , " %c " , ch ) < 0 )
return EVENT_ERROR ;
2012-04-06 02:47:53 +04:00
return type ;
case EVENT_OP :
switch ( ch ) {
case ' - ' :
next_ch = __peek_char ( ) ;
if ( next_ch = = ' > ' ) {
buf [ i + + ] = __read_char ( ) ;
break ;
}
/* fall through */
case ' + ' :
case ' | ' :
case ' & ' :
case ' > ' :
case ' < ' :
last_ch = ch ;
ch = __peek_char ( ) ;
if ( ch ! = last_ch )
goto test_equal ;
buf [ i + + ] = __read_char ( ) ;
switch ( last_ch ) {
case ' > ' :
case ' < ' :
goto test_equal ;
default :
break ;
}
break ;
case ' ! ' :
case ' = ' :
goto test_equal ;
default : /* what should we do instead? */
break ;
}
buf [ i ] = 0 ;
* tok = strdup ( buf ) ;
return type ;
test_equal :
ch = __peek_char ( ) ;
if ( ch = = ' = ' )
buf [ i + + ] = __read_char ( ) ;
goto out ;
case EVENT_DQUOTE :
case EVENT_SQUOTE :
/* don't keep quotes */
i - - ;
quote_ch = ch ;
last_ch = 0 ;
concat :
do {
if ( i = = ( BUFSIZ - 1 ) ) {
buf [ i ] = 0 ;
tok_size + = BUFSIZ ;
2012-04-09 06:54:30 +04:00
if ( extend_token ( tok , buf , tok_size ) < 0 )
return EVENT_NONE ;
2012-04-06 02:47:53 +04:00
i = 0 ;
}
last_ch = ch ;
ch = __read_char ( ) ;
buf [ i + + ] = ch ;
/* the '\' '\' will cancel itself */
if ( ch = = ' \\ ' & & last_ch = = ' \\ ' )
last_ch = 0 ;
} while ( ch ! = quote_ch | | last_ch = = ' \\ ' ) ;
/* remove the last quote */
i - - ;
/*
* For strings ( double quotes ) check the next token .
* If it is another string , concatinate the two .
*/
if ( type = = EVENT_DQUOTE ) {
unsigned long long save_input_buf_ptr = input_buf_ptr ;
do {
ch = __read_char ( ) ;
} while ( isspace ( ch ) ) ;
if ( ch = = ' " ' )
goto concat ;
input_buf_ptr = save_input_buf_ptr ;
}
goto out ;
case EVENT_ERROR . . . EVENT_SPACE :
case EVENT_ITEM :
default :
break ;
}
while ( get_type ( __peek_char ( ) ) = = type ) {
if ( i = = ( BUFSIZ - 1 ) ) {
buf [ i ] = 0 ;
2012-04-09 06:54:30 +04:00
tok_size + = BUFSIZ ;
2012-04-06 02:47:53 +04:00
2012-04-09 06:54:30 +04:00
if ( extend_token ( tok , buf , tok_size ) < 0 )
2012-04-06 02:47:53 +04:00
return EVENT_NONE ;
i = 0 ;
}
ch = __read_char ( ) ;
buf [ i + + ] = ch ;
}
out :
buf [ i ] = 0 ;
2012-04-09 06:54:30 +04:00
if ( extend_token ( tok , buf , tok_size + i + 1 ) < 0 )
2012-04-06 02:47:53 +04:00
return EVENT_NONE ;
if ( type = = EVENT_ITEM ) {
/*
* Older versions of the kernel has a bug that
* creates invalid symbols and will break the mac80211
* parsing . This is a work around to that bug .
*
* See Linux kernel commit :
* 811 cb50baf63461ce0bdb234927046131fc7fa8b
*/
if ( strcmp ( * tok , " LOCAL_PR_FMT " ) = = 0 ) {
free ( * tok ) ;
* tok = NULL ;
return force_token ( " \" \ %s \" " , tok ) ;
} else if ( strcmp ( * tok , " STA_PR_FMT " ) = = 0 ) {
free ( * tok ) ;
* tok = NULL ;
return force_token ( " \" sta:%pM \" " , tok ) ;
} else if ( strcmp ( * tok , " VIF_PR_FMT " ) = = 0 ) {
free ( * tok ) ;
* tok = NULL ;
return force_token ( " \" vif:%p(%d) \ " " , tok ) ;
}
}
return type ;
}
static enum event_type force_token ( const char * str , char * * tok )
{
const char * save_input_buf ;
unsigned long long save_input_buf_ptr ;
unsigned long long save_input_buf_siz ;
enum event_type type ;
/* save off the current input pointers */
save_input_buf = input_buf ;
save_input_buf_ptr = input_buf_ptr ;
save_input_buf_siz = input_buf_siz ;
init_input_buf ( str , strlen ( str ) ) ;
type = __read_token ( tok ) ;
/* reset back to original token */
input_buf = save_input_buf ;
input_buf_ptr = save_input_buf_ptr ;
input_buf_siz = save_input_buf_siz ;
return type ;
}
static void free_token ( char * tok )
{
if ( tok )
free ( tok ) ;
}
static enum event_type read_token ( char * * tok )
{
enum event_type type ;
for ( ; ; ) {
type = __read_token ( tok ) ;
if ( type ! = EVENT_SPACE )
return type ;
free_token ( * tok ) ;
}
/* not reached */
* tok = NULL ;
return EVENT_NONE ;
}
/**
* pevent_read_token - access to utilites to use the pevent parser
* @ tok : The token to return
*
* This will parse tokens from the string given by
* pevent_init_data ( ) .
*
* Returns the token type .
*/
enum event_type pevent_read_token ( char * * tok )
{
return read_token ( tok ) ;
}
/**
* pevent_free_token - free a token returned by pevent_read_token
* @ token : the token to free
*/
void pevent_free_token ( char * token )
{
free_token ( token ) ;
}
/* no newline */
static enum event_type read_token_item ( char * * tok )
{
enum event_type type ;
for ( ; ; ) {
type = __read_token ( tok ) ;
if ( type ! = EVENT_SPACE & & type ! = EVENT_NEWLINE )
return type ;
free_token ( * tok ) ;
* tok = NULL ;
}
/* not reached */
* tok = NULL ;
return EVENT_NONE ;
}
static int test_type ( enum event_type type , enum event_type expect )
{
if ( type ! = expect ) {
do_warning ( " Error: expected type %d but read %d " ,
expect , type ) ;
return - 1 ;
}
return 0 ;
}
static int test_type_token ( enum event_type type , const char * token ,
enum event_type expect , const char * expect_tok )
{
if ( type ! = expect ) {
do_warning ( " Error: expected type %d but read %d " ,
expect , type ) ;
return - 1 ;
}
if ( strcmp ( token , expect_tok ) ! = 0 ) {
do_warning ( " Error: expected '%s' but read '%s' " ,
expect_tok , token ) ;
return - 1 ;
}
return 0 ;
}
static int __read_expect_type ( enum event_type expect , char * * tok , int newline_ok )
{
enum event_type type ;
if ( newline_ok )
type = read_token ( tok ) ;
else
type = read_token_item ( tok ) ;
return test_type ( type , expect ) ;
}
static int read_expect_type ( enum event_type expect , char * * tok )
{
return __read_expect_type ( expect , tok , 1 ) ;
}
static int __read_expected ( enum event_type expect , const char * str ,
int newline_ok )
{
enum event_type type ;
char * token ;
int ret ;
if ( newline_ok )
type = read_token ( & token ) ;
else
type = read_token_item ( & token ) ;
ret = test_type_token ( type , token , expect , str ) ;
free_token ( token ) ;
return ret ;
}
static int read_expected ( enum event_type expect , const char * str )
{
return __read_expected ( expect , str , 1 ) ;
}
static int read_expected_item ( enum event_type expect , const char * str )
{
return __read_expected ( expect , str , 0 ) ;
}
static char * event_read_name ( void )
{
char * token ;
if ( read_expected ( EVENT_ITEM , " name " ) < 0 )
return NULL ;
if ( read_expected ( EVENT_OP , " : " ) < 0 )
return NULL ;
if ( read_expect_type ( EVENT_ITEM , & token ) < 0 )
goto fail ;
return token ;
fail :
free_token ( token ) ;
return NULL ;
}
static int event_read_id ( void )
{
char * token ;
int id ;
if ( read_expected_item ( EVENT_ITEM , " ID " ) < 0 )
return - 1 ;
if ( read_expected ( EVENT_OP , " : " ) < 0 )
return - 1 ;
if ( read_expect_type ( EVENT_ITEM , & token ) < 0 )
goto fail ;
id = strtoul ( token , NULL , 0 ) ;
free_token ( token ) ;
return id ;
fail :
free_token ( token ) ;
return - 1 ;
}
static int field_is_string ( struct format_field * field )
{
if ( ( field - > flags & FIELD_IS_ARRAY ) & &
( strstr ( field - > type , " char " ) | | strstr ( field - > type , " u8 " ) | |
strstr ( field - > type , " s8 " ) ) )
return 1 ;
return 0 ;
}
static int field_is_dynamic ( struct format_field * field )
{
if ( strncmp ( field - > type , " __data_loc " , 10 ) = = 0 )
return 1 ;
return 0 ;
}
static int field_is_long ( struct format_field * field )
{
/* includes long long */
if ( strstr ( field - > type , " long " ) )
return 1 ;
return 0 ;
}
2013-01-25 00:46:43 +04:00
static unsigned int type_size ( const char * name )
{
/* This covers all FIELD_IS_STRING types. */
static struct {
const char * type ;
unsigned int size ;
} table [ ] = {
{ " u8 " , 1 } ,
{ " u16 " , 2 } ,
{ " u32 " , 4 } ,
{ " u64 " , 8 } ,
{ " s8 " , 1 } ,
{ " s16 " , 2 } ,
{ " s32 " , 4 } ,
{ " s64 " , 8 } ,
{ " char " , 1 } ,
{ } ,
} ;
int i ;
for ( i = 0 ; table [ i ] . type ; i + + ) {
if ( ! strcmp ( table [ i ] . type , name ) )
return table [ i ] . size ;
}
return 0 ;
}
2012-04-06 02:47:53 +04:00
static int event_read_fields ( struct event_format * event , struct format_field * * fields )
{
struct format_field * field = NULL ;
enum event_type type ;
char * token ;
char * last_token ;
int count = 0 ;
do {
2013-01-25 00:46:43 +04:00
unsigned int size_dynamic = 0 ;
2012-04-06 02:47:53 +04:00
type = read_token ( & token ) ;
if ( type = = EVENT_NEWLINE ) {
free_token ( token ) ;
return count ;
}
count + + ;
if ( test_type_token ( type , token , EVENT_ITEM , " field " ) )
goto fail ;
free_token ( token ) ;
type = read_token ( & token ) ;
/*
* The ftrace fields may still use the " special " name .
* Just ignore it .
*/
if ( event - > flags & EVENT_FL_ISFTRACE & &
type = = EVENT_ITEM & & strcmp ( token , " special " ) = = 0 ) {
free_token ( token ) ;
type = read_token ( & token ) ;
}
if ( test_type_token ( type , token , EVENT_OP , " : " ) < 0 )
goto fail ;
free_token ( token ) ;
if ( read_expect_type ( EVENT_ITEM , & token ) < 0 )
goto fail ;
last_token = token ;
2012-09-12 22:39:59 +04:00
field = calloc ( 1 , sizeof ( * field ) ) ;
if ( ! field )
goto fail ;
2012-04-06 02:47:53 +04:00
field - > event = event ;
/* read the rest of the type */
for ( ; ; ) {
type = read_token ( & token ) ;
if ( type = = EVENT_ITEM | |
( type = = EVENT_OP & & strcmp ( token , " * " ) = = 0 ) | |
/*
* Some of the ftrace fields are broken and have
* an illegal " . " in them .
*/
( event - > flags & EVENT_FL_ISFTRACE & &
type = = EVENT_OP & & strcmp ( token , " . " ) = = 0 ) ) {
if ( strcmp ( token , " * " ) = = 0 )
field - > flags | = FIELD_IS_POINTER ;
if ( field - > type ) {
2012-04-09 06:54:33 +04:00
char * new_type ;
new_type = realloc ( field - > type ,
strlen ( field - > type ) +
strlen ( last_token ) + 2 ) ;
if ( ! new_type ) {
free ( last_token ) ;
goto fail ;
}
field - > type = new_type ;
2012-04-06 02:47:53 +04:00
strcat ( field - > type , " " ) ;
strcat ( field - > type , last_token ) ;
free ( last_token ) ;
} else
field - > type = last_token ;
last_token = token ;
continue ;
}
break ;
}
if ( ! field - > type ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: no type found " , __func__ ) ;
2012-04-06 02:47:53 +04:00
goto fail ;
}
2015-04-18 18:50:18 +03:00
field - > name = field - > alias = last_token ;
2012-04-06 02:47:53 +04:00
if ( test_type ( type , EVENT_OP ) )
goto fail ;
if ( strcmp ( token , " [ " ) = = 0 ) {
enum event_type last_type = type ;
char * brackets = token ;
2012-04-09 06:54:33 +04:00
char * new_brackets ;
2012-04-06 02:47:53 +04:00
int len ;
field - > flags | = FIELD_IS_ARRAY ;
type = read_token ( & token ) ;
if ( type = = EVENT_ITEM )
field - > arraylen = strtoul ( token , NULL , 0 ) ;
else
field - > arraylen = 0 ;
while ( strcmp ( token , " ] " ) ! = 0 ) {
if ( last_type = = EVENT_ITEM & &
type = = EVENT_ITEM )
len = 2 ;
else
len = 1 ;
last_type = type ;
2012-04-09 06:54:33 +04:00
new_brackets = realloc ( brackets ,
strlen ( brackets ) +
strlen ( token ) + len ) ;
if ( ! new_brackets ) {
free ( brackets ) ;
goto fail ;
}
brackets = new_brackets ;
2012-04-06 02:47:53 +04:00
if ( len = = 2 )
strcat ( brackets , " " ) ;
strcat ( brackets , token ) ;
/* We only care about the last token */
field - > arraylen = strtoul ( token , NULL , 0 ) ;
free_token ( token ) ;
type = read_token ( & token ) ;
if ( type = = EVENT_NONE ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " failed to find token " ) ;
2012-04-06 02:47:53 +04:00
goto fail ;
}
}
free_token ( token ) ;
2012-04-09 06:54:33 +04:00
new_brackets = realloc ( brackets , strlen ( brackets ) + 2 ) ;
if ( ! new_brackets ) {
free ( brackets ) ;
goto fail ;
}
brackets = new_brackets ;
2012-04-06 02:47:53 +04:00
strcat ( brackets , " ] " ) ;
/* add brackets to type */
type = read_token ( & token ) ;
/*
* If the next token is not an OP , then it is of
* the format : type [ ] item ;
*/
if ( type = = EVENT_ITEM ) {
2012-04-09 06:54:33 +04:00
char * new_type ;
new_type = realloc ( field - > type ,
strlen ( field - > type ) +
strlen ( field - > name ) +
strlen ( brackets ) + 2 ) ;
if ( ! new_type ) {
free ( brackets ) ;
goto fail ;
}
field - > type = new_type ;
2012-04-06 02:47:53 +04:00
strcat ( field - > type , " " ) ;
strcat ( field - > type , field - > name ) ;
2013-01-25 00:46:43 +04:00
size_dynamic = type_size ( field - > name ) ;
2012-04-06 02:47:53 +04:00
free_token ( field - > name ) ;
strcat ( field - > type , brackets ) ;
2015-04-18 18:50:18 +03:00
field - > name = field - > alias = token ;
2012-04-06 02:47:53 +04:00
type = read_token ( & token ) ;
} else {
2012-04-09 06:54:33 +04:00
char * new_type ;
new_type = realloc ( field - > type ,
strlen ( field - > type ) +
strlen ( brackets ) + 1 ) ;
if ( ! new_type ) {
free ( brackets ) ;
goto fail ;
}
field - > type = new_type ;
2012-04-06 02:47:53 +04:00
strcat ( field - > type , brackets ) ;
}
free ( brackets ) ;
}
if ( field_is_string ( field ) )
field - > flags | = FIELD_IS_STRING ;
if ( field_is_dynamic ( field ) )
field - > flags | = FIELD_IS_DYNAMIC ;
if ( field_is_long ( field ) )
field - > flags | = FIELD_IS_LONG ;
if ( test_type_token ( type , token , EVENT_OP , " ; " ) )
goto fail ;
free_token ( token ) ;
if ( read_expected ( EVENT_ITEM , " offset " ) < 0 )
goto fail_expect ;
if ( read_expected ( EVENT_OP , " : " ) < 0 )
goto fail_expect ;
if ( read_expect_type ( EVENT_ITEM , & token ) )
goto fail ;
field - > offset = strtoul ( token , NULL , 0 ) ;
free_token ( token ) ;
if ( read_expected ( EVENT_OP , " ; " ) < 0 )
goto fail_expect ;
if ( read_expected ( EVENT_ITEM , " size " ) < 0 )
goto fail_expect ;
if ( read_expected ( EVENT_OP , " : " ) < 0 )
goto fail_expect ;
if ( read_expect_type ( EVENT_ITEM , & token ) )
goto fail ;
field - > size = strtoul ( token , NULL , 0 ) ;
free_token ( token ) ;
if ( read_expected ( EVENT_OP , " ; " ) < 0 )
goto fail_expect ;
type = read_token ( & token ) ;
if ( type ! = EVENT_NEWLINE ) {
/* newer versions of the kernel have a "signed" type */
if ( test_type_token ( type , token , EVENT_ITEM , " signed " ) )
goto fail ;
free_token ( token ) ;
if ( read_expected ( EVENT_OP , " : " ) < 0 )
goto fail_expect ;
if ( read_expect_type ( EVENT_ITEM , & token ) )
goto fail ;
2013-01-18 23:51:25 +04:00
if ( strtoul ( token , NULL , 0 ) )
field - > flags | = FIELD_IS_SIGNED ;
2012-04-06 02:47:53 +04:00
free_token ( token ) ;
if ( read_expected ( EVENT_OP , " ; " ) < 0 )
goto fail_expect ;
if ( read_expect_type ( EVENT_NEWLINE , & token ) )
goto fail ;
}
free_token ( token ) ;
if ( field - > flags & FIELD_IS_ARRAY ) {
if ( field - > arraylen )
field - > elementsize = field - > size / field - > arraylen ;
2013-01-25 00:46:43 +04:00
else if ( field - > flags & FIELD_IS_DYNAMIC )
field - > elementsize = size_dynamic ;
2012-04-06 02:47:53 +04:00
else if ( field - > flags & FIELD_IS_STRING )
field - > elementsize = 1 ;
2013-01-25 00:46:43 +04:00
else if ( field - > flags & FIELD_IS_LONG )
field - > elementsize = event - > pevent ?
event - > pevent - > long_size :
sizeof ( long ) ;
2012-04-06 02:47:53 +04:00
} else
field - > elementsize = field - > size ;
* fields = field ;
fields = & field - > next ;
} while ( 1 ) ;
return 0 ;
fail :
free_token ( token ) ;
fail_expect :
2012-05-23 06:36:47 +04:00
if ( field ) {
free ( field - > type ) ;
free ( field - > name ) ;
2012-04-06 02:47:53 +04:00
free ( field ) ;
2012-05-23 06:36:47 +04:00
}
2012-04-06 02:47:53 +04:00
return - 1 ;
}
static int event_read_format ( struct event_format * event )
{
char * token ;
int ret ;
if ( read_expected_item ( EVENT_ITEM , " format " ) < 0 )
return - 1 ;
if ( read_expected ( EVENT_OP , " : " ) < 0 )
return - 1 ;
if ( read_expect_type ( EVENT_NEWLINE , & token ) )
goto fail ;
free_token ( token ) ;
ret = event_read_fields ( event , & event - > format . common_fields ) ;
if ( ret < 0 )
return ret ;
event - > format . nr_common = ret ;
ret = event_read_fields ( event , & event - > format . fields ) ;
if ( ret < 0 )
return ret ;
event - > format . nr_fields = ret ;
return 0 ;
fail :
free_token ( token ) ;
return - 1 ;
}
static enum event_type
process_arg_token ( struct event_format * event , struct print_arg * arg ,
char * * tok , enum event_type type ) ;
static enum event_type
process_arg ( struct event_format * event , struct print_arg * arg , char * * tok )
{
enum event_type type ;
char * token ;
type = read_token ( & token ) ;
* tok = token ;
return process_arg_token ( event , arg , tok , type ) ;
}
static enum event_type
process_op ( struct event_format * event , struct print_arg * arg , char * * tok ) ;
2013-11-18 23:23:14 +04:00
/*
* For __print_symbolic ( ) and __print_flags , we need to completely
* evaluate the first argument , which defines what to print next .
*/
static enum event_type
process_field_arg ( struct event_format * event , struct print_arg * arg , char * * tok )
{
enum event_type type ;
type = process_arg ( event , arg , tok ) ;
while ( type = = EVENT_OP ) {
type = process_op ( event , arg , tok ) ;
}
return type ;
}
2012-04-06 02:47:53 +04:00
static enum event_type
process_cond ( struct event_format * event , struct print_arg * top , char * * tok )
{
struct print_arg * arg , * left , * right ;
enum event_type type ;
char * token = NULL ;
arg = alloc_arg ( ) ;
left = alloc_arg ( ) ;
right = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! arg | | ! left | | ! right ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: not enough memory! " , __func__ ) ;
2012-09-20 06:09:19 +04:00
/* arg will be freed at out_free */
free_arg ( left ) ;
free_arg ( right ) ;
goto out_free ;
}
2012-04-06 02:47:53 +04:00
arg - > type = PRINT_OP ;
arg - > op . left = left ;
arg - > op . right = right ;
* tok = NULL ;
type = process_arg ( event , left , & token ) ;
again :
2015-08-20 18:16:32 +03:00
if ( type = = EVENT_ERROR )
goto out_free ;
2012-04-06 02:47:53 +04:00
/* Handle other operations in the arguments */
if ( type = = EVENT_OP & & strcmp ( token , " : " ) ! = 0 ) {
type = process_op ( event , left , & token ) ;
goto again ;
}
if ( test_type_token ( type , token , EVENT_OP , " : " ) )
goto out_free ;
arg - > op . op = token ;
type = process_arg ( event , right , & token ) ;
top - > op . right = arg ;
* tok = token ;
return type ;
out_free :
/* Top may point to itself */
top - > op . right = NULL ;
free_token ( token ) ;
free_arg ( arg ) ;
return EVENT_ERROR ;
}
static enum event_type
process_array ( struct event_format * event , struct print_arg * top , char * * tok )
{
struct print_arg * arg ;
enum event_type type ;
char * token = NULL ;
arg = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! arg ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: not enough memory! " , __func__ ) ;
2012-09-20 06:09:19 +04:00
/* '*tok' is set to top->op.op. No need to free. */
* tok = NULL ;
return EVENT_ERROR ;
}
2012-04-06 02:47:53 +04:00
* tok = NULL ;
type = process_arg ( event , arg , & token ) ;
if ( test_type_token ( type , token , EVENT_OP , " ] " ) )
goto out_free ;
top - > op . right = arg ;
free_token ( token ) ;
type = read_token_item ( & token ) ;
* tok = token ;
return type ;
out_free :
2012-09-19 10:58:41 +04:00
free_token ( token ) ;
2012-04-06 02:47:53 +04:00
free_arg ( arg ) ;
return EVENT_ERROR ;
}
static int get_op_prio ( char * op )
{
if ( ! op [ 1 ] ) {
switch ( op [ 0 ] ) {
case ' ~ ' :
case ' ! ' :
return 4 ;
case ' * ' :
case ' / ' :
case ' % ' :
return 6 ;
case ' + ' :
case ' - ' :
return 7 ;
/* '>>' and '<<' are 8 */
case ' < ' :
case ' > ' :
return 9 ;
/* '==' and '!=' are 10 */
case ' & ' :
return 11 ;
case ' ^ ' :
return 12 ;
case ' | ' :
return 13 ;
case ' ? ' :
return 16 ;
default :
2012-04-06 02:48:01 +04:00
do_warning ( " unknown op '%c' " , op [ 0 ] ) ;
2012-04-06 02:47:53 +04:00
return - 1 ;
}
} else {
if ( strcmp ( op , " ++ " ) = = 0 | |
strcmp ( op , " -- " ) = = 0 ) {
return 3 ;
} else if ( strcmp ( op , " >> " ) = = 0 | |
strcmp ( op , " << " ) = = 0 ) {
return 8 ;
} else if ( strcmp ( op , " >= " ) = = 0 | |
strcmp ( op , " <= " ) = = 0 ) {
return 9 ;
} else if ( strcmp ( op , " == " ) = = 0 | |
strcmp ( op , " != " ) = = 0 ) {
return 10 ;
} else if ( strcmp ( op , " && " ) = = 0 ) {
return 14 ;
} else if ( strcmp ( op , " || " ) = = 0 ) {
return 15 ;
} else {
2012-04-06 02:48:01 +04:00
do_warning ( " unknown op '%s' " , op ) ;
2012-04-06 02:47:53 +04:00
return - 1 ;
}
}
}
2012-04-06 02:48:01 +04:00
static int set_op_prio ( struct print_arg * arg )
2012-04-06 02:47:53 +04:00
{
/* single ops are the greatest */
2012-04-06 02:48:01 +04:00
if ( ! arg - > op . left | | arg - > op . left - > type = = PRINT_NULL )
2012-04-06 02:47:53 +04:00
arg - > op . prio = 0 ;
2012-04-06 02:48:01 +04:00
else
arg - > op . prio = get_op_prio ( arg - > op . op ) ;
2012-04-06 02:47:53 +04:00
2012-04-06 02:48:01 +04:00
return arg - > op . prio ;
2012-04-06 02:47:53 +04:00
}
/* Note, *tok does not get freed, but will most likely be saved */
static enum event_type
process_op ( struct event_format * event , struct print_arg * arg , char * * tok )
{
struct print_arg * left , * right = NULL ;
enum event_type type ;
char * token ;
/* the op is passed in via tok */
token = * tok ;
if ( arg - > type = = PRINT_OP & & ! arg - > op . left ) {
/* handle single op */
if ( token [ 1 ] ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " bad op token %s " , token ) ;
2012-04-06 02:47:53 +04:00
goto out_free ;
}
switch ( token [ 0 ] ) {
case ' ~ ' :
case ' ! ' :
case ' + ' :
case ' - ' :
break ;
default :
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " bad op token %s " , token ) ;
2012-04-06 02:47:53 +04:00
goto out_free ;
}
/* make an empty left */
left = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! left )
goto out_warn_free ;
2012-04-06 02:47:53 +04:00
left - > type = PRINT_NULL ;
arg - > op . left = left ;
right = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! right )
goto out_warn_free ;
2012-04-06 02:47:53 +04:00
arg - > op . right = right ;
/* do not free the token, it belongs to an op */
* tok = NULL ;
type = process_arg ( event , right , tok ) ;
} else if ( strcmp ( token , " ? " ) = = 0 ) {
left = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! left )
goto out_warn_free ;
2012-04-06 02:47:53 +04:00
/* copy the top arg to the left */
* left = * arg ;
arg - > type = PRINT_OP ;
arg - > op . op = token ;
arg - > op . left = left ;
arg - > op . prio = 0 ;
2012-09-19 10:58:42 +04:00
/* it will set arg->op.right */
2012-04-06 02:47:53 +04:00
type = process_cond ( event , arg , tok ) ;
} else if ( strcmp ( token , " >> " ) = = 0 | |
strcmp ( token , " << " ) = = 0 | |
strcmp ( token , " & " ) = = 0 | |
strcmp ( token , " | " ) = = 0 | |
strcmp ( token , " && " ) = = 0 | |
strcmp ( token , " || " ) = = 0 | |
strcmp ( token , " - " ) = = 0 | |
strcmp ( token , " + " ) = = 0 | |
strcmp ( token , " * " ) = = 0 | |
strcmp ( token , " ^ " ) = = 0 | |
strcmp ( token , " / " ) = = 0 | |
strcmp ( token , " < " ) = = 0 | |
strcmp ( token , " > " ) = = 0 | |
2013-01-15 12:02:19 +04:00
strcmp ( token , " <= " ) = = 0 | |
strcmp ( token , " >= " ) = = 0 | |
2012-04-06 02:47:53 +04:00
strcmp ( token , " == " ) = = 0 | |
strcmp ( token , " != " ) = = 0 ) {
left = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! left )
goto out_warn_free ;
2012-04-06 02:47:53 +04:00
/* copy the top arg to the left */
* left = * arg ;
arg - > type = PRINT_OP ;
arg - > op . op = token ;
arg - > op . left = left ;
2012-09-19 10:58:42 +04:00
arg - > op . right = NULL ;
2012-04-06 02:47:53 +04:00
2012-04-06 02:48:01 +04:00
if ( set_op_prio ( arg ) = = - 1 ) {
event - > flags | = EVENT_FL_FAILED ;
2012-05-23 06:36:49 +04:00
/* arg->op.op (= token) will be freed at out_free */
arg - > op . op = NULL ;
2012-04-06 02:48:01 +04:00
goto out_free ;
}
2012-04-06 02:47:53 +04:00
type = read_token_item ( & token ) ;
* tok = token ;
/* could just be a type pointer */
if ( ( strcmp ( arg - > op . op , " * " ) = = 0 ) & &
type = = EVENT_DELIM & & ( strcmp ( token , " ) " ) = = 0 ) ) {
2012-04-09 06:54:33 +04:00
char * new_atom ;
2012-09-13 00:30:50 +04:00
if ( left - > type ! = PRINT_ATOM ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " bad pointer type " ) ;
2012-09-13 00:30:50 +04:00
goto out_free ;
}
2012-04-09 06:54:33 +04:00
new_atom = realloc ( left - > atom . atom ,
2012-04-06 02:47:53 +04:00
strlen ( left - > atom . atom ) + 3 ) ;
2012-04-09 06:54:33 +04:00
if ( ! new_atom )
2012-09-20 06:09:19 +04:00
goto out_warn_free ;
2012-04-09 06:54:33 +04:00
left - > atom . atom = new_atom ;
2012-04-06 02:47:53 +04:00
strcat ( left - > atom . atom , " * " ) ;
free ( arg - > op . op ) ;
* arg = * left ;
free ( left ) ;
return type ;
}
right = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! right )
goto out_warn_free ;
2012-04-06 02:47:53 +04:00
type = process_arg_token ( event , right , tok , type ) ;
2015-08-20 18:16:32 +03:00
if ( type = = EVENT_ERROR ) {
free_arg ( right ) ;
/* token was freed in process_arg_token() via *tok */
token = NULL ;
goto out_free ;
}
2015-04-06 08:36:16 +03:00
if ( right - > type = = PRINT_OP & &
get_op_prio ( arg - > op . op ) < get_op_prio ( right - > op . op ) ) {
struct print_arg tmp ;
/* rotate ops according to the priority */
arg - > op . right = right - > op . left ;
tmp = * arg ;
* arg = * right ;
* right = tmp ;
arg - > op . left = right ;
} else {
arg - > op . right = right ;
}
2012-04-06 02:47:53 +04:00
} else if ( strcmp ( token , " [ " ) = = 0 ) {
left = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! left )
goto out_warn_free ;
2012-04-06 02:47:53 +04:00
* left = * arg ;
arg - > type = PRINT_OP ;
arg - > op . op = token ;
arg - > op . left = left ;
arg - > op . prio = 0 ;
2012-09-19 10:58:42 +04:00
/* it will set arg->op.right */
2012-04-06 02:47:53 +04:00
type = process_array ( event , arg , tok ) ;
} else {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " unknown op '%s' " , token ) ;
2012-04-06 02:47:53 +04:00
event - > flags | = EVENT_FL_FAILED ;
/* the arg is now the left side */
goto out_free ;
}
if ( type = = EVENT_OP & & strcmp ( * tok , " : " ) ! = 0 ) {
int prio ;
/* higher prios need to be closer to the root */
prio = get_op_prio ( * tok ) ;
if ( prio > arg - > op . prio )
return process_op ( event , arg , tok ) ;
return process_op ( event , right , tok ) ;
}
return type ;
2012-09-20 06:09:19 +04:00
out_warn_free :
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: not enough memory! " , __func__ ) ;
2012-09-20 06:09:19 +04:00
out_free :
2012-04-06 02:47:53 +04:00
free_token ( token ) ;
* tok = NULL ;
return EVENT_ERROR ;
}
static enum event_type
2012-09-11 02:15:03 +04:00
process_entry ( struct event_format * event __maybe_unused , struct print_arg * arg ,
2012-04-06 02:47:53 +04:00
char * * tok )
{
enum event_type type ;
char * field ;
char * token ;
if ( read_expected ( EVENT_OP , " -> " ) < 0 )
goto out_err ;
if ( read_expect_type ( EVENT_ITEM , & token ) < 0 )
goto out_free ;
field = token ;
arg - > type = PRINT_FIELD ;
arg - > field . name = field ;
2012-04-06 02:47:58 +04:00
if ( is_flag_field ) {
arg - > field . field = pevent_find_any_field ( event , arg - > field . name ) ;
arg - > field . field - > flags | = FIELD_IS_FLAG ;
is_flag_field = 0 ;
} else if ( is_symbolic_field ) {
arg - > field . field = pevent_find_any_field ( event , arg - > field . name ) ;
arg - > field . field - > flags | = FIELD_IS_SYMBOLIC ;
is_symbolic_field = 0 ;
}
2012-04-06 02:47:53 +04:00
type = read_token ( & token ) ;
* tok = token ;
return type ;
out_free :
free_token ( token ) ;
out_err :
* tok = NULL ;
return EVENT_ERROR ;
}
2015-03-20 21:12:55 +03:00
static int alloc_and_process_delim ( struct event_format * event , char * next_token ,
struct print_arg * * print_arg )
{
struct print_arg * field ;
enum event_type type ;
char * token ;
int ret = 0 ;
field = alloc_arg ( ) ;
if ( ! field ) {
do_warning_event ( event , " %s: not enough memory! " , __func__ ) ;
errno = ENOMEM ;
return - 1 ;
}
type = process_arg ( event , field , & token ) ;
if ( test_type_token ( type , token , EVENT_DELIM , next_token ) ) {
errno = EINVAL ;
ret = - 1 ;
free_arg ( field ) ;
goto out_free_token ;
}
* print_arg = field ;
out_free_token :
free_token ( token ) ;
return ret ;
}
2012-04-06 02:47:53 +04:00
static char * arg_eval ( struct print_arg * arg ) ;
static unsigned long long
eval_type_str ( unsigned long long val , const char * type , int pointer )
{
int sign = 0 ;
char * ref ;
int len ;
len = strlen ( type ) ;
if ( pointer ) {
if ( type [ len - 1 ] ! = ' * ' ) {
do_warning ( " pointer expected with non pointer type " ) ;
return val ;
}
2012-09-13 00:30:50 +04:00
ref = malloc ( len ) ;
if ( ! ref ) {
do_warning ( " %s: not enough memory! " , __func__ ) ;
return val ;
}
2012-04-06 02:47:53 +04:00
memcpy ( ref , type , len ) ;
/* chop off the " *" */
ref [ len - 2 ] = 0 ;
val = eval_type_str ( val , ref , 0 ) ;
free ( ref ) ;
return val ;
}
/* check if this is a pointer */
if ( type [ len - 1 ] = = ' * ' )
return val ;
/* Try to figure out the arg size*/
if ( strncmp ( type , " struct " , 6 ) = = 0 )
/* all bets off */
return val ;
if ( strcmp ( type , " u8 " ) = = 0 )
return val & 0xff ;
if ( strcmp ( type , " u16 " ) = = 0 )
return val & 0xffff ;
if ( strcmp ( type , " u32 " ) = = 0 )
return val & 0xffffffff ;
if ( strcmp ( type , " u64 " ) = = 0 | |
strcmp ( type , " s64 " ) )
return val ;
if ( strcmp ( type , " s8 " ) = = 0 )
return ( unsigned long long ) ( char ) val & 0xff ;
if ( strcmp ( type , " s16 " ) = = 0 )
return ( unsigned long long ) ( short ) val & 0xffff ;
if ( strcmp ( type , " s32 " ) = = 0 )
return ( unsigned long long ) ( int ) val & 0xffffffff ;
if ( strncmp ( type , " unsigned " , 9 ) = = 0 ) {
sign = 0 ;
type + = 9 ;
}
if ( strcmp ( type , " char " ) = = 0 ) {
if ( sign )
return ( unsigned long long ) ( char ) val & 0xff ;
else
return val & 0xff ;
}
if ( strcmp ( type , " short " ) = = 0 ) {
if ( sign )
return ( unsigned long long ) ( short ) val & 0xffff ;
else
return val & 0xffff ;
}
if ( strcmp ( type , " int " ) = = 0 ) {
if ( sign )
return ( unsigned long long ) ( int ) val & 0xffffffff ;
else
return val & 0xffffffff ;
}
return val ;
}
/*
* Try to figure out the type .
*/
static unsigned long long
eval_type ( unsigned long long val , struct print_arg * arg , int pointer )
{
2012-09-13 00:30:50 +04:00
if ( arg - > type ! = PRINT_TYPE ) {
do_warning ( " expected type argument " ) ;
return 0 ;
}
2012-04-06 02:47:53 +04:00
return eval_type_str ( val , arg - > typecast . type , pointer ) ;
}
2012-04-06 02:48:00 +04:00
static int arg_num_eval ( struct print_arg * arg , long long * val )
2012-04-06 02:47:53 +04:00
{
long long left , right ;
2012-04-06 02:48:00 +04:00
int ret = 1 ;
2012-04-06 02:47:53 +04:00
switch ( arg - > type ) {
case PRINT_ATOM :
2012-04-06 02:48:00 +04:00
* val = strtoll ( arg - > atom . atom , NULL , 0 ) ;
2012-04-06 02:47:53 +04:00
break ;
case PRINT_TYPE :
2012-04-06 02:48:00 +04:00
ret = arg_num_eval ( arg - > typecast . item , val ) ;
if ( ! ret )
break ;
* val = eval_type ( * val , arg , 0 ) ;
2012-04-06 02:47:53 +04:00
break ;
case PRINT_OP :
switch ( arg - > op . op [ 0 ] ) {
case ' | ' :
2012-04-06 02:48:00 +04:00
ret = arg_num_eval ( arg - > op . left , & left ) ;
if ( ! ret )
break ;
ret = arg_num_eval ( arg - > op . right , & right ) ;
if ( ! ret )
break ;
2012-04-06 02:47:53 +04:00
if ( arg - > op . op [ 1 ] )
2012-04-06 02:48:00 +04:00
* val = left | | right ;
2012-04-06 02:47:53 +04:00
else
2012-04-06 02:48:00 +04:00
* val = left | right ;
2012-04-06 02:47:53 +04:00
break ;
case ' & ' :
2012-04-06 02:48:00 +04:00
ret = arg_num_eval ( arg - > op . left , & left ) ;
if ( ! ret )
break ;
ret = arg_num_eval ( arg - > op . right , & right ) ;
if ( ! ret )
break ;
2012-04-06 02:47:53 +04:00
if ( arg - > op . op [ 1 ] )
2012-04-06 02:48:00 +04:00
* val = left & & right ;
2012-04-06 02:47:53 +04:00
else
2012-04-06 02:48:00 +04:00
* val = left & right ;
2012-04-06 02:47:53 +04:00
break ;
case ' < ' :
2012-04-06 02:48:00 +04:00
ret = arg_num_eval ( arg - > op . left , & left ) ;
if ( ! ret )
break ;
ret = arg_num_eval ( arg - > op . right , & right ) ;
if ( ! ret )
break ;
2012-04-06 02:47:53 +04:00
switch ( arg - > op . op [ 1 ] ) {
case 0 :
2012-04-06 02:48:00 +04:00
* val = left < right ;
2012-04-06 02:47:53 +04:00
break ;
case ' < ' :
2012-04-06 02:48:00 +04:00
* val = left < < right ;
2012-04-06 02:47:53 +04:00
break ;
case ' = ' :
2012-04-06 02:48:00 +04:00
* val = left < = right ;
2012-04-06 02:47:53 +04:00
break ;
default :
2012-04-06 02:48:00 +04:00
do_warning ( " unknown op '%s' " , arg - > op . op ) ;
ret = 0 ;
2012-04-06 02:47:53 +04:00
}
break ;
case ' > ' :
2012-04-06 02:48:00 +04:00
ret = arg_num_eval ( arg - > op . left , & left ) ;
if ( ! ret )
break ;
ret = arg_num_eval ( arg - > op . right , & right ) ;
if ( ! ret )
break ;
2012-04-06 02:47:53 +04:00
switch ( arg - > op . op [ 1 ] ) {
case 0 :
2012-04-06 02:48:00 +04:00
* val = left > right ;
2012-04-06 02:47:53 +04:00
break ;
case ' > ' :
2012-04-06 02:48:00 +04:00
* val = left > > right ;
2012-04-06 02:47:53 +04:00
break ;
case ' = ' :
2012-04-06 02:48:00 +04:00
* val = left > = right ;
2012-04-06 02:47:53 +04:00
break ;
default :
2012-04-06 02:48:00 +04:00
do_warning ( " unknown op '%s' " , arg - > op . op ) ;
ret = 0 ;
2012-04-06 02:47:53 +04:00
}
break ;
case ' = ' :
2012-04-06 02:48:00 +04:00
ret = arg_num_eval ( arg - > op . left , & left ) ;
if ( ! ret )
break ;
ret = arg_num_eval ( arg - > op . right , & right ) ;
if ( ! ret )
break ;
2012-04-06 02:47:53 +04:00
2012-04-06 02:48:00 +04:00
if ( arg - > op . op [ 1 ] ! = ' = ' ) {
do_warning ( " unknown op '%s' " , arg - > op . op ) ;
ret = 0 ;
} else
* val = left = = right ;
2012-04-06 02:47:53 +04:00
break ;
case ' ! ' :
2012-04-06 02:48:00 +04:00
ret = arg_num_eval ( arg - > op . left , & left ) ;
if ( ! ret )
break ;
ret = arg_num_eval ( arg - > op . right , & right ) ;
if ( ! ret )
break ;
2012-04-06 02:47:53 +04:00
switch ( arg - > op . op [ 1 ] ) {
case ' = ' :
2012-04-06 02:48:00 +04:00
* val = left ! = right ;
2012-04-06 02:47:53 +04:00
break ;
default :
2012-04-06 02:48:00 +04:00
do_warning ( " unknown op '%s' " , arg - > op . op ) ;
ret = 0 ;
2012-04-06 02:47:53 +04:00
}
break ;
case ' - ' :
/* check for negative */
if ( arg - > op . left - > type = = PRINT_NULL )
left = 0 ;
else
2012-04-06 02:48:00 +04:00
ret = arg_num_eval ( arg - > op . left , & left ) ;
if ( ! ret )
break ;
ret = arg_num_eval ( arg - > op . right , & right ) ;
if ( ! ret )
break ;
* val = left - right ;
2012-04-06 02:47:53 +04:00
break ;
2012-04-06 02:48:03 +04:00
case ' + ' :
if ( arg - > op . left - > type = = PRINT_NULL )
left = 0 ;
else
ret = arg_num_eval ( arg - > op . left , & left ) ;
if ( ! ret )
break ;
ret = arg_num_eval ( arg - > op . right , & right ) ;
if ( ! ret )
break ;
* val = left + right ;
break ;
2012-04-06 02:47:53 +04:00
default :
2012-04-06 02:48:00 +04:00
do_warning ( " unknown op '%s' " , arg - > op . op ) ;
ret = 0 ;
2012-04-06 02:47:53 +04:00
}
break ;
case PRINT_NULL :
case PRINT_FIELD . . . PRINT_SYMBOL :
case PRINT_STRING :
case PRINT_BSTRING :
2014-06-03 07:20:16 +04:00
case PRINT_BITMASK :
2012-04-06 02:47:53 +04:00
default :
2012-04-06 02:48:00 +04:00
do_warning ( " invalid eval type %d " , arg - > type ) ;
ret = 0 ;
2012-04-06 02:47:53 +04:00
}
2012-04-06 02:48:00 +04:00
return ret ;
2012-04-06 02:47:53 +04:00
}
static char * arg_eval ( struct print_arg * arg )
{
long long val ;
static char buf [ 20 ] ;
switch ( arg - > type ) {
case PRINT_ATOM :
return arg - > atom . atom ;
case PRINT_TYPE :
return arg_eval ( arg - > typecast . item ) ;
case PRINT_OP :
2012-04-06 02:48:00 +04:00
if ( ! arg_num_eval ( arg , & val ) )
break ;
2012-04-06 02:47:53 +04:00
sprintf ( buf , " %lld " , val ) ;
return buf ;
case PRINT_NULL :
case PRINT_FIELD . . . PRINT_SYMBOL :
case PRINT_STRING :
case PRINT_BSTRING :
2014-06-03 07:20:16 +04:00
case PRINT_BITMASK :
2012-04-06 02:47:53 +04:00
default :
2012-09-13 00:30:50 +04:00
do_warning ( " invalid eval type %d " , arg - > type ) ;
2012-04-06 02:47:53 +04:00
break ;
}
return NULL ;
}
static enum event_type
process_fields ( struct event_format * event , struct print_flag_sym * * list , char * * tok )
{
enum event_type type ;
struct print_arg * arg = NULL ;
struct print_flag_sym * field ;
char * token = * tok ;
char * value ;
do {
free_token ( token ) ;
type = read_token_item ( & token ) ;
if ( test_type_token ( type , token , EVENT_OP , " { " ) )
break ;
arg = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! arg )
goto out_free ;
2012-04-06 02:47:53 +04:00
free_token ( token ) ;
type = process_arg ( event , arg , & token ) ;
2012-05-23 06:36:42 +04:00
if ( type = = EVENT_OP )
type = process_op ( event , arg , & token ) ;
if ( type = = EVENT_ERROR )
goto out_free ;
2012-04-06 02:47:53 +04:00
if ( test_type_token ( type , token , EVENT_DELIM , " , " ) )
goto out_free ;
2012-09-12 22:39:59 +04:00
field = calloc ( 1 , sizeof ( * field ) ) ;
if ( ! field )
goto out_free ;
2012-04-06 02:47:53 +04:00
value = arg_eval ( arg ) ;
2012-04-06 02:48:00 +04:00
if ( value = = NULL )
2012-09-19 10:58:43 +04:00
goto out_free_field ;
2012-04-06 02:47:53 +04:00
field - > value = strdup ( value ) ;
2012-04-09 06:54:31 +04:00
if ( field - > value = = NULL )
2012-09-19 10:58:43 +04:00
goto out_free_field ;
2012-04-06 02:47:53 +04:00
free_arg ( arg ) ;
arg = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! arg )
goto out_free ;
2012-04-06 02:47:53 +04:00
free_token ( token ) ;
type = process_arg ( event , arg , & token ) ;
if ( test_type_token ( type , token , EVENT_OP , " } " ) )
2012-09-19 10:58:43 +04:00
goto out_free_field ;
2012-04-06 02:47:53 +04:00
value = arg_eval ( arg ) ;
2012-04-06 02:48:00 +04:00
if ( value = = NULL )
2012-09-19 10:58:43 +04:00
goto out_free_field ;
2012-04-06 02:47:53 +04:00
field - > str = strdup ( value ) ;
2012-04-09 06:54:31 +04:00
if ( field - > str = = NULL )
2012-09-19 10:58:43 +04:00
goto out_free_field ;
2012-04-06 02:47:53 +04:00
free_arg ( arg ) ;
arg = NULL ;
* list = field ;
list = & field - > next ;
free_token ( token ) ;
type = read_token_item ( & token ) ;
} while ( type = = EVENT_DELIM & & strcmp ( token , " , " ) = = 0 ) ;
* tok = token ;
return type ;
2012-09-19 10:58:43 +04:00
out_free_field :
free_flag_sym ( field ) ;
2012-04-06 02:47:53 +04:00
out_free :
free_arg ( arg ) ;
free_token ( token ) ;
* tok = NULL ;
return EVENT_ERROR ;
}
static enum event_type
process_flags ( struct event_format * event , struct print_arg * arg , char * * tok )
{
struct print_arg * field ;
enum event_type type ;
2014-06-24 15:09:10 +04:00
char * token = NULL ;
2012-04-06 02:47:53 +04:00
memset ( arg , 0 , sizeof ( * arg ) ) ;
arg - > type = PRINT_FLAGS ;
field = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! field ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: not enough memory! " , __func__ ) ;
2012-09-20 06:09:19 +04:00
goto out_free ;
}
2012-04-06 02:47:53 +04:00
2013-11-18 23:23:14 +04:00
type = process_field_arg ( event , field , & token ) ;
2012-04-06 02:47:53 +04:00
/* Handle operations in the first argument */
while ( type = = EVENT_OP )
type = process_op ( event , field , & token ) ;
if ( test_type_token ( type , token , EVENT_DELIM , " , " ) )
2012-09-19 10:58:44 +04:00
goto out_free_field ;
2012-04-06 02:47:53 +04:00
free_token ( token ) ;
arg - > flags . field = field ;
type = read_token_item ( & token ) ;
if ( event_item_type ( type ) ) {
arg - > flags . delim = token ;
type = read_token_item ( & token ) ;
}
if ( test_type_token ( type , token , EVENT_DELIM , " , " ) )
goto out_free ;
type = process_fields ( event , & arg - > flags . flags , & token ) ;
if ( test_type_token ( type , token , EVENT_DELIM , " ) " ) )
goto out_free ;
free_token ( token ) ;
type = read_token_item ( tok ) ;
return type ;
2012-09-19 10:58:44 +04:00
out_free_field :
free_arg ( field ) ;
out_free :
2012-04-06 02:47:53 +04:00
free_token ( token ) ;
* tok = NULL ;
return EVENT_ERROR ;
}
static enum event_type
process_symbols ( struct event_format * event , struct print_arg * arg , char * * tok )
{
struct print_arg * field ;
enum event_type type ;
2014-06-24 15:09:10 +04:00
char * token = NULL ;
2012-04-06 02:47:53 +04:00
memset ( arg , 0 , sizeof ( * arg ) ) ;
arg - > type = PRINT_SYMBOL ;
field = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! field ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: not enough memory! " , __func__ ) ;
2012-09-20 06:09:19 +04:00
goto out_free ;
}
2012-04-06 02:47:53 +04:00
2013-11-18 23:23:14 +04:00
type = process_field_arg ( event , field , & token ) ;
2012-04-06 02:47:53 +04:00
if ( test_type_token ( type , token , EVENT_DELIM , " , " ) )
2012-09-19 10:58:44 +04:00
goto out_free_field ;
2012-04-06 02:47:53 +04:00
arg - > symbol . field = field ;
type = process_fields ( event , & arg - > symbol . symbols , & token ) ;
if ( test_type_token ( type , token , EVENT_DELIM , " ) " ) )
goto out_free ;
free_token ( token ) ;
type = read_token_item ( tok ) ;
return type ;
2012-09-19 10:58:44 +04:00
out_free_field :
free_arg ( field ) ;
out_free :
2012-04-06 02:47:53 +04:00
free_token ( token ) ;
* tok = NULL ;
return EVENT_ERROR ;
}
2012-06-27 04:41:41 +04:00
static enum event_type
process_hex ( struct event_format * event , struct print_arg * arg , char * * tok )
{
memset ( arg , 0 , sizeof ( * arg ) ) ;
arg - > type = PRINT_HEX ;
2015-03-20 21:12:55 +03:00
if ( alloc_and_process_delim ( event , " , " , & arg - > hex . field ) )
goto out ;
2012-09-20 06:09:19 +04:00
2015-03-20 21:12:55 +03:00
if ( alloc_and_process_delim ( event , " ) " , & arg - > hex . size ) )
goto free_field ;
2012-06-27 04:41:41 +04:00
2015-03-20 21:12:55 +03:00
return read_token_item ( tok ) ;
2012-06-27 04:41:41 +04:00
2015-03-20 21:12:55 +03:00
free_field :
free_arg ( arg - > hex . field ) ;
out :
2012-06-27 04:41:41 +04:00
* tok = NULL ;
return EVENT_ERROR ;
}
2015-03-24 14:07:19 +03:00
static enum event_type
process_int_array ( struct event_format * event , struct print_arg * arg , char * * tok )
{
memset ( arg , 0 , sizeof ( * arg ) ) ;
arg - > type = PRINT_INT_ARRAY ;
2012-09-20 06:09:19 +04:00
2015-03-24 14:07:19 +03:00
if ( alloc_and_process_delim ( event , " , " , & arg - > int_array . field ) )
goto out ;
2012-06-27 04:41:41 +04:00
2015-03-24 14:07:19 +03:00
if ( alloc_and_process_delim ( event , " , " , & arg - > int_array . count ) )
goto free_field ;
2012-06-27 04:41:41 +04:00
2015-03-24 14:07:19 +03:00
if ( alloc_and_process_delim ( event , " ) " , & arg - > int_array . el_size ) )
goto free_size ;
2012-06-27 04:41:41 +04:00
2015-03-24 14:07:19 +03:00
return read_token_item ( tok ) ;
2012-06-27 04:41:41 +04:00
2015-03-24 14:07:19 +03:00
free_size :
free_arg ( arg - > int_array . count ) ;
free_field :
free_arg ( arg - > int_array . field ) ;
out :
2012-06-27 04:41:41 +04:00
* tok = NULL ;
return EVENT_ERROR ;
}
2012-04-06 02:47:53 +04:00
static enum event_type
process_dynamic_array ( struct event_format * event , struct print_arg * arg , char * * tok )
{
struct format_field * field ;
enum event_type type ;
char * token ;
memset ( arg , 0 , sizeof ( * arg ) ) ;
arg - > type = PRINT_DYNAMIC_ARRAY ;
/*
* The item within the parenthesis is another field that holds
* the index into where the array starts .
*/
type = read_token ( & token ) ;
* tok = token ;
if ( type ! = EVENT_ITEM )
goto out_free ;
/* Find the field */
field = pevent_find_field ( event , token ) ;
if ( ! field )
goto out_free ;
arg - > dynarray . field = field ;
arg - > dynarray . index = 0 ;
if ( read_expected ( EVENT_DELIM , " ) " ) < 0 )
goto out_free ;
free_token ( token ) ;
type = read_token_item ( & token ) ;
* tok = token ;
if ( type ! = EVENT_OP | | strcmp ( token , " [ " ) ! = 0 )
return type ;
free_token ( token ) ;
arg = alloc_arg ( ) ;
2012-12-22 00:00:58 +04:00
if ( ! arg ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: not enough memory! " , __func__ ) ;
2012-09-20 06:09:19 +04:00
* tok = NULL ;
return EVENT_ERROR ;
}
2012-04-06 02:47:53 +04:00
type = process_arg ( event , arg , & token ) ;
if ( type = = EVENT_ERROR )
2012-05-23 06:36:50 +04:00
goto out_free_arg ;
2012-04-06 02:47:53 +04:00
if ( ! test_type_token ( type , token , EVENT_OP , " ] " ) )
2012-05-23 06:36:50 +04:00
goto out_free_arg ;
2012-04-06 02:47:53 +04:00
free_token ( token ) ;
type = read_token_item ( tok ) ;
return type ;
2012-05-23 06:36:50 +04:00
out_free_arg :
free_arg ( arg ) ;
2012-04-06 02:47:53 +04:00
out_free :
free_token ( token ) ;
* tok = NULL ;
return EVENT_ERROR ;
}
static enum event_type
process_paren ( struct event_format * event , struct print_arg * arg , char * * tok )
{
struct print_arg * item_arg ;
enum event_type type ;
char * token ;
type = process_arg ( event , arg , & token ) ;
if ( type = = EVENT_ERROR )
goto out_free ;
if ( type = = EVENT_OP )
type = process_op ( event , arg , & token ) ;
if ( type = = EVENT_ERROR )
goto out_free ;
if ( test_type_token ( type , token , EVENT_DELIM , " ) " ) )
goto out_free ;
free_token ( token ) ;
type = read_token_item ( & token ) ;
/*
* If the next token is an item or another open paren , then
* this was a typecast .
*/
if ( event_item_type ( type ) | |
( type = = EVENT_DELIM & & strcmp ( token , " ( " ) = = 0 ) ) {
/* make this a typecast and contine */
/* prevous must be an atom */
2012-09-13 00:30:50 +04:00
if ( arg - > type ! = PRINT_ATOM ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " previous needed to be PRINT_ATOM " ) ;
2012-09-13 00:30:50 +04:00
goto out_free ;
}
2012-04-06 02:47:53 +04:00
item_arg = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! item_arg ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: not enough memory! " ,
__func__ ) ;
2012-09-20 06:09:19 +04:00
goto out_free ;
}
2012-04-06 02:47:53 +04:00
arg - > type = PRINT_TYPE ;
arg - > typecast . type = arg - > atom . atom ;
arg - > typecast . item = item_arg ;
type = process_arg_token ( event , item_arg , & token , type ) ;
}
* tok = token ;
return type ;
out_free :
free_token ( token ) ;
* tok = NULL ;
return EVENT_ERROR ;
}
static enum event_type
2012-09-11 02:15:03 +04:00
process_str ( struct event_format * event __maybe_unused , struct print_arg * arg ,
char * * tok )
2012-04-06 02:47:53 +04:00
{
enum event_type type ;
char * token ;
if ( read_expect_type ( EVENT_ITEM , & token ) < 0 )
goto out_free ;
arg - > type = PRINT_STRING ;
arg - > string . string = token ;
arg - > string . offset = - 1 ;
if ( read_expected ( EVENT_DELIM , " ) " ) < 0 )
goto out_err ;
type = read_token ( & token ) ;
* tok = token ;
return type ;
out_free :
free_token ( token ) ;
out_err :
* tok = NULL ;
return EVENT_ERROR ;
}
2014-06-03 07:20:16 +04:00
static enum event_type
process_bitmask ( struct event_format * event __maybe_unused , struct print_arg * arg ,
char * * tok )
{
enum event_type type ;
char * token ;
if ( read_expect_type ( EVENT_ITEM , & token ) < 0 )
goto out_free ;
arg - > type = PRINT_BITMASK ;
arg - > bitmask . bitmask = token ;
arg - > bitmask . offset = - 1 ;
if ( read_expected ( EVENT_DELIM , " ) " ) < 0 )
goto out_err ;
type = read_token ( & token ) ;
* tok = token ;
return type ;
out_free :
free_token ( token ) ;
out_err :
* tok = NULL ;
return EVENT_ERROR ;
}
2012-04-06 02:47:53 +04:00
static struct pevent_function_handler *
find_func_handler ( struct pevent * pevent , char * func_name )
{
struct pevent_function_handler * func ;
2012-10-02 04:13:51 +04:00
if ( ! pevent )
return NULL ;
2012-04-06 02:47:53 +04:00
for ( func = pevent - > func_handlers ; func ; func = func - > next ) {
if ( strcmp ( func - > name , func_name ) = = 0 )
break ;
}
return func ;
}
static void remove_func_handler ( struct pevent * pevent , char * func_name )
{
struct pevent_function_handler * func ;
struct pevent_function_handler * * next ;
next = & pevent - > func_handlers ;
while ( ( func = * next ) ) {
if ( strcmp ( func - > name , func_name ) = = 0 ) {
* next = func - > next ;
free_func_handle ( func ) ;
break ;
}
next = & func - > next ;
}
}
static enum event_type
process_func_handler ( struct event_format * event , struct pevent_function_handler * func ,
struct print_arg * arg , char * * tok )
{
struct print_arg * * next_arg ;
struct print_arg * farg ;
enum event_type type ;
char * token ;
int i ;
arg - > type = PRINT_FUNC ;
arg - > func . func = func ;
* tok = NULL ;
next_arg = & ( arg - > func . args ) ;
for ( i = 0 ; i < func - > nr_args ; i + + ) {
farg = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! farg ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: not enough memory! " ,
__func__ ) ;
2012-09-20 06:09:19 +04:00
return EVENT_ERROR ;
}
2012-04-06 02:47:53 +04:00
type = process_arg ( event , farg , & token ) ;
2013-11-19 06:38:20 +04:00
if ( i < ( func - > nr_args - 1 ) ) {
if ( type ! = EVENT_DELIM | | strcmp ( token , " , " ) ! = 0 ) {
2014-03-19 05:22:54 +04:00
do_warning_event ( event ,
" Error: function '%s()' expects %d arguments but event %s only uses %d " ,
2013-11-19 06:38:20 +04:00
func - > name , func - > nr_args ,
event - > name , i + 1 ) ;
goto err ;
}
} else {
if ( type ! = EVENT_DELIM | | strcmp ( token , " ) " ) ! = 0 ) {
2014-03-19 05:22:54 +04:00
do_warning_event ( event ,
" Error: function '%s()' only expects %d arguments but event %s has more " ,
2013-11-19 06:38:20 +04:00
func - > name , func - > nr_args , event - > name ) ;
goto err ;
}
2012-04-06 02:47:53 +04:00
}
* next_arg = farg ;
next_arg = & ( farg - > next ) ;
free_token ( token ) ;
}
type = read_token ( & token ) ;
* tok = token ;
return type ;
2013-11-19 06:38:20 +04:00
err :
free_arg ( farg ) ;
free_token ( token ) ;
return EVENT_ERROR ;
2012-04-06 02:47:53 +04:00
}
static enum event_type
process_function ( struct event_format * event , struct print_arg * arg ,
char * token , char * * tok )
{
struct pevent_function_handler * func ;
if ( strcmp ( token , " __print_flags " ) = = 0 ) {
free_token ( token ) ;
2012-04-06 02:47:58 +04:00
is_flag_field = 1 ;
2012-04-06 02:47:53 +04:00
return process_flags ( event , arg , tok ) ;
}
if ( strcmp ( token , " __print_symbolic " ) = = 0 ) {
free_token ( token ) ;
2012-04-06 02:47:58 +04:00
is_symbolic_field = 1 ;
2012-04-06 02:47:53 +04:00
return process_symbols ( event , arg , tok ) ;
}
2012-06-27 04:41:41 +04:00
if ( strcmp ( token , " __print_hex " ) = = 0 ) {
free_token ( token ) ;
return process_hex ( event , arg , tok ) ;
}
2015-03-24 14:07:19 +03:00
if ( strcmp ( token , " __print_array " ) = = 0 ) {
free_token ( token ) ;
return process_int_array ( event , arg , tok ) ;
}
2012-04-06 02:47:53 +04:00
if ( strcmp ( token , " __get_str " ) = = 0 ) {
free_token ( token ) ;
return process_str ( event , arg , tok ) ;
}
2014-06-03 07:20:16 +04:00
if ( strcmp ( token , " __get_bitmask " ) = = 0 ) {
free_token ( token ) ;
return process_bitmask ( event , arg , tok ) ;
}
2012-04-06 02:47:53 +04:00
if ( strcmp ( token , " __get_dynamic_array " ) = = 0 ) {
free_token ( token ) ;
return process_dynamic_array ( event , arg , tok ) ;
}
func = find_func_handler ( event - > pevent , token ) ;
if ( func ) {
free_token ( token ) ;
return process_func_handler ( event , func , arg , tok ) ;
}
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " function %s not defined " , token ) ;
2012-04-06 02:47:53 +04:00
free_token ( token ) ;
return EVENT_ERROR ;
}
static enum event_type
process_arg_token ( struct event_format * event , struct print_arg * arg ,
char * * tok , enum event_type type )
{
char * token ;
char * atom ;
token = * tok ;
switch ( type ) {
case EVENT_ITEM :
if ( strcmp ( token , " REC " ) = = 0 ) {
free_token ( token ) ;
type = process_entry ( event , arg , & token ) ;
break ;
}
atom = token ;
/* test the next token */
type = read_token_item ( & token ) ;
/*
* If the next token is a parenthesis , then this
* is a function .
*/
if ( type = = EVENT_DELIM & & strcmp ( token , " ( " ) = = 0 ) {
free_token ( token ) ;
token = NULL ;
/* this will free atom. */
type = process_function ( event , arg , atom , & token ) ;
break ;
}
/* atoms can be more than one token long */
while ( type = = EVENT_ITEM ) {
2012-04-09 06:54:33 +04:00
char * new_atom ;
new_atom = realloc ( atom ,
strlen ( atom ) + strlen ( token ) + 2 ) ;
if ( ! new_atom ) {
free ( atom ) ;
* tok = NULL ;
free_token ( token ) ;
return EVENT_ERROR ;
}
atom = new_atom ;
2012-04-06 02:47:53 +04:00
strcat ( atom , " " ) ;
strcat ( atom , token ) ;
free_token ( token ) ;
type = read_token_item ( & token ) ;
}
arg - > type = PRINT_ATOM ;
arg - > atom . atom = atom ;
break ;
case EVENT_DQUOTE :
case EVENT_SQUOTE :
arg - > type = PRINT_ATOM ;
arg - > atom . atom = token ;
type = read_token_item ( & token ) ;
break ;
case EVENT_DELIM :
if ( strcmp ( token , " ( " ) = = 0 ) {
free_token ( token ) ;
type = process_paren ( event , arg , & token ) ;
break ;
}
case EVENT_OP :
/* handle single ops */
arg - > type = PRINT_OP ;
arg - > op . op = token ;
arg - > op . left = NULL ;
type = process_op ( event , arg , & token ) ;
/* On error, the op is freed */
if ( type = = EVENT_ERROR )
arg - > op . op = NULL ;
/* return error type if errored */
break ;
case EVENT_ERROR . . . EVENT_NEWLINE :
default :
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " unexpected type %d " , type ) ;
2012-09-13 00:30:50 +04:00
return EVENT_ERROR ;
2012-04-06 02:47:53 +04:00
}
* tok = token ;
return type ;
}
static int event_read_print_args ( struct event_format * event , struct print_arg * * list )
{
enum event_type type = EVENT_ERROR ;
struct print_arg * arg ;
char * token ;
int args = 0 ;
do {
if ( type = = EVENT_NEWLINE ) {
type = read_token_item ( & token ) ;
continue ;
}
arg = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! arg ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: not enough memory! " ,
__func__ ) ;
2012-09-20 06:09:19 +04:00
return - 1 ;
}
2012-04-06 02:47:53 +04:00
type = process_arg ( event , arg , & token ) ;
if ( type = = EVENT_ERROR ) {
free_token ( token ) ;
free_arg ( arg ) ;
return - 1 ;
}
* list = arg ;
args + + ;
if ( type = = EVENT_OP ) {
type = process_op ( event , arg , & token ) ;
free_token ( token ) ;
if ( type = = EVENT_ERROR ) {
* list = NULL ;
free_arg ( arg ) ;
return - 1 ;
}
list = & arg - > next ;
continue ;
}
if ( type = = EVENT_DELIM & & strcmp ( token , " , " ) = = 0 ) {
free_token ( token ) ;
* list = arg ;
list = & arg - > next ;
continue ;
}
break ;
} while ( type ! = EVENT_NONE ) ;
if ( type ! = EVENT_NONE & & type ! = EVENT_ERROR )
free_token ( token ) ;
return args ;
}
static int event_read_print ( struct event_format * event )
{
enum event_type type ;
char * token ;
int ret ;
if ( read_expected_item ( EVENT_ITEM , " print " ) < 0 )
return - 1 ;
if ( read_expected ( EVENT_ITEM , " fmt " ) < 0 )
return - 1 ;
if ( read_expected ( EVENT_OP , " : " ) < 0 )
return - 1 ;
if ( read_expect_type ( EVENT_DQUOTE , & token ) < 0 )
goto fail ;
concat :
event - > print_fmt . format = token ;
event - > print_fmt . args = NULL ;
/* ok to have no arg */
type = read_token_item ( & token ) ;
if ( type = = EVENT_NONE )
return 0 ;
/* Handle concatenation of print lines */
if ( type = = EVENT_DQUOTE ) {
char * cat ;
2012-09-12 22:39:59 +04:00
if ( asprintf ( & cat , " %s%s " , event - > print_fmt . format , token ) < 0 )
goto fail ;
2012-04-06 02:47:53 +04:00
free_token ( token ) ;
free_token ( event - > print_fmt . format ) ;
event - > print_fmt . format = NULL ;
token = cat ;
goto concat ;
}
if ( test_type_token ( type , token , EVENT_DELIM , " , " ) )
goto fail ;
free_token ( token ) ;
ret = event_read_print_args ( event , & event - > print_fmt . args ) ;
if ( ret < 0 )
return - 1 ;
return ret ;
fail :
free_token ( token ) ;
return - 1 ;
}
/**
* pevent_find_common_field - return a common field by event
* @ event : handle for the event
* @ name : the name of the common field to return
*
* Returns a common field from the event by the given @ name .
* This only searchs the common fields and not all field .
*/
struct format_field *
pevent_find_common_field ( struct event_format * event , const char * name )
{
struct format_field * format ;
for ( format = event - > format . common_fields ;
format ; format = format - > next ) {
if ( strcmp ( format - > name , name ) = = 0 )
break ;
}
return format ;
}
/**
* pevent_find_field - find a non - common field
* @ event : handle for the event
* @ name : the name of the non - common field
*
* Returns a non - common field by the given @ name .
* This does not search common fields .
*/
struct format_field *
pevent_find_field ( struct event_format * event , const char * name )
{
struct format_field * format ;
for ( format = event - > format . fields ;
format ; format = format - > next ) {
if ( strcmp ( format - > name , name ) = = 0 )
break ;
}
return format ;
}
/**
* pevent_find_any_field - find any field by name
* @ event : handle for the event
* @ name : the name of the field
*
* Returns a field by the given @ name .
* This searchs the common field names first , then
* the non - common ones if a common one was not found .
*/
struct format_field *
pevent_find_any_field ( struct event_format * event , const char * name )
{
struct format_field * format ;
format = pevent_find_common_field ( event , name ) ;
if ( format )
return format ;
return pevent_find_field ( event , name ) ;
}
/**
* pevent_read_number - read a number from data
* @ pevent : handle for the pevent
* @ ptr : the raw data
* @ size : the size of the data that holds the number
*
* Returns the number ( converted to host ) from the
* raw data .
*/
unsigned long long pevent_read_number ( struct pevent * pevent ,
const void * ptr , int size )
{
switch ( size ) {
case 1 :
return * ( unsigned char * ) ptr ;
case 2 :
return data2host2 ( pevent , ptr ) ;
case 4 :
return data2host4 ( pevent , ptr ) ;
case 8 :
return data2host8 ( pevent , ptr ) ;
default :
/* BUG! */
return 0 ;
}
}
/**
* pevent_read_number_field - read a number from data
* @ field : a handle to the field
* @ data : the raw data to read
* @ value : the value to place the number in
*
* Reads raw data according to a field offset and size ,
* and translates it into @ value .
*
* Returns 0 on success , - 1 otherwise .
*/
int pevent_read_number_field ( struct format_field * field , const void * data ,
unsigned long long * value )
{
if ( ! field )
return - 1 ;
switch ( field - > size ) {
case 1 :
case 2 :
case 4 :
case 8 :
* value = pevent_read_number ( field - > event - > pevent ,
data + field - > offset , field - > size ) ;
return 0 ;
default :
return - 1 ;
}
}
static int get_common_info ( struct pevent * pevent ,
const char * type , int * offset , int * size )
{
struct event_format * event ;
struct format_field * field ;
/*
* All events should have the same common elements .
* Pick any event to find where the type is ;
*/
2012-09-13 00:30:50 +04:00
if ( ! pevent - > events ) {
do_warning ( " no event_list! " ) ;
return - 1 ;
}
2012-04-06 02:47:53 +04:00
event = pevent - > events [ 0 ] ;
field = pevent_find_common_field ( event , type ) ;
if ( ! field )
2012-05-22 09:52:40 +04:00
return - 1 ;
2012-04-06 02:47:53 +04:00
* offset = field - > offset ;
* size = field - > size ;
return 0 ;
}
static int __parse_common ( struct pevent * pevent , void * data ,
int * size , int * offset , const char * name )
{
int ret ;
if ( ! * size ) {
ret = get_common_info ( pevent , name , offset , size ) ;
if ( ret < 0 )
return ret ;
}
return pevent_read_number ( pevent , data + * offset , * size ) ;
}
static int trace_parse_common_type ( struct pevent * pevent , void * data )
{
return __parse_common ( pevent , data ,
& pevent - > type_size , & pevent - > type_offset ,
" common_type " ) ;
}
static int parse_common_pid ( struct pevent * pevent , void * data )
{
return __parse_common ( pevent , data ,
& pevent - > pid_size , & pevent - > pid_offset ,
" common_pid " ) ;
}
static int parse_common_pc ( struct pevent * pevent , void * data )
{
return __parse_common ( pevent , data ,
& pevent - > pc_size , & pevent - > pc_offset ,
" common_preempt_count " ) ;
}
static int parse_common_flags ( struct pevent * pevent , void * data )
{
return __parse_common ( pevent , data ,
& pevent - > flags_size , & pevent - > flags_offset ,
" common_flags " ) ;
}
static int parse_common_lock_depth ( struct pevent * pevent , void * data )
{
2012-05-22 09:52:40 +04:00
return __parse_common ( pevent , data ,
& pevent - > ld_size , & pevent - > ld_offset ,
" common_lock_depth " ) ;
}
2012-04-06 02:47:53 +04:00
2012-05-22 09:52:40 +04:00
static int parse_common_migrate_disable ( struct pevent * pevent , void * data )
{
return __parse_common ( pevent , data ,
& pevent - > ld_size , & pevent - > ld_offset ,
" common_migrate_disable " ) ;
2012-04-06 02:47:53 +04:00
}
static int events_id_cmp ( const void * a , const void * b ) ;
/**
* pevent_find_event - find an event by given id
* @ pevent : a handle to the pevent
* @ id : the id of the event
*
* Returns an event that has a given @ id .
*/
struct event_format * pevent_find_event ( struct pevent * pevent , int id )
{
struct event_format * * eventptr ;
struct event_format key ;
struct event_format * pkey = & key ;
/* Check cache first */
if ( pevent - > last_event & & pevent - > last_event - > id = = id )
return pevent - > last_event ;
key . id = id ;
eventptr = bsearch ( & pkey , pevent - > events , pevent - > nr_events ,
sizeof ( * pevent - > events ) , events_id_cmp ) ;
if ( eventptr ) {
pevent - > last_event = * eventptr ;
return * eventptr ;
}
return NULL ;
}
/**
* pevent_find_event_by_name - find an event by given name
* @ pevent : a handle to the pevent
* @ sys : the system name to search for
* @ name : the name of the event to search for
*
* This returns an event with a given @ name and under the system
* @ sys . If @ sys is NULL the first event with @ name is returned .
*/
struct event_format *
pevent_find_event_by_name ( struct pevent * pevent ,
const char * sys , const char * name )
{
struct event_format * event ;
int i ;
if ( pevent - > last_event & &
strcmp ( pevent - > last_event - > name , name ) = = 0 & &
( ! sys | | strcmp ( pevent - > last_event - > system , sys ) = = 0 ) )
return pevent - > last_event ;
for ( i = 0 ; i < pevent - > nr_events ; i + + ) {
event = pevent - > events [ i ] ;
if ( strcmp ( event - > name , name ) = = 0 ) {
if ( ! sys )
break ;
if ( strcmp ( event - > system , sys ) = = 0 )
break ;
}
}
if ( i = = pevent - > nr_events )
event = NULL ;
pevent - > last_event = event ;
return event ;
}
static unsigned long long
eval_num_arg ( void * data , int size , struct event_format * event , struct print_arg * arg )
{
struct pevent * pevent = event - > pevent ;
unsigned long long val = 0 ;
unsigned long long left , right ;
struct print_arg * typearg = NULL ;
struct print_arg * larg ;
unsigned long offset ;
unsigned int field_size ;
switch ( arg - > type ) {
case PRINT_NULL :
/* ?? */
return 0 ;
case PRINT_ATOM :
return strtoull ( arg - > atom . atom , NULL , 0 ) ;
case PRINT_FIELD :
if ( ! arg - > field . field ) {
arg - > field . field = pevent_find_any_field ( event , arg - > field . name ) ;
if ( ! arg - > field . field )
2012-09-13 00:30:50 +04:00
goto out_warning_field ;
2012-04-06 02:47:53 +04:00
}
/* must be a number */
val = pevent_read_number ( pevent , data + arg - > field . field - > offset ,
arg - > field . field - > size ) ;
break ;
case PRINT_FLAGS :
case PRINT_SYMBOL :
2015-03-24 14:07:19 +03:00
case PRINT_INT_ARRAY :
2012-06-27 04:41:41 +04:00
case PRINT_HEX :
2012-04-06 02:47:53 +04:00
break ;
case PRINT_TYPE :
val = eval_num_arg ( data , size , event , arg - > typecast . item ) ;
return eval_type ( val , arg , 0 ) ;
case PRINT_STRING :
case PRINT_BSTRING :
2014-06-03 07:20:16 +04:00
case PRINT_BITMASK :
2012-04-06 02:47:53 +04:00
return 0 ;
case PRINT_FUNC : {
struct trace_seq s ;
trace_seq_init ( & s ) ;
val = process_defined_func ( & s , data , size , event , arg ) ;
trace_seq_destroy ( & s ) ;
return val ;
}
case PRINT_OP :
if ( strcmp ( arg - > op . op , " [ " ) = = 0 ) {
/*
* Arrays are special , since we don ' t want
* to read the arg as is .
*/
right = eval_num_arg ( data , size , event , arg - > op . right ) ;
/* handle typecasts */
larg = arg - > op . left ;
while ( larg - > type = = PRINT_TYPE ) {
if ( ! typearg )
typearg = larg ;
larg = larg - > typecast . item ;
}
/* Default to long size */
field_size = pevent - > long_size ;
switch ( larg - > type ) {
case PRINT_DYNAMIC_ARRAY :
offset = pevent_read_number ( pevent ,
data + larg - > dynarray . field - > offset ,
larg - > dynarray . field - > size ) ;
if ( larg - > dynarray . field - > elementsize )
field_size = larg - > dynarray . field - > elementsize ;
/*
* The actual length of the dynamic array is stored
* in the top half of the field , and the offset
* is in the bottom half of the 32 bit field .
*/
offset & = 0xffff ;
offset + = right ;
break ;
case PRINT_FIELD :
if ( ! larg - > field . field ) {
larg - > field . field =
pevent_find_any_field ( event , larg - > field . name ) ;
2012-09-13 00:30:50 +04:00
if ( ! larg - > field . field ) {
arg = larg ;
goto out_warning_field ;
}
2012-04-06 02:47:53 +04:00
}
field_size = larg - > field . field - > elementsize ;
offset = larg - > field . field - > offset +
right * larg - > field . field - > elementsize ;
break ;
default :
goto default_op ; /* oops, all bets off */
}
val = pevent_read_number ( pevent ,
data + offset , field_size ) ;
if ( typearg )
val = eval_type ( val , typearg , 1 ) ;
break ;
} else if ( strcmp ( arg - > op . op , " ? " ) = = 0 ) {
left = eval_num_arg ( data , size , event , arg - > op . left ) ;
arg = arg - > op . right ;
if ( left )
val = eval_num_arg ( data , size , event , arg - > op . left ) ;
else
val = eval_num_arg ( data , size , event , arg - > op . right ) ;
break ;
}
default_op :
left = eval_num_arg ( data , size , event , arg - > op . left ) ;
right = eval_num_arg ( data , size , event , arg - > op . right ) ;
switch ( arg - > op . op [ 0 ] ) {
case ' ! ' :
switch ( arg - > op . op [ 1 ] ) {
case 0 :
val = ! right ;
break ;
case ' = ' :
val = left ! = right ;
break ;
default :
2012-09-13 00:30:50 +04:00
goto out_warning_op ;
2012-04-06 02:47:53 +04:00
}
break ;
case ' ~ ' :
val = ~ right ;
break ;
case ' | ' :
if ( arg - > op . op [ 1 ] )
val = left | | right ;
else
val = left | right ;
break ;
case ' & ' :
if ( arg - > op . op [ 1 ] )
val = left & & right ;
else
val = left & right ;
break ;
case ' < ' :
switch ( arg - > op . op [ 1 ] ) {
case 0 :
val = left < right ;
break ;
case ' < ' :
val = left < < right ;
break ;
case ' = ' :
val = left < = right ;
break ;
default :
2012-09-13 00:30:50 +04:00
goto out_warning_op ;
2012-04-06 02:47:53 +04:00
}
break ;
case ' > ' :
switch ( arg - > op . op [ 1 ] ) {
case 0 :
val = left > right ;
break ;
case ' > ' :
val = left > > right ;
break ;
case ' = ' :
val = left > = right ;
break ;
default :
2012-09-13 00:30:50 +04:00
goto out_warning_op ;
2012-04-06 02:47:53 +04:00
}
break ;
case ' = ' :
if ( arg - > op . op [ 1 ] ! = ' = ' )
2012-09-13 00:30:50 +04:00
goto out_warning_op ;
2012-04-06 02:47:53 +04:00
val = left = = right ;
break ;
case ' - ' :
val = left - right ;
break ;
case ' + ' :
val = left + right ;
break ;
2012-04-06 02:48:04 +04:00
case ' / ' :
val = left / right ;
break ;
case ' * ' :
val = left * right ;
break ;
2012-04-06 02:47:53 +04:00
default :
2012-09-13 00:30:50 +04:00
goto out_warning_op ;
2012-04-06 02:47:53 +04:00
}
break ;
2013-11-12 01:08:10 +04:00
case PRINT_DYNAMIC_ARRAY :
/* Without [], we pass the address to the dynamic data */
offset = pevent_read_number ( pevent ,
data + arg - > dynarray . field - > offset ,
arg - > dynarray . field - > size ) ;
/*
* The actual length of the dynamic array is stored
* in the top half of the field , and the offset
* is in the bottom half of the 32 bit field .
*/
offset & = 0xffff ;
2013-11-19 23:14:51 +04:00
val = ( unsigned long long ) ( ( unsigned long ) data + offset ) ;
2013-11-12 01:08:10 +04:00
break ;
2012-04-06 02:47:53 +04:00
default : /* not sure what to do there */
return 0 ;
}
return val ;
2012-09-13 00:30:50 +04:00
out_warning_op :
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: unknown op '%s' " , __func__ , arg - > op . op ) ;
2012-09-13 00:30:50 +04:00
return 0 ;
out_warning_field :
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: field %s not found " ,
__func__ , arg - > field . name ) ;
2012-09-13 00:30:50 +04:00
return 0 ;
2012-04-06 02:47:53 +04:00
}
struct flag {
const char * name ;
unsigned long long value ;
} ;
static const struct flag flags [ ] = {
{ " HI_SOFTIRQ " , 0 } ,
{ " TIMER_SOFTIRQ " , 1 } ,
{ " NET_TX_SOFTIRQ " , 2 } ,
{ " NET_RX_SOFTIRQ " , 3 } ,
{ " BLOCK_SOFTIRQ " , 4 } ,
{ " BLOCK_IOPOLL_SOFTIRQ " , 5 } ,
{ " TASKLET_SOFTIRQ " , 6 } ,
{ " SCHED_SOFTIRQ " , 7 } ,
{ " HRTIMER_SOFTIRQ " , 8 } ,
{ " RCU_SOFTIRQ " , 9 } ,
{ " HRTIMER_NORESTART " , 0 } ,
{ " HRTIMER_RESTART " , 1 } ,
} ;
tools lib traceevent: Zero should not be considered "not found" in eval_flag()
Guilherme Cox found that:
There is, however, a potential bug if there is an item with code zero
that is not the first one in the symbol list, since eval_flag(..)
returns 0 when it doesn't find anything.
That is, if you have the following enums:
enum {
FOO_START = 0,
FOO_GO = 1,
FOO_END = 2
}
and then have:
__print_symbolic(foo, FOO_GO, "go", FOO_START, "start",
FOO_END, "end")
If none of the enums are known to pevent, then eval_flag() will return
zero, and it will match it to the first item in the list, which would be
FOO_GO, which is not zero.
Luckily, in most cases, the first element would be zero, and the parsing
would match out of sheer luck.
Reported-by: Guilherme Cox <cox@computer.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/r/20150324145813.0bfe95ba@gandalf.local.home
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-03-24 21:58:13 +03:00
static long long eval_flag ( const char * flag )
2012-04-06 02:47:53 +04:00
{
int i ;
/*
* Some flags in the format files do not get converted .
* If the flag is not numeric , see if it is something that
* we already know about .
*/
if ( isdigit ( flag [ 0 ] ) )
return strtoull ( flag , NULL , 0 ) ;
for ( i = 0 ; i < ( int ) ( sizeof ( flags ) / sizeof ( flags [ 0 ] ) ) ; i + + )
if ( strcmp ( flags [ i ] . name , flag ) = = 0 )
return flags [ i ] . value ;
tools lib traceevent: Zero should not be considered "not found" in eval_flag()
Guilherme Cox found that:
There is, however, a potential bug if there is an item with code zero
that is not the first one in the symbol list, since eval_flag(..)
returns 0 when it doesn't find anything.
That is, if you have the following enums:
enum {
FOO_START = 0,
FOO_GO = 1,
FOO_END = 2
}
and then have:
__print_symbolic(foo, FOO_GO, "go", FOO_START, "start",
FOO_END, "end")
If none of the enums are known to pevent, then eval_flag() will return
zero, and it will match it to the first item in the list, which would be
FOO_GO, which is not zero.
Luckily, in most cases, the first element would be zero, and the parsing
would match out of sheer luck.
Reported-by: Guilherme Cox <cox@computer.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/r/20150324145813.0bfe95ba@gandalf.local.home
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-03-24 21:58:13 +03:00
return - 1LL ;
2012-04-06 02:47:53 +04:00
}
static void print_str_to_seq ( struct trace_seq * s , const char * format ,
int len_arg , const char * str )
{
if ( len_arg > = 0 )
trace_seq_printf ( s , format , len_arg , str ) ;
else
trace_seq_printf ( s , format , str ) ;
}
2014-06-03 07:20:16 +04:00
static void print_bitmask_to_seq ( struct pevent * pevent ,
struct trace_seq * s , const char * format ,
int len_arg , const void * data , int size )
{
int nr_bits = size * 8 ;
int str_size = ( nr_bits + 3 ) / 4 ;
int len = 0 ;
char buf [ 3 ] ;
char * str ;
int index ;
int i ;
/*
* The kernel likes to put in commas every 32 bits , we
* can do the same .
*/
str_size + = ( nr_bits - 1 ) / 32 ;
str = malloc ( str_size + 1 ) ;
if ( ! str ) {
do_warning ( " %s: not enough memory! " , __func__ ) ;
return ;
}
str [ str_size ] = 0 ;
/* Start out with -2 for the two chars per byte */
for ( i = str_size - 2 ; i > = 0 ; i - = 2 ) {
/*
* data points to a bit mask of size bytes .
* In the kernel , this is an array of long words , thus
* endianess is very important .
*/
if ( pevent - > file_bigendian )
index = size - ( len + 1 ) ;
else
index = len ;
snprintf ( buf , 3 , " %02x " , * ( ( unsigned char * ) data + index ) ) ;
memcpy ( str + i , buf , 2 ) ;
len + + ;
if ( ! ( len & 3 ) & & i > 0 ) {
i - - ;
str [ i ] = ' , ' ;
}
}
if ( len_arg > = 0 )
trace_seq_printf ( s , format , len_arg , str ) ;
else
trace_seq_printf ( s , format , str ) ;
free ( str ) ;
}
2012-04-06 02:47:53 +04:00
static void print_str_arg ( struct trace_seq * s , void * data , int size ,
struct event_format * event , const char * format ,
int len_arg , struct print_arg * arg )
{
struct pevent * pevent = event - > pevent ;
struct print_flag_sym * flag ;
2012-06-27 04:41:40 +04:00
struct format_field * field ;
2013-11-02 01:53:55 +04:00
struct printk_map * printk ;
tools lib traceevent: Zero should not be considered "not found" in eval_flag()
Guilherme Cox found that:
There is, however, a potential bug if there is an item with code zero
that is not the first one in the symbol list, since eval_flag(..)
returns 0 when it doesn't find anything.
That is, if you have the following enums:
enum {
FOO_START = 0,
FOO_GO = 1,
FOO_END = 2
}
and then have:
__print_symbolic(foo, FOO_GO, "go", FOO_START, "start",
FOO_END, "end")
If none of the enums are known to pevent, then eval_flag() will return
zero, and it will match it to the first item in the list, which would be
FOO_GO, which is not zero.
Luckily, in most cases, the first element would be zero, and the parsing
would match out of sheer luck.
Reported-by: Guilherme Cox <cox@computer.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/r/20150324145813.0bfe95ba@gandalf.local.home
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-03-24 21:58:13 +03:00
long long val , fval ;
2015-09-22 16:22:03 +03:00
unsigned long long addr ;
2012-04-06 02:47:53 +04:00
char * str ;
2012-06-27 04:41:41 +04:00
unsigned char * hex ;
2012-04-06 02:47:53 +04:00
int print ;
2012-06-27 04:41:41 +04:00
int i , len ;
2012-04-06 02:47:53 +04:00
switch ( arg - > type ) {
case PRINT_NULL :
/* ?? */
return ;
case PRINT_ATOM :
print_str_to_seq ( s , format , len_arg , arg - > atom . atom ) ;
return ;
case PRINT_FIELD :
2012-06-27 04:41:40 +04:00
field = arg - > field . field ;
if ( ! field ) {
field = pevent_find_any_field ( event , arg - > field . name ) ;
2012-09-13 00:30:50 +04:00
if ( ! field ) {
str = arg - > field . name ;
goto out_warning_field ;
}
2012-06-27 04:41:40 +04:00
arg - > field . field = field ;
2012-04-06 02:47:53 +04:00
}
/* Zero sized fields, mean the rest of the data */
2012-06-27 04:41:40 +04:00
len = field - > size ? : size - field - > offset ;
2012-04-06 02:47:53 +04:00
/*
* Some events pass in pointers . If this is not an array
* and the size is the same as long_size , assume that it
* is a pointer .
*/
2012-06-27 04:41:40 +04:00
if ( ! ( field - > flags & FIELD_IS_ARRAY ) & &
field - > size = = pevent - > long_size ) {
2015-09-22 16:22:03 +03:00
/* Handle heterogeneous recording and processing
* architectures
*
* CASE I :
* Traces recorded on 32 - bit devices ( 32 - bit
* addressing ) and processed on 64 - bit devices :
* In this case , only 32 bits should be read .
*
* CASE II :
* Traces recorded on 64 bit devices and processed
* on 32 - bit devices :
* In this case , 64 bits must be read .
*/
addr = ( pevent - > long_size = = 8 ) ?
* ( unsigned long long * ) ( data + field - > offset ) :
( unsigned long long ) * ( unsigned int * ) ( data + field - > offset ) ;
2013-11-02 01:53:55 +04:00
/* Check if it matches a print format */
printk = find_printk ( pevent , addr ) ;
if ( printk )
trace_seq_puts ( s , printk - > printk ) ;
else
2015-09-22 16:22:03 +03:00
trace_seq_printf ( s , " %llx " , addr ) ;
2012-04-06 02:47:53 +04:00
break ;
}
2012-09-13 00:30:50 +04:00
str = malloc ( len + 1 ) ;
if ( ! str ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: not enough memory! " ,
__func__ ) ;
2012-09-13 00:30:50 +04:00
return ;
}
2012-06-27 04:41:40 +04:00
memcpy ( str , data + field - > offset , len ) ;
2012-04-06 02:47:53 +04:00
str [ len ] = 0 ;
print_str_to_seq ( s , format , len_arg , str ) ;
free ( str ) ;
break ;
case PRINT_FLAGS :
val = eval_num_arg ( data , size , event , arg - > flags . field ) ;
print = 0 ;
for ( flag = arg - > flags . flags ; flag ; flag = flag - > next ) {
fval = eval_flag ( flag - > value ) ;
tools lib traceevent: Zero should not be considered "not found" in eval_flag()
Guilherme Cox found that:
There is, however, a potential bug if there is an item with code zero
that is not the first one in the symbol list, since eval_flag(..)
returns 0 when it doesn't find anything.
That is, if you have the following enums:
enum {
FOO_START = 0,
FOO_GO = 1,
FOO_END = 2
}
and then have:
__print_symbolic(foo, FOO_GO, "go", FOO_START, "start",
FOO_END, "end")
If none of the enums are known to pevent, then eval_flag() will return
zero, and it will match it to the first item in the list, which would be
FOO_GO, which is not zero.
Luckily, in most cases, the first element would be zero, and the parsing
would match out of sheer luck.
Reported-by: Guilherme Cox <cox@computer.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/r/20150324145813.0bfe95ba@gandalf.local.home
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-03-24 21:58:13 +03:00
if ( ! val & & fval < 0 ) {
2012-04-06 02:47:53 +04:00
print_str_to_seq ( s , format , len_arg , flag - > str ) ;
break ;
}
tools lib traceevent: Zero should not be considered "not found" in eval_flag()
Guilherme Cox found that:
There is, however, a potential bug if there is an item with code zero
that is not the first one in the symbol list, since eval_flag(..)
returns 0 when it doesn't find anything.
That is, if you have the following enums:
enum {
FOO_START = 0,
FOO_GO = 1,
FOO_END = 2
}
and then have:
__print_symbolic(foo, FOO_GO, "go", FOO_START, "start",
FOO_END, "end")
If none of the enums are known to pevent, then eval_flag() will return
zero, and it will match it to the first item in the list, which would be
FOO_GO, which is not zero.
Luckily, in most cases, the first element would be zero, and the parsing
would match out of sheer luck.
Reported-by: Guilherme Cox <cox@computer.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/r/20150324145813.0bfe95ba@gandalf.local.home
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-03-24 21:58:13 +03:00
if ( fval > 0 & & ( val & fval ) = = fval ) {
2012-04-06 02:47:53 +04:00
if ( print & & arg - > flags . delim )
trace_seq_puts ( s , arg - > flags . delim ) ;
print_str_to_seq ( s , format , len_arg , flag - > str ) ;
print = 1 ;
val & = ~ fval ;
}
}
break ;
case PRINT_SYMBOL :
val = eval_num_arg ( data , size , event , arg - > symbol . field ) ;
for ( flag = arg - > symbol . symbols ; flag ; flag = flag - > next ) {
fval = eval_flag ( flag - > value ) ;
if ( val = = fval ) {
print_str_to_seq ( s , format , len_arg , flag - > str ) ;
break ;
}
}
break ;
2012-06-27 04:41:41 +04:00
case PRINT_HEX :
2013-11-02 01:53:56 +04:00
if ( arg - > hex . field - > type = = PRINT_DYNAMIC_ARRAY ) {
unsigned long offset ;
offset = pevent_read_number ( pevent ,
data + arg - > hex . field - > dynarray . field - > offset ,
arg - > hex . field - > dynarray . field - > size ) ;
hex = data + ( offset & 0xffff ) ;
} else {
field = arg - > hex . field - > field . field ;
if ( ! field ) {
str = arg - > hex . field - > field . name ;
field = pevent_find_any_field ( event , str ) ;
if ( ! field )
goto out_warning_field ;
arg - > hex . field - > field . field = field ;
}
hex = data + field - > offset ;
2012-06-27 04:41:41 +04:00
}
len = eval_num_arg ( data , size , event , arg - > hex . size ) ;
for ( i = 0 ; i < len ; i + + ) {
if ( i )
trace_seq_putc ( s , ' ' ) ;
trace_seq_printf ( s , " %02x " , hex [ i ] ) ;
}
break ;
2012-04-06 02:47:53 +04:00
2015-03-24 14:07:19 +03:00
case PRINT_INT_ARRAY : {
void * num ;
int el_size ;
if ( arg - > int_array . field - > type = = PRINT_DYNAMIC_ARRAY ) {
unsigned long offset ;
struct format_field * field =
arg - > int_array . field - > dynarray . field ;
offset = pevent_read_number ( pevent ,
data + field - > offset ,
field - > size ) ;
num = data + ( offset & 0xffff ) ;
} else {
field = arg - > int_array . field - > field . field ;
if ( ! field ) {
str = arg - > int_array . field - > field . name ;
field = pevent_find_any_field ( event , str ) ;
if ( ! field )
goto out_warning_field ;
arg - > int_array . field - > field . field = field ;
}
num = data + field - > offset ;
}
len = eval_num_arg ( data , size , event , arg - > int_array . count ) ;
el_size = eval_num_arg ( data , size , event ,
arg - > int_array . el_size ) ;
for ( i = 0 ; i < len ; i + + ) {
if ( i )
trace_seq_putc ( s , ' ' ) ;
if ( el_size = = 1 ) {
trace_seq_printf ( s , " %u " , * ( uint8_t * ) num ) ;
} else if ( el_size = = 2 ) {
trace_seq_printf ( s , " %u " , * ( uint16_t * ) num ) ;
} else if ( el_size = = 4 ) {
trace_seq_printf ( s , " %u " , * ( uint32_t * ) num ) ;
} else if ( el_size = = 8 ) {
2015-04-24 04:45:16 +03:00
trace_seq_printf ( s , " % " PRIu64 , * ( uint64_t * ) num ) ;
2015-03-24 14:07:19 +03:00
} else {
trace_seq_printf ( s , " BAD SIZE:%d 0x%x " ,
el_size , * ( uint8_t * ) num ) ;
el_size = 1 ;
}
num + = el_size ;
}
break ;
}
2012-04-06 02:47:53 +04:00
case PRINT_TYPE :
break ;
case PRINT_STRING : {
int str_offset ;
if ( arg - > string . offset = = - 1 ) {
struct format_field * f ;
f = pevent_find_any_field ( event , arg - > string . string ) ;
arg - > string . offset = f - > offset ;
}
str_offset = data2host4 ( pevent , data + arg - > string . offset ) ;
str_offset & = 0xffff ;
print_str_to_seq ( s , format , len_arg , ( ( char * ) data ) + str_offset ) ;
break ;
}
case PRINT_BSTRING :
2011-11-16 03:47:48 +04:00
print_str_to_seq ( s , format , len_arg , arg - > string . string ) ;
2012-04-06 02:47:53 +04:00
break ;
2014-06-03 07:20:16 +04:00
case PRINT_BITMASK : {
int bitmask_offset ;
int bitmask_size ;
if ( arg - > bitmask . offset = = - 1 ) {
struct format_field * f ;
f = pevent_find_any_field ( event , arg - > bitmask . bitmask ) ;
arg - > bitmask . offset = f - > offset ;
}
bitmask_offset = data2host4 ( pevent , data + arg - > bitmask . offset ) ;
bitmask_size = bitmask_offset > > 16 ;
bitmask_offset & = 0xffff ;
print_bitmask_to_seq ( pevent , s , format , len_arg ,
data + bitmask_offset , bitmask_size ) ;
break ;
}
2012-04-06 02:47:53 +04:00
case PRINT_OP :
/*
* The only op for string should be ? :
*/
if ( arg - > op . op [ 0 ] ! = ' ? ' )
return ;
val = eval_num_arg ( data , size , event , arg - > op . left ) ;
if ( val )
print_str_arg ( s , data , size , event ,
format , len_arg , arg - > op . right - > op . left ) ;
else
print_str_arg ( s , data , size , event ,
format , len_arg , arg - > op . right - > op . right ) ;
break ;
case PRINT_FUNC :
process_defined_func ( s , data , size , event , arg ) ;
break ;
default :
/* well... */
break ;
}
2012-09-13 00:30:50 +04:00
return ;
out_warning_field :
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: field %s not found " ,
__func__ , arg - > field . name ) ;
2012-04-06 02:47:53 +04:00
}
static unsigned long long
process_defined_func ( struct trace_seq * s , void * data , int size ,
struct event_format * event , struct print_arg * arg )
{
struct pevent_function_handler * func_handle = arg - > func . func ;
struct pevent_func_params * param ;
unsigned long long * args ;
unsigned long long ret ;
struct print_arg * farg ;
struct trace_seq str ;
struct save_str {
struct save_str * next ;
char * str ;
} * strings = NULL , * string ;
int i ;
if ( ! func_handle - > nr_args ) {
ret = ( * func_handle - > func ) ( s , NULL ) ;
goto out ;
}
farg = arg - > func . args ;
param = func_handle - > params ;
2012-09-13 00:30:50 +04:00
ret = ULLONG_MAX ;
args = malloc ( sizeof ( * args ) * func_handle - > nr_args ) ;
if ( ! args )
goto out ;
2012-04-06 02:47:53 +04:00
for ( i = 0 ; i < func_handle - > nr_args ; i + + ) {
switch ( param - > type ) {
case PEVENT_FUNC_ARG_INT :
case PEVENT_FUNC_ARG_LONG :
case PEVENT_FUNC_ARG_PTR :
args [ i ] = eval_num_arg ( data , size , event , farg ) ;
break ;
case PEVENT_FUNC_ARG_STRING :
trace_seq_init ( & str ) ;
print_str_arg ( & str , data , size , event , " %s " , - 1 , farg ) ;
trace_seq_terminate ( & str ) ;
2012-09-13 00:30:50 +04:00
string = malloc ( sizeof ( * string ) ) ;
if ( ! string ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s(%d): malloc str " ,
__func__ , __LINE__ ) ;
2012-09-13 00:30:50 +04:00
goto out_free ;
}
2012-04-06 02:47:53 +04:00
string - > next = strings ;
string - > str = strdup ( str . buffer ) ;
2012-09-13 00:30:50 +04:00
if ( ! string - > str ) {
free ( string ) ;
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s(%d): malloc str " ,
__func__ , __LINE__ ) ;
2012-09-13 00:30:50 +04:00
goto out_free ;
}
2012-08-07 21:43:14 +04:00
args [ i ] = ( uintptr_t ) string - > str ;
2012-04-06 02:47:53 +04:00
strings = string ;
trace_seq_destroy ( & str ) ;
break ;
default :
/*
* Something went totally wrong , this is not
* an input error , something in this code broke .
*/
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " Unexpected end of arguments \n " ) ;
2012-09-13 00:30:50 +04:00
goto out_free ;
2012-04-06 02:47:53 +04:00
}
farg = farg - > next ;
2012-05-23 06:36:51 +04:00
param = param - > next ;
2012-04-06 02:47:53 +04:00
}
ret = ( * func_handle - > func ) ( s , args ) ;
2012-09-13 00:30:50 +04:00
out_free :
2012-04-06 02:47:53 +04:00
free ( args ) ;
while ( strings ) {
string = strings ;
strings = string - > next ;
free ( string - > str ) ;
free ( string ) ;
}
out :
/* TBD : handle return type here */
return ret ;
}
2012-09-12 22:39:59 +04:00
static void free_args ( struct print_arg * args )
{
struct print_arg * next ;
while ( args ) {
next = args - > next ;
free_arg ( args ) ;
args = next ;
}
}
2012-04-06 02:47:53 +04:00
static struct print_arg * make_bprint_args ( char * fmt , void * data , int size , struct event_format * event )
{
struct pevent * pevent = event - > pevent ;
struct format_field * field , * ip_field ;
struct print_arg * args , * arg , * * next ;
unsigned long long ip , val ;
char * ptr ;
void * bptr ;
2011-11-16 03:47:48 +04:00
int vsize ;
2012-04-06 02:47:53 +04:00
field = pevent - > bprint_buf_field ;
ip_field = pevent - > bprint_ip_field ;
if ( ! field ) {
field = pevent_find_field ( event , " buf " ) ;
2012-09-13 00:30:50 +04:00
if ( ! field ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " can't find buffer field for binary printk " ) ;
2012-09-13 00:30:50 +04:00
return NULL ;
}
2012-04-06 02:47:53 +04:00
ip_field = pevent_find_field ( event , " ip " ) ;
2012-09-13 00:30:50 +04:00
if ( ! ip_field ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " can't find ip field for binary printk " ) ;
2012-09-13 00:30:50 +04:00
return NULL ;
}
2012-04-06 02:47:53 +04:00
pevent - > bprint_buf_field = field ;
pevent - > bprint_ip_field = ip_field ;
}
ip = pevent_read_number ( pevent , data + ip_field - > offset , ip_field - > size ) ;
/*
* The first arg is the IP pointer .
*/
args = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! args ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s(%d): not enough memory! " ,
__func__ , __LINE__ ) ;
2012-09-20 06:09:19 +04:00
return NULL ;
}
2012-04-06 02:47:53 +04:00
arg = args ;
arg - > next = NULL ;
next = & arg - > next ;
arg - > type = PRINT_ATOM ;
2012-09-12 22:39:59 +04:00
if ( asprintf ( & arg - > atom . atom , " %lld " , ip ) < 0 )
goto out_free ;
2012-04-06 02:47:53 +04:00
2015-03-12 06:13:57 +03:00
/* skip the first "%ps: " */
2013-11-02 01:53:57 +04:00
for ( ptr = fmt + 5 , bptr = data + field - > offset ;
2012-04-06 02:47:53 +04:00
bptr < data + size & & * ptr ; ptr + + ) {
int ls = 0 ;
if ( * ptr = = ' % ' ) {
process_again :
ptr + + ;
switch ( * ptr ) {
case ' % ' :
break ;
case ' l ' :
ls + + ;
goto process_again ;
case ' L ' :
ls = 2 ;
goto process_again ;
case ' 0 ' . . . ' 9 ' :
goto process_again ;
2011-11-16 03:47:48 +04:00
case ' . ' :
goto process_again ;
2015-03-24 16:57:51 +03:00
case ' z ' :
case ' Z ' :
ls = 1 ;
goto process_again ;
2012-04-06 02:47:53 +04:00
case ' p ' :
ls = 1 ;
/* fall through */
case ' d ' :
case ' u ' :
case ' x ' :
case ' i ' :
switch ( ls ) {
case 0 :
2011-11-16 03:47:48 +04:00
vsize = 4 ;
2012-04-06 02:47:53 +04:00
break ;
case 1 :
2011-11-16 03:47:48 +04:00
vsize = pevent - > long_size ;
2012-04-06 02:47:53 +04:00
break ;
case 2 :
2011-11-16 03:47:48 +04:00
vsize = 8 ;
2012-04-25 01:19:40 +04:00
break ;
2012-04-06 02:47:53 +04:00
default :
2011-11-16 03:47:48 +04:00
vsize = ls ; /* ? */
2012-04-06 02:47:53 +04:00
break ;
}
2011-11-16 03:47:48 +04:00
/* fall through */
case ' * ' :
if ( * ptr = = ' * ' )
vsize = 4 ;
/* the pointers are always 4 bytes aligned */
bptr = ( void * ) ( ( ( unsigned long ) bptr + 3 ) &
~ 3 ) ;
val = pevent_read_number ( pevent , bptr , vsize ) ;
bptr + = vsize ;
2012-04-06 02:47:53 +04:00
arg = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! arg ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s(%d): not enough memory! " ,
2012-09-20 06:09:19 +04:00
__func__ , __LINE__ ) ;
goto out_free ;
}
2012-04-06 02:47:53 +04:00
arg - > next = NULL ;
arg - > type = PRINT_ATOM ;
2012-09-12 22:39:59 +04:00
if ( asprintf ( & arg - > atom . atom , " %lld " , val ) < 0 ) {
free ( arg ) ;
goto out_free ;
}
2012-04-06 02:47:53 +04:00
* next = arg ;
next = & arg - > next ;
2011-11-16 03:47:48 +04:00
/*
* The ' * ' case means that an arg is used as the length .
* We need to continue to figure out for what .
*/
if ( * ptr = = ' * ' )
goto process_again ;
2012-04-06 02:47:53 +04:00
break ;
case ' s ' :
arg = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! arg ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s(%d): not enough memory! " ,
2012-09-20 06:09:19 +04:00
__func__ , __LINE__ ) ;
goto out_free ;
}
2012-04-06 02:47:53 +04:00
arg - > next = NULL ;
arg - > type = PRINT_BSTRING ;
arg - > string . string = strdup ( bptr ) ;
2012-04-09 06:54:31 +04:00
if ( ! arg - > string . string )
2012-09-13 00:30:50 +04:00
goto out_free ;
2012-04-06 02:47:53 +04:00
bptr + = strlen ( bptr ) + 1 ;
* next = arg ;
next = & arg - > next ;
default :
break ;
}
}
}
return args ;
2012-09-12 22:39:59 +04:00
out_free :
free_args ( args ) ;
return NULL ;
2012-04-06 02:47:53 +04:00
}
static char *
2012-09-11 02:15:03 +04:00
get_bprint_format ( void * data , int size __maybe_unused ,
struct event_format * event )
2012-04-06 02:47:53 +04:00
{
struct pevent * pevent = event - > pevent ;
unsigned long long addr ;
struct format_field * field ;
struct printk_map * printk ;
char * format ;
field = pevent - > bprint_fmt_field ;
if ( ! field ) {
field = pevent_find_field ( event , " fmt " ) ;
2012-09-13 00:30:50 +04:00
if ( ! field ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " can't find format field for binary printk " ) ;
2012-09-13 00:30:50 +04:00
return NULL ;
}
2012-04-06 02:47:53 +04:00
pevent - > bprint_fmt_field = field ;
}
addr = pevent_read_number ( pevent , data + field - > offset , field - > size ) ;
printk = find_printk ( pevent , addr ) ;
if ( ! printk ) {
2013-11-02 01:53:57 +04:00
if ( asprintf ( & format , " %%pf: (NO FORMAT FOUND at %llx) \n " , addr ) < 0 )
2012-09-12 22:39:59 +04:00
return NULL ;
2012-04-06 02:47:53 +04:00
return format ;
}
2013-11-02 01:53:57 +04:00
if ( asprintf ( & format , " %s: %s " , " %pf " , printk - > printk ) < 0 )
2012-09-12 22:39:59 +04:00
return NULL ;
2012-04-06 02:47:53 +04:00
return format ;
}
static void print_mac_arg ( struct trace_seq * s , int mac , void * data , int size ,
struct event_format * event , struct print_arg * arg )
{
unsigned char * buf ;
2012-11-10 00:40:47 +04:00
const char * fmt = " %.2x:%.2x:%.2x:%.2x:%.2x:%.2x " ;
2012-04-06 02:47:53 +04:00
if ( arg - > type = = PRINT_FUNC ) {
process_defined_func ( s , data , size , event , arg ) ;
return ;
}
if ( arg - > type ! = PRINT_FIELD ) {
trace_seq_printf ( s , " ARG TYPE NOT FIELD BUT %d " ,
arg - > type ) ;
return ;
}
if ( mac = = ' m ' )
fmt = " %.2x%.2x%.2x%.2x%.2x%.2x " ;
if ( ! arg - > field . field ) {
arg - > field . field =
pevent_find_any_field ( event , arg - > field . name ) ;
2012-09-13 00:30:50 +04:00
if ( ! arg - > field . field ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " %s: field %s not found " ,
__func__ , arg - > field . name ) ;
2012-09-13 00:30:50 +04:00
return ;
}
2012-04-06 02:47:53 +04:00
}
if ( arg - > field . field - > size ! = 6 ) {
trace_seq_printf ( s , " INVALIDMAC " ) ;
return ;
}
buf = data + arg - > field . field - > offset ;
trace_seq_printf ( s , fmt , buf [ 0 ] , buf [ 1 ] , buf [ 2 ] , buf [ 3 ] , buf [ 4 ] , buf [ 5 ] ) ;
}
2014-12-19 05:11:11 +03:00
static void print_ip4_addr ( struct trace_seq * s , char i , unsigned char * buf )
{
const char * fmt ;
if ( i = = ' i ' )
fmt = " %03d.%03d.%03d.%03d " ;
else
fmt = " %d.%d.%d.%d " ;
trace_seq_printf ( s , fmt , buf [ 0 ] , buf [ 1 ] , buf [ 2 ] , buf [ 3 ] ) ;
}
static inline bool ipv6_addr_v4mapped ( const struct in6_addr * a )
{
return ( ( unsigned long ) ( a - > s6_addr32 [ 0 ] | a - > s6_addr32 [ 1 ] ) |
( unsigned long ) ( a - > s6_addr32 [ 2 ] ^ htonl ( 0x0000ffff ) ) ) = = 0UL ;
}
static inline bool ipv6_addr_is_isatap ( const struct in6_addr * addr )
{
return ( addr - > s6_addr32 [ 2 ] | htonl ( 0x02000000 ) ) = = htonl ( 0x02005EFE ) ;
}
static void print_ip6c_addr ( struct trace_seq * s , unsigned char * addr )
{
int i , j , range ;
unsigned char zerolength [ 8 ] ;
int longest = 1 ;
int colonpos = - 1 ;
uint16_t word ;
uint8_t hi , lo ;
bool needcolon = false ;
bool useIPv4 ;
struct in6_addr in6 ;
memcpy ( & in6 , addr , sizeof ( struct in6_addr ) ) ;
useIPv4 = ipv6_addr_v4mapped ( & in6 ) | | ipv6_addr_is_isatap ( & in6 ) ;
memset ( zerolength , 0 , sizeof ( zerolength ) ) ;
if ( useIPv4 )
range = 6 ;
else
range = 8 ;
/* find position of longest 0 run */
for ( i = 0 ; i < range ; i + + ) {
for ( j = i ; j < range ; j + + ) {
if ( in6 . s6_addr16 [ j ] ! = 0 )
break ;
zerolength [ i ] + + ;
}
}
for ( i = 0 ; i < range ; i + + ) {
if ( zerolength [ i ] > longest ) {
longest = zerolength [ i ] ;
colonpos = i ;
}
}
if ( longest = = 1 ) /* don't compress a single 0 */
colonpos = - 1 ;
/* emit address */
for ( i = 0 ; i < range ; i + + ) {
if ( i = = colonpos ) {
if ( needcolon | | i = = 0 )
trace_seq_printf ( s , " : " ) ;
trace_seq_printf ( s , " : " ) ;
needcolon = false ;
i + = longest - 1 ;
continue ;
}
if ( needcolon ) {
trace_seq_printf ( s , " : " ) ;
needcolon = false ;
}
/* hex u16 without leading 0s */
word = ntohs ( in6 . s6_addr16 [ i ] ) ;
hi = word > > 8 ;
lo = word & 0xff ;
if ( hi )
trace_seq_printf ( s , " %x%02x " , hi , lo ) ;
else
trace_seq_printf ( s , " %x " , lo ) ;
needcolon = true ;
}
if ( useIPv4 ) {
if ( needcolon )
trace_seq_printf ( s , " : " ) ;
print_ip4_addr ( s , ' I ' , & in6 . s6_addr [ 12 ] ) ;
}
return ;
}
static void print_ip6_addr ( struct trace_seq * s , char i , unsigned char * buf )
{
int j ;
for ( j = 0 ; j < 16 ; j + = 2 ) {
trace_seq_printf ( s , " %02x%02x " , buf [ j ] , buf [ j + 1 ] ) ;
if ( i = = ' I ' & & j < 14 )
trace_seq_printf ( s , " : " ) ;
}
}
/*
* % pi4 print an IPv4 address with leading zeros
* % pI4 print an IPv4 address without leading zeros
* % pi6 print an IPv6 address without colons
* % pI6 print an IPv6 address with colons
* % pI6c print an IPv6 address in compressed form with colons
* % pISpc print an IP address based on sockaddr ; p adds port .
*/
static int print_ipv4_arg ( struct trace_seq * s , const char * ptr , char i ,
void * data , int size , struct event_format * event ,
struct print_arg * arg )
{
unsigned char * buf ;
if ( arg - > type = = PRINT_FUNC ) {
process_defined_func ( s , data , size , event , arg ) ;
return 0 ;
}
if ( arg - > type ! = PRINT_FIELD ) {
trace_seq_printf ( s , " ARG TYPE NOT FIELD BUT %d " , arg - > type ) ;
return 0 ;
}
if ( ! arg - > field . field ) {
arg - > field . field =
pevent_find_any_field ( event , arg - > field . name ) ;
if ( ! arg - > field . field ) {
do_warning ( " %s: field %s not found " ,
__func__ , arg - > field . name ) ;
return 0 ;
}
}
buf = data + arg - > field . field - > offset ;
if ( arg - > field . field - > size ! = 4 ) {
trace_seq_printf ( s , " INVALIDIPv4 " ) ;
return 0 ;
}
print_ip4_addr ( s , i , buf ) ;
return 0 ;
}
static int print_ipv6_arg ( struct trace_seq * s , const char * ptr , char i ,
void * data , int size , struct event_format * event ,
struct print_arg * arg )
{
char have_c = 0 ;
unsigned char * buf ;
int rc = 0 ;
/* pI6c */
if ( i = = ' I ' & & * ptr = = ' c ' ) {
have_c = 1 ;
ptr + + ;
rc + + ;
}
if ( arg - > type = = PRINT_FUNC ) {
process_defined_func ( s , data , size , event , arg ) ;
return rc ;
}
if ( arg - > type ! = PRINT_FIELD ) {
trace_seq_printf ( s , " ARG TYPE NOT FIELD BUT %d " , arg - > type ) ;
return rc ;
}
if ( ! arg - > field . field ) {
arg - > field . field =
pevent_find_any_field ( event , arg - > field . name ) ;
if ( ! arg - > field . field ) {
do_warning ( " %s: field %s not found " ,
__func__ , arg - > field . name ) ;
return rc ;
}
}
buf = data + arg - > field . field - > offset ;
if ( arg - > field . field - > size ! = 16 ) {
trace_seq_printf ( s , " INVALIDIPv6 " ) ;
return rc ;
}
if ( have_c )
print_ip6c_addr ( s , buf ) ;
else
print_ip6_addr ( s , i , buf ) ;
return rc ;
}
static int print_ipsa_arg ( struct trace_seq * s , const char * ptr , char i ,
void * data , int size , struct event_format * event ,
struct print_arg * arg )
{
char have_c = 0 , have_p = 0 ;
unsigned char * buf ;
struct sockaddr_storage * sa ;
int rc = 0 ;
/* pISpc */
if ( i = = ' I ' ) {
if ( * ptr = = ' p ' ) {
have_p = 1 ;
ptr + + ;
rc + + ;
}
if ( * ptr = = ' c ' ) {
have_c = 1 ;
ptr + + ;
rc + + ;
}
}
if ( arg - > type = = PRINT_FUNC ) {
process_defined_func ( s , data , size , event , arg ) ;
return rc ;
}
if ( arg - > type ! = PRINT_FIELD ) {
trace_seq_printf ( s , " ARG TYPE NOT FIELD BUT %d " , arg - > type ) ;
return rc ;
}
if ( ! arg - > field . field ) {
arg - > field . field =
pevent_find_any_field ( event , arg - > field . name ) ;
if ( ! arg - > field . field ) {
do_warning ( " %s: field %s not found " ,
__func__ , arg - > field . name ) ;
return rc ;
}
}
sa = ( struct sockaddr_storage * ) ( data + arg - > field . field - > offset ) ;
if ( sa - > ss_family = = AF_INET ) {
struct sockaddr_in * sa4 = ( struct sockaddr_in * ) sa ;
if ( arg - > field . field - > size < sizeof ( struct sockaddr_in ) ) {
trace_seq_printf ( s , " INVALIDIPv4 " ) ;
return rc ;
}
print_ip4_addr ( s , i , ( unsigned char * ) & sa4 - > sin_addr ) ;
if ( have_p )
trace_seq_printf ( s , " :%d " , ntohs ( sa4 - > sin_port ) ) ;
} else if ( sa - > ss_family = = AF_INET6 ) {
struct sockaddr_in6 * sa6 = ( struct sockaddr_in6 * ) sa ;
if ( arg - > field . field - > size < sizeof ( struct sockaddr_in6 ) ) {
trace_seq_printf ( s , " INVALIDIPv6 " ) ;
return rc ;
}
if ( have_p )
trace_seq_printf ( s , " [ " ) ;
buf = ( unsigned char * ) & sa6 - > sin6_addr ;
if ( have_c )
print_ip6c_addr ( s , buf ) ;
else
print_ip6_addr ( s , i , buf ) ;
if ( have_p )
trace_seq_printf ( s , " ]:%d " , ntohs ( sa6 - > sin6_port ) ) ;
}
return rc ;
}
static int print_ip_arg ( struct trace_seq * s , const char * ptr ,
void * data , int size , struct event_format * event ,
struct print_arg * arg )
{
char i = * ptr ; /* 'i' or 'I' */
char ver ;
int rc = 0 ;
ptr + + ;
rc + + ;
ver = * ptr ;
ptr + + ;
rc + + ;
switch ( ver ) {
case ' 4 ' :
rc + = print_ipv4_arg ( s , ptr , i , data , size , event , arg ) ;
break ;
case ' 6 ' :
rc + = print_ipv6_arg ( s , ptr , i , data , size , event , arg ) ;
break ;
case ' S ' :
rc + = print_ipsa_arg ( s , ptr , i , data , size , event , arg ) ;
break ;
default :
return 0 ;
}
return rc ;
}
tools lib traceevent: Check string is really printable
When libtraceevent parses format fields, it assumes that array of 1 byte
is string but it's not always true. The kvm_emulate_insn contains 15 u8
array of insn that contains (binary) instructions. Thus when it's
printed, it'll have broken output like below:
kvm_emulate_insn: [FAILED TO PARSE] rip=3238197797 csbase=0 len=2 \
insn=<89>P^]<B4>& flags=5 failed=0
With this patch:
kvm_emulate_insn: [FAILED TO PARSE] rip=3238197797 csbase=0 len=2 \
insn=ARRAY[89, 10, 5d, c3, 8d, b4, 26, 00, 00, 00, 00, 55, 89, e5, 3e] flags=5 failed=0
Suggested-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/1340352615-20737-2-git-send-email-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2012-06-22 12:10:15 +04:00
static int is_printable_array ( char * p , unsigned int len )
{
unsigned int i ;
for ( i = 0 ; i < len & & p [ i ] ; i + + )
2013-11-02 01:53:58 +04:00
if ( ! isprint ( p [ i ] ) & & ! isspace ( p [ i ] ) )
tools lib traceevent: Check string is really printable
When libtraceevent parses format fields, it assumes that array of 1 byte
is string but it's not always true. The kvm_emulate_insn contains 15 u8
array of insn that contains (binary) instructions. Thus when it's
printed, it'll have broken output like below:
kvm_emulate_insn: [FAILED TO PARSE] rip=3238197797 csbase=0 len=2 \
insn=<89>P^]<B4>& flags=5 failed=0
With this patch:
kvm_emulate_insn: [FAILED TO PARSE] rip=3238197797 csbase=0 len=2 \
insn=ARRAY[89, 10, 5d, c3, 8d, b4, 26, 00, 00, 00, 00, 55, 89, e5, 3e] flags=5 failed=0
Suggested-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/1340352615-20737-2-git-send-email-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2012-06-22 12:10:15 +04:00
return 0 ;
return 1 ;
}
2012-11-09 22:18:57 +04:00
static void print_event_fields ( struct trace_seq * s , void * data ,
int size __maybe_unused ,
2012-04-06 02:47:53 +04:00
struct event_format * event )
{
struct format_field * field ;
unsigned long long val ;
unsigned int offset , len , i ;
field = event - > format . fields ;
while ( field ) {
trace_seq_printf ( s , " %s= " , field - > name ) ;
if ( field - > flags & FIELD_IS_ARRAY ) {
offset = field - > offset ;
len = field - > size ;
if ( field - > flags & FIELD_IS_DYNAMIC ) {
val = pevent_read_number ( event - > pevent , data + offset , len ) ;
offset = val ;
len = offset > > 16 ;
offset & = 0xffff ;
}
tools lib traceevent: Check string is really printable
When libtraceevent parses format fields, it assumes that array of 1 byte
is string but it's not always true. The kvm_emulate_insn contains 15 u8
array of insn that contains (binary) instructions. Thus when it's
printed, it'll have broken output like below:
kvm_emulate_insn: [FAILED TO PARSE] rip=3238197797 csbase=0 len=2 \
insn=<89>P^]<B4>& flags=5 failed=0
With this patch:
kvm_emulate_insn: [FAILED TO PARSE] rip=3238197797 csbase=0 len=2 \
insn=ARRAY[89, 10, 5d, c3, 8d, b4, 26, 00, 00, 00, 00, 55, 89, e5, 3e] flags=5 failed=0
Suggested-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/1340352615-20737-2-git-send-email-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2012-06-22 12:10:15 +04:00
if ( field - > flags & FIELD_IS_STRING & &
is_printable_array ( data + offset , len ) ) {
2012-04-06 02:47:53 +04:00
trace_seq_printf ( s , " %s " , ( char * ) data + offset ) ;
} else {
trace_seq_puts ( s , " ARRAY[ " ) ;
for ( i = 0 ; i < len ; i + + ) {
if ( i )
trace_seq_puts ( s , " , " ) ;
trace_seq_printf ( s , " %02x " ,
* ( ( unsigned char * ) data + offset + i ) ) ;
}
trace_seq_putc ( s , ' ] ' ) ;
tools lib traceevent: Check string is really printable
When libtraceevent parses format fields, it assumes that array of 1 byte
is string but it's not always true. The kvm_emulate_insn contains 15 u8
array of insn that contains (binary) instructions. Thus when it's
printed, it'll have broken output like below:
kvm_emulate_insn: [FAILED TO PARSE] rip=3238197797 csbase=0 len=2 \
insn=<89>P^]<B4>& flags=5 failed=0
With this patch:
kvm_emulate_insn: [FAILED TO PARSE] rip=3238197797 csbase=0 len=2 \
insn=ARRAY[89, 10, 5d, c3, 8d, b4, 26, 00, 00, 00, 00, 55, 89, e5, 3e] flags=5 failed=0
Suggested-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/1340352615-20737-2-git-send-email-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2012-06-22 12:10:15 +04:00
field - > flags & = ~ FIELD_IS_STRING ;
2012-04-06 02:47:53 +04:00
}
} else {
val = pevent_read_number ( event - > pevent , data + field - > offset ,
field - > size ) ;
if ( field - > flags & FIELD_IS_POINTER ) {
trace_seq_printf ( s , " 0x%llx " , val ) ;
} else if ( field - > flags & FIELD_IS_SIGNED ) {
switch ( field - > size ) {
case 4 :
/*
* If field is long then print it in hex .
* A long usually stores pointers .
*/
if ( field - > flags & FIELD_IS_LONG )
trace_seq_printf ( s , " 0x%x " , ( int ) val ) ;
else
trace_seq_printf ( s , " %d " , ( int ) val ) ;
break ;
case 2 :
trace_seq_printf ( s , " %2d " , ( short ) val ) ;
break ;
case 1 :
trace_seq_printf ( s , " %1d " , ( char ) val ) ;
break ;
default :
trace_seq_printf ( s , " %lld " , val ) ;
}
} else {
if ( field - > flags & FIELD_IS_LONG )
trace_seq_printf ( s , " 0x%llx " , val ) ;
else
trace_seq_printf ( s , " %llu " , val ) ;
}
}
field = field - > next ;
}
}
static void pretty_print ( struct trace_seq * s , void * data , int size , struct event_format * event )
{
struct pevent * pevent = event - > pevent ;
struct print_fmt * print_fmt = & event - > print_fmt ;
struct print_arg * arg = print_fmt - > args ;
struct print_arg * args = NULL ;
const char * ptr = print_fmt - > format ;
unsigned long long val ;
struct func_map * func ;
const char * saveptr ;
2013-11-20 03:29:37 +04:00
struct trace_seq p ;
2012-04-06 02:47:53 +04:00
char * bprint_fmt = NULL ;
char format [ 32 ] ;
int show_func ;
int len_as_arg ;
int len_arg ;
int len ;
int ls ;
if ( event - > flags & EVENT_FL_FAILED ) {
trace_seq_printf ( s , " [FAILED TO PARSE] " ) ;
print_event_fields ( s , data , size , event ) ;
return ;
}
if ( event - > flags & EVENT_FL_ISBPRINT ) {
bprint_fmt = get_bprint_format ( data , size , event ) ;
args = make_bprint_args ( bprint_fmt , data , size , event ) ;
arg = args ;
ptr = bprint_fmt ;
}
for ( ; * ptr ; ptr + + ) {
ls = 0 ;
if ( * ptr = = ' \\ ' ) {
ptr + + ;
switch ( * ptr ) {
case ' n ' :
trace_seq_putc ( s , ' \n ' ) ;
break ;
case ' t ' :
trace_seq_putc ( s , ' \t ' ) ;
break ;
case ' r ' :
trace_seq_putc ( s , ' \r ' ) ;
break ;
case ' \\ ' :
trace_seq_putc ( s , ' \\ ' ) ;
break ;
default :
trace_seq_putc ( s , * ptr ) ;
break ;
}
} else if ( * ptr = = ' % ' ) {
saveptr = ptr ;
show_func = 0 ;
len_as_arg = 0 ;
cont_process :
ptr + + ;
switch ( * ptr ) {
case ' % ' :
trace_seq_putc ( s , ' % ' ) ;
break ;
case ' # ' :
/* FIXME: need to handle properly */
goto cont_process ;
case ' h ' :
ls - - ;
goto cont_process ;
case ' l ' :
ls + + ;
goto cont_process ;
case ' L ' :
ls = 2 ;
goto cont_process ;
case ' * ' :
/* The argument is the length. */
2012-09-07 06:49:45 +04:00
if ( ! arg ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " no argument match " ) ;
2012-09-07 06:49:45 +04:00
event - > flags | = EVENT_FL_FAILED ;
goto out_failed ;
}
2012-04-06 02:47:53 +04:00
len_arg = eval_num_arg ( data , size , event , arg ) ;
len_as_arg = 1 ;
arg = arg - > next ;
goto cont_process ;
case ' . ' :
case ' z ' :
case ' Z ' :
case ' 0 ' . . . ' 9 ' :
2015-08-27 16:46:01 +03:00
case ' - ' :
2012-04-06 02:47:53 +04:00
goto cont_process ;
case ' p ' :
if ( pevent - > long_size = = 4 )
ls = 1 ;
else
ls = 2 ;
if ( * ( ptr + 1 ) = = ' F ' | |
* ( ptr + 1 ) = = ' f ' ) {
ptr + + ;
show_func = * ptr ;
} else if ( * ( ptr + 1 ) = = ' M ' | | * ( ptr + 1 ) = = ' m ' ) {
print_mac_arg ( s , * ( ptr + 1 ) , data , size , event , arg ) ;
ptr + + ;
2012-01-10 00:58:09 +04:00
arg = arg - > next ;
2012-04-06 02:47:53 +04:00
break ;
2014-12-19 05:11:11 +03:00
} else if ( * ( ptr + 1 ) = = ' I ' | | * ( ptr + 1 ) = = ' i ' ) {
int n ;
n = print_ip_arg ( s , ptr + 1 , data , size , event , arg ) ;
if ( n > 0 ) {
ptr + = n ;
arg = arg - > next ;
break ;
}
2012-04-06 02:47:53 +04:00
}
/* fall through */
case ' d ' :
case ' i ' :
case ' x ' :
case ' X ' :
case ' u ' :
2012-09-07 06:49:45 +04:00
if ( ! arg ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " no argument match " ) ;
2012-09-07 06:49:45 +04:00
event - > flags | = EVENT_FL_FAILED ;
goto out_failed ;
}
2012-04-06 02:47:53 +04:00
len = ( ( unsigned long ) ptr + 1 ) -
( unsigned long ) saveptr ;
/* should never happen */
2012-09-07 06:49:45 +04:00
if ( len > 31 ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " bad format! " ) ;
2012-09-07 06:49:45 +04:00
event - > flags | = EVENT_FL_FAILED ;
len = 31 ;
}
2012-04-06 02:47:53 +04:00
memcpy ( format , saveptr , len ) ;
format [ len ] = 0 ;
val = eval_num_arg ( data , size , event , arg ) ;
arg = arg - > next ;
if ( show_func ) {
func = find_func ( pevent , val ) ;
if ( func ) {
trace_seq_puts ( s , func - > func ) ;
if ( show_func = = ' F ' )
trace_seq_printf ( s ,
" +0x%llx " ,
val - func - > addr ) ;
break ;
}
}
2012-03-22 14:18:21 +04:00
if ( pevent - > long_size = = 8 & & ls & &
sizeof ( long ) ! = 8 ) {
2012-04-06 02:47:53 +04:00
char * p ;
ls = 2 ;
/* make %l into %ll */
p = strchr ( format , ' l ' ) ;
if ( p )
2012-03-22 14:18:21 +04:00
memmove ( p + 1 , p , strlen ( p ) + 1 ) ;
2012-04-06 02:47:53 +04:00
else if ( strcmp ( format , " %p " ) = = 0 )
strcpy ( format , " 0x%llx " ) ;
}
switch ( ls ) {
case - 2 :
if ( len_as_arg )
trace_seq_printf ( s , format , len_arg , ( char ) val ) ;
else
trace_seq_printf ( s , format , ( char ) val ) ;
break ;
case - 1 :
if ( len_as_arg )
trace_seq_printf ( s , format , len_arg , ( short ) val ) ;
else
trace_seq_printf ( s , format , ( short ) val ) ;
break ;
case 0 :
if ( len_as_arg )
trace_seq_printf ( s , format , len_arg , ( int ) val ) ;
else
trace_seq_printf ( s , format , ( int ) val ) ;
break ;
case 1 :
if ( len_as_arg )
trace_seq_printf ( s , format , len_arg , ( long ) val ) ;
else
trace_seq_printf ( s , format , ( long ) val ) ;
break ;
case 2 :
if ( len_as_arg )
trace_seq_printf ( s , format , len_arg ,
( long long ) val ) ;
else
trace_seq_printf ( s , format , ( long long ) val ) ;
break ;
default :
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " bad count (%d) " , ls ) ;
2012-09-07 06:49:45 +04:00
event - > flags | = EVENT_FL_FAILED ;
2012-04-06 02:47:53 +04:00
}
break ;
case ' s ' :
2012-09-07 06:49:45 +04:00
if ( ! arg ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " no matching argument " ) ;
2012-09-07 06:49:45 +04:00
event - > flags | = EVENT_FL_FAILED ;
goto out_failed ;
}
2012-04-06 02:47:53 +04:00
len = ( ( unsigned long ) ptr + 1 ) -
( unsigned long ) saveptr ;
/* should never happen */
2012-09-07 06:49:45 +04:00
if ( len > 31 ) {
2014-03-19 05:22:53 +04:00
do_warning_event ( event , " bad format! " ) ;
2012-09-07 06:49:45 +04:00
event - > flags | = EVENT_FL_FAILED ;
len = 31 ;
}
2012-04-06 02:47:53 +04:00
memcpy ( format , saveptr , len ) ;
format [ len ] = 0 ;
if ( ! len_as_arg )
len_arg = - 1 ;
2013-11-20 03:29:37 +04:00
/* Use helper trace_seq */
trace_seq_init ( & p ) ;
print_str_arg ( & p , data , size , event ,
2012-04-06 02:47:53 +04:00
format , len_arg , arg ) ;
2013-11-20 03:29:37 +04:00
trace_seq_terminate ( & p ) ;
trace_seq_puts ( s , p . buffer ) ;
2014-04-23 03:23:30 +04:00
trace_seq_destroy ( & p ) ;
2012-04-06 02:47:53 +04:00
arg = arg - > next ;
break ;
default :
trace_seq_printf ( s , " >%c< " , * ptr ) ;
}
} else
trace_seq_putc ( s , * ptr ) ;
}
2012-09-07 06:49:45 +04:00
if ( event - > flags & EVENT_FL_FAILED ) {
out_failed :
trace_seq_printf ( s , " [FAILED TO PARSE] " ) ;
}
2012-04-06 02:47:53 +04:00
if ( args ) {
free_args ( args ) ;
free ( bprint_fmt ) ;
}
}
/**
* pevent_data_lat_fmt - parse the data for the latency format
* @ pevent : a handle to the pevent
* @ s : the trace_seq to write to
2012-04-23 08:58:35 +04:00
* @ record : the record to read from
2012-04-06 02:47:53 +04:00
*
* This parses out the Latency format ( interrupts disabled ,
* need rescheduling , in hard / soft interrupt , preempt count
* and lock depth ) and places it into the trace_seq .
*/
void pevent_data_lat_fmt ( struct pevent * pevent ,
2012-04-06 02:48:06 +04:00
struct trace_seq * s , struct pevent_record * record )
2012-04-06 02:47:53 +04:00
{
static int check_lock_depth = 1 ;
2012-05-22 09:52:40 +04:00
static int check_migrate_disable = 1 ;
2012-04-06 02:47:53 +04:00
static int lock_depth_exists ;
2012-05-22 09:52:40 +04:00
static int migrate_disable_exists ;
2012-04-06 02:47:53 +04:00
unsigned int lat_flags ;
unsigned int pc ;
int lock_depth ;
2012-05-22 09:52:40 +04:00
int migrate_disable ;
2012-04-06 02:47:53 +04:00
int hardirq ;
int softirq ;
void * data = record - > data ;
lat_flags = parse_common_flags ( pevent , data ) ;
pc = parse_common_pc ( pevent , data ) ;
/* lock_depth may not always exist */
if ( lock_depth_exists )
lock_depth = parse_common_lock_depth ( pevent , data ) ;
2012-05-22 09:52:40 +04:00
else if ( check_lock_depth ) {
lock_depth = parse_common_lock_depth ( pevent , data ) ;
if ( lock_depth < 0 )
check_lock_depth = 0 ;
else
lock_depth_exists = 1 ;
}
/* migrate_disable may not always exist */
if ( migrate_disable_exists )
migrate_disable = parse_common_migrate_disable ( pevent , data ) ;
else if ( check_migrate_disable ) {
migrate_disable = parse_common_migrate_disable ( pevent , data ) ;
if ( migrate_disable < 0 )
check_migrate_disable = 0 ;
else
migrate_disable_exists = 1 ;
}
2012-04-06 02:47:53 +04:00
hardirq = lat_flags & TRACE_FLAG_HARDIRQ ;
softirq = lat_flags & TRACE_FLAG_SOFTIRQ ;
trace_seq_printf ( s , " %c%c%c " ,
( lat_flags & TRACE_FLAG_IRQS_OFF ) ? ' d ' :
( lat_flags & TRACE_FLAG_IRQS_NOSUPPORT ) ?
' X ' : ' . ' ,
( lat_flags & TRACE_FLAG_NEED_RESCHED ) ?
' N ' : ' . ' ,
( hardirq & & softirq ) ? ' H ' :
hardirq ? ' h ' : softirq ? ' s ' : ' . ' ) ;
if ( pc )
trace_seq_printf ( s , " %x " , pc ) ;
else
trace_seq_putc ( s , ' . ' ) ;
2012-05-22 09:52:40 +04:00
if ( migrate_disable_exists ) {
if ( migrate_disable < 0 )
trace_seq_putc ( s , ' . ' ) ;
else
trace_seq_printf ( s , " %d " , migrate_disable ) ;
}
2012-04-06 02:47:53 +04:00
if ( lock_depth_exists ) {
if ( lock_depth < 0 )
trace_seq_putc ( s , ' . ' ) ;
else
trace_seq_printf ( s , " %d " , lock_depth ) ;
}
trace_seq_terminate ( s ) ;
}
/**
* pevent_data_type - parse out the given event type
* @ pevent : a handle to the pevent
* @ rec : the record to read from
*
* This returns the event id from the @ rec .
*/
2012-04-06 02:48:06 +04:00
int pevent_data_type ( struct pevent * pevent , struct pevent_record * rec )
2012-04-06 02:47:53 +04:00
{
return trace_parse_common_type ( pevent , rec - > data ) ;
}
/**
* pevent_data_event_from_type - find the event by a given type
* @ pevent : a handle to the pevent
* @ type : the type of the event .
*
* This returns the event form a given @ type ;
*/
struct event_format * pevent_data_event_from_type ( struct pevent * pevent , int type )
{
return pevent_find_event ( pevent , type ) ;
}
/**
* pevent_data_pid - parse the PID from raw data
* @ pevent : a handle to the pevent
* @ rec : the record to parse
*
* This returns the PID from a raw data .
*/
2012-04-06 02:48:06 +04:00
int pevent_data_pid ( struct pevent * pevent , struct pevent_record * rec )
2012-04-06 02:47:53 +04:00
{
return parse_common_pid ( pevent , rec - > data ) ;
}
/**
* pevent_data_comm_from_pid - return the command line from PID
* @ pevent : a handle to the pevent
* @ pid : the PID of the task to search for
*
* This returns a pointer to the command line that has the given
* @ pid .
*/
const char * pevent_data_comm_from_pid ( struct pevent * pevent , int pid )
{
const char * comm ;
comm = find_cmdline ( pevent , pid ) ;
return comm ;
}
2015-03-24 16:57:52 +03:00
static struct cmdline *
pid_from_cmdlist ( struct pevent * pevent , const char * comm , struct cmdline * next )
{
struct cmdline_list * cmdlist = ( struct cmdline_list * ) next ;
if ( cmdlist )
cmdlist = cmdlist - > next ;
else
cmdlist = pevent - > cmdlist ;
while ( cmdlist & & strcmp ( cmdlist - > comm , comm ) ! = 0 )
cmdlist = cmdlist - > next ;
return ( struct cmdline * ) cmdlist ;
}
/**
* pevent_data_pid_from_comm - return the pid from a given comm
* @ pevent : a handle to the pevent
* @ comm : the cmdline to find the pid from
* @ next : the cmdline structure to find the next comm
*
* This returns the cmdline structure that holds a pid for a given
* comm , or NULL if none found . As there may be more than one pid for
* a given comm , the result of this call can be passed back into
* a recurring call in the @ next paramater , and then it will find the
* next pid .
* Also , it does a linear seach , so it may be slow .
*/
struct cmdline * pevent_data_pid_from_comm ( struct pevent * pevent , const char * comm ,
struct cmdline * next )
{
struct cmdline * cmdline ;
/*
* If the cmdlines have not been converted yet , then use
* the list .
*/
if ( ! pevent - > cmdlines )
return pid_from_cmdlist ( pevent , comm , next ) ;
if ( next ) {
/*
* The next pointer could have been still from
* a previous call before cmdlines were created
*/
if ( next < pevent - > cmdlines | |
next > = pevent - > cmdlines + pevent - > cmdline_count )
next = NULL ;
else
cmdline = next + + ;
}
if ( ! next )
cmdline = pevent - > cmdlines ;
while ( cmdline < pevent - > cmdlines + pevent - > cmdline_count ) {
if ( strcmp ( cmdline - > comm , comm ) = = 0 )
return cmdline ;
cmdline + + ;
}
return NULL ;
}
/**
* pevent_cmdline_pid - return the pid associated to a given cmdline
* @ cmdline : The cmdline structure to get the pid from
*
* Returns the pid for a give cmdline . If @ cmdline is NULL , then
* - 1 is returned .
*/
int pevent_cmdline_pid ( struct pevent * pevent , struct cmdline * cmdline )
{
struct cmdline_list * cmdlist = ( struct cmdline_list * ) cmdline ;
if ( ! cmdline )
return - 1 ;
/*
* If cmdlines have not been created yet , or cmdline is
* not part of the array , then treat it as a cmdlist instead .
*/
if ( ! pevent - > cmdlines | |
cmdline < pevent - > cmdlines | |
cmdline > = pevent - > cmdlines + pevent - > cmdline_count )
return cmdlist - > pid ;
return cmdline - > pid ;
}
2012-04-06 02:47:53 +04:00
/**
* pevent_data_comm_from_pid - parse the data into the print format
* @ s : the trace_seq to write to
* @ event : the handle to the event
2012-04-23 08:58:35 +04:00
* @ record : the record to read from
2012-04-06 02:47:53 +04:00
*
* This parses the raw @ data using the given @ event information and
* writes the print format into the trace_seq .
*/
void pevent_event_info ( struct trace_seq * s , struct event_format * event ,
2012-04-06 02:48:06 +04:00
struct pevent_record * record )
2012-04-06 02:47:53 +04:00
{
int print_pretty = 1 ;
2013-11-02 01:53:59 +04:00
if ( event - > pevent - > print_raw | | ( event - > flags & EVENT_FL_PRINTRAW ) )
2012-04-06 02:47:53 +04:00
print_event_fields ( s , record - > data , record - > size , event ) ;
else {
2013-11-02 01:53:59 +04:00
if ( event - > handler & & ! ( event - > flags & EVENT_FL_NOHANDLE ) )
2012-04-06 02:47:53 +04:00
print_pretty = event - > handler ( s , record , event ,
event - > context ) ;
if ( print_pretty )
pretty_print ( s , record - > data , record - > size , event ) ;
}
trace_seq_terminate ( s ) ;
}
2013-11-02 01:53:53 +04:00
static bool is_timestamp_in_us ( char * trace_clock , bool use_trace_clock )
{
if ( ! use_trace_clock )
return true ;
if ( ! strcmp ( trace_clock , " local " ) | | ! strcmp ( trace_clock , " global " )
| | ! strcmp ( trace_clock , " uptime " ) | | ! strcmp ( trace_clock , " perf " ) )
return true ;
/* trace_clock is setting in tsc or counter mode */
return false ;
}
2012-04-06 02:47:53 +04:00
void pevent_print_event ( struct pevent * pevent , struct trace_seq * s ,
2013-11-02 01:53:53 +04:00
struct pevent_record * record , bool use_trace_clock )
2012-04-06 02:47:53 +04:00
{
2012-11-10 00:40:47 +04:00
static const char * spaces = " " ; /* 20 spaces */
2012-04-06 02:47:53 +04:00
struct event_format * event ;
unsigned long secs ;
unsigned long usecs ;
2012-04-06 02:47:57 +04:00
unsigned long nsecs ;
2012-04-06 02:47:53 +04:00
const char * comm ;
void * data = record - > data ;
int type ;
int pid ;
int len ;
2012-04-06 02:47:57 +04:00
int p ;
2013-11-02 01:53:53 +04:00
bool use_usec_format ;
2012-04-06 02:47:53 +04:00
2013-11-02 01:53:53 +04:00
use_usec_format = is_timestamp_in_us ( pevent - > trace_clock ,
use_trace_clock ) ;
if ( use_usec_format ) {
secs = record - > ts / NSECS_PER_SEC ;
nsecs = record - > ts - secs * NSECS_PER_SEC ;
}
2012-04-06 02:47:53 +04:00
if ( record - > size < 0 ) {
do_warning ( " ug! negative record size %d " , record - > size ) ;
return ;
}
type = trace_parse_common_type ( pevent , data ) ;
event = pevent_find_event ( pevent , type ) ;
if ( ! event ) {
do_warning ( " ug! no event found for type %d " , type ) ;
return ;
}
pid = parse_common_pid ( pevent , data ) ;
comm = find_cmdline ( pevent , pid ) ;
if ( pevent - > latency_format ) {
trace_seq_printf ( s , " %8.8s-%-5d %3d " ,
comm , pid , record - > cpu ) ;
pevent_data_lat_fmt ( pevent , s , record ) ;
} else
trace_seq_printf ( s , " %16s-%-5d [%03d] " , comm , pid , record - > cpu ) ;
2013-11-02 01:53:53 +04:00
if ( use_usec_format ) {
if ( pevent - > flags & PEVENT_NSEC_OUTPUT ) {
usecs = nsecs ;
p = 9 ;
} else {
usecs = ( nsecs + 500 ) / NSECS_PER_USEC ;
p = 6 ;
}
2012-04-06 02:47:57 +04:00
2013-11-02 01:53:53 +04:00
trace_seq_printf ( s , " %5lu.%0*lu: %s: " ,
secs , p , usecs , event - > name ) ;
} else
trace_seq_printf ( s , " %12llu: %s: " ,
record - > ts , event - > name ) ;
2012-04-06 02:47:53 +04:00
/* Space out the event names evenly. */
len = strlen ( event - > name ) ;
if ( len < 20 )
trace_seq_printf ( s , " %.*s " , 20 - len , spaces ) ;
pevent_event_info ( s , event , record ) ;
}
static int events_id_cmp ( const void * a , const void * b )
{
struct event_format * const * ea = a ;
struct event_format * const * eb = b ;
if ( ( * ea ) - > id < ( * eb ) - > id )
return - 1 ;
if ( ( * ea ) - > id > ( * eb ) - > id )
return 1 ;
return 0 ;
}
static int events_name_cmp ( const void * a , const void * b )
{
struct event_format * const * ea = a ;
struct event_format * const * eb = b ;
int res ;
res = strcmp ( ( * ea ) - > name , ( * eb ) - > name ) ;
if ( res )
return res ;
res = strcmp ( ( * ea ) - > system , ( * eb ) - > system ) ;
if ( res )
return res ;
return events_id_cmp ( a , b ) ;
}
static int events_system_cmp ( const void * a , const void * b )
{
struct event_format * const * ea = a ;
struct event_format * const * eb = b ;
int res ;
res = strcmp ( ( * ea ) - > system , ( * eb ) - > system ) ;
if ( res )
return res ;
res = strcmp ( ( * ea ) - > name , ( * eb ) - > name ) ;
if ( res )
return res ;
return events_id_cmp ( a , b ) ;
}
struct event_format * * pevent_list_events ( struct pevent * pevent , enum event_sort_type sort_type )
{
struct event_format * * events ;
int ( * sort ) ( const void * a , const void * b ) ;
events = pevent - > sort_events ;
if ( events & & pevent - > last_type = = sort_type )
return events ;
if ( ! events ) {
events = malloc ( sizeof ( * events ) * ( pevent - > nr_events + 1 ) ) ;
if ( ! events )
return NULL ;
memcpy ( events , pevent - > events , sizeof ( * events ) * pevent - > nr_events ) ;
events [ pevent - > nr_events ] = NULL ;
pevent - > sort_events = events ;
/* the internal events are sorted by id */
if ( sort_type = = EVENT_SORT_ID ) {
pevent - > last_type = sort_type ;
return events ;
}
}
switch ( sort_type ) {
case EVENT_SORT_ID :
sort = events_id_cmp ;
break ;
case EVENT_SORT_NAME :
sort = events_name_cmp ;
break ;
case EVENT_SORT_SYSTEM :
sort = events_system_cmp ;
break ;
default :
return events ;
}
qsort ( events , pevent - > nr_events , sizeof ( * events ) , sort ) ;
pevent - > last_type = sort_type ;
return events ;
}
static struct format_field * *
get_event_fields ( const char * type , const char * name ,
int count , struct format_field * list )
{
struct format_field * * fields ;
struct format_field * field ;
int i = 0 ;
2012-09-13 00:30:50 +04:00
fields = malloc ( sizeof ( * fields ) * ( count + 1 ) ) ;
if ( ! fields )
return NULL ;
2012-04-06 02:47:53 +04:00
for ( field = list ; field ; field = field - > next ) {
fields [ i + + ] = field ;
if ( i = = count + 1 ) {
do_warning ( " event %s has more %s fields than specified " ,
name , type ) ;
i - - ;
break ;
}
}
if ( i ! = count )
do_warning ( " event %s has less %s fields than specified " ,
name , type ) ;
fields [ i ] = NULL ;
return fields ;
}
/**
* pevent_event_common_fields - return a list of common fields for an event
* @ event : the event to return the common fields of .
*
* Returns an allocated array of fields . The last item in the array is NULL .
* The array must be freed with free ( ) .
*/
struct format_field * * pevent_event_common_fields ( struct event_format * event )
{
return get_event_fields ( " common " , event - > name ,
event - > format . nr_common ,
event - > format . common_fields ) ;
}
/**
* pevent_event_fields - return a list of event specific fields for an event
* @ event : the event to return the fields of .
*
* Returns an allocated array of fields . The last item in the array is NULL .
* The array must be freed with free ( ) .
*/
struct format_field * * pevent_event_fields ( struct event_format * event )
{
return get_event_fields ( " event " , event - > name ,
event - > format . nr_fields ,
event - > format . fields ) ;
}
static void print_fields ( struct trace_seq * s , struct print_flag_sym * field )
{
trace_seq_printf ( s , " { %s, %s } " , field - > value , field - > str ) ;
if ( field - > next ) {
trace_seq_puts ( s , " , " ) ;
print_fields ( s , field - > next ) ;
}
}
/* for debugging */
static void print_args ( struct print_arg * args )
{
int print_paren = 1 ;
struct trace_seq s ;
switch ( args - > type ) {
case PRINT_NULL :
printf ( " null " ) ;
break ;
case PRINT_ATOM :
printf ( " %s " , args - > atom . atom ) ;
break ;
case PRINT_FIELD :
printf ( " REC->%s " , args - > field . name ) ;
break ;
case PRINT_FLAGS :
printf ( " __print_flags( " ) ;
print_args ( args - > flags . field ) ;
printf ( " , %s, " , args - > flags . delim ) ;
trace_seq_init ( & s ) ;
print_fields ( & s , args - > flags . flags ) ;
trace_seq_do_printf ( & s ) ;
trace_seq_destroy ( & s ) ;
printf ( " ) " ) ;
break ;
case PRINT_SYMBOL :
printf ( " __print_symbolic( " ) ;
print_args ( args - > symbol . field ) ;
printf ( " , " ) ;
trace_seq_init ( & s ) ;
print_fields ( & s , args - > symbol . symbols ) ;
trace_seq_do_printf ( & s ) ;
trace_seq_destroy ( & s ) ;
printf ( " ) " ) ;
break ;
2012-06-27 04:41:41 +04:00
case PRINT_HEX :
printf ( " __print_hex( " ) ;
print_args ( args - > hex . field ) ;
printf ( " , " ) ;
print_args ( args - > hex . size ) ;
printf ( " ) " ) ;
break ;
2015-03-24 14:07:19 +03:00
case PRINT_INT_ARRAY :
printf ( " __print_array( " ) ;
print_args ( args - > int_array . field ) ;
printf ( " , " ) ;
print_args ( args - > int_array . count ) ;
printf ( " , " ) ;
print_args ( args - > int_array . el_size ) ;
printf ( " ) " ) ;
break ;
2012-04-06 02:47:53 +04:00
case PRINT_STRING :
case PRINT_BSTRING :
printf ( " __get_str(%s) " , args - > string . string ) ;
break ;
2014-06-03 07:20:16 +04:00
case PRINT_BITMASK :
printf ( " __get_bitmask(%s) " , args - > bitmask . bitmask ) ;
break ;
2012-04-06 02:47:53 +04:00
case PRINT_TYPE :
printf ( " (%s) " , args - > typecast . type ) ;
print_args ( args - > typecast . item ) ;
break ;
case PRINT_OP :
if ( strcmp ( args - > op . op , " : " ) = = 0 )
print_paren = 0 ;
if ( print_paren )
printf ( " ( " ) ;
print_args ( args - > op . left ) ;
printf ( " %s " , args - > op . op ) ;
print_args ( args - > op . right ) ;
if ( print_paren )
printf ( " ) " ) ;
break ;
default :
/* we should warn... */
return ;
}
if ( args - > next ) {
printf ( " \n " ) ;
print_args ( args - > next ) ;
}
}
static void parse_header_field ( const char * field ,
int * offset , int * size , int mandatory )
{
unsigned long long save_input_buf_ptr ;
unsigned long long save_input_buf_siz ;
char * token ;
int type ;
save_input_buf_ptr = input_buf_ptr ;
save_input_buf_siz = input_buf_siz ;
if ( read_expected ( EVENT_ITEM , " field " ) < 0 )
return ;
if ( read_expected ( EVENT_OP , " : " ) < 0 )
return ;
/* type */
if ( read_expect_type ( EVENT_ITEM , & token ) < 0 )
goto fail ;
free_token ( token ) ;
/*
* If this is not a mandatory field , then test it first .
*/
if ( mandatory ) {
if ( read_expected ( EVENT_ITEM , field ) < 0 )
return ;
} else {
if ( read_expect_type ( EVENT_ITEM , & token ) < 0 )
goto fail ;
if ( strcmp ( token , field ) ! = 0 )
goto discard ;
free_token ( token ) ;
}
if ( read_expected ( EVENT_OP , " ; " ) < 0 )
return ;
if ( read_expected ( EVENT_ITEM , " offset " ) < 0 )
return ;
if ( read_expected ( EVENT_OP , " : " ) < 0 )
return ;
if ( read_expect_type ( EVENT_ITEM , & token ) < 0 )
goto fail ;
* offset = atoi ( token ) ;
free_token ( token ) ;
if ( read_expected ( EVENT_OP , " ; " ) < 0 )
return ;
if ( read_expected ( EVENT_ITEM , " size " ) < 0 )
return ;
if ( read_expected ( EVENT_OP , " : " ) < 0 )
return ;
if ( read_expect_type ( EVENT_ITEM , & token ) < 0 )
goto fail ;
* size = atoi ( token ) ;
free_token ( token ) ;
if ( read_expected ( EVENT_OP , " ; " ) < 0 )
return ;
type = read_token ( & token ) ;
if ( type ! = EVENT_NEWLINE ) {
/* newer versions of the kernel have a "signed" type */
if ( type ! = EVENT_ITEM )
goto fail ;
if ( strcmp ( token , " signed " ) ! = 0 )
goto fail ;
free_token ( token ) ;
if ( read_expected ( EVENT_OP , " : " ) < 0 )
return ;
if ( read_expect_type ( EVENT_ITEM , & token ) )
goto fail ;
free_token ( token ) ;
if ( read_expected ( EVENT_OP , " ; " ) < 0 )
return ;
if ( read_expect_type ( EVENT_NEWLINE , & token ) )
goto fail ;
}
fail :
free_token ( token ) ;
return ;
discard :
input_buf_ptr = save_input_buf_ptr ;
input_buf_siz = save_input_buf_siz ;
* offset = 0 ;
* size = 0 ;
free_token ( token ) ;
}
/**
* pevent_parse_header_page - parse the data stored in the header page
* @ pevent : the handle to the pevent
* @ buf : the buffer storing the header page format string
* @ size : the size of @ buf
* @ long_size : the long size to use if there is no header
*
* This parses the header page format for information on the
* ring buffer used . The @ buf should be copied from
*
* / sys / kernel / debug / tracing / events / header_page
*/
int pevent_parse_header_page ( struct pevent * pevent , char * buf , unsigned long size ,
int long_size )
{
int ignore ;
if ( ! size ) {
/*
* Old kernels did not have header page info .
* Sorry but we just use what we find here in user space .
*/
pevent - > header_page_ts_size = sizeof ( long long ) ;
pevent - > header_page_size_size = long_size ;
pevent - > header_page_data_offset = sizeof ( long long ) + long_size ;
pevent - > old_format = 1 ;
return - 1 ;
}
init_input_buf ( buf , size ) ;
parse_header_field ( " timestamp " , & pevent - > header_page_ts_offset ,
& pevent - > header_page_ts_size , 1 ) ;
parse_header_field ( " commit " , & pevent - > header_page_size_offset ,
& pevent - > header_page_size_size , 1 ) ;
parse_header_field ( " overwrite " , & pevent - > header_page_overwrite ,
& ignore , 0 ) ;
parse_header_field ( " data " , & pevent - > header_page_data_offset ,
& pevent - > header_page_data_size , 1 ) ;
return 0 ;
}
static int event_matches ( struct event_format * event ,
int id , const char * sys_name ,
const char * event_name )
{
if ( id > = 0 & & id ! = event - > id )
return 0 ;
if ( event_name & & ( strcmp ( event_name , event - > name ) ! = 0 ) )
return 0 ;
if ( sys_name & & ( strcmp ( sys_name , event - > system ) ! = 0 ) )
return 0 ;
return 1 ;
}
static void free_handler ( struct event_handler * handle )
{
free ( ( void * ) handle - > sys_name ) ;
free ( ( void * ) handle - > event_name ) ;
free ( handle ) ;
}
static int find_event_handle ( struct pevent * pevent , struct event_format * event )
{
struct event_handler * handle , * * next ;
for ( next = & pevent - > handlers ; * next ;
next = & ( * next ) - > next ) {
handle = * next ;
if ( event_matches ( event , handle - > id ,
handle - > sys_name ,
handle - > event_name ) )
break ;
}
if ( ! ( * next ) )
return 0 ;
pr_stat ( " overriding event (%d) %s:%s with new print handler " ,
event - > id , event - > system , event - > name ) ;
event - > handler = handle - > func ;
event - > context = handle - > context ;
* next = handle - > next ;
free_handler ( handle ) ;
return 1 ;
}
/**
2012-09-18 18:13:15 +04:00
* __pevent_parse_format - parse the event format
2012-04-06 02:47:53 +04:00
* @ buf : the buffer storing the event format string
* @ size : the size of @ buf
* @ sys : the system the event belongs to
*
* This parses the event format and creates an event structure
* to quickly parse raw data for a given event .
*
* These files currently come from :
*
* / sys / kernel / debug / tracing / events / . . . / . . . / format
*/
2012-09-18 18:13:15 +04:00
enum pevent_errno __pevent_parse_format ( struct event_format * * eventp ,
struct pevent * pevent , const char * buf ,
unsigned long size , const char * sys )
2012-04-06 02:47:53 +04:00
{
struct event_format * event ;
int ret ;
init_input_buf ( buf , size ) ;
2012-09-18 18:13:15 +04:00
* eventp = event = alloc_event ( ) ;
2012-04-06 02:47:53 +04:00
if ( ! event )
2012-08-22 11:00:29 +04:00
return PEVENT_ERRNO__MEM_ALLOC_FAILED ;
2012-04-06 02:47:53 +04:00
event - > name = event_read_name ( ) ;
if ( ! event - > name ) {
/* Bad event? */
2012-08-22 11:00:29 +04:00
ret = PEVENT_ERRNO__MEM_ALLOC_FAILED ;
goto event_alloc_failed ;
2012-04-06 02:47:53 +04:00
}
if ( strcmp ( sys , " ftrace " ) = = 0 ) {
event - > flags | = EVENT_FL_ISFTRACE ;
if ( strcmp ( event - > name , " bprint " ) = = 0 )
event - > flags | = EVENT_FL_ISBPRINT ;
}
event - > id = event_read_id ( ) ;
2012-08-22 11:00:29 +04:00
if ( event - > id < 0 ) {
ret = PEVENT_ERRNO__READ_ID_FAILED ;
/*
* This isn ' t an allocation error actually .
* But as the ID is critical , just bail out .
*/
goto event_alloc_failed ;
}
2012-04-06 02:47:53 +04:00
event - > system = strdup ( sys ) ;
2012-08-22 11:00:29 +04:00
if ( ! event - > system ) {
ret = PEVENT_ERRNO__MEM_ALLOC_FAILED ;
goto event_alloc_failed ;
}
2012-04-06 02:47:53 +04:00
2012-10-02 04:13:51 +04:00
/* Add pevent to event so that it can be referenced */
event - > pevent = pevent ;
2012-04-06 02:47:53 +04:00
ret = event_read_format ( event ) ;
if ( ret < 0 ) {
2012-08-22 11:00:29 +04:00
ret = PEVENT_ERRNO__READ_FORMAT_FAILED ;
goto event_parse_failed ;
2012-04-06 02:47:53 +04:00
}
/*
* If the event has an override , don ' t print warnings if the event
* print format fails to parse .
*/
2012-09-18 18:13:15 +04:00
if ( pevent & & find_event_handle ( pevent , event ) )
2012-04-06 02:47:53 +04:00
show_warning = 0 ;
ret = event_read_print ( event ) ;
2012-09-18 18:13:15 +04:00
show_warning = 1 ;
2012-04-06 02:47:53 +04:00
if ( ret < 0 ) {
2012-08-22 11:00:29 +04:00
ret = PEVENT_ERRNO__READ_PRINT_FAILED ;
goto event_parse_failed ;
2012-04-06 02:47:53 +04:00
}
if ( ! ret & & ( event - > flags & EVENT_FL_ISFTRACE ) ) {
struct format_field * field ;
struct print_arg * arg , * * list ;
/* old ftrace had no args */
list = & event - > print_fmt . args ;
for ( field = event - > format . fields ; field ; field = field - > next ) {
arg = alloc_arg ( ) ;
2012-09-20 06:09:19 +04:00
if ( ! arg ) {
event - > flags | = EVENT_FL_FAILED ;
return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED ;
}
2012-04-06 02:47:53 +04:00
arg - > type = PRINT_FIELD ;
arg - > field . name = strdup ( field - > name ) ;
2012-04-09 06:54:31 +04:00
if ( ! arg - > field . name ) {
2012-04-23 08:58:34 +04:00
event - > flags | = EVENT_FL_FAILED ;
2012-08-22 11:00:28 +04:00
free_arg ( arg ) ;
2012-08-22 11:00:29 +04:00
return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED ;
2012-04-09 06:54:31 +04:00
}
2012-04-06 02:47:53 +04:00
arg - > field . field = field ;
2012-08-22 11:00:28 +04:00
* list = arg ;
list = & arg - > next ;
2012-04-06 02:47:53 +04:00
}
return 0 ;
}
return 0 ;
2012-08-22 11:00:29 +04:00
event_parse_failed :
2012-04-06 02:47:53 +04:00
event - > flags | = EVENT_FL_FAILED ;
2012-08-22 11:00:29 +04:00
return ret ;
event_alloc_failed :
2012-09-18 18:13:15 +04:00
free ( event - > system ) ;
free ( event - > name ) ;
free ( event ) ;
* eventp = NULL ;
return ret ;
}
2013-12-03 17:09:19 +04:00
static enum pevent_errno
__pevent_parse_event ( struct pevent * pevent ,
struct event_format * * eventp ,
const char * buf , unsigned long size ,
const char * sys )
{
int ret = __pevent_parse_format ( eventp , pevent , buf , size , sys ) ;
struct event_format * event = * eventp ;
if ( event = = NULL )
return ret ;
if ( pevent & & add_event ( pevent , event ) ) {
ret = PEVENT_ERRNO__MEM_ALLOC_FAILED ;
goto event_add_failed ;
}
# define PRINT_ARGS 0
if ( PRINT_ARGS & & event - > print_fmt . args )
print_args ( event - > print_fmt . args ) ;
return 0 ;
event_add_failed :
pevent_free_format ( event ) ;
return ret ;
}
2012-09-18 18:13:15 +04:00
/**
* pevent_parse_format - parse the event format
2013-12-03 17:09:19 +04:00
* @ pevent : the handle to the pevent
* @ eventp : returned format
2012-09-18 18:13:15 +04:00
* @ buf : the buffer storing the event format string
* @ size : the size of @ buf
* @ sys : the system the event belongs to
*
* This parses the event format and creates an event structure
* to quickly parse raw data for a given event .
*
* These files currently come from :
*
* / sys / kernel / debug / tracing / events / . . . / . . . / format
*/
2013-12-03 17:09:19 +04:00
enum pevent_errno pevent_parse_format ( struct pevent * pevent ,
struct event_format * * eventp ,
const char * buf ,
2012-09-18 18:13:15 +04:00
unsigned long size , const char * sys )
{
2013-12-03 17:09:19 +04:00
return __pevent_parse_event ( pevent , eventp , buf , size , sys ) ;
2012-09-18 18:13:15 +04:00
}
/**
* pevent_parse_event - parse the event format
* @ pevent : the handle to the pevent
* @ buf : the buffer storing the event format string
* @ size : the size of @ buf
* @ sys : the system the event belongs to
*
* This parses the event format and creates an event structure
* to quickly parse raw data for a given event .
*
* These files currently come from :
*
* / sys / kernel / debug / tracing / events / . . . / . . . / format
*/
enum pevent_errno pevent_parse_event ( struct pevent * pevent , const char * buf ,
unsigned long size , const char * sys )
{
struct event_format * event = NULL ;
2013-12-03 17:09:19 +04:00
return __pevent_parse_event ( pevent , & event , buf , size , sys ) ;
2012-04-06 02:47:53 +04:00
}
2012-08-22 11:00:30 +04:00
# undef _PE
# define _PE(code, str) str
static const char * const pevent_error_str [ ] = {
PEVENT_ERRORS
} ;
# undef _PE
2012-11-09 22:18:57 +04:00
int pevent_strerror ( struct pevent * pevent __maybe_unused ,
enum pevent_errno errnum , char * buf , size_t buflen )
2012-08-22 11:00:30 +04:00
{
int idx ;
const char * msg ;
if ( errnum > = 0 ) {
2012-08-22 11:00:31 +04:00
msg = strerror_r ( errnum , buf , buflen ) ;
if ( msg ! = buf ) {
size_t len = strlen ( msg ) ;
2012-09-08 04:43:22 +04:00
memcpy ( buf , msg , min ( buflen - 1 , len ) ) ;
* ( buf + min ( buflen - 1 , len ) ) = ' \0 ' ;
2012-08-22 11:00:31 +04:00
}
2012-08-22 11:00:30 +04:00
return 0 ;
}
if ( errnum < = __PEVENT_ERRNO__START | |
errnum > = __PEVENT_ERRNO__END )
return - 1 ;
2012-08-23 11:37:00 +04:00
idx = errnum - __PEVENT_ERRNO__START - 1 ;
2012-08-22 11:00:30 +04:00
msg = pevent_error_str [ idx ] ;
2013-12-12 11:36:17 +04:00
snprintf ( buf , buflen , " %s " , msg ) ;
2012-08-22 11:00:30 +04:00
return 0 ;
}
2012-04-06 02:47:53 +04:00
int get_field_val ( struct trace_seq * s , struct format_field * field ,
2012-04-06 02:48:06 +04:00
const char * name , struct pevent_record * record ,
2012-04-06 02:47:53 +04:00
unsigned long long * val , int err )
{
if ( ! field ) {
if ( err )
trace_seq_printf ( s , " <CANT FIND FIELD %s> " , name ) ;
return - 1 ;
}
if ( pevent_read_number_field ( field , record - > data , val ) ) {
if ( err )
trace_seq_printf ( s , " %s=INVALID " , name ) ;
return - 1 ;
}
return 0 ;
}
/**
* pevent_get_field_raw - return the raw pointer into the data field
* @ s : The seq to print to on error
* @ event : the event that the field is for
* @ name : The name of the field
* @ record : The record with the field name .
* @ len : place to store the field length .
* @ err : print default error if failed .
*
* Returns a pointer into record - > data of the field and places
* the length of the field in @ len .
*
* On failure , it returns NULL .
*/
void * pevent_get_field_raw ( struct trace_seq * s , struct event_format * event ,
2012-04-06 02:48:06 +04:00
const char * name , struct pevent_record * record ,
2012-04-06 02:47:53 +04:00
int * len , int err )
{
struct format_field * field ;
void * data = record - > data ;
unsigned offset ;
int dummy ;
if ( ! event )
return NULL ;
field = pevent_find_field ( event , name ) ;
if ( ! field ) {
if ( err )
trace_seq_printf ( s , " <CANT FIND FIELD %s> " , name ) ;
return NULL ;
}
/* Allow @len to be NULL */
if ( ! len )
len = & dummy ;
offset = field - > offset ;
if ( field - > flags & FIELD_IS_DYNAMIC ) {
offset = pevent_read_number ( event - > pevent ,
data + offset , field - > size ) ;
* len = offset > > 16 ;
offset & = 0xffff ;
} else
* len = field - > size ;
return data + offset ;
}
/**
* pevent_get_field_val - find a field and return its value
* @ s : The seq to print to on error
* @ event : the event that the field is for
* @ name : The name of the field
* @ record : The record with the field name .
* @ val : place to store the value of the field .
* @ err : print default error if failed .
*
* Returns 0 on success - 1 on field not found .
*/
int pevent_get_field_val ( struct trace_seq * s , struct event_format * event ,
2012-04-06 02:48:06 +04:00
const char * name , struct pevent_record * record ,
2012-04-06 02:47:53 +04:00
unsigned long long * val , int err )
{
struct format_field * field ;
if ( ! event )
return - 1 ;
field = pevent_find_field ( event , name ) ;
return get_field_val ( s , field , name , record , val , err ) ;
}
/**
* pevent_get_common_field_val - find a common field and return its value
* @ s : The seq to print to on error
* @ event : the event that the field is for
* @ name : The name of the field
* @ record : The record with the field name .
* @ val : place to store the value of the field .
* @ err : print default error if failed .
*
* Returns 0 on success - 1 on field not found .
*/
int pevent_get_common_field_val ( struct trace_seq * s , struct event_format * event ,
2012-04-06 02:48:06 +04:00
const char * name , struct pevent_record * record ,
2012-04-06 02:47:53 +04:00
unsigned long long * val , int err )
{
struct format_field * field ;
if ( ! event )
return - 1 ;
field = pevent_find_common_field ( event , name ) ;
return get_field_val ( s , field , name , record , val , err ) ;
}
/**
* pevent_get_any_field_val - find a any field and return its value
* @ s : The seq to print to on error
* @ event : the event that the field is for
* @ name : The name of the field
* @ record : The record with the field name .
* @ val : place to store the value of the field .
* @ err : print default error if failed .
*
* Returns 0 on success - 1 on field not found .
*/
int pevent_get_any_field_val ( struct trace_seq * s , struct event_format * event ,
2012-04-06 02:48:06 +04:00
const char * name , struct pevent_record * record ,
2012-04-06 02:47:53 +04:00
unsigned long long * val , int err )
{
struct format_field * field ;
if ( ! event )
return - 1 ;
field = pevent_find_any_field ( event , name ) ;
return get_field_val ( s , field , name , record , val , err ) ;
}
/**
* pevent_print_num_field - print a field and a format
* @ s : The seq to print to
* @ fmt : The printf format to print the field with .
* @ event : the event that the field is for
* @ name : The name of the field
* @ record : The record with the field name .
* @ err : print default error if failed .
*
2012-04-23 08:58:35 +04:00
* Returns : 0 on success , - 1 field not found , or 1 if buffer is full .
2012-04-06 02:47:53 +04:00
*/
int pevent_print_num_field ( struct trace_seq * s , const char * fmt ,
struct event_format * event , const char * name ,
2012-04-06 02:48:06 +04:00
struct pevent_record * record , int err )
2012-04-06 02:47:53 +04:00
{
struct format_field * field = pevent_find_field ( event , name ) ;
unsigned long long val ;
if ( ! field )
goto failed ;
if ( pevent_read_number_field ( field , record - > data , & val ) )
goto failed ;
return trace_seq_printf ( s , fmt , val ) ;
failed :
if ( err )
trace_seq_printf ( s , " CAN'T FIND FIELD \" %s \" " , name ) ;
return - 1 ;
}
2013-11-02 01:54:00 +04:00
/**
* pevent_print_func_field - print a field and a format for function pointers
* @ s : The seq to print to
* @ fmt : The printf format to print the field with .
* @ event : the event that the field is for
* @ name : The name of the field
* @ record : The record with the field name .
* @ err : print default error if failed .
*
* Returns : 0 on success , - 1 field not found , or 1 if buffer is full .
*/
int pevent_print_func_field ( struct trace_seq * s , const char * fmt ,
struct event_format * event , const char * name ,
struct pevent_record * record , int err )
{
struct format_field * field = pevent_find_field ( event , name ) ;
struct pevent * pevent = event - > pevent ;
unsigned long long val ;
struct func_map * func ;
char tmp [ 128 ] ;
if ( ! field )
goto failed ;
if ( pevent_read_number_field ( field , record - > data , & val ) )
goto failed ;
func = find_func ( pevent , val ) ;
if ( func )
snprintf ( tmp , 128 , " %s/0x%llx " , func - > func , func - > addr - val ) ;
else
sprintf ( tmp , " 0x%08llx " , val ) ;
return trace_seq_printf ( s , fmt , tmp ) ;
failed :
if ( err )
trace_seq_printf ( s , " CAN'T FIND FIELD \" %s \" " , name ) ;
return - 1 ;
}
2012-04-06 02:47:53 +04:00
static void free_func_handle ( struct pevent_function_handler * func )
{
struct pevent_func_params * params ;
free ( func - > name ) ;
while ( func - > params ) {
params = func - > params ;
func - > params = params - > next ;
free ( params ) ;
}
free ( func ) ;
}
/**
* pevent_register_print_function - register a helper function
* @ pevent : the handle to the pevent
* @ func : the function to process the helper function
2012-04-23 08:58:35 +04:00
* @ ret_type : the return type of the helper function
2012-04-06 02:47:53 +04:00
* @ name : the name of the helper function
* @ parameters : A list of enum pevent_func_arg_type
*
* Some events may have helper functions in the print format arguments .
2012-04-23 08:58:35 +04:00
* This allows a plugin to dynamically create a way to process one
2012-04-06 02:47:53 +04:00
* of these functions .
*
* The @ parameters is a variable list of pevent_func_arg_type enums that
* must end with PEVENT_FUNC_ARG_VOID .
*/
int pevent_register_print_function ( struct pevent * pevent ,
pevent_func_handler func ,
enum pevent_func_arg_type ret_type ,
char * name , . . . )
{
struct pevent_function_handler * func_handle ;
struct pevent_func_params * * next_param ;
struct pevent_func_params * param ;
enum pevent_func_arg_type type ;
va_list ap ;
2012-09-07 06:49:47 +04:00
int ret ;
2012-04-06 02:47:53 +04:00
func_handle = find_func_handler ( pevent , name ) ;
if ( func_handle ) {
/*
* This is most like caused by the users own
* plugins updating the function . This overrides the
* system defaults .
*/
pr_stat ( " override of function helper '%s' " , name ) ;
remove_func_handler ( pevent , name ) ;
}
2012-09-12 22:39:59 +04:00
func_handle = calloc ( 1 , sizeof ( * func_handle ) ) ;
2012-09-07 06:49:47 +04:00
if ( ! func_handle ) {
do_warning ( " Failed to allocate function handler " ) ;
return PEVENT_ERRNO__MEM_ALLOC_FAILED ;
}
2012-04-06 02:47:53 +04:00
func_handle - > ret_type = ret_type ;
func_handle - > name = strdup ( name ) ;
func_handle - > func = func ;
2012-09-07 06:49:47 +04:00
if ( ! func_handle - > name ) {
do_warning ( " Failed to allocate function name " ) ;
free ( func_handle ) ;
return PEVENT_ERRNO__MEM_ALLOC_FAILED ;
}
2012-04-06 02:47:53 +04:00
next_param = & ( func_handle - > params ) ;
va_start ( ap , name ) ;
for ( ; ; ) {
type = va_arg ( ap , enum pevent_func_arg_type ) ;
if ( type = = PEVENT_FUNC_ARG_VOID )
break ;
2012-11-09 22:42:26 +04:00
if ( type > = PEVENT_FUNC_ARG_MAX_TYPES ) {
2012-09-07 06:49:47 +04:00
do_warning ( " Invalid argument type %d " , type ) ;
ret = PEVENT_ERRNO__INVALID_ARG_TYPE ;
2012-04-06 02:47:53 +04:00
goto out_free ;
}
2012-09-07 06:49:47 +04:00
param = malloc ( sizeof ( * param ) ) ;
if ( ! param ) {
do_warning ( " Failed to allocate function param " ) ;
ret = PEVENT_ERRNO__MEM_ALLOC_FAILED ;
goto out_free ;
}
2012-04-06 02:47:53 +04:00
param - > type = type ;
param - > next = NULL ;
* next_param = param ;
next_param = & ( param - > next ) ;
func_handle - > nr_args + + ;
}
va_end ( ap ) ;
func_handle - > next = pevent - > func_handlers ;
pevent - > func_handlers = func_handle ;
return 0 ;
out_free :
va_end ( ap ) ;
free_func_handle ( func_handle ) ;
2012-09-07 06:49:47 +04:00
return ret ;
2012-04-06 02:47:53 +04:00
}
2014-01-16 06:31:08 +04:00
/**
* pevent_unregister_print_function - unregister a helper function
* @ pevent : the handle to the pevent
* @ func : the function to process the helper function
* @ name : the name of the helper function
*
* This function removes existing print handler for function @ name .
*
* Returns 0 if the handler was removed successully , - 1 otherwise .
*/
int pevent_unregister_print_function ( struct pevent * pevent ,
pevent_func_handler func , char * name )
{
struct pevent_function_handler * func_handle ;
func_handle = find_func_handler ( pevent , name ) ;
if ( func_handle & & func_handle - > func = = func ) {
remove_func_handler ( pevent , name ) ;
return 0 ;
}
return - 1 ;
}
2014-01-16 06:31:07 +04:00
static struct event_format * pevent_search_event ( struct pevent * pevent , int id ,
const char * sys_name ,
const char * event_name )
{
struct event_format * event ;
if ( id > = 0 ) {
/* search by id */
event = pevent_find_event ( pevent , id ) ;
if ( ! event )
return NULL ;
if ( event_name & & ( strcmp ( event_name , event - > name ) ! = 0 ) )
return NULL ;
if ( sys_name & & ( strcmp ( sys_name , event - > system ) ! = 0 ) )
return NULL ;
} else {
event = pevent_find_event_by_name ( pevent , sys_name , event_name ) ;
if ( ! event )
return NULL ;
}
return event ;
}
2012-04-06 02:47:53 +04:00
/**
2012-04-23 08:58:35 +04:00
* pevent_register_event_handler - register a way to parse an event
2012-04-06 02:47:53 +04:00
* @ pevent : the handle to the pevent
* @ id : the id of the event to register
* @ sys_name : the system name the event belongs to
* @ event_name : the name of the event
* @ func : the function to call to parse the event information
2012-04-23 08:58:35 +04:00
* @ context : the data to be passed to @ func
2012-04-06 02:47:53 +04:00
*
* This function allows a developer to override the parsing of
* a given event . If for some reason the default print format
* is not sufficient , this function will register a function
* for an event to be used to parse the data instead .
*
* If @ id is > = 0 , then it is used to find the event .
* else @ sys_name and @ event_name are used .
*/
2013-06-04 09:20:18 +04:00
int pevent_register_event_handler ( struct pevent * pevent , int id ,
const char * sys_name , const char * event_name ,
pevent_event_handler_func func , void * context )
2012-04-06 02:47:53 +04:00
{
struct event_format * event ;
struct event_handler * handle ;
2014-01-16 06:31:07 +04:00
event = pevent_search_event ( pevent , id , sys_name , event_name ) ;
if ( event = = NULL )
goto not_found ;
2012-04-06 02:47:53 +04:00
pr_stat ( " overriding event (%d) %s:%s with new print handler " ,
event - > id , event - > system , event - > name ) ;
event - > handler = func ;
event - > context = context ;
return 0 ;
not_found :
/* Save for later use. */
2012-09-12 22:39:59 +04:00
handle = calloc ( 1 , sizeof ( * handle ) ) ;
2012-09-07 06:49:46 +04:00
if ( ! handle ) {
do_warning ( " Failed to allocate event handler " ) ;
return PEVENT_ERRNO__MEM_ALLOC_FAILED ;
}
2012-04-06 02:47:53 +04:00
handle - > id = id ;
if ( event_name )
handle - > event_name = strdup ( event_name ) ;
if ( sys_name )
handle - > sys_name = strdup ( sys_name ) ;
2012-04-09 06:54:31 +04:00
if ( ( event_name & & ! handle - > event_name ) | |
( sys_name & & ! handle - > sys_name ) ) {
2012-09-07 06:49:46 +04:00
do_warning ( " Failed to allocate event/sys name " ) ;
free ( ( void * ) handle - > event_name ) ;
free ( ( void * ) handle - > sys_name ) ;
free ( handle ) ;
return PEVENT_ERRNO__MEM_ALLOC_FAILED ;
2012-04-09 06:54:31 +04:00
}
2012-04-06 02:47:53 +04:00
handle - > func = func ;
handle - > next = pevent - > handlers ;
pevent - > handlers = handle ;
handle - > context = context ;
return - 1 ;
}
2014-01-16 06:31:07 +04:00
static int handle_matches ( struct event_handler * handler , int id ,
const char * sys_name , const char * event_name ,
pevent_event_handler_func func , void * context )
{
if ( id > = 0 & & id ! = handler - > id )
return 0 ;
if ( event_name & & ( strcmp ( event_name , handler - > event_name ) ! = 0 ) )
return 0 ;
if ( sys_name & & ( strcmp ( sys_name , handler - > sys_name ) ! = 0 ) )
return 0 ;
if ( func ! = handler - > func | | context ! = handler - > context )
return 0 ;
return 1 ;
}
/**
* pevent_unregister_event_handler - unregister an existing event handler
* @ pevent : the handle to the pevent
* @ id : the id of the event to unregister
* @ sys_name : the system name the handler belongs to
* @ event_name : the name of the event handler
* @ func : the function to call to parse the event information
* @ context : the data to be passed to @ func
*
* This function removes existing event handler ( parser ) .
*
* If @ id is > = 0 , then it is used to find the event .
* else @ sys_name and @ event_name are used .
*
* Returns 0 if handler was removed successfully , - 1 if event was not found .
*/
int pevent_unregister_event_handler ( struct pevent * pevent , int id ,
const char * sys_name , const char * event_name ,
pevent_event_handler_func func , void * context )
{
struct event_format * event ;
struct event_handler * handle ;
struct event_handler * * next ;
event = pevent_search_event ( pevent , id , sys_name , event_name ) ;
if ( event = = NULL )
goto not_found ;
if ( event - > handler = = func & & event - > context = = context ) {
pr_stat ( " removing override handler for event (%d) %s:%s. Going back to default handler. " ,
event - > id , event - > system , event - > name ) ;
event - > handler = NULL ;
event - > context = NULL ;
return 0 ;
}
not_found :
for ( next = & pevent - > handlers ; * next ; next = & ( * next ) - > next ) {
handle = * next ;
if ( handle_matches ( handle , id , sys_name , event_name ,
func , context ) )
break ;
}
if ( ! ( * next ) )
return - 1 ;
* next = handle - > next ;
free_handler ( handle ) ;
return 0 ;
}
2012-04-06 02:47:53 +04:00
/**
* pevent_alloc - create a pevent handle
*/
struct pevent * pevent_alloc ( void )
{
2012-09-12 22:39:59 +04:00
struct pevent * pevent = calloc ( 1 , sizeof ( * pevent ) ) ;
2012-04-06 02:47:53 +04:00
2012-09-12 22:39:59 +04:00
if ( pevent )
pevent - > ref_count = 1 ;
2012-04-06 02:47:53 +04:00
return pevent ;
}
void pevent_ref ( struct pevent * pevent )
{
pevent - > ref_count + + ;
}
2015-03-19 21:36:21 +03:00
void pevent_free_format_field ( struct format_field * field )
{
free ( field - > type ) ;
2015-04-18 18:50:18 +03:00
if ( field - > alias ! = field - > name )
free ( field - > alias ) ;
2015-03-19 21:36:21 +03:00
free ( field - > name ) ;
free ( field ) ;
}
2012-04-06 02:47:53 +04:00
static void free_format_fields ( struct format_field * field )
{
struct format_field * next ;
while ( field ) {
next = field - > next ;
2015-03-19 21:36:21 +03:00
pevent_free_format_field ( field ) ;
2012-04-06 02:47:53 +04:00
field = next ;
}
}
static void free_formats ( struct format * format )
{
free_format_fields ( format - > common_fields ) ;
free_format_fields ( format - > fields ) ;
}
2012-09-18 18:13:15 +04:00
void pevent_free_format ( struct event_format * event )
2012-04-06 02:47:53 +04:00
{
free ( event - > name ) ;
free ( event - > system ) ;
free_formats ( & event - > format ) ;
free ( event - > print_fmt . format ) ;
free_args ( event - > print_fmt . args ) ;
free ( event ) ;
}
/**
* pevent_free - free a pevent handle
* @ pevent : the pevent handle to free
*/
void pevent_free ( struct pevent * pevent )
{
2012-04-06 02:48:02 +04:00
struct cmdline_list * cmdlist , * cmdnext ;
struct func_list * funclist , * funcnext ;
struct printk_list * printklist , * printknext ;
2012-04-06 02:47:53 +04:00
struct pevent_function_handler * func_handler ;
struct event_handler * handle ;
int i ;
2012-04-06 02:48:02 +04:00
if ( ! pevent )
return ;
cmdlist = pevent - > cmdlist ;
funclist = pevent - > funclist ;
printklist = pevent - > printklist ;
2012-04-06 02:47:53 +04:00
pevent - > ref_count - - ;
if ( pevent - > ref_count )
return ;
if ( pevent - > cmdlines ) {
for ( i = 0 ; i < pevent - > cmdline_count ; i + + )
free ( pevent - > cmdlines [ i ] . comm ) ;
free ( pevent - > cmdlines ) ;
}
while ( cmdlist ) {
cmdnext = cmdlist - > next ;
free ( cmdlist - > comm ) ;
free ( cmdlist ) ;
cmdlist = cmdnext ;
}
if ( pevent - > func_map ) {
2012-11-09 22:32:00 +04:00
for ( i = 0 ; i < ( int ) pevent - > func_count ; i + + ) {
2012-04-06 02:47:53 +04:00
free ( pevent - > func_map [ i ] . func ) ;
free ( pevent - > func_map [ i ] . mod ) ;
}
free ( pevent - > func_map ) ;
}
while ( funclist ) {
funcnext = funclist - > next ;
free ( funclist - > func ) ;
free ( funclist - > mod ) ;
free ( funclist ) ;
funclist = funcnext ;
}
while ( pevent - > func_handlers ) {
func_handler = pevent - > func_handlers ;
pevent - > func_handlers = func_handler - > next ;
free_func_handle ( func_handler ) ;
}
if ( pevent - > printk_map ) {
2012-11-09 22:32:00 +04:00
for ( i = 0 ; i < ( int ) pevent - > printk_count ; i + + )
2012-04-06 02:47:53 +04:00
free ( pevent - > printk_map [ i ] . printk ) ;
free ( pevent - > printk_map ) ;
}
while ( printklist ) {
printknext = printklist - > next ;
free ( printklist - > printk ) ;
free ( printklist ) ;
printklist = printknext ;
}
for ( i = 0 ; i < pevent - > nr_events ; i + + )
2012-09-18 18:13:15 +04:00
pevent_free_format ( pevent - > events [ i ] ) ;
2012-04-06 02:47:53 +04:00
while ( pevent - > handlers ) {
handle = pevent - > handlers ;
pevent - > handlers = handle - > next ;
free_handler ( handle ) ;
}
2015-03-24 16:57:50 +03:00
free ( pevent - > trace_clock ) ;
2012-04-06 02:47:53 +04:00
free ( pevent - > events ) ;
free ( pevent - > sort_events ) ;
2015-07-22 18:36:55 +03:00
free ( pevent - > func_resolver ) ;
2012-04-06 02:47:53 +04:00
free ( pevent ) ;
}
void pevent_unref ( struct pevent * pevent )
{
pevent_free ( pevent ) ;
}