Skip to content
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

runtime, runtime/trace: EvGoCreate for empty function may lack stack #55116

Open
dominikh opened this issue Sep 17, 2022 · 2 comments
Open

runtime, runtime/trace: EvGoCreate for empty function may lack stack #55116

dominikh opened this issue Sep 17, 2022 · 2 comments
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided.
Milestone

Comments

@dominikh
Copy link
Member

dominikh commented Sep 17, 2022

One of EvGoCreate's arguments is a stack for the called function. When the called function is empty, and it is the last function in the executable (I think), the stack's only frame has a PC of zero and no information. This is caused by this series of events:

  • runtime.traceGoCreate calls (*runtime.traceStackTable).put with the called function's PC and sys.PCQuantum added to it.
  • (*traceStackTable).dump, which is responsible for writing resolved stack frames to the trace, uses runtime.CallersFrames to turn the PCs into frames
  • (*runtime.Frames).Next calls runtime.findfunc, which calls runtime.findmoduledatap. findmoduledatap contains a loop, searching for the correct module, based on the module's minpc and maxpc: if datap.minpc <= pc && pc < datap.maxpc { return datap } - In our case, however, pc is equal to datap.maxpc.

Looking at an example binary, the last function in it is

000000000046ca60 <main.baz3>:
func baz3() {}
  46ca60:       c3                      ret    
  46ca61:       cc                      int3   
  46ca62:       cc                      int3   
  46ca63:       cc                      int3   
  46ca64:       cc                      int3   
  46ca65:       cc                      int3   
  46ca66:       cc                      int3   
  46ca67:       cc                      int3   
  46ca68:       cc                      int3   
  46ca69:       cc                      int3   
  46ca6a:       cc                      int3   
  46ca6b:       cc                      int3   
  46ca6c:       cc                      int3   
  46ca6d:       cc                      int3   
  46ca6e:       cc                      int3   
  46ca6f:       cc                      int3   

On go baz3, the EvGoCreate event contains a stack with the PC 46ca60 + 1 (I'm on amd64, where sys.PCQuantum is 1). The last module that findmoduledatap looks at has a maxpc of 46ca61, which equals our PC. findmoduledatap returns nil because it couldn't find a matching module, and that bubbles all the way up to (*runtime.Frames).Next returning an empty runtime.Frame.

@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Sep 17, 2022
@cherrymui
Copy link
Member

cherrymui commented Sep 19, 2022

Could you show the source code of an example? Is it that you have a go statement with a function with empty body?

Perhaps we can have maxpc include at least 1 byte padding at the end.

@cherrymui cherrymui added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. labels Sep 19, 2022
@cherrymui cherrymui added this to the Unplanned milestone Sep 19, 2022
@dominikh
Copy link
Member Author

dominikh commented Sep 19, 2022

A runnable example:

package main

import (
	"os"
	"runtime/trace"
	"time"
)

func main() {
	trace.Start(os.Stdout)
	defer trace.Stop()

	go foo1()
	go foo2()

	time.Sleep(500 * time.Millisecond)
}

func foo1() {}
func foo2() {}

The creation of foo1 has a proper stack trace, foo2 does not. You can for example see this in go tool trace:
image
image

Is it that you have a go statement with a function with empty body?

Yes, but the bug only happens if the empty function is the last function in the package.

$ nm -n foo
...
000000000046abc0 T main.main
000000000046ac60 T main.foo1
000000000046ac80 T main.foo2
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided.
Projects
None yet
Development

No branches or pull requests

3 participants