From 5668bc3f9a71f88061ffab72d4250ed0cad72d1d Mon Sep 17 00:00:00 2001
From: Nathan Scott <nathans@redhat.com>
Date: Tue, 16 Jan 2018 10:58:10 +1100
Subject: [PATCH 01/11] correct error reporting on python text compilation

The exception raised in the BPF class constructor assumed
that the "src_file" argument was used to pass eBPF code -
this is not the case for many of the tools scripts, which
tend to use "text".

Signed-off-by: Nathan Scott <nathans@redhat.com>
(cherry picked from commit d10c642949ba3e78a066106db35e532f146b803f)
---
 src/python/bcc/__init__.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/python/bcc/__init__.py b/src/python/bcc/__init__.py
index 8e38bee..331a948 100644
--- a/src/python/bcc/__init__.py
+++ b/src/python/bcc/__init__.py
@@ -287,6 +287,8 @@ class BPF(object):
         if text:
             self.module = lib.bpf_module_create_c_from_string(text.encode("ascii"),
                     self.debug, cflags_array, len(cflags_array))
+            if not self.module:
+                raise Exception("Failed to compile BPF text:\n%s" % text)
         else:
             src_file = BPF._find_file(src_file)
             hdr_file = BPF._find_file(hdr_file)
@@ -296,9 +298,8 @@ class BPF(object):
             else:
                 self.module = lib.bpf_module_create_c(src_file.encode("ascii"),
                         self.debug, cflags_array, len(cflags_array))
-
-        if not self.module:
-            raise Exception("Failed to compile BPF module %s" % src_file)
+            if not self.module:
+                raise Exception("Failed to compile BPF module %s" % src_file)
 
         for usdt_context in usdt_contexts:
             usdt_context.attach_uprobes(self)
-- 
2.16.1


From 9ba86c63442a2254d76ce68dd9536a47d5b3dbd4 Mon Sep 17 00:00:00 2001
From: Teng Qin <qinteng@fb.com>
Date: Fri, 15 Dec 2017 15:46:57 -0800
Subject: [PATCH 02/11] Clean libbpf interface arguments for tracing events

(cherry picked from commit b68e67b4be8abf4dfb1b3efc27d1b150f61339e9)
---
 src/cc/api/BPF.cc          |  8 ++++----
 src/cc/libbpf.c            | 39 +++++++++++++++++++++++----------------
 src/cc/libbpf.h            | 17 ++++++++---------
 src/lua/bcc/bpf.lua        |  8 ++------
 src/lua/bcc/libbcc.lua     | 12 +++++-------
 src/python/bcc/__init__.py | 20 ++++++++++----------
 src/python/bcc/libbcc.py   | 10 +++++-----
 7 files changed, 57 insertions(+), 57 deletions(-)

diff --git a/src/cc/api/BPF.cc b/src/cc/api/BPF.cc
index dfda5b7..c13d180 100644
--- a/src/cc/api/BPF.cc
+++ b/src/cc/api/BPF.cc
@@ -172,7 +172,7 @@ StatusTuple BPF::attach_kprobe(const std::string& kernel_func,
 
   void* res =
       bpf_attach_kprobe(probe_fd, attach_type, probe_event.c_str(), kernel_func.c_str(),
-                        pid, cpu, group_fd, cb, cb_cookie);
+                        cb, cb_cookie);
 
   if (!res) {
     TRY2(unload_func(probe_func));
@@ -208,7 +208,7 @@ StatusTuple BPF::attach_uprobe(const std::string& binary_path,
 
   void* res =
       bpf_attach_uprobe(probe_fd, attach_type, probe_event.c_str(), binary_path.c_str(),
-                        offset, pid, cpu, group_fd, cb, cb_cookie);
+                        offset, pid, cb, cb_cookie);
 
   if (!res) {
     TRY2(unload_func(probe_func));
@@ -275,8 +275,8 @@ StatusTuple BPF::attach_tracepoint(const std::string& tracepoint,
   TRY2(load_func(probe_func, BPF_PROG_TYPE_TRACEPOINT, probe_fd));
 
   void* res =
-      bpf_attach_tracepoint(probe_fd, tp_category.c_str(), tp_name.c_str(), pid,
-                            cpu, group_fd, cb, cb_cookie);
+      bpf_attach_tracepoint(probe_fd, tp_category.c_str(), tp_name.c_str(), cb,
+                            cb_cookie);
 
   if (!res) {
     TRY2(unload_func(probe_func));
diff --git a/src/cc/libbpf.c b/src/cc/libbpf.c
index ef6daf3..65976dd 100644
--- a/src/cc/libbpf.c
+++ b/src/cc/libbpf.c
@@ -527,8 +527,8 @@ int bpf_attach_socket(int sock, int prog) {
 }
 
 static int bpf_attach_tracing_event(int progfd, const char *event_path,
-    struct perf_reader *reader, int pid, int cpu, int group_fd) {
-  int efd, pfd;
+                                    struct perf_reader *reader, int pid) {
+  int efd, pfd, cpu = 0;
   ssize_t bytes;
   char buf[256];
   struct perf_event_attr attr = {};
@@ -553,7 +553,15 @@ static int bpf_attach_tracing_event(int progfd, const char *event_path,
   attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN;
   attr.sample_period = 1;
   attr.wakeup_events = 1;
-  pfd = syscall(__NR_perf_event_open, &attr, pid, cpu, group_fd, PERF_FLAG_FD_CLOEXEC);
+  // PID filter is only possible for uprobe events.
+  if (pid < 0)
+    pid = -1;
+  // perf_event_open API doesn't allow both pid and cpu to be -1.
+  // So only set it to -1 when PID is not -1.
+  // Tracing events do not do CPU filtering in any cases.
+  if (pid != -1)
+    cpu = -1;
+  pfd = syscall(__NR_perf_event_open, &attr, pid, cpu, -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
   if (pfd < 0) {
     fprintf(stderr, "perf_event_open(%s/id): %s\n", event_path, strerror(errno));
     return -1;
@@ -575,9 +583,8 @@ static int bpf_attach_tracing_event(int progfd, const char *event_path,
   return 0;
 }
 
-void * bpf_attach_kprobe(int progfd, enum bpf_probe_attach_type attach_type, const char *ev_name,
-                        const char *fn_name,
-                        pid_t pid, int cpu, int group_fd,
+void *bpf_attach_kprobe(int progfd, enum bpf_probe_attach_type attach_type,
+                        const char *ev_name, const char *fn_name,
                         perf_reader_cb cb, void *cb_cookie)
 {
   int kfd;
@@ -609,7 +616,7 @@ void * bpf_attach_kprobe(int progfd, enum bpf_probe_attach_type attach_type, con
   close(kfd);
 
   snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/%ss/%s", event_type, event_alias);
-  if (bpf_attach_tracing_event(progfd, buf, reader, pid, cpu, group_fd) < 0)
+  if (bpf_attach_tracing_event(progfd, buf, reader, -1 /* PID */) < 0)
     goto error;
 
   return reader;
@@ -681,10 +688,10 @@ static void exit_mount_ns(int fd) {
     perror("setns");
 }
 
-void * bpf_attach_uprobe(int progfd, enum bpf_probe_attach_type attach_type, const char *ev_name,
-                        const char *binary_path, uint64_t offset,
-                        pid_t pid, int cpu, int group_fd,
-                        perf_reader_cb cb, void *cb_cookie)
+void *bpf_attach_uprobe(int progfd, enum bpf_probe_attach_type attach_type,
+                        const char *ev_name, const char *binary_path,
+                        uint64_t offset, pid_t pid, perf_reader_cb cb,
+                        void *cb_cookie)
 {
   char buf[PATH_MAX];
   char event_alias[PATH_MAX];
@@ -726,7 +733,7 @@ void * bpf_attach_uprobe(int progfd, enum bpf_probe_attach_type attach_type, con
   ns_fd = -1;
 
   snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/%ss/%s", event_type, event_alias);
-  if (bpf_attach_tracing_event(progfd, buf, reader, pid, cpu, group_fd) < 0)
+  if (bpf_attach_tracing_event(progfd, buf, reader, pid) < 0)
     goto error;
 
   return reader;
@@ -780,9 +787,9 @@ int bpf_detach_uprobe(const char *ev_name)
 }
 
 
-void * bpf_attach_tracepoint(int progfd, const char *tp_category,
-                             const char *tp_name, int pid, int cpu,
-                             int group_fd, perf_reader_cb cb, void *cb_cookie) {
+void *bpf_attach_tracepoint(int progfd, const char *tp_category,
+                            const char *tp_name, perf_reader_cb cb,
+                            void *cb_cookie) {
   char buf[256];
   struct perf_reader *reader = NULL;
 
@@ -792,7 +799,7 @@ void * bpf_attach_tracepoint(int progfd, const char *tp_category,
 
   snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/%s/%s",
            tp_category, tp_name);
-  if (bpf_attach_tracing_event(progfd, buf, reader, pid, cpu, group_fd) < 0)
+  if (bpf_attach_tracing_event(progfd, buf, reader, -1 /* PID */) < 0)
     goto error;
 
   return reader;
diff --git a/src/cc/libbpf.h b/src/cc/libbpf.h
index ceb0b7a..7e729a7 100644
--- a/src/cc/libbpf.h
+++ b/src/cc/libbpf.h
@@ -70,23 +70,22 @@ typedef void (*perf_reader_cb)(void *cb_cookie, int pid, uint64_t callchain_num,
 typedef void (*perf_reader_raw_cb)(void *cb_cookie, void *raw, int raw_size);
 typedef void (*perf_reader_lost_cb)(uint64_t lost);
 
-void * bpf_attach_kprobe(int progfd, enum bpf_probe_attach_type attach_type,
+void *bpf_attach_kprobe(int progfd, enum bpf_probe_attach_type attach_type,
                         const char *ev_name, const char *fn_name,
-                        pid_t pid, int cpu, int group_fd,
                         perf_reader_cb cb, void *cb_cookie);
 
 int bpf_detach_kprobe(const char *ev_name);
 
-void * bpf_attach_uprobe(int progfd, enum bpf_probe_attach_type attach_type,
-                        const char *ev_name, const char *binary_path, uint64_t offset,
-                        pid_t pid, int cpu, int group_fd,
-                        perf_reader_cb cb, void *cb_cookie);
+void *bpf_attach_uprobe(int progfd, enum bpf_probe_attach_type attach_type,
+                        const char *ev_name, const char *binary_path,
+                        uint64_t offset, pid_t pid, perf_reader_cb cb,
+                        void *cb_cookie);
 
 int bpf_detach_uprobe(const char *ev_name);
 
-void * bpf_attach_tracepoint(int progfd, const char *tp_category,
-                             const char *tp_name, int pid, int cpu,
-                             int group_fd, perf_reader_cb cb, void *cb_cookie);
+void *bpf_attach_tracepoint(int progfd, const char *tp_category,
+                            const char *tp_name, perf_reader_cb cb,
+                            void *cb_cookie);
 int bpf_detach_tracepoint(const char *tp_category, const char *tp_name);
 
 void * bpf_open_perf_buffer(perf_reader_raw_cb raw_cb,
diff --git a/src/lua/bcc/bpf.lua b/src/lua/bcc/bpf.lua
index db27c75..2e3954a 100644
--- a/src/lua/bcc/bpf.lua
+++ b/src/lua/bcc/bpf.lua
@@ -189,9 +189,7 @@ function Bpf:attach_uprobe(args)
   local retprobe = args.retprobe and 1 or 0
 
   local res = libbcc.bpf_attach_uprobe(fn.fd, retprobe, ev_name, path, addr,
-    args.pid or -1,
-    args.cpu or 0,
-    args.group_fd or -1, nil, nil) -- TODO; reader callback
+    args.pid or -1, nil, nil) -- TODO; reader callback
 
   assert(res ~= nil, "failed to attach BPF to uprobe")
   self:probe_store("uprobe", ev_name, res)
@@ -209,9 +207,7 @@ function Bpf:attach_kprobe(args)
   local retprobe = args.retprobe and 1 or 0
 
   local res = libbcc.bpf_attach_kprobe(fn.fd, retprobe, ev_name, event,
-    args.pid or -1,
-    args.cpu or 0,
-    args.group_fd or -1, nil, nil) -- TODO; reader callback
+    nil, nil) -- TODO; reader callback
 
   assert(res ~= nil, "failed to attach BPF to kprobe")
   self:probe_store("kprobe", ev_name, res)
diff --git a/src/lua/bcc/libbcc.lua b/src/lua/bcc/libbcc.lua
index dee1c0e..e663647 100644
--- a/src/lua/bcc/libbcc.lua
+++ b/src/lua/bcc/libbcc.lua
@@ -43,16 +43,14 @@ typedef void (*perf_reader_cb)(void *cb_cookie, int pid, uint64_t callchain_num,
 typedef void (*perf_reader_raw_cb)(void *cb_cookie, void *raw, int raw_size);
 typedef void (*perf_reader_lost_cb)(uint64_t lost);
 
-void * bpf_attach_kprobe(int progfd, int attach_type, const char *ev_name,
-                        const char *fn_name,
-                        int pid, int cpu, int group_fd,
-                        perf_reader_cb cb, void *cb_cookie);
+void *bpf_attach_kprobe(int progfd, int attach_type, const char *ev_name,
+                        const char *fn_name, perf_reader_cb cb,
+                        void *cb_cookie);
 
 int bpf_detach_kprobe(const char *ev_name);
 
-void * bpf_attach_uprobe(int progfd, int attach_type, const char *ev_name,
-                        const char *binary_path, uint64_t offset,
-                        int pid, int cpu, int group_fd,
+void *bpf_attach_uprobe(int progfd, int attach_type, const char *ev_name,
+                        const char *binary_path, uint64_t offset, int pid,
                         perf_reader_cb cb, void *cb_cookie);
 
 int bpf_detach_uprobe(const char *ev_name);
diff --git a/src/python/bcc/__init__.py b/src/python/bcc/__init__.py
index 331a948..9964513 100644
--- a/src/python/bcc/__init__.py
+++ b/src/python/bcc/__init__.py
@@ -520,8 +520,8 @@ class BPF(object):
         fn = self.load_func(fn_name, BPF.KPROBE)
         ev_name = "p_" + event.replace("+", "_").replace(".", "_")
         res = lib.bpf_attach_kprobe(fn.fd, 0, ev_name.encode("ascii"),
-                event.encode("ascii"), pid, cpu, group_fd,
-                self._reader_cb_impl, ct.cast(id(self), ct.py_object))
+                event.encode("ascii"), self._reader_cb_impl,
+                ct.cast(id(self), ct.py_object))
         res = ct.cast(res, ct.c_void_p)
         if not res:
             raise Exception("Failed to attach BPF to kprobe")
@@ -557,8 +557,8 @@ class BPF(object):
         fn = self.load_func(fn_name, BPF.KPROBE)
         ev_name = "r_" + event.replace("+", "_").replace(".", "_")
         res = lib.bpf_attach_kprobe(fn.fd, 1, ev_name.encode("ascii"),
-                event.encode("ascii"), pid, cpu, group_fd,
-                self._reader_cb_impl, ct.cast(id(self), ct.py_object))
+                event.encode("ascii"), self._reader_cb_impl,
+                ct.cast(id(self), ct.py_object))
         res = ct.cast(res, ct.c_void_p)
         if not res:
             raise Exception("Failed to attach BPF to kprobe")
@@ -681,8 +681,8 @@ class BPF(object):
         fn = self.load_func(fn_name, BPF.TRACEPOINT)
         (tp_category, tp_name) = tp.split(':')
         res = lib.bpf_attach_tracepoint(fn.fd, tp_category.encode("ascii"),
-                tp_name.encode("ascii"), pid, cpu, group_fd,
-                self._reader_cb_impl, ct.cast(id(self), ct.py_object))
+                tp_name.encode("ascii"), self._reader_cb_impl,
+                ct.cast(id(self), ct.py_object))
         res = ct.cast(res, ct.c_void_p)
         if not res:
             raise Exception("Failed to attach BPF to tracepoint")
@@ -834,8 +834,8 @@ class BPF(object):
         fn = self.load_func(fn_name, BPF.KPROBE)
         ev_name = self._get_uprobe_evname("p", path, addr, pid)
         res = lib.bpf_attach_uprobe(fn.fd, 0, ev_name.encode("ascii"),
-                path.encode("ascii"), addr, pid, cpu, group_fd,
-                self._reader_cb_impl, ct.cast(id(self), ct.py_object))
+                path.encode("ascii"), addr, pid, self._reader_cb_impl,
+                ct.cast(id(self), ct.py_object))
         res = ct.cast(res, ct.c_void_p)
         if not res:
             raise Exception("Failed to attach BPF to uprobe")
@@ -884,8 +884,8 @@ class BPF(object):
         fn = self.load_func(fn_name, BPF.KPROBE)
         ev_name = self._get_uprobe_evname("r", path, addr, pid)
         res = lib.bpf_attach_uprobe(fn.fd, 1, ev_name.encode("ascii"),
-                path.encode("ascii"), addr, pid, cpu, group_fd,
-                self._reader_cb_impl, ct.cast(id(self), ct.py_object))
+                path.encode("ascii"), addr, pid, self._reader_cb_impl,
+                ct.cast(id(self), ct.py_object))
         res = ct.cast(res, ct.c_void_p)
         if not res:
             raise Exception("Failed to attach BPF to uprobe")
diff --git a/src/python/bcc/libbcc.py b/src/python/bcc/libbcc.py
index 7a63865..b761b1a 100644
--- a/src/python/bcc/libbcc.py
+++ b/src/python/bcc/libbcc.py
@@ -90,18 +90,18 @@ _CB_TYPE = ct.CFUNCTYPE(None, ct.py_object, ct.c_int,
         ct.c_ulonglong, ct.POINTER(ct.c_ulonglong))
 _RAW_CB_TYPE = ct.CFUNCTYPE(None, ct.py_object, ct.c_void_p, ct.c_int)
 _LOST_CB_TYPE = ct.CFUNCTYPE(None, ct.c_ulonglong)
-lib.bpf_attach_kprobe.argtypes = [ct.c_int, ct.c_int, ct.c_char_p, ct.c_char_p, ct.c_int,
-        ct.c_int, ct.c_int, _CB_TYPE, ct.py_object]
+lib.bpf_attach_kprobe.argtypes = [ct.c_int, ct.c_int, ct.c_char_p, ct.c_char_p,
+        _CB_TYPE, ct.py_object]
 lib.bpf_detach_kprobe.restype = ct.c_int
 lib.bpf_detach_kprobe.argtypes = [ct.c_char_p]
 lib.bpf_attach_uprobe.restype = ct.c_void_p
 lib.bpf_attach_uprobe.argtypes = [ct.c_int, ct.c_int, ct.c_char_p, ct.c_char_p,
-        ct.c_ulonglong, ct.c_int, ct.c_int, ct.c_int, _CB_TYPE, ct.py_object]
+        ct.c_ulonglong, ct.c_int, _CB_TYPE, ct.py_object]
 lib.bpf_detach_uprobe.restype = ct.c_int
 lib.bpf_detach_uprobe.argtypes = [ct.c_char_p]
 lib.bpf_attach_tracepoint.restype = ct.c_void_p
-lib.bpf_attach_tracepoint.argtypes = [ct.c_int, ct.c_char_p, ct.c_char_p, ct.c_int,
-        ct.c_int, ct.c_int, _CB_TYPE, ct.py_object]
+lib.bpf_attach_tracepoint.argtypes = [ct.c_int, ct.c_char_p, ct.c_char_p,
+        _CB_TYPE, ct.py_object]
 lib.bpf_detach_tracepoint.restype = ct.c_int
 lib.bpf_detach_tracepoint.argtypes = [ct.c_char_p, ct.c_char_p]
 lib.bpf_open_perf_buffer.restype = ct.c_void_p
-- 
2.16.1


From c8da0c9824eb82a3a4fa24edea3103944ee1bc14 Mon Sep 17 00:00:00 2001
From: Teng Qin <qinteng@fb.com>
Date: Fri, 15 Dec 2017 15:53:53 -0800
Subject: [PATCH 03/11] Clean Python interface arguments for tracing events

(cherry picked from commit fd2440564a5f5b38fd190777d98f1ff550ff34d4)
---
 src/python/bcc/__init__.py  | 35 +++++++++++++----------------------
 tests/python/test_trace1.py |  6 +++---
 tests/python/test_trace2.py |  2 +-
 tests/python/test_trace3.py |  4 ++--
 tools/deadlock_detector.py  |  4 +---
 tools/funccount.py          |  6 ++----
 tools/stackcount.py         |  6 ++----
 7 files changed, 24 insertions(+), 39 deletions(-)

diff --git a/src/python/bcc/__init__.py b/src/python/bcc/__init__.py
index 9964513..456caa2 100644
--- a/src/python/bcc/__init__.py
+++ b/src/python/bcc/__init__.py
@@ -500,8 +500,7 @@ class BPF(object):
         del self.open_kprobes[name]
         _num_open_probes -= 1
 
-    def attach_kprobe(self, event="", fn_name="", event_re="",
-            pid=-1, cpu=0, group_fd=-1):
+    def attach_kprobe(self, event="", fn_name="", event_re=""):
 
         # allow the caller to glob multiple functions together
         if event_re:
@@ -509,8 +508,7 @@ class BPF(object):
             self._check_probe_quota(len(matches))
             for line in matches:
                 try:
-                    self.attach_kprobe(event=line, fn_name=fn_name, pid=pid,
-                            cpu=cpu, group_fd=group_fd)
+                    self.attach_kprobe(event=line, fn_name=fn_name)
                 except:
                     pass
             return
@@ -539,15 +537,13 @@ class BPF(object):
             raise Exception("Failed to detach BPF from kprobe")
         self._del_kprobe(ev_name)
 
-    def attach_kretprobe(self, event="", fn_name="", event_re="",
-            pid=-1, cpu=0, group_fd=-1):
+    def attach_kretprobe(self, event="", fn_name="", event_re=""):
 
         # allow the caller to glob multiple functions together
         if event_re:
             for line in BPF.get_kprobe_functions(event_re):
                 try:
-                    self.attach_kretprobe(event=line, fn_name=fn_name, pid=pid,
-                            cpu=cpu, group_fd=group_fd)
+                    self.attach_kretprobe(event=line, fn_name=fn_name)
                 except:
                     pass
             return
@@ -649,10 +645,8 @@ class BPF(object):
                         results.append(tp)
         return results
 
-    def attach_tracepoint(self, tp="", tp_re="", fn_name="", pid=-1,
-                          cpu=0, group_fd=-1):
-        """attach_tracepoint(tp="", tp_re="", fn_name="", pid=-1,
-                             cpu=0, group_fd=-1)
+    def attach_tracepoint(self, tp="", tp_re="", fn_name=""):
+        """attach_tracepoint(tp="", tp_re="", fn_name="")
 
         Run the bpf function denoted by fn_name every time the kernel tracepoint
         specified by 'tp' is hit. The optional parameters pid, cpu, and group_fd
@@ -674,8 +668,7 @@ class BPF(object):
 
         if tp_re:
             for tp in BPF.get_tracepoints(tp_re):
-                self.attach_tracepoint(tp=tp, fn_name=fn_name, pid=pid,
-                                       cpu=cpu, group_fd=group_fd)
+                self.attach_tracepoint(tp=tp, fn_name=fn_name)
             return
 
         fn = self.load_func(fn_name, BPF.TRACEPOINT)
@@ -795,9 +788,9 @@ class BPF(object):
             return "%s_%s_0x%x_%d" % (prefix, self._probe_repl.sub("_", path), addr, pid)
 
     def attach_uprobe(self, name="", sym="", sym_re="", addr=None,
-            fn_name="", pid=-1, cpu=0, group_fd=-1):
+            fn_name="", pid=-1):
         """attach_uprobe(name="", sym="", sym_re="", addr=None, fn_name=""
-                         pid=-1, cpu=0, group_fd=-1)
+                         pid=-1)
 
         Run the bpf function denoted by fn_name every time the symbol sym in
         the library or binary 'name' is encountered. The real address addr may
@@ -824,8 +817,7 @@ class BPF(object):
             self._check_probe_quota(len(addresses))
             for sym_addr in addresses:
                 self.attach_uprobe(name=name, addr=sym_addr,
-                                   fn_name=fn_name, pid=pid, cpu=cpu,
-                                   group_fd=group_fd)
+                                   fn_name=fn_name, pid=pid)
             return
 
         (path, addr) = BPF._check_path_symbol(name, sym, addr, pid)
@@ -861,9 +853,9 @@ class BPF(object):
         self._del_uprobe(ev_name)
 
     def attach_uretprobe(self, name="", sym="", sym_re="", addr=None,
-            fn_name="", pid=-1, cpu=0, group_fd=-1):
+            fn_name="", pid=-1):
         """attach_uretprobe(name="", sym="", sym_re="", addr=None, fn_name=""
-                            pid=-1, cpu=0, group_fd=-1)
+                            pid=-1)
 
         Run the bpf function denoted by fn_name every time the symbol sym in
         the library or binary 'name' finishes execution. See attach_uprobe for
@@ -873,8 +865,7 @@ class BPF(object):
         if sym_re:
             for sym_addr in BPF.get_user_addresses(name, sym_re):
                 self.attach_uretprobe(name=name, addr=sym_addr,
-                                      fn_name=fn_name, pid=pid, cpu=cpu,
-                                      group_fd=group_fd)
+                                      fn_name=fn_name, pid=pid)
             return
 
         name = str(name)
diff --git a/tests/python/test_trace1.py b/tests/python/test_trace1.py
index 9b7b94a..301791c 100755
--- a/tests/python/test_trace1.py
+++ b/tests/python/test_trace1.py
@@ -27,9 +27,9 @@ class TestKprobe(TestCase):
     def setUp(self):
         b = BPF(arg1, arg2, debug=0)
         self.stats = b.get_table("stats", Key, Leaf)
-        b.attach_kprobe(event="sys_write", fn_name="sys_wr", pid=0, cpu=-1)
-        b.attach_kprobe(event="sys_read", fn_name="sys_rd", pid=0, cpu=-1)
-        b.attach_kprobe(event="htab_map_get_next_key", fn_name="sys_rd", pid=0, cpu=-1)
+        b.attach_kprobe(event="sys_write", fn_name="sys_wr")
+        b.attach_kprobe(event="sys_read", fn_name="sys_rd")
+        b.attach_kprobe(event="htab_map_get_next_key", fn_name="sys_rd")
 
     def test_trace1(self):
         with open("/dev/null", "a") as f:
diff --git a/tests/python/test_trace2.py b/tests/python/test_trace2.py
index 04e29a3..8e4516f 100755
--- a/tests/python/test_trace2.py
+++ b/tests/python/test_trace2.py
@@ -26,7 +26,7 @@ class TestTracingEvent(TestCase):
     def setUp(self):
         b = BPF(text=text, debug=0)
         self.stats = b.get_table("stats")
-        b.attach_kprobe(event="finish_task_switch", fn_name="count_sched", pid=0, cpu=-1)
+        b.attach_kprobe(event="finish_task_switch", fn_name="count_sched")
 
     def test_sched1(self):
         for i in range(0, 100):
diff --git a/tests/python/test_trace3.py b/tests/python/test_trace3.py
index 10e6266..a317290 100755
--- a/tests/python/test_trace3.py
+++ b/tests/python/test_trace3.py
@@ -19,9 +19,9 @@ class TestBlkRequest(TestCase):
         b = BPF(arg1, arg2, debug=0)
         self.latency = b.get_table("latency", c_uint, c_ulong)
         b.attach_kprobe(event="blk_start_request",
-                fn_name="probe_blk_start_request", pid=-1, cpu=0)
+                fn_name="probe_blk_start_request")
         b.attach_kprobe(event="blk_update_request",
-                fn_name="probe_blk_update_request", pid=-1, cpu=0)
+                fn_name="probe_blk_update_request")
 
     def test_blk1(self):
         import subprocess
diff --git a/tools/deadlock_detector.py b/tools/deadlock_detector.py
index a788faa..bb9447f 100755
--- a/tools/deadlock_detector.py
+++ b/tools/deadlock_detector.py
@@ -468,9 +468,7 @@ def main():
     bpf = BPF(src_file='deadlock_detector.c')
 
     # Trace where threads are created
-    bpf.attach_kretprobe(
-        event='sys_clone', fn_name='trace_clone', pid=args.pid
-    )
+    bpf.attach_kretprobe(event='sys_clone', fn_name='trace_clone')
 
     # We must trace unlock first, otherwise in the time we attached the probe
     # on lock() and have not yet attached the probe on unlock(), a thread can
diff --git a/tools/funccount.py b/tools/funccount.py
index 822c662..98fa04b 100755
--- a/tools/funccount.py
+++ b/tools/funccount.py
@@ -90,8 +90,7 @@ class Probe(object):
             for index, function in self.trace_functions.items():
                 self.bpf.attach_kprobe(
                         event=function,
-                        fn_name="trace_count_%d" % index,
-                        pid=self.pid or -1)
+                        fn_name="trace_count_%d" % index)
         elif self.type == "p" and self.library:
             for index, function in self.trace_functions.items():
                 self.bpf.attach_uprobe(
@@ -103,8 +102,7 @@ class Probe(object):
             for index, function in self.trace_functions.items():
                 self.bpf.attach_tracepoint(
                         tp=function,
-                        fn_name="trace_count_%d" % index,
-                        pid=self.pid or -1)
+                        fn_name="trace_count_%d" % index)
         elif self.type == "u":
             pass    # Nothing to do -- attach already happened in `load`
 
diff --git a/tools/stackcount.py b/tools/stackcount.py
index 7973432..c03bf1f 100755
--- a/tools/stackcount.py
+++ b/tools/stackcount.py
@@ -90,13 +90,11 @@ class Probe(object):
                 self.matched = self.bpf.num_open_uprobes()
             else:
                 self.bpf.attach_kprobe(event_re=self.pattern,
-                                       fn_name="trace_count",
-                                       pid=self.pid or -1)
+                                       fn_name="trace_count")
                 self.matched = self.bpf.num_open_kprobes()
         elif self.type == "t":
             self.bpf.attach_tracepoint(tp_re=self.pattern,
-                                       fn_name="trace_count",
-                                       pid=self.pid or -1)
+                                       fn_name="trace_count")
             self.matched = self.bpf.num_open_tracepoints()
         elif self.type == "u":
             pass    # Nothing to do -- attach already happened in `load`
-- 
2.16.1


From 40ccc465728a82379348848b7a4f958578f096cb Mon Sep 17 00:00:00 2001
From: Teng Qin <qinteng@fb.com>
Date: Fri, 15 Dec 2017 16:08:04 -0800
Subject: [PATCH 04/11] Clean C++ interface arguments for tracing events

(cherry picked from commit d3b9d6a1d9afe31ae574308f51336d0869d7d553)
---
 src/cc/api/BPF.cc | 61 ++++++++++++++++++++++---------------------------------
 src/cc/api/BPF.h  | 55 +++++++++++++++++++++++--------------------------
 2 files changed, 49 insertions(+), 67 deletions(-)

diff --git a/src/cc/api/BPF.cc b/src/cc/api/BPF.cc
index c13d180..56c14f9 100644
--- a/src/cc/api/BPF.cc
+++ b/src/cc/api/BPF.cc
@@ -161,7 +161,6 @@ StatusTuple BPF::detach_all() {
 StatusTuple BPF::attach_kprobe(const std::string& kernel_func,
                                const std::string& probe_func,
                                bpf_probe_attach_type attach_type,
-                               pid_t pid, int cpu, int group_fd,
                                perf_reader_cb cb, void* cb_cookie) {
   std::string probe_event = get_kprobe_event(kernel_func, attach_type);
   if (kprobes_.find(probe_event) != kprobes_.end())
@@ -170,9 +169,8 @@ StatusTuple BPF::attach_kprobe(const std::string& kernel_func,
   int probe_fd;
   TRY2(load_func(probe_func, BPF_PROG_TYPE_KPROBE, probe_fd));
 
-  void* res =
-      bpf_attach_kprobe(probe_fd, attach_type, probe_event.c_str(), kernel_func.c_str(),
-                        cb, cb_cookie);
+  void* res = bpf_attach_kprobe(probe_fd, attach_type, probe_event.c_str(),
+                                kernel_func.c_str(), cb, cb_cookie);
 
   if (!res) {
     TRY2(unload_func(probe_func));
@@ -192,8 +190,7 @@ StatusTuple BPF::attach_uprobe(const std::string& binary_path,
                                const std::string& symbol,
                                const std::string& probe_func,
                                uint64_t symbol_addr,
-                               bpf_probe_attach_type attach_type,
-                               pid_t pid, int cpu, int group_fd,
+                               bpf_probe_attach_type attach_type, pid_t pid,
                                perf_reader_cb cb, void* cb_cookie) {
   std::string module;
   uint64_t offset;
@@ -207,8 +204,8 @@ StatusTuple BPF::attach_uprobe(const std::string& binary_path,
   TRY2(load_func(probe_func, BPF_PROG_TYPE_KPROBE, probe_fd));
 
   void* res =
-      bpf_attach_uprobe(probe_fd, attach_type, probe_event.c_str(), binary_path.c_str(),
-                        offset, pid, cb, cb_cookie);
+      bpf_attach_uprobe(probe_fd, attach_type, probe_event.c_str(),
+                        binary_path.c_str(), offset, pid, cb, cb_cookie);
 
   if (!res) {
     TRY2(unload_func(probe_func));
@@ -226,8 +223,7 @@ StatusTuple BPF::attach_uprobe(const std::string& binary_path,
   return StatusTuple(0);
 }
 
-StatusTuple BPF::attach_usdt(const USDT& usdt, pid_t pid, int cpu,
-                             int group_fd) {
+StatusTuple BPF::attach_usdt(const USDT& usdt, pid_t pid) {
   for (const auto& u : usdt_)
     if (u == usdt) {
       bool failed = false;
@@ -259,7 +255,6 @@ StatusTuple BPF::attach_usdt(const USDT& usdt, pid_t pid, int cpu,
 
 StatusTuple BPF::attach_tracepoint(const std::string& tracepoint,
                                    const std::string& probe_func,
-                                   pid_t pid, int cpu, int group_fd,
                                    perf_reader_cb cb, void* cb_cookie) {
   if (tracepoints_.find(tracepoint) != tracepoints_.end())
     return StatusTuple(-1, "Tracepoint %s already attached",
@@ -274,9 +269,8 @@ StatusTuple BPF::attach_tracepoint(const std::string& tracepoint,
   int probe_fd;
   TRY2(load_func(probe_func, BPF_PROG_TYPE_TRACEPOINT, probe_fd));
 
-  void* res =
-      bpf_attach_tracepoint(probe_fd, tp_category.c_str(), tp_name.c_str(), cb,
-                            cb_cookie);
+  void* res = bpf_attach_tracepoint(probe_fd, tp_category.c_str(),
+                                    tp_name.c_str(), cb, cb_cookie);
 
   if (!res) {
     TRY2(unload_func(probe_func));
@@ -309,7 +303,7 @@ StatusTuple BPF::attach_perf_event(uint32_t ev_type, uint32_t ev_config,
     cpus.push_back(cpu);
   else
     cpus = get_online_cpus();
-  for (int i: cpus) {
+  for (int i : cpus) {
     int fd = bpf_attach_perf_event(probe_fd, ev_type, ev_config, sample_period,
                                    sample_freq, pid, i, group_fd);
     if (fd < 0) {
@@ -347,8 +341,7 @@ StatusTuple BPF::detach_kprobe(const std::string& kernel_func,
 
 StatusTuple BPF::detach_uprobe(const std::string& binary_path,
                                const std::string& symbol, uint64_t symbol_addr,
-                               bpf_probe_attach_type attach_type,
-                               pid_t pid) {
+                               bpf_probe_attach_type attach_type, pid_t pid) {
   std::string module;
   uint64_t offset;
   TRY2(check_binary_symbol(binary_path, symbol, symbol_addr, module, offset));
@@ -399,21 +392,19 @@ StatusTuple BPF::detach_tracepoint(const std::string& tracepoint) {
 StatusTuple BPF::detach_perf_event(uint32_t ev_type, uint32_t ev_config) {
   auto it = perf_events_.find(std::make_pair(ev_type, ev_config));
   if (it == perf_events_.end())
-    return StatusTuple(-1, "Perf Event type %d config %d not attached",
-                       ev_type, ev_config);
+    return StatusTuple(-1, "Perf Event type %d config %d not attached", ev_type,
+                       ev_config);
   TRY2(detach_perf_event_all_cpu(it->second));
   perf_events_.erase(it);
   return StatusTuple(0);
 }
 
-StatusTuple BPF::open_perf_event(const std::string& name,
-                                 uint32_t type,
+StatusTuple BPF::open_perf_event(const std::string& name, uint32_t type,
                                  uint64_t config) {
   if (perf_event_arrays_.find(name) == perf_event_arrays_.end()) {
     TableStorage::iterator it;
     if (!bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
-      return StatusTuple(-1,
-                         "open_perf_event: unable to find table_storage %s",
+      return StatusTuple(-1, "open_perf_event: unable to find table_storage %s",
                          name.c_str());
     perf_event_arrays_[name] = new BPFPerfEventArray(it->second);
   }
@@ -434,8 +425,7 @@ StatusTuple BPF::close_perf_event(const std::string& name) {
 
 StatusTuple BPF::open_perf_buffer(const std::string& name,
                                   perf_reader_raw_cb cb,
-                                  perf_reader_lost_cb lost_cb,
-                                  void* cb_cookie,
+                                  perf_reader_lost_cb lost_cb, void* cb_cookie,
                                   int page_cnt) {
   if (perf_buffers_.find(name) == perf_buffers_.end()) {
     TableStorage::iterator it;
@@ -467,8 +457,8 @@ void BPF::poll_perf_buffer(const std::string& name, int timeout) {
   it->second->poll(timeout);
 }
 
-StatusTuple BPF::load_func(const std::string& func_name,
-                           bpf_prog_type type, int& fd) {
+StatusTuple BPF::load_func(const std::string& func_name, bpf_prog_type type,
+                           int& fd) {
   if (funcs_.find(func_name) != funcs_.end()) {
     fd = funcs_[func_name];
     return StatusTuple(0);
@@ -487,17 +477,15 @@ StatusTuple BPF::load_func(const std::string& func_name,
     log_level = 1;
 
   fd = bpf_prog_load(type, func_name.c_str(),
-                     reinterpret_cast<struct bpf_insn*>(func_start),
-                     func_size, bpf_module_->license(),
-                     bpf_module_->kern_version(),
+                     reinterpret_cast<struct bpf_insn*>(func_start), func_size,
+                     bpf_module_->license(), bpf_module_->kern_version(),
                      log_level, nullptr, 0);
 
   if (fd < 0)
     return StatusTuple(-1, "Failed to load %s: %d", func_name.c_str(), fd);
 
-  bpf_module_->annotate_prog_tag(func_name, fd,
-                                 reinterpret_cast<struct bpf_insn*>(func_start),
-                                 func_size);
+  bpf_module_->annotate_prog_tag(
+      func_name, fd, reinterpret_cast<struct bpf_insn*>(func_start), func_size);
   funcs_[func_name] = fd;
   return StatusTuple(0);
 }
@@ -518,8 +506,8 @@ StatusTuple BPF::unload_func(const std::string& func_name) {
 StatusTuple BPF::check_binary_symbol(const std::string& binary_path,
                                      const std::string& symbol,
                                      uint64_t symbol_addr,
-                                     std::string &module_res,
-                                     uint64_t &offset_res) {
+                                     std::string& module_res,
+                                     uint64_t& offset_res) {
   bcc_symbol output;
   int res = bcc_resolve_symname(binary_path.c_str(), symbol.c_str(),
                                 symbol_addr, -1, nullptr, &output);
@@ -552,8 +540,7 @@ BPFProgTable BPF::get_prog_table(const std::string& name) {
   return BPFProgTable({});
 }
 
-BPFStackTable BPF::get_stack_table(const std::string& name,
-                                   bool use_debug_file,
+BPFStackTable BPF::get_stack_table(const std::string& name, bool use_debug_file,
                                    bool check_debug_file_crc) {
   TableStorage::iterator it;
   if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
diff --git a/src/cc/api/BPF.h b/src/cc/api/BPF.h
index b51ac04..5930178 100644
--- a/src/cc/api/BPF.h
+++ b/src/cc/api/BPF.h
@@ -42,7 +42,7 @@ struct open_probe_t {
 class USDT;
 
 class BPF {
-public:
+ public:
   static const int BPF_MAX_STACK_DEPTH = 127;
 
   explicit BPF(unsigned int flag = 0, TableStorage* ts = nullptr)
@@ -54,33 +54,31 @@ public:
   ~BPF();
   StatusTuple detach_all();
 
-  StatusTuple attach_kprobe(
-      const std::string& kernel_func, const std::string& probe_func,
-      bpf_probe_attach_type = BPF_PROBE_ENTRY,
-      pid_t pid = -1, int cpu = 0, int group_fd = -1,
-      perf_reader_cb cb = nullptr, void* cb_cookie = nullptr);
+  StatusTuple attach_kprobe(const std::string& kernel_func,
+                            const std::string& probe_func,
+                            bpf_probe_attach_type = BPF_PROBE_ENTRY,
+                            perf_reader_cb cb = nullptr,
+                            void* cb_cookie = nullptr);
   StatusTuple detach_kprobe(
       const std::string& kernel_func,
       bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY);
 
-  StatusTuple attach_uprobe(
-      const std::string& binary_path, const std::string& symbol,
-      const std::string& probe_func, uint64_t symbol_addr = 0,
-      bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY,
-      pid_t pid = -1, int cpu = 0, int group_fd = -1,
-      perf_reader_cb cb = nullptr, void* cb_cookie = nullptr);
-  StatusTuple detach_uprobe(
-      const std::string& binary_path, const std::string& symbol,
-      uint64_t symbol_addr = 0,
-      bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY,
-      pid_t pid = -1);
-  StatusTuple attach_usdt(const USDT& usdt, pid_t pid = -1, int cpu = 0,
-                          int group_fd = -1);
+  StatusTuple attach_uprobe(const std::string& binary_path,
+                            const std::string& symbol,
+                            const std::string& probe_func,
+                            uint64_t symbol_addr = 0,
+                            bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY,
+                            pid_t pid = -1, perf_reader_cb cb = nullptr,
+                            void* cb_cookie = nullptr);
+  StatusTuple detach_uprobe(const std::string& binary_path,
+                            const std::string& symbol, uint64_t symbol_addr = 0,
+                            bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY,
+                            pid_t pid = -1);
+  StatusTuple attach_usdt(const USDT& usdt, pid_t pid = -1);
   StatusTuple detach_usdt(const USDT& usdt);
 
   StatusTuple attach_tracepoint(const std::string& tracepoint,
                                 const std::string& probe_func,
-                                pid_t pid = -1, int cpu = 0, int group_fd = -1,
                                 perf_reader_cb cb = nullptr,
                                 void* cb_cookie = nullptr);
   StatusTuple detach_tracepoint(const std::string& tracepoint);
@@ -121,14 +119,12 @@ public:
                                 bool use_debug_file = true,
                                 bool check_debug_file_crc = true);
 
-  StatusTuple open_perf_event(const std::string& name,
-                              uint32_t type,
+  StatusTuple open_perf_event(const std::string& name, uint32_t type,
                               uint64_t config);
 
   StatusTuple close_perf_event(const std::string& name);
 
-  StatusTuple open_perf_buffer(const std::string& name,
-                               perf_reader_raw_cb cb,
+  StatusTuple open_perf_buffer(const std::string& name, perf_reader_raw_cb cb,
                                perf_reader_lost_cb lost_cb = nullptr,
                                void* cb_cookie = nullptr,
                                int page_cnt = DEFAULT_PERF_BUFFER_PAGE_CNT);
@@ -139,7 +135,7 @@ public:
                         int& fd);
   StatusTuple unload_func(const std::string& func_name);
 
-private:
+ private:
   std::string get_kprobe_event(const std::string& kernel_func,
                                bpf_probe_attach_type type);
   std::string get_uprobe_event(const std::string& binary_path, uint64_t offset,
@@ -181,9 +177,8 @@ private:
 
   StatusTuple check_binary_symbol(const std::string& binary_path,
                                   const std::string& symbol,
-                                  uint64_t symbol_addr,
-                                  std::string &module_res,
-                                  uint64_t &offset_res);
+                                  uint64_t symbol_addr, std::string& module_res,
+                                  uint64_t& offset_res);
 
   int flag_;
 
@@ -202,7 +197,7 @@ private:
 };
 
 class USDT {
-public:
+ public:
   USDT(const std::string& binary_path, const std::string& provider,
        const std::string& name, const std::string& probe_func)
       : initialized_(false),
@@ -221,7 +216,7 @@ public:
     return provider_ + ":" + name_ + " from " + binary_path_;
   }
 
-private:
+ private:
   StatusTuple init();
   bool initialized_;
 
-- 
2.16.1


From b9c3ba71403b29833cfcd2012a7d7d261432aa76 Mon Sep 17 00:00:00 2001
From: Paul Chaignon <paul.chaignon@gmail.com>
Date: Thu, 18 Jan 2018 09:56:38 +0100
Subject: [PATCH 05/11] Use /proc/kallsyms for regex filtering

instead of /sys/kernel/debug/tracing/available_filter_functions.
available_filter_functions does not contain all tracable functions,
only those ftrace can use.

(cherry picked from commit fe033552648fad166ff33c6533fa32c9b07705ee)
---
 src/python/bcc/__init__.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/python/bcc/__init__.py b/src/python/bcc/__init__.py
index 456caa2..8aed710 100644
--- a/src/python/bcc/__init__.py
+++ b/src/python/bcc/__init__.py
@@ -478,10 +478,11 @@ class BPF(object):
             blacklist = set([line.rstrip().split()[1] for line in
                     blacklist_file])
         fns = []
-        with open("%s/available_filter_functions" % TRACEFS) as avail_file:
+        with open("/proc/kallsyms") as avail_file:
             for line in avail_file:
-                fn = line.rstrip().split()[0]
-                if re.match(event_re, fn) and fn not in blacklist:
+                (_, t, fn) = line.rstrip().split()[:3]
+                if (t.lower() in ['t', 'w']) and re.match(event_re, fn) \
+                    and fn not in blacklist:
                     fns.append(fn)
         return set(fns)     # Some functions may appear more than once
 
-- 
2.16.1


From 04a55616e467ec85c00d33a97e29151dc89a639c Mon Sep 17 00:00:00 2001
From: Brenden Blanco <bblanco@gmail.com>
Date: Thu, 8 Feb 2018 15:08:04 -0800
Subject: [PATCH 06/11] Fix file desc leak in test_tools_smoke

Signed-off-by: Brenden Blanco <bblanco@gmail.com>
(cherry picked from commit c28f6e8634671ff0b954731156bcf2d3e9facab2)
---
 tests/python/test_tools_smoke.py | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/tests/python/test_tools_smoke.py b/tests/python/test_tools_smoke.py
index ecf8502..641cc99 100755
--- a/tests/python/test_tools_smoke.py
+++ b/tests/python/test_tools_smoke.py
@@ -52,12 +52,12 @@ class SmokeTests(TestCase):
                         or (rc == 137 and kill), "rc was %d" % rc)
 
     def kmod_loaded(self, mod):
-        mods = open("/proc/modules", "r")
-        reg = re.compile("^%s\s" % mod)
-        for line in mods:
-            if reg.match(line):
-                return 1
-            return 0
+        with open("/proc/modules", "r") as mods:
+            reg = re.compile("^%s\s" % mod)
+            for line in mods:
+                if reg.match(line):
+                    return 1
+                return 0
 
     def setUp(self):
         pass
-- 
2.16.1


From 7cd692a6eaacc5ca9128ef5b401e2731d6918ee2 Mon Sep 17 00:00:00 2001
From: Brenden Blanco <bblanco@gmail.com>
Date: Mon, 24 Apr 2017 14:14:43 -0700
Subject: [PATCH 07/11] python: Add 2/3 compat wrappers for byte strings

Introduce some helpers for managing bytes/unicode objects in a way that
bridges the gap from python2 to 3.

1. Add printb() helper for writing bytes output directly to stdout. This
avoids complaints from print() in python3, which expects a unicode
str(). Since python 3.5, `b"" % bytes()` style format strings should
work and we can write tools with common code, once we convert format
strings to bytes.
http://legacy.python.org/dev/peps/pep-0461/

2. Add a class for wrapping command line arguments that are intended for
comparing to debugged memory, for instance running process COMM or
kernel pathname data. The approach takes some of the discussion from
http://legacy.python.org/dev/peps/pep-0383/ into account, though
unfortunately the python2-future implementation of "surrogateescape" is
buggy, therefore this iteration is partial.

The object instance should only ever be coerced into a bytes object.
This silently invokes encode(sys.getfilesystemencoding()), which if it
fails implies that the tool was passed junk characters on the command
line. Thereafter the tool should implement only bytes-bytes comparisons
(for instance re.search(b"", b"")) and bytes stdout printing (see
printb).

3. Add an _assert_is_bytes helper to check for improper usage of str
objects in python arguments. The behavior of the assertion can be
tweaked by changing the bcc.utils._strict_bytes bool.

Going forward, one should never invoke decode() on a bpf data stream,
e.g. the result of a table lookup or perf ring output. Leave that data
in the native bytes() representation.

Signed-off-by: Brenden Blanco <bblanco@gmail.com>
(cherry picked from commit e663541c1b073bc4f594ad6d27f1860aaab42cd1)
---
 src/python/bcc/utils.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/src/python/bcc/utils.py b/src/python/bcc/utils.py
index be9f19e..692614e 100644
--- a/src/python/bcc/utils.py
+++ b/src/python/bcc/utils.py
@@ -12,6 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 import ctypes as ct
+import sys
+import traceback
+import warnings
 
 from .libbcc import lib
 
@@ -39,3 +42,57 @@ def detect_language(candidates, pid):
     res = lib.bcc_procutils_language(pid)
     language = ct.cast(res, ct.c_char_p).value.decode()
     return language if language in candidates else None
+
+FILESYSTEMENCODING = sys.getfilesystemencoding()
+
+def printb(s, file=sys.stdout):
+    """
+    printb(s)
+
+    print a bytes object to stdout and flush
+    """
+    buf = file.buffer if hasattr(file, "buffer") else file
+
+    buf.write(s)
+    buf.write(b"\n")
+    file.flush()
+
+class ArgString(object):
+    """
+    ArgString(arg)
+
+    encapsulate a system argument that can be easily coerced to a bytes()
+    object, which is better for comparing to kernel or probe data (which should
+    never be en/decode()'ed).
+    """
+    def __init__(self, arg):
+        if sys.version_info[0] >= 3:
+            self.s = arg
+        else:
+            self.s = arg.decode(FILESYSTEMENCODING)
+
+    def __bytes__(self):
+        return self.s.encode(FILESYSTEMENCODING)
+
+    def __str__(self):
+        return self.__bytes__()
+
+def warn_with_traceback(message, category, filename, lineno, file=None, line=None):
+    log = file if hasattr(file, "write") else sys.stderr
+    traceback.print_stack(f=sys._getframe(2), file=log)
+    log.write(warnings.formatwarning(message, category, filename, lineno, line))
+
+# uncomment to get full tracebacks for invalid uses of python3+str in arguments
+#warnings.showwarning = warn_with_traceback
+
+_strict_bytes = False
+def _assert_is_bytes(arg):
+    if arg is None:
+        return arg
+    if _strict_bytes:
+        assert type(arg) is bytes, "not a bytes object: %r" % arg
+    elif type(arg) is not bytes:
+        warnings.warn("not a bytes object: %r" % arg, DeprecationWarning, 2)
+        return ArgString(arg).__bytes__()
+    return arg
+
-- 
2.16.1


From d3219dfd70bc23ba4c1a0049e0b4928f1694c96c Mon Sep 17 00:00:00 2001
From: Brenden Blanco <bblanco@gmail.com>
Date: Tue, 9 May 2017 13:50:04 -0700
Subject: [PATCH 08/11] python/bcc: add internal _assert_is_bytes usage

Move bcc internals to sanitize all arguments as bytes instead of str
type. For now, disable warnings or actual assertion, to be turned on
incrementally.

(cherry picked from commit c0ca99a26a9ea36870f84cd37cf1b09504c88a6b)
---
 src/python/bcc/__init__.py | 270 +++++++++++++++++++++++++--------------------
 1 file changed, 149 insertions(+), 121 deletions(-)

diff --git a/src/python/bcc/__init__.py b/src/python/bcc/__init__.py
index 8aed710..c14e9c9 100644
--- a/src/python/bcc/__init__.py
+++ b/src/python/bcc/__init__.py
@@ -27,7 +27,7 @@ basestring = (unicode if sys.version_info[0] < 3 else str)
 from .libbcc import lib, _CB_TYPE, bcc_symbol, bcc_symbol_option, _SYM_CB_TYPE
 from .table import Table, PerfEventArray
 from .perf import Perf
-from .utils import get_online_cpus
+from .utils import get_online_cpus, printb, _assert_is_bytes, ArgString
 
 _kprobe_limit = 1000
 _num_open_probes = 0
@@ -76,21 +76,21 @@ class SymbolCache(object):
         if res < 0:
             if sym.module and sym.offset:
                 return (None, sym.offset,
-                        ct.cast(sym.module, ct.c_char_p).value.decode())
+                        ct.cast(sym.module, ct.c_char_p).value)
             return (None, addr, None)
         if demangle:
-            name_res = sym.demangle_name.decode()
+            name_res = sym.demangle_name
             lib.bcc_symbol_free_demangle_name(ct.byref(sym))
         else:
-            name_res = sym.name.decode()
-        return (name_res, sym.offset,
-                ct.cast(sym.module, ct.c_char_p).value.decode())
+            name_res = sym.name
+        return (name_res, sym.offset, ct.cast(sym.module, ct.c_char_p).value)
 
     def resolve_name(self, module, name):
+        module = _assert_is_bytes(module)
+        name = _assert_is_bytes(name)
         addr = ct.c_ulonglong()
-        if lib.bcc_symcache_resolve_name(
-                    self.cache, module.encode("ascii") if module else None,
-                    name.encode("ascii"), ct.byref(addr)) < 0:
+        if lib.bcc_symcache_resolve_name(self.cache, module, name,
+                ct.byref(addr)) < 0:
             return -1
         return addr.value
 
@@ -142,7 +142,7 @@ class BPF(object):
     XDP_PASS = 2
     XDP_TX = 3
 
-    _probe_repl = re.compile("[^a-zA-Z0-9_]")
+    _probe_repl = re.compile(b"[^a-zA-Z0-9_]")
     _sym_caches = {}
 
     _auto_includes = {
@@ -207,7 +207,8 @@ class BPF(object):
         """ If filename is invalid, search in ./ of argv[0] """
         if filename:
             if not os.path.isfile(filename):
-                t = "/".join([os.path.abspath(os.path.dirname(sys.argv[0])), filename])
+                argv0 = ArgString(sys.argv[0])
+                t = b"/".join([os.path.abspath(os.path.dirname(argv0)), filename])
                 if os.path.isfile(t):
                     filename = t
                 else:
@@ -243,7 +244,7 @@ class BPF(object):
                     return exe_file
         return None
 
-    def __init__(self, src_file="", hdr_file="", text=None, cb=None, debug=0,
+    def __init__(self, src_file=b"", hdr_file=b"", text=None, cb=None, debug=0,
             cflags=[], usdt_contexts=[]):
         """Create a new BPF module with the given source code.
 
@@ -259,6 +260,10 @@ class BPF(object):
                                    See "Debug flags" for explanation
         """
 
+        src_file = _assert_is_bytes(src_file)
+        hdr_file = _assert_is_bytes(hdr_file)
+        text = _assert_is_bytes(text)
+
         self.open_kprobes = {}
         self.open_uprobes = {}
         self.open_tracepoints = {}
@@ -271,12 +276,14 @@ class BPF(object):
         self.debug = debug
         self.funcs = {}
         self.tables = {}
+        self.module = None
         cflags_array = (ct.c_char_p * len(cflags))()
-        for i, s in enumerate(cflags): cflags_array[i] = s.encode("ascii")
+        for i, s in enumerate(cflags): cflags_array[i] = bytes(ArgString(s))
         if text:
             ctx_array = (ct.c_void_p * len(usdt_contexts))()
-            for i, usdt in enumerate(usdt_contexts): ctx_array[i] = ct.c_void_p(usdt.get_context())
-            usdt_text = lib.bcc_usdt_genargs(ctx_array, len(usdt_contexts)).decode()
+            for i, usdt in enumerate(usdt_contexts):
+                ctx_array[i] = ct.c_void_p(usdt.get_context())
+            usdt_text = lib.bcc_usdt_genargs(ctx_array, len(usdt_contexts))
             if usdt_text is None:
                 raise Exception("can't generate USDT probe arguments; " +
                                 "possible cause is missing pid when a " +
@@ -285,19 +292,19 @@ class BPF(object):
             text = usdt_text + text
 
         if text:
-            self.module = lib.bpf_module_create_c_from_string(text.encode("ascii"),
+            self.module = lib.bpf_module_create_c_from_string(text,
                     self.debug, cflags_array, len(cflags_array))
             if not self.module:
                 raise Exception("Failed to compile BPF text:\n%s" % text)
         else:
             src_file = BPF._find_file(src_file)
             hdr_file = BPF._find_file(hdr_file)
-            if src_file.endswith(".b"):
-                self.module = lib.bpf_module_create_b(src_file.encode("ascii"),
-                        hdr_file.encode("ascii"), self.debug)
+            if src_file.endswith(b".b"):
+                self.module = lib.bpf_module_create_b(src_file, hdr_file,
+                        self.debug)
             else:
-                self.module = lib.bpf_module_create_c(src_file.encode("ascii"),
-                        self.debug, cflags_array, len(cflags_array))
+                self.module = lib.bpf_module_create_c(src_file, self.debug,
+                        cflags_array, len(cflags_array))
             if not self.module:
                 raise Exception("Failed to compile BPF module %s" % src_file)
 
@@ -316,25 +323,25 @@ class BPF(object):
 
         fns = []
         for i in range(0, lib.bpf_num_functions(self.module)):
-            func_name = lib.bpf_function_name(self.module, i).decode()
+            func_name = lib.bpf_function_name(self.module, i)
             fns.append(self.load_func(func_name, prog_type))
 
         return fns
 
     def load_func(self, func_name, prog_type):
+        func_name = _assert_is_bytes(func_name)
         if func_name in self.funcs:
             return self.funcs[func_name]
-        if not lib.bpf_function_start(self.module, func_name.encode("ascii")):
+        if not lib.bpf_function_start(self.module, func_name):
             raise Exception("Unknown program %s" % func_name)
         log_level = 0
         if (self.debug & DEBUG_BPF_REGISTER_STATE):
             log_level = 2
         elif (self.debug & DEBUG_BPF):
             log_level = 1
-        fd = lib.bpf_prog_load(prog_type,
-                func_name.encode("ascii"),
-                lib.bpf_function_start(self.module, func_name.encode("ascii")),
-                lib.bpf_function_size(self.module, func_name.encode("ascii")),
+        fd = lib.bpf_prog_load(prog_type, func_name,
+                lib.bpf_function_start(self.module, func_name),
+                lib.bpf_function_size(self.module, func_name),
                 lib.bpf_module_license(self.module),
                 lib.bpf_module_kern_version(self.module),
                 log_level, None, 0);
@@ -357,11 +364,12 @@ class BPF(object):
         """
         Return the eBPF bytecodes for the specified function as a string
         """
-        if not lib.bpf_function_start(self.module, func_name.encode("ascii")):
+        func_name = _assert_is_bytes(func_name)
+        if not lib.bpf_function_start(self.module, func_name):
             raise Exception("Unknown program %s" % func_name)
 
-        start, = lib.bpf_function_start(self.module, func_name.encode("ascii")),
-        size, = lib.bpf_function_size(self.module, func_name.encode("ascii")),
+        start, = lib.bpf_function_start(self.module, func_name),
+        size, = lib.bpf_function_size(self.module, func_name),
         return ct.string_at(start, size)
 
     str2ctype = {
@@ -419,20 +427,21 @@ class BPF(object):
         return cls
 
     def get_table(self, name, keytype=None, leaftype=None, reducer=None):
-        map_id = lib.bpf_table_id(self.module, name.encode("ascii"))
-        map_fd = lib.bpf_table_fd(self.module, name.encode("ascii"))
+        name = _assert_is_bytes(name)
+        map_id = lib.bpf_table_id(self.module, name)
+        map_fd = lib.bpf_table_fd(self.module, name)
         if map_fd < 0:
             raise KeyError
         if not keytype:
-            key_desc = lib.bpf_table_key_desc(self.module, name.encode("ascii"))
+            key_desc = lib.bpf_table_key_desc(self.module, name)
             if not key_desc:
                 raise Exception("Failed to load BPF Table %s key desc" % name)
-            keytype = BPF._decode_table_type(json.loads(key_desc.decode()))
+            keytype = BPF._decode_table_type(json.loads(key_desc))
         if not leaftype:
-            leaf_desc = lib.bpf_table_leaf_desc(self.module, name.encode("ascii"))
+            leaf_desc = lib.bpf_table_leaf_desc(self.module, name)
             if not leaf_desc:
                 raise Exception("Failed to load BPF Table %s leaf desc" % name)
-            leaftype = BPF._decode_table_type(json.loads(leaf_desc.decode()))
+            leaftype = BPF._decode_table_type(json.loads(leaf_desc))
         return Table(self, map_id, map_fd, keytype, leaftype, reducer=reducer)
 
     def __getitem__(self, key):
@@ -459,9 +468,10 @@ class BPF(object):
 
     @staticmethod
     def attach_raw_socket(fn, dev):
+        dev = _assert_is_bytes(dev)
         if not isinstance(fn, BPF.Function):
             raise Exception("arg 1 must be of type BPF.Function")
-        sock = lib.bpf_open_raw_sock(dev.encode("ascii"))
+        sock = lib.bpf_open_raw_sock(dev)
         if sock < 0:
             errstr = os.strerror(ct.get_errno())
             raise Exception("Failed to open raw device %s: %s" % (dev, errstr))
@@ -474,14 +484,13 @@ class BPF(object):
 
     @staticmethod
     def get_kprobe_functions(event_re):
-        with open("%s/../kprobes/blacklist" % TRACEFS) as blacklist_file:
-            blacklist = set([line.rstrip().split()[1] for line in
-                    blacklist_file])
+        with open("%s/../kprobes/blacklist" % TRACEFS, "rb") as blacklist_f:
+            blacklist = set([line.rstrip().split()[1] for line in blacklist_f])
         fns = []
-        with open("/proc/kallsyms") as avail_file:
+        with open("/proc/kallsyms", "rb") as avail_file:
             for line in avail_file:
                 (_, t, fn) = line.rstrip().split()[:3]
-                if (t.lower() in ['t', 'w']) and re.match(event_re, fn) \
+                if (t.lower() in [b't', b'w']) and re.match(event_re, fn) \
                     and fn not in blacklist:
                     fns.append(fn)
         return set(fns)     # Some functions may appear more than once
@@ -501,7 +510,10 @@ class BPF(object):
         del self.open_kprobes[name]
         _num_open_probes -= 1
 
-    def attach_kprobe(self, event="", fn_name="", event_re=""):
+    def attach_kprobe(self, event=b"", fn_name=b"", event_re=b""):
+        event = _assert_is_bytes(event)
+        fn_name = _assert_is_bytes(fn_name)
+        event_re = _assert_is_bytes(event_re)
 
         # allow the caller to glob multiple functions together
         if event_re:
@@ -514,13 +526,11 @@ class BPF(object):
                     pass
             return
 
-        event = str(event)
         self._check_probe_quota(1)
         fn = self.load_func(fn_name, BPF.KPROBE)
-        ev_name = "p_" + event.replace("+", "_").replace(".", "_")
-        res = lib.bpf_attach_kprobe(fn.fd, 0, ev_name.encode("ascii"),
-                event.encode("ascii"), self._reader_cb_impl,
-                ct.cast(id(self), ct.py_object))
+        ev_name = b"p_" + event.replace(b"+", b"_").replace(b".", b"_")
+        res = lib.bpf_attach_kprobe(fn.fd, 0, ev_name, event,
+                self._reader_cb_impl, ct.cast(id(self), ct.py_object))
         res = ct.cast(res, ct.c_void_p)
         if not res:
             raise Exception("Failed to attach BPF to kprobe")
@@ -528,17 +538,20 @@ class BPF(object):
         return self
 
     def detach_kprobe(self, event):
-        event = str(event)
-        ev_name = "p_" + event.replace("+", "_").replace(".", "_")
+        event = _assert_is_bytes(event)
+        ev_name = b"p_" + event.replace(b"+", b"_").replace(b".", b"_")
         if ev_name not in self.open_kprobes:
             raise Exception("Kprobe %s is not attached" % event)
         lib.perf_reader_free(self.open_kprobes[ev_name])
-        res = lib.bpf_detach_kprobe(ev_name.encode("ascii"))
+        res = lib.bpf_detach_kprobe(ev_name)
         if res < 0:
             raise Exception("Failed to detach BPF from kprobe")
         self._del_kprobe(ev_name)
 
-    def attach_kretprobe(self, event="", fn_name="", event_re=""):
+    def attach_kretprobe(self, event=b"", fn_name=b"", event_re=b""):
+        event = _assert_is_bytes(event)
+        fn_name = _assert_is_bytes(fn_name)
+        event_re = _assert_is_bytes(event_re)
 
         # allow the caller to glob multiple functions together
         if event_re:
@@ -549,12 +562,11 @@ class BPF(object):
                     pass
             return
 
-        event = str(event)
         self._check_probe_quota(1)
         fn = self.load_func(fn_name, BPF.KPROBE)
-        ev_name = "r_" + event.replace("+", "_").replace(".", "_")
-        res = lib.bpf_attach_kprobe(fn.fd, 1, ev_name.encode("ascii"),
-                event.encode("ascii"), self._reader_cb_impl,
+        ev_name = b"r_" + event.replace(b"+", b"_").replace(b".", b"_")
+        res = lib.bpf_attach_kprobe(fn.fd, 1, ev_name, event,
+                self._reader_cb_impl,
                 ct.cast(id(self), ct.py_object))
         res = ct.cast(res, ct.c_void_p)
         if not res:
@@ -563,12 +575,12 @@ class BPF(object):
         return self
 
     def detach_kretprobe(self, event):
-        event = str(event)
-        ev_name = "r_" + event.replace("+", "_").replace(".", "_")
+        event = _assert_is_bytes(event)
+        ev_name = b"r_" + event.replace(b"+", b"_").replace(b".", b"_")
         if ev_name not in self.open_kprobes:
             raise Exception("Kretprobe %s is not attached" % event)
         lib.perf_reader_free(self.open_kprobes[ev_name])
-        res = lib.bpf_detach_kprobe(ev_name.encode("ascii"))
+        res = lib.bpf_detach_kprobe(ev_name)
         if res < 0:
             raise Exception("Failed to detach BPF from kprobe")
         self._del_kprobe(ev_name)
@@ -579,9 +591,10 @@ class BPF(object):
             This function attaches a BPF function to a device on the device
             driver level (XDP)
         '''
+        dev = _assert_is_bytes(dev)
         if not isinstance(fn, BPF.Function):
             raise Exception("arg 1 must be of type BPF.Function")
-        res = lib.bpf_attach_xdp(dev.encode("ascii"), fn.fd, flags)
+        res = lib.bpf_attach_xdp(dev, fn.fd, flags)
         if res < 0:
             err_no = ct.get_errno()
             if err_no == errno.EBADMSG:
@@ -598,7 +611,8 @@ class BPF(object):
             This function removes any BPF function from a device on the
             device driver level (XDP)
         '''
-        res = lib.bpf_attach_xdp(dev.encode("ascii"), -1, flags)
+        dev = _assert_is_bytes(dev)
+        res = lib.bpf_attach_xdp(dev, -1, flags)
         if res < 0:
             errstr = os.strerror(ct.get_errno())
             raise Exception("Failed to detach BPF from device %s: %s"
@@ -608,25 +622,28 @@ class BPF(object):
 
     @classmethod
     def _check_path_symbol(cls, module, symname, addr, pid):
+        module = _assert_is_bytes(module)
+        symname = _assert_is_bytes(symname)
         sym = bcc_symbol()
         c_pid = 0 if pid == -1 else pid
         if lib.bcc_resolve_symname(
-            module.encode("ascii"), symname.encode("ascii"),
+            module, symname,
             addr or 0x0, c_pid,
             ct.cast(None, ct.POINTER(bcc_symbol_option)),
             ct.byref(sym),
         ) < 0:
             raise Exception("could not determine address of symbol %s" % symname)
-        module_path = ct.cast(sym.module, ct.c_char_p).value.decode()
+        module_path = ct.cast(sym.module, ct.c_char_p).value
         lib.bcc_procutils_free(sym.module)
         return module_path, sym.offset
 
     @staticmethod
     def find_library(libname):
-        res = lib.bcc_procutils_which_so(libname.encode("ascii"), 0)
+        libname = _assert_is_bytes(libname)
+        res = lib.bcc_procutils_which_so(libname, 0)
         if not res:
             return None
-        libpath = ct.cast(res, ct.c_char_p).value.decode()
+        libpath = ct.cast(res, ct.c_char_p).value
         lib.bcc_procutils_free(res)
         return libpath
 
@@ -646,7 +663,7 @@ class BPF(object):
                         results.append(tp)
         return results
 
-    def attach_tracepoint(self, tp="", tp_re="", fn_name=""):
+    def attach_tracepoint(self, tp=b"", tp_re=b"", fn_name=b""):
         """attach_tracepoint(tp="", tp_re="", fn_name="")
 
         Run the bpf function denoted by fn_name every time the kernel tracepoint
@@ -667,23 +684,25 @@ class BPF(object):
             BPF(text).attach_tracepoint(tp_re="sched:.*", fn_name="on_switch")
         """
 
+        tp = _assert_is_bytes(tp)
+        tp_re = _assert_is_bytes(tp_re)
+        fn_name = _assert_is_bytes(fn_name)
         if tp_re:
             for tp in BPF.get_tracepoints(tp_re):
                 self.attach_tracepoint(tp=tp, fn_name=fn_name)
             return
 
         fn = self.load_func(fn_name, BPF.TRACEPOINT)
-        (tp_category, tp_name) = tp.split(':')
-        res = lib.bpf_attach_tracepoint(fn.fd, tp_category.encode("ascii"),
-                tp_name.encode("ascii"), self._reader_cb_impl,
-                ct.cast(id(self), ct.py_object))
+        (tp_category, tp_name) = tp.split(b':')
+        res = lib.bpf_attach_tracepoint(fn.fd, tp_category, tp_name,
+                self._reader_cb_impl, ct.cast(id(self), ct.py_object))
         res = ct.cast(res, ct.c_void_p)
         if not res:
             raise Exception("Failed to attach BPF to tracepoint")
         self.open_tracepoints[tp] = res
         return self
 
-    def detach_tracepoint(self, tp=""):
+    def detach_tracepoint(self, tp=b""):
         """detach_tracepoint(tp="")
 
         Stop running a bpf function that is attached to the kernel tracepoint
@@ -692,12 +711,12 @@ class BPF(object):
         Example: bpf.detach_tracepoint("sched:sched_switch")
         """
 
+        tp = _assert_is_bytes(tp)
         if tp not in self.open_tracepoints:
             raise Exception("Tracepoint %s is not attached" % tp)
         lib.perf_reader_free(self.open_tracepoints[tp])
-        (tp_category, tp_name) = tp.split(':')
-        res = lib.bpf_detach_tracepoint(tp_category.encode("ascii"),
-                                        tp_name.encode("ascii"))
+        (tp_category, tp_name) = tp.split(b':')
+        res = lib.bpf_detach_tracepoint(tp_category, tp_name)
         if res < 0:
             raise Exception("Failed to detach BPF from tracepoint")
         del self.open_tracepoints[tp]
@@ -710,8 +729,9 @@ class BPF(object):
             raise Exception("Failed to attach BPF to perf event")
         return res
 
-    def attach_perf_event(self, ev_type=-1, ev_config=-1, fn_name="",
+    def attach_perf_event(self, ev_type=-1, ev_config=-1, fn_name=b"",
             sample_period=0, sample_freq=0, pid=-1, cpu=-1, group_fd=-1):
+        fn_name = _assert_is_bytes(fn_name)
         fn = self.load_func(fn_name, BPF.PERF_EVENT)
         res = {}
         if cpu >= 0:
@@ -767,29 +787,30 @@ class BPF(object):
 
     @staticmethod
     def get_user_functions_and_addresses(name, sym_re):
+        name = _assert_is_bytes(name)
+        sym_re = _assert_is_bytes(sym_re)
         addresses = []
         def sym_cb(sym_name, addr):
-            dname = sym_name.decode()
+            dname = sym_name
             if re.match(sym_re, dname):
                 addresses.append((dname, addr))
             return 0
 
-        res = lib.bcc_foreach_function_symbol(
-                name.encode('ascii'), _SYM_CB_TYPE(sym_cb))
+        res = lib.bcc_foreach_function_symbol(name, _SYM_CB_TYPE(sym_cb))
         if res < 0:
             raise Exception("Error %d enumerating symbols in %s" % (res, name))
         return addresses
 
     def _get_uprobe_evname(self, prefix, path, addr, pid):
         if pid == -1:
-            return "%s_%s_0x%x" % (prefix, self._probe_repl.sub("_", path), addr)
+            return b"%s_%s_0x%x" % (prefix, self._probe_repl.sub(b"_", path), addr)
         else:
             # if pid is valid, put pid in the name, so different pid
             # can have different event names
-            return "%s_%s_0x%x_%d" % (prefix, self._probe_repl.sub("_", path), addr, pid)
+            return b"%s_%s_0x%x_%d" % (prefix, self._probe_repl.sub(b"_", path), addr, pid)
 
-    def attach_uprobe(self, name="", sym="", sym_re="", addr=None,
-            fn_name="", pid=-1):
+    def attach_uprobe(self, name=b"", sym=b"", sym_re=b"", addr=None,
+            fn_name=b"", pid=-1):
         """attach_uprobe(name="", sym="", sym_re="", addr=None, fn_name=""
                          pid=-1)
 
@@ -811,7 +832,10 @@ class BPF(object):
                  BPF(text).attach_uprobe("/usr/bin/python3", "main")
         """
 
-        name = str(name)
+        name = _assert_is_bytes(name)
+        sym = _assert_is_bytes(sym)
+        sym_re = _assert_is_bytes(sym_re)
+        fn_name = _assert_is_bytes(fn_name)
 
         if sym_re:
             addresses = BPF.get_user_addresses(name, sym_re)
@@ -825,36 +849,36 @@ class BPF(object):
 
         self._check_probe_quota(1)
         fn = self.load_func(fn_name, BPF.KPROBE)
-        ev_name = self._get_uprobe_evname("p", path, addr, pid)
-        res = lib.bpf_attach_uprobe(fn.fd, 0, ev_name.encode("ascii"),
-                path.encode("ascii"), addr, pid, self._reader_cb_impl,
-                ct.cast(id(self), ct.py_object))
+        ev_name = self._get_uprobe_evname(b"p", path, addr, pid)
+        res = lib.bpf_attach_uprobe(fn.fd, 0, ev_name, path, addr, pid,
+                self._reader_cb_impl, ct.cast(id(self), ct.py_object))
         res = ct.cast(res, ct.c_void_p)
         if not res:
             raise Exception("Failed to attach BPF to uprobe")
         self._add_uprobe(ev_name, res)
         return self
 
-    def detach_uprobe(self, name="", sym="", addr=None, pid=-1):
+    def detach_uprobe(self, name=b"", sym=b"", addr=None, pid=-1):
         """detach_uprobe(name="", sym="", addr=None, pid=-1)
 
         Stop running a bpf function that is attached to symbol 'sym' in library
         or binary 'name'.
         """
 
-        name = str(name)
+        name = _assert_is_bytes(name)
+        sym = _assert_is_bytes(sym)
         (path, addr) = BPF._check_path_symbol(name, sym, addr, pid)
-        ev_name = self._get_uprobe_evname("p", path, addr, pid)
+        ev_name = self._get_uprobe_evname(b"p", path, addr, pid)
         if ev_name not in self.open_uprobes:
             raise Exception("Uprobe %s is not attached" % ev_name)
         lib.perf_reader_free(self.open_uprobes[ev_name])
-        res = lib.bpf_detach_uprobe(ev_name.encode("ascii"))
+        res = lib.bpf_detach_uprobe(ev_name)
         if res < 0:
             raise Exception("Failed to detach BPF from uprobe")
         self._del_uprobe(ev_name)
 
-    def attach_uretprobe(self, name="", sym="", sym_re="", addr=None,
-            fn_name="", pid=-1):
+    def attach_uretprobe(self, name=b"", sym=b"", sym_re=b"", addr=None,
+            fn_name=b"", pid=-1):
         """attach_uretprobe(name="", sym="", sym_re="", addr=None, fn_name=""
                             pid=-1)
 
@@ -863,57 +887,62 @@ class BPF(object):
         meaning of additional parameters.
         """
 
+        name = _assert_is_bytes(name)
+        sym = _assert_is_bytes(sym)
+        sym_re = _assert_is_bytes(sym_re)
+        fn_name = _assert_is_bytes(fn_name)
+
         if sym_re:
             for sym_addr in BPF.get_user_addresses(name, sym_re):
                 self.attach_uretprobe(name=name, addr=sym_addr,
                                       fn_name=fn_name, pid=pid)
             return
 
-        name = str(name)
         (path, addr) = BPF._check_path_symbol(name, sym, addr, pid)
 
         self._check_probe_quota(1)
         fn = self.load_func(fn_name, BPF.KPROBE)
-        ev_name = self._get_uprobe_evname("r", path, addr, pid)
-        res = lib.bpf_attach_uprobe(fn.fd, 1, ev_name.encode("ascii"),
-                path.encode("ascii"), addr, pid, self._reader_cb_impl,
-                ct.cast(id(self), ct.py_object))
+        ev_name = self._get_uprobe_evname(b"r", path, addr, pid)
+        res = lib.bpf_attach_uprobe(fn.fd, 1, ev_name, path, addr, pid,
+                self._reader_cb_impl, ct.cast(id(self), ct.py_object))
         res = ct.cast(res, ct.c_void_p)
         if not res:
             raise Exception("Failed to attach BPF to uprobe")
         self._add_uprobe(ev_name, res)
         return self
 
-    def detach_uretprobe(self, name="", sym="", addr=None, pid=-1):
+    def detach_uretprobe(self, name=b"", sym=b"", addr=None, pid=-1):
         """detach_uretprobe(name="", sym="", addr=None, pid=-1)
 
         Stop running a bpf function that is attached to symbol 'sym' in library
         or binary 'name'.
         """
 
-        name = str(name)
+        name = _assert_is_bytes(name)
+        sym = _assert_is_bytes(sym)
+
         (path, addr) = BPF._check_path_symbol(name, sym, addr, pid)
-        ev_name = self._get_uprobe_evname("r", path, addr, pid)
+        ev_name = self._get_uprobe_evname(b"r", path, addr, pid)
         if ev_name not in self.open_uprobes:
             raise Exception("Uretprobe %s is not attached" % ev_name)
         lib.perf_reader_free(self.open_uprobes[ev_name])
-        res = lib.bpf_detach_uprobe(ev_name.encode("ascii"))
+        res = lib.bpf_detach_uprobe(ev_name)
         if res < 0:
             raise Exception("Failed to detach BPF from uprobe")
         self._del_uprobe(ev_name)
 
     def _trace_autoload(self):
         for i in range(0, lib.bpf_num_functions(self.module)):
-            func_name = str(lib.bpf_function_name(self.module, i).decode())
-            if func_name.startswith("kprobe__"):
+            func_name = lib.bpf_function_name(self.module, i)
+            if func_name.startswith(b"kprobe__"):
                 fn = self.load_func(func_name, BPF.KPROBE)
                 self.attach_kprobe(event=fn.name[8:], fn_name=fn.name)
-            elif func_name.startswith("kretprobe__"):
+            elif func_name.startswith(b"kretprobe__"):
                 fn = self.load_func(func_name, BPF.KPROBE)
                 self.attach_kretprobe(event=fn.name[11:], fn_name=fn.name)
-            elif func_name.startswith("tracepoint__"):
+            elif func_name.startswith(b"tracepoint__"):
                 fn = self.load_func(func_name, BPF.TRACEPOINT)
-                tp = fn.name[len("tracepoint__"):].replace("__", ":")
+                tp = fn.name[len(b"tracepoint__"):].replace(b"__", b":")
                 self.attach_tracepoint(tp=tp, fn_name=fn.name)
 
     def trace_open(self, nonblocking=False):
@@ -922,7 +951,7 @@ class BPF(object):
         Open the trace_pipe if not already open
         """
         if not self.tracefile:
-            self.tracefile = open("%s/trace_pipe" % TRACEFS)
+            self.tracefile = open("%s/trace_pipe" % TRACEFS, "rb")
             if nonblocking:
                 fd = self.tracefile.fileno()
                 fl = fcntl.fcntl(fd, fcntl.F_GETFL)
@@ -941,10 +970,10 @@ class BPF(object):
                 line = self.trace_readline(nonblocking)
                 if not line and nonblocking: return (None,) * 6
                 # don't print messages related to lost events
-                if line.startswith("CPU:"): continue
+                if line.startswith(b"CPU:"): continue
                 task = line[:16].lstrip()
                 line = line[17:]
-                ts_end = line.find(":")
+                ts_end = line.find(b":")
                 pid, cpu, flags, ts = line[:ts_end].split()
                 cpu = cpu[1:-1]
                 msg = line[ts_end + 4:]
@@ -1023,11 +1052,11 @@ class BPF(object):
             "start_thread"
         """
         name, offset, module = BPF._sym_cache(pid).resolve(addr, demangle)
-        offset = "+0x%x" % offset if show_offset and name is not None else ""
-        name = name or "[unknown]"
+        offset = b"+0x%x" % offset if show_offset and name is not None else b""
+        name = name or b"[unknown]"
         name = name + offset
-        module = " [%s]" % os.path.basename(module) \
-            if show_module and module is not None else ""
+        module = b" [%s]" % os.path.basename(module) \
+            if show_module and module is not None else b""
         return name + module
 
     @staticmethod
@@ -1059,7 +1088,7 @@ class BPF(object):
         event_re is used while attaching and detaching probes. Excludes
         perf_events readers.
         """
-        return len([k for k in self.open_kprobes.keys() if isinstance(k, str)])
+        return len([k for k in self.open_kprobes.keys() if type(k) is bytes])
 
     def num_open_uprobes(self):
         """num_open_uprobes()
@@ -1095,9 +1124,9 @@ class BPF(object):
     def cleanup(self):
         for k, v in list(self.open_kprobes.items()):
             # non-string keys here include the perf_events reader
-            if isinstance(k, str):
+            if isinstance(k, bytes):
                 lib.perf_reader_free(v)
-                lib.bpf_detach_kprobe(str(k).encode("ascii"))
+                lib.bpf_detach_kprobe(bytes(k))
                 self._del_kprobe(k)
         # clean up opened perf ring buffer and perf events
         table_keys = list(self.tables.keys())
@@ -1106,13 +1135,12 @@ class BPF(object):
                 del self.tables[key]
         for k, v in list(self.open_uprobes.items()):
             lib.perf_reader_free(v)
-            lib.bpf_detach_uprobe(str(k).encode("ascii"))
+            lib.bpf_detach_uprobe(bytes(k))
             self._del_uprobe(k)
         for k, v in self.open_tracepoints.items():
             lib.perf_reader_free(v)
-            (tp_category, tp_name) = k.split(':')
-            lib.bpf_detach_tracepoint(tp_category.encode("ascii"),
-                    tp_name.encode("ascii"))
+            (tp_category, tp_name) = k.split(b':')
+            lib.bpf_detach_tracepoint(tp_category, tp_name)
         self.open_tracepoints.clear()
         for (ev_type, ev_config) in list(self.open_perf_events.keys()):
             self.detach_perf_event(ev_type, ev_config)
-- 
2.16.1


From 1769a12724c352f38c6e5fa03a2438623e8b2635 Mon Sep 17 00:00:00 2001
From: Brenden Blanco <bblanco@gmail.com>
Date: Mon, 24 Apr 2017 14:31:28 -0700
Subject: [PATCH 09/11] tools: switch to v2/3 independent bytes usage

Conform to bytes encoding for some portion of the tools/tests, such that
smoke tests pass on python3. More conversions are surely required.

Signed-off-by: Brenden Blanco <bblanco@gmail.com>
(cherry picked from commit 42d6098fb133af4fe90a122fb8fc9aeb40cec6f8)
---
 tests/python/test_debuginfo.py   | 26 ++++++++--------
 tests/python/test_probe_count.py |  6 ++--
 tests/python/test_stackid.py     |  2 +-
 tools/cachestat.py               |  8 ++---
 tools/execsnoop.py               | 16 ++++++----
 tools/funccount.py               | 65 ++++++++++++++++++++--------------------
 tools/memleak.py                 |  2 +-
 tools/slabratetop.py             |  3 +-
 8 files changed, 68 insertions(+), 60 deletions(-)

diff --git a/tests/python/test_debuginfo.py b/tests/python/test_debuginfo.py
index 01dc1ab..316567c 100755
--- a/tests/python/test_debuginfo.py
+++ b/tests/python/test_debuginfo.py
@@ -15,14 +15,14 @@ class TestKSyms(TestCase):
         # Grab the first symbol in kallsyms that has type 't' or 'T'.
         # Also, find all aliases of this symbol which are identifiable
         # by the same address.
-        with open("/proc/kallsyms") as f:
+        with open("/proc/kallsyms", "rb") as f:
             for line in f:
 
                 # Extract the first 3 columns only. The 4th column
                 # containing the module name may not exist for all
                 # symbols.
                 (addr, t, name) = line.strip().split()[:3]
-                if t == "t" or t == "T":
+                if t == b"t" or t == b"T":
                     if not address:
                         address = addr
                     if addr == address:
@@ -32,7 +32,7 @@ class TestKSyms(TestCase):
         return (address, aliases)
 
     def test_ksymname(self):
-        sym = BPF.ksymname("__kmalloc")
+        sym = BPF.ksymname(b"__kmalloc")
         self.assertIsNotNone(sym)
         self.assertNotEqual(sym, 0)
 
@@ -58,21 +58,23 @@ class Harness(TestCase):
     def tearDown(self):
         self.process.kill()
         self.process.wait()
+        self.process.stdout.close()
+        self.process = None
 
     def resolve_addr(self):
         sym, offset, module = self.syms.resolve(self.addr, False)
         self.assertEqual(sym, self.mangled_name)
         self.assertEqual(offset, 0)
-        self.assertTrue(module[-5:] == 'dummy')
+        self.assertTrue(module[-5:] == b'dummy')
         sym, offset, module = self.syms.resolve(self.addr, True)
-        self.assertEqual(sym, 'some_namespace::some_function(int, int)')
+        self.assertEqual(sym, b'some_namespace::some_function(int, int)')
         self.assertEqual(offset, 0)
-        self.assertTrue(module[-5:] == 'dummy')
+        self.assertTrue(module[-5:] == b'dummy')
 
 
     def resolve_name(self):
-        script_dir = os.path.dirname(os.path.realpath(__file__))
-        addr = self.syms.resolve_name(os.path.join(script_dir, 'dummy'),
+        script_dir = os.path.dirname(os.path.realpath(__file__).encode("utf8"))
+        addr = self.syms.resolve_name(os.path.join(script_dir, b'dummy'),
                                       self.mangled_name)
         self.assertEqual(addr, self.addr)
         pass
@@ -82,8 +84,8 @@ class TestDebuglink(Harness):
         subprocess.check_output('g++ -o dummy dummy.cc'.split())
         lines = subprocess.check_output('nm dummy'.split()).splitlines()
         for line in lines:
-            if "some_function" in line:
-                self.mangled_name = line.split(' ')[2]
+            if b"some_function" in line:
+                self.mangled_name = line.split(b' ')[2]
                 break
         self.assertTrue(self.mangled_name)
 
@@ -108,8 +110,8 @@ class TestBuildid(Harness):
                .split())
         lines = subprocess.check_output('nm dummy'.split()).splitlines()
         for line in lines:
-            if "some_function" in line:
-                self.mangled_name = line.split(' ')[2]
+            if b"some_function" in line:
+                self.mangled_name = line.split(b' ')[2]
                 break
         self.assertTrue(self.mangled_name)
 
diff --git a/tests/python/test_probe_count.py b/tests/python/test_probe_count.py
index a9392af..ed8982f 100755
--- a/tests/python/test_probe_count.py
+++ b/tests/python/test_probe_count.py
@@ -2,7 +2,7 @@
 # Copyright (c) Suchakra Sharma <suchakrapani.sharma@polymtl.ca>
 # Licensed under the Apache License, Version 2.0 (the "License")
 
-from bcc import BPF, _get_num_open_probes
+from bcc import BPF, _get_num_open_probes, TRACEFS
 import os
 import sys
 from unittest import main, TestCase
@@ -18,9 +18,9 @@ class TestKprobeCnt(TestCase):
 
     def test_attach1(self):
         actual_cnt = 0
-        with open("/sys/kernel/debug/tracing/available_filter_functions") as f:
+        with open("%s/available_filter_functions" % TRACEFS, "rb") as f:
             for line in f:
-                if str(line).startswith("vfs_"):
+                if line.startswith(b"vfs_"):
                     actual_cnt += 1
         open_cnt = self.b.num_open_kprobes()
         self.assertEqual(actual_cnt, open_cnt)
diff --git a/tests/python/test_stackid.py b/tests/python/test_stackid.py
index 0becad0..cb23b10 100755
--- a/tests/python/test_stackid.py
+++ b/tests/python/test_stackid.py
@@ -47,7 +47,7 @@ int kprobe__htab_map_lookup_elem(struct pt_regs *ctx, struct bpf_map *map, u64 *
         stackid = stack_entries[k]
         self.assertIsNotNone(stackid)
         stack = stack_traces[stackid].ip
-        self.assertEqual(b.ksym(stack[0]), "htab_map_lookup_elem")
+        self.assertEqual(b.ksym(stack[0]), b"htab_map_lookup_elem")
 
 
 if __name__ == "__main__":
diff --git a/tools/cachestat.py b/tools/cachestat.py
index 65e5389..c4de1b3 100755
--- a/tools/cachestat.py
+++ b/tools/cachestat.py
@@ -138,16 +138,16 @@ while 1:
     counts = b.get_table("counts")
     for k, v in sorted(counts.items(), key=lambda counts: counts[1].value):
 
-        if re.match('mark_page_accessed', b.ksym(k.ip)) is not None:
+        if re.match(b'mark_page_accessed', b.ksym(k.ip)) is not None:
             mpa = max(0, v.value)
 
-        if re.match('mark_buffer_dirty', b.ksym(k.ip)) is not None:
+        if re.match(b'mark_buffer_dirty', b.ksym(k.ip)) is not None:
             mbd = max(0, v.value)
 
-        if re.match('add_to_page_cache_lru', b.ksym(k.ip)) is not None:
+        if re.match(b'add_to_page_cache_lru', b.ksym(k.ip)) is not None:
             apcl = max(0, v.value)
 
-        if re.match('account_page_dirtied', b.ksym(k.ip)) is not None:
+        if re.match(b'account_page_dirtied', b.ksym(k.ip)) is not None:
             apd = max(0, v.value)
 
         # access = total cache access incl. reads(mpa) and writes(mbd)
diff --git a/tools/execsnoop.py b/tools/execsnoop.py
index 5bb1246..40e184e 100755
--- a/tools/execsnoop.py
+++ b/tools/execsnoop.py
@@ -18,6 +18,8 @@
 
 from __future__ import print_function
 from bcc import BPF
+from bcc.utils import ArgString, printb
+import bcc.utils as utils
 import argparse
 import ctypes as ct
 import re
@@ -41,8 +43,10 @@ parser.add_argument("-t", "--timestamp", action="store_true",
 parser.add_argument("-x", "--fails", action="store_true",
     help="include failed exec()s")
 parser.add_argument("-n", "--name",
+    type=ArgString,
     help="only print commands matching this name (regex), any arg")
 parser.add_argument("-l", "--line",
+    type=ArgString,
     help="only print commands where arg contains this line (regex)")
 parser.add_argument("--max-args", default="20",
     help="maximum number of arguments parsed and displayed, defaults to 20")
@@ -178,19 +182,19 @@ def print_event(cpu, data, size):
     elif event.type == EventType.EVENT_RET:
         if event.retval != 0 and not args.fails:
             skip = True
-        if args.name and not re.search(args.name, event.comm):
+        if args.name and not re.search(bytes(args.name), event.comm):
             skip = True
-        if args.line and not re.search(args.line,
-                                       b' '.join(argv[event.pid]).decode()):
+        if args.line and not re.search(bytes(args.line),
+                                       b' '.join(argv[event.pid])):
             skip = True
 
         if not skip:
             if args.timestamp:
                 print("%-8.3f" % (time.time() - start_ts), end="")
             ppid = get_ppid(event.pid)
-            print("%-16s %-6s %-6s %3s %s" % (event.comm.decode(), event.pid,
-                    ppid if ppid > 0 else "?", event.retval,
-                    b' '.join(argv[event.pid]).decode()))
+            ppid = b"%d" % ppid if ppid > 0 else b"?"
+            printb(b"%-16s %-6d %-6s %3d %s" % (event.comm, event.pid,
+                   ppid, event.retval, b' '.join(argv[event.pid])))
         try:
             del(argv[event.pid])
         except Exception:
diff --git a/tools/funccount.py b/tools/funccount.py
index 98fa04b..8c98461 100755
--- a/tools/funccount.py
+++ b/tools/funccount.py
@@ -16,7 +16,7 @@
 # 18-Oct-2016   Sasha Goldshtein    Generalized for uprobes, tracepoints, USDT.
 
 from __future__ import print_function
-from bcc import BPF, USDT
+from bcc import ArgString, BPF, USDT
 from time import sleep, strftime
 import argparse
 import os
@@ -49,15 +49,15 @@ class Probe(object):
             t:cat:event     -- probe a kernel tracepoint
             u:lib:probe     -- probe a USDT tracepoint
         """
-        parts = pattern.split(':')
+        parts = bytes(pattern).split(b':')
         if len(parts) == 1:
-            parts = ["p", "", parts[0]]
+            parts = [b"p", b"", parts[0]]
         elif len(parts) == 2:
-            parts = ["p", parts[0], parts[1]]
+            parts = [b"p", parts[0], parts[1]]
         elif len(parts) == 3:
-            if parts[0] == "t":
-                parts = ["t", "", "%s:%s" % tuple(parts[1:])]
-            if parts[0] not in ["p", "t", "u"]:
+            if parts[0] == b"t":
+                parts = [b"t", b"", b"%s:%s" % tuple(parts[1:])]
+            if parts[0] not in [b"p", b"t", b"u"]:
                 raise Exception("Type must be 'p', 't', or 'u', but got %s" %
                                 parts[0])
         else:
@@ -66,10 +66,10 @@ class Probe(object):
 
         (self.type, self.library, self.pattern) = parts
         if not use_regex:
-            self.pattern = self.pattern.replace('*', '.*')
-            self.pattern = '^' + self.pattern + '$'
+            self.pattern = self.pattern.replace(b'*', b'.*')
+            self.pattern = b'^' + self.pattern + b'$'
 
-        if (self.type == "p" and self.library) or self.type == "u":
+        if (self.type == b"p" and self.library) or self.type == b"u":
             libpath = BPF.find_library(self.library)
             if libpath is None:
                 # This might be an executable (e.g. 'bash')
@@ -83,46 +83,46 @@ class Probe(object):
         self.trace_functions = {}   # map location number to function name
 
     def is_kernel_probe(self):
-        return self.type == "t" or (self.type == "p" and self.library == "")
+        return self.type == b"t" or (self.type == b"p" and self.library == b"")
 
     def attach(self):
-        if self.type == "p" and not self.library:
+        if self.type == b"p" and not self.library:
             for index, function in self.trace_functions.items():
                 self.bpf.attach_kprobe(
                         event=function,
                         fn_name="trace_count_%d" % index)
-        elif self.type == "p" and self.library:
+        elif self.type == b"p" and self.library:
             for index, function in self.trace_functions.items():
                 self.bpf.attach_uprobe(
                         name=self.library,
                         sym=function,
                         fn_name="trace_count_%d" % index,
                         pid=self.pid or -1)
-        elif self.type == "t":
+        elif self.type == b"t":
             for index, function in self.trace_functions.items():
                 self.bpf.attach_tracepoint(
                         tp=function,
                         fn_name="trace_count_%d" % index)
-        elif self.type == "u":
+        elif self.type == b"u":
             pass    # Nothing to do -- attach already happened in `load`
 
     def _add_function(self, template, probe_name):
-        new_func = "trace_count_%d" % self.matched
-        text = template.replace("PROBE_FUNCTION", new_func)
-        text = text.replace("LOCATION", str(self.matched))
+        new_func = b"trace_count_%d" % self.matched
+        text = template.replace(b"PROBE_FUNCTION", new_func)
+        text = text.replace(b"LOCATION", b"%d" % self.matched)
         self.trace_functions[self.matched] = probe_name
         self.matched += 1
         return text
 
     def _generate_functions(self, template):
         self.usdt = None
-        text = ""
-        if self.type == "p" and not self.library:
+        text = b""
+        if self.type == b"p" and not self.library:
             functions = BPF.get_kprobe_functions(self.pattern)
             verify_limit(len(functions))
             for function in functions:
                 text += self._add_function(template, function)
-        elif self.type == "p" and self.library:
+        elif self.type == b"p" and self.library:
             # uprobes are tricky because the same function may have multiple
             # addresses, and the same address may be mapped to multiple
             # functions. We aren't allowed to create more than one uprobe
@@ -139,12 +139,12 @@ class Probe(object):
                 addresses.add(address)
                 functions.add(function)
                 text += self._add_function(template, function)
-        elif self.type == "t":
+        elif self.type == b"t":
             tracepoints = BPF.get_tracepoints(self.pattern)
             verify_limit(len(tracepoints))
             for tracepoint in tracepoints:
                 text += self._add_function(template, tracepoint)
-        elif self.type == "u":
+        elif self.type == b"u":
             self.usdt = USDT(path=self.library, pid=self.pid)
             matches = []
             for probe in self.usdt.enumerate_probes():
@@ -154,7 +154,7 @@ class Probe(object):
                     matches.append(probe.name)
             verify_limit(len(matches))
             for match in matches:
-                new_func = "trace_count_%d" % self.matched
+                new_func = b"trace_count_%d" % self.matched
                 text += self._add_function(template, match)
                 self.usdt.enable_probe(match, new_func)
             if debug:
@@ -162,7 +162,7 @@ class Probe(object):
         return text
 
     def load(self):
-        trace_count_text = """
+        trace_count_text = b"""
 int PROBE_FUNCTION(void *ctx) {
     FILTER
     int loc = LOCATION;
@@ -174,7 +174,7 @@ int PROBE_FUNCTION(void *ctx) {
     return 0;
 }
         """
-        bpf_text = """#include <uapi/linux/ptrace.h>
+        bpf_text = b"""#include <uapi/linux/ptrace.h>
 
 BPF_ARRAY(counts, u64, NUMLOCATIONS);
         """
@@ -182,15 +182,15 @@ BPF_ARRAY(counts, u64, NUMLOCATIONS);
         # We really mean the tgid from the kernel's perspective, which is in
         # the top 32 bits of bpf_get_current_pid_tgid().
         if self.pid:
-            trace_count_text = trace_count_text.replace('FILTER',
-                """u32 pid = bpf_get_current_pid_tgid() >> 32;
+            trace_count_text = trace_count_text.replace(b'FILTER',
+                b"""u32 pid = bpf_get_current_pid_tgid() >> 32;
                    if (pid != %d) { return 0; }""" % self.pid)
         else:
-            trace_count_text = trace_count_text.replace('FILTER', '')
+            trace_count_text = trace_count_text.replace(b'FILTER', b'')
 
         bpf_text += self._generate_functions(trace_count_text)
-        bpf_text = bpf_text.replace("NUMLOCATIONS",
-                                    str(len(self.trace_functions)))
+        bpf_text = bpf_text.replace(b"NUMLOCATIONS",
+                                    b"%d" % len(self.trace_functions))
         if debug:
             print(bpf_text)
 
@@ -242,6 +242,7 @@ class Tool(object):
         parser.add_argument("-D", "--debug", action="store_true",
             help="print BPF program before starting (for debugging purposes)")
         parser.add_argument("pattern",
+            type=ArgString,
             help="search expression for events")
         self.args = parser.parse_args()
         global debug
@@ -260,7 +261,7 @@ class Tool(object):
         self.probe.load()
         self.probe.attach()
         print("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." %
-              (self.probe.matched, self.args.pattern))
+              (self.probe.matched, bytes(self.args.pattern)))
         exiting = 0 if self.args.interval else 1
         seconds = 0
         while True:
diff --git a/tools/memleak.py b/tools/memleak.py
index 1858e93..adc6dd7 100755
--- a/tools/memleak.py
+++ b/tools/memleak.py
@@ -465,7 +465,7 @@ def print_outstanding():
                          key=lambda a: a.size)[-top_stacks:]
         for alloc in to_show:
                 print("\t%d bytes in %d allocations from stack\n\t\t%s" %
-                      (alloc.size, alloc.count, "\n\t\t".join(alloc.stack)))
+                      (alloc.size, alloc.count, b"\n\t\t".join(alloc.stack)))
 
 def print_outstanding_combined():
         stack_traces = bpf["stack_traces"]
diff --git a/tools/slabratetop.py b/tools/slabratetop.py
index 4c2604f..956508b 100755
--- a/tools/slabratetop.py
+++ b/tools/slabratetop.py
@@ -17,6 +17,7 @@
 
 from __future__ import print_function
 from bcc import BPF
+from bcc.utils import printb
 from time import sleep, strftime
 import argparse
 import signal
@@ -120,7 +121,7 @@ while 1:
     line = 0
     for k, v in reversed(sorted(counts.items(),
                                 key=lambda counts: counts[1].size)):
-        print("%-32s %6d %10d" % (k.name.decode(), v.count, v.size))
+        printb(b"%-32s %6d %10d" % (k.name, v.count, v.size))
 
         line += 1
         if line >= maxrows:
-- 
2.16.1


From 5c29d7ab95d1f6a34423501931e0c45f2b38d623 Mon Sep 17 00:00:00 2001
From: Yonghong Song <yhs@fb.com>
Date: Tue, 2 Jan 2018 21:18:06 -0800
Subject: [PATCH 10/11] fix runqlen.py with 4.15 kernel

The following kernel commit changes linux_src:kernel/sched/sched.h
struct cfs_rq structure:

```
commit 1ea6c46a23f1213d1972bfae220db5c165e27bba
Author: Peter Zijlstra <peterz@infradead.org>
Date:   Sat May 6 15:59:54 2017 +0200

    sched/fair: Propagate an effective runnable_load_avg

    The load balancer uses runnable_load_avg as load indicator. For
    !cgroup this is:

      runnable_load_avg = \Sum se->avg.load_avg ; where se->on_rq

    That is, a direct sum of all runnable tasks on that runqueue. As
    opposed to load_avg, which is a sum of all tasks on the runqueue,
    which includes a blocked component.
...
```

The commit is in kernel 4.15 release and will make current
runqlen.py internal cfs_rq_partial structure not syncing with the kernel one.
As a result, runqlen.py will produce incorrect results on 4.15.

This patch attempts to solve this issue by compiling a bpf program,
which accesses one of fields introduced by the above commit.
The successful compilation will indicate that we should amend
the cfs_rq_partial structure.

Signed-off-by: Yonghong Song <yhs@fb.com>
(cherry picked from commit ffa47e67319efe717138deb7e32429dab1c262c0)
---
 tools/runqlen.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/tools/runqlen.py b/tools/runqlen.py
index 728c6f9..e5730d4 100755
--- a/tools/runqlen.py
+++ b/tools/runqlen.py
@@ -22,6 +22,8 @@
 from __future__ import print_function
 from bcc import BPF, PerfType, PerfSWConfig
 from time import sleep, strftime
+from tempfile import NamedTemporaryFile
+from os import open, close, dup, unlink, O_WRONLY
 import argparse
 
 # arguments
@@ -51,6 +53,66 @@ countdown = int(args.count)
 debug = 0
 frequency = 99
 
+# Linux 4.15 introduced a new field runnable_weight
+# in linux_src:kernel/sched/sched.h as
+#     struct cfs_rq {
+#         struct load_weight load;
+#         unsigned long runnable_weight;
+#         unsigned int nr_running, h_nr_running;
+#         ......
+#     }
+# and this tool requires to access nr_running to get
+# runqueue len information.
+#
+# The commit which introduces cfs_rq->runnable_weight
+# field also introduces the field sched_entity->runnable_weight
+# where sched_entity is defined in linux_src:include/linux/sched.h.
+#
+# To cope with pre-4.15 and 4.15/post-4.15 releases,
+# we run a simple BPF program to detect whether
+# field sched_entity->runnable_weight exists. The existence of
+# this field should infer the existence of cfs_rq->runnable_weight.
+#
+# This will need maintenance as the relationship between these
+# two fields may change in the future.
+#
+def check_runnable_weight_field():
+    # Define the bpf program for checking purpose
+    bpf_check_text = """
+#include <linux/sched.h>
+unsigned long dummy(struct sched_entity *entity)
+{
+    return entity->runnable_weight;
+}
+"""
+
+    # Get a temporary file name
+    tmp_file = NamedTemporaryFile(delete=False)
+    tmp_file.close();
+
+    # Duplicate and close stderr (fd = 2)
+    old_stderr = dup(2)
+    close(2)
+
+    # Open a new file, should get fd number 2
+    # This will avoid printing llvm errors on the screen
+    fd = open(tmp_file.name, O_WRONLY)
+    try:
+        t = BPF(text=bpf_check_text)
+        success_compile = True
+    except:
+        success_compile = False
+
+    # Release the fd 2, and next dup should restore old stderr
+    close(fd)
+    dup(old_stderr)
+    close(old_stderr)
+
+    # remove the temporary file and return
+    unlink(tmp_file.name)
+    return success_compile
+
+
 # define BPF program
 bpf_text = """
 #include <uapi/linux/ptrace.h>
@@ -60,6 +122,7 @@ bpf_text = """
 // header. This will need maintenance. It is from kernel/sched/sched.h:
 struct cfs_rq_partial {
     struct load_weight load;
+    RUNNABLE_WEIGHT_FIELD
     unsigned int nr_running, h_nr_running;
 };
 
@@ -106,6 +169,11 @@ else:
         'BPF_HISTOGRAM(dist, unsigned int);')
     bpf_text = bpf_text.replace('STORE', 'dist.increment(len);')
 
+if check_runnable_weight_field():
+    bpf_text = bpf_text.replace('RUNNABLE_WEIGHT_FIELD', 'unsigned long runnable_weight;')
+else:
+    bpf_text = bpf_text.replace('RUNNABLE_WEIGHT_FIELD', '')
+
 # code substitutions
 if debug:
     print(bpf_text)
-- 
2.16.1


From addc4cb885012c2bfa6416deb7a1ce1855303a67 Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Mon, 12 Feb 2018 16:51:14 +0800
Subject: [PATCH 11/11] opensnoop: convert args.name to bytes

This commit fixes the following error with python3:

Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 234, in 'calling callback function'
  File "/usr/lib/python3.6/site-packages/bcc/table.py", line 508, in raw_cb_
    callback(cpu, data, size)
  File "./opensnoop.py", line 169, in print_event
    if args.name and args.name not in event.comm:
TypeError: a bytes-like object is required, not 'str'

Signed-off-by: Gary Lin <glin@suse.com>
---
 tools/opensnoop.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/tools/opensnoop.py b/tools/opensnoop.py
index 0834086..09a0ace 100755
--- a/tools/opensnoop.py
+++ b/tools/opensnoop.py
@@ -14,7 +14,7 @@
 # 08-Oct-2016   Dina Goldshtein Support filtering by PID and TID.
 
 from __future__ import print_function
-from bcc import BPF
+from bcc import ArgString, BPF
 import argparse
 import ctypes as ct
 
@@ -40,6 +40,7 @@ parser.add_argument("-p", "--pid",
 parser.add_argument("-t", "--tid",
     help="trace this TID only")
 parser.add_argument("-n", "--name",
+    type=ArgString,
     help="only print process names containing this name")
 args = parser.parse_args()
 debug = 0
@@ -166,7 +167,7 @@ def print_event(cpu, data, size):
     if args.failed and (event.ret >= 0):
         return
 
-    if args.name and args.name not in event.comm:
+    if args.name and bytes(args.name) not in event.comm:
         return
 
     if args.timestamp:
-- 
2.16.1

