diff options
author | David Finkel <david.finkel@gmail.com> | 2019-08-04 15:14:48 -0400 |
---|---|---|
committer | Hyang-Ah Hana Kim <hyangah@gmail.com> | 2020-04-22 16:01:25 +0000 |
commit | 38c2c12bc1b3da40e1b33cac9268b7df9fa49a7e (patch) | |
tree | d420fecda8b99294301f913db08fcf20777e6c21 /src/runtime/mprof.go | |
parent | 0329c915a03199d202581d0b33b092371fde08dc (diff) | |
download | go-38c2c12bc1b3da40e1b33cac9268b7df9fa49a7e.tar.gz go-38c2c12bc1b3da40e1b33cac9268b7df9fa49a7e.zip |
runtime/pprof: plumb labels for goroutine profiles
Goroutines are directly associated with labels. It's relatively easy to
plumb those through without creating goroutine-locals in the wild.
This is accomplished by splitting out most of the code from the public
`runtime.GoroutineProfile` into a new unexported
`runtime.goroutineProfileWithLabels`, which then has a thin wrapper
linked into the `runtime/pprof` package as
`runtime_goroutineProfileWithLabels`. (mirroring the way labels get
associated with the `g` for a goroutine in the first place)
Per-#6104, OS-thread creation profiles are a bit useless, as `M`s tend
to be created be created by a background goroutine. As such, I decided
not to add support for capturing the labels at `M`-creation-time, since
the stack-traces seem to always come out `nil` for my simple test
binaries.
This change currently provides labels for debug=0 and debug=1, as
debug=2 is currently entirely generated by the runtime package and I
don't see a clean way of getting the `labelMap` type handled properly
within the `runtime` package.
Update the comment added in cl/131275 to mention goroutine support for
labels.
Updates #23458
Change-Id: Ia4b558893d7d10156b77121cd9b70c4ccd9e1889
Reviewed-on: https://go-review.googlesource.com/c/go/+/189318
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Diffstat (limited to 'src/runtime/mprof.go')
-rw-r--r-- | src/runtime/mprof.go | 41 |
1 files changed, 32 insertions, 9 deletions
diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go index 2bd41b650f..128498d69b 100644 --- a/src/runtime/mprof.go +++ b/src/runtime/mprof.go @@ -711,13 +711,16 @@ func ThreadCreateProfile(p []StackRecord) (n int, ok bool) { return } -// GoroutineProfile returns n, the number of records in the active goroutine stack profile. -// If len(p) >= n, GoroutineProfile copies the profile into p and returns n, true. -// If len(p) < n, GoroutineProfile does not change p and returns n, false. -// -// Most clients should use the runtime/pprof package instead -// of calling GoroutineProfile directly. -func GoroutineProfile(p []StackRecord) (n int, ok bool) { +//go:linkname runtime_goroutineProfileWithLabels runtime/pprof.runtime_goroutineProfileWithLabels +func runtime_goroutineProfileWithLabels(p []StackRecord, labels []unsafe.Pointer) (n int, ok bool) { + return goroutineProfileWithLabels(p, labels) +} + +// labels may be nil. If labels is non-nil, it must have the same length as p. +func goroutineProfileWithLabels(p []StackRecord, labels []unsafe.Pointer) (n int, ok bool) { + if labels != nil && len(labels) != len(p) { + labels = nil + } gp := getg() isOK := func(gp1 *g) bool { @@ -737,7 +740,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) { if n <= len(p) { ok = true - r := p + r, lbl := p, labels // Save current goroutine. sp := getcallersp() @@ -747,6 +750,12 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) { }) r = r[1:] + // If we have a place to put our goroutine labelmap, insert it there. + if labels != nil { + lbl[0] = gp.labels + lbl = lbl[1:] + } + // Save other goroutines. for _, gp1 := range allgs { if isOK(gp1) { @@ -756,16 +765,30 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) { break } saveg(^uintptr(0), ^uintptr(0), gp1, &r[0]) + if labels != nil { + lbl[0] = gp1.labels + lbl = lbl[1:] + } r = r[1:] } } } startTheWorld() - return n, ok } +// GoroutineProfile returns n, the number of records in the active goroutine stack profile. +// If len(p) >= n, GoroutineProfile copies the profile into p and returns n, true. +// If len(p) < n, GoroutineProfile does not change p and returns n, false. +// +// Most clients should use the runtime/pprof package instead +// of calling GoroutineProfile directly. +func GoroutineProfile(p []StackRecord) (n int, ok bool) { + + return goroutineProfileWithLabels(p, nil) +} + func saveg(pc, sp uintptr, gp *g, r *StackRecord) { n := gentraceback(pc, sp, 0, gp, 0, &r.Stack0[0], len(r.Stack0), nil, nil, 0) if n < len(r.Stack0) { |