2017-10-04 20:10:04 -07:00
/*
* Copyright ( C ) 2017 Netronome Systems , Inc .
*
* This software is dual licensed under the GNU General License Version 2 ,
* June 1991 as shown in the file COPYING in the top - level directory of this
* source tree or the BSD 2 - Clause License provided below . You have the
* option to license this software under the complete terms of either license .
*
* The BSD 2 - Clause License :
*
* Redistribution and use in source and binary forms , with or
* without modification , are permitted provided that the following
* conditions are met :
*
* 1. Redistributions of source code must retain the above
* copyright notice , this list of conditions and the following
* disclaimer .
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
/* Author: Jakub Kicinski <kubakici@wp.pl> */
# include <errno.h>
# include <fcntl.h>
2017-10-09 10:30:13 -07:00
# include <stdarg.h>
2017-10-04 20:10:04 -07:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <bpf.h>
# include "main.h"
2017-10-09 10:30:13 -07:00
# include "disasm.h"
2017-10-04 20:10:04 -07:00
static const char * const prog_type_name [ ] = {
[ BPF_PROG_TYPE_UNSPEC ] = " unspec " ,
[ BPF_PROG_TYPE_SOCKET_FILTER ] = " socket_filter " ,
[ BPF_PROG_TYPE_KPROBE ] = " kprobe " ,
[ BPF_PROG_TYPE_SCHED_CLS ] = " sched_cls " ,
[ BPF_PROG_TYPE_SCHED_ACT ] = " sched_act " ,
[ BPF_PROG_TYPE_TRACEPOINT ] = " tracepoint " ,
[ BPF_PROG_TYPE_XDP ] = " xdp " ,
[ BPF_PROG_TYPE_PERF_EVENT ] = " perf_event " ,
[ BPF_PROG_TYPE_CGROUP_SKB ] = " cgroup_skb " ,
[ BPF_PROG_TYPE_CGROUP_SOCK ] = " cgroup_sock " ,
[ BPF_PROG_TYPE_LWT_IN ] = " lwt_in " ,
[ BPF_PROG_TYPE_LWT_OUT ] = " lwt_out " ,
[ BPF_PROG_TYPE_LWT_XMIT ] = " lwt_xmit " ,
[ BPF_PROG_TYPE_SOCK_OPS ] = " sock_ops " ,
[ BPF_PROG_TYPE_SK_SKB ] = " sk_skb " ,
} ;
static void print_boot_time ( __u64 nsecs , char * buf , unsigned int size )
{
struct timespec real_time_ts , boot_time_ts ;
time_t wallclock_secs ;
struct tm load_tm ;
buf [ - - size ] = ' \0 ' ;
if ( clock_gettime ( CLOCK_REALTIME , & real_time_ts ) | |
clock_gettime ( CLOCK_BOOTTIME , & boot_time_ts ) ) {
perror ( " Can't read clocks " ) ;
snprintf ( buf , size , " %llu " , nsecs / 1000000000 ) ;
return ;
}
wallclock_secs = ( real_time_ts . tv_sec - boot_time_ts . tv_sec ) +
nsecs / 1000000000 ;
if ( ! localtime_r ( & wallclock_secs , & load_tm ) ) {
snprintf ( buf , size , " %llu " , nsecs / 1000000000 ) ;
return ;
}
strftime ( buf , size , " %b %d/%H:%M " , & load_tm ) ;
}
static int prog_fd_by_tag ( unsigned char * tag )
{
struct bpf_prog_info info = { } ;
__u32 len = sizeof ( info ) ;
unsigned int id = 0 ;
int err ;
int fd ;
while ( true ) {
err = bpf_prog_get_next_id ( id , & id ) ;
if ( err ) {
2017-10-23 09:24:13 -07:00
p_err ( " %s " , strerror ( errno ) ) ;
2017-10-04 20:10:04 -07:00
return - 1 ;
}
fd = bpf_prog_get_fd_by_id ( id ) ;
if ( fd < 0 ) {
2017-10-23 09:24:13 -07:00
p_err ( " can't get prog by id (%u): %s " ,
id , strerror ( errno ) ) ;
2017-10-04 20:10:04 -07:00
return - 1 ;
}
err = bpf_obj_get_info_by_fd ( fd , & info , & len ) ;
if ( err ) {
2017-10-23 09:24:13 -07:00
p_err ( " can't get prog info (%u): %s " ,
id , strerror ( errno ) ) ;
2017-10-04 20:10:04 -07:00
close ( fd ) ;
return - 1 ;
}
if ( ! memcmp ( tag , info . tag , BPF_TAG_SIZE ) )
return fd ;
close ( fd ) ;
}
}
int prog_parse_fd ( int * argc , char * * * argv )
{
int fd ;
if ( is_prefix ( * * argv , " id " ) ) {
unsigned int id ;
char * endptr ;
NEXT_ARGP ( ) ;
id = strtoul ( * * argv , & endptr , 0 ) ;
if ( * endptr ) {
2017-10-23 09:24:13 -07:00
p_err ( " can't parse %s as ID " , * * argv ) ;
2017-10-04 20:10:04 -07:00
return - 1 ;
}
NEXT_ARGP ( ) ;
fd = bpf_prog_get_fd_by_id ( id ) ;
if ( fd < 0 )
2017-10-23 09:24:13 -07:00
p_err ( " get by id (%u): %s " , id , strerror ( errno ) ) ;
2017-10-04 20:10:04 -07:00
return fd ;
} else if ( is_prefix ( * * argv , " tag " ) ) {
unsigned char tag [ BPF_TAG_SIZE ] ;
NEXT_ARGP ( ) ;
if ( sscanf ( * * argv , BPF_TAG_FMT , tag , tag + 1 , tag + 2 ,
tag + 3 , tag + 4 , tag + 5 , tag + 6 , tag + 7 )
! = BPF_TAG_SIZE ) {
2017-10-23 09:24:13 -07:00
p_err ( " can't parse tag " ) ;
2017-10-04 20:10:04 -07:00
return - 1 ;
}
NEXT_ARGP ( ) ;
return prog_fd_by_tag ( tag ) ;
} else if ( is_prefix ( * * argv , " pinned " ) ) {
char * path ;
NEXT_ARGP ( ) ;
path = * * argv ;
NEXT_ARGP ( ) ;
return open_obj_pinned_any ( path , BPF_OBJ_PROG ) ;
}
2017-10-23 09:24:13 -07:00
p_err ( " expected 'id', 'tag' or 'pinned', got: '%s'? " , * * argv ) ;
2017-10-04 20:10:04 -07:00
return - 1 ;
}
static void show_prog_maps ( int fd , u32 num_maps )
{
struct bpf_prog_info info = { } ;
__u32 len = sizeof ( info ) ;
__u32 map_ids [ num_maps ] ;
unsigned int i ;
int err ;
info . nr_map_ids = num_maps ;
info . map_ids = ptr_to_u64 ( map_ids ) ;
err = bpf_obj_get_info_by_fd ( fd , & info , & len ) ;
if ( err | | ! info . nr_map_ids )
return ;
2017-10-23 09:24:08 -07:00
if ( json_output ) {
jsonw_name ( json_wtr , " map_ids " ) ;
jsonw_start_array ( json_wtr ) ;
for ( i = 0 ; i < info . nr_map_ids ; i + + )
jsonw_uint ( json_wtr , map_ids [ i ] ) ;
jsonw_end_array ( json_wtr ) ;
} else {
printf ( " map_ids " ) ;
for ( i = 0 ; i < info . nr_map_ids ; i + + )
printf ( " %u%s " , map_ids [ i ] ,
i = = info . nr_map_ids - 1 ? " " : " , " ) ;
}
2017-10-04 20:10:04 -07:00
}
2017-10-23 09:24:08 -07:00
static void print_prog_json ( struct bpf_prog_info * info , int fd )
2017-10-04 20:10:04 -07:00
{
char * memlock ;
2017-10-23 09:24:08 -07:00
jsonw_start_object ( json_wtr ) ;
jsonw_uint_field ( json_wtr , " id " , info - > id ) ;
if ( info - > type < ARRAY_SIZE ( prog_type_name ) )
jsonw_string_field ( json_wtr , " type " ,
prog_type_name [ info - > type ] ) ;
else
jsonw_uint_field ( json_wtr , " type " , info - > type ) ;
if ( * info - > name )
jsonw_string_field ( json_wtr , " name " , info - > name ) ;
jsonw_name ( json_wtr , " tag " ) ;
jsonw_printf ( json_wtr , " \" " BPF_TAG_FMT " \" " ,
info - > tag [ 0 ] , info - > tag [ 1 ] , info - > tag [ 2 ] , info - > tag [ 3 ] ,
info - > tag [ 4 ] , info - > tag [ 5 ] , info - > tag [ 6 ] , info - > tag [ 7 ] ) ;
if ( info - > load_time ) {
char buf [ 32 ] ;
print_boot_time ( info - > load_time , buf , sizeof ( buf ) ) ;
/* Piggy back on load_time, since 0 uid is a valid one */
jsonw_string_field ( json_wtr , " loaded_at " , buf ) ;
jsonw_uint_field ( json_wtr , " uid " , info - > created_by_uid ) ;
}
jsonw_uint_field ( json_wtr , " bytes_xlated " , info - > xlated_prog_len ) ;
if ( info - > jited_prog_len ) {
jsonw_bool_field ( json_wtr , " jited " , true ) ;
jsonw_uint_field ( json_wtr , " bytes_jited " , info - > jited_prog_len ) ;
} else {
jsonw_bool_field ( json_wtr , " jited " , false ) ;
2017-10-04 20:10:04 -07:00
}
2017-10-23 09:24:08 -07:00
memlock = get_fdinfo ( fd , " memlock " ) ;
if ( memlock )
jsonw_int_field ( json_wtr , " bytes_memlock " , atoi ( memlock ) ) ;
free ( memlock ) ;
if ( info - > nr_map_ids )
show_prog_maps ( fd , info - > nr_map_ids ) ;
tools: bpftool: show filenames of pinned objects
Added support to show filenames of pinned objects.
For example:
root@test# ./bpftool prog
3: tracepoint name tracepoint__irq tag f677a7dd722299a3
loaded_at Oct 26/11:39 uid 0
xlated 160B not jited memlock 4096B map_ids 4
pinned /sys/fs/bpf/softirq_prog
4: tracepoint name tracepoint__irq tag ea5dc530d00b92b6
loaded_at Oct 26/11:39 uid 0
xlated 392B not jited memlock 4096B map_ids 4,6
root@test# ./bpftool --json --pretty prog
[{
"id": 3,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "f677a7dd722299a3",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 160,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4
],
"pinned": ["/sys/fs/bpf/softirq_prog"
]
},{
"id": 4,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "ea5dc530d00b92b6",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 392,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4,6
],
"pinned": []
}
]
root@test# ./bpftool map
4: hash name start flags 0x0
key 4B value 16B max_entries 10240 memlock 1003520B
pinned /sys/fs/bpf/softirq_map1
5: hash name iptr flags 0x0
key 4B value 8B max_entries 10240 memlock 921600B
root@test# ./bpftool --json --pretty map
[{
"id": 4,
"type": "hash",
"name": "start",
"flags": 0,
"bytes_key": 4,
"bytes_value": 16,
"max_entries": 10240,
"bytes_memlock": 1003520,
"pinned": ["/sys/fs/bpf/softirq_map1"
]
},{
"id": 5,
"type": "hash",
"name": "iptr",
"flags": 0,
"bytes_key": 4,
"bytes_value": 8,
"max_entries": 10240,
"bytes_memlock": 921600,
"pinned": []
}
]
Signed-off-by: Prashant Bhole <bhole_prashant_q7@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-11-08 13:55:48 +09:00
if ( ! hash_empty ( prog_table . table ) ) {
struct pinned_obj * obj ;
jsonw_name ( json_wtr , " pinned " ) ;
jsonw_start_array ( json_wtr ) ;
hash_for_each_possible ( prog_table . table , obj , hash , info - > id ) {
if ( obj - > id = = info - > id )
jsonw_string ( json_wtr , obj - > path ) ;
}
jsonw_end_array ( json_wtr ) ;
}
2017-10-23 09:24:08 -07:00
jsonw_end_object ( json_wtr ) ;
}
static void print_prog_plain ( struct bpf_prog_info * info , int fd )
{
char * memlock ;
printf ( " %u: " , info - > id ) ;
if ( info - > type < ARRAY_SIZE ( prog_type_name ) )
printf ( " %s " , prog_type_name [ info - > type ] ) ;
2017-10-04 20:10:04 -07:00
else
2017-10-23 09:24:08 -07:00
printf ( " type %u " , info - > type ) ;
2017-10-04 20:10:04 -07:00
2017-10-23 09:24:08 -07:00
if ( * info - > name )
printf ( " name %s " , info - > name ) ;
2017-10-04 20:10:04 -07:00
printf ( " tag " ) ;
2017-10-23 09:24:08 -07:00
fprint_hex ( stdout , info - > tag , BPF_TAG_SIZE , " " ) ;
2017-10-04 20:10:04 -07:00
printf ( " \n " ) ;
2017-10-23 09:24:08 -07:00
if ( info - > load_time ) {
2017-10-04 20:10:04 -07:00
char buf [ 32 ] ;
2017-10-23 09:24:08 -07:00
print_boot_time ( info - > load_time , buf , sizeof ( buf ) ) ;
2017-10-04 20:10:04 -07:00
/* Piggy back on load_time, since 0 uid is a valid one */
2017-10-23 09:24:08 -07:00
printf ( " \t loaded_at %s uid %u \n " , buf , info - > created_by_uid ) ;
2017-10-04 20:10:04 -07:00
}
2017-10-23 09:24:08 -07:00
printf ( " \t xlated %uB " , info - > xlated_prog_len ) ;
2017-10-04 20:10:04 -07:00
2017-10-23 09:24:08 -07:00
if ( info - > jited_prog_len )
printf ( " jited %uB " , info - > jited_prog_len ) ;
2017-10-04 20:10:04 -07:00
else
printf ( " not jited " ) ;
memlock = get_fdinfo ( fd , " memlock " ) ;
if ( memlock )
printf ( " memlock %sB " , memlock ) ;
free ( memlock ) ;
2017-10-23 09:24:08 -07:00
if ( info - > nr_map_ids )
show_prog_maps ( fd , info - > nr_map_ids ) ;
2017-10-04 20:10:04 -07:00
tools: bpftool: show filenames of pinned objects
Added support to show filenames of pinned objects.
For example:
root@test# ./bpftool prog
3: tracepoint name tracepoint__irq tag f677a7dd722299a3
loaded_at Oct 26/11:39 uid 0
xlated 160B not jited memlock 4096B map_ids 4
pinned /sys/fs/bpf/softirq_prog
4: tracepoint name tracepoint__irq tag ea5dc530d00b92b6
loaded_at Oct 26/11:39 uid 0
xlated 392B not jited memlock 4096B map_ids 4,6
root@test# ./bpftool --json --pretty prog
[{
"id": 3,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "f677a7dd722299a3",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 160,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4
],
"pinned": ["/sys/fs/bpf/softirq_prog"
]
},{
"id": 4,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "ea5dc530d00b92b6",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 392,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4,6
],
"pinned": []
}
]
root@test# ./bpftool map
4: hash name start flags 0x0
key 4B value 16B max_entries 10240 memlock 1003520B
pinned /sys/fs/bpf/softirq_map1
5: hash name iptr flags 0x0
key 4B value 8B max_entries 10240 memlock 921600B
root@test# ./bpftool --json --pretty map
[{
"id": 4,
"type": "hash",
"name": "start",
"flags": 0,
"bytes_key": 4,
"bytes_value": 16,
"max_entries": 10240,
"bytes_memlock": 1003520,
"pinned": ["/sys/fs/bpf/softirq_map1"
]
},{
"id": 5,
"type": "hash",
"name": "iptr",
"flags": 0,
"bytes_key": 4,
"bytes_value": 8,
"max_entries": 10240,
"bytes_memlock": 921600,
"pinned": []
}
]
Signed-off-by: Prashant Bhole <bhole_prashant_q7@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-11-08 13:55:48 +09:00
if ( ! hash_empty ( prog_table . table ) ) {
struct pinned_obj * obj ;
printf ( " \n " ) ;
hash_for_each_possible ( prog_table . table , obj , hash , info - > id ) {
if ( obj - > id = = info - > id )
printf ( " \t pinned %s \n " , obj - > path ) ;
}
}
2017-10-04 20:10:04 -07:00
printf ( " \n " ) ;
2017-10-23 09:24:08 -07:00
}
static int show_prog ( int fd )
{
struct bpf_prog_info info = { } ;
__u32 len = sizeof ( info ) ;
int err ;
err = bpf_obj_get_info_by_fd ( fd , & info , & len ) ;
if ( err ) {
2017-10-23 09:24:13 -07:00
p_err ( " can't get prog info: %s " , strerror ( errno ) ) ;
2017-10-23 09:24:08 -07:00
return - 1 ;
}
if ( json_output )
print_prog_json ( & info , fd ) ;
else
print_prog_plain ( & info , fd ) ;
2017-10-04 20:10:04 -07:00
return 0 ;
}
static int do_show ( int argc , char * * argv )
2017-10-23 09:24:08 -07:00
{
__u32 id = 0 ;
2017-10-04 20:10:04 -07:00
int err ;
int fd ;
2017-11-08 13:55:49 +09:00
if ( show_pinned )
build_pinned_obj_table ( & prog_table , BPF_OBJ_PROG ) ;
tools: bpftool: show filenames of pinned objects
Added support to show filenames of pinned objects.
For example:
root@test# ./bpftool prog
3: tracepoint name tracepoint__irq tag f677a7dd722299a3
loaded_at Oct 26/11:39 uid 0
xlated 160B not jited memlock 4096B map_ids 4
pinned /sys/fs/bpf/softirq_prog
4: tracepoint name tracepoint__irq tag ea5dc530d00b92b6
loaded_at Oct 26/11:39 uid 0
xlated 392B not jited memlock 4096B map_ids 4,6
root@test# ./bpftool --json --pretty prog
[{
"id": 3,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "f677a7dd722299a3",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 160,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4
],
"pinned": ["/sys/fs/bpf/softirq_prog"
]
},{
"id": 4,
"type": "tracepoint",
"name": "tracepoint__irq",
"tag": "ea5dc530d00b92b6",
"loaded_at": "Oct 26/11:39",
"uid": 0,
"bytes_xlated": 392,
"jited": false,
"bytes_memlock": 4096,
"map_ids": [4,6
],
"pinned": []
}
]
root@test# ./bpftool map
4: hash name start flags 0x0
key 4B value 16B max_entries 10240 memlock 1003520B
pinned /sys/fs/bpf/softirq_map1
5: hash name iptr flags 0x0
key 4B value 8B max_entries 10240 memlock 921600B
root@test# ./bpftool --json --pretty map
[{
"id": 4,
"type": "hash",
"name": "start",
"flags": 0,
"bytes_key": 4,
"bytes_value": 16,
"max_entries": 10240,
"bytes_memlock": 1003520,
"pinned": ["/sys/fs/bpf/softirq_map1"
]
},{
"id": 5,
"type": "hash",
"name": "iptr",
"flags": 0,
"bytes_key": 4,
"bytes_value": 8,
"max_entries": 10240,
"bytes_memlock": 921600,
"pinned": []
}
]
Signed-off-by: Prashant Bhole <bhole_prashant_q7@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-11-08 13:55:48 +09:00
2017-10-04 20:10:04 -07:00
if ( argc = = 2 ) {
fd = prog_parse_fd ( & argc , & argv ) ;
if ( fd < 0 )
return - 1 ;
return show_prog ( fd ) ;
}
if ( argc )
return BAD_ARG ( ) ;
2017-10-23 09:24:08 -07:00
if ( json_output )
jsonw_start_array ( json_wtr ) ;
2017-10-04 20:10:04 -07:00
while ( true ) {
err = bpf_prog_get_next_id ( id , & id ) ;
if ( err ) {
2017-10-19 15:46:20 -07:00
if ( errno = = ENOENT ) {
err = 0 ;
2017-10-04 20:10:04 -07:00
break ;
2017-10-19 15:46:20 -07:00
}
2017-10-23 09:24:13 -07:00
p_err ( " can't get next program: %s%s " , strerror ( errno ) ,
errno = = EINVAL ? " -- kernel too old? " : " " ) ;
2017-10-23 09:24:08 -07:00
err = - 1 ;
break ;
2017-10-04 20:10:04 -07:00
}
fd = bpf_prog_get_fd_by_id ( id ) ;
if ( fd < 0 ) {
2017-10-23 09:24:13 -07:00
p_err ( " can't get prog by id (%u): %s " ,
id , strerror ( errno ) ) ;
2017-10-23 09:24:08 -07:00
err = - 1 ;
break ;
2017-10-04 20:10:04 -07:00
}
err = show_prog ( fd ) ;
close ( fd ) ;
if ( err )
2017-10-23 09:24:08 -07:00
break ;
2017-10-04 20:10:04 -07:00
}
2017-10-23 09:24:08 -07:00
if ( json_output )
jsonw_end_array ( json_wtr ) ;
return err ;
2017-10-04 20:10:04 -07:00
}
2017-10-09 10:30:13 -07:00
static void print_insn ( struct bpf_verifier_env * env , const char * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
vprintf ( fmt , args ) ;
va_end ( args ) ;
}
tools: bpftool: add JSON output for `bpftool prog dump xlated *` command
Add a new printing function to dump translated eBPF instructions as
JSON. As for plain output, opcodes are printed only on request (when
`opcodes` is provided on the command line).
The disassembled output is generated by the same code that is used by
the kernel verifier.
Example output:
$ bpftool --json --pretty prog dump xlated id 1
[{
"disasm": "(bf) r6 = r1"
},{
"disasm": "(61) r7 = *(u32 *)(r6 +16)"
},{
"disasm": "(95) exit"
}
]
$ bpftool --json --pretty prog dump xlated id 1 opcodes
[{
"disasm": "(bf) r6 = r1",
"opcodes": {
"code": "0xbf",
"src_reg": "0x1",
"dst_reg": "0x6",
"off": ["0x00","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
},{
"disasm": "(61) r7 = *(u32 *)(r6 +16)",
"opcodes": {
"code": "0x61",
"src_reg": "0x6",
"dst_reg": "0x7",
"off": ["0x10","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
},{
"disasm": "(95) exit",
"opcodes": {
"code": "0x95",
"src_reg": "0x0",
"dst_reg": "0x0",
"off": ["0x00","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
}
]
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-10-23 09:24:10 -07:00
static void dump_xlated_plain ( void * buf , unsigned int len , bool opcodes )
2017-10-09 10:30:13 -07:00
{
struct bpf_insn * insn = buf ;
2017-10-19 15:46:24 -07:00
bool double_insn = false ;
2017-10-09 10:30:13 -07:00
unsigned int i ;
for ( i = 0 ; i < len / sizeof ( * insn ) ; i + + ) {
2017-10-19 15:46:24 -07:00
if ( double_insn ) {
double_insn = false ;
continue ;
}
double_insn = insn [ i ] . code = = ( BPF_LD | BPF_IMM | BPF_DW ) ;
2017-10-09 10:30:13 -07:00
printf ( " % 4d: " , i ) ;
print_bpf_insn ( print_insn , NULL , insn + i , true ) ;
if ( opcodes ) {
printf ( " " ) ;
2017-10-19 15:46:19 -07:00
fprint_hex ( stdout , insn + i , 8 , " " ) ;
2017-10-19 15:46:24 -07:00
if ( double_insn & & i < len - 1 ) {
printf ( " " ) ;
fprint_hex ( stdout , insn + i + 1 , 8 , " " ) ;
}
2017-10-09 10:30:13 -07:00
printf ( " \n " ) ;
}
}
}
tools: bpftool: add JSON output for `bpftool prog dump xlated *` command
Add a new printing function to dump translated eBPF instructions as
JSON. As for plain output, opcodes are printed only on request (when
`opcodes` is provided on the command line).
The disassembled output is generated by the same code that is used by
the kernel verifier.
Example output:
$ bpftool --json --pretty prog dump xlated id 1
[{
"disasm": "(bf) r6 = r1"
},{
"disasm": "(61) r7 = *(u32 *)(r6 +16)"
},{
"disasm": "(95) exit"
}
]
$ bpftool --json --pretty prog dump xlated id 1 opcodes
[{
"disasm": "(bf) r6 = r1",
"opcodes": {
"code": "0xbf",
"src_reg": "0x1",
"dst_reg": "0x6",
"off": ["0x00","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
},{
"disasm": "(61) r7 = *(u32 *)(r6 +16)",
"opcodes": {
"code": "0x61",
"src_reg": "0x6",
"dst_reg": "0x7",
"off": ["0x10","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
},{
"disasm": "(95) exit",
"opcodes": {
"code": "0x95",
"src_reg": "0x0",
"dst_reg": "0x0",
"off": ["0x00","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
}
]
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-10-23 09:24:10 -07:00
static void print_insn_json ( struct bpf_verifier_env * env , const char * fmt , . . . )
{
unsigned int l = strlen ( fmt ) ;
char chomped_fmt [ l ] ;
va_list args ;
va_start ( args , fmt ) ;
if ( l > 0 ) {
strncpy ( chomped_fmt , fmt , l - 1 ) ;
chomped_fmt [ l - 1 ] = ' \0 ' ;
}
jsonw_vprintf_enquote ( json_wtr , chomped_fmt , args ) ;
va_end ( args ) ;
}
static void dump_xlated_json ( void * buf , unsigned int len , bool opcodes )
{
struct bpf_insn * insn = buf ;
bool double_insn = false ;
unsigned int i ;
jsonw_start_array ( json_wtr ) ;
for ( i = 0 ; i < len / sizeof ( * insn ) ; i + + ) {
if ( double_insn ) {
double_insn = false ;
continue ;
}
double_insn = insn [ i ] . code = = ( BPF_LD | BPF_IMM | BPF_DW ) ;
jsonw_start_object ( json_wtr ) ;
jsonw_name ( json_wtr , " disasm " ) ;
print_bpf_insn ( print_insn_json , NULL , insn + i , true ) ;
if ( opcodes ) {
jsonw_name ( json_wtr , " opcodes " ) ;
jsonw_start_object ( json_wtr ) ;
jsonw_name ( json_wtr , " code " ) ;
jsonw_printf ( json_wtr , " \" 0x%02hhx \" " , insn [ i ] . code ) ;
jsonw_name ( json_wtr , " src_reg " ) ;
jsonw_printf ( json_wtr , " \" 0x%hhx \" " , insn [ i ] . src_reg ) ;
jsonw_name ( json_wtr , " dst_reg " ) ;
jsonw_printf ( json_wtr , " \" 0x%hhx \" " , insn [ i ] . dst_reg ) ;
jsonw_name ( json_wtr , " off " ) ;
print_hex_data_json ( ( uint8_t * ) ( & insn [ i ] . off ) , 2 ) ;
jsonw_name ( json_wtr , " imm " ) ;
if ( double_insn & & i < len - 1 )
print_hex_data_json ( ( uint8_t * ) ( & insn [ i ] . imm ) ,
12 ) ;
else
print_hex_data_json ( ( uint8_t * ) ( & insn [ i ] . imm ) ,
4 ) ;
jsonw_end_object ( json_wtr ) ;
}
jsonw_end_object ( json_wtr ) ;
}
jsonw_end_array ( json_wtr ) ;
}
2017-10-04 20:10:04 -07:00
static int do_dump ( int argc , char * * argv )
{
struct bpf_prog_info info = { } ;
__u32 len = sizeof ( info ) ;
unsigned int buf_size ;
char * filepath = NULL ;
bool opcodes = false ;
unsigned char * buf ;
__u32 * member_len ;
__u64 * member_ptr ;
ssize_t n ;
int err ;
int fd ;
if ( is_prefix ( * argv , " jited " ) ) {
member_len = & info . jited_prog_len ;
member_ptr = & info . jited_prog_insns ;
} else if ( is_prefix ( * argv , " xlated " ) ) {
member_len = & info . xlated_prog_len ;
member_ptr = & info . xlated_prog_insns ;
} else {
2017-10-23 09:24:13 -07:00
p_err ( " expected 'xlated' or 'jited', got: %s " , * argv ) ;
2017-10-04 20:10:04 -07:00
return - 1 ;
}
NEXT_ARG ( ) ;
if ( argc < 2 )
usage ( ) ;
fd = prog_parse_fd ( & argc , & argv ) ;
if ( fd < 0 )
return - 1 ;
if ( is_prefix ( * argv , " file " ) ) {
NEXT_ARG ( ) ;
if ( ! argc ) {
2017-10-23 09:24:13 -07:00
p_err ( " expected file path " ) ;
2017-10-04 20:10:04 -07:00
return - 1 ;
}
filepath = * argv ;
NEXT_ARG ( ) ;
} else if ( is_prefix ( * argv , " opcodes " ) ) {
opcodes = true ;
NEXT_ARG ( ) ;
}
if ( argc ) {
usage ( ) ;
return - 1 ;
}
err = bpf_obj_get_info_by_fd ( fd , & info , & len ) ;
if ( err ) {
2017-10-23 09:24:13 -07:00
p_err ( " can't get prog info: %s " , strerror ( errno ) ) ;
2017-10-04 20:10:04 -07:00
return - 1 ;
}
if ( ! * member_len ) {
2017-10-23 09:24:13 -07:00
p_info ( " no instructions returned " ) ;
2017-10-04 20:10:04 -07:00
close ( fd ) ;
return 0 ;
}
buf_size = * member_len ;
buf = malloc ( buf_size ) ;
if ( ! buf ) {
2017-10-23 09:24:13 -07:00
p_err ( " mem alloc failed " ) ;
2017-10-04 20:10:04 -07:00
close ( fd ) ;
return - 1 ;
}
memset ( & info , 0 , sizeof ( info ) ) ;
* member_ptr = ptr_to_u64 ( buf ) ;
* member_len = buf_size ;
err = bpf_obj_get_info_by_fd ( fd , & info , & len ) ;
close ( fd ) ;
if ( err ) {
2017-10-23 09:24:13 -07:00
p_err ( " can't get prog info: %s " , strerror ( errno ) ) ;
2017-10-04 20:10:04 -07:00
goto err_free ;
}
if ( * member_len > buf_size ) {
2017-10-23 09:24:13 -07:00
p_err ( " too many instructions returned " ) ;
2017-10-04 20:10:04 -07:00
goto err_free ;
}
if ( filepath ) {
fd = open ( filepath , O_WRONLY | O_CREAT | O_TRUNC , 0600 ) ;
if ( fd < 0 ) {
2017-10-23 09:24:13 -07:00
p_err ( " can't open file %s: %s " , filepath ,
strerror ( errno ) ) ;
2017-10-04 20:10:04 -07:00
goto err_free ;
}
n = write ( fd , buf , * member_len ) ;
close ( fd ) ;
if ( n ! = * member_len ) {
2017-10-23 09:24:13 -07:00
p_err ( " error writing output file: %s " ,
n < 0 ? strerror ( errno ) : " short write " ) ;
2017-10-04 20:10:04 -07:00
goto err_free ;
}
} else {
2017-10-09 10:30:13 -07:00
if ( member_len = = & info . jited_prog_len )
disasm_print_insn ( buf , * member_len , opcodes ) ;
else
tools: bpftool: add JSON output for `bpftool prog dump xlated *` command
Add a new printing function to dump translated eBPF instructions as
JSON. As for plain output, opcodes are printed only on request (when
`opcodes` is provided on the command line).
The disassembled output is generated by the same code that is used by
the kernel verifier.
Example output:
$ bpftool --json --pretty prog dump xlated id 1
[{
"disasm": "(bf) r6 = r1"
},{
"disasm": "(61) r7 = *(u32 *)(r6 +16)"
},{
"disasm": "(95) exit"
}
]
$ bpftool --json --pretty prog dump xlated id 1 opcodes
[{
"disasm": "(bf) r6 = r1",
"opcodes": {
"code": "0xbf",
"src_reg": "0x1",
"dst_reg": "0x6",
"off": ["0x00","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
},{
"disasm": "(61) r7 = *(u32 *)(r6 +16)",
"opcodes": {
"code": "0x61",
"src_reg": "0x6",
"dst_reg": "0x7",
"off": ["0x10","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
},{
"disasm": "(95) exit",
"opcodes": {
"code": "0x95",
"src_reg": "0x0",
"dst_reg": "0x0",
"off": ["0x00","0x00"
],
"imm": ["0x00","0x00","0x00","0x00"
]
}
}
]
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-10-23 09:24:10 -07:00
if ( json_output )
dump_xlated_json ( buf , * member_len , opcodes ) ;
else
dump_xlated_plain ( buf , * member_len , opcodes ) ;
2017-10-04 20:10:04 -07:00
}
free ( buf ) ;
return 0 ;
err_free :
free ( buf ) ;
return - 1 ;
}
static int do_pin ( int argc , char * * argv )
{
2017-10-23 09:24:14 -07:00
int err ;
err = do_pin_any ( argc , argv , bpf_prog_get_fd_by_id ) ;
if ( ! err & & json_output )
jsonw_null ( json_wtr ) ;
return err ;
2017-10-04 20:10:04 -07:00
}
static int do_help ( int argc , char * * argv )
{
2017-10-23 09:24:14 -07:00
if ( json_output ) {
jsonw_null ( json_wtr ) ;
return 0 ;
}
2017-10-04 20:10:04 -07:00
fprintf ( stderr ,
" Usage: %s %s show [PROG] \n "
2017-10-19 15:46:25 -07:00
" %s %s dump xlated PROG [{ file FILE | opcodes }] \n "
" %s %s dump jited PROG [{ file FILE | opcodes }] \n "
2017-10-04 20:10:04 -07:00
" %s %s pin PROG FILE \n "
" %s %s help \n "
" \n "
" " HELP_SPEC_PROGRAM " \n "
2017-10-23 09:24:16 -07:00
" " HELP_SPEC_OPTIONS " \n "
2017-10-04 20:10:04 -07:00
" " ,
bin_name , argv [ - 2 ] , bin_name , argv [ - 2 ] , bin_name , argv [ - 2 ] ,
bin_name , argv [ - 2 ] , bin_name , argv [ - 2 ] ) ;
return 0 ;
}
static const struct cmd cmds [ ] = {
{ " show " , do_show } ,
2017-10-19 15:46:22 -07:00
{ " help " , do_help } ,
2017-10-04 20:10:04 -07:00
{ " dump " , do_dump } ,
{ " pin " , do_pin } ,
{ 0 }
} ;
int do_prog ( int argc , char * * argv )
{
return cmd_select ( cmds , argc , argv , do_help ) ;
}