Add debug flag to enable prints from kernel verifier
authorBrenden Blanco <bblanco@plumgrid.com>
Tue, 25 Aug 2015 06:42:42 +0000 (23:42 -0700)
committerBrenden Blanco <bblanco@plumgrid.com>
Tue, 25 Aug 2015 06:42:42 +0000 (23:42 -0700)
Add flag to BPF() to enable debug prints from the verifier. Use by
passing 0x2 to debug= in the constructor. Add docstring for BPF
constructor.

Also, optimize bpf_prog_load so that by default the kernel is not
asked to print the verbose output. The program will be "loaded" a second
time to capture the verifier failure message.

Fixes: #147
Signed-off-by: Brenden Blanco <bblanco@plumgrid.com>
src/cc/libbpf.c
src/python/bpf/__init__.py

index 5e88dcf..17c907e 100644 (file)
@@ -122,8 +122,6 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
                   const char *license, unsigned kern_version,
                   char *log_buf, unsigned log_buf_size)
 {
-  log_buf = log_buf ? log_buf : bpf_log_buf;
-  log_buf_size = log_buf_size ? log_buf_size : LOG_BUF_SIZE;
   union bpf_attr attr = {
     .prog_type = prog_type,
     .insns = ptr_to_u64((void *) insns),
@@ -131,14 +129,19 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
     .license = ptr_to_u64((void *) license),
     .log_buf = ptr_to_u64(log_buf),
     .log_size = log_buf_size,
-    .log_level = 1,
+    .log_level = log_buf ? 1 : 0,
   };
 
   attr.kern_version = kern_version;
-  log_buf[0] = 0;
+  if (log_buf)
+    log_buf[0] = 0;
 
   int ret = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
-  if (ret < 0 && log_buf == bpf_log_buf) {
+  if (ret < 0 && !log_buf) {
+    // caller did not specify log_buf but failure should be printed,
+    // so call recursively and print the result to stderr
+    bpf_prog_load(prog_type, insns, prog_len, license, kern_version,
+        bpf_log_buf, LOG_BUF_SIZE);
     fprintf(stderr, "bpf: %s\n%s\n", strerror(errno), bpf_log_buf);
   }
   return ret;
index b53c721..ffb9246 100644 (file)
@@ -259,6 +259,21 @@ class BPF(object):
         return filename
 
     def __init__(self, src_file="", hdr_file="", text=None, debug=0):
+        """Create a a new BPF module with the given source code.
+
+        Note:
+            All fields are marked as optional, but either `src_file` or `text`
+            must be supplied, and not both.
+
+        Args:
+            src_file (Optional[str]): Path to a source file for the module
+            hdr_file (Optional[str]): Path to a helper header file for the `src_file`
+            text (Optional[str]): Contents of a source file for the module
+            debug (Optional[int]): Flags used for debug prints, can be |'d together
+                0x1: print LLVM IR to stderr
+                0x2: print BPF bytecode to stderr
+        """
+
         self.debug = debug
         self.funcs = {}
         self.tables = {}
@@ -284,16 +299,19 @@ class BPF(object):
         if lib.bpf_function_start(self.module, func_name.encode("ascii")) == None:
             raise Exception("Unknown program %s" % func_name)
 
+        log_buf = ct.create_string_buffer(65536) if self.debug else None
+
         fd = lib.bpf_prog_load(prog_type,
                 lib.bpf_function_start(self.module, func_name.encode("ascii")),
                 lib.bpf_function_size(self.module, func_name.encode("ascii")),
                 lib.bpf_module_license(self.module),
                 lib.bpf_module_kern_version(self.module),
-                None, 0)
+                log_buf, ct.sizeof(log_buf) if log_buf else 0)
+
+        if self.debug & 0x2:
+            print(log_buf.value.decode(), file=sys.stderr)
 
         if fd < 0:
-            print((ct.c_char * 65536).in_dll(lib, "bpf_log_buf").value)
-            #print(ct.c_char_p.in_dll(lib, "bpf_log_buf").value)
             raise Exception("Failed to load BPF program %s" % func_name)
 
         fn = BPF.Function(self, func_name, fd)