Add a python test for usdt
authorYonghong Song <yhs@fb.com>
Tue, 30 May 2017 13:37:02 +0000 (06:37 -0700)
committerBrenden Blanco <bblanco@gmail.com>
Sat, 3 Jun 2017 03:35:17 +0000 (20:35 -0700)
  o The sample application covers different aspects
    of usdt rewriter code generation:
    - const
    - *(ctx + offset)
    - use bpf_probe_read to get trace data
    - the switch in ctx->ip if the same probe
      is triggered in multiple .text locations.
  o folly (https://github.com/facebook/folly/) tracing
    header files are pulled into python test directory and
    used to produce USDT probes.

Signed-off-by: Yonghong Song <yhs@fb.com>
tests/python/CMakeLists.txt
tests/python/include/folly/tracing/StaticTracepoint-ELFx86.h [new file with mode: 0644]
tests/python/include/folly/tracing/StaticTracepoint.h [new file with mode: 0644]
tests/python/test_usdt.py [new file with mode: 0755]

index db2e08af424eb6fa140a82f0efb583a7db5cd777..7db58cab746242a3c68f2e97beb0d6e7a0ba8f67 100644 (file)
@@ -66,3 +66,5 @@ add_test(NAME py_test_dump_func WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
   COMMAND ${TEST_WRAPPER} py_dump_func simple ${CMAKE_CURRENT_SOURCE_DIR}/test_dump_func.py)
 add_test(NAME py_test_tools_smoke WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
   COMMAND ${TEST_WRAPPER} py_test_tools_smoke sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_tools_smoke.py)
+add_test(NAME py_test_usdt WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  COMMAND ${TEST_WRAPPER} py_test_usdt sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_usdt.py)
diff --git a/tests/python/include/folly/tracing/StaticTracepoint-ELFx86.h b/tests/python/include/folly/tracing/StaticTracepoint-ELFx86.h
new file mode 100644 (file)
index 0000000..6d35be7
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// Default constraint for the probe arguments as operands.
+#ifndef FOLLY_SDT_ARG_CONSTRAINT
+#define FOLLY_SDT_ARG_CONSTRAINT      "nor"
+#endif
+
+// Instruction to emit for the probe.
+#define FOLLY_SDT_NOP                 nop
+
+// Note section properties.
+#define FOLLY_SDT_NOTE_NAME           "stapsdt"
+#define FOLLY_SDT_NOTE_TYPE           3
+
+// Size of address depending on platform.
+#ifdef __LP64__
+#define FOLLY_SDT_ASM_ADDR            .8byte
+#else
+#define FOLLY_SDT_ASM_ADDR            .4byte
+#endif
+
+// Assembler helper Macros.
+#define FOLLY_SDT_S(x)                #x
+#define FOLLY_SDT_ASM_1(x)            FOLLY_SDT_S(x) "\n"
+#define FOLLY_SDT_ASM_2(a, b)         FOLLY_SDT_S(a) "," FOLLY_SDT_S(b) "\n"
+#define FOLLY_SDT_ASM_3(a, b, c)      FOLLY_SDT_S(a) "," FOLLY_SDT_S(b) ","    \
+                                      FOLLY_SDT_S(c) "\n"
+#define FOLLY_SDT_ASM_STRING(x)       FOLLY_SDT_ASM_1(.asciz FOLLY_SDT_S(x))
+
+// Helper to determine the size of an argument.
+#define FOLLY_SDT_ISARRAY(x)  (__builtin_classify_type(x) == 14)
+#define FOLLY_SDT_ARGSIZE(x)  (FOLLY_SDT_ISARRAY(x) ? sizeof(void*) : sizeof(x))
+
+// Format of each probe arguments as operand.
+// Size of the arugment tagged with FOLLY_SDT_Sn, with "n" constraint.
+// Value of the argument tagged with FOLLY_SDT_An, with configured constraint.
+#define FOLLY_SDT_ARG(n, x)                                                    \
+  [FOLLY_SDT_S##n] "n"                ((size_t)FOLLY_SDT_ARGSIZE(x)),          \
+  [FOLLY_SDT_A##n] FOLLY_SDT_ARG_CONSTRAINT (x)
+
+// Templates to append arguments as operands.
+#define FOLLY_SDT_OPERANDS_0()        [__sdt_dummy] "g" (0)
+#define FOLLY_SDT_OPERANDS_1(_1)      FOLLY_SDT_ARG(1, _1)
+#define FOLLY_SDT_OPERANDS_2(_1, _2)                                           \
+  FOLLY_SDT_OPERANDS_1(_1), FOLLY_SDT_ARG(2, _2)
+#define FOLLY_SDT_OPERANDS_3(_1, _2, _3)                                       \
+  FOLLY_SDT_OPERANDS_2(_1, _2), FOLLY_SDT_ARG(3, _3)
+#define FOLLY_SDT_OPERANDS_4(_1, _2, _3, _4)                                   \
+  FOLLY_SDT_OPERANDS_3(_1, _2, _3), FOLLY_SDT_ARG(4, _4)
+#define FOLLY_SDT_OPERANDS_5(_1, _2, _3, _4, _5)                               \
+  FOLLY_SDT_OPERANDS_4(_1, _2, _3, _4), FOLLY_SDT_ARG(5, _5)
+#define FOLLY_SDT_OPERANDS_6(_1, _2, _3, _4, _5, _6)                           \
+  FOLLY_SDT_OPERANDS_5(_1, _2, _3, _4, _5), FOLLY_SDT_ARG(6, _6)
+#define FOLLY_SDT_OPERANDS_7(_1, _2, _3, _4, _5, _6, _7)                       \
+  FOLLY_SDT_OPERANDS_6(_1, _2, _3, _4, _5, _6), FOLLY_SDT_ARG(7, _7)
+#define FOLLY_SDT_OPERANDS_8(_1, _2, _3, _4, _5, _6, _7, _8)                   \
+  FOLLY_SDT_OPERANDS_7(_1, _2, _3, _4, _5, _6, _7), FOLLY_SDT_ARG(8, _8)
+
+// Templates to reference the arguments from operands in note section.
+#define FOLLY_SDT_ARGFMT(no)        %n[FOLLY_SDT_S##no]@%[FOLLY_SDT_A##no]
+#define FOLLY_SDT_ARG_TEMPLATE_0    /*No arguments*/
+#define FOLLY_SDT_ARG_TEMPLATE_1    FOLLY_SDT_ARGFMT(1)
+#define FOLLY_SDT_ARG_TEMPLATE_2    FOLLY_SDT_ARG_TEMPLATE_1 FOLLY_SDT_ARGFMT(2)
+#define FOLLY_SDT_ARG_TEMPLATE_3    FOLLY_SDT_ARG_TEMPLATE_2 FOLLY_SDT_ARGFMT(3)
+#define FOLLY_SDT_ARG_TEMPLATE_4    FOLLY_SDT_ARG_TEMPLATE_3 FOLLY_SDT_ARGFMT(4)
+#define FOLLY_SDT_ARG_TEMPLATE_5    FOLLY_SDT_ARG_TEMPLATE_4 FOLLY_SDT_ARGFMT(5)
+#define FOLLY_SDT_ARG_TEMPLATE_6    FOLLY_SDT_ARG_TEMPLATE_5 FOLLY_SDT_ARGFMT(6)
+#define FOLLY_SDT_ARG_TEMPLATE_7    FOLLY_SDT_ARG_TEMPLATE_6 FOLLY_SDT_ARGFMT(7)
+#define FOLLY_SDT_ARG_TEMPLATE_8    FOLLY_SDT_ARG_TEMPLATE_7 FOLLY_SDT_ARGFMT(8)
+
+// Structure of note section for the probe.
+#define FOLLY_SDT_NOTE_CONTENT(provider, name, arg_template)                   \
+  FOLLY_SDT_ASM_1(990: FOLLY_SDT_NOP)                                          \
+  FOLLY_SDT_ASM_3(     .pushsection .note.stapsdt,"","note")                   \
+  FOLLY_SDT_ASM_1(     .balign 4)                                              \
+  FOLLY_SDT_ASM_3(     .4byte 992f-991f, 994f-993f, FOLLY_SDT_NOTE_TYPE)       \
+  FOLLY_SDT_ASM_1(991: .asciz FOLLY_SDT_NOTE_NAME)                             \
+  FOLLY_SDT_ASM_1(992: .balign 4)                                              \
+  FOLLY_SDT_ASM_1(993: FOLLY_SDT_ASM_ADDR 990b)                                \
+  FOLLY_SDT_ASM_1(     FOLLY_SDT_ASM_ADDR 0) /*Reserved for Semaphore address*/\
+  FOLLY_SDT_ASM_1(     FOLLY_SDT_ASM_ADDR 0) /*Reserved for Semaphore name*/   \
+  FOLLY_SDT_ASM_STRING(provider)                                               \
+  FOLLY_SDT_ASM_STRING(name)                                                   \
+  FOLLY_SDT_ASM_STRING(arg_template)                                           \
+  FOLLY_SDT_ASM_1(994: .balign 4)                                              \
+  FOLLY_SDT_ASM_1(     .popsection)
+
+// Main probe Macro.
+#define FOLLY_SDT_PROBE(provider, name, n, arglist)                            \
+    __asm__ __volatile__ (                                                     \
+      FOLLY_SDT_NOTE_CONTENT(provider, name, FOLLY_SDT_ARG_TEMPLATE_##n)       \
+      :: FOLLY_SDT_OPERANDS_##n arglist                                        \
+    )                                                                          \
+
+// Helper Macros to handle variadic arguments.
+#define FOLLY_SDT_NARG_(_0, _1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
+#define FOLLY_SDT_NARG(...)                                                    \
+  FOLLY_SDT_NARG_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#define FOLLY_SDT_PROBE_N(provider, name, N, ...)                              \
+  FOLLY_SDT_PROBE(provider, name, N, (__VA_ARGS__))
diff --git a/tests/python/include/folly/tracing/StaticTracepoint.h b/tests/python/include/folly/tracing/StaticTracepoint.h
new file mode 100644 (file)
index 0000000..6b37db6
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#if defined(__ELF__) && (defined(__x86_64__) || defined(__i386__))
+#include <folly/tracing/StaticTracepoint-ELFx86.h>
+
+#define FOLLY_SDT(provider, name, ...)                                         \
+  FOLLY_SDT_PROBE_N(                                                           \
+    provider, name, FOLLY_SDT_NARG(0, ##__VA_ARGS__), ##__VA_ARGS__)
+#else
+#define FOLLY_SDT(provider, name, ...) do {} while(0)
+#endif
diff --git a/tests/python/test_usdt.py b/tests/python/test_usdt.py
new file mode 100755 (executable)
index 0000000..7f91638
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/python
+#
+# USAGE: test_usdt.py
+#
+# Copyright 2017 Facebook, Inc
+# Licensed under the Apache License, Version 2.0 (the "License")
+
+from __future__ import print_function
+from bcc import BPF, USDT
+from unittest import main, TestCase
+from subprocess import Popen, PIPE
+from tempfile import NamedTemporaryFile
+import os
+import signal
+
+class TestUDST(TestCase):
+    def setUp(self):
+        # Application, minimum, to define three trace points
+        app_text = b"""
+#include <unistd.h>
+#include <folly/tracing/StaticTracepoint.h>
+
+int main() {
+  char s[100];
+  int i, a = 20, b = 40;
+  for (i = 0; i < 100; i++) s[i] = (i & 7) + (i & 6);
+  while (1) {
+    FOLLY_SDT(test, probe_point_1, s[7], b);
+    FOLLY_SDT(test, probe_point_3, a, b);
+    sleep(3);
+    a++; b++;
+    FOLLY_SDT(test, probe_point_1, s[4], a);
+    FOLLY_SDT(test, probe_point_2, 5, s[10]);
+    FOLLY_SDT(test, probe_point_3, s[4], s[7]);
+  }
+  return 1;
+}
+"""
+        # BPF program
+        self.bpf_text = """
+#include <uapi/linux/ptrace.h>
+int do_trace(struct pt_regs *ctx) {
+    return 0;
+};
+int do_trace2(struct pt_regs *ctx) {
+    return 0;
+}
+int do_trace3(struct pt_regs *ctx) {
+    return 0;
+}
+"""
+
+        # Compile and run the application
+        self.ftemp = NamedTemporaryFile(delete=False)
+        self.ftemp.close()
+        comp = Popen(["gcc", "-I", "%s/include" % os.getcwd(),
+                      "-x", "c", "-o", self.ftemp.name, "-"],
+                     stdin=PIPE)
+        comp.stdin.write(app_text)
+        comp.stdin.close()
+        self.assertEqual(comp.wait(), 0)
+        self.app = Popen([self.ftemp.name])
+
+    def test_attach1(self):
+        # enable USDT probe from given PID and verifier generated BPF programs
+        u = USDT(pid=int(self.app.pid))
+        u.enable_probe(probe="probe_point_1", fn_name="do_trace")
+        u.enable_probe(probe="probe_point_2", fn_name="do_trace2")
+        u.enable_probe(probe="probe_point_3", fn_name="do_trace3")
+        b = BPF(text=self.bpf_text, usdt_contexts=[u])
+
+    def tearDown(self):
+        # kill the subprocess, clean the environment
+        self.app.kill()
+        self.app.wait()
+        os.unlink(self.ftemp.name)
+
+if __name__ == "__main__":
+    main()