2009-08-17 18:18:06 +04:00
/*
* Copyright ( C ) 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
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
2010-02-04 11:46:42 +03:00
# define _FILE_OFFSET_BITS 64
2009-08-17 18:18:06 +04:00
# include <dirent.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <getopt.h>
# include <stdarg.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/wait.h>
# include <sys/mman.h>
# include <pthread.h>
# include <fcntl.h>
# include <unistd.h>
# include <ctype.h>
# include <errno.h>
2009-08-28 05:09:58 +04:00
# include "../perf.h"
2009-08-17 18:18:06 +04:00
# include "util.h"
# include "trace-event.h"
static int input_fd ;
static int read_page ;
int file_bigendian ;
int host_bigendian ;
static int long_size ;
static unsigned long page_size ;
static int read_or_die ( void * data , int size )
{
int r ;
r = read ( input_fd , data , size ) ;
if ( r ! = size )
die ( " reading input file (size expected=%d received=%d) " ,
size , r ) ;
return r ;
}
static unsigned int read4 ( void )
{
unsigned int data ;
read_or_die ( & data , 4 ) ;
return __data2host4 ( data ) ;
}
static unsigned long long read8 ( void )
{
unsigned long long data ;
read_or_die ( & data , 8 ) ;
return __data2host8 ( data ) ;
}
static char * read_string ( void )
{
char buf [ BUFSIZ ] ;
char * str = NULL ;
int size = 0 ;
int i ;
2010-02-04 11:46:42 +03:00
off_t r ;
2009-08-17 18:18:06 +04:00
for ( ; ; ) {
r = read ( input_fd , buf , BUFSIZ ) ;
if ( r < 0 )
die ( " reading input file " ) ;
if ( ! r )
die ( " no data " ) ;
for ( i = 0 ; i < r ; i + + ) {
if ( ! buf [ i ] )
break ;
}
if ( i < r )
break ;
if ( str ) {
size + = BUFSIZ ;
str = realloc ( str , size ) ;
if ( ! str )
die ( " malloc of size %d " , size ) ;
memcpy ( str + ( size - BUFSIZ ) , buf , BUFSIZ ) ;
} else {
size = BUFSIZ ;
str = malloc_or_die ( size ) ;
memcpy ( str , buf , size ) ;
}
}
2009-09-03 18:22:45 +04:00
/* trailing \0: */
i + + ;
2009-08-17 18:18:06 +04:00
/* move the file descriptor to the end of the string */
2010-02-04 11:46:42 +03:00
r = lseek ( input_fd , - ( r - i ) , SEEK_CUR ) ;
if ( r = = ( off_t ) - 1 )
2009-08-17 18:18:06 +04:00
die ( " lseek " ) ;
if ( str ) {
size + = i ;
str = realloc ( str , size ) ;
if ( ! str )
die ( " malloc of size %d " , size ) ;
memcpy ( str + ( size - i ) , buf , i ) ;
} else {
size = i ;
str = malloc_or_die ( i ) ;
memcpy ( str , buf , i ) ;
}
return str ;
}
static void read_proc_kallsyms ( void )
{
unsigned int size ;
char * buf ;
size = read4 ( ) ;
if ( ! size )
return ;
2009-12-06 14:10:49 +03:00
buf = malloc_or_die ( size + 1 ) ;
2009-08-17 18:18:06 +04:00
read_or_die ( buf , size ) ;
2009-12-06 14:10:49 +03:00
buf [ size ] = ' \0 ' ;
2009-08-17 18:18:06 +04:00
parse_proc_kallsyms ( buf , size ) ;
free ( buf ) ;
}
static void read_ftrace_printk ( void )
{
unsigned int size ;
char * buf ;
size = read4 ( ) ;
if ( ! size )
return ;
buf = malloc_or_die ( size ) ;
read_or_die ( buf , size ) ;
parse_ftrace_printk ( buf , size ) ;
free ( buf ) ;
}
static void read_header_files ( void )
{
unsigned long long size ;
char * header_page ;
char * header_event ;
char buf [ BUFSIZ ] ;
read_or_die ( buf , 12 ) ;
if ( memcmp ( buf , " header_page " , 12 ) ! = 0 )
die ( " did not read header page " ) ;
size = read8 ( ) ;
header_page = malloc_or_die ( size ) ;
read_or_die ( header_page , size ) ;
parse_header_page ( header_page , size ) ;
free ( header_page ) ;
/*
* The size field in the page is of type long ,
* use that instead , since it represents the kernel .
*/
long_size = header_page_size_size ;
read_or_die ( buf , 13 ) ;
if ( memcmp ( buf , " header_event " , 13 ) ! = 0 )
die ( " did not read header event " ) ;
size = read8 ( ) ;
header_event = malloc_or_die ( size ) ;
read_or_die ( header_event , size ) ;
free ( header_event ) ;
}
static void read_ftrace_file ( unsigned long long size )
{
char * buf ;
buf = malloc_or_die ( size ) ;
read_or_die ( buf , size ) ;
parse_ftrace_file ( buf , size ) ;
free ( buf ) ;
}
static void read_event_file ( char * sys , unsigned long long size )
{
char * buf ;
buf = malloc_or_die ( size ) ;
read_or_die ( buf , size ) ;
parse_event_file ( buf , size , sys ) ;
free ( buf ) ;
}
static void read_ftrace_files ( void )
{
unsigned long long size ;
int count ;
int i ;
count = read4 ( ) ;
for ( i = 0 ; i < count ; i + + ) {
size = read8 ( ) ;
read_ftrace_file ( size ) ;
}
}
static void read_event_files ( void )
{
unsigned long long size ;
char * sys ;
int systems ;
int count ;
int i , x ;
systems = read4 ( ) ;
for ( i = 0 ; i < systems ; i + + ) {
sys = read_string ( ) ;
count = read4 ( ) ;
for ( x = 0 ; x < count ; x + + ) {
size = read8 ( ) ;
read_event_file ( sys , size ) ;
}
}
}
struct cpu_data {
unsigned long long offset ;
unsigned long long size ;
unsigned long long timestamp ;
struct record * next ;
char * page ;
int cpu ;
int index ;
int page_size ;
} ;
static struct cpu_data * cpu_data ;
static void update_cpu_data_index ( int cpu )
{
cpu_data [ cpu ] . offset + = page_size ;
cpu_data [ cpu ] . size - = page_size ;
cpu_data [ cpu ] . index = 0 ;
}
static void get_next_page ( int cpu )
{
2010-02-04 11:46:42 +03:00
off_t save_seek ;
off_t ret ;
2009-08-17 18:18:06 +04:00
if ( ! cpu_data [ cpu ] . page )
return ;
if ( read_page ) {
if ( cpu_data [ cpu ] . size < = page_size ) {
free ( cpu_data [ cpu ] . page ) ;
cpu_data [ cpu ] . page = NULL ;
return ;
}
update_cpu_data_index ( cpu ) ;
/* other parts of the code may expect the pointer to not move */
2010-02-04 11:46:42 +03:00
save_seek = lseek ( input_fd , 0 , SEEK_CUR ) ;
2009-08-17 18:18:06 +04:00
2010-02-04 11:46:42 +03:00
ret = lseek ( input_fd , cpu_data [ cpu ] . offset , SEEK_SET ) ;
if ( ret = = ( off_t ) - 1 )
2009-08-17 18:18:06 +04:00
die ( " failed to lseek " ) ;
ret = read ( input_fd , cpu_data [ cpu ] . page , page_size ) ;
if ( ret < 0 )
die ( " failed to read page " ) ;
/* reset the file pointer back */
2010-02-04 11:46:42 +03:00
lseek ( input_fd , save_seek , SEEK_SET ) ;
2009-08-17 18:18:06 +04:00
return ;
}
munmap ( cpu_data [ cpu ] . page , page_size ) ;
cpu_data [ cpu ] . page = NULL ;
if ( cpu_data [ cpu ] . size < = page_size )
return ;
update_cpu_data_index ( cpu ) ;
cpu_data [ cpu ] . page = mmap ( NULL , page_size , PROT_READ , MAP_PRIVATE ,
input_fd , cpu_data [ cpu ] . offset ) ;
if ( cpu_data [ cpu ] . page = = MAP_FAILED )
die ( " failed to mmap cpu %d at offset 0x%llx " ,
cpu , cpu_data [ cpu ] . offset ) ;
}
static unsigned int type_len4host ( unsigned int type_len_ts )
{
if ( file_bigendian )
return ( type_len_ts > > 27 ) & ( ( 1 < < 5 ) - 1 ) ;
else
return type_len_ts & ( ( 1 < < 5 ) - 1 ) ;
}
static unsigned int ts4host ( unsigned int type_len_ts )
{
if ( file_bigendian )
return type_len_ts & ( ( 1 < < 27 ) - 1 ) ;
else
return type_len_ts > > 5 ;
}
static int calc_index ( void * ptr , int cpu )
{
return ( unsigned long ) ptr - ( unsigned long ) cpu_data [ cpu ] . page ;
}
struct record * trace_peek_data ( int cpu )
{
struct record * data ;
void * page = cpu_data [ cpu ] . page ;
int idx = cpu_data [ cpu ] . index ;
void * ptr = page + idx ;
unsigned long long extend ;
unsigned int type_len_ts ;
unsigned int type_len ;
unsigned int delta ;
unsigned int length = 0 ;
if ( cpu_data [ cpu ] . next )
return cpu_data [ cpu ] . next ;
if ( ! page )
return NULL ;
if ( ! idx ) {
/* FIXME: handle header page */
if ( header_page_ts_size ! = 8 )
die ( " expected a long long type for timestamp " ) ;
cpu_data [ cpu ] . timestamp = data2host8 ( ptr ) ;
ptr + = 8 ;
switch ( header_page_size_size ) {
case 4 :
cpu_data [ cpu ] . page_size = data2host4 ( ptr ) ;
ptr + = 4 ;
break ;
case 8 :
cpu_data [ cpu ] . page_size = data2host8 ( ptr ) ;
ptr + = 8 ;
break ;
default :
die ( " bad long size " ) ;
}
ptr = cpu_data [ cpu ] . page + header_page_data_offset ;
}
read_again :
idx = calc_index ( ptr , cpu ) ;
if ( idx > = cpu_data [ cpu ] . page_size ) {
get_next_page ( cpu ) ;
return trace_peek_data ( cpu ) ;
}
type_len_ts = data2host4 ( ptr ) ;
ptr + = 4 ;
type_len = type_len4host ( type_len_ts ) ;
delta = ts4host ( type_len_ts ) ;
switch ( type_len ) {
case RINGBUF_TYPE_PADDING :
if ( ! delta )
die ( " error, hit unexpected end of page " ) ;
length = data2host4 ( ptr ) ;
ptr + = 4 ;
length * = 4 ;
ptr + = length ;
goto read_again ;
case RINGBUF_TYPE_TIME_EXTEND :
extend = data2host4 ( ptr ) ;
ptr + = 4 ;
extend < < = TS_SHIFT ;
extend + = delta ;
cpu_data [ cpu ] . timestamp + = extend ;
goto read_again ;
case RINGBUF_TYPE_TIME_STAMP :
ptr + = 12 ;
break ;
case 0 :
length = data2host4 ( ptr ) ;
ptr + = 4 ;
die ( " here! length=%d " , length ) ;
break ;
default :
length = type_len * 4 ;
break ;
}
cpu_data [ cpu ] . timestamp + = delta ;
data = malloc_or_die ( sizeof ( * data ) ) ;
memset ( data , 0 , sizeof ( * data ) ) ;
data - > ts = cpu_data [ cpu ] . timestamp ;
data - > size = length ;
data - > data = ptr ;
ptr + = length ;
cpu_data [ cpu ] . index = calc_index ( ptr , cpu ) ;
cpu_data [ cpu ] . next = data ;
return data ;
}
struct record * trace_read_data ( int cpu )
{
struct record * data ;
data = trace_peek_data ( cpu ) ;
cpu_data [ cpu ] . next = NULL ;
return data ;
}
2009-10-07 01:36:47 +04:00
void trace_report ( int fd )
2009-08-17 18:18:06 +04:00
{
char buf [ BUFSIZ ] ;
char test [ ] = { 23 , 8 , 68 } ;
char * version ;
2009-09-12 12:08:34 +04:00
int show_version = 0 ;
2009-08-17 18:18:06 +04:00
int show_funcs = 0 ;
int show_printk = 0 ;
2009-10-07 01:36:47 +04:00
input_fd = fd ;
2009-08-17 18:18:06 +04:00
read_or_die ( buf , 3 ) ;
if ( memcmp ( buf , test , 3 ) ! = 0 )
2009-11-21 19:31:26 +03:00
die ( " no trace data in the file " ) ;
2009-08-17 18:18:06 +04:00
read_or_die ( buf , 7 ) ;
if ( memcmp ( buf , " tracing " , 7 ) ! = 0 )
2009-11-21 19:31:26 +03:00
die ( " not a trace file (missing 'tracing' tag) " ) ;
2009-08-17 18:18:06 +04:00
version = read_string ( ) ;
2009-09-12 12:08:34 +04:00
if ( show_version )
printf ( " version = %s \n " , version ) ;
2009-08-17 18:18:06 +04:00
free ( version ) ;
read_or_die ( buf , 1 ) ;
file_bigendian = buf [ 0 ] ;
host_bigendian = bigendian ( ) ;
read_or_die ( buf , 1 ) ;
long_size = buf [ 0 ] ;
page_size = read4 ( ) ;
read_header_files ( ) ;
read_ftrace_files ( ) ;
read_event_files ( ) ;
read_proc_kallsyms ( ) ;
read_ftrace_printk ( ) ;
if ( show_funcs ) {
print_funcs ( ) ;
return ;
}
if ( show_printk ) {
print_printk ( ) ;
return ;
}
return ;
}