[llvm-dev] llvm bpf debug info. Re: [RFC PATCH v4 3/3] bpf: Introduce function for outputing data to perf event

Wangnan (F) via llvm-dev llvm-dev at lists.llvm.org
Tue Aug 11 22:28:49 PDT 2015

On 2015/8/12 12:57, Alexei Starovoitov wrote:
> On Wed, Aug 12, 2015 at 10:34:43AM +0800, Wangnan (F) via llvm-dev wrote:
>> Think about a program like this:
>> struct strA { int a; }
>> struct strB { int b; }
>> int func() {
>>    struct strA a;
>>    struct strB b;
>>    a.a = 1;
>>    b.b = 2;
>>    bpf_output(gettype(a), &a);
>>    bpf_output(gettype(b), &b);
>>    return 0;
>> }
>> BPF backend can't (and needn't) tell the difference between local
>> variables a and b in theory. In LLVM implementation, it filters type
>> information out using ComputeValueVTs().  Please have a look at
>> SelectionDAGBuilder::visitIntrinsicCall in
>> lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp and
>> SelectionDAGBuilder::visitTargetIntrinsic in the same file. in
>> visitTargetIntrinsic, ComputeValueVTs acts as a barrier which strips
>> type information out from CallInst ("I"), and leave SDValue and SDVTList
>> ("Ops" and "VTs") to target code. SDValue and SDVTList are wrappers of
>> EVT and MVT, all information we concern won't be passed here.
>> I think now we have 2 choices:
>> 1. Hacking into clang, implement target specific builtin function. Now I
>>     have worked out a ugly but workable patch which setup a builtin function:
>>     __builtin_bpf_typeid(), which accepts local or global variable then
>>     returns different constant for different types.
>> 2. Implementing an LLVM intrinsic call (llvm.typeid), make it be processed
>> in
>>     visitIntrinsicCall(). I think we can get something useful if it is
>> processed
>>     with that function.
> Yeah. You're right about pure target intrinsics.
> I think llvm.typeid might work. imo it's cleaner than
> doing it at clang level.
>> The next thing should be generating debug information to map type and
>> constants which issued by __builtin_bpf_typeid() or llvm.typeid. Now we
>> have a crazy idea that, if we limit the name of the structure to 8 bytes,
>> we can insert the name into a u64, then there would be no need to consider
>> type information in DWARF. For example, in the above sample code, gettype(a)
>> will issue 0x0000000041727473 because its type is "strA". What do you think?
> that's way too hacky.
> I was thinking when compiling we can keep llvm ir along with .o
> instead of dwarf and extract type info from there.
> dwarf has names and other things that we don't need. We only
> care about actual field layout of the structs.
> But it probably won't be easy to parse llvm ir on perf side
> instead of dwarf.

Shipping both llvm IR and .o to perf makes it harder to use. I'm
not sure whether it is a good idea. If we are unable to encode the
structure using a u64, let's still dig into dwarf.

We have another idea that we can utilize dwarf's existing feature.
For example, when __buildin_bpf_typeid() get called, define an enumerate
type in dwarf info, so you'll find:

  <1><2a>: Abbrev Number: 2 (DW_TAG_enumeration_type)
     <2b>   DW_AT_name        : (indirect string, offset: 0xec): TYPEINFO
     <2f>   DW_AT_byte_size   : 4
     <30>   DW_AT_decl_file   : 1
     <31>   DW_AT_decl_line   : 3
  <2><32>: Abbrev Number: 3 (DW_TAG_enumerator)
     <33>   DW_AT_name        : (indirect string, offset: 0xcc): 
     <37>   DW_AT_const_value : 2
  <2><38>: Abbrev Number: 3 (DW_TAG_enumerator)
     <39>   DW_AT_name        : (indirect string, offset: 0xdc): 
     <3d>   DW_AT_const_value : 3

or this:

  <3><54>: Abbrev Number: 4 (DW_TAG_variable)
     <55>   DW_AT_const_value : 2
     <66>   DW_AT_name        : (indirect string, offset: 0x1e): 
     <6a>   DW_AT_decl_file   : 1
     <6b>   DW_AT_decl_line   : 29
     <6c>   DW_AT_type        : <0x72>

then from DW_AT_name and DW_AT_const_value we can do the mapping. 
Drawback is that
all __typeinfo_ prefixed names become reserved.

> btw, if you haven't looked at iovisor/bcc, there we're solving
> similar problem differently. There we use clang rewriter, so all
> structs fields are visible at this level, then we use bpf backend
> in JIT mode and push bpf instructions into the kernel on the fly
> completely skipping ELF and .o
> For example in:
> https://github.com/iovisor/bcc/blob/master/examples/distributed_bridge/tunnel.c
> when you see
> struct ethernet_t {
>    unsigned long long  dst:48;
>    unsigned long long  src:48;
>    unsigned int        type:16;
> struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
> ... ethernet->src ...
> is recognized by clang rewriter and ->src is converted to a different
> C code that is sent again into clang.
> So there is no need to use dwarf or patch clang/llvm. clang rewriter
> has all the info.

Could you please give us further information about your clang rewriter?
I guess you need a new .so when injecting those code into kernel?

> I'm not sure you can live with clang/llvm on the host where you
> want to run the tracing bits, but if you can that's an easier option.

I'm not sure. Our target platform should be embedded devices like 
Bringing full clang/llvm environment there is not acceptable.

Thank you.

More information about the llvm-dev mailing list