Add clang support to bcc
authorBrenden Blanco <bblanco@plumgrid.com>
Tue, 26 May 2015 18:48:17 +0000 (11:48 -0700)
committerBrenden Blanco <bblanco@plumgrid.com>
Tue, 26 May 2015 18:48:17 +0000 (11:48 -0700)
* Frontend options: b and modified c languages
* Tests written for both languages
* Automatic kbuild integration
* Some cleanups and files moved

Signed-off-by: Brenden Blanco <bblanco@plumgrid.com>
30 files changed:
CMakeLists.txt
src/bpf.py
src/cc/CMakeLists.txt
src/cc/b_frontend_action.cc [new file with mode: 0644]
src/cc/b_frontend_action.h [new file with mode: 0644]
src/cc/bitops.c [deleted file]
src/cc/bpf_common.cc
src/cc/bpf_common.h
src/cc/bpf_helpers.h
src/cc/bpf_program.cc
src/cc/bpf_program.h
src/cc/codegen_llvm.cc
src/cc/kbuild_helper.cc [new file with mode: 0644]
src/cc/kbuild_helper.h [new file with mode: 0644]
src/cc/lexer.ll
src/cc/libbpf.c
src/libbpf.h
tests/jit/CMakeLists.txt
tests/jit/proto.b
tests/jit/test1.b
tests/jit/test1.c [new file with mode: 0644]
tests/jit/test1.py
tests/jit/test2.b
tests/jit/test2.py
tests/jit/trace1.py
tests/jit/trace2.c [new file with mode: 0644]
tests/jit/trace2.py
tests/jit/trace3.c [new file with mode: 0644]
tests/jit/trace3.py [new file with mode: 0755]
tests/wrapper.sh.in

index 1f33639..d5c89c4 100644 (file)
@@ -22,6 +22,7 @@ find_library(libclangEdit NAMES clangEdit HINTS ${CLANG_SEARCH})
 find_library(libclangFrontend NAMES clangFrontend HINTS ${CLANG_SEARCH})
 find_library(libclangLex NAMES clangLex HINTS ${CLANG_SEARCH})
 find_library(libclangParse NAMES clangParse HINTS ${CLANG_SEARCH})
+find_library(libclangRewrite NAMES clangRewrite HINTS ${CLANG_SEARCH})
 find_library(libclangSema NAMES clangSema HINTS ${CLANG_SEARCH})
 find_library(libclangSerialization NAMES clangSerialization HINTS ${CLANG_SEARCH})
 
index 774f9f7..a2deb1a 100644 (file)
@@ -13,6 +13,8 @@ lib.bpf_program_size.restype = ct.c_size_t
 lib.bpf_program_size.argtypes = [ct.c_void_p, ct.c_char_p]
 lib.bpf_program_license.restype = ct.c_char_p
 lib.bpf_program_license.argtypes = [ct.c_void_p]
+lib.bpf_program_kern_version.restype = ct.c_uint
+lib.bpf_program_kern_version.argtypes = [ct.c_void_p]
 lib.bpf_program_table_fd.restype = ct.c_int
 lib.bpf_program_table_fd.argtypes = [ct.c_void_p, ct.c_char_p]
 
@@ -33,7 +35,7 @@ lib.bpf_attach_filter.restype = ct.c_int
 lib.bpf_attach_filter.argtypes = [ct.c_int, ct.c_char_p, ct.c_uint, ct.c_ubyte, ct.c_uint]
 lib.bpf_prog_load.restype = ct.c_int
 lib.bpf_prog_load.argtypes = [ct.c_int, ct.c_void_p, ct.c_size_t,
-        ct.c_char_p]
+        ct.c_char_p, ct.c_uint]
 lib.bpf_attach_kprobe.restype = ct.c_int
 lib.bpf_attach_kprobe.argtypes = [ct.c_int, ct.c_char_p, ct.c_char_p, ct.c_int, ct.c_int, ct.c_int]
 
@@ -67,7 +69,8 @@ class BPF(object):
         self.fd[prog_name] = lib.bpf_prog_load(self.prog_type,
                 lib.bpf_program_start(self.prog, prog_name.encode("ascii")),
                 lib.bpf_program_size(self.prog, prog_name.encode("ascii")),
-                lib.bpf_program_license(self.prog))
+                lib.bpf_program_license(self.prog),
+                lib.bpf_program_kern_version(self.prog))
 
         if self.fd[prog_name] < 0:
             print((ct.c_char * 65536).in_dll(lib, "bpf_log_buf").value)
index cf544da..0cc26d5 100644 (file)
@@ -10,16 +10,17 @@ ADD_FLEX_BISON_DEPENDENCY(Lexer Parser)
 set(CMAKE_SHARED_LINKER_FLAGS "-static-libstdc++ -Wl,--exclude-libs=ALL")
 
 add_library(bpfprog SHARED bpf_common.cc bpf_program.cc codegen_llvm.cc
-  node.cc parser.cc printer.cc type_check.cc libbpf.c
+  node.cc parser.cc printer.cc type_check.cc libbpf.c b_frontend_action.cc
+  kbuild_helper.cc
   ${BISON_Parser_OUTPUTS} ${FLEX_Lexer_OUTPUTS})
 
 # BPF is still experimental otherwise it should be available
 #llvm_map_components_to_libnames(llvm_libs bpf mcjit irreader passes)
 llvm_map_components_to_libnames(llvm_libs mcjit irreader passes linker instrumentation objcarcopts bitwriter option)
 # order is important
-set(clang_libs ${libclangFrontend} ${libclangParse} ${libclangSema} ${libclangCodeGen}
-  ${libclangDriver} ${libclangAnalysis} ${libclangSerialization} ${libclangEdit}
-  ${libclangLex} ${libclangAST} ${libclangBasic})
+set(clang_libs ${libclangFrontend} ${libclangSerialization} ${libclangDriver} ${libclangParse}
+  ${libclangSema} ${libclangCodeGen} ${libclangAnalysis} ${libclangRewrite} ${libclangEdit}
+  ${libclangAST} ${libclangLex} ${libclangBasic})
 
 # Link against LLVM libraries
 target_link_libraries(bpfprog ${clang_libs} ${llvm_libs} LLVMBPFCodeGen mnl)
diff --git a/src/cc/b_frontend_action.cc b/src/cc/b_frontend_action.cc
new file mode 100644 (file)
index 0000000..c2f8a04
--- /dev/null
@@ -0,0 +1,197 @@
+#include <linux/bpf.h>
+
+#include <clang/AST/ASTConsumer.h>
+#include <clang/AST/ASTContext.h>
+#include <clang/AST/RecordLayout.h>
+#include <clang/Frontend/CompilerInstance.h>
+#include <clang/Rewrite/Core/Rewriter.h>
+
+#include "b_frontend_action.h"
+
+extern "C"
+int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
+                   int max_entries);
+
+namespace ebpf {
+using std::map;
+using std::string;
+using std::unique_ptr;
+using namespace clang;
+
+BTypeVisitor::BTypeVisitor(ASTContext &C, Rewriter &rewriter, map<string, BPFTable> &tables)
+    : C(C), rewriter_(rewriter), out_(llvm::errs()), tables_(tables) {
+}
+
+bool BTypeVisitor::VisitFunctionDecl(FunctionDecl *D) {
+  return true;
+}
+
+// convert calls of the type:
+//  table.foo(&key)
+// to:
+//  bpf_table_foo_elem(bpf_pseudo_fd(table), &key [,&leaf])
+bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
+  // make sure node is a reference to a bpf table, which is assured by the
+  // presence of the section("maps/<typename>") GNU __attribute__
+  if (MemberExpr *Memb = dyn_cast<MemberExpr>(Call->getCallee()->IgnoreImplicit())) {
+    StringRef memb_name = Memb->getMemberDecl()->getName();
+    if (DeclRefExpr *Ref = dyn_cast<DeclRefExpr>(Memb->getBase())) {
+      if (SectionAttr *A = Ref->getDecl()->getAttr<SectionAttr>()) {
+        if (!A->getName().startswith("maps"))
+          return true;
+        // find the table fd, which was opened at declaration time
+        auto table_it = tables_.find(Ref->getDecl()->getName());
+        if (table_it == tables_.end()) {
+          C.getDiagnostics().Report(Ref->getLocEnd(), diag::err_expected)
+              << "initialized handle for bpf_table";
+          return false;
+        }
+        string fd = std::to_string(table_it->second.fd);
+        string prefix, suffix;
+        string map_update_policy = "BPF_ANY";
+        if (memb_name == "get") {
+          prefix = "bpf_map_lookup_elem_";
+          suffix = ")";
+        } else if (memb_name == "put") {
+          prefix = "bpf_map_update_elem_";
+          suffix = ", " + map_update_policy + ")";
+        } else if (memb_name == "delete") {
+          prefix = "bpf_map_delete_elem_";
+          suffix = ")";
+        } else {
+          llvm::errs() << "error: unknown bpf_table operation " << memb_name << "\n";
+          return false;
+        }
+        prefix += "(bpf_pseudo_fd(1, " + fd + "), ";
+
+        SourceRange argRange(Call->getArg(0)->getLocStart(),
+                             Call->getArg(Call->getNumArgs()-1)->getLocEnd());
+        string args = rewriter_.getRewrittenText(argRange);
+        rewriter_.ReplaceText(SourceRange(Call->getLocStart(), Call->getLocEnd()), prefix + args + suffix);
+        return true;
+      }
+    }
+  }
+  return true;
+}
+
+// look for table subscript references, and turn them into auto table entries:
+// table.data[key]
+//  becomes:
+// struct Key key = {123};
+// struct Leaf *leaf = table.get(&key);
+// if (!leaf) {
+//   struct Leaf zleaf = {0};
+//   table.put(&key, &zleaf, BPF_NOEXIST);
+//   leaf = table.get(&key);
+//   if (!leaf) return -1;
+// }
+bool BTypeVisitor::VisitArraySubscriptExpr(ArraySubscriptExpr *Arr) {
+  Expr *LHS = Arr->getLHS()->IgnoreImplicit();
+  Expr *RHS = Arr->getRHS()->IgnoreImplicit();
+  if (MemberExpr *Memb = dyn_cast<MemberExpr>(LHS)) {
+    if (DeclRefExpr *Ref = dyn_cast<DeclRefExpr>(Memb->getBase())) {
+      if (SectionAttr *A = Ref->getDecl()->getAttr<SectionAttr>()) {
+        if (A->getName().startswith("maps")) {
+          auto table_it = tables_.find(Ref->getDecl()->getName());
+          if (table_it == tables_.end()) {
+            C.getDiagnostics().Report(Ref->getLocEnd(), diag::err_expected)
+                << "initialized handle for bpf_table";
+            return false;
+          }
+          string fd = std::to_string(table_it->second.fd);
+          string map_update_policy = "BPF_NOEXIST";
+          string name = Ref->getDecl()->getName();
+          SourceRange argRange(RHS->getLocStart(), RHS->getLocEnd());
+          string args = rewriter_.getRewrittenText(argRange);
+          string lookup = "bpf_map_lookup_elem_(bpf_pseudo_fd(1, " + fd + ")";
+          string update = "bpf_map_update_elem_(bpf_pseudo_fd(1, " + fd + ")";
+          string txt = "(*({typeof(" + name + ".leaf) *leaf = " + lookup + ", " + args + "); ";
+          txt         += "if (!leaf) {";
+          txt         += " typeof(" + name + ".leaf) zleaf = {0};";
+          txt         += " " + update + ", " + args + ", &zleaf, " + map_update_policy + ");";
+          txt         += " leaf = " + lookup + ", " + args + ");";
+          txt         += " if (!leaf) return -1;";
+          txt         += "}";
+          txt         += "leaf;}))";
+          rewriter_.ReplaceText(SourceRange(Arr->getLocStart(), Arr->getLocEnd()), txt);
+        }
+      }
+    }
+  }
+  return true;
+}
+
+// Open table FDs when bpf tables (as denoted by section("maps*") attribute)
+// are declared.
+bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
+  const RecordType *R = Decl->getType()->getAs<RecordType>();
+  if (SectionAttr *A = Decl->getAttr<SectionAttr>()) {
+    if (!A->getName().startswith("maps"))
+      return true;
+    if (!R) {
+      C.getDiagnostics().Report(Decl->getLocEnd(), diag::err_expected)
+          << "struct type for bpf_table";
+      return false;
+    }
+    const RecordDecl *RD = R->getDecl()->getDefinition();
+    BPFTable table;
+    unsigned i = 0;
+    for (auto F : RD->fields()) {
+      size_t sz = C.getTypeSize(F->getType()) >> 3;
+      if (F->getName() == "key") {
+        table.key_size = sz;
+      } else if (F->getName() == "leaf") {
+        table.leaf_size = sz;
+      } else if (F->getName() == "data") {
+        table.max_entries = sz / table.leaf_size;
+      }
+      ++i;
+    }
+    bpf_map_type map_type = BPF_MAP_TYPE_UNSPEC;
+    if (A->getName() == "maps/hash")
+      map_type = BPF_MAP_TYPE_HASH;
+    else if (A->getName() == "maps/array")
+      map_type = BPF_MAP_TYPE_ARRAY;
+    table.fd = bpf_create_map(map_type, table.key_size, table.leaf_size, table.max_entries);
+    if (table.fd < 0) {
+      llvm::errs() << "error: could not open bpf fd\n";
+      return false;
+    }
+    tables_[Decl->getName()] = table;
+  }
+  return true;
+}
+bool BTypeVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
+  //ValueDecl *D = E->getDecl();
+  //BPFTableAttr *A = D->getAttr<BPFTableAttr>();
+  return true;
+}
+
+BTypeConsumer::BTypeConsumer(ASTContext &C, Rewriter &rewriter, map<string, BPFTable> &tables)
+    : visitor_(C, rewriter, tables) {
+}
+
+bool BTypeConsumer::HandleTopLevelDecl(DeclGroupRef D) {
+  for (auto it : D)
+    visitor_.TraverseDecl(it);
+  return true;
+}
+
+BFrontendAction::BFrontendAction(llvm::raw_ostream &os)
+    : rewriter_(new Rewriter), os_(os), tables_(new map<string, BPFTable>) {
+}
+
+void BFrontendAction::EndSourceFileAction() {
+  // uncomment to see rewritten source
+  //rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(llvm::errs());
+  rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(os_);
+  os_.flush();
+}
+
+unique_ptr<ASTConsumer> BFrontendAction::CreateASTConsumer(CompilerInstance &Compiler, llvm::StringRef InFile) {
+  rewriter_->setSourceMgr(Compiler.getSourceManager(), Compiler.getLangOpts());
+  return unique_ptr<ASTConsumer>(new BTypeConsumer(Compiler.getASTContext(), *rewriter_, *tables_));
+}
+
+}
diff --git a/src/cc/b_frontend_action.h b/src/cc/b_frontend_action.h
new file mode 100644 (file)
index 0000000..0bf0ce3
--- /dev/null
@@ -0,0 +1,86 @@
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <clang/AST/RecursiveASTVisitor.h>
+#include <clang/Frontend/FrontendAction.h>
+#include <clang/Rewrite/Core/Rewriter.h>
+
+namespace clang {
+class ASTConsumer;
+class ASTContext;
+class CompilerInstance;
+}
+
+namespace llvm {
+class raw_ostream;
+class StringRef;
+}
+
+namespace ebpf {
+
+struct BPFTable {
+  int fd;
+  size_t key_size;
+  size_t leaf_size;
+  size_t max_entries;
+};
+
+// Type visitor and rewriter for B programs.
+// It will look for B-specific features and rewrite them into a valid
+// C program. As part of the processing, open the necessary BPF tables
+// and store the open handles in a map of table-to-fd's.
+class BTypeVisitor : public clang::RecursiveASTVisitor<BTypeVisitor> {
+ public:
+  explicit BTypeVisitor(clang::ASTContext &C, clang::Rewriter &rewriter,
+                        std::map<std::string, BPFTable> &tables);
+  bool VisitFunctionDecl(clang::FunctionDecl *D);
+  bool VisitCallExpr(clang::CallExpr *Call);
+  bool VisitVarDecl(clang::VarDecl *Decl);
+  bool VisitArraySubscriptExpr(clang::ArraySubscriptExpr *E);
+  bool VisitDeclRefExpr(clang::DeclRefExpr *E);
+
+ private:
+  clang::ASTContext &C;
+  clang::Rewriter &rewriter_;  /// modifications to the source go into this class
+  llvm::raw_ostream &out_;  /// for debugging
+  std::map<std::string, BPFTable> &tables_;  /// store the open FDs
+};
+
+// A helper class to the frontend action, walks the decls
+class BTypeConsumer : public clang::ASTConsumer {
+ public:
+  explicit BTypeConsumer(clang::ASTContext &C, clang::Rewriter &rewriter,
+                         std::map<std::string, BPFTable> &tables);
+  bool HandleTopLevelDecl(clang::DeclGroupRef D) override;
+ private:
+  BTypeVisitor visitor_;
+};
+
+// Create a B program in 2 phases (everything else is normal C frontend):
+// 1. Catch the map declarations and open the fd's
+// 2. Capture the IR
+class BFrontendAction : public clang::ASTFrontendAction {
+ public:
+  // Initialize with the output stream where the new source file contents
+  // should be written.
+  explicit BFrontendAction(llvm::raw_ostream &os);
+
+  // Called by clang when the AST has been completed, here the output stream
+  // will be flushed.
+  void EndSourceFileAction() override;
+
+  std::unique_ptr<clang::ASTConsumer>
+      CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef InFile) override;
+
+  // take ownership of the table-to-fd mapping data structure
+  std::unique_ptr<std::map<std::string, BPFTable>> take_tables() { return move(tables_); }
+ private:
+  std::unique_ptr<clang::Rewriter> rewriter_;
+  llvm::raw_ostream &os_;
+  std::unique_ptr<std::map<std::string, BPFTable>> tables_;
+};
+
+}  // namespace visitor
diff --git a/src/cc/bitops.c b/src/cc/bitops.c
deleted file mode 100644 (file)
index 896d39d..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * ====================================================================
- * Copyright (c) 2012-2013, PLUMgrid, http://plumgrid.com
- *
- * This source is subject to the PLUMgrid License.
- * All rights reserved.
- *
- * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
- * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
- * PARTICULAR PURPOSE.
- *
- * PLUMgrid confidential information, delete if you are not the
- * intended recipient.
- *
- * ====================================================================
- */
-
-#include <linux/skbuff.h>
-#include <linux/bpf.h>
-#include "bpf_helpers.h"
-#define assert(v)
-
-static inline u16 bpf_ntohs(u16 val) {
-  /* will be recognized by gcc into rotate insn and eventually rolw 8 */
-  return (val << 8) | (val >> 8);
-}
-
-static inline u32 bpf_ntohl(u32 val) {
-  /* gcc will use bswapsi2 insn */
-  return __builtin_bswap32(val);
-}
-
-static inline u64 bpf_ntohll(u64 val) {
-  /* gcc will use bswapdi2 insn */
-  return __builtin_bswap64(val);
-}
-
-static inline unsigned __int128 bpf_ntoh128(unsigned __int128 val) {
-  return (((unsigned __int128)bpf_ntohll(val) << 64) | (u64)bpf_ntohll(val >> 64));
-}
-
-static inline u16 bpf_htons(u16 val) {
-  return bpf_ntohs(val);
-}
-
-static inline u32 bpf_htonl(u32 val) {
-  return bpf_ntohl(val);
-}
-static inline u64 bpf_htonll(u64 val) {
-  return bpf_ntohll(val);
-}
-static inline unsigned __int128 bpf_hton128(unsigned __int128 val) {
-  return bpf_ntoh128(val);
-}
-
-static inline u64 load_dword(void *skb, u64 off) {
-  return ((u64)load_word(skb, off) << 4) | load_word(skb, off + 4);
-}
-
-void bpf_store_byte(void *skb, u64 off, u64 val) asm("llvm.bpf.store.byte");
-void bpf_store_half(void *skb, u64 off, u64 val) asm("llvm.bpf.store.half");
-void bpf_store_word(void *skb, u64 off, u64 val) asm("llvm.bpf.store.word");
-static inline void bpf_store_dword(void *skb, u64 off, u64 val) {
-  bpf_store_word(skb, off, (u32)val);
-  bpf_store_word(skb, off + 4, val >> 32);
-}
-
-#define MASK(_n) ((_n) < 64 ? (1ull << (_n)) - 1 : ((u64)-1LL))
-#define MASK128(_n) ((_n) < 128 ? ((unsigned __int128)1 << (_n)) - 1 : ((unsigned __int128)-1))
-
-struct _skbuff;
-struct bpf_context;
-
-//static inline __attribute__((always_inline))
-SEC("helpers")
-u64 bpf_dext_pkt(void *pkt, u64 off, u64 bofs, u64 bsz) {
-  if (bofs == 0 && bsz == 8) {
-    return load_byte(pkt, off);
-  } else if (bofs + bsz <= 8) {
-    return load_byte(pkt, off) >> (8 - (bofs + bsz))  &  MASK(bsz);
-  } else if (bofs == 0 && bsz == 16) {
-    return load_half(pkt, off);
-  } else if (bofs + bsz <= 16) {
-    return load_half(pkt, off) >> (16 - (bofs + bsz))  &  MASK(bsz);
-  } else if (bofs == 0 && bsz == 32) {
-    return load_word(pkt, off);
-  } else if (bofs + bsz <= 32) {
-    return load_word(pkt, off) >> (32 - (bofs + bsz))  &  MASK(bsz);
-  } else if (bofs + bsz <= 64) {
-    return bpf_ntohll(load_dword(pkt, off)) >> (64 - (bofs + bsz))  &  MASK(bsz);
-  } else {
-    assert(0);
-  }
-  return 0;
-}
-
-//static inline __attribute__((always_inline))
-SEC("helpers")
-void bpf_dins_pkt(void *pkt, u64 off, u64 bofs, u64 bsz, u64 val) {
-  // The load_xxx function does a bswap before returning the short/word/dword,
-  // so the value in register will always be host endian. However, the bytes
-  // written back need to be in network order.
-  if (bofs == 0 && bsz == 8) {
-    bpf_skb_store_bytes(pkt, off, &val, 1, 0);
-  } else if (bofs + bsz <= 8) {
-    u8 v = load_byte(pkt, off);
-    v &= ~(MASK(bsz) << (8 - (bofs + bsz)));
-    v |= ((val & MASK(bsz)) << (8 - (bofs + bsz)));
-    bpf_skb_store_bytes(pkt, off, &v, 1, 0);
-  } else if (bofs == 0 && bsz == 16) {
-    u16 v = bpf_htons(val);
-    bpf_skb_store_bytes(pkt, off, &v, 2, 0);
-  } else if (bofs + bsz <= 16) {
-    u16 v = load_half(pkt, off);
-    v &= ~(MASK(bsz) << (16 - (bofs + bsz)));
-    v |= ((val & MASK(bsz)) << (16 - (bofs + bsz)));
-    v = bpf_htons(v);
-    bpf_skb_store_bytes(pkt, off, &v, 2, 0);
-  } else if (bofs == 0 && bsz == 32) {
-    u32 v = bpf_htonl(val);
-    bpf_skb_store_bytes(pkt, off, &v, 4, 0);
-  } else if (bofs + bsz <= 32) {
-    u32 v = load_word(pkt, off);
-    v &= ~(MASK(bsz) << (32 - (bofs + bsz)));
-    v |= ((val & MASK(bsz)) << (32 - (bofs + bsz)));
-    v = bpf_htonl(v);
-    bpf_skb_store_bytes(pkt, off, &v, 4, 0);
-  } else if (bofs == 0 && bsz == 64) {
-    u64 v = bpf_htonll(val);
-    bpf_skb_store_bytes(pkt, off, &v, 8, 0);
-  } else if (bofs + bsz <= 64) {
-    u64 v = load_dword(pkt, off);
-    v &= ~(MASK(bsz) << (64 - (bofs + bsz)));
-    v |= ((val & MASK(bsz)) << (64 - (bofs + bsz)));
-    v = bpf_htonll(v);
-    bpf_skb_store_bytes(pkt, off, &v, 8, 0);
-  } else if (bofs + bsz <= 128) {
-    assert(0);
-    //bpf_store_16bytes(pkt, off, bpf_hton128(~(MASK128(bsz) << (128 - (bofs + bsz)))),
-    //                 bpf_hton128((val & MASK128(bsz)) << (128 - (bofs + bsz))));
-  } else {
-    assert(0);
-  }
-}
-
-SEC("helpers")
-void * bpf_map_lookup_elem_(uintptr_t map, void *key) {
-  return bpf_map_lookup_elem((void *)map, key);
-}
-
-SEC("helpers")
-int bpf_map_update_elem_(uintptr_t map, void *key, void *value, u64 flags) {
-  return bpf_map_update_elem((void *)map, key, value, flags);
-}
-
-SEC("helpers")
-int bpf_map_delete_elem_(uintptr_t map, void *key) {
-  return bpf_map_delete_elem((void *)map, key);
-}
-
-SEC("helpers")
-int bpf_skb_store_bytes_(void *ctx, u64 off, void *from, u64 len, u64 flags) {
-  return bpf_skb_store_bytes(ctx, off, from, len, flags);
-}
-
-SEC("helpers")
-int bpf_l3_csum_replace_(void *ctx, u64 off, u64 from, u64 to, u64 flags) {
-  switch (flags & 0xf) {
-    case 2:
-      return bpf_l3_csum_replace(ctx, off, bpf_htons(from), bpf_htons(to), flags);
-    case 4:
-      return bpf_l3_csum_replace(ctx, off, bpf_htonl(from), bpf_htonl(to), flags);
-    case 8:
-      return bpf_l3_csum_replace(ctx, off, bpf_htonll(from), bpf_htonll(to), flags);
-    default:
-      {}
-  }
-  return bpf_l3_csum_replace(ctx, off, from, to, flags);
-}
-
-SEC("helpers")
-int bpf_l4_csum_replace_(void *ctx, u64 off, u64 from, u64 to, u64 flags) {
-  switch (flags & 0xf) {
-    case 2:
-      return bpf_l4_csum_replace(ctx, off, bpf_htons(from), bpf_htons(to), flags);
-    case 4:
-      return bpf_l4_csum_replace(ctx, off, bpf_htonl(from), bpf_htonl(to), flags);
-    case 8:
-      return bpf_l4_csum_replace(ctx, off, bpf_htonll(from), bpf_htonll(to), flags);
-    default:
-      {}
-  }
-  return bpf_l4_csum_replace(ctx, off, from, to, flags);
-}
-
-#undef assert
-
index 9b4ee0e..29e3225 100644 (file)
@@ -35,6 +35,12 @@ char * bpf_program_license(void *program) {
   return prog->license();
 }
 
+unsigned bpf_program_kern_version(void *program) {
+  auto prog = static_cast<ebpf::BPFProgram *>(program);
+  if (!prog) return 0;
+  return prog->kern_version();
+}
+
 int bpf_program_table_fd(void *program, const char *table_name) {
   auto prog = static_cast<ebpf::BPFProgram *>(program);
   if (!prog) return -1;
index a6bb952..6f41d44 100644 (file)
@@ -11,6 +11,7 @@ void bpf_program_destroy(void *program);
 void * bpf_program_start(void *program, const char *name);
 size_t bpf_program_size(void *program, const char *name);
 char * bpf_program_license(void *program);
+unsigned bpf_program_kern_version(void *program);
 int bpf_program_table_fd(void *program, const char *table_name);
 
 #ifdef __cplusplus
index 36e1dae..9fe58a3 100644 (file)
@@ -1,12 +1,36 @@
 #ifndef __BPF_HELPERS_H
 #define __BPF_HELPERS_H
 
+#include <linux/bpf.h>
+#include <linux/version.h>
+
 /* helper macro to place programs, maps, license in
  * different sections in elf_bpf file. Section names
  * are interpreted by elf_bpf loader
  */
 #define SEC(NAME) __attribute__((section(NAME), used))
 
+// Changes to the macro require changes in BFrontendAction classes
+#define BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries) \
+struct _name##_table_t { \
+  _key_type key; \
+  _leaf_type leaf; \
+  _leaf_type * (*get) (_key_type *); \
+  int (*put) (_key_type *, _leaf_type *); \
+  int (*delete) (_key_type *); \
+  _leaf_type data[_max_entries]; \
+}; \
+__attribute__((section("maps/" _table_type))) \
+struct _name##_table_t _name
+
+// export this function to llvm by putting it into a specially named section
+//#define BPF_EXPORT(_ret, _name, ...) SEC("." #_name) _ret _name(__VA_ARGS__)
+#define BPF_EXPORT(_name) __attribute__((section("." #_name)))
+
+char _license[4] SEC("license") = "GPL";
+
+unsigned _version SEC("version") = LINUX_VERSION_CODE;
+
 /* helper functions called from eBPF programs written in C */
 static void *(*bpf_map_lookup_elem)(void *map, void *key) =
        (void *) BPF_FUNC_map_lookup_elem;
@@ -53,4 +77,169 @@ static int (*bpf_l4_csum_replace)(void *ctx, unsigned long long off, unsigned lo
                                  unsigned long long to, unsigned long long flags) =
        (void *) BPF_FUNC_l4_csum_replace;
 
+static inline u16 bpf_ntohs(u16 val) {
+  /* will be recognized by gcc into rotate insn and eventually rolw 8 */
+  return (val << 8) | (val >> 8);
+}
+
+static inline u32 bpf_ntohl(u32 val) {
+  /* gcc will use bswapsi2 insn */
+  return __builtin_bswap32(val);
+}
+
+static inline u64 bpf_ntohll(u64 val) {
+  /* gcc will use bswapdi2 insn */
+  return __builtin_bswap64(val);
+}
+
+static inline unsigned __int128 bpf_ntoh128(unsigned __int128 val) {
+  return (((unsigned __int128)bpf_ntohll(val) << 64) | (u64)bpf_ntohll(val >> 64));
+}
+
+static inline u16 bpf_htons(u16 val) {
+  return bpf_ntohs(val);
+}
+
+static inline u32 bpf_htonl(u32 val) {
+  return bpf_ntohl(val);
+}
+static inline u64 bpf_htonll(u64 val) {
+  return bpf_ntohll(val);
+}
+static inline unsigned __int128 bpf_hton128(unsigned __int128 val) {
+  return bpf_ntoh128(val);
+}
+
+static inline u64 load_dword(void *skb, u64 off) {
+  return ((u64)load_word(skb, off) << 4) | load_word(skb, off + 4);
+}
+
+void bpf_store_byte(void *skb, u64 off, u64 val) asm("llvm.bpf.store.byte");
+void bpf_store_half(void *skb, u64 off, u64 val) asm("llvm.bpf.store.half");
+void bpf_store_word(void *skb, u64 off, u64 val) asm("llvm.bpf.store.word");
+u64 bpf_pseudo_fd(u64, u64) asm("llvm.bpf.pseudo");
+static inline void bpf_store_dword(void *skb, u64 off, u64 val) {
+  bpf_store_word(skb, off, (u32)val);
+  bpf_store_word(skb, off + 4, val >> 32);
+}
+
+#define MASK(_n) ((_n) < 64 ? (1ull << (_n)) - 1 : ((u64)-1LL))
+#define MASK128(_n) ((_n) < 128 ? ((unsigned __int128)1 << (_n)) - 1 : ((unsigned __int128)-1))
+
+struct bpf_context;
+
+//static inline __attribute__((always_inline))
+SEC("helpers")
+u64 bpf_dext_pkt(void *pkt, u64 off, u64 bofs, u64 bsz) {
+  if (bofs == 0 && bsz == 8) {
+    return load_byte(pkt, off);
+  } else if (bofs + bsz <= 8) {
+    return load_byte(pkt, off) >> (8 - (bofs + bsz))  &  MASK(bsz);
+  } else if (bofs == 0 && bsz == 16) {
+    return load_half(pkt, off);
+  } else if (bofs + bsz <= 16) {
+    return load_half(pkt, off) >> (16 - (bofs + bsz))  &  MASK(bsz);
+  } else if (bofs == 0 && bsz == 32) {
+    return load_word(pkt, off);
+  } else if (bofs + bsz <= 32) {
+    return load_word(pkt, off) >> (32 - (bofs + bsz))  &  MASK(bsz);
+  } else if (bofs + bsz <= 64) {
+    return bpf_ntohll(load_dword(pkt, off)) >> (64 - (bofs + bsz))  &  MASK(bsz);
+  }
+  return 0;
+}
+
+//static inline __attribute__((always_inline))
+SEC("helpers")
+void bpf_dins_pkt(void *pkt, u64 off, u64 bofs, u64 bsz, u64 val) {
+  // The load_xxx function does a bswap before returning the short/word/dword,
+  // so the value in register will always be host endian. However, the bytes
+  // written back need to be in network order.
+  if (bofs == 0 && bsz == 8) {
+    bpf_skb_store_bytes(pkt, off, &val, 1, 0);
+  } else if (bofs + bsz <= 8) {
+    u8 v = load_byte(pkt, off);
+    v &= ~(MASK(bsz) << (8 - (bofs + bsz)));
+    v |= ((val & MASK(bsz)) << (8 - (bofs + bsz)));
+    bpf_skb_store_bytes(pkt, off, &v, 1, 0);
+  } else if (bofs == 0 && bsz == 16) {
+    u16 v = bpf_htons(val);
+    bpf_skb_store_bytes(pkt, off, &v, 2, 0);
+  } else if (bofs + bsz <= 16) {
+    u16 v = load_half(pkt, off);
+    v &= ~(MASK(bsz) << (16 - (bofs + bsz)));
+    v |= ((val & MASK(bsz)) << (16 - (bofs + bsz)));
+    v = bpf_htons(v);
+    bpf_skb_store_bytes(pkt, off, &v, 2, 0);
+  } else if (bofs == 0 && bsz == 32) {
+    u32 v = bpf_htonl(val);
+    bpf_skb_store_bytes(pkt, off, &v, 4, 0);
+  } else if (bofs + bsz <= 32) {
+    u32 v = load_word(pkt, off);
+    v &= ~(MASK(bsz) << (32 - (bofs + bsz)));
+    v |= ((val & MASK(bsz)) << (32 - (bofs + bsz)));
+    v = bpf_htonl(v);
+    bpf_skb_store_bytes(pkt, off, &v, 4, 0);
+  } else if (bofs == 0 && bsz == 64) {
+    u64 v = bpf_htonll(val);
+    bpf_skb_store_bytes(pkt, off, &v, 8, 0);
+  } else if (bofs + bsz <= 64) {
+    u64 v = load_dword(pkt, off);
+    v &= ~(MASK(bsz) << (64 - (bofs + bsz)));
+    v |= ((val & MASK(bsz)) << (64 - (bofs + bsz)));
+    v = bpf_htonll(v);
+    bpf_skb_store_bytes(pkt, off, &v, 8, 0);
+  }
+}
+
+SEC("helpers")
+void * bpf_map_lookup_elem_(uintptr_t map, void *key) {
+  return bpf_map_lookup_elem((void *)map, key);
+}
+
+SEC("helpers")
+int bpf_map_update_elem_(uintptr_t map, void *key, void *value, u64 flags) {
+  return bpf_map_update_elem((void *)map, key, value, flags);
+}
+
+SEC("helpers")
+int bpf_map_delete_elem_(uintptr_t map, void *key) {
+  return bpf_map_delete_elem((void *)map, key);
+}
+
+SEC("helpers")
+int bpf_skb_store_bytes_(void *ctx, u64 off, void *from, u64 len, u64 flags) {
+  return bpf_skb_store_bytes(ctx, off, from, len, flags);
+}
+
+SEC("helpers")
+int bpf_l3_csum_replace_(void *ctx, u64 off, u64 from, u64 to, u64 flags) {
+  switch (flags & 0xf) {
+    case 2:
+      return bpf_l3_csum_replace(ctx, off, bpf_htons(from), bpf_htons(to), flags);
+    case 4:
+      return bpf_l3_csum_replace(ctx, off, bpf_htonl(from), bpf_htonl(to), flags);
+    case 8:
+      return bpf_l3_csum_replace(ctx, off, bpf_htonll(from), bpf_htonll(to), flags);
+    default:
+      {}
+  }
+  return bpf_l3_csum_replace(ctx, off, from, to, flags);
+}
+
+SEC("helpers")
+int bpf_l4_csum_replace_(void *ctx, u64 off, u64 from, u64 to, u64 flags) {
+  switch (flags & 0xf) {
+    case 2:
+      return bpf_l4_csum_replace(ctx, off, bpf_htons(from), bpf_htons(to), flags);
+    case 4:
+      return bpf_l4_csum_replace(ctx, off, bpf_htonl(from), bpf_htonl(to), flags);
+    case 8:
+      return bpf_l4_csum_replace(ctx, off, bpf_htonll(from), bpf_htonll(to), flags);
+    default:
+      {}
+  }
+  return bpf_l4_csum_replace(ctx, off, from, to, flags);
+}
+
 #endif
index 29b3988..fcc6c83 100644 (file)
@@ -8,22 +8,22 @@
 #include <sys/utsname.h>
 #include <unistd.h>
 #include <vector>
+#include <linux/bpf.h>
 
 #include <clang/Basic/FileManager.h>
 #include <clang/Basic/TargetInfo.h>
 #include <clang/CodeGen/BackendUtil.h>
 #include <clang/CodeGen/CodeGenAction.h>
-#include <clang/CodeGen/ModuleBuilder.h>
 #include <clang/Driver/Compilation.h>
 #include <clang/Driver/Driver.h>
 #include <clang/Driver/Job.h>
 #include <clang/Driver/Tool.h>
 #include <clang/Frontend/CompilerInstance.h>
 #include <clang/Frontend/CompilerInvocation.h>
+#include <clang/Frontend/FrontendActions.h>
 #include <clang/Frontend/FrontendDiagnostic.h>
 #include <clang/Frontend/TextDiagnosticPrinter.h>
 #include <clang/FrontendTool/Utils.h>
-#include "clang/Parse/ParseAST.h"
 
 #include <llvm/ADT/STLExtras.h>
 #include <llvm/ExecutionEngine/MCJIT.h>
 #include "parser.h"
 #include "type_check.h"
 #include "codegen_llvm.h"
+#include "b_frontend_action.h"
 #include "bpf_program.h"
-
-#define KERNEL_MODULES_DIR "/lib/modules"
-
-// This is temporary, to be removed in the next commit
-#define HELPER_FILE "../../src/cc/bitops.c"
+#include "kbuild_helper.h"
 
 namespace ebpf {
 
@@ -108,177 +105,7 @@ BPFProgram::~BPFProgram() {
   ctx_.reset();
 }
 
-int BPFProgram::parse() {
-  int rc;
-
-  proto_parser_ = make_unique<ebpf::cc::Parser>(proto_filename_);
-  rc = proto_parser_->parse();
-  if (rc) {
-    fprintf(stderr, "In file: %s\n", filename_.c_str());
-    return rc;
-  }
-
-  parser_ = make_unique<ebpf::cc::Parser>(filename_);
-  rc = parser_->parse();
-  if (rc) {
-    fprintf(stderr, "In file: %s\n", filename_.c_str());
-    return rc;
-  }
-
-  //ebpf::cc::Printer printer(stderr);
-  //printer.visit(parser_->root_node_);
-
-  ebpf::cc::TypeCheck type_check(parser_->scopes_.get(), proto_parser_->scopes_.get(), parser_->pragmas_);
-  auto ret = type_check.visit(parser_->root_node_);
-  if (get<0>(ret) != 0 || get<1>(ret).size()) {
-    fprintf(stderr, "Type error @line=%d: %s\n", get<0>(ret), get<1>(ret).c_str());
-    exit(1);
-  }
-
-  codegen_ = ebpf::make_unique<ebpf::cc::CodegenLLVM>(mod_, parser_->scopes_.get(), proto_parser_->scopes_.get());
-  ret = codegen_->visit(parser_->root_node_);
-  if (get<0>(ret) != 0 || get<1>(ret).size()) {
-    fprintf(stderr, "Codegen error @line=%d: %s\n", get<0>(ret), get<1>(ret).c_str());
-    return get<0>(ret);
-  }
-
-  return 0;
-}
-
-// Helper with pushd/popd semantics
-class DirStack {
- public:
-  explicit DirStack(const char *dst) : ok_(false) {
-    if (getcwd(cwd_, sizeof(cwd_)) == NULL) {
-      ::perror("getcwd");
-      return;
-    }
-    if (::chdir(dst)) {
-      fprintf(stderr, "chdir(%s): %s\n", dst, strerror(errno));
-      return;
-    }
-    ok_ = true;
-  }
-  ~DirStack() {
-    if (!ok_) return;
-    if (::chdir(cwd_)) {
-      fprintf(stderr, "chdir(%s): %s\n", cwd_, strerror(errno));
-    }
-  }
-  bool ok() const { return ok_; }
-  const char * cwd() const { return cwd_; }
- private:
-  bool ok_;
-  char cwd_[256];
-};
-
-struct FileDeleter {
-  void operator() (FILE *fp) {
-    fclose(fp);
-  }
-};
-typedef std::unique_ptr<FILE, FileDeleter> FILEPtr;
-
-// Scoped class to manage the creation/deletion of tmpdirs
-class TmpDir {
- public:
-  explicit TmpDir(const string &prefix = "/tmp/bcc-")
-      : ok_(false), prefix_(prefix) {
-    prefix_ += "XXXXXX";
-    if (::mkdtemp((char *)prefix_.data()) == NULL)
-      ::perror("mkdtemp");
-    else
-      ok_ = true;
-  }
-  ~TmpDir() {
-    auto fn = [] (const char *path, const struct stat *, int) -> int {
-      return ::remove(path);
-    };
-    if (::ftw(prefix_.c_str(), fn, 20) < 0)
-      ::perror("ftw");
-    else
-      ::remove(prefix_.c_str());
-  }
-  bool ok() const { return ok_; }
-  const string & str() const { return prefix_; }
- private:
-  bool ok_;
-  string prefix_;
-};
-
-// Compute the kbuild flags for the currently running kernel
-// Do this by:
-//   1. Create temp Makefile with stub dummy.c
-//   2. Run module build on that makefile, saving the computed flags to a file
-//   3. Cache the file for fast flag lookup in subsequent runs
-//  Note: Depending on environment, different cache locations may be desired. In
-//  case we eventually support non-root user programs, cache in $HOME.
-
-// Makefile helper for kbuild_flags
-static int learn_flags(const string &tmpdir, const char *uname_release, const char *cachefile) {
-  {
-    // Create a kbuild file to generate the flags
-    string makefile = tmpdir + "/Makefile";
-    FILEPtr mf(::fopen(makefile.c_str(), "w"));
-    if (!mf)
-      return -1;
-    fprintf(&*mf, "obj-y := dummy.o\n");
-    fprintf(&*mf, "CACHEDIR=$(dir %s)\n", cachefile);
-    fprintf(&*mf, "$(CACHEDIR):\n");
-    fprintf(&*mf, "\t@mkdir -p $(CACHEDIR)\n");
-    fprintf(&*mf, "$(obj)/%%.o: $(src)/%%.c $(CACHEDIR)\n");
-    fprintf(&*mf, "\t@echo -n \"$(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) "
-                    "-D__KERNEL__ -Wno-unused-value -Wno-pointer-sign \" > %s\n", cachefile);
-  }
-  {
-    string cfile = tmpdir + "/dummy.c";
-    FILEPtr cf(::fopen(cfile.c_str(), "w"));
-    if (!cf)
-      return -1;
-  }
-  string cmd = "make -s";
-  cmd += " -C " KERNEL_MODULES_DIR "/" + string(uname_release) + "/build";
-  cmd += " M=" + tmpdir + " dummy.o";
-  int rc = ::system(cmd.c_str());
-  if (rc < 0) {
-    ::perror("system");
-    return -1;
-  }
-  return ::open(cachefile, O_RDONLY);
-}
-
-// read the flags from cache or learn
-int BPFProgram::kbuild_flags(const char *uname_release, vector<string> *cflags) {
-  char cachefile[256];
-  char *home = ::getenv("HOME");
-  if (home)
-    snprintf(cachefile, sizeof(cachefile), "%s/.cache/bcc/%s.flags", home, uname_release);
-  else
-    snprintf(cachefile, sizeof(cachefile), "/var/run/bcc/%s.flags", uname_release);
-  int cachefd = ::open(cachefile, O_RDONLY);
-  if (cachefd < 0) {
-    TmpDir tmpdir;
-    if (!tmpdir.ok())
-      return -1;
-    cachefd = learn_flags(tmpdir.str(), uname_release, cachefile);
-    if (cachefd < 0)
-      return -1;
-  }
-  FILEPtr f(::fdopen(cachefd, "r"));
-  size_t len = 0;
-  char *line = NULL;
-  ssize_t nread;
-  while ((nread = getdelim(&line, &len, ' ', &*f)) >= 0) {
-    if (nread == 0 || (nread == 1 && line[0] == ' ')) continue;
-    if (line[nread - 1] == ' ')
-      --nread;
-    cflags->push_back(string(line, nread));
-  }
-  free(line);
-  return 0;
-}
-
-int BPFProgram::load_helper(unique_ptr<llvm::Module> *mod) {
+int BPFProgram::load_file_module(unique_ptr<llvm::Module> *mod, const string &file) {
   using namespace clang;
 
   struct utsname un;
@@ -286,33 +113,39 @@ int BPFProgram::load_helper(unique_ptr<llvm::Module> *mod) {
   char kdir[256];
   snprintf(kdir, sizeof(kdir), "%s/%s/build", KERNEL_MODULES_DIR, un.release);
 
+  // clang needs to run inside the kernel dir
   DirStack dstack(kdir);
   if (!dstack.ok())
     return -1;
 
-  string file = string(dstack.cwd()) + "/" HELPER_FILE;
-  vector<const char *> flags_cstr({"-fsyntax-only", "-emit-llvm", "-o", "/dev/null",
-                                   "-c", file.c_str()});
+  string abs_file = string(dstack.cwd()) + "/" + file;
+  if (file[0] == '/')
+    abs_file = file;
+  vector<const char *> flags_cstr({"-O0", "-emit-llvm", "-I", dstack.cwd(),
+                                   "-x", "c", "-c", abs_file.c_str()});
 
+  KBuildHelper kbuild_helper;
   vector<string> kflags;
-  if (kbuild_flags(un.release, &kflags))
+  if (kbuild_helper.get_flags(un.release, &kflags))
     return -1;
   for (auto it = kflags.begin(); it != kflags.end(); ++it)
     flags_cstr.push_back(it->c_str());
 
+  // set up the error reporting class
   IntrusiveRefCntPtr<DiagnosticOptions> diag_opts(new DiagnosticOptions());
   auto diag_client = new TextDiagnosticPrinter(llvm::errs(), &*diag_opts);
 
   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
   DiagnosticsEngine diags(DiagID, &*diag_opts, diag_client);
 
+  // set up the command line argument wrapper
   driver::Driver drv("", "x86_64-unknown-linux-gnu", diags);
   drv.setTitle("bcc-clang-driver");
   drv.setCheckInputsExist(false);
 
   unique_ptr<driver::Compilation> compilation(drv.BuildCompilation(flags_cstr));
   if (!compilation)
-    return 0;
+    return -1;
 
   // expect exactly 1 job, otherwise error
   const driver::JobList &jobs = compilation->getJobs();
@@ -332,38 +165,54 @@ int BPFProgram::load_helper(unique_ptr<llvm::Module> *mod) {
 
   // Initialize a compiler invocation object from the clang (-cc1) arguments.
   const driver::ArgStringList &ccargs = cmd.getArguments();
-  auto invocation = make_unique<CompilerInvocation>();
-  CompilerInvocation::CreateFromArgs(*invocation, const_cast<const char **>(ccargs.data()),
-                                     const_cast<const char **>(ccargs.data()) + ccargs.size(), diags);
 
-  // Show the invocation, with -v.
-  if (invocation->getHeaderSearchOpts().Verbose)
-    jobs.Print(llvm::errs(), "\n", true);
+  // first pass
+  auto invocation1 = make_unique<CompilerInvocation>();
+  if (!CompilerInvocation::CreateFromArgs(*invocation1, const_cast<const char **>(ccargs.data()),
+                                          const_cast<const char **>(ccargs.data()) + ccargs.size(), diags))
+    return -1;
 
-  // Create a compiler instance to handle the actual work.
-  CompilerInstance compiler;
-  compiler.setInvocation(invocation.release());
+  CompilerInstance compiler1;
+  compiler1.setInvocation(invocation1.release());
+  compiler1.createDiagnostics();
 
-  // Create the compilers actual diagnostics engine.
-  compiler.createDiagnostics();
-  if (!compiler.hasDiagnostics())
+  // capture the rewritten c file
+  string out_str;
+  llvm::raw_string_ostream os(out_str);
+  BFrontendAction bact(os);
+  if (!compiler1.ExecuteAction(bact))
     return -1;
+  // this contains the open FDs
+  tables_ = bact.take_tables();
 
-  // Create and execute the frontend to generate an LLVM bitcode module.
-  EmitLLVMOnlyAction act(&*ctx_);
-  if (!compiler.ExecuteAction(act))
+  // second pass, clear input and take rewrite buffer
+  auto invocation2 = make_unique<CompilerInvocation>();
+  if (!CompilerInvocation::CreateFromArgs(*invocation2, const_cast<const char **>(ccargs.data()),
+                                          const_cast<const char **>(ccargs.data()) + ccargs.size(), diags))
     return -1;
-
-  *mod = act.takeModule();
+  CompilerInstance compiler2;
+  invocation2->getPreprocessorOpts().addRemappedFile("<bcc-memory-buffer>",
+                                                     llvm::MemoryBuffer::getMemBuffer(out_str).release());
+  invocation2->getFrontendOpts().Inputs.clear();
+  invocation2->getFrontendOpts().Inputs.push_back(FrontendInputFile("<bcc-memory-buffer>", IK_C));
+  // suppress warnings in the 2nd pass, but bail out on errors (our fault)
+  invocation2->getDiagnosticOpts().IgnoreWarnings = true;
+  compiler2.setInvocation(invocation2.release());
+  compiler2.createDiagnostics();
+
+  EmitLLVMOnlyAction ir_act(&*ctx_);
+  if (!compiler2.ExecuteAction(ir_act))
+    return -1;
+  *mod = ir_act.takeModule();
 
   return 0;
 }
 
 // Load in a pre-built list of functions into the initial Module object, then
 // build an ExecutionEngine.
-int BPFProgram::init_engine() {
+int BPFProgram::load_includes(const string &tmpfile) {
   unique_ptr<Module> mod;
-  if (load_helper(&mod))
+  if (load_file_module(&mod, tmpfile))
     return -1;
   mod_ = &*mod;
 
@@ -393,6 +242,47 @@ void BPFProgram::dump_ir() {
   PM.run(*mod_);
 }
 
+int BPFProgram::parse() {
+  int rc;
+
+  proto_parser_ = make_unique<ebpf::cc::Parser>(proto_filename_);
+  rc = proto_parser_->parse();
+  if (rc) {
+    fprintf(stderr, "In file: %s\n", filename_.c_str());
+    return rc;
+  }
+
+  parser_ = make_unique<ebpf::cc::Parser>(filename_);
+  rc = parser_->parse();
+  if (rc) {
+    fprintf(stderr, "In file: %s\n", filename_.c_str());
+    return rc;
+  }
+
+  //ebpf::cc::Printer printer(stderr);
+  //printer.visit(parser_->root_node_);
+
+  ebpf::cc::TypeCheck type_check(parser_->scopes_.get(), proto_parser_->scopes_.get(), parser_->pragmas_);
+  auto ret = type_check.visit(parser_->root_node_);
+  if (get<0>(ret) != 0 || get<1>(ret).size()) {
+    fprintf(stderr, "Type error @line=%d: %s\n", get<0>(ret), get<1>(ret).c_str());
+    return -1;
+  }
+
+  // TODO: clean this
+  if (load_includes("../../src/cc/bpf_helpers.h") < 0)
+    return -1;
+
+  codegen_ = ebpf::make_unique<ebpf::cc::CodegenLLVM>(mod_, parser_->scopes_.get(), proto_parser_->scopes_.get());
+  ret = codegen_->visit(parser_->root_node_);
+  if (get<0>(ret) != 0 || get<1>(ret).size()) {
+    fprintf(stderr, "Codegen error @line=%d: %s\n", get<0>(ret), get<1>(ret).c_str());
+    return get<0>(ret);
+  }
+
+  return 0;
+}
+
 int BPFProgram::finalize() {
   if (verifyModule(*mod_, &errs())) {
     if (flags_ & 1)
@@ -439,8 +329,20 @@ char * BPFProgram::license() const {
   return (char *)get<0>(section->second);
 }
 
+unsigned BPFProgram::kern_version() const {
+  auto section = sections_.find("version");
+  if (section == sections_.end())
+    return 0;
+
+  return *(unsigned *)get<0>(section->second);
+}
+
 int BPFProgram::table_fd(const string &name) const {
-  return codegen_->get_table_fd(name);
+  int fd = codegen_ ? codegen_->get_table_fd(name) : -1;
+  if (fd >= 0) return fd;
+  auto table_it = tables_->find(name);
+  if (table_it == tables_->end()) return -1;
+  return table_it->second.fd;
 }
 
 int BPFProgram::load(const string &filename, const string &proto_filename) {
@@ -450,10 +352,15 @@ int BPFProgram::load(const string &filename, const string &proto_filename) {
   }
   filename_ = filename;
   proto_filename_ = proto_filename;
-  if (int rc = init_engine())
-    return rc;
-  if (int rc = parse())
-    return rc;
+  if (proto_filename_.empty()) {
+    // direct load of .b file
+    if (int rc = load_includes(filename_))
+      return rc;
+  } else {
+    // old lex .b file
+    if (int rc = parse())
+      return rc;
+  }
   if (int rc = finalize())
     return rc;
   return 0;
index 36ae1c8..05a6a34 100644 (file)
@@ -31,6 +31,7 @@ class Module;
 }
 
 namespace ebpf {
+class BPFTable;
 
 namespace cc {
 class CodegenLLVM;
@@ -43,7 +44,8 @@ class BPFProgram {
   int parse();
   int finalize();
   void dump_ir();
-  int load_helper(std::unique_ptr<llvm::Module> *mod);
+  int load_file_module(std::unique_ptr<llvm::Module> *mod, const std::string &file);
+  int load_includes(const std::string &tmpfile);
   int kbuild_flags(const char *uname_release, std::vector<std::string> *cflags);
  public:
   BPFProgram(unsigned flags);
@@ -53,6 +55,7 @@ class BPFProgram {
   size_t size(const std::string &name) const;
   int table_fd(const std::string &name) const;
   char * license() const;
+  unsigned kern_version() const;
  private:
   unsigned flags_;  // 0x1 for printing
   std::string filename_;
@@ -64,6 +67,7 @@ class BPFProgram {
   std::unique_ptr<ebpf::cc::Parser> proto_parser_;
   std::unique_ptr<ebpf::cc::CodegenLLVM> codegen_;
   std::map<std::string, std::tuple<uint8_t *, uintptr_t>> sections_;
+  std::unique_ptr<std::map<std::string, BPFTable>> tables_;
 };
 
 }  // namespace ebpf
index 4c8ee10..1ffdda0 100644 (file)
@@ -1151,7 +1151,10 @@ StatusTuple CodegenLLVM::visit_func_decl_stmt_node(FuncDeclStmtNode *n) {
     VariableDeclStmtNode *formal = it->get();
     if (formal->is_struct()) {
       StructType *stype;
-      TRY2(lookup_struct_type(formal, &stype));
+      //TRY2(lookup_struct_type(formal, &stype));
+      auto var = (StructVariableDeclStmtNode *)formal;
+      stype = mod_->getTypeByName("struct." + var->struct_id_->name_);
+      if (!stype) return mkstatus_(n, "could not find type %s", var->struct_id_->c_str());
       formals.push_back(PointerType::getUnqual(stype));
     } else {
       formals.push_back(B.getIntNTy(formal->bit_width_));
diff --git a/src/cc/kbuild_helper.cc b/src/cc/kbuild_helper.cc
new file mode 100644 (file)
index 0000000..2376836
--- /dev/null
@@ -0,0 +1,78 @@
+#include <fcntl.h>
+#include <ftw.h>
+#include "kbuild_helper.h"
+
+namespace ebpf {
+
+using std::string;
+using std::vector;
+
+KBuildHelper::KBuildHelper() {
+  char *home = ::getenv("HOME");
+  if (home)
+    cache_dir_ = string(home) + "/.cache/bcc";
+  else
+    cache_dir_ = "/var/run/bcc";
+}
+
+// Makefile helper for kbuild_flags
+int KBuildHelper::learn_flags(const string &tmpdir, const char *uname_release, const char *cachefile) {
+  {
+    // Create a kbuild file to generate the flags
+    string makefile = tmpdir + "/Makefile";
+    FILEPtr mf(::fopen(makefile.c_str(), "w"));
+    if (!mf)
+      return -1;
+    fprintf(&*mf, "obj-y := dummy.o\n");
+    fprintf(&*mf, "CACHEDIR=$(dir %s)\n", cachefile);
+    fprintf(&*mf, "$(CACHEDIR):\n");
+    fprintf(&*mf, "\t@mkdir -p $(CACHEDIR)\n");
+    fprintf(&*mf, "$(obj)/%%.o: $(src)/%%.c $(CACHEDIR)\n");
+    fprintf(&*mf, "\t@echo -n \"$(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) "
+                    "-D__KERNEL__ -Wno-unused-value -Wno-pointer-sign \" > %s\n", cachefile);
+  }
+  {
+    string cfile = tmpdir + "/dummy.c";
+    FILEPtr cf(::fopen(cfile.c_str(), "w"));
+    if (!cf)
+      return -1;
+  }
+  string cmd = "make -s";
+  cmd += " -C " KERNEL_MODULES_DIR "/" + string(uname_release) + "/build";
+  cmd += " M=" + tmpdir + " dummy.o";
+  int rc = ::system(cmd.c_str());
+  if (rc < 0) {
+    ::perror("system");
+    return -1;
+  }
+  return ::open(cachefile, O_RDONLY);
+}
+
+// read the flags from cache or learn
+int KBuildHelper::get_flags(const char *uname_release, vector<string> *cflags) {
+  char cachefile[256];
+  snprintf(cachefile, sizeof(cachefile), "%s/%s.flags", cache_dir_.c_str(), uname_release);
+  int cachefd = ::open(cachefile, O_RDONLY);
+  if (cachefd < 0) {
+    TmpDir tmpdir;
+    if (!tmpdir.ok())
+      return -1;
+    cachefd = learn_flags(tmpdir.str(), uname_release, cachefile);
+    if (cachefd < 0)
+      return -1;
+  }
+  FILEPtr f(::fdopen(cachefd, "r"));
+  size_t len = 0;
+  char *line = NULL;
+  ssize_t nread;
+  while ((nread = getdelim(&line, &len, ' ', &*f)) >= 0) {
+    if (nread == 0 || (nread == 1 && line[0] == ' ')) continue;
+    if (line[nread - 1] == ' ')
+      --nread;
+    cflags->push_back(string(line, nread));
+  }
+  free(line);
+  return 0;
+}
+
+}  // namespace ebpf
diff --git a/src/cc/kbuild_helper.h b/src/cc/kbuild_helper.h
new file mode 100644 (file)
index 0000000..e84c582
--- /dev/null
@@ -0,0 +1,89 @@
+#include <cstring>
+#include <memory>
+#include <string>
+#include <vector>
+#include <unistd.h>
+
+#define KERNEL_MODULES_DIR "/lib/modules"
+
+namespace ebpf {
+
+struct FileDeleter {
+  void operator() (FILE *fp) {
+    fclose(fp);
+  }
+};
+typedef std::unique_ptr<FILE, FileDeleter> FILEPtr;
+
+// Helper with pushd/popd semantics
+class DirStack {
+ public:
+  explicit DirStack(const char *dst) : ok_(false) {
+    if (getcwd(cwd_, sizeof(cwd_)) == NULL) {
+      ::perror("getcwd");
+      return;
+    }
+    if (::chdir(dst)) {
+      fprintf(stderr, "chdir(%s): %s\n", dst, strerror(errno));
+      return;
+    }
+    ok_ = true;
+  }
+  ~DirStack() {
+    if (!ok_) return;
+    if (::chdir(cwd_)) {
+      fprintf(stderr, "chdir(%s): %s\n", cwd_, strerror(errno));
+    }
+  }
+  bool ok() const { return ok_; }
+  const char * cwd() const { return cwd_; }
+ private:
+  bool ok_;
+  char cwd_[256];
+};
+
+// Scoped class to manage the creation/deletion of tmpdirs
+class TmpDir {
+ public:
+  explicit TmpDir(const std::string &prefix = "/tmp/bcc-")
+      : ok_(false), prefix_(prefix) {
+    prefix_ += "XXXXXX";
+    if (::mkdtemp((char *)prefix_.data()) == NULL)
+      ::perror("mkdtemp");
+    else
+      ok_ = true;
+  }
+  ~TmpDir() {
+    auto fn = [] (const char *path, const struct stat *, int) -> int {
+      return ::remove(path);
+    };
+    if (::ftw(prefix_.c_str(), fn, 20) < 0)
+      ::perror("ftw");
+    else
+      ::remove(prefix_.c_str());
+  }
+  bool ok() const { return ok_; }
+  const std::string & str() const { return prefix_; }
+ private:
+  bool ok_;
+  std::string prefix_;
+};
+
+// Compute the kbuild flags for the currently running kernel
+// Do this by:
+//   1. Create temp Makefile with stub dummy.c
+//   2. Run module build on that makefile, saving the computed flags to a file
+//   3. Cache the file for fast flag lookup in subsequent runs
+//  Note: Depending on environment, different cache locations may be desired. In
+//  case we eventually support non-root user programs, cache in $HOME.
+class KBuildHelper {
+ private:
+  int learn_flags(const std::string &tmpdir, const char *uname_release, const char *cachefile);
+ public:
+  KBuildHelper();
+  int get_flags(const char *uname_release, std::vector<std::string> *cflags);
+ private:
+  std::string cache_dir_;
+};
+
+}  // namespace ebpf
index d8b5fd6..c77204f 100644 (file)
@@ -103,7 +103,7 @@ std::string tmp_str_cc;
 "u32"                   return save(Tok::TU32);
 "u64"                   return save(Tok::TU64);
 
-[a-zA-Z][a-zA-Z0-9_]*   return save(Tok::TIDENTIFIER);
+[a-zA-Z_][a-zA-Z0-9_]*  return save(Tok::TIDENTIFIER);
 [0-9]+                  return save(Tok::TINTEGER);
 0x[0-9a-fA-F]+          return save(Tok::THEXINTEGER);
 
index 4816950..8ed6e4b 100644 (file)
@@ -85,7 +85,7 @@ char bpf_log_buf[LOG_BUF_SIZE];
 
 int bpf_prog_load(enum bpf_prog_type prog_type,
                   const struct bpf_insn *insns, int prog_len,
-                  const char *license)
+                  const char *license, unsigned kern_version)
 {
   union bpf_attr attr = {
     .prog_type = prog_type,
@@ -97,7 +97,7 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
     .log_level = 1,
   };
 
-  attr.kern_version = LINUX_VERSION_CODE;
+  attr.kern_version = kern_version;
   bpf_log_buf[0] = 0;
 
   int ret = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
index 3e2e851..7eb5d21 100644 (file)
@@ -19,7 +19,7 @@ int bpf_get_next_key(int fd, void *key, void *next_key);
 
 int bpf_prog_load(enum bpf_prog_type prog_type,
                  const struct bpf_insn *insns, int insn_len,
-                 const char *license);
+                 const char *license, unsigned kern_version);
 int bpf_attach_socket(int sockfd, int progfd);
 int bpf_attach_filter(int progfd, const char *prog_name, uint32_t ifindex,
                       uint8_t prio, uint32_t classid);
index e439f0f..b58602d 100644 (file)
@@ -1,8 +1,14 @@
-add_test(NAME py_test1 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-  COMMAND ${TEST_WRAPPER} py_test1 ${CMAKE_CURRENT_SOURCE_DIR}/test1.py namespace)
+add_test(NAME py_test1_b WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  COMMAND ${TEST_WRAPPER} py_test1_b namespace ${CMAKE_CURRENT_SOURCE_DIR}/test1.py test1.b proto.b)
+add_test(NAME py_test1_c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  COMMAND ${TEST_WRAPPER} py_test1_c namespace ${CMAKE_CURRENT_SOURCE_DIR}/test1.py test1.c)
 add_test(NAME py_test2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-  COMMAND ${TEST_WRAPPER} py_test2 ${CMAKE_CURRENT_SOURCE_DIR}/test2.py namespace)
+  COMMAND ${TEST_WRAPPER} py_test2 namespace ${CMAKE_CURRENT_SOURCE_DIR}/test2.py test2.b proto.b)
 add_test(NAME py_trace1 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-  COMMAND ${TEST_WRAPPER} py_trace1 ${CMAKE_CURRENT_SOURCE_DIR}/trace1.py sudo)
-add_test(NAME py_trace2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-  COMMAND ${TEST_WRAPPER} py_trace2 ${CMAKE_CURRENT_SOURCE_DIR}/trace2.py sudo)
+  COMMAND ${TEST_WRAPPER} py_trace1 sudo ${CMAKE_CURRENT_SOURCE_DIR}/trace1.py trace1.b kprobe.b)
+add_test(NAME py_trace2_b WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  COMMAND ${TEST_WRAPPER} py_trace2_b sudo ${CMAKE_CURRENT_SOURCE_DIR}/trace2.py trace2.b kprobe.b)
+add_test(NAME py_trace2_c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  COMMAND ${TEST_WRAPPER} py_trace2_c sudo ${CMAKE_CURRENT_SOURCE_DIR}/trace2.py trace2.c)
+add_test(NAME py_trace3_c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  COMMAND ${TEST_WRAPPER} py_trace3_c sudo ${CMAKE_CURRENT_SOURCE_DIR}/trace3.py trace3.c)
index 8187418..8112455 100644 (file)
@@ -1,18 +1,6 @@
 
 #packed "true"
 
-struct skbuff {
-  u32 len:32;
-  u32 pkt_type:32;
-  u32 mark:32;
-  u32 queue_mapping:32;
-  u32 protocol:32;
-  u32 vlan_present:32;
-  u32 vlan_tci:32;
-  u32 vlan_proto:32;
-  u32 priority:32;
-};
-
 struct ethernet {
   u64 dst:48;
   u64 src:48;
index a2c4c5b..4884b7f 100644 (file)
@@ -1,5 +1,3 @@
-#packed "false"
-
 struct IPKey {
   u32 dip:32;
   u32 sip:32;
@@ -10,7 +8,11 @@ struct IPLeaf {
 };
 Table<IPKey, IPLeaf, FIXED_MATCH, AUTO> stats(1024);
 
-u32 main(struct proto::skbuff *skb) {
+struct skbuff {
+  u32 type:32;
+};
+
+u32 main(struct skbuff *skb) {
   u32 ret:32 = 0;
 
   goto proto::ethernet;
diff --git a/tests/jit/test1.c b/tests/jit/test1.c
new file mode 100644 (file)
index 0000000..05bc692
--- /dev/null
@@ -0,0 +1,102 @@
+#include "../../src/cc/bpf_helpers.h"
+
+struct IPKey {
+  u32 dip;
+  u32 sip;
+};
+struct IPLeaf {
+  u64 rx_pkts;
+  u64 tx_pkts;
+};
+
+BPF_TABLE("hash", struct IPKey, struct IPLeaf, stats, 256);
+
+BPF_EXPORT(main)
+int foo(struct __sk_buff *skb) {
+  size_t next = 0, cur = 0;
+ethernet:
+{
+  cur = next; next += 14;
+
+  switch (bpf_dext_pkt(skb, cur + 12, 0, 16)) {
+    case 0x800: goto ip;
+    case 0x8100: goto dot1q;
+    default: goto EOP;
+  }
+}
+dot1q:
+{
+  cur = next; next += 4;
+
+  switch (bpf_dext_pkt(skb, cur + 2, 0, 16)) {
+    case 0x0800: goto ip;
+    default: goto EOP;
+  }
+}
+
+ip:
+{
+  cur = next; next += 20;
+
+  int rx = 0;
+  int tx = 0;
+  struct IPKey key = {0};
+  if (bpf_dext_pkt(skb, cur + 16, 0, 32) > bpf_dext_pkt(skb, cur + 12, 0, 32)) {
+    key.sip = bpf_dext_pkt(skb, cur + 12, 0, 32);
+    key.dip = bpf_dext_pkt(skb, cur + 16, 0, 32);
+    rx = 1;
+  } else {
+    key.dip = bpf_dext_pkt(skb, cur + 12, 0, 32);
+    key.sip = bpf_dext_pkt(skb, cur + 16, 0, 32);
+    tx = 1;
+  }
+  // try to get here:
+  //stats[key].rx_pkts += rx;
+  //stats[key].tx_pkts += tx;
+  // or here:
+  //struct IPLeaf *leaf = stats[key];
+  //if (leaf) {
+  //  __sync_fetch_and_add(&leaf->rx_pkts, rx);
+  //  __sync_fetch_and_add(&leaf->tx_pkts, tx);
+  //}
+  struct IPLeaf *leaf;
+  leaf = stats.get(&key);
+  if (!leaf) {
+    struct IPLeaf zleaf = {0};
+    stats.put(&key, &zleaf);
+    leaf = stats.get(&key);
+  }
+  if (leaf) {
+    __sync_fetch_and_add(&leaf->rx_pkts, rx);
+    __sync_fetch_and_add(&leaf->tx_pkts, tx);
+  }
+
+  switch (bpf_dext_pkt(skb, cur + 9, 0, 8)) {
+    case 6: goto tcp;
+    case 17: goto udp;
+    //case 47: goto gre;
+    default: goto EOP;
+  }
+}
+
+udp:
+{
+  cur = next; next += 8;
+
+  switch (bpf_dext_pkt(skb, cur + 2, 0, 16)) {
+    //case 8472: goto vxlan;
+    //case 4789: goto vxlan;
+    default: goto EOP;
+  }
+}
+
+tcp:
+{
+  cur = next; next += 20;
+
+  goto EOP;
+}
+
+EOP:
+  return 0;
+}
index b717984..b156d1b 100755 (executable)
@@ -7,8 +7,14 @@ from ctypes import c_uint, c_ulong, Structure
 from netaddr import IPAddress
 from bpf import BPF
 from subprocess import check_call
+import sys
 from unittest import main, TestCase
 
+arg1 = sys.argv.pop(1)
+arg2 = ""
+if len(sys.argv) > 1:
+  arg2 = sys.argv.pop(1)
+
 class Key(Structure):
     _fields_ = [("dip", c_uint),
                 ("sip", c_uint)]
@@ -18,7 +24,7 @@ class Leaf(Structure):
 
 class TestBPFSocket(TestCase):
     def setUp(self):
-        self.prog = BPF("main", "test1.b", "proto.b", debug=0)
+        self.prog = BPF("main", arg1, arg2, debug=0)
         self.prog.attach("eth0")
         self.stats = self.prog.table("stats", Key, Leaf)
 
index 8c5b6df..5f3d964 100644 (file)
@@ -13,7 +13,11 @@ struct IPLeaf {
 };
 Table<IPKey, IPLeaf, FIXED_MATCH, NONE> xlate(1024);
 
-u32 main (struct proto::skbuff *skb) {
+struct skbuff {
+  u32 type:32;
+};
+
+u32 main (struct skbuff *skb) {
   u32 ret:32 = 1;
 
   u32 orig_dip:32 = 0;
index 914793a..9afe058 100755 (executable)
@@ -4,9 +4,15 @@ from ctypes import c_uint, c_ulonglong, Structure
 from netaddr import IPAddress
 from bpf import BPF
 from socket import socket, AF_INET, SOCK_DGRAM
+import sys
 from time import sleep
 from unittest import main, TestCase
 
+arg1 = sys.argv.pop(1)
+arg2 = ""
+if len(sys.argv) > 1:
+  arg2 = sys.argv.pop(1)
+
 class Key(Structure):
     _fields_ = [("dip", c_uint),
                 ("sip", c_uint)]
@@ -17,7 +23,7 @@ class Leaf(Structure):
 
 class TestBPFSocket(TestCase):
     def setUp(self):
-        self.prog = BPF("main", "test2.b", "proto.b",
+        self.prog = BPF("main", arg1, arg2,
                 BPF.BPF_PROG_TYPE_SCHED_CLS, debug=0)
         with open("/sys/class/net/eth0/ifindex") as f:
             ifindex = int(f.read())
index b6bb367..eddf561 100755 (executable)
@@ -4,8 +4,14 @@ from ctypes import c_uint, c_ulong, Structure
 from bpf import BPF
 import os
 from time import sleep
+import sys
 from unittest import main, TestCase
 
+arg1 = sys.argv.pop(1)
+arg2 = ""
+if len(sys.argv) > 1:
+  arg2 = sys.argv.pop(1)
+
 class Key(Structure):
     _fields_ = [("fd", c_ulong)]
 class Leaf(Structure):
@@ -14,7 +20,7 @@ class Leaf(Structure):
 
 class TestKprobe(TestCase):
     def setUp(self):
-        self.prog = BPF("trace1", "trace1.b", "kprobe.b",
+        self.prog = BPF("trace1", arg1, arg2,
                 prog_type=BPF.BPF_PROG_TYPE_KPROBE, debug=0)
         self.prog.load("sys_wr")
         self.prog.load("sys_rd")
diff --git a/tests/jit/trace2.c b/tests/jit/trace2.c
new file mode 100644 (file)
index 0000000..9b70bbb
--- /dev/null
@@ -0,0 +1,17 @@
+#include <linux/ptrace.h>
+#include "../../src/cc/bpf_helpers.h"
+struct Ptr { u64 ptr; };
+struct Counters { u64 stat1; };
+BPF_TABLE("hash", struct Ptr, struct Counters, stats, 1024);
+
+BPF_EXPORT(count_sched)
+int count_sched(struct pt_regs *ctx) {
+  struct Ptr key = {.ptr=ctx->bx};
+#if 1
+  stats.data[(u64)&key].stat1++;
+#else
+  struct Counters zleaf = {0};
+  stats.upsert(&key, &zleaf)->stat1++;
+#endif
+  return 0;
+}
index d83f6d6..57c9b60 100755 (executable)
@@ -3,8 +3,14 @@
 from ctypes import c_uint, c_ulong, Structure
 from bpf import BPF
 from time import sleep
+import sys
 from unittest import main, TestCase
 
+arg1 = sys.argv.pop(1)
+arg2 = ""
+if len(sys.argv) > 1:
+  arg2 = sys.argv.pop(1)
+
 class Ptr(Structure):
     _fields_ = [("ptr", c_ulong)]
 class Counters(Structure):
@@ -12,7 +18,7 @@ class Counters(Structure):
 
 class TestTracingEvent(TestCase):
     def setUp(self):
-        self.prog = BPF("trace2", "trace2.b", "kprobe.b",
+        self.prog = BPF("trace2", arg1, arg2,
                 prog_type=BPF.BPF_PROG_TYPE_KPROBE, debug=0)
         self.prog.load("count_sched")
         self.stats = self.prog.table("stats", Ptr, Counters)
diff --git a/tests/jit/trace3.c b/tests/jit/trace3.c
new file mode 100644 (file)
index 0000000..58dbe25
--- /dev/null
@@ -0,0 +1,51 @@
+#include <linux/ptrace.h>
+#include <linux/blkdev.h>
+#include "../../src/cc/bpf_helpers.h"
+struct Request { u64 rq; };
+struct Time { u64 start; };
+BPF_TABLE("hash", struct Request, struct Time, requests, 1024);
+#define SLOTS 100
+BPF_TABLE("array", u32, u64, latency, SLOTS);
+
+static u32 log2(u32 v) {
+  u32 r, shift;
+
+  r = (v > 0xFFFF) << 4; v >>= r;
+  shift = (v > 0xFF) << 3; v >>= shift; r |= shift;
+  shift = (v > 0xF) << 2; v >>= shift; r |= shift;
+  shift = (v > 0x3) << 1; v >>= shift; r |= shift;
+  r |= (v >> 1);
+  return r;
+}
+
+static u32 log2l(u64 v) {
+  u32 hi = v >> 32;
+  if (hi)
+    return log2(hi) + 32;
+  else
+    return log2(v);
+}
+
+BPF_EXPORT(probe_blk_start_request)
+int probe_blk_start_request(struct pt_regs *ctx) {
+  struct Request rq = {.rq = ctx->di};
+  struct Time tm = {.start = bpf_ktime_get_ns()};
+  requests.put(&rq, &tm);
+  return 0;
+}
+
+BPF_EXPORT(probe_blk_update_request)
+int probe_blk_update_request(struct pt_regs *ctx) {
+  struct Request rq = {.rq = ctx->di};
+  struct Time *tm = requests.get(&rq);
+  if (!tm) return 0;
+  u64 delta = bpf_ktime_get_ns() - tm->start;
+  requests.delete(&rq);
+  u64 lg = log2l(delta);
+  u64 base = 1ull << lg;
+  u32 index = (lg * 64 + (delta - base) * 64 / base) * 3 / 64;
+  if (index >= SLOTS)
+    index = SLOTS - 1;
+  __sync_fetch_and_add(&latency.data[(u64)&index], 1);
+  return 0;
+}
diff --git a/tests/jit/trace3.py b/tests/jit/trace3.py
new file mode 100755 (executable)
index 0000000..9fa7ee7
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+from ctypes import c_uint, c_ulong, Structure
+from bpf import BPF
+from time import sleep
+import sys
+from unittest import main, TestCase
+
+arg1 = sys.argv.pop(1)
+arg2 = ""
+if len(sys.argv) > 1:
+  arg2 = sys.argv.pop(1)
+
+
+class TestBlkRequest(TestCase):
+    def setUp(self):
+        self.prog = BPF("trace3", arg1, arg2,
+                prog_type=BPF.BPF_PROG_TYPE_KPROBE, debug=0)
+        self.prog.load("probe_blk_start_request")
+        self.prog.load("probe_blk_update_request")
+        self.latency = self.prog.table("latency", c_uint, c_ulong)
+        self.prog.attach_kprobe("blk_start_request", "probe_blk_start_request", 0, -1)
+        self.prog.attach_kprobe("blk_update_request", "probe_blk_update_request", 0, -1)
+
+    def test_blk1(self):
+        import subprocess
+        import os
+        for i in range(0, 2):
+            with open("/srv/trace3.txt", "w") as f:
+                f.write("a" * 4096 * 4096)
+            subprocess.call(["sync"])
+        os.unlink("/srv/trace3.txt")
+        for key in self.latency.iter():
+            leaf = self.latency.get(key)
+            print("latency %u:" % key.value, "count %u" % leaf.value)
+        sys.stdout.flush()
+
+if __name__ == "__main__":
+    main()
index 16baf4b..067c46e 100755 (executable)
@@ -3,8 +3,8 @@
 #set -x
 
 name=$1; shift
-cmd=$1; shift
 kind=$1; shift
+cmd=$1; shift
 
 PYTHONPATH=@CMAKE_SOURCE_DIR@/src
 LD_LIBRARY_PATH=@CMAKE_BINARY_DIR@:@CMAKE_BINARY_DIR@/src/cc
@@ -30,11 +30,11 @@ function ns_run() {
   sudo ip netns exec $ns ip link set eth0 up
   sudo ip addr add dev $ns.out 172.16.1.1/24
   sudo ip link set $ns.out up
-  sudo bash -c "PYTHONPATH=$PYTHONPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH ip netns exec $ns $cmd $@"
+  sudo bash -c "PYTHONPATH=$PYTHONPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH ip netns exec $ns $cmd $1 $2"
   return $?
 }
 function sudo_run() {
-  sudo bash -c "PYTHONPATH=$PYTHONPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH $cmd $@"
+  sudo bash -c "PYTHONPATH=$PYTHONPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH $cmd $1 $2"
   return $?
 }