Skip to content

Commit

Permalink
[uprobe] support for pid targeting for shared libs
Browse files Browse the repository at this point in the history
This adds support for specifying the pid even when targeting
a uprobe/uretprobe in a library shared by multiple pids.

Example:
```
sudo bpftrace -p 1899508 -e 'uprobe:libc:getaddrinfo {
  print((comm, pid));
}
```

[Issue 2817](#2817)
  • Loading branch information
Jordan Rome committed Nov 19, 2023
1 parent 3afd12a commit c156b30
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 15 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ and this project adheres to
- [#2781](https://github.com/iovisor/bpftrace/pull/2781)
- Add support for uprobe_multi link
- [#2810](https://github.com/iovisor/bpftrace/pull/2810)
- Add support for uprobe pid targeting
- [#2830](https://github.com/iovisor/bpftrace/pull/2830)
#### Changed
#### Deprecated
#### Removed
Expand Down
3 changes: 2 additions & 1 deletion man/adoc/bpftrace.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ u:lib.so:"fn(char const*)" { printf("arg0:%s\n", str(arg0));}

*-p* _PID_::
Attach to the process with _PID_. If the process terminates, bpftrace will also terminate.
When using USDT probes they will be attached to only this process. For uprobes/uretprobes if you also set the target to '*' the process's address space will be searched for the symbols.
When using USDT probes, uprobes, and uretprobes they will be attached to only this process.
For listing uprobes/uretprobes if you set the target to '*' the process's address space will be searched for the symbols.

*-c* _COMMAND_::
Run _COMMAND_ as a child process.
Expand Down
28 changes: 17 additions & 11 deletions src/attached_probe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ AttachedProbe::AttachedProbe(Probe &probe,
// If BPF_PROG_TYPE_RAW_TRACEPOINT is available, no need to attach prog
// to anything -- we will simply BPF_PROG_RUN it
if (!feature.has_raw_tp_special())
attach_uprobe(safe_mode);
attach_uprobe(0, safe_mode);
break;
case ProbeType::kprobe:
attach_kprobe(safe_mode);
Expand All @@ -200,10 +200,6 @@ AttachedProbe::AttachedProbe(Probe &probe,
check_banned_kretprobes(probe_.attach_point);
attach_kprobe(safe_mode);
break;
case ProbeType::uprobe:
case ProbeType::uretprobe:
attach_uprobe(safe_mode);
break;
case ProbeType::tracepoint:
attach_tracepoint();
break;
Expand Down Expand Up @@ -239,7 +235,8 @@ AttachedProbe::AttachedProbe(Probe &probe,
BpfProgram &&prog,
int pid,
BPFfeature &feature,
BTF &btf)
BTF &btf,
bool safe_mode)
: probe_(probe), prog_(std::move(prog)), btf_(btf)
{
load_prog(feature);
Expand All @@ -252,6 +249,10 @@ AttachedProbe::AttachedProbe(Probe &probe,
case ProbeType::asyncwatchpoint:
attach_watchpoint(pid, probe.mode);
break;
case ProbeType::uprobe:
case ProbeType::uretprobe:
attach_uprobe(pid, safe_mode);
break;
default:
LOG(FATAL) << "invalid attached probe type \""
<< probetypeName(probe_.type) << "\"";
Expand Down Expand Up @@ -1081,7 +1082,7 @@ static void resolve_offset_uprobe_multi(const std::string &path,
}
}

void AttachedProbe::attach_multi_uprobe(void)
void AttachedProbe::attach_multi_uprobe(int pid)
{
std::vector<std::string> syms;
std::vector<uint64_t> offsets;
Expand All @@ -1100,6 +1101,11 @@ void AttachedProbe::attach_multi_uprobe(void)
opts.uprobe_multi.flags = probe_.type == ProbeType::uretprobe
? BPF_F_UPROBE_MULTI_RETURN
: 0;
if (pid != 0)
{
opts.uprobe_multi.pid = pid;
}

if (bt_verbose)
{
std::cout << "Attaching to " << probe_.funcs.size() << " functions"
Expand All @@ -1125,16 +1131,16 @@ void AttachedProbe::attach_multi_uprobe(void)
}
}
#else
void AttachedProbe::attach_multi_uprobe(void)
void AttachedProbe::attach_multi_uprobe(int)
{
}
#endif // HAVE_LIBBPF_UPROBE_MULTI

void AttachedProbe::attach_uprobe(bool safe_mode)
void AttachedProbe::attach_uprobe(int pid, bool safe_mode)
{
if (!probe_.funcs.empty())
{
attach_multi_uprobe();
attach_multi_uprobe(pid);
return;
}

Expand All @@ -1146,7 +1152,7 @@ void AttachedProbe::attach_uprobe(bool safe_mode)
eventname().c_str(),
probe_.path.c_str(),
offset_,
probe_.pid,
pid == 0 ? -1 : pid,
0);

if (perf_event_fd < 0)
Expand Down
7 changes: 4 additions & 3 deletions src/attached_probe.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class AttachedProbe
BpfProgram &&prog,
int pid,
BPFfeature &feature,
BTF &btf);
BTF &btf,
bool safe_mode = false);
~AttachedProbe();
AttachedProbe(const AttachedProbe &) = delete;
AttachedProbe &operator=(const AttachedProbe &) = delete;
Expand All @@ -47,9 +48,9 @@ class AttachedProbe
bool resolve_offset_uprobe(bool safe_mode);
void load_prog(BPFfeature &feature);
void attach_multi_kprobe(void);
void attach_multi_uprobe(void);
void attach_multi_uprobe(int pid);
void attach_kprobe(bool safe_mode);
void attach_uprobe(bool safe_mode);
void attach_uprobe(int pid, bool safe_mode);

// Note: the following usdt attachment functions will only activate a
// semaphore if one exists.
Expand Down
7 changes: 7 additions & 0 deletions src/bpftrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,13 @@ std::vector<std::unique_ptr<AttachedProbe>> BPFtrace::attach_probe(

return ret;
}
else if (probe.type == ProbeType::uprobe ||
probe.type == ProbeType::uretprobe)
{
ret.emplace_back(std::make_unique<AttachedProbe>(
probe, std::move(*program), pid, *feature_, *btf_, safe_mode_));
return ret;
}
else if (probe.type == ProbeType::watchpoint ||
probe.type == ProbeType::asyncwatchpoint)
{
Expand Down
18 changes: 18 additions & 0 deletions tests/runtime/scripts/uprobe_pid_check.bt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
BEGIN {
@count = 0;
}

uprobe:./testprogs/uprobe_fork_loop:uprobeFunction1 {
if (pid == $1) {
// make sure the pid is always what we expect
if (@count == 10) {
print("hello");
exit();
} else {
@count++;
}
} else {
// pid should always be the first positional param
exit();
}
}
6 changes: 6 additions & 0 deletions tests/runtime/uprobe
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,9 @@ NAME uprobes - probe function in non-executable library
PROG uprobe:./testlibs/libsimple.so:fun {}
EXPECT Attaching 1 probe...
TIMEOUT 5

NAME uprobes - attach to single process with pid arg
RUN {{BPFTRACE}} runtime/scripts/uprobe_pid_check.bt -p {{BEFORE_PID}} {{BEFORE_PID}}
EXPECT hello
TIMEOUT 5
BEFORE ./testprogs/uprobe_fork_loop
36 changes: 36 additions & 0 deletions tests/testprogs/uprobe_fork_loop.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <unistd.h>

int GLOBAL_A = 0x55555555;
int GLOBAL_B = 0x88888888;
int GLOBAL_C = 0x33333333;
char GLOBAL_D = 8;

int uprobeFunction1(int *n, char c __attribute__((unused)))
{
return *n;
}

void spin()
{
while (1)
{
int n = 13;
char c = 'x';
uprobeFunction1(&n, c);

Check warning

Code scanning / CodeQL

Expression has no effect Warning

This expression has no effect (because
uprobeFunction1
has no external side effects).
usleep(500);
}
}

int main(int argc __attribute__((unused)), char **argv __attribute__((unused)))
{
if (fork() == 0)
{
spin();
}
else
{
spin();
}

return 0;
}

0 comments on commit c156b30

Please sign in to comment.