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 <linux/version.h>
17 #include <signal.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21
22 #include "catch.hpp"
23 #include "usdt.h"
24 #include "api/BPF.h"
25
26 /* required to insert USDT probes on this very executable --
27 * we're gonna be testing them live! */
28 #include "folly/tracing/StaticTracepoint.h"
29
a_probed_function()30 static int a_probed_function() {
31 int an_int = 23 + getpid();
32 void *a_pointer = malloc(4);
33 FOLLY_SDT(libbcc_test, sample_probe_1, an_int, a_pointer);
34 free(a_pointer);
35 return an_int;
36 }
37
38 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
FOLLY_SDT_DEFINE_SEMAPHORE(libbcc_test,sample_probe_2)39 FOLLY_SDT_DEFINE_SEMAPHORE(libbcc_test, sample_probe_2)
40 static int a_probed_function_with_sem() {
41 int an_int = 23 + getpid();
42 void *a_pointer = malloc(4);
43 FOLLY_SDT_WITH_SEMAPHORE(libbcc_test, sample_probe_2, an_int, a_pointer);
44 free(a_pointer);
45 return an_int;
46 }
47 #endif // linux version >= 4.20
48
49 extern "C" int lib_probed_function();
50
call_shared_lib_func()51 int call_shared_lib_func() {
52 return lib_probed_function();
53 }
54
55 TEST_CASE("test finding a probe in our own process", "[usdt]") {
56 USDT::Context ctx(getpid());
57 REQUIRE(ctx.num_probes() >= 1);
58
59 SECTION("our test probe") {
60 auto probe = ctx.get("sample_probe_1");
61 REQUIRE(probe);
62
63 if(probe->in_shared_object(probe->bin_path()))
64 return;
65 REQUIRE(probe->name() == "sample_probe_1");
66 REQUIRE(probe->provider() == "libbcc_test");
67 REQUIRE(probe->bin_path().find("/test_libbcc") != std::string::npos);
68
69 REQUIRE(probe->num_locations() == 1);
70 REQUIRE(probe->num_arguments() == 2);
71 REQUIRE(probe->need_enable() == false);
72
73 REQUIRE(a_probed_function() != 0);
74 }
75 }
76
77 TEST_CASE("test probe's attributes with C++ API", "[usdt]") {
78 const ebpf::USDT u("/proc/self/exe", "libbcc_test", "sample_probe_1", "on_event");
79
80 REQUIRE(u.binary_path() == "/proc/self/exe");
81 REQUIRE(u.pid() == -1);
82 REQUIRE(u.provider() == "libbcc_test");
83 REQUIRE(u.name() == "sample_probe_1");
84 REQUIRE(u.probe_func() == "on_event");
85 }
86
87 TEST_CASE("test fine a probe in our own binary with C++ API", "[usdt]") {
88 ebpf::BPF bpf;
89 ebpf::USDT u("/proc/self/exe", "libbcc_test", "sample_probe_1", "on_event");
90
91 auto res = bpf.init("int on_event() { return 0; }", {}, {u});
92 REQUIRE(res.ok());
93
94 res = bpf.attach_usdt(u);
95 REQUIRE(res.ok());
96
97 res = bpf.detach_usdt(u);
98 REQUIRE(res.ok());
99 }
100
101 TEST_CASE("test fine probes in our own binary with C++ API", "[usdt]") {
102 ebpf::BPF bpf;
103 ebpf::USDT u("/proc/self/exe", "libbcc_test", "sample_probe_1", "on_event");
104
105 auto res = bpf.init("int on_event() { return 0; }", {}, {u});
106 REQUIRE(res.ok());
107
108 res = bpf.attach_usdt_all();
109 REQUIRE(res.ok());
110
111 res = bpf.detach_usdt_all();
112 REQUIRE(res.ok());
113 }
114
115 TEST_CASE("test fine a probe in our Process with C++ API", "[usdt]") {
116 ebpf::BPF bpf;
117 ebpf::USDT u(::getpid(), "libbcc_test", "sample_probe_1", "on_event");
118
119 auto res = bpf.init("int on_event() { return 0; }", {}, {u});
120 REQUIRE(res.ok());
121
122 res = bpf.attach_usdt(u);
123 REQUIRE(res.ok());
124
125 res = bpf.detach_usdt(u);
126 REQUIRE(res.ok());
127 }
128
129 TEST_CASE("test find a probe in our process' shared libs with c++ API", "[usdt]") {
130 ebpf::BPF bpf;
131 ebpf::USDT u(::getpid(), "libbcc_test", "sample_lib_probe_1", "on_event");
132
133 auto res = bpf.init("int on_event() { return 0; }", {}, {u});
134 REQUIRE(res.msg() == "");
135 REQUIRE(res.ok());
136 }
137
138 TEST_CASE("test usdt partial init w/ fail init_usdt", "[usdt]") {
139 ebpf::BPF bpf;
140 ebpf::USDT u(::getpid(), "libbcc_test", "sample_lib_probe_nonexistent", "on_event");
141 ebpf::USDT p(::getpid(), "libbcc_test", "sample_lib_probe_1", "on_event");
142
143 // We should be able to fail initialization and subsequently do bpf.init w/o USDT
144 // successfully
145 auto res = bpf.init_usdt(u);
146 REQUIRE(res.msg() != "");
147 REQUIRE(!res.ok());
148
149 // Shouldn't be necessary to re-init bpf object either after failure to init w/
150 // bad USDT
151 res = bpf.init("int on_event() { return 0; }", {}, {u});
152 REQUIRE(res.msg() != "");
153 REQUIRE(!res.ok());
154
155 res = bpf.init_usdt(p);
156 REQUIRE(res.msg() == "");
157 REQUIRE(res.ok());
158
159 res = bpf.init("int on_event() { return 0; }", {}, {});
160 REQUIRE(res.msg() == "");
161 REQUIRE(res.ok());
162 }
163
164 class ChildProcess {
165 pid_t pid_;
166
167 public:
ChildProcess(const char * name,char * const argv[])168 ChildProcess(const char *name, char *const argv[]) {
169 pid_ = fork();
170 if (pid_ == 0) {
171 execvp(name, argv);
172 exit(0);
173 }
174 if (spawned()) {
175 usleep(250000);
176 if (kill(pid_, 0) < 0)
177 pid_ = -1;
178 }
179 }
180
~ChildProcess()181 ~ChildProcess() {
182 if (spawned()) {
183 int status;
184 kill(pid_, SIGKILL);
185 if (waitpid(pid_, &status, 0) != pid_)
186 abort();
187 }
188 }
189
spawned() const190 bool spawned() const { return pid_ > 0; }
pid() const191 pid_t pid() const { return pid_; }
192 };
193
194 extern int cmd_scanf(const char *cmd, const char *fmt, ...);
195
probe_num_locations(const char * bin_path,const char * func_name)196 static int probe_num_locations(const char *bin_path, const char *func_name) {
197 int num_locations;
198 char cmd[512];
199 const char *cmdfmt = "readelf -n %s | grep -c \"Name: %s$\"";
200
201 sprintf(cmd, cmdfmt, bin_path, func_name);
202 if (cmd_scanf(cmd, "%d", &num_locations) != 0) {
203 return -1;
204 }
205
206 return num_locations;
207 }
208
probe_num_arguments(const char * bin_path,const char * func_name)209 static int probe_num_arguments(const char *bin_path, const char *func_name) {
210 int num_arguments;
211 char cmd[512];
212 const char *cmdfmt = "readelf -n %s | grep -m 1 -A 2 \" %s$\" | " \
213 "tail -1 | cut -d \" \" -f 6- | wc -w";
214
215 sprintf(cmd, cmdfmt, bin_path, func_name);
216 if (cmd_scanf(cmd, "%d", &num_arguments) != 0) {
217 return -1;
218 }
219
220 return num_arguments;
221 }
222
223 // Unsharing pid namespace requires forking
224 // this uses pgrep to find the child process, by searching for a process
225 // that has the unshare as its parent
unshared_child_pid(const int ppid)226 static int unshared_child_pid(const int ppid) {
227 int child_pid;
228 char cmd[512];
229 const char *cmdfmt = "pgrep -P %d";
230
231 sprintf(cmd, cmdfmt, ppid);
232 if (cmd_scanf(cmd, "%d", &child_pid) != 0) {
233 return -1;
234 }
235 return child_pid;
236 }
237
238 // FIXME This seems like a legitimate bug with probing ruby where the
239 // ruby symbols are in libruby.so?
240 TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt][!mayfail]") {
241 size_t mri_probe_count = 0;
242
243 SECTION("without a running Ruby process") {
244 USDT::Context ctx("ruby");
245
246 if (!ctx.loaded())
247 return;
248
249 REQUIRE(ctx.num_probes() > 10);
250 mri_probe_count = ctx.num_probes();
251
252 SECTION("GC static probe") {
253 auto name = "gc__mark__begin";
254 auto probe = ctx.get(name);
255 REQUIRE(probe);
256
257 REQUIRE(probe->in_shared_object(probe->bin_path()) == true);
258 REQUIRE(probe->name() == name);
259 REQUIRE(probe->provider() == "ruby");
260
261 auto bin_path = probe->bin_path();
262 bool bin_path_match =
263 (bin_path.find("/ruby") != std::string::npos) ||
264 (bin_path.find("/libruby") != std::string::npos);
265 REQUIRE(bin_path_match);
266
267 int exp_locations, exp_arguments;
268 exp_locations = probe_num_locations(bin_path.c_str(), name);
269 exp_arguments = probe_num_arguments(bin_path.c_str(), name);
270 REQUIRE(probe->num_locations() == exp_locations);
271 REQUIRE(probe->num_arguments() == exp_arguments);
272 REQUIRE(probe->need_enable() == true);
273 }
274
275 SECTION("object creation probe") {
276 auto name = "object__create";
277 auto probe = ctx.get(name);
278 REQUIRE(probe);
279
280 REQUIRE(probe->in_shared_object(probe->bin_path()) == true);
281 REQUIRE(probe->name() == name);
282 REQUIRE(probe->provider() == "ruby");
283
284 auto bin_path = probe->bin_path();
285 bool bin_path_match =
286 (bin_path.find("/ruby") != std::string::npos) ||
287 (bin_path.find("/libruby") != std::string::npos);
288 REQUIRE(bin_path_match);
289
290 int exp_locations, exp_arguments;
291 exp_locations = probe_num_locations(bin_path.c_str(), name);
292 exp_arguments = probe_num_arguments(bin_path.c_str(), name);
293 REQUIRE(probe->num_locations() == exp_locations);
294 REQUIRE(probe->num_arguments() == exp_arguments);
295 REQUIRE(probe->need_enable() == true);
296 }
297
298 SECTION("array creation probe") {
299 auto name = "array__create";
300 auto probe = ctx.get(name);
301 REQUIRE(probe);
302 REQUIRE(probe->name() == name);
303
304 auto bin_path = probe->bin_path().c_str();
305 int exp_locations, exp_arguments;
306 exp_locations = probe_num_locations(bin_path, name);
307 exp_arguments = probe_num_arguments(bin_path, name);
308 REQUIRE(probe->num_locations() == exp_locations);
309 REQUIRE(probe->num_arguments() == exp_arguments);
310 REQUIRE(probe->need_enable() == true);
311 }
312 }
313
314 SECTION("with a running Ruby process") {
315 static char _ruby[] = "ruby";
316 char *const argv[2] = {_ruby, NULL};
317
318 ChildProcess ruby(argv[0], argv);
319 if (!ruby.spawned())
320 return;
321
322 USDT::Context ctx(ruby.pid());
323 REQUIRE(ctx.num_probes() >= mri_probe_count);
324
325 SECTION("get probe in running process") {
326 auto name = "gc__mark__begin";
327 auto probe = ctx.get(name);
328 REQUIRE(probe);
329
330 REQUIRE(probe->in_shared_object(probe->bin_path()) == true);
331 REQUIRE(probe->name() == name);
332 REQUIRE(probe->provider() == "ruby");
333
334 auto bin_path = probe->bin_path();
335 bool bin_path_match =
336 (bin_path.find("/ruby") != std::string::npos) ||
337 (bin_path.find("/libruby") != std::string::npos);
338 REQUIRE(bin_path_match);
339
340 int exp_locations, exp_arguments;
341 exp_locations = probe_num_locations(bin_path.c_str(), name);
342 exp_arguments = probe_num_arguments(bin_path.c_str(), name);
343 REQUIRE(probe->num_locations() == exp_locations);
344 REQUIRE(probe->num_arguments() == exp_arguments);
345 REQUIRE(probe->need_enable() == true);
346 }
347 }
348 }
349
350 // These tests are expected to fail if there is no Ruby with dtrace probes
351 TEST_CASE("test probing running Ruby process in namespaces",
352 "[usdt][!mayfail]") {
353 SECTION("in separate mount namespace") {
354 static char _unshare[] = "unshare";
355 const char *const argv[4] = {_unshare, "--mount", "ruby", NULL};
356
357 ChildProcess unshare(argv[0], (char **const)argv);
358 if (!unshare.spawned())
359 return;
360 int ruby_pid = unshare.pid();
361
362 ebpf::BPF bpf;
363 ebpf::USDT u(ruby_pid, "ruby", "gc__mark__begin", "on_event");
364 u.set_probe_matching_kludge(1); // Also required for overlayfs...
365
366 auto res = bpf.init("int on_event() { return 0; }", {}, {u});
367 REQUIRE(res.msg() == "");
368 REQUIRE(res.ok());
369
370 res = bpf.attach_usdt(u, ruby_pid);
371 REQUIRE(res.ok());
372
373 res = bpf.detach_usdt(u, ruby_pid);
374 REQUIRE(res.ok());
375 }
376
377 SECTION("in separate mount namespace and separate PID namespace") {
378 static char _unshare[] = "unshare";
379 const char *const argv[8] = {_unshare, "--fork",
380 "--mount", "--pid", "--mount-proc",
381 "ruby", NULL};
382
383 ChildProcess unshare(argv[0], (char **const)argv);
384 if (!unshare.spawned())
385 return;
386 int ruby_pid = unshared_child_pid(unshare.pid());
387
388 ebpf::BPF bpf;
389 ebpf::USDT u(ruby_pid, "ruby", "gc__mark__begin", "on_event");
390 u.set_probe_matching_kludge(1); // Also required for overlayfs...
391
392 auto res = bpf.init("int on_event() { return 0; }", {}, {u});
393 REQUIRE(res.msg() == "");
394 REQUIRE(res.ok());
395
396 res = bpf.attach_usdt(u, ruby_pid);
397 REQUIRE(res.ok());
398
399 res = bpf.detach_usdt(u, ruby_pid);
400 REQUIRE(res.ok());
401
402 struct bcc_symbol sym;
403 std::string pid_root= "/proc/" + std::to_string(ruby_pid) + "/root/";
404 std::string module = pid_root + "usr/local/bin/ruby";
405 REQUIRE(bcc_resolve_symname(module.c_str(), "rb_gc_mark", 0x0, ruby_pid, nullptr, &sym) == 0);
406 REQUIRE(std::string(sym.module).find(pid_root, 1) == std::string::npos);
407 kill(ruby_pid, SIGKILL);
408 bcc_procutils_free(sym.module);
409 }
410 }
411
412 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
413 TEST_CASE("Test uprobe refcnt semaphore activation", "[usdt]") {
414 ebpf::BPF bpf;
415
416 REQUIRE(!FOLLY_SDT_IS_ENABLED(libbcc_test, sample_probe_2));
417
418 ebpf::USDT u("/proc/self/exe", "libbcc_test", "sample_probe_2", "on_event");
419
420 auto res = bpf.init("int on_event() { return 0; }", {}, {u});
421 REQUIRE(res.ok());
422
423 res = bpf.attach_usdt(u);
424 REQUIRE(res.ok());
425
426 REQUIRE(FOLLY_SDT_IS_ENABLED(libbcc_test, sample_probe_2));
427
428 res = bpf.detach_usdt(u);
429 REQUIRE(res.ok());
430
431 REQUIRE(a_probed_function_with_sem() != 0);
432 }
433 #endif // linux version >= 4.20
434