You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
7.3 KiB
256 lines
7.3 KiB
/*
|
|
* Copyright (c) 2016 GitHub, 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.
|
|
*/
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#include "catch.hpp"
|
|
#include "usdt.h"
|
|
#include "api/BPF.h"
|
|
|
|
#ifdef HAVE_SDT_HEADER
|
|
/* required to insert USDT probes on this very executable --
|
|
* we're gonna be testing them live! */
|
|
#include <sys/sdt.h>
|
|
|
|
static int a_probed_function() {
|
|
int an_int = 23 + getpid();
|
|
void *a_pointer = malloc(4);
|
|
DTRACE_PROBE2(libbcc_test, sample_probe_1, an_int, a_pointer);
|
|
free(a_pointer);
|
|
return an_int;
|
|
}
|
|
|
|
TEST_CASE("test finding a probe in our own process", "[usdt]") {
|
|
USDT::Context ctx(getpid());
|
|
REQUIRE(ctx.num_probes() >= 1);
|
|
|
|
SECTION("our test probe") {
|
|
auto probe = ctx.get("sample_probe_1");
|
|
REQUIRE(probe);
|
|
|
|
REQUIRE(probe->in_shared_object(probe->bin_path()) == false);
|
|
REQUIRE(probe->name() == "sample_probe_1");
|
|
REQUIRE(probe->provider() == "libbcc_test");
|
|
REQUIRE(probe->bin_path().find("/test_libbcc") != std::string::npos);
|
|
|
|
REQUIRE(probe->num_locations() == 1);
|
|
REQUIRE(probe->num_arguments() == 2);
|
|
REQUIRE(probe->need_enable() == false);
|
|
|
|
REQUIRE(a_probed_function() != 0);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("test fine a probe in our own binary with C++ API", "[usdt]") {
|
|
ebpf::BPF bpf;
|
|
ebpf::USDT u("/proc/self/exe", "libbcc_test", "sample_probe_1", "on_event");
|
|
|
|
auto res = bpf.init("int on_event() { return 0; }", {}, {u});
|
|
REQUIRE(res.code() == 0);
|
|
|
|
res = bpf.attach_usdt(u);
|
|
REQUIRE(res.code() == 0);
|
|
|
|
res = bpf.detach_usdt(u);
|
|
REQUIRE(res.code() == 0);
|
|
}
|
|
|
|
TEST_CASE("test fine a probe in our Process with C++ API", "[usdt]") {
|
|
ebpf::BPF bpf;
|
|
ebpf::USDT u(::getpid(), "libbcc_test", "sample_probe_1", "on_event");
|
|
|
|
auto res = bpf.init("int on_event() { return 0; }", {}, {u});
|
|
REQUIRE(res.code() == 0);
|
|
|
|
res = bpf.attach_usdt(u);
|
|
REQUIRE(res.code() == 0);
|
|
|
|
res = bpf.detach_usdt(u);
|
|
REQUIRE(res.code() == 0);
|
|
}
|
|
#endif // HAVE_SDT_HEADER
|
|
|
|
class ChildProcess {
|
|
pid_t pid_;
|
|
|
|
public:
|
|
ChildProcess(const char *name, char *const argv[]) {
|
|
pid_ = fork();
|
|
if (pid_ == 0) {
|
|
execvp(name, argv);
|
|
exit(0);
|
|
}
|
|
if (spawned()) {
|
|
usleep(250000);
|
|
if (kill(pid_, 0) < 0)
|
|
pid_ = -1;
|
|
}
|
|
}
|
|
|
|
~ChildProcess() {
|
|
if (spawned()) {
|
|
int status;
|
|
kill(pid_, SIGKILL);
|
|
if (waitpid(pid_, &status, 0) != pid_)
|
|
abort();
|
|
}
|
|
}
|
|
|
|
bool spawned() const { return pid_ > 0; }
|
|
pid_t pid() const { return pid_; }
|
|
};
|
|
|
|
extern int cmd_scanf(const char *cmd, const char *fmt, ...);
|
|
|
|
static int probe_num_locations(const char *bin_path, const char *func_name) {
|
|
int num_locations;
|
|
char cmd[512];
|
|
const char *cmdfmt = "readelf -n %s | grep -c \"Name: %s$\"";
|
|
|
|
sprintf(cmd, cmdfmt, bin_path, func_name);
|
|
if (cmd_scanf(cmd, "%d", &num_locations) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return num_locations;
|
|
}
|
|
|
|
static int probe_num_arguments(const char *bin_path, const char *func_name) {
|
|
int num_arguments;
|
|
char cmd[512];
|
|
const char *cmdfmt = "readelf -n %s | grep -m 1 -A 2 \" %s$\" | " \
|
|
"tail -1 | cut -d \" \" -f 6- | wc -w";
|
|
|
|
sprintf(cmd, cmdfmt, bin_path, func_name);
|
|
if (cmd_scanf(cmd, "%d", &num_arguments) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return num_arguments;
|
|
}
|
|
|
|
TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") {
|
|
size_t mri_probe_count = 0;
|
|
|
|
SECTION("without a running Ruby process") {
|
|
USDT::Context ctx("ruby");
|
|
|
|
if (!ctx.loaded())
|
|
return;
|
|
|
|
REQUIRE(ctx.num_probes() > 10);
|
|
mri_probe_count = ctx.num_probes();
|
|
|
|
SECTION("GC static probe") {
|
|
auto name = "gc__mark__begin";
|
|
auto probe = ctx.get(name);
|
|
REQUIRE(probe);
|
|
|
|
REQUIRE(probe->in_shared_object(probe->bin_path()) == true);
|
|
REQUIRE(probe->name() == name);
|
|
REQUIRE(probe->provider() == "ruby");
|
|
|
|
auto bin_path = probe->bin_path();
|
|
bool bin_path_match =
|
|
(bin_path.find("/ruby") != std::string::npos) ||
|
|
(bin_path.find("/libruby") != std::string::npos);
|
|
REQUIRE(bin_path_match);
|
|
|
|
int exp_locations, exp_arguments;
|
|
exp_locations = probe_num_locations(bin_path.c_str(), name);
|
|
exp_arguments = probe_num_arguments(bin_path.c_str(), name);
|
|
REQUIRE(probe->num_locations() == exp_locations);
|
|
REQUIRE(probe->num_arguments() == exp_arguments);
|
|
REQUIRE(probe->need_enable() == true);
|
|
}
|
|
|
|
SECTION("object creation probe") {
|
|
auto name = "object__create";
|
|
auto probe = ctx.get(name);
|
|
REQUIRE(probe);
|
|
|
|
REQUIRE(probe->in_shared_object(probe->bin_path()) == true);
|
|
REQUIRE(probe->name() == name);
|
|
REQUIRE(probe->provider() == "ruby");
|
|
|
|
auto bin_path = probe->bin_path();
|
|
bool bin_path_match =
|
|
(bin_path.find("/ruby") != std::string::npos) ||
|
|
(bin_path.find("/libruby") != std::string::npos);
|
|
REQUIRE(bin_path_match);
|
|
|
|
int exp_locations, exp_arguments;
|
|
exp_locations = probe_num_locations(bin_path.c_str(), name);
|
|
exp_arguments = probe_num_arguments(bin_path.c_str(), name);
|
|
REQUIRE(probe->num_locations() == exp_locations);
|
|
REQUIRE(probe->num_arguments() == exp_arguments);
|
|
REQUIRE(probe->need_enable() == true);
|
|
}
|
|
|
|
SECTION("array creation probe") {
|
|
auto name = "array__create";
|
|
auto probe = ctx.get(name);
|
|
REQUIRE(probe);
|
|
REQUIRE(probe->name() == name);
|
|
|
|
auto bin_path = probe->bin_path().c_str();
|
|
int exp_locations, exp_arguments;
|
|
exp_locations = probe_num_locations(bin_path, name);
|
|
exp_arguments = probe_num_arguments(bin_path, name);
|
|
REQUIRE(probe->num_locations() == exp_locations);
|
|
REQUIRE(probe->num_arguments() == exp_arguments);
|
|
REQUIRE(probe->need_enable() == true);
|
|
}
|
|
}
|
|
|
|
SECTION("with a running Ruby process") {
|
|
static char _ruby[] = "ruby";
|
|
char *const argv[2] = {_ruby, NULL};
|
|
|
|
ChildProcess ruby(argv[0], argv);
|
|
if (!ruby.spawned())
|
|
return;
|
|
|
|
USDT::Context ctx(ruby.pid());
|
|
REQUIRE(ctx.num_probes() >= mri_probe_count);
|
|
|
|
SECTION("get probe in running process") {
|
|
auto name = "gc__mark__begin";
|
|
auto probe = ctx.get(name);
|
|
REQUIRE(probe);
|
|
|
|
REQUIRE(probe->in_shared_object(probe->bin_path()) == true);
|
|
REQUIRE(probe->name() == name);
|
|
REQUIRE(probe->provider() == "ruby");
|
|
|
|
auto bin_path = probe->bin_path();
|
|
bool bin_path_match =
|
|
(bin_path.find("/ruby") != std::string::npos) ||
|
|
(bin_path.find("/libruby") != std::string::npos);
|
|
REQUIRE(bin_path_match);
|
|
|
|
int exp_locations, exp_arguments;
|
|
exp_locations = probe_num_locations(bin_path.c_str(), name);
|
|
exp_arguments = probe_num_arguments(bin_path.c_str(), name);
|
|
REQUIRE(probe->num_locations() == exp_locations);
|
|
REQUIRE(probe->num_arguments() == exp_arguments);
|
|
REQUIRE(probe->need_enable() == true);
|
|
}
|
|
}
|
|
}
|