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