-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BTF kernel code support #734
Conversation
I believe @anakryiko has been working on a tool that converts BTF to a standard C header file. I wonder if we could reuse any of that. Andrii, do you have any details you could share? |
@danobi, yes, it is already available as part of bpftool. You can just do:
to compilable .h file. As for the description of this change, I'm wondering why all those steps to dump .BTF section into separate file is needed? Why can't you just point to kernel image w/ .BTF and use libbpf to extract BTF data using existing API. No hassle and it should just work. |
cool, will check on that
there's not always kernel-debuginfo installed on the server and we plan but I'll change the code to check for vmlinux if it's available thanks, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partial review for files other than btf.cpp
# LIBBPF_LIBRARIES - Link these to use libbpf | ||
# LIBBPF_DEFINITIONS - Compiler switches required for using libbpf | ||
|
||
#if (LIBBPF_LIBRARIES AND LIBBPF_INCLUDE_DIRS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't claim to know what these lines are for in FindLibElf.cmake, but why are they commented out here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's so people can pass in paths via cmake -D
stuff. If we aren't gonna use it here we should delete it
tests/CMakeLists.txt
Outdated
@@ -27,6 +27,7 @@ add_executable(bpftrace_test | |||
${CMAKE_SOURCE_DIR}/src/tracepoint_format_parser.cpp | |||
${CMAKE_SOURCE_DIR}/src/types.cpp | |||
${CMAKE_SOURCE_DIR}/src/utils.cpp | |||
${CMAKE_SOURCE_DIR}/src/btf.cpp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep these in alphabetical order please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
src/CMakeLists.txt
Outdated
@@ -41,6 +42,12 @@ else() | |||
endif() | |||
target_link_libraries(bpftrace ${LIBELF_LIBRARIES}) | |||
|
|||
if (LIBBPF_FOUND AND NOT STATIC_LINKING) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there no static library version of libbpf? I'm not keen on the static bpftrace having fewer features than the dynamic one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is static version, I'll add it
src/ast/semantic_analyser.cpp
Outdated
* If we are using curtak in field access, retype it | ||
* to its original type: struct task_truct. | ||
*/ | ||
if (builtin.ident == "curtask" && builtin.is_field_access) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would people ever not want curtask
defined as a struct like this? I'd say always treat it as a struct task_struct *
and get rid of is_field_access
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, will check
tests/CMakeLists.txt
Outdated
@@ -43,6 +44,15 @@ if(HAVE_GET_CURRENT_CGROUP_ID) | |||
endif(HAVE_GET_CURRENT_CGROUP_ID) | |||
target_link_libraries(bpftrace_test arch ast parser resources) | |||
|
|||
find_library(BPF_LIB bpf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this covered by find_package(LibBpf)
in the base CMakeLists.txt
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops.. right, I'll remove it
It looks like libbpf in net-next has support for parsing BTF ( https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/tree/tools/bpf/bpftool/btf.c#n349 ). IMO (and also spoke w/ @anakryiko), it'd be better if we reused that code instead of writing our own parser. It looks like your PR directly constructs bpftrace struct info but I wonder if it'd be cleaner if we had libbpf dump the type info as a C header into a string buffer and then gave LLVM/clang the string buffer. |
One interesting guarantee for btf_dump__dump_type() from libbpf, which I'm not sure I emphasized anywhere, is that it will emit only requested types and all the types to ensure correct compilation, skipping everything else from BTF that's not referenced. So for bpftrace, if you already know which types are referenced from user program, then using btf_dump__dump_type() selectively will allow to produce minimal set of type definitions to satisfy build. This should improve debuggability, if anything goes wrong (less types to weed through), as well as generally speed up compilation, I'd imagine. |
cool, did not know about this.. will check, and repost, thanks |
Pushed out new version with following changes:
|
src/clang_parser.cpp
Outdated
if (!btf.has_data()) | ||
return true; | ||
|
||
// We need to process every type separately, because BTF::c_def |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm... from just briefly skimming and reading this comment, I think this is suboptimal.
It would be way more efficient to do btf_dump__new() once, then call btf_dump__dump_type() for each type you need, then compile resulting header file with all the definitions (it won't have duplicates). Creating and discarding btf_dump object for every single instance of referenced type is quite wasteful.
Also, keep in mind that btf__find_by_name() does a linear search across all types, so it might be more efficient to first create a set of type names you care about, then do single linear pass over all BTF types and dump those that match one of the name in a set. That way it's O(N) or O(NlogM) at most (if using sorted set) vs O(NM), where N is total number of BTF types, and M is number of types referenced from BPF program code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, will change
src/btf.cpp
Outdated
} | ||
|
||
btf = btf__new(data, (__u32) size); | ||
if (IS_ERR(btf)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the way it's supposed to be done with libbpf without (re-)defining your own IS_ERR/PTR_ERR, etc is to use libbpf_get_error() function. Like this:
int err;
btf = btf__new(...);
err = libbpf_get_error(btf);
if (err) {
// error handling
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then you can just drop MAX_ERRNO, IS_ERR, PTR_ERR, IS_ERR_VALUE definitions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, will change, thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new version pushed with review comments changes
e46f2a9
to
5b1ef77
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reviewed only btf_dump parts, haven't looked at anything else
src/btf.cpp
Outdated
int err = libbpf_get_error(btf); | ||
if (err) | ||
{ | ||
libbpf_strerror(err, err_buf, sizeof(err_buf)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reset btf to nullptr, otherwise destructor will attempt to free invalid pointer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, fixed
src/btf.cpp
Outdated
std::set<std::string> myset(set); | ||
__s32 id, max = (__s32) btf__get_nr_types(btf); | ||
|
||
for (id = 1; id < max && myset.size(); id++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
id <= max
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ugh.. should have checked bpf sources.. strange, fixed
src/btf.cpp
Outdated
{ | ||
const struct btf_type *t = btf__type_by_id(btf, id); | ||
|
||
if (t == NULL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should never happen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, fixed
src/btf.cpp
Outdated
return; | ||
} | ||
|
||
asprintf(&path, "/lib/modules/%s/build/vmlinux", uts.release); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, I'm working on a patch for the kernel that will expose kernel BTF through procfs or sysfs, so it will be a stable path, hopefully making this searching of kernel BTF more reliable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool, also it'll be available without debuginfo package,
could you please CC me on that kernel change, thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, will do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me from libbpf/BTF standpoint. I'm not a big fan of using global set for collecting resolved types, but I'll let people more familiar with bpftrace to comment on that.
Thanks!
src/btf.cpp
Outdated
return; | ||
} | ||
|
||
asprintf(&path, "/lib/modules/%s/build/vmlinux", uts.release); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, will do
@ajor @mmarchini any comments on this one? thanks |
ping ;-) @ajor @mmarchini @brendangregg thanks |
FYI I'll push new version with support to check and read /sys/kernel/btf/vmlinux |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some small comments but overall I really like how simple this is.
Didn't look too close at the libbpf stuff. Assuming @anakryiko took a good look at it.
src/ast/ast.cpp
Outdated
@@ -5,6 +5,8 @@ | |||
namespace bpftrace { | |||
namespace ast { | |||
|
|||
std::set<std::string> Expression::resolve = std::set<std::string>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can lead to SIOF issues if another static method / initializer uses this symbol. Instead, it's much safer to do a meyer's singleton. ie
ast.h
class Expression {
...
static std::set<std::string>& getResolve();
...
};
ast.cpp
std::set<std::string>& Expression::getResolve() {
static std::set<std::string> s;
return s;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, will change
src/ast/semantic_analyser.cpp
Outdated
/* | ||
* Retype curtask to its original type: struct task_truct. | ||
*/ | ||
if (builtin.ident == "curtask") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably split this out into its own else if
branch. It looks like curtask
is difference enough from the other things in this branch now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
src/clang_parser.h
Outdated
@@ -30,6 +33,8 @@ class ClangParser | |||
unsigned num_unsaved_files, | |||
unsigned options); | |||
|
|||
bool check_diagnostics(std::string& input); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't look like input
is being modified
bool check_diagnostics(std::string& input); | |
bool check_diagnostics(const std::string& input); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, will use const in here
tests/btf_data.h
Outdated
@@ -0,0 +1,48 @@ | |||
#pragma once | |||
|
|||
#if 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not a comment instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
habit ;-) I'll change for /* ... */
tests/btf_data.h
Outdated
0x6f, 0x32, 0x00, 0x69, 0x6e, 0x74, 0x00, 0x6c, 0x6f, 0x6e, 0x67, 0x20, | ||
0x69, 0x6e, 0x74, 0x00 | ||
}; | ||
unsigned int btf_data_len = 268; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be better so you don't have to count the number of elements
unsigned int btf_data_len = 268; | |
unsigned int btf_data_len = sizeof(btf_data) / sizeof(btf_data[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
src/btf.cpp
Outdated
return vfprintf(stderr, msg, ap); | ||
} | ||
|
||
struct btf_location { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you put this at the top of this file so it's easier to spot?
Also, can you document what the raw
field means? Not very clear from reading the struct def
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do
src/btf.cpp
Outdated
struct stat st; | ||
|
||
if (stat(file, &st)) | ||
return NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And also for the rest of this file
return NULL; | |
return nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
src/btf.cpp
Outdated
const struct btf_type *t = btf__type_by_id(btf, id); | ||
const char *str = btf_str(btf, t->name_off); | ||
|
||
std::set<std::string>::iterator it = myset.find(str); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::set<std::string>::iterator it = myset.find(str); | |
auto it = myset.find(str); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
src/ast/ast.h
Outdated
@@ -5,6 +5,7 @@ | |||
#include <map> | |||
#include <string> | |||
#include <vector> | |||
#include <set> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer unordered_set. It's faster and I don't think we need the ordering, right?
#include <set> | |
#include <unordered_set> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, no order is needed here
{ | ||
// 'borrowed' from libbpf's bpf_core_find_kernel_btf | ||
// from Andrii Nakryiko | ||
struct btf_location locs_normal[] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use std::array
instead. It knows its own size so you can do a rangeed for loop over it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, will check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on the other hand I need to know size of the std::array when
defining it, so it seems more convenient to me to use C array
in here.. however if you want to stick with c++ objects I understand
and can change it, let me know
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's kinda annoying. I think they're trying to add support for it in newer c++: https://en.cppreference.com/w/cpp/experimental/make_array . We can use that then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops marked as approved by accident
thanks for review, I'll push new version |
Adding default cast for curtask builtin expressions, so we don't need to cast it explicitly like in following script: # cat pid.bt #include <linux/sched.h> kprobe:do_dentry_open { printf("pid %d\n", curtask->pid); } Before: # bpftrace pid.bt Can not access field 'pid' on expression of type 'integer' printf: %d specifier expects a value of type integer (none supplied) After: # bpftrace pid.bt Attaching 1 probe... pid 4109 ...
Adding Expression::resolve string unordered set to keep a list of strings that need to be resolved. It will be used in following patches as a list to resolve with BTF data. It's static set so it's cleared in the Driver constructor, which is the starting point for parsing.
It will be detected on usual place, pluys user can specify non standard libbpf place (like /opt/libbpf/ below) with: $ cmake ... -DLIBBPF_INCLUDE_DIRS=/opt/libbpf/include \ -DLIBBPF_LIBRARIES=/opt/libbpf/lib64/libbpf.so Adding detection of btf_dump__new symbol, which is needed in following changes. If found the LIBBPF_BTF_DUMP_FOUND variable is set to TRUE.
Adding BTF class with constructor that checks on available BTF data in /sys/kernel/btf/vmlinux plus othe standard locations for vmlinux debuginfo. The list is 'borrowed' from libbpf's bpf_core_find_kernel_btf code from Andrii Nakryiko. If BTF data is loaded the BTF::has_data function returns true. Also linking bpftrace with libbpf.
Adding c_def function to BTF class that dumps C code definitions for given types set.
Adding ClangParser::visit_children function to contains clang_visitChildren call, so it can be used from multiple places.
Adding ClangParserHandler::check_diagnostics function to contain diagnostic messages processing, so it can be used from multiple places.
Adding SizedType::operator!= function, to ease up next changes.
Adding ClangParser::parse_btf_definitions function to load BTF definitions before parsing the user specified includes. Adding also check and warning in case those would collide.
Currently we always load BTF data before parsing user specified includes. Assuming that using include definitions and BTF data are mostly mutually exclusive, this change limits the BTF data processing to following cases: - there are no includes specified by user, or - the --btf option is specified
Adding BTF test to clang_parser modules with generated BTF data.
@danobi could you please check on the latest version? thanks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks! This is a great addition to bpftrace
Adding support to use BTF kernel data to access kernel fields.
This is a follow up on recent RFC pull request addressing the
build issues and adding automated tests.
The bpftrace now detects BTF data and if present it will use it
to acess kernel related fields. With this patchset it's possible to
run following:
The bpftrace build now detects libbpf devel package (present in Fedora 30),
and enables the btf code accordingly. It's switched off for static build now.
To use it, you still need to generate BTF kernel data manualy. I plan for
Fedora kernel to package this soon, but for now, you need to do following:
CONFIG_DEBUG_INFO_BTF=y
and build new kernel$ objcopy --dump-section .BTF=./btf ./vmlinux
/lib/modules/``uname -r``/btf
or run bpftrace with
BPFTRACE_BTF=./btf bpftrace ...