2009-08-17 16:18:05 +02:00
/*
* Copyright ( C ) 2008 , 2009 , Steven Rostedt < srostedt @ redhat . com >
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 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 General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
# define _GNU_SOURCE
# include <dirent.h>
2009-12-19 16:40:28 -05:00
# include <mntent.h>
2009-08-17 16:18:05 +02:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <stdarg.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/wait.h>
# include <pthread.h>
# include <fcntl.h>
# include <unistd.h>
# include <ctype.h>
# include <errno.h>
2009-08-28 03:09:58 +02:00
# include <stdbool.h>
2011-01-03 16:39:04 -02:00
# include <linux/list.h>
2009-11-21 14:31:26 -02:00
# include <linux/kernel.h>
2009-08-17 16:18:05 +02:00
2009-08-28 03:09:58 +02:00
# include "../perf.h"
2009-08-17 16:18:05 +02:00
# include "trace-event.h"
2009-12-28 16:48:30 +08:00
# include "debugfs.h"
2011-01-03 16:39:04 -02:00
# include "evsel.h"
2009-08-17 16:18:05 +02:00
# define VERSION "0.5"
# define _STR(x) #x
# define STR(x) _STR(x)
# define MAX_PATH 256
# define TRACE_CTRL "tracing_on"
# define TRACE "trace"
# define AVAILABLE "available_tracers"
# define CURRENT "current_tracer"
# define ITER_CTRL "trace_options"
# define MAX_LATENCY "tracing_max_latency"
unsigned int page_size ;
static const char * output_file = " trace.info " ;
static int output_fd ;
struct event_list {
struct event_list * next ;
const char * event ;
} ;
struct events {
struct events * sibling ;
struct events * children ;
struct events * next ;
char * name ;
} ;
static void die ( const char * fmt , . . . )
{
va_list ap ;
int ret = errno ;
if ( errno )
perror ( " trace-cmd " ) ;
else
ret = - 1 ;
va_start ( ap , fmt ) ;
fprintf ( stderr , " " ) ;
vfprintf ( stderr , fmt , ap ) ;
va_end ( ap ) ;
fprintf ( stderr , " \n " ) ;
exit ( ret ) ;
}
void * malloc_or_die ( unsigned int size )
{
void * data ;
data = malloc ( size ) ;
if ( ! data )
die ( " malloc " ) ;
return data ;
}
static const char * find_debugfs ( void )
{
2009-12-28 16:48:30 +08:00
const char * path = debugfs_mount ( NULL ) ;
2009-08-17 16:18:05 +02:00
2009-12-28 16:48:30 +08:00
if ( ! path )
die ( " Your kernel not support debugfs filesystem " ) ;
2009-08-17 16:18:05 +02:00
2009-12-28 16:48:30 +08:00
return path ;
2009-08-17 16:18:05 +02:00
}
/*
* Finds the path to the debugfs / tracing
* Allocates the string and stores it .
*/
static const char * find_tracing_dir ( void )
{
static char * tracing ;
static int tracing_found ;
const char * debugfs ;
if ( tracing_found )
return tracing ;
debugfs = find_debugfs ( ) ;
tracing = malloc_or_die ( strlen ( debugfs ) + 9 ) ;
sprintf ( tracing , " %s/tracing " , debugfs ) ;
tracing_found = 1 ;
return tracing ;
}
static char * get_tracing_file ( const char * name )
{
const char * tracing ;
char * file ;
tracing = find_tracing_dir ( ) ;
if ( ! tracing )
return NULL ;
file = malloc_or_die ( strlen ( tracing ) + strlen ( name ) + 2 ) ;
sprintf ( file , " %s/%s " , tracing , name ) ;
return file ;
}
static void put_tracing_file ( char * file )
{
free ( file ) ;
}
2010-04-01 23:59:21 -05:00
static ssize_t calc_data_size ;
2009-08-17 16:18:05 +02:00
static ssize_t write_or_die ( const void * buf , size_t len )
{
int ret ;
2010-04-01 23:59:21 -05:00
if ( calc_data_size ) {
calc_data_size + = len ;
return len ;
}
2009-08-17 16:18:05 +02:00
ret = write ( output_fd , buf , len ) ;
if ( ret < 0 )
die ( " writing to '%s' " , output_file ) ;
return ret ;
}
int bigendian ( void )
{
unsigned char str [ ] = { 0x1 , 0x2 , 0x3 , 0x4 , 0x0 , 0x0 , 0x0 , 0x0 } ;
unsigned int * ptr ;
2009-09-02 14:55:55 +02:00
ptr = ( unsigned int * ) ( void * ) str ;
2009-08-17 16:18:05 +02:00
return * ptr = = 0x01020304 ;
}
static unsigned long long copy_file_fd ( int fd )
{
unsigned long long size = 0 ;
char buf [ BUFSIZ ] ;
int r ;
do {
r = read ( fd , buf , BUFSIZ ) ;
if ( r > 0 ) {
size + = r ;
write_or_die ( buf , r ) ;
}
} while ( r > 0 ) ;
return size ;
}
static unsigned long long copy_file ( const char * file )
{
unsigned long long size = 0 ;
int fd ;
fd = open ( file , O_RDONLY ) ;
if ( fd < 0 )
die ( " Can't read '%s' " , file ) ;
size = copy_file_fd ( fd ) ;
close ( fd ) ;
return size ;
}
static unsigned long get_size_fd ( int fd )
{
unsigned long long size = 0 ;
char buf [ BUFSIZ ] ;
int r ;
do {
r = read ( fd , buf , BUFSIZ ) ;
if ( r > 0 )
size + = r ;
} while ( r > 0 ) ;
lseek ( fd , 0 , SEEK_SET ) ;
return size ;
}
static unsigned long get_size ( const char * file )
{
unsigned long long size = 0 ;
int fd ;
fd = open ( file , O_RDONLY ) ;
if ( fd < 0 )
die ( " Can't read '%s' " , file ) ;
size = get_size_fd ( fd ) ;
close ( fd ) ;
return size ;
}
static void read_header_files ( void )
{
unsigned long long size , check_size ;
char * path ;
int fd ;
path = get_tracing_file ( " events/header_page " ) ;
fd = open ( path , O_RDONLY ) ;
if ( fd < 0 )
die ( " can't read '%s' " , path ) ;
/* unfortunately, you can not stat debugfs files for size */
size = get_size_fd ( fd ) ;
write_or_die ( " header_page " , 12 ) ;
write_or_die ( & size , 8 ) ;
check_size = copy_file_fd ( fd ) ;
2009-12-28 16:49:38 +08:00
close ( fd ) ;
2009-08-17 16:18:05 +02:00
if ( size ! = check_size )
die ( " wrong size for '%s' size=%lld read=%lld " ,
path , size , check_size ) ;
put_tracing_file ( path ) ;
path = get_tracing_file ( " events/header_event " ) ;
fd = open ( path , O_RDONLY ) ;
if ( fd < 0 )
die ( " can't read '%s' " , path ) ;
size = get_size_fd ( fd ) ;
write_or_die ( " header_event " , 13 ) ;
write_or_die ( & size , 8 ) ;
check_size = copy_file_fd ( fd ) ;
if ( size ! = check_size )
die ( " wrong size for '%s' " , path ) ;
put_tracing_file ( path ) ;
2009-12-28 16:49:38 +08:00
close ( fd ) ;
2009-08-17 16:18:05 +02:00
}
2009-08-28 03:09:58 +02:00
static bool name_in_tp_list ( char * sys , struct tracepoint_path * tps )
{
while ( tps ) {
if ( ! strcmp ( sys , tps - > name ) )
return true ;
tps = tps - > next ;
}
return false ;
}
static void copy_event_system ( const char * sys , struct tracepoint_path * tps )
2009-08-17 16:18:05 +02:00
{
unsigned long long size , check_size ;
struct dirent * dent ;
struct stat st ;
char * format ;
DIR * dir ;
int count = 0 ;
int ret ;
dir = opendir ( sys ) ;
if ( ! dir )
die ( " can't read directory '%s' " , sys ) ;
while ( ( dent = readdir ( dir ) ) ) {
2009-12-19 16:40:28 -05:00
if ( dent - > d_type ! = DT_DIR | |
strcmp ( dent - > d_name , " . " ) = = 0 | |
2009-08-28 03:09:58 +02:00
strcmp ( dent - > d_name , " .. " ) = = 0 | |
! name_in_tp_list ( dent - > d_name , tps ) )
2009-08-17 16:18:05 +02:00
continue ;
format = malloc_or_die ( strlen ( sys ) + strlen ( dent - > d_name ) + 10 ) ;
sprintf ( format , " %s/%s/format " , sys , dent - > d_name ) ;
ret = stat ( format , & st ) ;
free ( format ) ;
if ( ret < 0 )
continue ;
count + + ;
}
write_or_die ( & count , 4 ) ;
rewinddir ( dir ) ;
while ( ( dent = readdir ( dir ) ) ) {
2009-12-19 16:40:28 -05:00
if ( dent - > d_type ! = DT_DIR | |
strcmp ( dent - > d_name , " . " ) = = 0 | |
2009-08-28 03:09:58 +02:00
strcmp ( dent - > d_name , " .. " ) = = 0 | |
! name_in_tp_list ( dent - > d_name , tps ) )
2009-08-17 16:18:05 +02:00
continue ;
format = malloc_or_die ( strlen ( sys ) + strlen ( dent - > d_name ) + 10 ) ;
sprintf ( format , " %s/%s/format " , sys , dent - > d_name ) ;
ret = stat ( format , & st ) ;
if ( ret > = 0 ) {
/* unfortunately, you can not stat debugfs files for size */
size = get_size ( format ) ;
write_or_die ( & size , 8 ) ;
check_size = copy_file ( format ) ;
if ( size ! = check_size )
die ( " error in size of file '%s' " , format ) ;
}
free ( format ) ;
}
2009-12-28 16:49:38 +08:00
closedir ( dir ) ;
2009-08-17 16:18:05 +02:00
}
2009-08-28 03:09:58 +02:00
static void read_ftrace_files ( struct tracepoint_path * tps )
2009-08-17 16:18:05 +02:00
{
char * path ;
path = get_tracing_file ( " events/ftrace " ) ;
2009-08-28 03:09:58 +02:00
copy_event_system ( path , tps ) ;
2009-08-17 16:18:05 +02:00
put_tracing_file ( path ) ;
}
2009-08-28 03:09:58 +02:00
static bool system_in_tp_list ( char * sys , struct tracepoint_path * tps )
{
while ( tps ) {
if ( ! strcmp ( sys , tps - > system ) )
return true ;
tps = tps - > next ;
}
return false ;
}
static void read_event_files ( struct tracepoint_path * tps )
2009-08-17 16:18:05 +02:00
{
struct dirent * dent ;
struct stat st ;
char * path ;
char * sys ;
DIR * dir ;
int count = 0 ;
int ret ;
path = get_tracing_file ( " events " ) ;
dir = opendir ( path ) ;
if ( ! dir )
die ( " can't read directory '%s' " , path ) ;
while ( ( dent = readdir ( dir ) ) ) {
2009-12-19 16:40:28 -05:00
if ( dent - > d_type ! = DT_DIR | |
strcmp ( dent - > d_name , " . " ) = = 0 | |
2009-08-17 16:18:05 +02:00
strcmp ( dent - > d_name , " .. " ) = = 0 | |
2009-08-28 03:09:58 +02:00
strcmp ( dent - > d_name , " ftrace " ) = = 0 | |
! system_in_tp_list ( dent - > d_name , tps ) )
2009-08-17 16:18:05 +02:00
continue ;
2009-12-19 16:40:28 -05:00
count + + ;
2009-08-17 16:18:05 +02:00
}
write_or_die ( & count , 4 ) ;
rewinddir ( dir ) ;
while ( ( dent = readdir ( dir ) ) ) {
2009-12-19 16:40:28 -05:00
if ( dent - > d_type ! = DT_DIR | |
strcmp ( dent - > d_name , " . " ) = = 0 | |
2009-08-17 16:18:05 +02:00
strcmp ( dent - > d_name , " .. " ) = = 0 | |
2009-08-28 03:09:58 +02:00
strcmp ( dent - > d_name , " ftrace " ) = = 0 | |
! system_in_tp_list ( dent - > d_name , tps ) )
2009-08-17 16:18:05 +02:00
continue ;
sys = malloc_or_die ( strlen ( path ) + strlen ( dent - > d_name ) + 2 ) ;
sprintf ( sys , " %s/%s " , path , dent - > d_name ) ;
ret = stat ( sys , & st ) ;
if ( ret > = 0 ) {
2009-12-19 16:40:28 -05:00
write_or_die ( dent - > d_name , strlen ( dent - > d_name ) + 1 ) ;
copy_event_system ( sys , tps ) ;
2009-08-17 16:18:05 +02:00
}
free ( sys ) ;
}
2009-12-28 16:49:38 +08:00
closedir ( dir ) ;
2009-08-17 16:18:05 +02:00
put_tracing_file ( path ) ;
}
static void read_proc_kallsyms ( void )
{
unsigned int size , check_size ;
const char * path = " /proc/kallsyms " ;
struct stat st ;
int ret ;
ret = stat ( path , & st ) ;
if ( ret < 0 ) {
/* not found */
size = 0 ;
write_or_die ( & size , 4 ) ;
return ;
}
size = get_size ( path ) ;
write_or_die ( & size , 4 ) ;
check_size = copy_file ( path ) ;
if ( size ! = check_size )
die ( " error in size of file '%s' " , path ) ;
}
static void read_ftrace_printk ( void )
{
unsigned int size , check_size ;
2009-09-17 16:34:23 +08:00
char * path ;
2009-08-17 16:18:05 +02:00
struct stat st ;
int ret ;
path = get_tracing_file ( " printk_formats " ) ;
ret = stat ( path , & st ) ;
if ( ret < 0 ) {
/* not found */
size = 0 ;
write_or_die ( & size , 4 ) ;
2009-09-17 16:34:23 +08:00
goto out ;
2009-08-17 16:18:05 +02:00
}
size = get_size ( path ) ;
write_or_die ( & size , 4 ) ;
check_size = copy_file ( path ) ;
if ( size ! = check_size )
die ( " error in size of file '%s' " , path ) ;
2009-09-17 16:34:23 +08:00
out :
put_tracing_file ( path ) ;
2009-08-17 16:18:05 +02:00
}
2009-08-28 03:09:58 +02:00
static struct tracepoint_path *
2011-01-03 16:39:04 -02:00
get_tracepoints_path ( struct list_head * pattrs )
2009-08-28 03:09:58 +02:00
{
struct tracepoint_path path , * ppath = & path ;
2011-01-03 16:39:04 -02:00
struct perf_evsel * pos ;
int nr_tracepoints = 0 ;
2009-08-28 03:09:58 +02:00
2011-01-03 16:39:04 -02:00
list_for_each_entry ( pos , pattrs , node ) {
if ( pos - > attr . type ! = PERF_TYPE_TRACEPOINT )
2009-08-28 03:09:58 +02:00
continue ;
2009-11-21 14:31:26 -02:00
+ + nr_tracepoints ;
2011-01-03 16:39:04 -02:00
ppath - > next = tracepoint_id_to_path ( pos - > attr . config ) ;
2009-08-28 03:09:58 +02:00
if ( ! ppath - > next )
die ( " %s \n " , " No memory to alloc tracepoints list " ) ;
ppath = ppath - > next ;
}
2009-11-21 14:31:26 -02:00
return nr_tracepoints > 0 ? path . next : NULL ;
2009-08-28 03:09:58 +02:00
}
2009-11-21 14:31:26 -02:00
2011-01-03 16:39:04 -02:00
bool have_tracepoints ( struct list_head * pattrs )
2010-05-03 00:14:48 -05:00
{
2011-01-03 16:39:04 -02:00
struct perf_evsel * pos ;
2010-05-04 22:20:16 -05:00
2011-01-03 16:39:04 -02:00
list_for_each_entry ( pos , pattrs , node )
if ( pos - > attr . type = = PERF_TYPE_TRACEPOINT )
2010-05-04 22:20:16 -05:00
return true ;
return false ;
2010-05-03 00:14:48 -05:00
}
2011-01-03 16:39:04 -02:00
int read_tracing_data ( int fd , struct list_head * pattrs )
2009-08-17 16:18:05 +02:00
{
char buf [ BUFSIZ ] ;
2011-01-03 16:39:04 -02:00
struct tracepoint_path * tps = get_tracepoints_path ( pattrs ) ;
2009-11-21 14:31:26 -02:00
/*
* What ? No tracepoints ? No sense writing anything here , bail out .
*/
if ( tps = = NULL )
return - 1 ;
2009-08-17 16:18:05 +02:00
2009-10-06 23:36:47 +02:00
output_fd = fd ;
2009-08-17 16:18:05 +02:00
buf [ 0 ] = 23 ;
buf [ 1 ] = 8 ;
buf [ 2 ] = 68 ;
memcpy ( buf + 3 , " tracing " , 7 ) ;
write_or_die ( buf , 10 ) ;
write_or_die ( VERSION , strlen ( VERSION ) + 1 ) ;
/* save endian */
if ( bigendian ( ) )
buf [ 0 ] = 1 ;
else
buf [ 0 ] = 0 ;
write_or_die ( buf , 1 ) ;
/* save size of long */
buf [ 0 ] = sizeof ( long ) ;
write_or_die ( buf , 1 ) ;
/* save page_size */
2010-01-14 18:30:04 -02:00
page_size = sysconf ( _SC_PAGESIZE ) ;
2009-08-17 16:18:05 +02:00
write_or_die ( & page_size , 4 ) ;
read_header_files ( ) ;
2009-08-28 03:09:58 +02:00
read_ftrace_files ( tps ) ;
read_event_files ( tps ) ;
2009-08-17 16:18:05 +02:00
read_proc_kallsyms ( ) ;
read_ftrace_printk ( ) ;
2009-11-21 14:31:26 -02:00
return 0 ;
2009-08-17 16:18:05 +02:00
}
2010-04-01 23:59:21 -05:00
2011-01-03 16:39:04 -02:00
ssize_t read_tracing_data_size ( int fd , struct list_head * pattrs )
2010-04-01 23:59:21 -05:00
{
ssize_t size ;
int err = 0 ;
calc_data_size = 1 ;
2011-01-03 16:39:04 -02:00
err = read_tracing_data ( fd , pattrs ) ;
2010-04-01 23:59:21 -05:00
size = calc_data_size - 1 ;
calc_data_size = 0 ;
if ( err < 0 )
return err ;
return size ;
}