Hi,
This is v2. There are two key changes which mostly affect patches 2 and 3.
First, we have two BTF outputs:
1. when -j or -p switches are supplied to a map command - this is json- and
backward- compatible
2. when neither of -j and -p is supplied - this makes no promises on json- or
backward- compatibility, and aimed for humans
Second, in addition to map dump command, map lookup command has also been
updated to print data with btf. The rules around -j and -p are same as above.
Here is a summary of changes in v2:
patch 1:
- line continuation alignment fixes + other style fixes
patch 2:
- introduce struct btf_dumper which contains context for btf_dumper operation
- line continuation alignment fixes + other style fixes
- fix SPDX licence comment style to be C++ style
- reverse christmas tree style comments
- in btf_dumper_array() ensure we end json_writer array in case of error
patch 3:
- btf output for humans is shown when neither -j nor -p is supplied
- when -j or -p are supplied, augment output with "formatted" object which shows btf data in json
- added btf output to map lookup command also
- declarations to follow reverse christmas tree style
- error message grammar fix and remove full stop
- line continuation alignment fixes + other style fixes
- reorganise do_dump_btf() to remove goto and make it clearer
- remove misleading comment about end of root json object
- add comment to explain allocation btf buffer
- brackets around else clause to harmonise with braces on if clause
Thanks,
Okash
This consumes functionality exported in the previous patch. It does the
main job of printing with BTF data. This is used in the following patch
to provide a more readable output of a map's dump. It relies on
json_writer to do json printing. Below is sample output where map keys
are ints and values are of type struct A:
typedef int int_type;
enum E {
E0,
E1,
};
struct B {
int x;
int y;
};
struct A {
int m;
unsigned long long n;
char o;
int p[8];
int q[4][8];
enum E r;
void *s;
struct B t;
const int u;
int_type v;
unsigned int w1: 3;
unsigned int w2: 3;
};
$ sudo bpftool map dump id 14
[{
"key": 0,
"value": {
"m": 1,
"n": 2,
"o": "c",
"p": [15,16,17,18,15,16,17,18
],
"q": [[25,26,27,28,25,26,27,28
],[35,36,37,38,35,36,37,38
],[45,46,47,48,45,46,47,48
],[55,56,57,58,55,56,57,58
]
],
"r": 1,
"s": 0x7ffd80531cf8,
"t": {
"x": 5,
"y": 10
},
"u": 100,
"v": 20,
"w1": 0x7,
"w2": 0x3
}
}
]
This patch uses json's {} and [] to imply struct/union and array. More
explicit information can be added later. For example, a command line
option can be introduced to print whether a key or value is struct
or union, name of a struct etc. This will however come at the expense
of duplicating info when, for example, printing an array of structs.
enums are printed as ints without their names.
Signed-off-by: Okash Khawaja <redacted>
---
tools/bpf/bpftool/btf_dumper.c | 263 +++++++++++++++++++++++++++++++++++++++++
tools/bpf/bpftool/btf_dumper.h | 23 +++
2 files changed, 286 insertions(+)
--- /dev/null+++ b/tools/bpf/bpftool/btf_dumper.c
@@ -0,0 +1,263 @@+// SPDX-License-Identifier: GPL-2.0+/* Copyright (c) 2018 Facebook */++#include<linux/btf.h>+#include<linux/err.h>+#include<stdio.h> /* for (FILE *) used by json_writer */+#include<linux/bitops.h>+#include<string.h>+#include<ctype.h>++#include"btf.h"+#include"json_writer.h"+#include"btf_dumper.h"++#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1)+#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK)+#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3)+#define BITS_ROUNDUP_BYTES(bits) \+(BITS_ROUNDDOWN_BYTES(bits)+!!BITS_PER_BYTE_MASKED(bits))++staticintbtf_dumper_do_type(conststructbtf_dumper*d,uint32_ttype_id,+uint8_tbit_offset,constvoid*data);++staticvoidbtf_dumper_ptr(constvoid*data,json_writer_t*jw,+boolis_plain_text)+{+if(is_plain_text)+jsonw_printf(jw,"%p",*((uintptr_t*)data));+else+jsonw_printf(jw,"%u",*((uintptr_t*)data));+}++staticintbtf_dumper_modifier(conststructbtf_dumper*d,uint32_ttype_id,+constvoid*data)+{+int32_tactual_type_id=btf__resolve_type(d->btf,type_id);+intret;++if(actual_type_id<0)+returnactual_type_id;++ret=btf_dumper_do_type(d,actual_type_id,0,data);++returnret;+}++staticvoidbtf_dumper_enum(constvoid*data,json_writer_t*jw)+{+jsonw_printf(jw,"%d",*((int32_t*)data));+}++staticintbtf_dumper_array(conststructbtf_dumper*d,uint32_ttype_id,+constvoid*data)+{+conststructbtf_type*t=btf__type_by_id(d->btf,type_id);+structbtf_array*arr=(structbtf_array*)(t+1);+int64_telem_size;+intret=0;+uint32_ti;++elem_size=btf__resolve_size(d->btf,arr->type);+if(elem_size<0)+returnelem_size;++jsonw_start_array(d->jw);+for(i=0;i<arr->nelems;i++){+ret=btf_dumper_do_type(d,arr->type,0,+data+(i*elem_size));+if(ret)+break;+}++jsonw_end_array(d->jw);+returnret;+}++staticvoidbtf_dumper_int_bits(uint32_tint_type,uint8_tbit_offset,+constvoid*data,json_writer_t*jw,+boolis_plain_text)+{+uint32_tbits=BTF_INT_BITS(int_type);+uint16_ttotal_bits_offset;+uint16_tbytes_to_copy;+uint16_tbits_to_copy;+uint8_tupper_bits;+union{+uint64_tu64_num;+uint8_tu8_nums[8];+}print_num;++total_bits_offset=bit_offset+BTF_INT_OFFSET(int_type);+data+=BITS_ROUNDDOWN_BYTES(total_bits_offset);+bit_offset=BITS_PER_BYTE_MASKED(total_bits_offset);+bits_to_copy=bits+bit_offset;+bytes_to_copy=BITS_ROUNDUP_BYTES(bits_to_copy);++print_num.u64_num=0;+memcpy(&print_num.u64_num,data,bytes_to_copy);++upper_bits=BITS_PER_BYTE_MASKED(bits_to_copy);+if(upper_bits){+uint8_tmask=(1<<upper_bits)-1;++print_num.u8_nums[bytes_to_copy-1]&=mask;+}++print_num.u64_num>>=bit_offset;++if(is_plain_text)+jsonw_printf(jw,"0x%llx",print_num.u64_num);+else+jsonw_printf(jw,"%llu",print_num.u64_num);+}++staticintbtf_dumper_int(conststructbtf_type*t,uint8_tbit_offset,+constvoid*data,json_writer_t*jw,+boolis_plain_text)+{+uint32_t*int_type=(uint32_t*)(t+1);+uint32_tbits=BTF_INT_BITS(*int_type);+intret=0;++/* if this is bit field */+if(bit_offset||BTF_INT_OFFSET(*int_type)||+BITS_PER_BYTE_MASKED(bits)){+btf_dumper_int_bits(*int_type,bit_offset,data,jw,+is_plain_text);+returnret;+}++switch(BTF_INT_ENCODING(*int_type)){+case0:+if(BTF_INT_BITS(*int_type)==64)+jsonw_printf(jw,"%lu",*((uint64_t*)data));+elseif(BTF_INT_BITS(*int_type)==32)+jsonw_printf(jw,"%u",*((uint32_t*)data));+elseif(BTF_INT_BITS(*int_type)==16)+jsonw_printf(jw,"%hu",*((uint16_t*)data));+elseif(BTF_INT_BITS(*int_type)==8)+jsonw_printf(jw,"%hhu",*((uint8_t*)data));+else+btf_dumper_int_bits(*int_type,bit_offset,data,jw,+is_plain_text);+break;+caseBTF_INT_SIGNED:+if(BTF_INT_BITS(*int_type)==64)+jsonw_printf(jw,"%ld",*((int64_t*)data));+elseif(BTF_INT_BITS(*int_type)==32)+jsonw_printf(jw,"%d",*((int32_t*)data));+elseif(BTF_INT_BITS(*int_type)==16)+jsonw_printf(jw,"%hd",*((int16_t*)data));+elseif(BTF_INT_BITS(*int_type)==8)+jsonw_printf(jw,"%hhd",*((int8_t*)data));+else+btf_dumper_int_bits(*int_type,bit_offset,data,jw,+is_plain_text);+break;+caseBTF_INT_CHAR:+if(*((char*)data)=='\0')+jsonw_null(jw);+elseif(isprint(*((char*)data)))+jsonw_printf(jw,"\"%c\"",*((char*)data));+else+if(is_plain_text)+jsonw_printf(jw,"%hhx",*((char*)data));+else+jsonw_printf(jw,"%hhd",*((char*)data));+break;+caseBTF_INT_BOOL:+jsonw_bool(jw,*((int*)data));+break;+default:+/* shouldn't happen */+ret=-EINVAL;+break;+}++returnret;+}++staticintbtf_dumper_struct(conststructbtf_dumper*d,uint32_ttype_id,+constvoid*data)+{+conststructbtf_type*t=btf__type_by_id(d->btf,type_id);+structbtf_member*m;+intret=0;+inti,vlen;++if(!t)+return-EINVAL;++vlen=BTF_INFO_VLEN(t->info);+jsonw_start_object(d->jw);+m=(structbtf_member*)(t+1);++for(i=0;i<vlen;i++){+jsonw_name(d->jw,btf__name_by_offset(d->btf,m[i].name_off));+ret=btf_dumper_do_type(d,m[i].type,+BITS_PER_BYTE_MASKED(m[i].offset),data++BITS_ROUNDDOWN_BYTES(m[i].offset));+if(ret)+returnret;+}++jsonw_end_object(d->jw);++return0;+}++staticintbtf_dumper_do_type(conststructbtf_dumper*d,uint32_ttype_id,+uint8_tbit_offset,constvoid*data)+{+conststructbtf_type*t=btf__type_by_id(d->btf,type_id);+intret=0;++switch(BTF_INFO_KIND(t->info)){+caseBTF_KIND_INT:+ret=btf_dumper_int(t,bit_offset,data,d->jw,+d->is_plain_text);+break;+caseBTF_KIND_STRUCT:+caseBTF_KIND_UNION:+ret=btf_dumper_struct(d,type_id,data);+break;+caseBTF_KIND_ARRAY:+ret=btf_dumper_array(d,type_id,data);+break;+caseBTF_KIND_ENUM:+btf_dumper_enum(data,d->jw);+break;+caseBTF_KIND_PTR:+btf_dumper_ptr(data,d->jw,d->is_plain_text);+break;+caseBTF_KIND_UNKN:+jsonw_printf(d->jw,"(unknown)");+break;+caseBTF_KIND_FWD:+/* map key or value can't be forward */+ret=-EINVAL;+break;+caseBTF_KIND_TYPEDEF:+caseBTF_KIND_VOLATILE:+caseBTF_KIND_CONST:+caseBTF_KIND_RESTRICT:+ret=btf_dumper_modifier(d,type_id,data);+break;+default:+jsonw_printf(d->jw,"(unsupported-kind");+ret=-EINVAL;+break;+}++returnret;+}++int32_tbtf_dumper_type(conststructbtf_dumper*d,uint32_ttype_id,+constvoid*data)+{+if(!d)+return-EINVAL;++returnbtf_dumper_do_type(d,type_id,0,data);+}---/dev/null+++b/tools/bpf/bpftool/btf_dumper.h
@@ -0,0 +1,23 @@+/* SPDX-License-Identifier: GPL-2.0 */+/* Copyright (c) 2018 Facebook */++#ifndef BTF_DUMPER_H+#define BTF_DUMPER_H++structbtf_dumper{+conststructbtf*btf;+json_writer_t*jw;+boolis_plain_text;+};++/* btf_dumper_type - print data along with type information+*@d:aninstancecontainingcontextfordumpingtypes+*@type_id:indexinbtf->typesarray.thispointstothetypetobedumped+*@data:pointertheactualdata,i.e.thevaluestobeprinted+*+*Returnszeroonsuccessandnegativeerrorcodeotherwise+*/+int32_tbtf_dumper_type(conststructbtf_dumper*d,uint32_ttype_id,+constvoid*data);++#endif
This patch augments the output of bpftool's map dump and map lookup
commands to print data along side btf info, if the correspondin btf
info is available. The outputs for each of map dump and map lookup
commands are augmented in two ways:
1. when neither of -j and -p are supplied, btf-ful map data is printed
whose aim is human readability. This means no commitments for json- or
backward- compatibility.
2. when either -j or -p are supplied, a new json object named
"formatted" is added for each key-value pair. This object contains the
same data as the key-value pair, but with btf info. "formatted" object
promises json- and backward- compatibility. Below is a sample output.
$ bpftool map dump -p id 8
[{
"key": ["0x0f","0x00","0x00","0x00"
],
"value": ["0x03", "0x00", "0x00", "0x00", ...
],
"formatted": {
"key": 15,
"value": {
"int_field": 3,
...
}
}
}
]
This patch calls btf_dumper introduced in previous patch to accomplish
the above. Indeed, btf-ful info is only displayed if btf data for the
given map is available. Otherwise existing output is displayed as-is.
Signed-off-by: Okash Khawaja <redacted>
---
tools/bpf/bpftool/map.c | 174 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 166 insertions(+), 8 deletions(-)
@@ -148,8 +152,99 @@ int map_parse_fd_and_info(int *argc, chareturnfd;}+staticintdo_dump_btf(conststructbtf_dumper*d,+structbpf_map_info*map_info,void*key,+void*value)+{+intret;++/* start of key-value pair */+jsonw_start_object(d->jw);++jsonw_name(d->jw,"key");++ret=btf_dumper_type(d,map_info->btf_key_type_id,key);+if(ret)+returnret;++jsonw_name(d->jw,"value");++ret=btf_dumper_type(d,map_info->btf_value_type_id,value);++/* end of key-value pair */+jsonw_end_object(d->jw);++returnret;+}++staticstructbtf*get_btf(structbpf_map_info*map_info)+{+intbtf_fd=bpf_btf_get_fd_by_id(map_info->btf_id);+structbpf_btf_infobtf_info={0};+__u32len=sizeof(btf_info);+void*ptr=NULL,*temp_ptr;+structbtf*btf=NULL;+uint32_tlast_size;+interr;++if(btf_fd<0)+returnNULL;++/* we won't know btf_size until we call bpf_obj_get_info_by_fd(). so+*let'sstartwithasanedefault-4KiBhere-andresizeitonlyif+*bpf_obj_get_info_by_fd()needsabiggerbuffer.thedo-whileloop+*belowshouldrunamaximumoftwoiterationsandthatwillbewhen+*wehavetoresizetoabiggerbuffer.+*/+btf_info.btf_size=4096;+do{+last_size=btf_info.btf_size;+temp_ptr=realloc(ptr,last_size);+if(!temp_ptr){+p_err("unable to allocate memory for debug info");+gotoexit_free;+}++ptr=temp_ptr;+bzero(ptr,last_size);+btf_info.btf=ptr_to_u64(ptr);+err=bpf_obj_get_info_by_fd(btf_fd,&btf_info,&len);+}while(!err&&btf_info.btf_size>last_size&&last_size==4096);++if(err||btf_info.btf_size>last_size){+p_info("can't get btf info. debug info won't be displayed. error: %s",+err?strerror(errno):"exceeds size retry");+gotoexit_free;+}++btf=btf__new((uint8_t*)btf_info.btf,+btf_info.btf_size,NULL);+if(IS_ERR(btf)){+printf("error when initialising btf: %s\n",+strerror(PTR_ERR(btf)));+btf=NULL;+}++exit_free:+close(btf_fd);+free(ptr);++returnbtf;+}++staticjson_writer_t*get_btf_writer(void)+{+json_writer_t*jw=jsonw_new(stdout);++if(!jw)+returnNULL;+jsonw_pretty(jw,true);++returnjw;+}+staticvoidprint_entry_json(structbpf_map_info*info,unsignedchar*key,-unsignedchar*value)+unsignedchar*value,structbtf*btf){jsonw_start_object(json_wtr);
@@ -508,10 +612,12 @@ static int do_show(int argc, char **argvstaticintdo_dump(intargc,char**argv){+structbpf_map_infoinfo={};void*key,*value,*prev_key;unsignedintnum_elems=0;-structbpf_map_infoinfo={};__u32len=sizeof(info);+json_writer_t*btf_wtr;+structbtf*btf=NULL;interr;intfd;
@@ -537,8 +643,22 @@ static int do_dump(int argc, char **argv}prev_key=NULL;++btf=get_btf(&info);if(json_output)jsonw_start_array(json_wtr);+else+if(btf){+btf_wtr=get_btf_writer();+if(!btf_wtr){+p_info("failed to create json writer for btf. falling back to plain output");+btf__free(btf);+btf=NULL;+}else{+jsonw_start_array(btf_wtr);+}+}+while(true){err=bpf_map_get_next_key(fd,prev_key,key);if(err){
@@ -549,9 +669,18 @@ static int do_dump(int argc, char **argvif(!bpf_map_lookup_elem(fd,key,value)){if(json_output)-print_entry_json(&info,key,value);+print_entry_json(&info,key,value,btf);else-print_entry_plain(&info,key,value);+if(btf){+structbtf_dumperd={+.btf=btf,+.jw=btf_wtr,+.is_plain_text=true,+};+do_dump_btf(&d,&info,key,value);+}else{+print_entry_plain(&info,key,value);+}}else{if(json_output){jsonw_name(json_wtr,"key");
@@ -637,6 +771,8 @@ static int do_lookup(int argc, char **ar{structbpf_map_infoinfo={};__u32len=sizeof(info);+json_writer_t*btf_wtr;+structbtf*btf=NULL;void*key,*value;interr;intfd;
@@ -662,10 +798,31 @@ static int do_lookup(int argc, char **arerr=bpf_map_lookup_elem(fd,key,value);if(!err){-if(json_output)-print_entry_json(&info,key,value);-else+btf=get_btf(&info);+if(json_output){+print_entry_json(&info,key,value,btf);+}elseif(btf){+/* if here json_wtr wouldn't have been initialised,+*solet'screateseparatewriterforbtf+*/+btf_wtr=get_btf_writer();+if(!btf_wtr){+p_info("failed to create json writer for btf. falling back to plain output");+btf__free(btf);+btf=NULL;+print_entry_plain(&info,key,value);+}else{+structbtf_dumperd={+.btf=btf,+.jw=btf_wtr,+.is_plain_text=true,+};+do_dump_btf(&d,&info,key,value);+jsonw_destroy(&btf_wtr);+}+}else{print_entry_plain(&info,key,value);+}}elseif(errno==ENOENT){if(json_output){jsonw_null(json_wtr);
From: Jakub Kicinski <hidden> Date: 2018-07-03 05:07:11
On Mon, 2 Jul 2018 11:39:15 -0700, Okash Khawaja wrote:
quoted hunk
This consumes functionality exported in the previous patch. It does the
main job of printing with BTF data. This is used in the following patch
to provide a more readable output of a map's dump. It relies on
json_writer to do json printing. Below is sample output where map keys
are ints and values are of type struct A:
typedef int int_type;
enum E {
E0,
E1,
};
struct B {
int x;
int y;
};
struct A {
int m;
unsigned long long n;
char o;
int p[8];
int q[4][8];
enum E r;
void *s;
struct B t;
const int u;
int_type v;
unsigned int w1: 3;
unsigned int w2: 3;
};
$ sudo bpftool map dump id 14
[{
"key": 0,
"value": {
"m": 1,
"n": 2,
"o": "c",
"p": [15,16,17,18,15,16,17,18
],
"q": [[25,26,27,28,25,26,27,28
],[35,36,37,38,35,36,37,38
],[45,46,47,48,45,46,47,48
],[55,56,57,58,55,56,57,58
]
],
"r": 1,
"s": 0x7ffd80531cf8,
"t": {
"x": 5,
"y": 10
},
"u": 100,
"v": 20,
"w1": 0x7,
"w2": 0x3
}
}
]
This patch uses json's {} and [] to imply struct/union and array. More
explicit information can be added later. For example, a command line
option can be introduced to print whether a key or value is struct
or union, name of a struct etc. This will however come at the expense
of duplicating info when, for example, printing an array of structs.
enums are printed as ints without their names.
Signed-off-by: Okash Khawaja <redacted>
---
tools/bpf/bpftool/btf_dumper.c | 263 +++++++++++++++++++++++++++++++++++++++++
tools/bpf/bpftool/btf_dumper.h | 23 +++
2 files changed, 286 insertions(+)
--- /dev/null+++ b/tools/bpf/bpftool/btf_dumper.c
@@ -0,0 +1,263 @@+// SPDX-License-Identifier: GPL-2.0+/* Copyright (c) 2018 Facebook */++#include<linux/btf.h>+#include<linux/err.h>+#include<stdio.h> /* for (FILE *) used by json_writer */+#include<linux/bitops.h>+#include<string.h>+#include<ctype.h>++#include"btf.h"+#include"json_writer.h"+#include"btf_dumper.h"
Please use normal int types for things which don't have to be
explicitly sized. Using explicitly sized variables is bad style,
and ALU operations other than on word or byte quantities are usually
slower on modern CPUs.
From: Jakub Kicinski <hidden> Date: 2018-07-03 05:29:27
On Mon, 2 Jul 2018 11:39:16 -0700, Okash Khawaja wrote:
quoted hunk
This patch augments the output of bpftool's map dump and map lookup
commands to print data along side btf info, if the correspondin btf
info is available. The outputs for each of map dump and map lookup
commands are augmented in two ways:
1. when neither of -j and -p are supplied, btf-ful map data is printed
whose aim is human readability. This means no commitments for json- or
backward- compatibility.
2. when either -j or -p are supplied, a new json object named
"formatted" is added for each key-value pair. This object contains the
same data as the key-value pair, but with btf info. "formatted" object
promises json- and backward- compatibility. Below is a sample output.
$ bpftool map dump -p id 8
[{
"key": ["0x0f","0x00","0x00","0x00"
],
"value": ["0x03", "0x00", "0x00", "0x00", ...
],
"formatted": {
"key": 15,
"value": {
"int_field": 3,
...
}
}
}
]
This patch calls btf_dumper introduced in previous patch to accomplish
the above. Indeed, btf-ful info is only displayed if btf data for the
given map is available. Otherwise existing output is displayed as-is.
Signed-off-by: Okash Khawaja <redacted>
---
tools/bpf/bpftool/map.c | 174 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 166 insertions(+), 8 deletions(-)
@@ -148,8 +152,99 @@ int map_parse_fd_and_info(int *argc, cha return fd; }+static int do_dump_btf(const struct btf_dumper *d,+ struct bpf_map_info *map_info, void *key,+ void *value)+{+ int ret;++ /* start of key-value pair */+ jsonw_start_object(d->jw);++ jsonw_name(d->jw, "key");++ ret = btf_dumper_type(d, map_info->btf_key_type_id, key);+ if (ret)+ return ret;
goto err_end_obj;
+ jsonw_name(d->jw, "value");
+
+ ret = btf_dumper_type(d, map_info->btf_value_type_id, value);
err_end_obj:
+ /* end of key-value pair */
+ jsonw_end_object(d->jw);
+
+ return ret;
+}
+
+static struct btf *get_btf(struct bpf_map_info *map_info)
+{
+ int btf_fd = bpf_btf_get_fd_by_id(map_info->btf_id);
No failing functions in initializers please.
+ struct bpf_btf_info btf_info = { 0 };
+ __u32 len = sizeof(btf_info);
+ void *ptr = NULL, *temp_ptr;
+ struct btf *btf = NULL;
+ uint32_t last_size;
+ int err;
+
+ if (btf_fd < 0)
+ return NULL;
+
+ /* we won't know btf_size until we call bpf_obj_get_info_by_fd(). so
+ * let's start with a sane default - 4KiB here - and resize it only if
+ * bpf_obj_get_info_by_fd() needs a bigger buffer. the do-while loop
+ * below should run a maximum of two iterations and that will be when
+ * we have to resize to a bigger buffer.
+ */
+ btf_info.btf_size = 4096;
+ do {
+ last_size = btf_info.btf_size;
+ temp_ptr = realloc(ptr, last_size);
+ if (!temp_ptr) {
+ p_err("unable to allocate memory for debug info");
+ goto exit_free;
+ }
+
+ ptr = temp_ptr;
+ bzero(ptr, last_size);
+ btf_info.btf = ptr_to_u64(ptr);
+ err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
+ } while (!err && btf_info.btf_size > last_size && last_size == 4096);
+
+ if (err || btf_info.btf_size > last_size) {
+ p_info("can't get btf info. debug info won't be displayed. error: %s",
+ err ? strerror(errno) : "exceeds size retry");
+ goto exit_free;
+ }
This "run me twice while handling realloc failure" loop seems very
unappealing. Could you just open code this?
+ btf = btf__new((uint8_t *)btf_info.btf,
+ btf_info.btf_size, NULL);
+ if (IS_ERR(btf)) {
+ printf("error when initialising btf: %s\n",
+ strerror(PTR_ERR(btf)));
@@ -637,6 +771,8 @@ static int do_lookup(int argc, char **ar { struct bpf_map_info info = {}; __u32 len = sizeof(info);+ json_writer_t *btf_wtr;+ struct btf *btf = NULL; void *key, *value; int err; int fd;
@@ -662,10 +798,31 @@ static int do_lookup(int argc, char **ar err = bpf_map_lookup_elem(fd, key, value); if (!err) {- if (json_output)- print_entry_json(&info, key, value);- else+ btf = get_btf(&info);+ if (json_output) {+ print_entry_json(&info, key, value, btf);+ } else if (btf) {+ /* if here json_wtr wouldn't have been initialised,+ * so let's create separate writer for btf+ */+ btf_wtr = get_btf_writer();+ if (!btf_wtr) {+ p_info("failed to create json writer for btf. falling back to plain output");+ btf__free(btf);+ btf = NULL;+ print_entry_plain(&info, key, value);+ } else {+ struct btf_dumper d = {+ .btf = btf,+ .jw = btf_wtr,+ .is_plain_text = true,+ };+ do_dump_btf(&d, &info, key, value);+ jsonw_destroy(&btf_wtr);+ }+ } else {
This is way too much code in a if (!err) branch.
Please refactor so that err = bpf_map_lookup_elem(fd, key, value); is
followed by error handling and the actual printing is non-indented.
quoted hunk
print_entry_plain(&info, key, value);
+ }
} else if (errno == ENOENT) {
if (json_output) {
jsonw_null(json_wtr);
Please use normal int types for things which don't have to be
explicitly sized. Using explicitly sized variables is bad style,
and ALU operations other than on word or byte quantities are usually
slower on modern CPUs.
... I think you need to always print a string, and express it as
\u00%02hhx for non-printable.
Okay that makes sense
Yeah, IDK, char can be used as a byte as well as a string. In eBPF
it may actually be more likely to just be used as a raw byte buffer...
Either way I think it may be nice to keep it consistent, at least for
the JSON output could we do either always ints or always characters?
... I think you need to always print a string, and express it as
\u00%02hhx for non-printable.
Okay that makes sense
Yeah, IDK, char can be used as a byte as well as a string. In eBPF
it may actually be more likely to just be used as a raw byte buffer...
Actually, what is the definition/purpose of BTF_INT_CHAR? There seems
to be no BTF_INT_SHORT and BTF_INT_SIGNED can simply be of size 8...
Is normal int only used for bitfields of size 8 and BTF_INT_CHAR for
char variables?
The kernel seems to be rejecting combinations of those flags, is
unsigned char going to not be marked as char then?
Either way I think it may be nice to keep it consistent, at least for
the JSON output could we do either always ints or always characters?
... I think you need to always print a string, and express it as
\u00%02hhx for non-printable.
Okay that makes sense
Yeah, IDK, char can be used as a byte as well as a string. In eBPF
it may actually be more likely to just be used as a raw byte buffer...
Actually, what is the definition/purpose of BTF_INT_CHAR? There seems
to be no BTF_INT_SHORT and BTF_INT_SIGNED can simply be of size 8...
Is normal int only used for bitfields of size 8 and BTF_INT_CHAR for
char variables?
The kernel seems to be rejecting combinations of those flags, is
unsigned char going to not be marked as char then?
BTF_INT_ENOCODING (CHAR/SIGNED/BOOL) is for formatting (e.g. pretty
print). It is mainly how CTF is using it also. Hence, BTF_INT_ENCODINGs
is not a 1:1 mapping to C integer types.
The size of an interger is described by BTF_INT_BITS instead.
quoted
Either way I think it may be nice to keep it consistent, at least for
the JSON output could we do either always ints or always characters?
... I think you need to always print a string, and express it as
\u00%02hhx for non-printable.
Okay that makes sense
Yeah, IDK, char can be used as a byte as well as a string. In eBPF
it may actually be more likely to just be used as a raw byte buffer...
Either way I think it may be nice to keep it consistent, at least for
the JSON output could we do either always ints or always characters?
yes, makes sense. i'll keep them always characters.
... I think you need to always print a string, and express it as
\u00%02hhx for non-printable.
Okay that makes sense
Yeah, IDK, char can be used as a byte as well as a string. In eBPF
it may actually be more likely to just be used as a raw byte buffer...
Actually, what is the definition/purpose of BTF_INT_CHAR? There seems
to be no BTF_INT_SHORT and BTF_INT_SIGNED can simply be of size 8...
Is normal int only used for bitfields of size 8 and BTF_INT_CHAR for
char variables?
The kernel seems to be rejecting combinations of those flags, is
unsigned char going to not be marked as char then?
BTF_INT_ENOCODING (CHAR/SIGNED/BOOL) is for formatting (e.g. pretty
print). It is mainly how CTF is using it also. Hence, BTF_INT_ENCODINGs
is not a 1:1 mapping to C integer types.
The size of an interger is described by BTF_INT_BITS instead.
quoted
quoted
Either way I think it may be nice to keep it consistent, at least for
the JSON output could we do either always ints or always characters?
for !isprint() case, will "\x%02hhx" make more sense?
... I think you need to always print a string, and express it as
\u00%02hhx for non-printable.
Okay that makes sense
Yeah, IDK, char can be used as a byte as well as a string. In eBPF
it may actually be more likely to just be used as a raw byte buffer...
Actually, what is the definition/purpose of BTF_INT_CHAR? There seems
to be no BTF_INT_SHORT and BTF_INT_SIGNED can simply be of size 8...
Is normal int only used for bitfields of size 8 and BTF_INT_CHAR for
char variables?
The kernel seems to be rejecting combinations of those flags, is
unsigned char going to not be marked as char then?
BTF_INT_ENOCODING (CHAR/SIGNED/BOOL) is for formatting (e.g. pretty
print). It is mainly how CTF is using it also. Hence, BTF_INT_ENCODINGs
is not a 1:1 mapping to C integer types.
The size of an interger is described by BTF_INT_BITS instead.
quoted
quoted
Either way I think it may be nice to keep it consistent, at least for
the JSON output could we do either always ints or always characters?
for !isprint() case, will "\x%02hhx" make more sense?
According to (quick look over) the JSON standard \x%02hhx is not a
valid escape sequence, everything has to be Unicode, so \u00%02hhx.
JSON validators online agree seem to reject \x as well.