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
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
2011-11-16 12:55:59 -02:00
# include "util.h"
2009-08-17 16:18:05 +02:00
# 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 <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 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 ;
} ;
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 ;
}
2011-07-14 13:34:43 +10:00
/* unfortunately, you can not stat debugfs or proc files for size */
static void record_file ( const char * file , size_t hdr_sz )
2009-08-17 16:18:05 +02:00
{
unsigned long long size = 0 ;
2011-07-14 13:34:43 +10:00
char buf [ BUFSIZ ] , * sizep ;
off_t hdr_pos = lseek ( output_fd , 0 , SEEK_CUR ) ;
int r , fd ;
2009-08-17 16:18:05 +02:00
fd = open ( file , O_RDONLY ) ;
if ( fd < 0 )
die ( " Can't read '%s' " , file ) ;
2011-07-14 13:34:43 +10:00
/* put in zeros for file size, then fill true size later */
2011-10-20 15:59:43 +02:00
if ( hdr_sz )
write_or_die ( & size , hdr_sz ) ;
2009-08-17 16:18:05 +02:00
do {
r = read ( fd , buf , BUFSIZ ) ;
2011-07-14 13:34:43 +10:00
if ( r > 0 ) {
2009-08-17 16:18:05 +02:00
size + = r ;
2011-07-14 13:34:43 +10:00
write_or_die ( buf , r ) ;
}
2009-08-17 16:18:05 +02:00
} while ( r > 0 ) ;
close ( fd ) ;
2011-07-14 13:34:43 +10:00
/* ugh, handle big-endian hdr_size == 4 */
sizep = ( char * ) & size ;
if ( bigendian ( ) )
sizep + = sizeof ( u64 ) - hdr_sz ;
2011-10-20 15:59:43 +02:00
if ( hdr_sz & & pwrite ( output_fd , sizep , hdr_sz , hdr_pos ) < 0 )
2011-07-14 13:34:43 +10:00
die ( " writing to %s " , output_file ) ;
2009-08-17 16:18:05 +02:00
}
static void read_header_files ( void )
{
char * path ;
2011-07-14 13:34:43 +10:00
struct stat st ;
2009-08-17 16:18:05 +02:00
path = get_tracing_file ( " events/header_page " ) ;
2011-07-14 13:34:43 +10:00
if ( stat ( path , & st ) < 0 )
2009-08-17 16:18:05 +02:00
die ( " can't read '%s' " , path ) ;
write_or_die ( " header_page " , 12 ) ;
2011-07-14 13:34:43 +10:00
record_file ( path , 8 ) ;
2009-08-17 16:18:05 +02:00
put_tracing_file ( path ) ;
path = get_tracing_file ( " events/header_event " ) ;
2011-07-14 13:34:43 +10:00
if ( stat ( path , & st ) < 0 )
2009-08-17 16:18:05 +02:00
die ( " can't read '%s' " , path ) ;
write_or_die ( " header_event " , 13 ) ;
2011-07-14 13:34:43 +10:00
record_file ( path , 8 ) ;
2009-08-17 16:18:05 +02:00
put_tracing_file ( path ) ;
}
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
{
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 ) ;
2011-07-14 13:34:43 +10:00
if ( ret > = 0 )
record_file ( format , 8 ) ;
2009-08-17 16:18:05 +02:00
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 )
{
2011-07-14 13:34:43 +10:00
unsigned int size ;
2009-08-17 16:18:05 +02:00
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 ;
}
2011-07-14 13:34:43 +10:00
record_file ( path , 4 ) ;
2009-08-17 16:18:05 +02:00
}
static void read_ftrace_printk ( void )
{
2011-07-14 13:34:43 +10:00
unsigned int 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
}
2011-07-14 13:34:43 +10:00
record_file ( path , 4 ) ;
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-10-20 15:59:43 +02:00
static void
put_tracepoints_path ( struct tracepoint_path * tps )
{
while ( tps ) {
struct tracepoint_path * t = tps ;
tps = tps - > next ;
free ( t - > name ) ;
free ( t - > system ) ;
free ( t ) ;
}
}
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-10-20 15:59:43 +02:00
static void tracing_data_header ( void )
2009-08-17 16:18:05 +02:00
{
2011-10-20 15:59:43 +02:00
char buf [ 20 ] ;
2009-08-17 16:18:05 +02:00
2011-10-20 15:59:43 +02:00
/* just guessing this is someone's birthday.. ;) */
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 ) ;
2011-10-20 15:59:43 +02:00
}
struct tracing_data * tracing_data_get ( struct list_head * pattrs ,
int fd , bool temp )
{
struct tracepoint_path * tps ;
struct tracing_data * tdata ;
output_fd = fd ;
tps = get_tracepoints_path ( pattrs ) ;
if ( ! tps )
return NULL ;
2009-08-17 16:18:05 +02:00
2011-10-20 15:59:43 +02:00
tdata = malloc_or_die ( sizeof ( * tdata ) ) ;
tdata - > temp = temp ;
tdata - > size = 0 ;
if ( temp ) {
int temp_fd ;
snprintf ( tdata - > temp_file , sizeof ( tdata - > temp_file ) ,
" /tmp/perf-XXXXXX " ) ;
if ( ! mkstemp ( tdata - > temp_file ) )
die ( " Can't make temp file " ) ;
temp_fd = open ( tdata - > temp_file , O_RDWR ) ;
if ( temp_fd < 0 )
die ( " Can't read '%s' " , tdata - > temp_file ) ;
/*
* Set the temp file the default output , so all the
* tracing data are stored into it .
*/
output_fd = temp_fd ;
}
tracing_data_header ( ) ;
2009-08-17 16:18:05 +02:00
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
2011-10-20 15:59:43 +02:00
/*
* All tracing data are stored by now , we can restore
* the default output file in case we used temp file .
*/
if ( temp ) {
tdata - > size = lseek ( output_fd , 0 , SEEK_CUR ) ;
close ( output_fd ) ;
output_fd = fd ;
}
put_tracepoints_path ( tps ) ;
return tdata ;
2009-08-17 16:18:05 +02:00
}
2010-04-01 23:59:21 -05:00
2011-10-20 15:59:43 +02:00
void tracing_data_put ( struct tracing_data * tdata )
2010-04-01 23:59:21 -05:00
{
2011-10-20 15:59:43 +02:00
if ( tdata - > temp ) {
record_file ( tdata - > temp_file , 0 ) ;
unlink ( tdata - > temp_file ) ;
}
2010-04-01 23:59:21 -05:00
2011-10-20 15:59:43 +02:00
free ( tdata ) ;
}
2010-04-01 23:59:21 -05:00
2011-10-20 15:59:43 +02:00
int read_tracing_data ( int fd , struct list_head * pattrs )
{
struct tracing_data * tdata ;
2010-04-01 23:59:21 -05:00
2011-10-20 15:59:43 +02:00
/*
* We work over the real file , so we can write data
* directly , no temp file is needed .
*/
tdata = tracing_data_get ( pattrs , fd , false ) ;
if ( ! tdata )
return - ENOMEM ;
tracing_data_put ( tdata ) ;
return 0 ;
2010-04-01 23:59:21 -05:00
}