Skip to content

Commit

Permalink
Add global vars which can be set from userspace
Browse files Browse the repository at this point in the history
This adds the ability to create const volatile global
variables in bpf and allow them to be updated in userspace
after program opening but before program loading.

This is useful for things like AOT, where we don't know
know the number of CPUs on the machine where the bpftrace
program will run at the time of codegen.
  • Loading branch information
Jordan Rome authored and jordalgo committed Jul 13, 2024
1 parent 6cc2798 commit 36c4b72
Show file tree
Hide file tree
Showing 38 changed files with 932 additions and 732 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 29,7 @@ add_library(runtime
disasm.cpp
dwarf_parser.cpp
format_string.cpp
globalvars.cpp
log.cpp
mapkey.cpp
output.cpp
Expand Down
2 changes: 1 addition & 1 deletion src/aot/aot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 341,7 @@ int load(BPFtrace &bpftrace, const std::string &in)

bpftrace.bytecode_ = BpfBytecode(btaot_section hdr->elf_off,
hdr->elf_len,
bpftrace.config_);
bpftrace);
if (err)
goto out;

Expand Down
9 changes: 0 additions & 9 deletions src/aot/aot_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 117,6 @@ int main(int argc, char* argv[])

BPFtrace bpftrace(std::move(output));

// TODO: remove this once we move to libbpf or move to open-coded iterators
auto num_cpus = bpftrace.get_num_possible_cpus();
if (num_cpus > 1024) {
LOG(WARNING) << "Detected " << num_cpus
<< " cpus. For ahead-of-time compilation there is a max of "
"1024 cpus so there may be incorrect data for 'count' "
"and 'sum' aggregations.";
}

int err = aot::load(bpftrace, argv[0]);
if (err) {
LOG(ERROR) << "Failed to load AOT script";
Expand Down
7 changes: 7 additions & 0 deletions src/ast/dibuilderbpf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,5 240,12 @@ DIGlobalVariableExpression *DIBuilderBPF::createMapEntry(
file, name, "global", file, 0, map_entry_type, false);
}

DIGlobalVariableExpression *DIBuilderBPF::createGlobalInt64(
std::string_view name)
{
return createGlobalVariableExpression(
file, name, "global", file, 0, getInt64Ty(), false);
}

} // namespace ast
} // namespace bpftrace
1 change: 1 addition & 0 deletions src/ast/dibuilderbpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 44,7 @@ class DIBuilderBPF : public DIBuilder {
uint64_t max_entries,
const MapKey &key,
const SizedType &value_type);
DIGlobalVariableExpression *createGlobalInt64(std::string_view name);

DIFile *file = nullptr;

Expand Down
15 changes: 6 additions & 9 deletions src/ast/irbuilderbpf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 12,7 @@
#include "ast/codegen_helper.h"
#include "bpfmap.h"
#include "bpftrace.h"
#include "globalvars.h"
#include "log.h"
#include "utils.h"

Expand Down Expand Up @@ -576,8 577,7 @@ Value *IRBuilderBPF::CreatePerCpuMapAggElems(Value *ctx,
Map &map,
Value *key,
const SizedType &type,
const location &loc,
bool is_aot)
const location &loc)
{
/*
* int ret = 0;
Expand All @@ -604,10 604,6 @@ Value *IRBuilderBPF::CreatePerCpuMapAggElems(Value *ctx,
AllocaInst *ret = CreateAllocaBPF(getInt64Ty(), "ret");
AllocaInst *i = CreateAllocaBPF(getInt32Ty(), "i");

// Set a large upper bound if we don't know the number of cpus
// when generating the instructions
int nr_cpus = is_aot ? 1024 : bpftrace_.get_num_possible_cpus();

CreateStore(getInt32(0), i);
CreateStore(getInt64(0), ret);

Expand All @@ -623,11 619,12 @@ Value *IRBuilderBPF::CreatePerCpuMapAggElems(Value *ctx,
parent);
CreateBr(while_cond);
SetInsertPoint(while_cond);
// TODO: after full libbpf support update the number of cpus from userspace
// dynamically using a global mutable variable and the skeleton

auto *cond = CreateICmp(CmpInst::ICMP_ULT,
CreateLoad(getInt32Ty(), i),
getInt32(nr_cpus),
CreateLoad(getInt32Ty(),
module_.getGlobalVariable(
bpftrace::globalvars::NUM_CPUS)),
"num_cpu.cmp");
CreateCondBr(cond, while_body, while_end);

Expand Down
3 changes: 1 addition & 2 deletions src/ast/irbuilderbpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 91,7 @@ class IRBuilderBPF : public IRBuilder<> {
Map &map,
Value *key,
const SizedType &type,
const location &loc,
bool is_aot);
const location &loc);
void CreateMapUpdateElem(Value *ctx,
const std::string &map_ident,
Value *key,
Expand Down
37 changes: 25 additions & 12 deletions src/ast/passes/codegen_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 39,7 @@
#include "ast/signal_bt.h"
#include "bpfmap.h"
#include "collect_nodes.h"
#include "globalvars.h"
#include "log.h"
#include "tracepoint_format_parser.h"
#include "types.h"
Expand All @@ -47,14 48,13 @@
namespace bpftrace {
namespace ast {

CodegenLLVM::CodegenLLVM(Node *root, BPFtrace &bpftrace, bool is_aot)
: CodegenLLVM(root, bpftrace, is_aot, std::make_unique<USDTHelper>())
CodegenLLVM::CodegenLLVM(Node *root, BPFtrace &bpftrace)
: CodegenLLVM(root, bpftrace, std::make_unique<USDTHelper>())
{
}

CodegenLLVM::CodegenLLVM(Node *root,
BPFtrace &bpftrace,
bool is_aot,
std::unique_ptr<USDTHelper> usdt_helper)
: root_(root),
bpftrace_(bpftrace),
Expand All @@ -63,8 63,7 @@ CodegenLLVM::CodegenLLVM(Node *root,
module_(std::make_unique<Module>("bpftrace", *context_)),
async_ids_(AsyncIds()),
b_(*context_, *module_, bpftrace, async_ids_),
debug_(*module_),
is_aot_(is_aot)
debug_(*module_)
{
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
Expand Down Expand Up @@ -1399,17 1398,16 @@ void CodegenLLVM::visit(Map &map)
if (val_type.IsAvgTy()) {
AllocaInst *count_key = getHistMapKey(map, b_.getInt64(0));
Value *count_val = b_.CreatePerCpuMapAggElems(
ctx_, map, count_key, val_type, map.loc, is_aot_);
ctx_, map, count_key, val_type, map.loc);
b_.CreateLifetimeEnd(count_key);

AllocaInst *total_key = getHistMapKey(map, b_.getInt64(1));
Value *total_val = b_.CreatePerCpuMapAggElems(
ctx_, map, total_key, val_type, map.loc, is_aot_);
ctx_, map, total_key, val_type, map.loc);
b_.CreateLifetimeEnd(total_key);
value = b_.CreateUDiv(total_val, count_val);
} else {
value = b_.CreatePerCpuMapAggElems(
ctx_, map, key, val_type, map.loc, is_aot_);
value = b_.CreatePerCpuMapAggElems(ctx_, map, key, val_type, map.loc);
}
} else {
value = b_.CreateMapLookupElem(ctx_, map, key, map.loc);
Expand Down Expand Up @@ -3480,6 3478,8 @@ void CodegenLLVM::generate_ir()
{
assert(state_ == State::INIT);
generate_maps(bpftrace_.resources);
generate_global_vars(bpftrace_.resources);

auto scoped_del = accept(root_);
debug_.finalize();
state_ = State::IR;
Expand Down Expand Up @@ -3652,6 3652,20 @@ void CodegenLLVM::generate_maps(const RequiredResources &resources)
CreateInt(loss_cnt_val_size));
}

void CodegenLLVM::generate_global_vars(const RequiredResources &resources)
{
for (const auto &name : resources.needed_global_vars) {
auto var = llvm::dyn_cast<GlobalVariable>(
module_->getOrInsertGlobal(name, b_.getInt64Ty()));
var->setInitializer(b_.getInt64(1));
var->setConstant(true);
var->setSection(bpftrace::globalvars::SECTION_NAME);
var->setExternallyInitialized(true);
var->setDSOLocal(true);
var->addDebugInfo(debug_.createGlobalInt64(name));
}
}

void CodegenLLVM::emit_elf(const std::string &filename)
{
assert(state_ == State::OPT);
Expand Down Expand Up @@ -3759,7 3773,7 @@ BpfBytecode CodegenLLVM::emit(void)
assert(!output.empty());

state_ = State::DONE;
return BpfBytecode(output.data(), output.size(), bpftrace_.config_);
return BpfBytecode(output.data(), output.size(), bpftrace_);
}

BpfBytecode CodegenLLVM::compile(void)
Expand Down Expand Up @@ -4231,8 4245,7 @@ Function *CodegenLLVM::createForEachMapCallback(const For &f, llvm::Type *ctx_t)
"lookup_key");
b_.CreateStore(key, key_ptr);

val = b_.CreatePerCpuMapAggElems(
ctx_, map, key_ptr, map_val_type, map.loc, is_aot_);
val = b_.CreatePerCpuMapAggElems(ctx_, map, key_ptr, map_val_type, map.loc);
} else if (!onStack(val_type)) {
val = b_.CreateLoad(b_.GetType(val_type), val, "val");
}
Expand Down
5 changes: 2 additions & 3 deletions src/ast/passes/codegen_llvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 29,9 @@ using CallArgs = std::vector<std::tuple<FormatString, std::vector<Field>>>;

class CodegenLLVM : public Visitor {
public:
explicit CodegenLLVM(Node *root, BPFtrace &bpftrace, bool is_aot = false);
explicit CodegenLLVM(Node *root, BPFtrace &bpftrace);
explicit CodegenLLVM(Node *root,
BPFtrace &bpftrace,
bool is_aot,
std::unique_ptr<USDTHelper> usdt_helper);

void visit(Integer &integer) override;
Expand Down Expand Up @@ -99,6 98,7 @@ class CodegenLLVM : public Visitor {
libbpf::bpf_map_type get_map_type(const SizedType &val_type,
const MapKey &key);
void generate_maps(const RequiredResources &resources);
void generate_global_vars(const RequiredResources &resources);
void optimize(void);
bool verify(void);
BpfBytecode emit(void);
Expand Down Expand Up @@ -252,7 252,6 @@ class CodegenLLVM : public Visitor {
IRBuilderBPF b_;

DIBuilderBPF debug_;
bool is_aot_;

const DataLayout &datalayout() const
{
Expand Down
4 changes: 4 additions & 0 deletions src/ast/passes/resource_analyser.cpp
Original file line number Diff line number Diff line change
@@ -1,6 1,7 @@
#include "resource_analyser.h"

#include "bpftrace.h"
#include "globalvars.h"
#include "log.h"
#include "struct.h"

Expand Down Expand Up @@ -115,6 116,9 @@ void ResourceAnalyser::visit(Call &call)
: " ";
resources_.join_args.push_back(delim);
resources_.needs_join_map = true;
} else if (call.func == "count" || call.func == "sum" || call.func == "min" ||
call.func == "max" || call.func == "avg") {
resources_.needed_global_vars.insert(bpftrace::globalvars::NUM_CPUS);
} else if (call.func == "hist") {
auto &map_info = resources_.maps_info[call.map->ident];
int bits = static_cast<Integer *>(call.vargs->at(1))->n;
Expand Down
26 changes: 24 additions & 2 deletions src/bpfbytecode.cpp
Original file line number Diff line number Diff line change
@@ -1,6 1,7 @@
#include "bpfbytecode.h"

#include "bpftrace.h"
#include "globalvars.h"
#include "log.h"
#include "utils.h"

Expand All @@ -11,8 12,8 @@

namespace bpftrace {

BpfBytecode::BpfBytecode(const void *elf, size_t elf_size, const Config &config)
: log_size_(config.get(ConfigKeyInt::log_size))
BpfBytecode::BpfBytecode(const void *elf, size_t elf_size, BPFtrace &bpftrace)
: log_size_(bpftrace.config_.get(ConfigKeyInt::log_size))
{
int log_level = 0;
// In debug mode, show full verifier log.
Expand All @@ -31,12 32,33 @@ BpfBytecode::BpfBytecode(const void *elf, size_t elf_size, const Config &config)
if (!bpf_object_)
LOG(BUG) << "The produced ELF is not a valid BPF object";

struct bpf_map *global_vars_map = nullptr;
bool needs_global_vars = !bpftrace.resources.needed_global_vars.empty();

// Discover maps
struct bpf_map *m;
bpf_map__for_each (m, bpf_object_.get()) {
if (needs_global_vars) {
std::string_view name = bpf_map__name(m);
// there are some random chars in the beginning of the map name
if (name.npos != name.find(globalvars::SECTION_NAME)) {
global_vars_map = m;
continue;
}
}
maps_.emplace(bpftrace_map_name(bpf_map__name(m)), m);
}

if (needs_global_vars) {
if (!global_vars_map) {
LOG(BUG) << "No map found for " << globalvars::SECTION_NAME
<< " which is needed to set global variables";
}
globalvars::update_global_vars(bpf_object_.get(),
global_vars_map,
bpftrace);
}

// Discover programs
struct bpf_program *p;
bpf_object__for_each_program (p, bpf_object_.get()) {
Expand Down
2 changes: 1 addition & 1 deletion src/bpfbytecode.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 23,7 @@ class BpfBytecode {
BpfBytecode()
{
}
BpfBytecode(const void *elf, size_t elf_size, const Config &config);
BpfBytecode(const void *elf, size_t elf_size, BPFtrace &bpftrace);

BpfBytecode(const BpfBytecode &) = delete;
BpfBytecode &operator=(const BpfBytecode &) = delete;
Expand Down
5 changes: 0 additions & 5 deletions src/bpftrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2180,11 2180,6 @@ struct bcc_symbol_option &BPFtrace::get_symbol_opts()
return symopts;
}

int BPFtrace::get_num_possible_cpus() const
{
return libbpf_num_possible_cpus();
}

/*
* This prevents an ABBA deadlock when attaching to spin lock internal
* functions e.g. "kfunc:queued_spin_lock_slowpath".
Expand Down
1 change: 0 additions & 1 deletion src/bpftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 140,6 @@ class BPFtrace {
std::optional<int64_t> get_int_literal(const ast::Expression *expr) const;
std::optional<std::string> get_watchpoint_binary_path() const;
virtual bool is_traceable_func(const std::string &func_name) const;
virtual int get_num_possible_cpus() const;
virtual std::unordered_set<std::string> get_func_modules(
const std::string &func_name) const;
int create_pcaps(void);
Expand Down
Loading

0 comments on commit 36c4b72

Please sign in to comment.