Commit 10436451 authored by Vicent Marti's avatar Vicent Marti

cc: Better memory handling for USDT probes

parent 4ea4af45
...@@ -24,17 +24,16 @@ ...@@ -24,17 +24,16 @@
#include "bcc_syms.h" #include "bcc_syms.h"
#include "syms.h" #include "syms.h"
#include "vendor/tinyformat.hpp"
ino_t ProcStat::getinode_() { ino_t ProcStat::getinode_() {
struct stat s; struct stat s;
return (!stat(procfs_.c_str(), &s)) ? s.st_ino : -1; return (!stat(procfs_.c_str(), &s)) ? s.st_ino : -1;
} }
ProcStat::ProcStat(int pid) : inode_(-1) { ProcStat::ProcStat(int pid) :
char buffer[128]; procfs_(tfm::format("/proc/%d/exe", pid)),
snprintf(buffer, sizeof(buffer), "/proc/%d/exe", pid); inode_(getinode_()) {}
procfs_ = buffer;
}
void KSyms::_add_symbol(const char *symname, uint64_t addr, void *p) { void KSyms::_add_symbol(const char *symname, uint64_t addr, void *p) {
KSyms *ks = static_cast<KSyms *>(p); KSyms *ks = static_cast<KSyms *>(p);
...@@ -84,11 +83,15 @@ bool KSyms::resolve_name(const char *_unused, const char *name, ...@@ -84,11 +83,15 @@ bool KSyms::resolve_name(const char *_unused, const char *name,
return true; return true;
} }
ProcSyms::ProcSyms(int pid) : pid_(pid), procstat_(pid) { refresh(); } ProcSyms::ProcSyms(int pid) : pid_(pid), procstat_(pid) { load_modules(); }
bool ProcSyms::load_modules() {
return bcc_procutils_each_module(pid_, _add_module, this) == 0;
}
void ProcSyms::refresh() { void ProcSyms::refresh() {
modules_.clear(); modules_.clear();
bcc_procutils_each_module(pid_, _add_module, this); load_modules();
procstat_.reset(); procstat_.reset();
} }
......
...@@ -93,6 +93,7 @@ class ProcSyms : SymbolCache { ...@@ -93,6 +93,7 @@ class ProcSyms : SymbolCache {
ProcStat procstat_; ProcStat procstat_;
static int _add_module(const char *, uint64_t, uint64_t, void *); static int _add_module(const char *, uint64_t, uint64_t, void *);
bool load_modules();
public: public:
ProcSyms(int pid); ProcSyms(int pid);
......
...@@ -38,11 +38,12 @@ Probe::Location::Location(uint64_t addr, const char *arg_fmt) : address_(addr) { ...@@ -38,11 +38,12 @@ Probe::Location::Location(uint64_t addr, const char *arg_fmt) : address_(addr) {
} }
Probe::Probe(const char *bin_path, const char *provider, const char *name, Probe::Probe(const char *bin_path, const char *provider, const char *name,
uint64_t semaphore) uint64_t semaphore, const optional<int> &pid)
: bin_path_(bin_path), : bin_path_(bin_path),
provider_(provider), provider_(provider),
name_(name), name_(name),
semaphore_(semaphore) {} semaphore_(semaphore),
pid_(pid) {}
bool Probe::in_shared_object() { bool Probe::in_shared_object() {
if (!in_shared_object_) if (!in_shared_object_)
...@@ -50,45 +51,36 @@ bool Probe::in_shared_object() { ...@@ -50,45 +51,36 @@ bool Probe::in_shared_object() {
return in_shared_object_.value(); return in_shared_object_.value();
} }
bool Probe::resolve_global_address(uint64_t *global, const uint64_t addr, bool Probe::resolve_global_address(uint64_t *global, const uint64_t addr) {
optional<int> pid) {
if (in_shared_object()) { if (in_shared_object()) {
return (pid && return (pid_ && !bcc_resolve_global_addr(
bcc_resolve_global_addr(*pid, bin_path_.c_str(), addr, global) == *pid_, bin_path_.c_str(), addr, global));
0);
} }
*global = addr; *global = addr;
return true; return true;
} }
bool Probe::lookup_semaphore_addr(uint64_t *address, int pid) { bool Probe::add_to_semaphore(int16_t val) {
auto it = semaphores_.find(pid); assert(pid_ && attached_semaphore_);
if (it != semaphores_.end()) {
*address = it->second;
return true;
}
if (!resolve_global_address(address, semaphore_, pid)) if (!attached_semaphore_) {
uint64_t addr;
if (!resolve_global_address(&addr, semaphore_))
return false; return false;
attached_semaphore_ = addr;
}
semaphores_[pid] = *address; off_t address = static_cast<off_t>(attached_semaphore_.value());
return true;
}
bool Probe::add_to_semaphore(int pid, int16_t val) {
uint64_t address;
if (!lookup_semaphore_addr(&address, pid))
return false;
std::string procmem = tfm::format("/proc/%d/mem", pid); std::string procmem = tfm::format("/proc/%d/mem", pid_.value());
int memfd = ::open(procmem.c_str(), O_RDWR); int memfd = ::open(procmem.c_str(), O_RDWR);
if (memfd < 0) if (memfd < 0)
return false; return false;
int16_t original; // TODO: should this be unsigned? int16_t original;
if (::lseek(memfd, static_cast<off_t>(address), SEEK_SET) < 0 || if (::lseek(memfd, address, SEEK_SET) < 0 ||
::read(memfd, &original, 2) != 2) { ::read(memfd, &original, 2) != 2) {
::close(memfd); ::close(memfd);
return false; return false;
...@@ -96,7 +88,7 @@ bool Probe::add_to_semaphore(int pid, int16_t val) { ...@@ -96,7 +88,7 @@ bool Probe::add_to_semaphore(int pid, int16_t val) {
original = original + val; original = original + val;
if (::lseek(memfd, static_cast<off_t>(address), SEEK_SET) < 0 || if (::lseek(memfd, address, SEEK_SET) < 0 ||
::write(memfd, &original, 2) != 2) { ::write(memfd, &original, 2) != 2) {
::close(memfd); ::close(memfd);
return false; return false;
...@@ -106,28 +98,33 @@ bool Probe::add_to_semaphore(int pid, int16_t val) { ...@@ -106,28 +98,33 @@ bool Probe::add_to_semaphore(int pid, int16_t val) {
return true; return true;
} }
bool Probe::enable(int pid) { bool Probe::enable(const std::string &fn_name) {
if (enabled_semaphores_.find(pid) != enabled_semaphores_.end()) if (attached_to_)
return true; return false;
if (need_enable()) {
if (!pid_)
return false;
if (!add_to_semaphore(pid, +1)) if (!add_to_semaphore(+1))
return false; return false;
}
enabled_semaphores_.emplace(pid, std::move(ProcStat(pid))); attached_to_ = fn_name;
return true; return true;
} }
bool Probe::disable(int pid) { bool Probe::disable() {
auto it = enabled_semaphores_.find(pid); if (!attached_to_)
if (it == enabled_semaphores_.end())
return false; return false;
bool result = true; attached_to_ = nullopt;
if (!it->second.is_stale())
result = add_to_semaphore(pid, -1);
enabled_semaphores_.erase(it); if (need_enable()) {
return result; assert(pid_);
return add_to_semaphore(-1);
}
return true;
} }
std::string Probe::largest_arg_type(size_t arg_n) { std::string Probe::largest_arg_type(size_t arg_n) {
...@@ -143,10 +140,12 @@ std::string Probe::largest_arg_type(size_t arg_n) { ...@@ -143,10 +140,12 @@ std::string Probe::largest_arg_type(size_t arg_n) {
return largest->ctype(); return largest->ctype();
} }
bool Probe::usdt_getarg(std::ostream &stream, bool Probe::usdt_getarg(std::ostream &stream) {
const std::string &fn_name, const optional<int> &pid) {
const size_t arg_count = locations_[0].arguments_.size(); const size_t arg_count = locations_[0].arguments_.size();
if (!attached_to_)
return false;
if (arg_count == 0) if (arg_count == 0)
return true; return true;
...@@ -158,13 +157,13 @@ bool Probe::usdt_getarg(std::ostream &stream, ...@@ -158,13 +157,13 @@ bool Probe::usdt_getarg(std::ostream &stream,
"static inline int _bpf_readarg_%s_%d(" "static inline int _bpf_readarg_%s_%d("
"struct pt_regs *ctx, void *dest, size_t len) {\n" "struct pt_regs *ctx, void *dest, size_t len) {\n"
" if (len != sizeof(%s)) return -1;\n", " if (len != sizeof(%s)) return -1;\n",
fn_name, arg_n + 1, ctype); attached_to_.value(), arg_n + 1, ctype);
if (locations_.size() == 1) { if (locations_.size() == 1) {
Location &location = locations_.front(); Location &location = locations_.front();
stream << " "; stream << " ";
if (!location.arguments_[arg_n].assign_to_local(stream, cptr, if (!location.arguments_[arg_n].assign_to_local(stream, cptr,
bin_path_, pid)) bin_path_, pid_))
return false; return false;
stream << "\n return 0;\n}\n"; stream << "\n return 0;\n}\n";
} else { } else {
...@@ -172,12 +171,12 @@ bool Probe::usdt_getarg(std::ostream &stream, ...@@ -172,12 +171,12 @@ bool Probe::usdt_getarg(std::ostream &stream,
for (Location &location : locations_) { for (Location &location : locations_) {
uint64_t global_address; uint64_t global_address;
if (!resolve_global_address(&global_address, location.address_, pid)) if (!resolve_global_address(&global_address, location.address_))
return false; return false;
tfm::format(stream, " case 0x%xULL: ", global_address); tfm::format(stream, " case 0x%xULL: ", global_address);
if (!location.arguments_[arg_n].assign_to_local(stream, cptr, if (!location.arguments_[arg_n].assign_to_local(stream, cptr,
bin_path_, pid)) bin_path_, pid_))
return false; return false;
stream << " return 0;\n"; stream << " return 0;\n";
...@@ -205,22 +204,16 @@ int Context::_each_module(const char *modpath, uint64_t, uint64_t, void *p) { ...@@ -205,22 +204,16 @@ int Context::_each_module(const char *modpath, uint64_t, uint64_t, void *p) {
} }
void Context::add_probe(const char *binpath, const struct bcc_elf_usdt *probe) { void Context::add_probe(const char *binpath, const struct bcc_elf_usdt *probe) {
Probe *found_probe = nullptr; for (auto &p : probes_) {
for (Probe *p : probes_) {
if (p->provider_ == probe->provider && p->name_ == probe->name) { if (p->provider_ == probe->provider && p->name_ == probe->name) {
found_probe = p; p->add_location(probe->pc, probe->arg_fmt);
break; return;
}
} }
if (!found_probe) {
found_probe =
new Probe(binpath, probe->provider, probe->name, probe->semaphore);
probes_.push_back(found_probe);
} }
found_probe->add_location(probe->pc, probe->arg_fmt); probes_.emplace_back(new Probe(binpath, probe->provider,
probe->name, probe->semaphore, pid_));
probes_.back()->add_location(probe->pc, probe->arg_fmt);
} }
std::string Context::resolve_bin_path(const std::string &bin_path) { std::string Context::resolve_bin_path(const std::string &bin_path) {
...@@ -236,8 +229,8 @@ std::string Context::resolve_bin_path(const std::string &bin_path) { ...@@ -236,8 +229,8 @@ std::string Context::resolve_bin_path(const std::string &bin_path) {
return result; return result;
} }
Probe *Context::get(const std::string &probe_name) const { std::shared_ptr<Probe> Context::get(const std::string &probe_name) {
for (Probe *p : probes_) { for (auto &p : probes_) {
if (p->name_ == probe_name) if (p->name_ == probe_name)
return p; return p;
} }
...@@ -246,8 +239,8 @@ Probe *Context::get(const std::string &probe_name) const { ...@@ -246,8 +239,8 @@ Probe *Context::get(const std::string &probe_name) const {
bool Context::generate_usdt_args(std::ostream &stream) { bool Context::generate_usdt_args(std::ostream &stream) {
stream << "#include <uapi/linux/ptrace.h>\n"; stream << "#include <uapi/linux/ptrace.h>\n";
for (auto &p : uprobes_) { for (auto &p : probes_) {
if (!p.first->usdt_getarg(stream, p.second, pid_)) if (p->enabled() && !p->usdt_getarg(stream))
return false; return false;
} }
return true; return true;
...@@ -255,23 +248,20 @@ bool Context::generate_usdt_args(std::ostream &stream) { ...@@ -255,23 +248,20 @@ bool Context::generate_usdt_args(std::ostream &stream) {
bool Context::enable_probe(const std::string &probe_name, bool Context::enable_probe(const std::string &probe_name,
const std::string &fn_name) { const std::string &fn_name) {
Probe *p = get(probe_name); if (pid_stat_ && pid_stat_->is_stale())
if (!p)
return false; return false;
if (p->need_enable()) { auto p = get(probe_name);
if (!pid_ || !p->enable(pid_.value())) return p && p->enable(fn_name);
return false;
}
uprobes_.emplace_back(p, fn_name);
return true;
} }
void Context::each_uprobe(each_uprobe_cb callback) { void Context::each_uprobe(each_uprobe_cb callback) {
for (auto &p : uprobes_) { for (auto &p : probes_) {
for (Probe::Location &loc : p.first->locations_) { if (!p->enabled())
callback(p.first->bin_path_.c_str(), p.second.c_str(), loc.address_, continue;
for (Probe::Location &loc : p->locations_) {
callback(p->bin_path_.c_str(), p->attached_to_->c_str(), loc.address_,
pid_.value_or(-1)); pid_.value_or(-1));
} }
} }
...@@ -285,16 +275,15 @@ Context::Context(const std::string &bin_path) : loaded_(false) { ...@@ -285,16 +275,15 @@ Context::Context(const std::string &bin_path) : loaded_(false) {
} }
} }
Context::Context(int pid) : pid_(pid), loaded_(false) { Context::Context(int pid) : pid_(pid), pid_stat_(pid), loaded_(false) {
if (bcc_procutils_each_module(pid, _each_module, this) == 0) if (bcc_procutils_each_module(pid, _each_module, this) == 0)
loaded_ = true; loaded_ = true;
} }
Context::~Context() { Context::~Context() {
for (Probe *p : probes_) { if (pid_stat_ && !pid_stat_->is_stale()) {
if (pid_ && p->enabled()) for (auto &p : probes_)
p->disable(pid_.value()); p->disable();
delete p;
} }
} }
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
#pragma once #pragma once
#include <memory>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
...@@ -127,32 +128,34 @@ class Probe { ...@@ -127,32 +128,34 @@ class Probe {
}; };
std::vector<Location> locations_; std::vector<Location> locations_;
std::unordered_map<int, uint64_t> semaphores_;
std::unordered_map<int, ProcStat> enabled_semaphores_; optional<int> pid_;
optional<bool> in_shared_object_; optional<bool> in_shared_object_;
optional<std::string> attached_to_;
optional<uint64_t> attached_semaphore_;
std::string largest_arg_type(size_t arg_n); std::string largest_arg_type(size_t arg_n);
bool add_to_semaphore(int pid, int16_t val); bool add_to_semaphore(int16_t val);
bool resolve_global_address(uint64_t *global, const uint64_t addr, bool resolve_global_address(uint64_t *global, const uint64_t addr);
optional<int> pid); bool lookup_semaphore_addr(uint64_t *address);
bool lookup_semaphore_addr(uint64_t *address, int pid);
void add_location(uint64_t addr, const char *fmt); void add_location(uint64_t addr, const char *fmt);
public: public:
Probe(const char *bin_path, const char *provider, const char *name, Probe(const char *bin_path, const char *provider, const char *name,
uint64_t semaphore); uint64_t semaphore, const optional<int> &pid);
size_t num_locations() const { return locations_.size(); } size_t num_locations() const { return locations_.size(); }
size_t num_arguments() const { return locations_.front().arguments_.size(); } size_t num_arguments() const { return locations_.front().arguments_.size(); }
uint64_t address(size_t n = 0) const { return locations_[n].address_; } uint64_t address(size_t n = 0) const { return locations_[n].address_; }
bool usdt_getarg(std::ostream &stream, const std::string &fn_name, const optional<int> &pid = nullopt); bool usdt_getarg(std::ostream &stream);
bool need_enable() const { return semaphore_ != 0x0; } bool need_enable() const { return semaphore_ != 0x0; }
bool enable(int pid); bool enable(const std::string &fn_name);
bool disable(int pid); bool disable();
bool enabled() const { return !enabled_semaphores_.empty(); } bool enabled() const { return !!attached_to_; }
bool in_shared_object(); bool in_shared_object();
const std::string &name() { return name_; } const std::string &name() { return name_; }
...@@ -163,9 +166,10 @@ public: ...@@ -163,9 +166,10 @@ public:
}; };
class Context { class Context {
std::vector<Probe *> probes_; std::vector<std::shared_ptr<Probe>> probes_;
std::vector<std::pair<Probe *, std::string>> uprobes_;
optional<int> pid_; optional<int> pid_;
optional<ProcStat> pid_stat_;
bool loaded_; bool loaded_;
static void _each_probe(const char *binpath, const struct bcc_elf_usdt *probe, static void _each_probe(const char *binpath, const struct bcc_elf_usdt *probe,
...@@ -184,8 +188,8 @@ public: ...@@ -184,8 +188,8 @@ public:
bool loaded() const { return loaded_; } bool loaded() const { return loaded_; }
size_t num_probes() const { return probes_.size(); } size_t num_probes() const { return probes_.size(); }
Probe *get(const std::string &probe_name) const; std::shared_ptr<Probe> get(const std::string &probe_name);
Probe *get(int pos) const { return probes_[pos]; } std::shared_ptr<Probe> get(int pos) { return probes_[pos]; }
bool enable_probe(const std::string &probe_name, const std::string &fn_name); bool enable_probe(const std::string &probe_name, const std::string &fn_name);
bool generate_usdt_args(std::ostream &stream); bool generate_usdt_args(std::ostream &stream);
......
...@@ -39,8 +39,8 @@ TEST_CASE("test finding a probe in our own process", "[usdt]") { ...@@ -39,8 +39,8 @@ TEST_CASE("test finding a probe in our own process", "[usdt]") {
REQUIRE(ctx.num_probes() >= 1); REQUIRE(ctx.num_probes() >= 1);
SECTION("our test probe") { SECTION("our test probe") {
USDT::Probe *probe = ctx.get("sample_probe_1"); auto probe = ctx.get("sample_probe_1");
REQUIRE(probe != nullptr); REQUIRE(probe);
REQUIRE(probe->in_shared_object() == false); REQUIRE(probe->in_shared_object() == false);
REQUIRE(probe->name() == "sample_probe_1"); REQUIRE(probe->name() == "sample_probe_1");
...@@ -108,8 +108,8 @@ TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") { ...@@ -108,8 +108,8 @@ TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") {
mri_probe_count = ctx.num_probes(); mri_probe_count = ctx.num_probes();
SECTION("GC static probe") { SECTION("GC static probe") {
USDT::Probe *probe = ctx.get("gc__mark__begin"); auto probe = ctx.get("gc__mark__begin");
REQUIRE(probe != nullptr); REQUIRE(probe);
REQUIRE(probe->in_shared_object() == true); REQUIRE(probe->in_shared_object() == true);
REQUIRE(probe->name() == "gc__mark__begin"); REQUIRE(probe->name() == "gc__mark__begin");
...@@ -122,8 +122,8 @@ TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") { ...@@ -122,8 +122,8 @@ TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") {
} }
SECTION("object creation probe") { SECTION("object creation probe") {
USDT::Probe *probe = ctx.get("object__create"); auto probe = ctx.get("object__create");
REQUIRE(probe != nullptr); REQUIRE(probe);
REQUIRE(probe->in_shared_object() == true); REQUIRE(probe->in_shared_object() == true);
REQUIRE(probe->name() == "object__create"); REQUIRE(probe->name() == "object__create");
...@@ -136,8 +136,8 @@ TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") { ...@@ -136,8 +136,8 @@ TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") {
} }
SECTION("array creation probe") { SECTION("array creation probe") {
USDT::Probe *probe = ctx.get("array__create"); auto probe = ctx.get("array__create");
REQUIRE(probe != nullptr); REQUIRE(probe);
REQUIRE(probe->name() == "array__create"); REQUIRE(probe->name() == "array__create");
REQUIRE(probe->num_locations() == 7); REQUIRE(probe->num_locations() == 7);
...@@ -158,8 +158,8 @@ TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") { ...@@ -158,8 +158,8 @@ TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") {
REQUIRE(ctx.num_probes() >= mri_probe_count); REQUIRE(ctx.num_probes() >= mri_probe_count);
SECTION("get probe in running process") { SECTION("get probe in running process") {
USDT::Probe *probe = ctx.get("gc__mark__begin"); auto probe = ctx.get("gc__mark__begin");
REQUIRE(probe != nullptr); REQUIRE(probe);
REQUIRE(probe->in_shared_object() == true); REQUIRE(probe->in_shared_object() == true);
REQUIRE(probe->name() == "gc__mark__begin"); REQUIRE(probe->name() == "gc__mark__begin");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment