2018-04-29 08:28:13 +03:00
// SPDX-License-Identifier: GPL-2.0
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <assert.h>
# include <errno.h>
# include <poll.h>
# include <unistd.h>
# include <linux/perf_event.h>
# include <sys/mman.h>
# include "trace_helpers.h"
# define MAX_SYMS 300000
static struct ksym syms [ MAX_SYMS ] ;
static int sym_cnt ;
static int ksym_cmp ( const void * p1 , const void * p2 )
{
return ( ( struct ksym * ) p1 ) - > addr - ( ( struct ksym * ) p2 ) - > addr ;
}
int load_kallsyms ( void )
{
FILE * f = fopen ( " /proc/kallsyms " , " r " ) ;
char func [ 256 ] , buf [ 256 ] ;
char symbol ;
void * addr ;
int i = 0 ;
if ( ! f )
return - ENOENT ;
while ( ! feof ( f ) ) {
if ( ! fgets ( buf , sizeof ( buf ) , f ) )
break ;
if ( sscanf ( buf , " %p %c %s " , & addr , & symbol , func ) ! = 3 )
break ;
if ( ! addr )
continue ;
syms [ i ] . addr = ( long ) addr ;
syms [ i ] . name = strdup ( func ) ;
i + + ;
}
2018-10-18 18:18:36 +03:00
fclose ( f ) ;
2018-04-29 08:28:13 +03:00
sym_cnt = i ;
qsort ( syms , sym_cnt , sizeof ( struct ksym ) , ksym_cmp ) ;
return 0 ;
}
struct ksym * ksym_search ( long key )
{
int start = 0 , end = sym_cnt ;
int result ;
while ( start < end ) {
size_t mid = start + ( end - start ) / 2 ;
result = key - syms [ mid ] . addr ;
if ( result < 0 )
end = mid ;
else if ( result > 0 )
start = mid + 1 ;
else
return & syms [ mid ] ;
}
if ( start > = 1 & & syms [ start - 1 ] . addr < key & &
key < syms [ start ] . addr )
/* valid ksym */
return & syms [ start - 1 ] ;
/* out of range. return _stext */
return & syms [ 0 ] ;
}
2018-05-24 21:21:11 +03:00
long ksym_get_addr ( const char * name )
{
int i ;
for ( i = 0 ; i < sym_cnt ; i + + ) {
if ( strcmp ( syms [ i ] . name , name ) = = 0 )
return syms [ i ] . addr ;
}
return 0 ;
}
2018-04-29 08:28:13 +03:00
static int page_size ;
static int page_cnt = 8 ;
2018-05-10 20:24:40 +03:00
static struct perf_event_mmap_page * header ;
2018-04-29 08:28:13 +03:00
2018-06-25 15:25:02 +03:00
int perf_event_mmap_header ( int fd , struct perf_event_mmap_page * * header )
2018-04-29 08:28:13 +03:00
{
void * base ;
int mmap_size ;
page_size = getpagesize ( ) ;
mmap_size = page_size * ( page_cnt + 1 ) ;
base = mmap ( NULL , mmap_size , PROT_READ | PROT_WRITE , MAP_SHARED , fd , 0 ) ;
if ( base = = MAP_FAILED ) {
printf ( " mmap err \n " ) ;
return - 1 ;
}
2018-06-25 15:25:02 +03:00
* header = base ;
2018-04-29 08:28:13 +03:00
return 0 ;
}
2018-06-25 15:25:02 +03:00
int perf_event_mmap ( int fd )
{
return perf_event_mmap_header ( fd , & header ) ;
}
2018-04-29 08:28:13 +03:00
static int perf_event_poll ( int fd )
{
struct pollfd pfd = { . fd = fd , . events = POLLIN } ;
return poll ( & pfd , 1 , 1000 ) ;
}
struct perf_event_sample {
struct perf_event_header header ;
__u32 size ;
char data [ ] ;
} ;
2018-10-21 03:09:28 +03:00
static enum bpf_perf_event_ret
bpf_perf_event_print ( struct perf_event_header * hdr , void * private_data )
2018-04-29 08:28:13 +03:00
{
2018-10-21 03:09:28 +03:00
struct perf_event_sample * e = ( struct perf_event_sample * ) hdr ;
perf_event_print_fn fn = private_data ;
2018-04-29 08:28:13 +03:00
int ret ;
2018-05-10 20:24:40 +03:00
if ( e - > header . type = = PERF_RECORD_SAMPLE ) {
ret = fn ( e - > data , e - > size ) ;
if ( ret ! = LIBBPF_PERF_EVENT_CONT )
return ret ;
} else if ( e - > header . type = = PERF_RECORD_LOST ) {
struct {
struct perf_event_header header ;
__u64 id ;
__u64 lost ;
} * lost = ( void * ) e ;
printf ( " lost %lld events \n " , lost - > lost ) ;
} else {
printf ( " unknown event type=%d size=%d \n " ,
e - > header . type , e - > header . size ) ;
2018-04-29 08:28:13 +03:00
}
2018-05-10 20:24:40 +03:00
return LIBBPF_PERF_EVENT_CONT ;
2018-04-29 08:28:13 +03:00
}
int perf_event_poller ( int fd , perf_event_print_fn output_fn )
{
2018-05-10 20:24:40 +03:00
enum bpf_perf_event_ret ret ;
void * buf = NULL ;
size_t len = 0 ;
2018-04-29 08:28:13 +03:00
for ( ; ; ) {
perf_event_poll ( fd ) ;
2018-05-10 20:24:40 +03:00
ret = bpf_perf_event_read_simple ( header , page_cnt * page_size ,
page_size , & buf , & len ,
bpf_perf_event_print ,
output_fn ) ;
if ( ret ! = LIBBPF_PERF_EVENT_CONT )
break ;
2018-04-29 08:28:13 +03:00
}
2018-05-10 20:24:40 +03:00
free ( buf ) ;
2018-04-29 08:28:13 +03:00
2018-05-10 20:24:40 +03:00
return ret ;
2018-04-29 08:28:13 +03:00
}
2018-06-25 15:25:02 +03:00
int perf_event_poller_multi ( int * fds , struct perf_event_mmap_page * * headers ,
int num_fds , perf_event_print_fn output_fn )
{
enum bpf_perf_event_ret ret ;
struct pollfd * pfds ;
void * buf = NULL ;
size_t len = 0 ;
int i ;
pfds = calloc ( num_fds , sizeof ( * pfds ) ) ;
if ( ! pfds )
return LIBBPF_PERF_EVENT_ERROR ;
for ( i = 0 ; i < num_fds ; i + + ) {
pfds [ i ] . fd = fds [ i ] ;
pfds [ i ] . events = POLLIN ;
}
for ( ; ; ) {
poll ( pfds , num_fds , 1000 ) ;
for ( i = 0 ; i < num_fds ; i + + ) {
if ( ! pfds [ i ] . revents )
continue ;
ret = bpf_perf_event_read_simple ( headers [ i ] ,
page_cnt * page_size ,
page_size , & buf , & len ,
bpf_perf_event_print ,
output_fn ) ;
if ( ret ! = LIBBPF_PERF_EVENT_CONT )
break ;
}
}
free ( buf ) ;
free ( pfds ) ;
return ret ;
}