2018-12-13 06:59:26 +03:00
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2018-05-04 04:37:16 +03:00
/* Copyright (C) 2018 Netronome Systems, Inc. */
/* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation .
*/
# include <errno.h>
# include <fcntl.h>
2020-01-20 16:06:46 +03:00
# include <bpf/libbpf.h>
2018-05-04 04:37:16 +03:00
# include <poll.h>
# include <signal.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
# include <unistd.h>
# include <linux/bpf.h>
# include <linux/perf_event.h>
# include <sys/ioctl.h>
# include <sys/mman.h>
# include <sys/syscall.h>
2020-01-20 16:06:46 +03:00
# include <bpf/bpf.h>
2018-05-04 04:37:16 +03:00
# include <perf-sys.h>
# include "main.h"
# define MMAP_PAGE_CNT 16
2019-07-06 21:06:27 +03:00
static volatile bool stop ;
2018-05-04 04:37:16 +03:00
struct event_ring_info {
int fd ;
int key ;
unsigned int cpu ;
void * mem ;
} ;
struct perf_event_sample {
struct perf_event_header header ;
2018-05-10 20:24:37 +03:00
u64 time ;
2018-05-04 04:37:16 +03:00
__u32 size ;
unsigned char data [ ] ;
} ;
2019-07-06 21:06:27 +03:00
struct perf_event_lost {
struct perf_event_header header ;
__u64 id ;
__u64 lost ;
} ;
2018-05-04 04:37:16 +03:00
static void int_exit ( int signo )
{
fprintf ( stderr , " Stopping... \n " ) ;
stop = true ;
}
2019-07-06 21:06:27 +03:00
struct event_pipe_ctx {
bool all_cpus ;
int cpu ;
int idx ;
} ;
2018-10-21 03:09:28 +03:00
static enum bpf_perf_event_ret
2019-07-06 21:06:27 +03:00
print_bpf_output ( void * private_data , int cpu , struct perf_event_header * event )
2018-05-04 04:37:16 +03:00
{
2019-07-06 21:06:27 +03:00
struct perf_event_sample * e = container_of ( event ,
struct perf_event_sample ,
2018-10-21 03:09:28 +03:00
header ) ;
2019-07-06 21:06:27 +03:00
struct perf_event_lost * lost = container_of ( event ,
struct perf_event_lost ,
header ) ;
struct event_pipe_ctx * ctx = private_data ;
int idx = ctx - > all_cpus ? cpu : ctx - > idx ;
2018-05-04 04:37:16 +03:00
if ( json_output ) {
jsonw_start_object ( json_wtr ) ;
jsonw_name ( json_wtr , " type " ) ;
jsonw_uint ( json_wtr , e - > header . type ) ;
jsonw_name ( json_wtr , " cpu " ) ;
2019-07-06 21:06:27 +03:00
jsonw_uint ( json_wtr , cpu ) ;
2018-05-04 04:37:16 +03:00
jsonw_name ( json_wtr , " index " ) ;
2019-07-06 21:06:27 +03:00
jsonw_uint ( json_wtr , idx ) ;
2018-05-04 04:37:16 +03:00
if ( e - > header . type = = PERF_RECORD_SAMPLE ) {
2018-05-10 20:24:37 +03:00
jsonw_name ( json_wtr , " timestamp " ) ;
jsonw_uint ( json_wtr , e - > time ) ;
2018-05-04 04:37:16 +03:00
jsonw_name ( json_wtr , " data " ) ;
print_data_json ( e - > data , e - > size ) ;
} else if ( e - > header . type = = PERF_RECORD_LOST ) {
jsonw_name ( json_wtr , " lost " ) ;
jsonw_start_object ( json_wtr ) ;
jsonw_name ( json_wtr , " id " ) ;
jsonw_uint ( json_wtr , lost - > id ) ;
jsonw_name ( json_wtr , " count " ) ;
jsonw_uint ( json_wtr , lost - > lost ) ;
jsonw_end_object ( json_wtr ) ;
}
jsonw_end_object ( json_wtr ) ;
} else {
if ( e - > header . type = = PERF_RECORD_SAMPLE ) {
2018-05-10 20:24:37 +03:00
printf ( " == @%lld.%09lld CPU: %d index: %d ===== \n " ,
e - > time / 1000000000ULL , e - > time % 1000000000ULL ,
2019-07-06 21:06:27 +03:00
cpu , idx ) ;
2018-05-04 04:37:16 +03:00
fprint_hex ( stdout , e - > data , e - > size , " " ) ;
printf ( " \n " ) ;
} else if ( e - > header . type = = PERF_RECORD_LOST ) {
printf ( " lost %lld events \n " , lost - > lost ) ;
} else {
printf ( " unknown event type=%d size=%d \n " ,
e - > header . type , e - > header . size ) ;
}
}
2018-05-10 20:24:40 +03:00
return LIBBPF_PERF_EVENT_CONT ;
2018-05-04 04:37:16 +03:00
}
2019-07-06 21:06:27 +03:00
int do_event_pipe ( int argc , char * * argv )
2018-05-04 04:37:16 +03:00
{
2019-07-06 21:06:27 +03:00
struct perf_event_attr perf_attr = {
2018-05-10 20:24:37 +03:00
. sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME ,
2018-05-04 04:37:16 +03:00
. type = PERF_TYPE_SOFTWARE ,
. config = PERF_COUNT_SW_BPF_OUTPUT ,
2019-07-06 21:06:27 +03:00
. sample_period = 1 ,
. wakeup_events = 1 ,
2018-05-04 04:37:16 +03:00
} ;
struct bpf_map_info map_info = { } ;
2019-07-06 21:06:27 +03:00
struct perf_buffer_raw_opts opts = { } ;
struct event_pipe_ctx ctx = {
. all_cpus = true ,
. cpu = - 1 ,
. idx = - 1 ,
} ;
struct perf_buffer * pb ;
2018-05-04 04:37:16 +03:00
__u32 map_info_len ;
2019-07-06 21:06:27 +03:00
int err , map_fd ;
2018-05-04 04:37:16 +03:00
map_info_len = sizeof ( map_info ) ;
map_fd = map_parse_fd_and_info ( & argc , & argv , & map_info , & map_info_len ) ;
if ( map_fd < 0 )
return - 1 ;
if ( map_info . type ! = BPF_MAP_TYPE_PERF_EVENT_ARRAY ) {
p_err ( " map is not a perf event array " ) ;
goto err_close_map ;
}
while ( argc ) {
2018-08-23 19:46:25 +03:00
if ( argc < 2 ) {
2018-05-04 04:37:16 +03:00
BAD_ARG ( ) ;
2018-08-23 19:46:25 +03:00
goto err_close_map ;
}
2018-05-04 04:37:16 +03:00
if ( is_prefix ( * argv , " cpu " ) ) {
char * endptr ;
NEXT_ARG ( ) ;
2019-07-06 21:06:27 +03:00
ctx . cpu = strtoul ( * argv , & endptr , 0 ) ;
2018-05-04 04:37:16 +03:00
if ( * endptr ) {
2019-08-15 17:32:15 +03:00
p_err ( " can't parse %s as CPU ID " , * argv ) ;
2018-05-04 04:37:16 +03:00
goto err_close_map ;
}
NEXT_ARG ( ) ;
} else if ( is_prefix ( * argv , " index " ) ) {
char * endptr ;
NEXT_ARG ( ) ;
2019-07-06 21:06:27 +03:00
ctx . idx = strtoul ( * argv , & endptr , 0 ) ;
2018-05-04 04:37:16 +03:00
if ( * endptr ) {
2019-08-15 17:32:15 +03:00
p_err ( " can't parse %s as index " , * argv ) ;
2018-05-04 04:37:16 +03:00
goto err_close_map ;
}
NEXT_ARG ( ) ;
} else {
BAD_ARG ( ) ;
2018-08-23 19:46:25 +03:00
goto err_close_map ;
2018-05-04 04:37:16 +03:00
}
2019-07-06 21:06:27 +03:00
ctx . all_cpus = false ;
2018-05-04 04:37:16 +03:00
}
2019-07-06 21:06:27 +03:00
if ( ! ctx . all_cpus ) {
if ( ctx . idx = = - 1 | | ctx . cpu = = - 1 ) {
2018-05-04 04:37:16 +03:00
p_err ( " cpu and index must be specified together " ) ;
goto err_close_map ;
}
} else {
2019-07-06 21:06:27 +03:00
ctx . cpu = 0 ;
ctx . idx = 0 ;
2018-05-04 04:37:16 +03:00
}
2019-07-06 21:06:27 +03:00
opts . attr = & perf_attr ;
opts . event_cb = print_bpf_output ;
opts . ctx = & ctx ;
opts . cpu_cnt = ctx . all_cpus ? 0 : 1 ;
opts . cpus = & ctx . cpu ;
opts . map_keys = & ctx . idx ;
pb = perf_buffer__new_raw ( map_fd , MMAP_PAGE_CNT , & opts ) ;
err = libbpf_get_error ( pb ) ;
if ( err ) {
p_err ( " failed to create perf buffer: %s (%d) " ,
strerror ( err ) , err ) ;
2018-05-04 04:37:16 +03:00
goto err_close_map ;
}
signal ( SIGINT , int_exit ) ;
signal ( SIGHUP , int_exit ) ;
signal ( SIGTERM , int_exit ) ;
if ( json_output )
jsonw_start_array ( json_wtr ) ;
while ( ! stop ) {
2019-07-06 21:06:27 +03:00
err = perf_buffer__poll ( pb , 200 ) ;
if ( err < 0 & & err ! = - EINTR ) {
p_err ( " perf buffer polling failed: %s (%d) " ,
strerror ( err ) , err ) ;
goto err_close_pb ;
}
2018-05-04 04:37:16 +03:00
}
if ( json_output )
jsonw_end_array ( json_wtr ) ;
2019-07-06 21:06:27 +03:00
perf_buffer__free ( pb ) ;
2018-05-04 04:37:16 +03:00
close ( map_fd ) ;
return 0 ;
2019-07-06 21:06:27 +03:00
err_close_pb :
perf_buffer__free ( pb ) ;
2018-05-04 04:37:16 +03:00
err_close_map :
close ( map_fd ) ;
return - 1 ;
}