We now call wg.Add(n) before we loop over n observers. We also run the
loop inside a goroutine and therefore more quickly handle all (pid,
offset) pairs.
gdb_test.go and testdata implement a go test to compare and verify that
we get correct results from the scanner.
testdata/simple.c is a small program that embeds Python and runs the
same code as in runforever.py. The Makefile compiles this using
python3.7. Adjustments to the flags might be needed in your environment
gdb_test.go contains only one test, TestSimpleGDB, that
1. compiles simple.c
2. runs it multiple times
3. calls gdb to extract the address of symbol _PyRuntime for each pid
4. runs our Scanner
5. compares the results from gdb and our scanner
By introducing a lowercase type alias 'logger' for *log.Logger we can
now embed 'logger' in Scanner and not export it:
% go doc Scanner
package symbolyze // import "."
type Scanner struct {
// Has unexported fields.
}
symbolyze.go has been simplified and cleaned up. It now also is documented,
f.e.:
% go doc Scanner
package symbolyze // import "."
type Scanner struct {
*log.Logger // Embedded logger
// Has unexported fields.
}
Scanner represents an engine for scanning for a specific symbol in all
ELF-files matching a certain pattern. The pattern is described in
fileapth.Match().
Once a Scanner is created with New(), it should be populated with Observer
functions using OnFound(). Optionally, the scanner can be put into debugging
mode by a call to DebugOn() prior to a call to Run().
A call to Scanner.Run() then starts the engine and it will scan all pids in
/proc. Whenever a match is found, all observers will be called with the
(pid, offset), concurrently.
func New(symbol, pathglob string) *Scanner
func (S *Scanner) DebugOn()
func (S *Scanner) OnFound(fun Observer)
func (S *Scanner) Run() error
symbolyze/ now contains a module that exposes a Finder type with a
simple API, like:
finder := symbolyze.New("_PyRuntime", "*python3*")
finder.Debug(true)
finder.OnFound(mapFD.Set)
finder.Run()
Instead of writing (pid, offset) directly to a eBPF-map, it implements
an observer-pattern and expects a callback.
TODOs/next steps:
- Write documentation
- Add tests
- Experiment and re-evaluate design
main.go:
- reading /proc
- iteration over entries in NNN/maps
- filter glob-search for "*python3*" in pathname
- find symbol and its offset in pathnanme
- calculate offset in memory
- add pid and offset to map
TODO: encapsulating this into a module
ebpf.go:
- added type MapFD int, changing all function on a FD to methods
This allows us to enrich the data type going forward
- added bpf_update_elem() from the manpage ebpf2.
.updateElement() is the verbatim wrapper to it.
- added .Add/.Change/.Set methods, which call .updateElement
with specific flags
TODO: re-implement ebpf.go with pure go, using direct syscalls.