aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/trace.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2016-08-08 16:02:09 +0200
committerDmitry Vyukov <dvyukov@google.com>2016-08-22 17:40:10 +0000
commit747a158ef314bb458b90da95f3e3d67aa4140622 (patch)
tree8382246510bcf50c4147faddb46f7f2e1e4e5751 /src/runtime/trace.go
parent7c5f33b173d7bde6b3ae33bab940b76b4c991556 (diff)
downloadgo-747a158ef314bb458b90da95f3e3d67aa4140622.tar.gz
go-747a158ef314bb458b90da95f3e3d67aa4140622.zip
runtime: speed up StartTrace with lots of blocked goroutines
In StartTrace we emit EvGoCreate for all existing goroutines. This includes stack unwind to obtain current stack. Real Go programs can contain hundreds of thousands of blocked goroutines. For such programs StartTrace can take up to a second (few ms per goroutine). Obtain current stack ID once and use it for all EvGoCreate events. This speeds up StartTrace with 10K blocked goroutines from 20ms to 4 ms (win for StartTrace called from net/http/pprof hander will be bigger as stack is deeper). Change-Id: I9e5ff9468331a840f8fdcdd56c5018c2cfde61fc Reviewed-on: https://go-review.googlesource.com/25573 Run-TryBot: Dmitry Vyukov <dvyukov@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Diffstat (limited to 'src/runtime/trace.go')
-rw-r--r--src/runtime/trace.go61
1 files changed, 38 insertions, 23 deletions
diff --git a/src/runtime/trace.go b/src/runtime/trace.go
index 092f941f0c..436f31dc2e 100644
--- a/src/runtime/trace.go
+++ b/src/runtime/trace.go
@@ -184,10 +184,21 @@ func StartTrace() error {
// trace.enabled is set afterwards once we have emitted all preliminary events.
_g_ := getg()
_g_.m.startingtrace = true
+
+ // Obtain current stack ID to use in all traceEvGoCreate events below.
+ mp := acquirem()
+ stkBuf := make([]uintptr, traceStackSize)
+ stackID := traceStackID(mp, stkBuf, 2)
+ releasem(mp)
+
for _, gp := range allgs {
status := readgstatus(gp)
if status != _Gdead {
- traceGoCreate(gp, gp.startpc) // also resets gp.traceseq/tracelastp
+ gp.traceseq = 0
+ gp.tracelastp = getg().m.p
+ // +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum.
+ id := trace.stackTab.put([]uintptr{gp.startpc + sys.PCQuantum})
+ traceEvent(traceEvGoCreate, -1, uint64(gp.goid), uint64(id), stackID)
}
if status == _Gwaiting {
// traceEvGoWaiting is implied to have seq=1.
@@ -513,28 +524,7 @@ func traceEvent(ev byte, skip int, args ...uint64) {
if skip == 0 {
buf.varint(0)
} else if skip > 0 {
- _g_ := getg()
- gp := mp.curg
- var nstk int
- if gp == _g_ {
- nstk = callers(skip, buf.stk[:])
- } else if gp != nil {
- gp = mp.curg
- // This may happen when tracing a system call,
- // so we must lock the stack.
- if gcTryLockStackBarriers(gp) {
- nstk = gcallers(gp, skip, buf.stk[:])
- gcUnlockStackBarriers(gp)
- }
- }
- if nstk > 0 {
- nstk-- // skip runtime.goexit
- }
- if nstk > 0 && gp.goid == 1 {
- nstk-- // skip runtime.main
- }
- id := trace.stackTab.put(buf.stk[:nstk])
- buf.varint(uint64(id))
+ buf.varint(traceStackID(mp, buf.stk[:], skip))
}
evSize := buf.pos - startPos
if evSize > maxSize {
@@ -547,6 +537,31 @@ func traceEvent(ev byte, skip int, args ...uint64) {
traceReleaseBuffer(pid)
}
+func traceStackID(mp *m, buf []uintptr, skip int) uint64 {
+ _g_ := getg()
+ gp := mp.curg
+ var nstk int
+ if gp == _g_ {
+ nstk = callers(skip+1, buf[:])
+ } else if gp != nil {
+ gp = mp.curg
+ // This may happen when tracing a system call,
+ // so we must lock the stack.
+ if gcTryLockStackBarriers(gp) {
+ nstk = gcallers(gp, skip, buf[:])
+ gcUnlockStackBarriers(gp)
+ }
+ }
+ if nstk > 0 {
+ nstk-- // skip runtime.goexit
+ }
+ if nstk > 0 && gp.goid == 1 {
+ nstk-- // skip runtime.main
+ }
+ id := trace.stackTab.put(buf[:nstk])
+ return uint64(id)
+}
+
// traceAcquireBuffer returns trace buffer to use and, if necessary, locks it.
func traceAcquireBuffer() (mp *m, pid int32, bufp *traceBufPtr) {
mp = acquirem()