Home | History | Annotate | Download | only in cc
      1 /*
      2  * Copyright (c) 2016 GitHub, Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 #include <signal.h>
     17 #include <sys/types.h>
     18 #include <sys/wait.h>
     19 #include <unistd.h>
     20 
     21 #include "catch.hpp"
     22 #include "usdt.h"
     23 #include "api/BPF.h"
     24 
     25 #ifdef HAVE_SDT_HEADER
     26 /* required to insert USDT probes on this very executable --
     27  * we're gonna be testing them live! */
     28 #include <sys/sdt.h>
     29 
     30 static int a_probed_function() {
     31   int an_int = 23 + getpid();
     32   void *a_pointer = malloc(4);
     33   DTRACE_PROBE2(libbcc_test, sample_probe_1, an_int, a_pointer);
     34   free(a_pointer);
     35   return an_int;
     36 }
     37 
     38 TEST_CASE("test finding a probe in our own process", "[usdt]") {
     39   USDT::Context ctx(getpid());
     40   REQUIRE(ctx.num_probes() >= 1);
     41 
     42   SECTION("our test probe") {
     43     auto probe = ctx.get("sample_probe_1");
     44     REQUIRE(probe);
     45 
     46     REQUIRE(probe->in_shared_object(probe->bin_path()) == false);
     47     REQUIRE(probe->name() == "sample_probe_1");
     48     REQUIRE(probe->provider() == "libbcc_test");
     49     REQUIRE(probe->bin_path().find("/test_libbcc") != std::string::npos);
     50 
     51     REQUIRE(probe->num_locations() == 1);
     52     REQUIRE(probe->num_arguments() == 2);
     53     REQUIRE(probe->need_enable() == false);
     54 
     55     REQUIRE(a_probed_function() != 0);
     56   }
     57 }
     58 
     59 TEST_CASE("test fine a probe in our own binary with C++ API", "[usdt]") {
     60     ebpf::BPF bpf;
     61     ebpf::USDT u("/proc/self/exe", "libbcc_test", "sample_probe_1", "on_event");
     62 
     63     auto res = bpf.init("int on_event() { return 0; }", {}, {u});
     64     REQUIRE(res.code() == 0);
     65 
     66     res = bpf.attach_usdt(u);
     67     REQUIRE(res.code() == 0);
     68 
     69     res = bpf.detach_usdt(u);
     70     REQUIRE(res.code() == 0);
     71 }
     72 
     73 TEST_CASE("test fine a probe in our Process with C++ API", "[usdt]") {
     74     ebpf::BPF bpf;
     75     ebpf::USDT u(::getpid(), "libbcc_test", "sample_probe_1", "on_event");
     76 
     77     auto res = bpf.init("int on_event() { return 0; }", {}, {u});
     78     REQUIRE(res.code() == 0);
     79 
     80     res = bpf.attach_usdt(u);
     81     REQUIRE(res.code() == 0);
     82 
     83     res = bpf.detach_usdt(u);
     84     REQUIRE(res.code() == 0);
     85 }
     86 #endif  // HAVE_SDT_HEADER
     87 
     88 class ChildProcess {
     89   pid_t pid_;
     90 
     91 public:
     92   ChildProcess(const char *name, char *const argv[]) {
     93     pid_ = fork();
     94     if (pid_ == 0) {
     95       execvp(name, argv);
     96       exit(0);
     97     }
     98     if (spawned()) {
     99       usleep(250000);
    100       if (kill(pid_, 0) < 0)
    101         pid_ = -1;
    102     }
    103   }
    104 
    105   ~ChildProcess() {
    106     if (spawned()) {
    107       int status;
    108       kill(pid_, SIGKILL);
    109       if (waitpid(pid_, &status, 0) != pid_)
    110         abort();
    111     }
    112   }
    113 
    114   bool spawned() const { return pid_ > 0; }
    115   pid_t pid() const { return pid_; }
    116 };
    117 
    118 extern int cmd_scanf(const char *cmd, const char *fmt, ...);
    119 
    120 static int probe_num_locations(const char *bin_path, const char *func_name) {
    121   int num_locations;
    122   char cmd[512];
    123   const char *cmdfmt = "readelf -n %s | grep -c \"Name: %s$\"";
    124 
    125   sprintf(cmd, cmdfmt, bin_path, func_name);
    126   if (cmd_scanf(cmd, "%d", &num_locations) != 0) {
    127     return -1;
    128   }
    129 
    130   return num_locations;
    131 }
    132 
    133 static int probe_num_arguments(const char *bin_path, const char *func_name) {
    134   int num_arguments;
    135   char cmd[512];
    136   const char *cmdfmt = "readelf -n %s | grep -m 1 -A 2 \" %s$\" | " \
    137                        "tail -1 | cut -d \" \" -f 6- | wc -w";
    138 
    139   sprintf(cmd, cmdfmt, bin_path, func_name);
    140   if (cmd_scanf(cmd, "%d", &num_arguments) != 0) {
    141     return -1;
    142   }
    143 
    144   return num_arguments;
    145 }
    146 
    147 TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") {
    148   size_t mri_probe_count = 0;
    149 
    150   SECTION("without a running Ruby process") {
    151     USDT::Context ctx("ruby");
    152 
    153     if (!ctx.loaded())
    154       return;
    155 
    156     REQUIRE(ctx.num_probes() > 10);
    157     mri_probe_count = ctx.num_probes();
    158 
    159     SECTION("GC static probe") {
    160       auto name = "gc__mark__begin";
    161       auto probe = ctx.get(name);
    162       REQUIRE(probe);
    163 
    164       REQUIRE(probe->in_shared_object(probe->bin_path()) == true);
    165       REQUIRE(probe->name() == name);
    166       REQUIRE(probe->provider() == "ruby");
    167 
    168       auto bin_path = probe->bin_path();
    169       bool bin_path_match =
    170             (bin_path.find("/ruby") != std::string::npos) ||
    171             (bin_path.find("/libruby") != std::string::npos);
    172       REQUIRE(bin_path_match);
    173 
    174       int exp_locations, exp_arguments;
    175       exp_locations = probe_num_locations(bin_path.c_str(), name);
    176       exp_arguments = probe_num_arguments(bin_path.c_str(), name);
    177       REQUIRE(probe->num_locations() == exp_locations);
    178       REQUIRE(probe->num_arguments() == exp_arguments);
    179       REQUIRE(probe->need_enable() == true);
    180     }
    181 
    182     SECTION("object creation probe") {
    183       auto name = "object__create";
    184       auto probe = ctx.get(name);
    185       REQUIRE(probe);
    186 
    187       REQUIRE(probe->in_shared_object(probe->bin_path()) == true);
    188       REQUIRE(probe->name() == name);
    189       REQUIRE(probe->provider() == "ruby");
    190 
    191       auto bin_path = probe->bin_path();
    192       bool bin_path_match =
    193             (bin_path.find("/ruby") != std::string::npos) ||
    194             (bin_path.find("/libruby") != std::string::npos);
    195       REQUIRE(bin_path_match);
    196 
    197       int exp_locations, exp_arguments;
    198       exp_locations = probe_num_locations(bin_path.c_str(), name);
    199       exp_arguments = probe_num_arguments(bin_path.c_str(), name);
    200       REQUIRE(probe->num_locations() == exp_locations);
    201       REQUIRE(probe->num_arguments() == exp_arguments);
    202       REQUIRE(probe->need_enable() == true);
    203     }
    204 
    205     SECTION("array creation probe") {
    206       auto name = "array__create";
    207       auto probe = ctx.get(name);
    208       REQUIRE(probe);
    209       REQUIRE(probe->name() == name);
    210 
    211       auto bin_path = probe->bin_path().c_str();
    212       int exp_locations, exp_arguments;
    213       exp_locations = probe_num_locations(bin_path, name);
    214       exp_arguments = probe_num_arguments(bin_path, name);
    215       REQUIRE(probe->num_locations() == exp_locations);
    216       REQUIRE(probe->num_arguments() == exp_arguments);
    217       REQUIRE(probe->need_enable() == true);
    218     }
    219   }
    220 
    221   SECTION("with a running Ruby process") {
    222     static char _ruby[] = "ruby";
    223     char *const argv[2] = {_ruby, NULL};
    224 
    225     ChildProcess ruby(argv[0], argv);
    226     if (!ruby.spawned())
    227       return;
    228 
    229     USDT::Context ctx(ruby.pid());
    230     REQUIRE(ctx.num_probes() >= mri_probe_count);
    231 
    232     SECTION("get probe in running process") {
    233       auto name = "gc__mark__begin";
    234       auto probe = ctx.get(name);
    235       REQUIRE(probe);
    236 
    237       REQUIRE(probe->in_shared_object(probe->bin_path()) == true);
    238       REQUIRE(probe->name() == name);
    239       REQUIRE(probe->provider() == "ruby");
    240 
    241       auto bin_path = probe->bin_path();
    242       bool bin_path_match =
    243             (bin_path.find("/ruby") != std::string::npos) ||
    244             (bin_path.find("/libruby") != std::string::npos);
    245       REQUIRE(bin_path_match);
    246 
    247       int exp_locations, exp_arguments;
    248       exp_locations = probe_num_locations(bin_path.c_str(), name);
    249       exp_arguments = probe_num_arguments(bin_path.c_str(), name);
    250       REQUIRE(probe->num_locations() == exp_locations);
    251       REQUIRE(probe->num_arguments() == exp_arguments);
    252       REQUIRE(probe->need_enable() == true);
    253     }
    254   }
    255 }
    256