-
Notifications
You must be signed in to change notification settings - Fork 110
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
Add Go version detection #485
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #485 +/- ##
===========================================
- Coverage 77.23% 40.58% -36.65%
===========================================
Files 67 65 -2
Lines 5311 5196 -115
===========================================
- Hits 4102 2109 -1993
- Misses 988 2952 +1964
+ Partials 221 135 -86
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
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.
Thank you @myhro!!
I think this PR will work in the case that the executable does not have DWARF debug info embedded, and then we look for the offsets in our prefetched database.
I think it would be simpler if the Go version fetching and checking is performed directly in the InspectOffsets
public method, in the offsets.go
file, right after the execElf == nil
check.
However, when the executable has debug information, it follows a different path that
pkg/internal/goexec/gofile.go
Outdated
@@ -20,6 +40,12 @@ func findLibraryVersions(elfFile *elf.File) (map[string]string, error) { | |||
|
|||
goVersion = strings.ReplaceAll(goVersion, "go", "") | |||
log().Debug("Go version detected", "version", goVersion) | |||
|
|||
if !supportedGoVersion(goVersion) { | |||
log().Debug("Unsupported Go version detected. Skipping instrumentation", "version", goVersion) |
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 line is not required, as the error already describes the message and will be logged later.
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.
No problem. I'll drop it.
pkg/internal/goexec/structmembers.go
Outdated
@@ -115,8 +115,12 @@ func structMemberOffsets(elfFile *elf.File) (FieldOffsets, error) { | |||
var expected map[string]struct{} | |||
dwarfData, err := elfFile.DWARF() | |||
if err == nil { | |||
offs, expected = structMemberOffsetsFromDwarf(dwarfData) | |||
_, _, err := getGoDetails(elfFile) |
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.
Sorry, why is this needed?
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 believe we need this because the library versions are only checked if we can't find the symbols, in structMemberPreFetchedOffsets
. If the executable has symbols and it's compiled with older Go version we won't check otherwise.
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 suggestion came from the discussion in the related PR:
Finally, I think we should also call the
getGoDetails()
function at this line here:beyla/pkg/internal/goexec/structmembers.go
Line 123 in 760d3ad
return offs, nil In case we find go symbols in the ELF we wouldn't bother checking for what's the Go version, we trust what the ELF says the offsets are. A simple call with only caring about the returned err should suffice. If we get an error there we can just propagate it upward to make the go instrumentation fail.
My understanding is that there's not much of a point in processing the binary further is this check fails. At the same time, as it's not 100% related to this PR, I can drop it if you prefer as well.
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.
LGTM! This looks good to me! Thanks for working on this @myhro!
I think we can make a follow-up integration test in another PR. I need to add a Docker image of older Go binary to our test repo to be able to use it. I'll tag you in a PR once I have that 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.
I think we can make a follow-up integration test in another PR. I need to add a Docker image of older Go binary to our test repo to be able to use it. I'll tag you in a PR once I have that done.
Awesome! That's really great. I'll try to understand the integration tests setup in the meantime.
I think it would be simpler if the Go version fetching and checking is performed directly in the
InspectOffsets
public method, in theoffsets.go
file, right after theexecElf == nil
check.
You mean calling getGoDetails()
here?
beyla/pkg/internal/goexec/offsets.go
Lines 25 to 28 in 289dec9
func InspectOffsets(execElf *exec.FileInfo, funcs []string) (*Offsets, error) { | |
if execElf == nil { | |
return nil, fmt.Errorf("executable not found") | |
} |
I think it should work and maybe the implementation could turn out to be even cleaner, but I'm not sure if we would avoid calling getGoDetails()
again down the execution path. What do you think, @grcevski ?
pkg/internal/goexec/gofile.go
Outdated
@@ -20,6 +40,12 @@ func findLibraryVersions(elfFile *elf.File) (map[string]string, error) { | |||
|
|||
goVersion = strings.ReplaceAll(goVersion, "go", "") | |||
log().Debug("Go version detected", "version", goVersion) | |||
|
|||
if !supportedGoVersion(goVersion) { | |||
log().Debug("Unsupported Go version detected. Skipping instrumentation", "version", goVersion) |
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.
No problem. I'll drop it.
pkg/internal/goexec/structmembers.go
Outdated
@@ -115,8 +115,12 @@ func structMemberOffsets(elfFile *elf.File) (FieldOffsets, error) { | |||
var expected map[string]struct{} | |||
dwarfData, err := elfFile.DWARF() | |||
if err == nil { | |||
offs, expected = structMemberOffsetsFromDwarf(dwarfData) | |||
_, _, err := getGoDetails(elfFile) |
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 suggestion came from the discussion in the related PR:
Finally, I think we should also call the
getGoDetails()
function at this line here:beyla/pkg/internal/goexec/structmembers.go
Line 123 in 760d3ad
return offs, nil In case we find go symbols in the ELF we wouldn't bother checking for what's the Go version, we trust what the ELF says the offsets are. A simple call with only caring about the returned err should suffice. If we get an error there we can just propagate it upward to make the go instrumentation fail.
My understanding is that there's not much of a point in processing the binary further is this check fails. At the same time, as it's not 100% related to this PR, I can drop it if you prefer as well.
270c18f
to
20bc64b
Compare
20bc64b
to
80071be
Compare
@grcevski thanks for preparing the integration tests. I've dropped the I'll ping you as soon as the CI checks turn green. Right now I'm trying to figure what's causing the error below:
|
Ah, I think then maybe the fix doesn't work as I expected it would. The test error says we couldn't find events in Prometheus based on the instrumentation, since we likely still instrumented as a Go application, but it's too old of a version. I will also look more tomorrow, we might be missing something... |
Yeah, I think there's a misunderstanding on how the Go version is discovered. I've recompiled the
The beyla/pkg/internal/goexec/gofile.go Line 16 in 54209a7
beyla/pkg/internal/goexec/structmembers.go Line 142 in 54209a7
beyla/pkg/internal/goexec/structmembers.go Line 133 in 54209a7
But it is actually never called, because on all three binaries it returns earlier, on line beyla/pkg/internal/goexec/structmembers.go Lines 120 to 123 in 54209a7
So I guess you were right that we need a check before
I'll also continue tomorrow, as it's getting late for me as well. Please let me know if I'm missing anything. These debugging sessions were made by running
|
I have some proposed changes that should make this work as expected now here: https://github.com/myhro/beyla/pull/1/files. It was a bit more complex as I expected :). Moving the check earlier worked, but since we detected the executable type as Go we still tried to instrument the program as a Go program and failed since it's not supported. This was better than before, since we now said in the Debug logs that the program is ignored, but we didn't fall back on using the generic instrumentation. I added support to remember the error and use that to activate the generic instrumentation for the Go program. With appropriate warning, so that customers see this in the logs even with default logging. |
I've just merged your PR, @grcevski. Thank you very much for going deep into the fix! I see that there are some linter complaints, which probably weren't caught because the GitHub Actions workflows weren't executed in the fork. I'll take a look into them and push the fixes as soon as the integration tests CI check turns green. |
3a5ca0e
to
b964977
Compare
Integrations tests are green now. I've fixed the indent-error-flow linter complaint and ignored the |
b964977
to
62a6663
Compare
As I don't have permissions to re-run the CI checks, I've amended the last commit to trick it into re-running them. Do you think we are good to go, @grcevski? Or do we need anything else? |
LGTM! Thank you @myhro for your contribution |
Followed the discussions in the original issue and also in the related PR.
@grcevski I think I've tackled all your suggestions, but please let me know if there's still something missing. I'm interested in working on the integration test you mentioned as well. Should it be done here or in a separate PR?
Closes #436.