From 4a09dd563e71d6959b68be8e7e0c35d1cb325280 Mon Sep 17 00:00:00 2001 From: Brenden Blanco Date: Tue, 11 Aug 2015 09:30:13 -0700 Subject: [PATCH] Add printf key/leaf writer functionality This extends upon the sscanf reader functionality, with the intent of providing key/leaf pretty printing Signed-off-by: Brenden Blanco --- src/cc/bpf_common.cc | 11 +++ src/cc/bpf_common.h | 4 ++ src/cc/bpf_module.cc | 199 +++++++++++++++++++++++++++++++++++++++++++-------- src/cc/bpf_module.h | 8 ++- src/cc/table_desc.h | 2 + 5 files changed, 192 insertions(+), 32 deletions(-) diff --git a/src/cc/bpf_common.cc b/src/cc/bpf_common.cc index c320e65..212e1ef 100644 --- a/src/cc/bpf_common.cc +++ b/src/cc/bpf_common.cc @@ -182,4 +182,15 @@ int bpf_table_update_id(void *program, size_t id, const char *key, const char *l return mod->table_update(id, key, leaf); } +int bpf_table_key_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *key) { + auto mod = static_cast(program); + if (!mod) return 0; + return mod->table_key_printf(id, buf, buflen, key); +} +int bpf_table_leaf_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *leaf) { + auto mod = static_cast(program); + if (!mod) return 0; + return mod->table_key_printf(id, buf, buflen, leaf); +} + } diff --git a/src/cc/bpf_common.h b/src/cc/bpf_common.h index de089c1..c483f38 100644 --- a/src/cc/bpf_common.h +++ b/src/cc/bpf_common.h @@ -48,6 +48,10 @@ size_t bpf_table_key_size(void *program, const char *table_name); size_t bpf_table_key_size_id(void *program, size_t id); size_t bpf_table_leaf_size(void *program, const char *table_name); size_t bpf_table_leaf_size_id(void *program, size_t id); +int bpf_table_key_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *key); +int bpf_table_leaf_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *leaf); +//int bpf_table_key_sscanf(void *program, size_t id, const char *buf, void *key); +//int bpf_table_leaf_sscanf(void *program, size_t id, const char *buf, void *leaf); int bpf_table_update(void *program, const char *table_name, const char *key, const char *leaf); int bpf_table_update_id(void *program, size_t id, const char *key, const char *leaf); diff --git a/src/cc/bpf_module.cc b/src/cc/bpf_module.cc index f508a0d..0b6e02c 100644 --- a/src/cc/bpf_module.cc +++ b/src/cc/bpf_module.cc @@ -109,31 +109,52 @@ BPFModule::BPFModule(unsigned flags) BPFModule::~BPFModule() { engine_.reset(); + rw_engine_.reset(); ctx_.reset(); } +static void debug_printf(Module *mod, IRBuilder<> &B, const string &fmt, vector args) { + GlobalVariable *fmt_gvar = B.CreateGlobalString(fmt, "fmt"); + args.insert(args.begin(), B.CreateInBoundsGEP(fmt_gvar, vector({B.getInt64(0), B.getInt64(0)}))); + args.insert(args.begin(), B.getInt64((uintptr_t)stderr)); + Function *fprintf_fn = mod->getFunction("fprintf"); + if (!fprintf_fn) { + vector fprintf_fn_args({B.getInt64Ty(), B.getInt8PtrTy()}); + FunctionType *fprintf_fn_type = FunctionType::get(B.getInt32Ty(), fprintf_fn_args, /*isvarArg=*/true); + fprintf_fn = Function::Create(fprintf_fn_type, GlobalValue::ExternalLinkage, "fprintf", mod); + fprintf_fn->setCallingConv(CallingConv::C); + fprintf_fn->addFnAttr(Attribute::NoUnwind); + } + B.CreateCall(fprintf_fn, args); +} + // recursive helper to capture the arguments -void parse_type(IRBuilder<> &B, vector *args, string *fmt, Type *type, Value *out) { +static void parse_type(IRBuilder<> &B, vector *args, string *fmt, + Type *type, Value *out, bool is_writer) { if (StructType *st = dyn_cast(type)) { *fmt += "{ "; unsigned idx = 0; for (auto field : st->elements()) { - parse_type(B, args, fmt, field, B.CreateStructGEP(type, out, idx++)); + parse_type(B, args, fmt, field, B.CreateStructGEP(type, out, idx++), is_writer); *fmt += " "; } *fmt += "}"; } else if (IntegerType *it = dyn_cast(type)) { + if (is_writer) + *fmt += "0x"; if (it->getBitWidth() <= 8) - *fmt += "%hhi"; + *fmt += "%hh"; else if (it->getBitWidth() <= 16) - *fmt += "%hi"; + *fmt += "%h"; else if (it->getBitWidth() <= 32) - *fmt += "%i"; - else if (it->getBitWidth() <= 64) - *fmt += "%li"; + *fmt += "%l"; else - *fmt += "%lli"; - args->push_back(out); + *fmt += "%ll"; + if (is_writer) + *fmt += "x"; + else + *fmt += "i"; + args->push_back(is_writer ? B.CreateLoad(out) : out); } } @@ -168,14 +189,13 @@ Function * BPFModule::make_reader(Module *mod, Type *type) { BasicBlock *label_exit = BasicBlock::Create(*ctx_, "exit", fn); B.SetInsertPoint(label_entry); - vector args; + vector args({arg_in, nullptr}); string fmt; - parse_type(B, &args, &fmt, type, arg_out); + parse_type(B, &args, &fmt, type, arg_out, false); GlobalVariable *fmt_gvar = B.CreateGlobalString(fmt, "fmt"); - args.insert(args.begin(), B.CreateInBoundsGEP(fmt_gvar, vector({B.getInt64(0), B.getInt64(0)}))); - args.insert(args.begin(), arg_in); + args[1] = B.CreateInBoundsGEP(fmt_gvar, vector({B.getInt64(0), B.getInt64(0)})); vector sscanf_fn_args({B.getInt8PtrTy(), B.getInt8PtrTy()}); FunctionType *sscanf_fn_type = FunctionType::get(B.getInt32Ty(), sscanf_fn_args, /*isVarArg=*/true); @@ -203,7 +223,63 @@ Function * BPFModule::make_reader(Module *mod, Type *type) { return fn; } -unique_ptr BPFModule::finalize_reader(unique_ptr m) { +Function * BPFModule::make_writer(Module *mod, Type *type) { + auto fn_it = writers_.find(type); + if (fn_it != writers_.end()) + return fn_it->second; + + // int write(int len, char *out, Type *in) { + // return snprintf(out, len, "{ %i ... }", out->field1, ...); + // } + + IRBuilder<> B(*ctx_); + + // The JIT currently supports a limited number of function prototypes, use the + // int (*) (int, char **, const char **) version + vector fn_args({B.getInt32Ty(), B.getInt8PtrTy(), PointerType::getUnqual(type)}); + FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), fn_args, /*isVarArg=*/false); + Function *fn = Function::Create(fn_type, GlobalValue::ExternalLinkage, + "writer" + std::to_string(writers_.size()), mod); + auto arg_it = fn->arg_begin(); + Argument *arg_len = arg_it++; + arg_len->setName("len"); + Argument *arg_out = arg_it++; + arg_out->setName("out"); + Argument *arg_in = arg_it++; + arg_in->setName("in"); + + BasicBlock *label_entry = BasicBlock::Create(*ctx_, "entry", fn); + B.SetInsertPoint(label_entry); + + vector args({arg_out, B.CreateZExt(arg_len, B.getInt64Ty()), nullptr}); + string fmt; + parse_type(B, &args, &fmt, type, arg_in, true); + + GlobalVariable *fmt_gvar = B.CreateGlobalString(fmt, "fmt"); + + args[2] = B.CreateInBoundsGEP(fmt_gvar, vector({B.getInt64(0), B.getInt64(0)})); + + if (0) + debug_printf(mod, B, "%d %p %p\n", vector({arg_len, arg_out, arg_in})); + + vector snprintf_fn_args({B.getInt8PtrTy(), B.getInt64Ty(), B.getInt8PtrTy()}); + FunctionType *snprintf_fn_type = FunctionType::get(B.getInt32Ty(), snprintf_fn_args, /*isVarArg=*/true); + Function *snprintf_fn = mod->getFunction("snprintf"); + if (!snprintf_fn) + snprintf_fn = Function::Create(snprintf_fn_type, GlobalValue::ExternalLinkage, "snprintf", mod); + snprintf_fn->setCallingConv(CallingConv::C); + snprintf_fn->addFnAttr(Attribute::NoUnwind); + + CallInst *call = B.CreateCall(snprintf_fn, args); + call->setTailCall(true); + + B.CreateRet(call); + + writers_[type] = fn; + return fn; +} + +unique_ptr BPFModule::finalize_rw(unique_ptr m) { Module *mod = &*m; run_pass_manager(*mod); @@ -256,22 +332,24 @@ int BPFModule::annotate() { Type *key_type = st->elements()[0]; Type *leaf_type = st->elements()[1]; table.key_reader = make_reader(&*m, key_type); - if (!table.key_reader) { + if (!table.key_reader) errs() << "Failed to compile reader for " << *key_type << "\n"; - continue; - } table.leaf_reader = make_reader(&*m, leaf_type); - if (!table.leaf_reader) { + if (!table.leaf_reader) errs() << "Failed to compile reader for " << *leaf_type << "\n"; - continue; - } + table.key_writer = make_writer(&*m, key_type); + if (!table.key_writer) + errs() << "Failed to compile writer for " << *key_type << "\n"; + table.leaf_writer = make_writer(&*m, leaf_type); + if (!table.leaf_writer) + errs() << "Failed to compile writer for " << *leaf_type << "\n"; } } } - reader_engine_ = finalize_reader(move(m)); - if (reader_engine_) - reader_engine_->finalizeObject(); + rw_engine_ = finalize_rw(move(m)); + if (rw_engine_) + rw_engine_->finalizeObject(); return 0; } @@ -467,7 +545,7 @@ int BPFModule::table_update(size_t id, const char *key_str, const char *leaf_str const TableDesc &desc = (*tables_)[id]; if (desc.fd < 0) return -1; - if (!reader_engine_ || !desc.key_reader || !desc.leaf_reader) { + if (!rw_engine_ || !desc.key_reader || !desc.leaf_reader) { fprintf(stderr, "Table sscanf not available\n"); return -1; } @@ -475,19 +553,80 @@ int BPFModule::table_update(size_t id, const char *key_str, const char *leaf_str unique_ptr key(new uint8_t[desc.key_size]); unique_ptr leaf(new uint8_t[desc.leaf_size]); GenericValue rc; - rc = reader_engine_->runFunction(desc.key_reader, vector({GenericValue(), - GenericValue((void *)key_str), - GenericValue((void *)key.get())})); + rc = rw_engine_->runFunction(desc.key_reader, vector({GenericValue(), + GenericValue((void *)key_str), + GenericValue((void *)key.get())})); if (rc.IntVal != 0) return -1; - rc = reader_engine_->runFunction(desc.leaf_reader, vector({GenericValue(), - GenericValue((void *)leaf_str), - GenericValue((void *)leaf.get())})); + rc = rw_engine_->runFunction(desc.leaf_reader, vector({GenericValue(), + GenericValue((void *)leaf_str), + GenericValue((void *)leaf.get())})); if (rc.IntVal != 0) return -1; return bpf_update_elem(desc.fd, key.get(), leaf.get(), 0); } +struct TableIterator { + TableIterator(size_t key_size, size_t leaf_size) + : key(new uint8_t[key_size]), leaf(new uint8_t[leaf_size]) { + } + unique_ptr key; + unique_ptr leaf; + uint8_t keyb[512]; +}; + +int BPFModule::table_key_printf(size_t id, char *buf, size_t buflen, const void *key) { + if (id >= tables_->size()) { + fprintf(stderr, "table id %zu out of range\n", id); + return -1; + } + + const TableDesc &desc = (*tables_)[id]; + if (!desc.key_writer) { + fprintf(stderr, "table snprintf not implemented for %s key\n", desc.name.c_str()); + return -1; + } + GenericValue gv_buflen; + gv_buflen.IntVal = APInt(32, buflen, true); + vector args({gv_buflen, GenericValue((void *)buf), GenericValue((void *)key)}); + GenericValue rc = rw_engine_->runFunction(desc.key_writer, args); + if (rc.IntVal.isNegative()) { + perror("snprintf"); + return -1; + } + if (rc.IntVal.sge(buflen)) { + fprintf(stderr, "snprintf ran out of buffer space\n"); + return -1; + } + return 0; +} + +int BPFModule::table_leaf_printf(size_t id, char *buf, size_t buflen, const void *leaf) { + if (id >= tables_->size()) { + fprintf(stderr, "table id %zu out of range\n", id); + return -1; + } + + const TableDesc &desc = (*tables_)[id]; + if (!desc.leaf_writer) { + fprintf(stderr, "table snprintf not implemented for %s leaf\n", desc.name.c_str()); + return -1; + } + GenericValue gv_buflen; + gv_buflen.IntVal = buflen; + vector args({gv_buflen, GenericValue((void *)buf), GenericValue((void *)leaf)}); + GenericValue rc = rw_engine_->runFunction(desc.leaf_writer, args); + if (rc.IntVal.isNegative()) { + perror("snprintf"); + return -1; + } + if (rc.IntVal.sge(buflen)) { + fprintf(stderr, "snprintf ran out of buffer space\n"); + return -1; + } + return 0; +} + // load a B file, which comes in two parts int BPFModule::load_b(const string &filename, const string &proto_filename) { if (!sections_.empty()) { diff --git a/src/cc/bpf_module.h b/src/cc/bpf_module.h index addd1e1..6b11ea0 100644 --- a/src/cc/bpf_module.h +++ b/src/cc/bpf_module.h @@ -42,8 +42,9 @@ class BPFModule { int parse(llvm::Module *mod); int finalize(); int annotate(); - std::unique_ptr finalize_reader(std::unique_ptr mod); + std::unique_ptr finalize_rw(std::unique_ptr mod); llvm::Function * make_reader(llvm::Module *mod, llvm::Type *type); + llvm::Function * make_writer(llvm::Module *mod, llvm::Type *type); void dump_ir(llvm::Module &mod); int load_file_module(std::unique_ptr *mod, const std::string &file, bool in_memory); int load_includes(const std::string &tmpfile); @@ -70,10 +71,12 @@ class BPFModule { const char * table_key_desc(const std::string &name) const; size_t table_key_size(size_t id) const; size_t table_key_size(const std::string &name) const; + int table_key_printf(size_t id, char *buf, size_t buflen, const void *key); const char * table_leaf_desc(size_t id) const; const char * table_leaf_desc(const std::string &name) const; size_t table_leaf_size(size_t id) const; size_t table_leaf_size(const std::string &name) const; + int table_leaf_printf(size_t id, char *buf, size_t buflen, const void *leaf); int table_update(size_t id, const char *key, const char *leaf); int table_update(const std::string &name, const char *key, const char *leaf); char * license() const; @@ -84,7 +87,7 @@ class BPFModule { std::string proto_filename_; std::unique_ptr ctx_; std::unique_ptr engine_; - std::unique_ptr reader_engine_; + std::unique_ptr rw_engine_; std::unique_ptr mod_; std::unique_ptr b_loader_; std::unique_ptr clang_loader_; @@ -93,6 +96,7 @@ class BPFModule { std::map table_names_; std::vector function_names_; std::map readers_; + std::map writers_; }; } // namespace ebpf diff --git a/src/cc/table_desc.h b/src/cc/table_desc.h index 3186c4c..afecad1 100644 --- a/src/cc/table_desc.h +++ b/src/cc/table_desc.h @@ -33,6 +33,8 @@ struct TableDesc { std::string leaf_desc; llvm::Function *key_reader; llvm::Function *leaf_reader; + llvm::Function *key_writer; + llvm::Function *leaf_writer; }; } // namespace ebpf -- 2.7.4