-
Notifications
You must be signed in to change notification settings - Fork 304
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
interpreter: add Golang #408
base: main
Are you sure you want to change the base?
Conversation
5ad982b
to
9078329
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments added. But before just blindly starting mixing Rust, I'd like a larger discussion a comments from all maintainers on feasibility of this.
I understand using existing Rust code might be faster, and the Rust code is likely good and tested.
On the other hand this the down sides are:
- core maintainers should also know Rust (possibly true?)
- CGO is slower than regular Go calls. not sure of the overhead.
- there is room for non-trivial errors as the calls need to use unsafe Go, potentially in non-trivial ways
Additionally go symbolization could be done directly using Go standard runtime libraries, or by extending the existing elfgopclntab code we have intree.
Could you elaborate why embedded Rust was chosen instead of the two above mentioned approaches? Do these reasons justify the overhead that mixing Go and Rust brings both in maintanenance, complexity and debugging?
processmanager/manager.go
Outdated
@@ -267,6 +267,21 @@ func (pm *ProcessManager) ConvertTrace(trace *host.Trace) (newTrace *libpf.Trace | |||
} | |||
} | |||
|
|||
if pm.eim.IsGolang(frame.File) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fairly ugly way to plug this in. Makes me wonder if the integration should be done in some other way. Perhaps split the interpreter into unwinder / symbolization stages. Or have some other hooking mechanism to tag the frames that should get Go symbolized. Perhaps add a mechanism to specify the ebpf sent frame type on per-DSO or mapping range basis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to discuss alternatives. For the moment, I think, this is the least intrusive way. This current approach should also be easily adaptable if the interpreter interface differentiates at some point between unwinding and symbolization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify, I really don't like the special golang check here. As minimum, I'd completely remove the IsGolang
call above (And introduction if it in eim
). We can just directly call symbolizeFrame
here. And make sure the existing interpreters ignore native frame (should be the case).
Or do you see some practical reasons why this check should be here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your suggestion sounds reasonable to me. It just needs some logic on the eBPF side to report Go frames for the respective executables. And I do think introducing a dedicated Go unwinder in eBPF doesn't make sense but the native unwinder needs to be extended in some way to switch the frame type at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applied the suggestion with 7128306
Besides the main reasons you outlined (Rust code performant/safe/well tested), using Regardless, I'm not opposed to doing Go symbolization in Go, assuming someone implements it we could evaluate and transition. But we don't have that right now (and at least at Elastic, have no plans to work on it) and we do have |
If going this way, I'd rather then use
Also looking at Rust code a bit, it seems to IMHO the |
That is the general idea going forward. At the moment the scope is limited and focused on Go as from this ecosystem there is the highest demand for Symbols. |
interpreter/golang/golang.go
Outdated
} | ||
|
||
frameID := libpf.NewFrameID(libpf.NewFileID(uint64(frame.File), uint64(frame.File)), | ||
libpf.AddressOrLineno(symbolsSlice[0].line_number)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I switched the AddressOrLineno part of frameID with 5bf7fd7. Previously pc was used, but this generated too much different frame IDs for the same function/source file/source line combination.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does prevent using the data for PGO. Do you have statistics?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't collect statistics. Like everywhere else for the on CPU sampling approach, leave frames are different which causes a high number of variance and differences in frame IDs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some additional new comments to clean up a bit.
Also, how much the profiler executable size goes up with this now that it pulls static Rust stuff?
interpreter/golang/golang.go
Outdated
frameID := libpf.NewFrameID(libpf.NewFileID(uint64(frame.File), uint64(frame.File)), | ||
libpf.AddressOrLineno(symbolsSlice[0].line_number)) | ||
|
||
trace.AppendFrameID(libpf.GolangFrame, frameID) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this provide the mapping information similar as the non-symbolized native code is done?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point Go frames are treated as interpreter and no longer as native code. So I guess the answer here is no, and mapping information is not provided similar to other native code.
interpreter/golang/golang.go
Outdated
} | ||
|
||
frameID := libpf.NewFrameID(libpf.NewFileID(uint64(frame.File), uint64(frame.File)), | ||
libpf.AddressOrLineno(symbolsSlice[0].line_number)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does prevent using the data for PGO. Do you have statistics?
A little note that also #407 adds |
|
Add symbolization functionality for Go executables. Signed-off-by: Florian Lehner <[email protected]>
Signed-off-by: Florian Lehner <[email protected]>
Signed-off-by: Florian Lehner <[email protected]>
Signed-off-by: Florian Lehner <[email protected]>
Signed-off-by: Florian Lehner <[email protected]>
Signed-off-by: Florian Lehner <[email protected]>
Signed-off-by: Florian Lehner <[email protected]>
Co-authored-by: Timo Teräs <[email protected]>
Signed-off-by: Florian Lehner <[email protected]>
Signed-off-by: Florian Lehner <[email protected]>
Signed-off-by: Florian Lehner <[email protected]>
Signed-off-by: Florian Lehner <[email protected]>
3222c8d
to
9a26452
Compare
Force pushed to resolve merge conflict with |
8b5ea8d
to
77bd162
Compare
8be0993
to
9a26452
Compare
Add symbolization functionality for Go executables.