proc,service: expose goroutine pprof labels in api
Labels can help in identifying a particular goroutine during debugging. Fixes #1763
This commit is contained in:
parent
1ac990c705
commit
ca3fe88899
23
_fixtures/goroutineLabels.go
Normal file
23
_fixtures/goroutineLabels.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"runtime"
|
||||||
|
"runtime/pprof"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
labels := pprof.Labels("k1", "v1", "k2", "v2")
|
||||||
|
runtime.Breakpoint()
|
||||||
|
pprof.Do(ctx, labels, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dummy int
|
||||||
|
|
||||||
|
func f(ctx context.Context) {
|
||||||
|
a := dummy
|
||||||
|
runtime.Breakpoint()
|
||||||
|
dummy++
|
||||||
|
dummy = a
|
||||||
|
}
|
@ -2305,6 +2305,27 @@ func TestIssue561(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGoroutineLables(t *testing.T) {
|
||||||
|
withTestProcess("goroutineLabels", t, func(p proc.Process, fixture protest.Fixture) {
|
||||||
|
assertNoError(proc.Continue(p), t, "Continue()")
|
||||||
|
g, err := proc.GetG(p.CurrentThread())
|
||||||
|
assertNoError(err, t, "GetG()")
|
||||||
|
if len(g.Labels) != 0 {
|
||||||
|
t.Fatalf("No labels expected")
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNoError(proc.Continue(p), t, "Continue()")
|
||||||
|
g, err = proc.GetG(p.CurrentThread())
|
||||||
|
assertNoError(err, t, "GetG()")
|
||||||
|
if v := g.Labels["k1"]; v != "v1" {
|
||||||
|
t.Errorf("Unexpected label value k1=%v", v)
|
||||||
|
}
|
||||||
|
if v := g.Labels["k2"]; v != "v2" {
|
||||||
|
t.Errorf("Unexpected label value k2=%v", v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestStepOut(t *testing.T) {
|
func TestStepOut(t *testing.T) {
|
||||||
testseq2(t, "testnextprog", "main.helloworld", []seqTest{{contContinue, 13}, {contStepout, 35}})
|
testseq2(t, "testnextprog", "main.helloworld", []seqTest{{contContinue, 13}, {contStepout, 35}})
|
||||||
}
|
}
|
||||||
|
@ -210,6 +210,8 @@ type G struct {
|
|||||||
variable *Variable
|
variable *Variable
|
||||||
|
|
||||||
Unreadable error // could not read the G struct
|
Unreadable error // could not read the G struct
|
||||||
|
|
||||||
|
Labels map[string]string // G's pprof labels
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defer returns the top-most defer of the goroutine.
|
// Defer returns the top-most defer of the goroutine.
|
||||||
@ -558,6 +560,27 @@ func (v *Variable) parseG() (*G, error) {
|
|||||||
|
|
||||||
status, _ := constant.Int64Val(v.fieldVariable("atomicstatus").Value)
|
status, _ := constant.Int64Val(v.fieldVariable("atomicstatus").Value)
|
||||||
f, l, fn := v.bi.PCToLine(uint64(pc))
|
f, l, fn := v.bi.PCToLine(uint64(pc))
|
||||||
|
|
||||||
|
var labels map[string]string
|
||||||
|
if labelsVar := v.loadFieldNamed("labels"); labelsVar != nil && len(labelsVar.Children) == 1 {
|
||||||
|
if address := labelsVar.Children[0]; address.Addr != 0 {
|
||||||
|
labelMapType, _ := v.bi.findType("runtime/pprof.labelMap")
|
||||||
|
if labelMapType != nil {
|
||||||
|
labelMap := newVariable("", address.Addr, labelMapType, v.bi, v.mem)
|
||||||
|
labelMap.loadValue(loadFullValue)
|
||||||
|
labels = map[string]string{}
|
||||||
|
// iterate through map as it is done in other places
|
||||||
|
for i := range labelMap.Children {
|
||||||
|
if i % 2 == 0 {
|
||||||
|
k := labelMap.Children[i]
|
||||||
|
v := labelMap.Children[i+1]
|
||||||
|
labels[constant.StringVal(k.Value)] = constant.StringVal(v.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g := &G{
|
g := &G{
|
||||||
ID: int(id),
|
ID: int(id),
|
||||||
GoPC: uint64(gopc),
|
GoPC: uint64(gopc),
|
||||||
@ -573,6 +596,7 @@ func (v *Variable) parseG() (*G, error) {
|
|||||||
stkbarPos: int(stkbarPos),
|
stkbarPos: int(stkbarPos),
|
||||||
stackhi: stackhi,
|
stackhi: stackhi,
|
||||||
stacklo: stacklo,
|
stacklo: stacklo,
|
||||||
|
Labels: labels,
|
||||||
}
|
}
|
||||||
return g, nil
|
return g, nil
|
||||||
}
|
}
|
||||||
|
@ -263,6 +263,7 @@ func ConvertGoroutine(g *proc.G) *Goroutine {
|
|||||||
GoStatementLoc: ConvertLocation(g.Go()),
|
GoStatementLoc: ConvertLocation(g.Go()),
|
||||||
StartLoc: ConvertLocation(g.StartLoc()),
|
StartLoc: ConvertLocation(g.StartLoc()),
|
||||||
ThreadID: tid,
|
ThreadID: tid,
|
||||||
|
Labels: g.Labels,
|
||||||
}
|
}
|
||||||
if g.Unreadable != nil {
|
if g.Unreadable != nil {
|
||||||
r.Unreadable = g.Unreadable.Error()
|
r.Unreadable = g.Unreadable.Error()
|
||||||
|
@ -308,6 +308,8 @@ type Goroutine struct {
|
|||||||
// ID of the associated thread for running goroutines
|
// ID of the associated thread for running goroutines
|
||||||
ThreadID int `json:"threadID"`
|
ThreadID int `json:"threadID"`
|
||||||
Unreadable string `json:"unreadable"`
|
Unreadable string `json:"unreadable"`
|
||||||
|
// Goroutine's pprof labels
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DebuggerCommand is a command which changes the debugger's execution state.
|
// DebuggerCommand is a command which changes the debugger's execution state.
|
||||||
|
Loading…
Reference in New Issue
Block a user