> psc uses eBPF iterators to read process and file descriptor information directly from kernel data structures. This bypasses the /proc filesystem entirely, providing visibility that cannot be subverted by userland rootkits or LD_PRELOAD tricks.
Thanks for including so many examples! Perhaps include one example output. Other than mention of the optional '--tree' parameter, it's unclear if the default result would be a list, table, JSON, etc.
This is neat but the examples comparing the tool against piping grep seem to counter the argument to me. A couple of pipes to grep seems much easier to remember and type, especially with all the quotes needed for psc. For scripts where you need exact output this looks great.
I've played with bpf iterators and wrote a post about them [1]. The benefit of iterating over tasks instead of scanning procfs is a pretty astounding performance difference:
> I ran benchmarks on current code in the datadog-agent which reads the relevant data from procfs as described at the beginning of this post. I then implemented benchmarks for capturing the same data with bpf. The performance results were a major improvement.
> On a linux system with around 250 Procs it took the procfs implemention 5.45 ms vs 75.6 us for bpf (bpf is ~72x faster). On a linux system with around 10,000 Procs it took the procfs implemention ~296us vs 3ms for bpf (bpf is ~100x faster).
And with eBPF iterators you can bail out early and move to next if you see a non-interesting item (or one that should be filtered out) instead of emitting textual data of all items and later grepping/filtering things out in post-processing.
I use early bailout a lot (in 0x.tools xcapture) when iterating through all threads in a system and determining which ones are “active” or interesting
I like this tool. I just replaced a multi-step script to find running processes with deleted files open (e.g., updated shared library or binary) that used to be as follows:
- grep /proc/*/maps for " (deleted)" (needs root)
- exclude irrelevancies like paths starting with "/memfd:" (I have lots of other similar exclusions) with grep -v
- extract the pid from the filename part of grep's output with sed
- for each pid, generate readable output from /proc/$pid/cmdline (which is NUL separated) with tr, xargs, bash printf
- show the pid, cmdline, file path
Yes, this is what needs-restarting does too.
With this tool, this pipe chain is now just:
doas psc -o "process.pid,process.cmdline,file.path" \
'file.path.endsWith(" (deleted)") && !file.path.startsWith("/memfd:") && !...' \
| sed 1d
Hey! thanks for publishing my tool, and thanks everybody for the great feedback here. Just started addressing some of your points.
Anyway, my need for the tool was mostly because of these few points:
- scripting can be much easier with psc, especially when you can output what you want
- ebpf iterators are so flexible: we can get anything that is defined in the task_struct that is not even exposed in the proc filesytem if we want. This alone makes the tool extremely powerful, with a reasonable amount of effort for just adding a new field
- I really like querying my system with a simple language. Sometimes I tend to forget about specific ss, lsof, or ps options. In this way, it's much easier for me to get what I need
- no traditional tooling has native container context. It can be extended to even retrieve data from the kubelet, for instance, but I'll think about it
Feel free to reach out if you have any particular need
15 comments
[ 0.19 ms ] story [ 47.4 ms ] threadIs there a trade off here?
ss -o state established '( dport = :ssh or sport = :ssh )'
> I ran benchmarks on current code in the datadog-agent which reads the relevant data from procfs as described at the beginning of this post. I then implemented benchmarks for capturing the same data with bpf. The performance results were a major improvement.
> On a linux system with around 250 Procs it took the procfs implemention 5.45 ms vs 75.6 us for bpf (bpf is ~72x faster). On a linux system with around 10,000 Procs it took the procfs implemention ~296us vs 3ms for bpf (bpf is ~100x faster).
[1] https://www.grant.pizza/blog/bpf-iter/
I use early bailout a lot (in 0x.tools xcapture) when iterating through all threads in a system and determining which ones are “active” or interesting
- grep /proc/*/maps for " (deleted)" (needs root)
- exclude irrelevancies like paths starting with "/memfd:" (I have lots of other similar exclusions) with grep -v
- extract the pid from the filename part of grep's output with sed
- for each pid, generate readable output from /proc/$pid/cmdline (which is NUL separated) with tr, xargs, bash printf
- show the pid, cmdline, file path
Yes, this is what needs-restarting does too.
With this tool, this pipe chain is now just:
Anyway, my need for the tool was mostly because of these few points:
- scripting can be much easier with psc, especially when you can output what you want
- ebpf iterators are so flexible: we can get anything that is defined in the task_struct that is not even exposed in the proc filesytem if we want. This alone makes the tool extremely powerful, with a reasonable amount of effort for just adding a new field
- I really like querying my system with a simple language. Sometimes I tend to forget about specific ss, lsof, or ps options. In this way, it's much easier for me to get what I need
- no traditional tooling has native container context. It can be extended to even retrieve data from the kubelet, for instance, but I'll think about it
Feel free to reach out if you have any particular need