runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package runtime
|
|
|
|
|
2015-11-11 10:39:30 -07:00
|
|
|
import (
|
2015-12-11 18:16:48 -07:00
|
|
|
"runtime/internal/atomic"
|
2015-11-11 10:39:30 -07:00
|
|
|
"runtime/internal/sys"
|
|
|
|
"unsafe"
|
|
|
|
)
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
|
|
|
|
// The code in this file implements stack trace walking for all architectures.
|
|
|
|
// The most important fact about a given architecture is whether it uses a link register.
|
|
|
|
// On systems with link registers, the prologue for a non-leaf function stores the
|
|
|
|
// incoming value of LR at the bottom of the newly allocated stack frame.
|
|
|
|
// On systems without link registers, the architecture pushes a return PC during
|
|
|
|
// the call instruction, so the return PC ends up above the stack frame.
|
|
|
|
// In this file, the return PC is always called LR, no matter how it was found.
|
|
|
|
//
|
|
|
|
// To date, the opposite of a link register architecture is an x86 architecture.
|
|
|
|
// This code may need to change if some other kind of non-link-register
|
|
|
|
// architecture comes along.
|
|
|
|
//
|
|
|
|
// The other important fact is the size of a pointer: on 32-bit systems the LR
|
|
|
|
// takes up only 4 bytes on the stack, while on 64-bit systems it takes up 8 bytes.
|
|
|
|
// Typically this is ptrSize.
|
|
|
|
//
|
|
|
|
// As an exception, amd64p32 has ptrSize == 4 but the CALL instruction still
|
|
|
|
// stores an 8-byte return PC onto the stack. To accommodate this, we use regSize
|
|
|
|
// as the size of the architecture-pushed return PC.
|
|
|
|
//
|
2015-10-08 02:52:03 -06:00
|
|
|
// usesLR is defined below in terms of minFrameSize, which is defined in
|
|
|
|
// arch_$GOARCH.go. ptrSize and regSize are defined in stubs.go.
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
|
2015-11-11 10:39:30 -07:00
|
|
|
const usesLR = sys.MinFrameSize > 0
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
|
2018-03-14 16:21:37 -06:00
|
|
|
var skipPC uintptr
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
|
2014-09-29 22:21:36 -06:00
|
|
|
func tracebackinit() {
|
|
|
|
// Go variable initialization happens late during runtime startup.
|
|
|
|
// Instead of initializing the variables above in the declarations,
|
|
|
|
// schedinit calls this function so that the variables are
|
|
|
|
// initialized and available earlier in the startup sequence.
|
2017-03-07 19:14:12 -07:00
|
|
|
skipPC = funcPC(skipPleaseUseCallersFrames)
|
2014-09-29 22:21:36 -06:00
|
|
|
}
|
|
|
|
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
// Traceback over the deferred function calls.
|
|
|
|
// Report them like calls that have been invoked but not started executing yet.
|
|
|
|
func tracebackdefers(gp *g, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer) {
|
|
|
|
var frame stkframe
|
|
|
|
for d := gp._defer; d != nil; d = d.link {
|
|
|
|
fn := d.fn
|
|
|
|
if fn == nil {
|
|
|
|
// Defer of nil function. Args don't matter.
|
|
|
|
frame.pc = 0
|
2017-02-20 20:37:07 -07:00
|
|
|
frame.fn = funcInfo{}
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
frame.argp = 0
|
|
|
|
frame.arglen = 0
|
|
|
|
frame.argmap = nil
|
|
|
|
} else {
|
2016-02-29 16:01:00 -07:00
|
|
|
frame.pc = fn.fn
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
f := findfunc(frame.pc)
|
2017-02-20 20:37:07 -07:00
|
|
|
if !f.valid() {
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
print("runtime: unknown pc in defer ", hex(frame.pc), "\n")
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("unknown pc")
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
}
|
|
|
|
frame.fn = f
|
|
|
|
frame.argp = uintptr(deferArgs(d))
|
runtime: fix getArgInfo for deferred reflection calls
getArgInfo for reflect.makeFuncStub and reflect.methodValueCall is
necessarily special. These have dynamically determined argument maps
that are stored in their context (that is, their *funcval). These
functions are written to store this context at 0(SP) when called, and
getArgInfo retrieves it from there.
This technique works if getArgInfo is passed an active call frame for
one of these functions. However, getArgInfo is also used in
tracebackdefers, where the "call" is not a true call with an active
stack frame, but a deferred call. In this situation, getArgInfo
currently crashes because tracebackdefers passes a frame with sp set
to 0. However, the entire approach used by getArgInfo is flawed in
this situation because the wrapper has not actually executed, and
hence hasn't saved this metadata to any stack frame.
In the defer case, we know the *funcval from the _defer itself, so we
can fix this by teaching getArgInfo to use the *funcval context
directly when its available, and otherwise get it from the active call
frame.
While we're here, this commit simplifies getArgInfo a bit by making it
play more nicely with the type system. Rather than decoding the
*reflect.methodValue that is the wrapper's context as a *[2]uintptr,
just write out a copy of the reflect.methodValue type in the runtime.
Fixes #16331. Fixes #17471.
Change-Id: I81db4d985179b4a81c68c490cceeccbfc675456a
Reviewed-on: https://go-review.googlesource.com/31138
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2016-10-16 16:23:39 -06:00
|
|
|
frame.arglen, frame.argmap = getArgInfo(&frame, f, true, fn)
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
}
|
|
|
|
frame.continpc = frame.pc
|
|
|
|
if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
|
runtime: handle inlined calls in runtime.Callers
The `skip` argument passed to runtime.Caller and runtime.Callers should
be interpreted as the number of logical calls to skip (rather than the
number of physical stack frames to skip). This changes runtime.Callers
to skip inlined calls in addition to physical stack frames.
The result value of runtime.Callers is a slice of program counters
([]uintptr) representing physical stack frames. If the `skip` parameter
to runtime.Callers skips part-way into a physical frame, there is no
convenient way to encode that in the resulting slice. To avoid changing
the API in an incompatible way, our solution is to store the number of
skipped logical calls of the first frame in the _second_ uintptr
returned by runtime.Callers. Since this number is a small integer, we
encode it as a valid PC value into a small symbol called:
runtime.skipPleaseUseCallersFrames
For example, if f() calls g(), g() calls `runtime.Callers(2, pcs)`, and
g() is inlined into f, then the frame for f will be partially skipped,
resulting in the following slice:
pcs = []uintptr{pc_in_f, runtime.skipPleaseUseCallersFrames+1, ...}
We store the skip PC in pcs[1] instead of pcs[0] so that `pcs[i:]` will
truncate the captured stack trace rather than grow it for all i.
Updates #19348.
Change-Id: I1c56f89ac48c29e6f52a5d085567c6d77d499cf1
Reviewed-on: https://go-review.googlesource.com/37854
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-03-06 12:48:36 -07:00
|
|
|
const sizeofSkipFunction = 256
|
|
|
|
|
|
|
|
// This function is defined in asm.s to be sizeofSkipFunction bytes long.
|
|
|
|
func skipPleaseUseCallersFrames()
|
|
|
|
|
2016-03-01 16:21:55 -07:00
|
|
|
// Generic traceback. Handles runtime stack prints (pcbuf == nil),
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
// the runtime.Callers function (pcbuf != nil), as well as the garbage
|
|
|
|
// collector (callback != nil). A little clunky to merge these, but avoids
|
|
|
|
// duplicating the code and all its subtlety.
|
runtime: handle inlined calls in runtime.Callers
The `skip` argument passed to runtime.Caller and runtime.Callers should
be interpreted as the number of logical calls to skip (rather than the
number of physical stack frames to skip). This changes runtime.Callers
to skip inlined calls in addition to physical stack frames.
The result value of runtime.Callers is a slice of program counters
([]uintptr) representing physical stack frames. If the `skip` parameter
to runtime.Callers skips part-way into a physical frame, there is no
convenient way to encode that in the resulting slice. To avoid changing
the API in an incompatible way, our solution is to store the number of
skipped logical calls of the first frame in the _second_ uintptr
returned by runtime.Callers. Since this number is a small integer, we
encode it as a valid PC value into a small symbol called:
runtime.skipPleaseUseCallersFrames
For example, if f() calls g(), g() calls `runtime.Callers(2, pcs)`, and
g() is inlined into f, then the frame for f will be partially skipped,
resulting in the following slice:
pcs = []uintptr{pc_in_f, runtime.skipPleaseUseCallersFrames+1, ...}
We store the skip PC in pcs[1] instead of pcs[0] so that `pcs[i:]` will
truncate the captured stack trace rather than grow it for all i.
Updates #19348.
Change-Id: I1c56f89ac48c29e6f52a5d085567c6d77d499cf1
Reviewed-on: https://go-review.googlesource.com/37854
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-03-06 12:48:36 -07:00
|
|
|
//
|
|
|
|
// The skip argument is only valid with pcbuf != nil and counts the number
|
|
|
|
// of logical frames to skip rather than physical frames (with inlining, a
|
|
|
|
// PC in pcbuf can represent multiple calls). If a PC is partially skipped
|
|
|
|
// and max > 1, pcbuf[1] will be runtime.skipPleaseUseCallersFrames+N where
|
|
|
|
// N indicates the number of logical frames to skip in pcbuf[0].
|
2015-02-24 22:41:21 -07:00
|
|
|
func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max int, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer, flags uint) int {
|
runtime: handle inlined calls in runtime.Callers
The `skip` argument passed to runtime.Caller and runtime.Callers should
be interpreted as the number of logical calls to skip (rather than the
number of physical stack frames to skip). This changes runtime.Callers
to skip inlined calls in addition to physical stack frames.
The result value of runtime.Callers is a slice of program counters
([]uintptr) representing physical stack frames. If the `skip` parameter
to runtime.Callers skips part-way into a physical frame, there is no
convenient way to encode that in the resulting slice. To avoid changing
the API in an incompatible way, our solution is to store the number of
skipped logical calls of the first frame in the _second_ uintptr
returned by runtime.Callers. Since this number is a small integer, we
encode it as a valid PC value into a small symbol called:
runtime.skipPleaseUseCallersFrames
For example, if f() calls g(), g() calls `runtime.Callers(2, pcs)`, and
g() is inlined into f, then the frame for f will be partially skipped,
resulting in the following slice:
pcs = []uintptr{pc_in_f, runtime.skipPleaseUseCallersFrames+1, ...}
We store the skip PC in pcs[1] instead of pcs[0] so that `pcs[i:]` will
truncate the captured stack trace rather than grow it for all i.
Updates #19348.
Change-Id: I1c56f89ac48c29e6f52a5d085567c6d77d499cf1
Reviewed-on: https://go-review.googlesource.com/37854
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-03-06 12:48:36 -07:00
|
|
|
if skip > 0 && callback != nil {
|
|
|
|
throw("gentraceback callback cannot be used with non-zero skip")
|
|
|
|
}
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
g := getg()
|
runtime: avoid gentraceback of self on user goroutine stack
Gentraceback may grow the stack.
One of the gentraceback wrappers may grow the stack.
One of the gentraceback callback calls may grow the stack.
Various stack pointers are stored in various stack locations
as type uintptr during the execution of these calls.
If the stack does grow, these stack pointers will not be
updated and will start trying to decode stack memory that
is no longer valid.
It may be possible to change the type of the stack pointer
variables to be unsafe.Pointer, but that's pretty subtle and
may still have problems, even if we catch every last one.
An easier, more obviously correct fix is to require that
gentraceback of the currently running goroutine must run
on the g0 stack, not on the goroutine's own stack.
Not doing this causes faults when you set
StackFromSystem = 1
StackFaultOnFree = 1
The new check in gentraceback will catch future lapses.
The more general problem is calling getcallersp but then
calling a function that might relocate the stack, which would
invalidate the result of getcallersp. Add note to stubs.go
declaration of getcallersp explaining the problem, and
check all existing calls to getcallersp. Most needed fixes.
This affects Callers, Stack, and nearly all the runtime
profiling routines. It does not affect stack copying directly
nor garbage collection.
LGTM=khr
R=khr, bradfitz
CC=golang-codereviews, r
https://golang.org/cl/167060043
2014-11-05 21:01:48 -07:00
|
|
|
if g == gp && g == g.m.curg {
|
|
|
|
// The starting sp has been passed in as a uintptr, and the caller may
|
|
|
|
// have other uintptr-typed stack references as well.
|
|
|
|
// If during one of the calls that got us here or during one of the
|
|
|
|
// callbacks below the stack must be grown, all these uintptr references
|
|
|
|
// to the stack will not be updated, and gentraceback will continue
|
|
|
|
// to inspect the old stack memory, which may no longer be valid.
|
|
|
|
// Even if all the variables were updated correctly, it is not clear that
|
|
|
|
// we want to expose a traceback that begins on one stack and ends
|
|
|
|
// on another stack. That could confuse callers quite a bit.
|
|
|
|
// Instead, we require that gentraceback and any other function that
|
|
|
|
// accepts an sp for the current goroutine (typically obtained by
|
|
|
|
// calling getcallersp) must not run on that goroutine's stack but
|
|
|
|
// instead on the g0 stack.
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("gentraceback cannot trace user goroutine on its own stack")
|
runtime: avoid gentraceback of self on user goroutine stack
Gentraceback may grow the stack.
One of the gentraceback wrappers may grow the stack.
One of the gentraceback callback calls may grow the stack.
Various stack pointers are stored in various stack locations
as type uintptr during the execution of these calls.
If the stack does grow, these stack pointers will not be
updated and will start trying to decode stack memory that
is no longer valid.
It may be possible to change the type of the stack pointer
variables to be unsafe.Pointer, but that's pretty subtle and
may still have problems, even if we catch every last one.
An easier, more obviously correct fix is to require that
gentraceback of the currently running goroutine must run
on the g0 stack, not on the goroutine's own stack.
Not doing this causes faults when you set
StackFromSystem = 1
StackFaultOnFree = 1
The new check in gentraceback will catch future lapses.
The more general problem is calling getcallersp but then
calling a function that might relocate the stack, which would
invalidate the result of getcallersp. Add note to stubs.go
declaration of getcallersp explaining the problem, and
check all existing calls to getcallersp. Most needed fixes.
This affects Callers, Stack, and nearly all the runtime
profiling routines. It does not affect stack copying directly
nor garbage collection.
LGTM=khr
R=khr, bradfitz
CC=golang-codereviews, r
https://golang.org/cl/167060043
2014-11-05 21:01:48 -07:00
|
|
|
}
|
2015-10-30 09:03:02 -06:00
|
|
|
level, _, _ := gotraceback()
|
runtime: implement GC stack barriers
This commit implements stack barriers to minimize the amount of
stack re-scanning that must be done during mark termination.
Currently the GC scans stacks of active goroutines twice during every
GC cycle: once at the beginning during root discovery and once at the
end during mark termination. The second scan happens while the world
is stopped and guarantees that we've seen all of the roots (since
there are no write barriers on writes to local stack
variables). However, this means pause time is proportional to stack
size. In particularly recursive programs, this can drive pause time up
past our 10ms goal (e.g., it takes about 150ms to scan a 50MB heap).
Re-scanning the entire stack is rarely necessary, especially for large
stacks, because usually most of the frames on the stack were not
active between the first and second scans and hence any changes to
these frames (via non-escaping pointers passed down the stack) were
tracked by write barriers.
To efficiently track how far a stack has been unwound since the first
scan (and, hence, how much needs to be re-scanned), this commit
introduces stack barriers. During the first scan, at exponentially
spaced points in each stack, the scan overwrites return PCs with the
PC of the stack barrier function. When "returned" to, the stack
barrier function records how far the stack has unwound and jumps to
the original return PC for that point in the stack. Then the second
scan only needs to proceed as far as the lowest barrier that hasn't
been hit.
For deeply recursive programs, this substantially reduces mark
termination time (and hence pause time). For the goscheme example
linked in issue #10898, prior to this change, mark termination times
were typically between 100 and 500ms; with this change, mark
termination times are typically between 10 and 20ms. As a result of
the reduced stack scanning work, this reduces overall execution time
of the goscheme example by 20%.
Fixes #10898.
The effect of this on programs that are not deeply recursive is
minimal:
name old time/op new time/op delta
BinaryTree17 3.16s ± 2% 3.26s ± 1% +3.31% (p=0.000 n=19+19)
Fannkuch11 2.42s ± 1% 2.48s ± 1% +2.24% (p=0.000 n=17+19)
FmtFprintfEmpty 50.0ns ± 3% 49.8ns ± 1% ~ (p=0.534 n=20+19)
FmtFprintfString 173ns ± 0% 175ns ± 0% +1.49% (p=0.000 n=16+19)
FmtFprintfInt 170ns ± 1% 175ns ± 1% +2.97% (p=0.000 n=20+19)
FmtFprintfIntInt 288ns ± 0% 295ns ± 0% +2.73% (p=0.000 n=16+19)
FmtFprintfPrefixedInt 242ns ± 1% 252ns ± 1% +4.13% (p=0.000 n=18+18)
FmtFprintfFloat 324ns ± 0% 323ns ± 0% -0.36% (p=0.000 n=20+19)
FmtManyArgs 1.14µs ± 0% 1.12µs ± 1% -1.01% (p=0.000 n=18+19)
GobDecode 8.88ms ± 1% 8.87ms ± 0% ~ (p=0.480 n=19+18)
GobEncode 6.80ms ± 1% 6.85ms ± 0% +0.82% (p=0.000 n=20+18)
Gzip 363ms ± 1% 363ms ± 1% ~ (p=0.077 n=18+20)
Gunzip 90.6ms ± 0% 90.0ms ± 1% -0.71% (p=0.000 n=17+18)
HTTPClientServer 51.5µs ± 1% 50.8µs ± 1% -1.32% (p=0.000 n=18+18)
JSONEncode 17.0ms ± 0% 17.1ms ± 0% +0.40% (p=0.000 n=18+17)
JSONDecode 61.8ms ± 0% 63.8ms ± 1% +3.11% (p=0.000 n=18+17)
Mandelbrot200 3.84ms ± 0% 3.84ms ± 1% ~ (p=0.583 n=19+19)
GoParse 3.71ms ± 1% 3.72ms ± 1% ~ (p=0.159 n=18+19)
RegexpMatchEasy0_32 100ns ± 0% 100ns ± 1% -0.19% (p=0.033 n=17+19)
RegexpMatchEasy0_1K 342ns ± 1% 331ns ± 0% -3.41% (p=0.000 n=19+19)
RegexpMatchEasy1_32 82.5ns ± 0% 81.7ns ± 0% -0.98% (p=0.000 n=18+18)
RegexpMatchEasy1_1K 505ns ± 0% 494ns ± 1% -2.16% (p=0.000 n=18+18)
RegexpMatchMedium_32 137ns ± 1% 137ns ± 1% -0.24% (p=0.048 n=20+18)
RegexpMatchMedium_1K 41.6µs ± 0% 41.3µs ± 1% -0.57% (p=0.004 n=18+20)
RegexpMatchHard_32 2.11µs ± 0% 2.11µs ± 1% +0.20% (p=0.037 n=17+19)
RegexpMatchHard_1K 63.9µs ± 2% 63.3µs ± 0% -0.99% (p=0.000 n=20+17)
Revcomp 560ms ± 1% 522ms ± 0% -6.87% (p=0.000 n=18+16)
Template 75.0ms ± 0% 75.1ms ± 1% +0.18% (p=0.013 n=18+19)
TimeParse 358ns ± 1% 364ns ± 0% +1.74% (p=0.000 n=20+15)
TimeFormat 360ns ± 0% 372ns ± 0% +3.55% (p=0.000 n=20+18)
Change-Id: If8a9bfae6c128d15a4f405e02bcfa50129df82a2
Reviewed-on: https://go-review.googlesource.com/10314
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-05-20 14:30:49 -06:00
|
|
|
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
if pc0 == ^uintptr(0) && sp0 == ^uintptr(0) { // Signal to fetch saved values from gp.
|
runtime: assume precisestack, copystack, StackCopyAlways, ScanStackByFrames
Commit to stack copying for stack growth.
We're carrying around a surprising amount of cruft from older schemes.
I am confident that precise stack scans and stack copying are here to stay.
Delete fallback code for when precise stack info is disabled.
Delete fallback code for when copying stacks is disabled.
Delete fallback code for when StackCopyAlways is disabled.
Delete Stktop chain - there is only one stack segment now.
Delete M.moreargp, M.moreargsize, M.moreframesize, M.cret.
Delete G.writenbuf (unrelated, just dead).
Delete runtime.lessstack, runtime.oldstack.
Delete many amd64 morestack variants.
Delete initialization of morestack frame/arg sizes (shortens split prologue!).
Replace G's stackguard/stackbase/stack0/stacksize/
syscallstack/syscallguard/forkstackguard with simple stack
bounds (lo, hi).
Update liblink, runtime/cgo for adjustments to G.
LGTM=khr
R=khr, bradfitz
CC=golang-codereviews, iant, r
https://golang.org/cl/137410043
2014-09-09 11:39:57 -06:00
|
|
|
if gp.syscallsp != 0 {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
pc0 = gp.syscallpc
|
|
|
|
sp0 = gp.syscallsp
|
|
|
|
if usesLR {
|
|
|
|
lr0 = 0
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pc0 = gp.sched.pc
|
|
|
|
sp0 = gp.sched.sp
|
|
|
|
if usesLR {
|
|
|
|
lr0 = gp.sched.lr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nprint := 0
|
|
|
|
var frame stkframe
|
|
|
|
frame.pc = pc0
|
|
|
|
frame.sp = sp0
|
|
|
|
if usesLR {
|
|
|
|
frame.lr = lr0
|
|
|
|
}
|
|
|
|
waspanic := false
|
2016-04-27 15:18:29 -06:00
|
|
|
cgoCtxt := gp.cgoCtxt
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
printing := pcbuf == nil && callback == nil
|
|
|
|
_defer := gp._defer
|
2017-11-09 15:55:45 -07:00
|
|
|
elideWrapper := false
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
|
2016-02-29 16:01:00 -07:00
|
|
|
for _defer != nil && _defer.sp == _NoArgs {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
_defer = _defer.link
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the PC is zero, it's likely a nil function call.
|
|
|
|
// Start in the caller's frame.
|
|
|
|
if frame.pc == 0 {
|
|
|
|
if usesLR {
|
|
|
|
frame.pc = *(*uintptr)(unsafe.Pointer(frame.sp))
|
|
|
|
frame.lr = 0
|
|
|
|
} else {
|
2015-11-11 10:39:30 -07:00
|
|
|
frame.pc = uintptr(*(*sys.Uintreg)(unsafe.Pointer(frame.sp)))
|
|
|
|
frame.sp += sys.RegSize
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
f := findfunc(frame.pc)
|
2017-02-20 20:37:07 -07:00
|
|
|
if !f.valid() {
|
2018-01-22 12:53:36 -07:00
|
|
|
if callback != nil || printing {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
print("runtime: unknown pc ", hex(frame.pc), "\n")
|
2018-01-22 12:53:36 -07:00
|
|
|
tracebackHexdump(gp.stack, &frame, 0)
|
|
|
|
}
|
|
|
|
if callback != nil {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("unknown pc")
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
frame.fn = f
|
|
|
|
|
runtime: add pcvalue cache to improve stack scan speed
The cost of scanning large stacks is currently dominated by the time
spent looking up and decoding the pcvalue table. However, large stacks
are usually large not because they contain calls to many different
functions, but because they contain many calls to the same, small set
of recursive functions. Hence, walking large stacks tends to make the
same pcvalue queries many times.
Based on this observation, this commit adds a small, very simple, and
fast cache in front of pcvalue lookup. We thread this cache down from
operations that make many pcvalue calls, such as gentraceback, stack
scanning, and stack adjusting.
This simple cache works well because it has minimal overhead when it's
not effective. I also tried a hashed direct-map cache, CLOCK-based
replacement, round-robin replacement, and round-robin with lookups
disabled until there had been at least 16 probes, but none of these
approaches had obvious wins over the random replacement policy in this
commit.
This nearly doubles the overall performance of the deep stack test
program from issue #10898:
name old time/op new time/op delta
Issue10898 16.5s ±12% 9.2s ±12% -44.37% (p=0.008 n=5+5)
It's a very slight win on the garbage benchmark:
name old time/op new time/op delta
XBenchGarbage-12 4.92ms ± 1% 4.89ms ± 1% -0.75% (p=0.000 n=18+19)
It's a wash (but doesn't harm performance) on the go1 benchmarks,
which don't have particularly deep stacks:
name old time/op new time/op delta
BinaryTree17-12 3.11s ± 2% 3.20s ± 3% +2.83% (p=0.000 n=17+20)
Fannkuch11-12 2.51s ± 1% 2.51s ± 1% -0.22% (p=0.034 n=19+18)
FmtFprintfEmpty-12 50.8ns ± 3% 50.6ns ± 2% ~ (p=0.793 n=20+20)
FmtFprintfString-12 174ns ± 0% 174ns ± 1% +0.17% (p=0.048 n=15+20)
FmtFprintfInt-12 177ns ± 0% 165ns ± 1% -6.99% (p=0.000 n=17+19)
FmtFprintfIntInt-12 283ns ± 1% 284ns ± 0% +0.22% (p=0.000 n=18+15)
FmtFprintfPrefixedInt-12 243ns ± 1% 244ns ± 1% +0.40% (p=0.000 n=20+19)
FmtFprintfFloat-12 318ns ± 0% 319ns ± 0% +0.27% (p=0.001 n=19+20)
FmtManyArgs-12 1.12µs ± 0% 1.14µs ± 0% +1.74% (p=0.000 n=19+20)
GobDecode-12 8.69ms ± 0% 8.73ms ± 1% +0.46% (p=0.000 n=18+18)
GobEncode-12 6.64ms ± 1% 6.61ms ± 1% -0.46% (p=0.000 n=20+20)
Gzip-12 323ms ± 2% 319ms ± 1% -1.11% (p=0.000 n=20+20)
Gunzip-12 42.8ms ± 0% 42.9ms ± 0% ~ (p=0.158 n=18+20)
HTTPClientServer-12 63.3µs ± 1% 63.1µs ± 1% -0.35% (p=0.011 n=20+20)
JSONEncode-12 16.9ms ± 1% 17.3ms ± 1% +2.84% (p=0.000 n=19+20)
JSONDecode-12 59.7ms ± 0% 58.5ms ± 0% -2.05% (p=0.000 n=19+17)
Mandelbrot200-12 3.92ms ± 0% 3.91ms ± 0% -0.16% (p=0.003 n=19+19)
GoParse-12 3.79ms ± 2% 3.75ms ± 2% -0.91% (p=0.005 n=20+20)
RegexpMatchEasy0_32-12 102ns ± 1% 101ns ± 1% -0.80% (p=0.001 n=14+20)
RegexpMatchEasy0_1K-12 337ns ± 1% 346ns ± 1% +2.90% (p=0.000 n=20+19)
RegexpMatchEasy1_32-12 84.4ns ± 2% 84.3ns ± 2% ~ (p=0.743 n=20+20)
RegexpMatchEasy1_1K-12 502ns ± 1% 505ns ± 0% +0.64% (p=0.000 n=20+20)
RegexpMatchMedium_32-12 133ns ± 1% 132ns ± 1% -0.85% (p=0.000 n=20+19)
RegexpMatchMedium_1K-12 40.1µs ± 1% 39.8µs ± 1% -0.77% (p=0.000 n=18+18)
RegexpMatchHard_32-12 2.08µs ± 1% 2.07µs ± 1% -0.55% (p=0.001 n=18+19)
RegexpMatchHard_1K-12 62.4µs ± 1% 62.0µs ± 1% -0.74% (p=0.000 n=19+19)
Revcomp-12 545ms ± 2% 545ms ± 3% ~ (p=0.771 n=19+20)
Template-12 73.7ms ± 1% 72.0ms ± 0% -2.33% (p=0.000 n=20+18)
TimeParse-12 358ns ± 1% 351ns ± 1% -2.07% (p=0.000 n=20+20)
TimeFormat-12 369ns ± 1% 356ns ± 0% -3.53% (p=0.000 n=20+18)
[Geo mean] 63.5µs 63.2µs -0.41%
name old speed new speed delta
GobDecode-12 88.3MB/s ± 0% 87.9MB/s ± 0% -0.43% (p=0.000 n=18+17)
GobEncode-12 116MB/s ± 1% 116MB/s ± 1% +0.47% (p=0.000 n=20+20)
Gzip-12 60.2MB/s ± 2% 60.8MB/s ± 1% +1.13% (p=0.000 n=20+20)
Gunzip-12 453MB/s ± 0% 453MB/s ± 0% ~ (p=0.160 n=18+20)
JSONEncode-12 115MB/s ± 1% 112MB/s ± 1% -2.76% (p=0.000 n=19+20)
JSONDecode-12 32.5MB/s ± 0% 33.2MB/s ± 0% +2.09% (p=0.000 n=19+17)
GoParse-12 15.3MB/s ± 2% 15.4MB/s ± 2% +0.92% (p=0.004 n=20+20)
RegexpMatchEasy0_32-12 311MB/s ± 1% 314MB/s ± 1% +0.78% (p=0.000 n=15+19)
RegexpMatchEasy0_1K-12 3.04GB/s ± 1% 2.95GB/s ± 1% -2.90% (p=0.000 n=19+19)
RegexpMatchEasy1_32-12 379MB/s ± 2% 380MB/s ± 2% ~ (p=0.779 n=20+20)
RegexpMatchEasy1_1K-12 2.04GB/s ± 1% 2.02GB/s ± 0% -0.62% (p=0.000 n=20+20)
RegexpMatchMedium_32-12 7.46MB/s ± 1% 7.53MB/s ± 1% +0.86% (p=0.000 n=20+19)
RegexpMatchMedium_1K-12 25.5MB/s ± 1% 25.7MB/s ± 1% +0.78% (p=0.000 n=18+18)
RegexpMatchHard_32-12 15.4MB/s ± 1% 15.5MB/s ± 1% +0.62% (p=0.000 n=19+19)
RegexpMatchHard_1K-12 16.4MB/s ± 1% 16.5MB/s ± 1% +0.82% (p=0.000 n=20+19)
Revcomp-12 466MB/s ± 2% 466MB/s ± 3% ~ (p=0.765 n=19+20)
Template-12 26.3MB/s ± 1% 27.0MB/s ± 0% +2.38% (p=0.000 n=20+18)
[Geo mean] 97.8MB/s 98.0MB/s +0.23%
Change-Id: I281044ae0b24990ba46487cacbc1069493274bc4
Reviewed-on: https://go-review.googlesource.com/13614
Reviewed-by: Keith Randall <khr@golang.org>
2015-08-12 21:43:43 -06:00
|
|
|
var cache pcvalueCache
|
|
|
|
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
n := 0
|
|
|
|
for n < max {
|
|
|
|
// Typically:
|
|
|
|
// pc is the PC of the running function.
|
|
|
|
// sp is the stack pointer at that program counter.
|
|
|
|
// fp is the frame pointer (caller's stack pointer) at that program counter, or nil if unknown.
|
|
|
|
// stk is the stack containing sp.
|
|
|
|
// The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)sp.
|
|
|
|
f = frame.fn
|
2016-01-06 10:45:23 -07:00
|
|
|
if f.pcsp == 0 {
|
|
|
|
// No frame information, must be external function, like race support.
|
|
|
|
// See golang.org/issue/13568.
|
|
|
|
break
|
|
|
|
}
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
|
|
|
|
// Found an actual function.
|
|
|
|
// Derive frame pointer and link register.
|
|
|
|
if frame.fp == 0 {
|
2015-04-30 08:32:54 -06:00
|
|
|
// We want to jump over the systemstack switch. If we're running on the
|
|
|
|
// g0, this systemstack is at the top of the stack.
|
|
|
|
// if we're not on g0 or there's a no curg, then this is a regular call.
|
|
|
|
sp := frame.sp
|
2018-03-14 16:21:37 -06:00
|
|
|
if flags&_TraceJumpStack != 0 && f.funcID == funcID_systemstack && gp == g.m.g0 && gp.m.curg != nil {
|
2015-04-30 08:32:54 -06:00
|
|
|
sp = gp.m.curg.sched.sp
|
runtime: update SP when jumping stacks in traceback
When gentraceback starts on a system stack in sigprof, it is
configured to jump to the user stack when it reaches the end of the
system stack. Currently this updates the current frame's FP, but not
its SP. This is okay on non-LR machines (x86) because frame.sp is only
used to find defers, which the bottom-most frame of the user stack
will never have.
However, on LR machines, we use frame.sp to find the saved LR. We then
use to resolve the function of the next frame, which is used to
resolved the size of the next frame. Since we're not updating frame.sp
on a stack jump, we read the saved LR from the system stack instead of
the user stack and wind up resolving the wrong function and hence the
wrong frame size for the next frame.
This has had remarkably few ill effects (though the resulting profiles
must be wrong). We noticed it because of a bad interaction with stack
barriers. Specifically, once we get the next frame size wrong, we also
get the location of its LR wrong. If we happen to get a stack slot
that contains a stale stack barrier LR (for a stack barrier we already
hit) and hasn't been overwritten with something else as we re-grew the
stack, gentraceback will fail with a "found next stack barrier at ..."
error, pointing at the slot that it thinks is an LR, but isn't.
Fixes #15138.
Updates #15313 (might fix it).
Change-Id: I13cfa322b44c0c2f23ac2b3d03e12631e4a6406b
Reviewed-on: https://go-review.googlesource.com/23291
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2016-05-20 12:57:55 -06:00
|
|
|
frame.sp = sp
|
2016-04-27 15:18:29 -06:00
|
|
|
cgoCtxt = gp.m.curg.cgoCtxt
|
2015-04-30 08:32:54 -06:00
|
|
|
}
|
runtime: add pcvalue cache to improve stack scan speed
The cost of scanning large stacks is currently dominated by the time
spent looking up and decoding the pcvalue table. However, large stacks
are usually large not because they contain calls to many different
functions, but because they contain many calls to the same, small set
of recursive functions. Hence, walking large stacks tends to make the
same pcvalue queries many times.
Based on this observation, this commit adds a small, very simple, and
fast cache in front of pcvalue lookup. We thread this cache down from
operations that make many pcvalue calls, such as gentraceback, stack
scanning, and stack adjusting.
This simple cache works well because it has minimal overhead when it's
not effective. I also tried a hashed direct-map cache, CLOCK-based
replacement, round-robin replacement, and round-robin with lookups
disabled until there had been at least 16 probes, but none of these
approaches had obvious wins over the random replacement policy in this
commit.
This nearly doubles the overall performance of the deep stack test
program from issue #10898:
name old time/op new time/op delta
Issue10898 16.5s ±12% 9.2s ±12% -44.37% (p=0.008 n=5+5)
It's a very slight win on the garbage benchmark:
name old time/op new time/op delta
XBenchGarbage-12 4.92ms ± 1% 4.89ms ± 1% -0.75% (p=0.000 n=18+19)
It's a wash (but doesn't harm performance) on the go1 benchmarks,
which don't have particularly deep stacks:
name old time/op new time/op delta
BinaryTree17-12 3.11s ± 2% 3.20s ± 3% +2.83% (p=0.000 n=17+20)
Fannkuch11-12 2.51s ± 1% 2.51s ± 1% -0.22% (p=0.034 n=19+18)
FmtFprintfEmpty-12 50.8ns ± 3% 50.6ns ± 2% ~ (p=0.793 n=20+20)
FmtFprintfString-12 174ns ± 0% 174ns ± 1% +0.17% (p=0.048 n=15+20)
FmtFprintfInt-12 177ns ± 0% 165ns ± 1% -6.99% (p=0.000 n=17+19)
FmtFprintfIntInt-12 283ns ± 1% 284ns ± 0% +0.22% (p=0.000 n=18+15)
FmtFprintfPrefixedInt-12 243ns ± 1% 244ns ± 1% +0.40% (p=0.000 n=20+19)
FmtFprintfFloat-12 318ns ± 0% 319ns ± 0% +0.27% (p=0.001 n=19+20)
FmtManyArgs-12 1.12µs ± 0% 1.14µs ± 0% +1.74% (p=0.000 n=19+20)
GobDecode-12 8.69ms ± 0% 8.73ms ± 1% +0.46% (p=0.000 n=18+18)
GobEncode-12 6.64ms ± 1% 6.61ms ± 1% -0.46% (p=0.000 n=20+20)
Gzip-12 323ms ± 2% 319ms ± 1% -1.11% (p=0.000 n=20+20)
Gunzip-12 42.8ms ± 0% 42.9ms ± 0% ~ (p=0.158 n=18+20)
HTTPClientServer-12 63.3µs ± 1% 63.1µs ± 1% -0.35% (p=0.011 n=20+20)
JSONEncode-12 16.9ms ± 1% 17.3ms ± 1% +2.84% (p=0.000 n=19+20)
JSONDecode-12 59.7ms ± 0% 58.5ms ± 0% -2.05% (p=0.000 n=19+17)
Mandelbrot200-12 3.92ms ± 0% 3.91ms ± 0% -0.16% (p=0.003 n=19+19)
GoParse-12 3.79ms ± 2% 3.75ms ± 2% -0.91% (p=0.005 n=20+20)
RegexpMatchEasy0_32-12 102ns ± 1% 101ns ± 1% -0.80% (p=0.001 n=14+20)
RegexpMatchEasy0_1K-12 337ns ± 1% 346ns ± 1% +2.90% (p=0.000 n=20+19)
RegexpMatchEasy1_32-12 84.4ns ± 2% 84.3ns ± 2% ~ (p=0.743 n=20+20)
RegexpMatchEasy1_1K-12 502ns ± 1% 505ns ± 0% +0.64% (p=0.000 n=20+20)
RegexpMatchMedium_32-12 133ns ± 1% 132ns ± 1% -0.85% (p=0.000 n=20+19)
RegexpMatchMedium_1K-12 40.1µs ± 1% 39.8µs ± 1% -0.77% (p=0.000 n=18+18)
RegexpMatchHard_32-12 2.08µs ± 1% 2.07µs ± 1% -0.55% (p=0.001 n=18+19)
RegexpMatchHard_1K-12 62.4µs ± 1% 62.0µs ± 1% -0.74% (p=0.000 n=19+19)
Revcomp-12 545ms ± 2% 545ms ± 3% ~ (p=0.771 n=19+20)
Template-12 73.7ms ± 1% 72.0ms ± 0% -2.33% (p=0.000 n=20+18)
TimeParse-12 358ns ± 1% 351ns ± 1% -2.07% (p=0.000 n=20+20)
TimeFormat-12 369ns ± 1% 356ns ± 0% -3.53% (p=0.000 n=20+18)
[Geo mean] 63.5µs 63.2µs -0.41%
name old speed new speed delta
GobDecode-12 88.3MB/s ± 0% 87.9MB/s ± 0% -0.43% (p=0.000 n=18+17)
GobEncode-12 116MB/s ± 1% 116MB/s ± 1% +0.47% (p=0.000 n=20+20)
Gzip-12 60.2MB/s ± 2% 60.8MB/s ± 1% +1.13% (p=0.000 n=20+20)
Gunzip-12 453MB/s ± 0% 453MB/s ± 0% ~ (p=0.160 n=18+20)
JSONEncode-12 115MB/s ± 1% 112MB/s ± 1% -2.76% (p=0.000 n=19+20)
JSONDecode-12 32.5MB/s ± 0% 33.2MB/s ± 0% +2.09% (p=0.000 n=19+17)
GoParse-12 15.3MB/s ± 2% 15.4MB/s ± 2% +0.92% (p=0.004 n=20+20)
RegexpMatchEasy0_32-12 311MB/s ± 1% 314MB/s ± 1% +0.78% (p=0.000 n=15+19)
RegexpMatchEasy0_1K-12 3.04GB/s ± 1% 2.95GB/s ± 1% -2.90% (p=0.000 n=19+19)
RegexpMatchEasy1_32-12 379MB/s ± 2% 380MB/s ± 2% ~ (p=0.779 n=20+20)
RegexpMatchEasy1_1K-12 2.04GB/s ± 1% 2.02GB/s ± 0% -0.62% (p=0.000 n=20+20)
RegexpMatchMedium_32-12 7.46MB/s ± 1% 7.53MB/s ± 1% +0.86% (p=0.000 n=20+19)
RegexpMatchMedium_1K-12 25.5MB/s ± 1% 25.7MB/s ± 1% +0.78% (p=0.000 n=18+18)
RegexpMatchHard_32-12 15.4MB/s ± 1% 15.5MB/s ± 1% +0.62% (p=0.000 n=19+19)
RegexpMatchHard_1K-12 16.4MB/s ± 1% 16.5MB/s ± 1% +0.82% (p=0.000 n=20+19)
Revcomp-12 466MB/s ± 2% 466MB/s ± 3% ~ (p=0.765 n=19+20)
Template-12 26.3MB/s ± 1% 27.0MB/s ± 0% +2.38% (p=0.000 n=20+18)
[Geo mean] 97.8MB/s 98.0MB/s +0.23%
Change-Id: I281044ae0b24990ba46487cacbc1069493274bc4
Reviewed-on: https://go-review.googlesource.com/13614
Reviewed-by: Keith Randall <khr@golang.org>
2015-08-12 21:43:43 -06:00
|
|
|
frame.fp = sp + uintptr(funcspdelta(f, frame.pc, &cache))
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
if !usesLR {
|
|
|
|
// On x86, call instruction pushes return PC before entering new function.
|
2015-11-11 10:39:30 -07:00
|
|
|
frame.fp += sys.RegSize
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
}
|
2017-02-20 20:37:07 -07:00
|
|
|
var flr funcInfo
|
2018-01-30 14:01:33 -07:00
|
|
|
if topofstack(f, gp.m != nil && gp == gp.m.g0) {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
frame.lr = 0
|
2017-02-20 20:37:07 -07:00
|
|
|
flr = funcInfo{}
|
2018-03-14 16:21:37 -06:00
|
|
|
} else if usesLR && f.funcID == funcID_jmpdefer {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
// jmpdefer modifies SP/LR/PC non-atomically.
|
|
|
|
// If a profiling interrupt arrives during jmpdefer,
|
|
|
|
// the stack unwind may see a mismatched register set
|
|
|
|
// and get confused. Stop if we see PC within jmpdefer
|
|
|
|
// to avoid that confusion.
|
|
|
|
// See golang.org/issue/8153.
|
|
|
|
if callback != nil {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("traceback_arm: found jmpdefer when tracing with callback")
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
frame.lr = 0
|
|
|
|
} else {
|
runtime: implement GC stack barriers
This commit implements stack barriers to minimize the amount of
stack re-scanning that must be done during mark termination.
Currently the GC scans stacks of active goroutines twice during every
GC cycle: once at the beginning during root discovery and once at the
end during mark termination. The second scan happens while the world
is stopped and guarantees that we've seen all of the roots (since
there are no write barriers on writes to local stack
variables). However, this means pause time is proportional to stack
size. In particularly recursive programs, this can drive pause time up
past our 10ms goal (e.g., it takes about 150ms to scan a 50MB heap).
Re-scanning the entire stack is rarely necessary, especially for large
stacks, because usually most of the frames on the stack were not
active between the first and second scans and hence any changes to
these frames (via non-escaping pointers passed down the stack) were
tracked by write barriers.
To efficiently track how far a stack has been unwound since the first
scan (and, hence, how much needs to be re-scanned), this commit
introduces stack barriers. During the first scan, at exponentially
spaced points in each stack, the scan overwrites return PCs with the
PC of the stack barrier function. When "returned" to, the stack
barrier function records how far the stack has unwound and jumps to
the original return PC for that point in the stack. Then the second
scan only needs to proceed as far as the lowest barrier that hasn't
been hit.
For deeply recursive programs, this substantially reduces mark
termination time (and hence pause time). For the goscheme example
linked in issue #10898, prior to this change, mark termination times
were typically between 100 and 500ms; with this change, mark
termination times are typically between 10 and 20ms. As a result of
the reduced stack scanning work, this reduces overall execution time
of the goscheme example by 20%.
Fixes #10898.
The effect of this on programs that are not deeply recursive is
minimal:
name old time/op new time/op delta
BinaryTree17 3.16s ± 2% 3.26s ± 1% +3.31% (p=0.000 n=19+19)
Fannkuch11 2.42s ± 1% 2.48s ± 1% +2.24% (p=0.000 n=17+19)
FmtFprintfEmpty 50.0ns ± 3% 49.8ns ± 1% ~ (p=0.534 n=20+19)
FmtFprintfString 173ns ± 0% 175ns ± 0% +1.49% (p=0.000 n=16+19)
FmtFprintfInt 170ns ± 1% 175ns ± 1% +2.97% (p=0.000 n=20+19)
FmtFprintfIntInt 288ns ± 0% 295ns ± 0% +2.73% (p=0.000 n=16+19)
FmtFprintfPrefixedInt 242ns ± 1% 252ns ± 1% +4.13% (p=0.000 n=18+18)
FmtFprintfFloat 324ns ± 0% 323ns ± 0% -0.36% (p=0.000 n=20+19)
FmtManyArgs 1.14µs ± 0% 1.12µs ± 1% -1.01% (p=0.000 n=18+19)
GobDecode 8.88ms ± 1% 8.87ms ± 0% ~ (p=0.480 n=19+18)
GobEncode 6.80ms ± 1% 6.85ms ± 0% +0.82% (p=0.000 n=20+18)
Gzip 363ms ± 1% 363ms ± 1% ~ (p=0.077 n=18+20)
Gunzip 90.6ms ± 0% 90.0ms ± 1% -0.71% (p=0.000 n=17+18)
HTTPClientServer 51.5µs ± 1% 50.8µs ± 1% -1.32% (p=0.000 n=18+18)
JSONEncode 17.0ms ± 0% 17.1ms ± 0% +0.40% (p=0.000 n=18+17)
JSONDecode 61.8ms ± 0% 63.8ms ± 1% +3.11% (p=0.000 n=18+17)
Mandelbrot200 3.84ms ± 0% 3.84ms ± 1% ~ (p=0.583 n=19+19)
GoParse 3.71ms ± 1% 3.72ms ± 1% ~ (p=0.159 n=18+19)
RegexpMatchEasy0_32 100ns ± 0% 100ns ± 1% -0.19% (p=0.033 n=17+19)
RegexpMatchEasy0_1K 342ns ± 1% 331ns ± 0% -3.41% (p=0.000 n=19+19)
RegexpMatchEasy1_32 82.5ns ± 0% 81.7ns ± 0% -0.98% (p=0.000 n=18+18)
RegexpMatchEasy1_1K 505ns ± 0% 494ns ± 1% -2.16% (p=0.000 n=18+18)
RegexpMatchMedium_32 137ns ± 1% 137ns ± 1% -0.24% (p=0.048 n=20+18)
RegexpMatchMedium_1K 41.6µs ± 0% 41.3µs ± 1% -0.57% (p=0.004 n=18+20)
RegexpMatchHard_32 2.11µs ± 0% 2.11µs ± 1% +0.20% (p=0.037 n=17+19)
RegexpMatchHard_1K 63.9µs ± 2% 63.3µs ± 0% -0.99% (p=0.000 n=20+17)
Revcomp 560ms ± 1% 522ms ± 0% -6.87% (p=0.000 n=18+16)
Template 75.0ms ± 0% 75.1ms ± 1% +0.18% (p=0.013 n=18+19)
TimeParse 358ns ± 1% 364ns ± 0% +1.74% (p=0.000 n=20+15)
TimeFormat 360ns ± 0% 372ns ± 0% +3.55% (p=0.000 n=20+18)
Change-Id: If8a9bfae6c128d15a4f405e02bcfa50129df82a2
Reviewed-on: https://go-review.googlesource.com/10314
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-05-20 14:30:49 -06:00
|
|
|
var lrPtr uintptr
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
if usesLR {
|
|
|
|
if n == 0 && frame.sp < frame.fp || frame.lr == 0 {
|
runtime: implement GC stack barriers
This commit implements stack barriers to minimize the amount of
stack re-scanning that must be done during mark termination.
Currently the GC scans stacks of active goroutines twice during every
GC cycle: once at the beginning during root discovery and once at the
end during mark termination. The second scan happens while the world
is stopped and guarantees that we've seen all of the roots (since
there are no write barriers on writes to local stack
variables). However, this means pause time is proportional to stack
size. In particularly recursive programs, this can drive pause time up
past our 10ms goal (e.g., it takes about 150ms to scan a 50MB heap).
Re-scanning the entire stack is rarely necessary, especially for large
stacks, because usually most of the frames on the stack were not
active between the first and second scans and hence any changes to
these frames (via non-escaping pointers passed down the stack) were
tracked by write barriers.
To efficiently track how far a stack has been unwound since the first
scan (and, hence, how much needs to be re-scanned), this commit
introduces stack barriers. During the first scan, at exponentially
spaced points in each stack, the scan overwrites return PCs with the
PC of the stack barrier function. When "returned" to, the stack
barrier function records how far the stack has unwound and jumps to
the original return PC for that point in the stack. Then the second
scan only needs to proceed as far as the lowest barrier that hasn't
been hit.
For deeply recursive programs, this substantially reduces mark
termination time (and hence pause time). For the goscheme example
linked in issue #10898, prior to this change, mark termination times
were typically between 100 and 500ms; with this change, mark
termination times are typically between 10 and 20ms. As a result of
the reduced stack scanning work, this reduces overall execution time
of the goscheme example by 20%.
Fixes #10898.
The effect of this on programs that are not deeply recursive is
minimal:
name old time/op new time/op delta
BinaryTree17 3.16s ± 2% 3.26s ± 1% +3.31% (p=0.000 n=19+19)
Fannkuch11 2.42s ± 1% 2.48s ± 1% +2.24% (p=0.000 n=17+19)
FmtFprintfEmpty 50.0ns ± 3% 49.8ns ± 1% ~ (p=0.534 n=20+19)
FmtFprintfString 173ns ± 0% 175ns ± 0% +1.49% (p=0.000 n=16+19)
FmtFprintfInt 170ns ± 1% 175ns ± 1% +2.97% (p=0.000 n=20+19)
FmtFprintfIntInt 288ns ± 0% 295ns ± 0% +2.73% (p=0.000 n=16+19)
FmtFprintfPrefixedInt 242ns ± 1% 252ns ± 1% +4.13% (p=0.000 n=18+18)
FmtFprintfFloat 324ns ± 0% 323ns ± 0% -0.36% (p=0.000 n=20+19)
FmtManyArgs 1.14µs ± 0% 1.12µs ± 1% -1.01% (p=0.000 n=18+19)
GobDecode 8.88ms ± 1% 8.87ms ± 0% ~ (p=0.480 n=19+18)
GobEncode 6.80ms ± 1% 6.85ms ± 0% +0.82% (p=0.000 n=20+18)
Gzip 363ms ± 1% 363ms ± 1% ~ (p=0.077 n=18+20)
Gunzip 90.6ms ± 0% 90.0ms ± 1% -0.71% (p=0.000 n=17+18)
HTTPClientServer 51.5µs ± 1% 50.8µs ± 1% -1.32% (p=0.000 n=18+18)
JSONEncode 17.0ms ± 0% 17.1ms ± 0% +0.40% (p=0.000 n=18+17)
JSONDecode 61.8ms ± 0% 63.8ms ± 1% +3.11% (p=0.000 n=18+17)
Mandelbrot200 3.84ms ± 0% 3.84ms ± 1% ~ (p=0.583 n=19+19)
GoParse 3.71ms ± 1% 3.72ms ± 1% ~ (p=0.159 n=18+19)
RegexpMatchEasy0_32 100ns ± 0% 100ns ± 1% -0.19% (p=0.033 n=17+19)
RegexpMatchEasy0_1K 342ns ± 1% 331ns ± 0% -3.41% (p=0.000 n=19+19)
RegexpMatchEasy1_32 82.5ns ± 0% 81.7ns ± 0% -0.98% (p=0.000 n=18+18)
RegexpMatchEasy1_1K 505ns ± 0% 494ns ± 1% -2.16% (p=0.000 n=18+18)
RegexpMatchMedium_32 137ns ± 1% 137ns ± 1% -0.24% (p=0.048 n=20+18)
RegexpMatchMedium_1K 41.6µs ± 0% 41.3µs ± 1% -0.57% (p=0.004 n=18+20)
RegexpMatchHard_32 2.11µs ± 0% 2.11µs ± 1% +0.20% (p=0.037 n=17+19)
RegexpMatchHard_1K 63.9µs ± 2% 63.3µs ± 0% -0.99% (p=0.000 n=20+17)
Revcomp 560ms ± 1% 522ms ± 0% -6.87% (p=0.000 n=18+16)
Template 75.0ms ± 0% 75.1ms ± 1% +0.18% (p=0.013 n=18+19)
TimeParse 358ns ± 1% 364ns ± 0% +1.74% (p=0.000 n=20+15)
TimeFormat 360ns ± 0% 372ns ± 0% +3.55% (p=0.000 n=20+18)
Change-Id: If8a9bfae6c128d15a4f405e02bcfa50129df82a2
Reviewed-on: https://go-review.googlesource.com/10314
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-05-20 14:30:49 -06:00
|
|
|
lrPtr = frame.sp
|
|
|
|
frame.lr = *(*uintptr)(unsafe.Pointer(lrPtr))
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if frame.lr == 0 {
|
2015-11-11 10:39:30 -07:00
|
|
|
lrPtr = frame.fp - sys.RegSize
|
|
|
|
frame.lr = uintptr(*(*sys.Uintreg)(unsafe.Pointer(lrPtr)))
|
runtime: implement GC stack barriers
This commit implements stack barriers to minimize the amount of
stack re-scanning that must be done during mark termination.
Currently the GC scans stacks of active goroutines twice during every
GC cycle: once at the beginning during root discovery and once at the
end during mark termination. The second scan happens while the world
is stopped and guarantees that we've seen all of the roots (since
there are no write barriers on writes to local stack
variables). However, this means pause time is proportional to stack
size. In particularly recursive programs, this can drive pause time up
past our 10ms goal (e.g., it takes about 150ms to scan a 50MB heap).
Re-scanning the entire stack is rarely necessary, especially for large
stacks, because usually most of the frames on the stack were not
active between the first and second scans and hence any changes to
these frames (via non-escaping pointers passed down the stack) were
tracked by write barriers.
To efficiently track how far a stack has been unwound since the first
scan (and, hence, how much needs to be re-scanned), this commit
introduces stack barriers. During the first scan, at exponentially
spaced points in each stack, the scan overwrites return PCs with the
PC of the stack barrier function. When "returned" to, the stack
barrier function records how far the stack has unwound and jumps to
the original return PC for that point in the stack. Then the second
scan only needs to proceed as far as the lowest barrier that hasn't
been hit.
For deeply recursive programs, this substantially reduces mark
termination time (and hence pause time). For the goscheme example
linked in issue #10898, prior to this change, mark termination times
were typically between 100 and 500ms; with this change, mark
termination times are typically between 10 and 20ms. As a result of
the reduced stack scanning work, this reduces overall execution time
of the goscheme example by 20%.
Fixes #10898.
The effect of this on programs that are not deeply recursive is
minimal:
name old time/op new time/op delta
BinaryTree17 3.16s ± 2% 3.26s ± 1% +3.31% (p=0.000 n=19+19)
Fannkuch11 2.42s ± 1% 2.48s ± 1% +2.24% (p=0.000 n=17+19)
FmtFprintfEmpty 50.0ns ± 3% 49.8ns ± 1% ~ (p=0.534 n=20+19)
FmtFprintfString 173ns ± 0% 175ns ± 0% +1.49% (p=0.000 n=16+19)
FmtFprintfInt 170ns ± 1% 175ns ± 1% +2.97% (p=0.000 n=20+19)
FmtFprintfIntInt 288ns ± 0% 295ns ± 0% +2.73% (p=0.000 n=16+19)
FmtFprintfPrefixedInt 242ns ± 1% 252ns ± 1% +4.13% (p=0.000 n=18+18)
FmtFprintfFloat 324ns ± 0% 323ns ± 0% -0.36% (p=0.000 n=20+19)
FmtManyArgs 1.14µs ± 0% 1.12µs ± 1% -1.01% (p=0.000 n=18+19)
GobDecode 8.88ms ± 1% 8.87ms ± 0% ~ (p=0.480 n=19+18)
GobEncode 6.80ms ± 1% 6.85ms ± 0% +0.82% (p=0.000 n=20+18)
Gzip 363ms ± 1% 363ms ± 1% ~ (p=0.077 n=18+20)
Gunzip 90.6ms ± 0% 90.0ms ± 1% -0.71% (p=0.000 n=17+18)
HTTPClientServer 51.5µs ± 1% 50.8µs ± 1% -1.32% (p=0.000 n=18+18)
JSONEncode 17.0ms ± 0% 17.1ms ± 0% +0.40% (p=0.000 n=18+17)
JSONDecode 61.8ms ± 0% 63.8ms ± 1% +3.11% (p=0.000 n=18+17)
Mandelbrot200 3.84ms ± 0% 3.84ms ± 1% ~ (p=0.583 n=19+19)
GoParse 3.71ms ± 1% 3.72ms ± 1% ~ (p=0.159 n=18+19)
RegexpMatchEasy0_32 100ns ± 0% 100ns ± 1% -0.19% (p=0.033 n=17+19)
RegexpMatchEasy0_1K 342ns ± 1% 331ns ± 0% -3.41% (p=0.000 n=19+19)
RegexpMatchEasy1_32 82.5ns ± 0% 81.7ns ± 0% -0.98% (p=0.000 n=18+18)
RegexpMatchEasy1_1K 505ns ± 0% 494ns ± 1% -2.16% (p=0.000 n=18+18)
RegexpMatchMedium_32 137ns ± 1% 137ns ± 1% -0.24% (p=0.048 n=20+18)
RegexpMatchMedium_1K 41.6µs ± 0% 41.3µs ± 1% -0.57% (p=0.004 n=18+20)
RegexpMatchHard_32 2.11µs ± 0% 2.11µs ± 1% +0.20% (p=0.037 n=17+19)
RegexpMatchHard_1K 63.9µs ± 2% 63.3µs ± 0% -0.99% (p=0.000 n=20+17)
Revcomp 560ms ± 1% 522ms ± 0% -6.87% (p=0.000 n=18+16)
Template 75.0ms ± 0% 75.1ms ± 1% +0.18% (p=0.013 n=18+19)
TimeParse 358ns ± 1% 364ns ± 0% +1.74% (p=0.000 n=20+15)
TimeFormat 360ns ± 0% 372ns ± 0% +3.55% (p=0.000 n=20+18)
Change-Id: If8a9bfae6c128d15a4f405e02bcfa50129df82a2
Reviewed-on: https://go-review.googlesource.com/10314
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-05-20 14:30:49 -06:00
|
|
|
}
|
|
|
|
}
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
flr = findfunc(frame.lr)
|
2017-02-20 20:37:07 -07:00
|
|
|
if !flr.valid() {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
// This happens if you get a profiling interrupt at just the wrong time.
|
|
|
|
// In that context it is okay to stop early.
|
|
|
|
// But if callback is set, we're doing a garbage collection and must
|
|
|
|
// get everything, so crash loudly.
|
2018-01-30 14:03:51 -07:00
|
|
|
doPrint := printing
|
2018-03-14 16:21:37 -06:00
|
|
|
if doPrint && gp.m.incgo && f.funcID == funcID_sigpanic {
|
2018-01-30 14:03:51 -07:00
|
|
|
// We can inject sigpanic
|
|
|
|
// calls directly into C code,
|
|
|
|
// in which case we'll see a C
|
|
|
|
// return PC. Don't complain.
|
|
|
|
doPrint = false
|
|
|
|
}
|
|
|
|
if callback != nil || doPrint {
|
2014-12-29 00:16:32 -07:00
|
|
|
print("runtime: unexpected return pc for ", funcname(f), " called from ", hex(frame.lr), "\n")
|
2018-01-22 12:53:36 -07:00
|
|
|
tracebackHexdump(gp.stack, &frame, lrPtr)
|
|
|
|
}
|
|
|
|
if callback != nil {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("unknown caller pc")
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
frame.varp = frame.fp
|
|
|
|
if !usesLR {
|
|
|
|
// On x86, call instruction pushes return PC before entering new function.
|
2015-11-11 10:39:30 -07:00
|
|
|
frame.varp -= sys.RegSize
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
|
2015-01-14 09:09:50 -07:00
|
|
|
// If framepointer_enabled and there's a frame, then
|
|
|
|
// there's a saved bp here.
|
2015-02-03 07:18:15 -07:00
|
|
|
if framepointer_enabled && GOARCH == "amd64" && frame.varp > frame.sp {
|
2015-11-11 10:39:30 -07:00
|
|
|
frame.varp -= sys.RegSize
|
2015-01-14 09:09:50 -07:00
|
|
|
}
|
|
|
|
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
// Derive size of arguments.
|
|
|
|
// Most functions have a fixed-size argument block,
|
|
|
|
// so we can use metadata about the function f.
|
|
|
|
// Not all, though: there are some variadic functions
|
|
|
|
// in package runtime and reflect, and for those we use call-specific
|
|
|
|
// metadata recorded by f's caller.
|
|
|
|
if callback != nil || printing {
|
2015-11-11 10:39:30 -07:00
|
|
|
frame.argp = frame.fp + sys.MinFrameSize
|
runtime: fix getArgInfo for deferred reflection calls
getArgInfo for reflect.makeFuncStub and reflect.methodValueCall is
necessarily special. These have dynamically determined argument maps
that are stored in their context (that is, their *funcval). These
functions are written to store this context at 0(SP) when called, and
getArgInfo retrieves it from there.
This technique works if getArgInfo is passed an active call frame for
one of these functions. However, getArgInfo is also used in
tracebackdefers, where the "call" is not a true call with an active
stack frame, but a deferred call. In this situation, getArgInfo
currently crashes because tracebackdefers passes a frame with sp set
to 0. However, the entire approach used by getArgInfo is flawed in
this situation because the wrapper has not actually executed, and
hence hasn't saved this metadata to any stack frame.
In the defer case, we know the *funcval from the _defer itself, so we
can fix this by teaching getArgInfo to use the *funcval context
directly when its available, and otherwise get it from the active call
frame.
While we're here, this commit simplifies getArgInfo a bit by making it
play more nicely with the type system. Rather than decoding the
*reflect.methodValue that is the wrapper's context as a *[2]uintptr,
just write out a copy of the reflect.methodValue type in the runtime.
Fixes #16331. Fixes #17471.
Change-Id: I81db4d985179b4a81c68c490cceeccbfc675456a
Reviewed-on: https://go-review.googlesource.com/31138
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2016-10-16 16:23:39 -06:00
|
|
|
frame.arglen, frame.argmap = getArgInfo(&frame, f, callback != nil, nil)
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Determine frame's 'continuation PC', where it can continue.
|
|
|
|
// Normally this is the return address on the stack, but if sigpanic
|
|
|
|
// is immediately below this function on the stack, then the frame
|
|
|
|
// stopped executing due to a trap, and frame.pc is probably not
|
|
|
|
// a safe point for looking up liveness information. In this panicking case,
|
|
|
|
// the function either doesn't return at all (if it has no defers or if the
|
|
|
|
// defers do not recover) or it returns from one of the calls to
|
|
|
|
// deferproc a second time (if the corresponding deferred func recovers).
|
|
|
|
// It suffices to assume that the most recent deferproc is the one that
|
|
|
|
// returns; everything live at earlier deferprocs is still live at that one.
|
|
|
|
frame.continpc = frame.pc
|
|
|
|
if waspanic {
|
2014-12-08 15:18:58 -07:00
|
|
|
if _defer != nil && _defer.sp == frame.sp {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
frame.continpc = _defer.pc
|
|
|
|
} else {
|
|
|
|
frame.continpc = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
// Unwind our local defer stack past this frame.
|
2014-12-08 15:18:58 -07:00
|
|
|
for _defer != nil && (_defer.sp == frame.sp || _defer.sp == _NoArgs) {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
_defer = _defer.link
|
|
|
|
}
|
|
|
|
|
|
|
|
if callback != nil {
|
|
|
|
if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) {
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
}
|
runtime: handle inlined calls in runtime.Callers
The `skip` argument passed to runtime.Caller and runtime.Callers should
be interpreted as the number of logical calls to skip (rather than the
number of physical stack frames to skip). This changes runtime.Callers
to skip inlined calls in addition to physical stack frames.
The result value of runtime.Callers is a slice of program counters
([]uintptr) representing physical stack frames. If the `skip` parameter
to runtime.Callers skips part-way into a physical frame, there is no
convenient way to encode that in the resulting slice. To avoid changing
the API in an incompatible way, our solution is to store the number of
skipped logical calls of the first frame in the _second_ uintptr
returned by runtime.Callers. Since this number is a small integer, we
encode it as a valid PC value into a small symbol called:
runtime.skipPleaseUseCallersFrames
For example, if f() calls g(), g() calls `runtime.Callers(2, pcs)`, and
g() is inlined into f, then the frame for f will be partially skipped,
resulting in the following slice:
pcs = []uintptr{pc_in_f, runtime.skipPleaseUseCallersFrames+1, ...}
We store the skip PC in pcs[1] instead of pcs[0] so that `pcs[i:]` will
truncate the captured stack trace rather than grow it for all i.
Updates #19348.
Change-Id: I1c56f89ac48c29e6f52a5d085567c6d77d499cf1
Reviewed-on: https://go-review.googlesource.com/37854
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-03-06 12:48:36 -07:00
|
|
|
|
|
|
|
if pcbuf != nil {
|
|
|
|
if skip == 0 {
|
|
|
|
(*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = frame.pc
|
|
|
|
} else {
|
|
|
|
// backup to CALL instruction to read inlining info (same logic as below)
|
|
|
|
tracepc := frame.pc
|
|
|
|
if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic {
|
|
|
|
tracepc--
|
|
|
|
}
|
|
|
|
inldata := funcdata(f, _FUNCDATA_InlTree)
|
|
|
|
|
|
|
|
// no inlining info, skip the physical frame
|
|
|
|
if inldata == nil {
|
|
|
|
skip--
|
|
|
|
goto skipped
|
|
|
|
}
|
|
|
|
|
|
|
|
ix := pcdatavalue(f, _PCDATA_InlTreeIndex, tracepc, &cache)
|
|
|
|
inltree := (*[1 << 20]inlinedCall)(inldata)
|
|
|
|
// skip the logical (inlined) frames
|
|
|
|
logicalSkipped := 0
|
|
|
|
for ix >= 0 && skip > 0 {
|
|
|
|
skip--
|
|
|
|
logicalSkipped++
|
|
|
|
ix = inltree[ix].parent
|
|
|
|
}
|
|
|
|
|
|
|
|
// skip the physical frame if there's more to skip
|
|
|
|
if skip > 0 {
|
|
|
|
skip--
|
|
|
|
goto skipped
|
|
|
|
}
|
|
|
|
|
|
|
|
// now we have a partially skipped frame
|
|
|
|
(*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = frame.pc
|
|
|
|
|
|
|
|
// if there's room, pcbuf[1] is a skip PC that encodes the number of skipped frames in pcbuf[0]
|
|
|
|
if n+1 < max {
|
|
|
|
n++
|
2018-03-14 16:21:37 -06:00
|
|
|
pc := skipPC + uintptr(logicalSkipped)
|
|
|
|
(*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = pc
|
runtime: handle inlined calls in runtime.Callers
The `skip` argument passed to runtime.Caller and runtime.Callers should
be interpreted as the number of logical calls to skip (rather than the
number of physical stack frames to skip). This changes runtime.Callers
to skip inlined calls in addition to physical stack frames.
The result value of runtime.Callers is a slice of program counters
([]uintptr) representing physical stack frames. If the `skip` parameter
to runtime.Callers skips part-way into a physical frame, there is no
convenient way to encode that in the resulting slice. To avoid changing
the API in an incompatible way, our solution is to store the number of
skipped logical calls of the first frame in the _second_ uintptr
returned by runtime.Callers. Since this number is a small integer, we
encode it as a valid PC value into a small symbol called:
runtime.skipPleaseUseCallersFrames
For example, if f() calls g(), g() calls `runtime.Callers(2, pcs)`, and
g() is inlined into f, then the frame for f will be partially skipped,
resulting in the following slice:
pcs = []uintptr{pc_in_f, runtime.skipPleaseUseCallersFrames+1, ...}
We store the skip PC in pcs[1] instead of pcs[0] so that `pcs[i:]` will
truncate the captured stack trace rather than grow it for all i.
Updates #19348.
Change-Id: I1c56f89ac48c29e6f52a5d085567c6d77d499cf1
Reviewed-on: https://go-review.googlesource.com/37854
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-03-06 12:48:36 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
if printing {
|
2017-11-09 15:55:45 -07:00
|
|
|
// assume skip=0 for printing.
|
|
|
|
//
|
|
|
|
// Never elide wrappers if we haven't printed
|
|
|
|
// any frames. And don't elide wrappers that
|
|
|
|
// called panic rather than the wrapped
|
|
|
|
// function. Otherwise, leave them out.
|
|
|
|
name := funcname(f)
|
|
|
|
nextElideWrapper := elideWrapperCalling(name)
|
|
|
|
if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0, elideWrapper && nprint != 0) {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
// Print during crash.
|
|
|
|
// main(0x1, 0x2, 0x3)
|
|
|
|
// /home/rsc/go/src/runtime/x.go:23 +0xf
|
|
|
|
//
|
|
|
|
tracepc := frame.pc // back up to CALL instruction for funcline.
|
runtime: fix line number in first stack frame in printed stack trace
Originally traceback was only used for printing the stack
when an unexpected signal came in. In that case, the
initial PC is taken from the signal and should be used
unaltered. For the callers, the PC is the return address,
which might be on the line after the call; we subtract 1
to get to the CALL instruction.
Traceback is now used for a variety of things, and for
almost all of those the initial PC is a return address,
whether from getcallerpc, or gp->sched.pc, or gp->syscallpc.
In those cases, we need to subtract 1 from this initial PC,
but the traceback code had a hard rule "never subtract 1
from the initial PC", left over from the signal handling days.
Change gentraceback to take a flag that specifies whether
we are tracing a trap.
Change traceback to default to "starting with a return PC",
which is the overwhelmingly common case.
Add tracebacktrap, like traceback but starting with a trap PC.
Use tracebacktrap in signal handlers.
Fixes #7690.
LGTM=iant, r
R=r, iant
CC=golang-codereviews
https://golang.org/cl/167810044
2014-10-29 13:14:24 -06:00
|
|
|
if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
tracepc--
|
|
|
|
}
|
2017-02-17 14:08:36 -07:00
|
|
|
file, line := funcline(f, tracepc)
|
|
|
|
inldata := funcdata(f, _FUNCDATA_InlTree)
|
|
|
|
if inldata != nil {
|
|
|
|
inltree := (*[1 << 20]inlinedCall)(inldata)
|
|
|
|
ix := pcdatavalue(f, _PCDATA_InlTreeIndex, tracepc, nil)
|
|
|
|
for ix != -1 {
|
|
|
|
name := funcnameFromNameoff(f, inltree[ix].func_)
|
|
|
|
print(name, "(...)\n")
|
|
|
|
print("\t", file, ":", line, "\n")
|
|
|
|
|
|
|
|
file = funcfile(f, inltree[ix].file)
|
|
|
|
line = inltree[ix].line
|
|
|
|
ix = inltree[ix].parent
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 08:33:51 -07:00
|
|
|
if name == "runtime.gopanic" {
|
|
|
|
name = "panic"
|
|
|
|
}
|
|
|
|
print(name, "(")
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
argp := (*[100]uintptr)(unsafe.Pointer(frame.argp))
|
2015-11-11 10:39:30 -07:00
|
|
|
for i := uintptr(0); i < frame.arglen/sys.PtrSize; i++ {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
if i >= 10 {
|
|
|
|
print(", ...")
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if i != 0 {
|
|
|
|
print(", ")
|
|
|
|
}
|
|
|
|
print(hex(argp[i]))
|
|
|
|
}
|
|
|
|
print(")\n")
|
|
|
|
print("\t", file, ":", line)
|
|
|
|
if frame.pc > f.entry {
|
|
|
|
print(" +", hex(frame.pc-f.entry))
|
|
|
|
}
|
2015-10-30 09:03:02 -06:00
|
|
|
if g.m.throwing > 0 && gp == g.m.curg || level >= 2 {
|
2017-06-09 09:58:53 -06:00
|
|
|
print(" fp=", hex(frame.fp), " sp=", hex(frame.sp), " pc=", hex(frame.pc))
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
print("\n")
|
|
|
|
nprint++
|
|
|
|
}
|
2017-11-09 15:55:45 -07:00
|
|
|
elideWrapper = nextElideWrapper
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
n++
|
|
|
|
|
|
|
|
skipped:
|
2018-03-14 16:21:37 -06:00
|
|
|
if f.funcID == funcID_cgocallback_gofunc && len(cgoCtxt) > 0 {
|
2016-04-27 15:18:29 -06:00
|
|
|
ctxt := cgoCtxt[len(cgoCtxt)-1]
|
|
|
|
cgoCtxt = cgoCtxt[:len(cgoCtxt)-1]
|
|
|
|
|
|
|
|
// skip only applies to Go frames.
|
|
|
|
// callback != nil only used when we only care
|
|
|
|
// about Go frames.
|
|
|
|
if skip == 0 && callback == nil {
|
|
|
|
n = tracebackCgoContext(pcbuf, printing, ctxt, n, max)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-14 16:21:37 -06:00
|
|
|
waspanic = f.funcID == funcID_sigpanic
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
|
|
|
|
// Do not unwind past the bottom of the stack.
|
2017-02-20 20:37:07 -07:00
|
|
|
if !flr.valid() {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unwind to next frame.
|
|
|
|
frame.fn = flr
|
|
|
|
frame.pc = frame.lr
|
|
|
|
frame.lr = 0
|
|
|
|
frame.sp = frame.fp
|
|
|
|
frame.fp = 0
|
2014-09-12 05:29:19 -06:00
|
|
|
frame.argmap = nil
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
|
|
|
|
// On link register architectures, sighandler saves the LR on stack
|
|
|
|
// before faking a call to sigpanic.
|
|
|
|
if usesLR && waspanic {
|
|
|
|
x := *(*uintptr)(unsafe.Pointer(frame.sp))
|
2015-11-11 10:39:30 -07:00
|
|
|
frame.sp += sys.MinFrameSize
|
2015-03-08 07:20:20 -06:00
|
|
|
if GOARCH == "arm64" {
|
|
|
|
// arm64 needs 16-byte aligned SP, always
|
2015-11-11 10:39:30 -07:00
|
|
|
frame.sp += sys.PtrSize
|
2015-03-08 07:20:20 -06:00
|
|
|
}
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
f = findfunc(frame.pc)
|
|
|
|
frame.fn = f
|
2017-02-20 20:37:07 -07:00
|
|
|
if !f.valid() {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
frame.pc = x
|
runtime: add pcvalue cache to improve stack scan speed
The cost of scanning large stacks is currently dominated by the time
spent looking up and decoding the pcvalue table. However, large stacks
are usually large not because they contain calls to many different
functions, but because they contain many calls to the same, small set
of recursive functions. Hence, walking large stacks tends to make the
same pcvalue queries many times.
Based on this observation, this commit adds a small, very simple, and
fast cache in front of pcvalue lookup. We thread this cache down from
operations that make many pcvalue calls, such as gentraceback, stack
scanning, and stack adjusting.
This simple cache works well because it has minimal overhead when it's
not effective. I also tried a hashed direct-map cache, CLOCK-based
replacement, round-robin replacement, and round-robin with lookups
disabled until there had been at least 16 probes, but none of these
approaches had obvious wins over the random replacement policy in this
commit.
This nearly doubles the overall performance of the deep stack test
program from issue #10898:
name old time/op new time/op delta
Issue10898 16.5s ±12% 9.2s ±12% -44.37% (p=0.008 n=5+5)
It's a very slight win on the garbage benchmark:
name old time/op new time/op delta
XBenchGarbage-12 4.92ms ± 1% 4.89ms ± 1% -0.75% (p=0.000 n=18+19)
It's a wash (but doesn't harm performance) on the go1 benchmarks,
which don't have particularly deep stacks:
name old time/op new time/op delta
BinaryTree17-12 3.11s ± 2% 3.20s ± 3% +2.83% (p=0.000 n=17+20)
Fannkuch11-12 2.51s ± 1% 2.51s ± 1% -0.22% (p=0.034 n=19+18)
FmtFprintfEmpty-12 50.8ns ± 3% 50.6ns ± 2% ~ (p=0.793 n=20+20)
FmtFprintfString-12 174ns ± 0% 174ns ± 1% +0.17% (p=0.048 n=15+20)
FmtFprintfInt-12 177ns ± 0% 165ns ± 1% -6.99% (p=0.000 n=17+19)
FmtFprintfIntInt-12 283ns ± 1% 284ns ± 0% +0.22% (p=0.000 n=18+15)
FmtFprintfPrefixedInt-12 243ns ± 1% 244ns ± 1% +0.40% (p=0.000 n=20+19)
FmtFprintfFloat-12 318ns ± 0% 319ns ± 0% +0.27% (p=0.001 n=19+20)
FmtManyArgs-12 1.12µs ± 0% 1.14µs ± 0% +1.74% (p=0.000 n=19+20)
GobDecode-12 8.69ms ± 0% 8.73ms ± 1% +0.46% (p=0.000 n=18+18)
GobEncode-12 6.64ms ± 1% 6.61ms ± 1% -0.46% (p=0.000 n=20+20)
Gzip-12 323ms ± 2% 319ms ± 1% -1.11% (p=0.000 n=20+20)
Gunzip-12 42.8ms ± 0% 42.9ms ± 0% ~ (p=0.158 n=18+20)
HTTPClientServer-12 63.3µs ± 1% 63.1µs ± 1% -0.35% (p=0.011 n=20+20)
JSONEncode-12 16.9ms ± 1% 17.3ms ± 1% +2.84% (p=0.000 n=19+20)
JSONDecode-12 59.7ms ± 0% 58.5ms ± 0% -2.05% (p=0.000 n=19+17)
Mandelbrot200-12 3.92ms ± 0% 3.91ms ± 0% -0.16% (p=0.003 n=19+19)
GoParse-12 3.79ms ± 2% 3.75ms ± 2% -0.91% (p=0.005 n=20+20)
RegexpMatchEasy0_32-12 102ns ± 1% 101ns ± 1% -0.80% (p=0.001 n=14+20)
RegexpMatchEasy0_1K-12 337ns ± 1% 346ns ± 1% +2.90% (p=0.000 n=20+19)
RegexpMatchEasy1_32-12 84.4ns ± 2% 84.3ns ± 2% ~ (p=0.743 n=20+20)
RegexpMatchEasy1_1K-12 502ns ± 1% 505ns ± 0% +0.64% (p=0.000 n=20+20)
RegexpMatchMedium_32-12 133ns ± 1% 132ns ± 1% -0.85% (p=0.000 n=20+19)
RegexpMatchMedium_1K-12 40.1µs ± 1% 39.8µs ± 1% -0.77% (p=0.000 n=18+18)
RegexpMatchHard_32-12 2.08µs ± 1% 2.07µs ± 1% -0.55% (p=0.001 n=18+19)
RegexpMatchHard_1K-12 62.4µs ± 1% 62.0µs ± 1% -0.74% (p=0.000 n=19+19)
Revcomp-12 545ms ± 2% 545ms ± 3% ~ (p=0.771 n=19+20)
Template-12 73.7ms ± 1% 72.0ms ± 0% -2.33% (p=0.000 n=20+18)
TimeParse-12 358ns ± 1% 351ns ± 1% -2.07% (p=0.000 n=20+20)
TimeFormat-12 369ns ± 1% 356ns ± 0% -3.53% (p=0.000 n=20+18)
[Geo mean] 63.5µs 63.2µs -0.41%
name old speed new speed delta
GobDecode-12 88.3MB/s ± 0% 87.9MB/s ± 0% -0.43% (p=0.000 n=18+17)
GobEncode-12 116MB/s ± 1% 116MB/s ± 1% +0.47% (p=0.000 n=20+20)
Gzip-12 60.2MB/s ± 2% 60.8MB/s ± 1% +1.13% (p=0.000 n=20+20)
Gunzip-12 453MB/s ± 0% 453MB/s ± 0% ~ (p=0.160 n=18+20)
JSONEncode-12 115MB/s ± 1% 112MB/s ± 1% -2.76% (p=0.000 n=19+20)
JSONDecode-12 32.5MB/s ± 0% 33.2MB/s ± 0% +2.09% (p=0.000 n=19+17)
GoParse-12 15.3MB/s ± 2% 15.4MB/s ± 2% +0.92% (p=0.004 n=20+20)
RegexpMatchEasy0_32-12 311MB/s ± 1% 314MB/s ± 1% +0.78% (p=0.000 n=15+19)
RegexpMatchEasy0_1K-12 3.04GB/s ± 1% 2.95GB/s ± 1% -2.90% (p=0.000 n=19+19)
RegexpMatchEasy1_32-12 379MB/s ± 2% 380MB/s ± 2% ~ (p=0.779 n=20+20)
RegexpMatchEasy1_1K-12 2.04GB/s ± 1% 2.02GB/s ± 0% -0.62% (p=0.000 n=20+20)
RegexpMatchMedium_32-12 7.46MB/s ± 1% 7.53MB/s ± 1% +0.86% (p=0.000 n=20+19)
RegexpMatchMedium_1K-12 25.5MB/s ± 1% 25.7MB/s ± 1% +0.78% (p=0.000 n=18+18)
RegexpMatchHard_32-12 15.4MB/s ± 1% 15.5MB/s ± 1% +0.62% (p=0.000 n=19+19)
RegexpMatchHard_1K-12 16.4MB/s ± 1% 16.5MB/s ± 1% +0.82% (p=0.000 n=20+19)
Revcomp-12 466MB/s ± 2% 466MB/s ± 3% ~ (p=0.765 n=19+20)
Template-12 26.3MB/s ± 1% 27.0MB/s ± 0% +2.38% (p=0.000 n=20+18)
[Geo mean] 97.8MB/s 98.0MB/s +0.23%
Change-Id: I281044ae0b24990ba46487cacbc1069493274bc4
Reviewed-on: https://go-review.googlesource.com/13614
Reviewed-by: Keith Randall <khr@golang.org>
2015-08-12 21:43:43 -06:00
|
|
|
} else if funcspdelta(f, frame.pc, &cache) == 0 {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
frame.lr = x
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-24 22:41:21 -07:00
|
|
|
if printing {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
n = nprint
|
|
|
|
}
|
|
|
|
|
|
|
|
// If callback != nil, we're being called to gather stack information during
|
|
|
|
// garbage collection or stack growth. In that context, require that we used
|
|
|
|
// up the entire defer stack. If not, then there is a bug somewhere and the
|
|
|
|
// garbage collection or stack growth may not have seen the correct picture
|
|
|
|
// of the stack. Crash now instead of silently executing the garbage collection
|
|
|
|
// or stack copy incorrectly and setting up for a mysterious crash later.
|
|
|
|
//
|
|
|
|
// Note that panic != nil is okay here: there can be leftover panics,
|
|
|
|
// because the defers on the panic stack do not nest in frame order as
|
|
|
|
// they do on the defer stack. If you have:
|
|
|
|
//
|
|
|
|
// frame 1 defers d1
|
|
|
|
// frame 2 defers d2
|
|
|
|
// frame 3 defers d3
|
|
|
|
// frame 4 panics
|
|
|
|
// frame 4's panic starts running defers
|
|
|
|
// frame 5, running d3, defers d4
|
|
|
|
// frame 5 panics
|
|
|
|
// frame 5's panic starts running defers
|
|
|
|
// frame 6, running d4, garbage collects
|
|
|
|
// frame 6, running d2, garbage collects
|
|
|
|
//
|
|
|
|
// During the execution of d4, the panic stack is d4 -> d3, which
|
|
|
|
// is nested properly, and we'll treat frame 3 as resumable, because we
|
|
|
|
// can find d3. (And in fact frame 3 is resumable. If d4 recovers
|
|
|
|
// and frame 5 continues running, d3, d3 can recover and we'll
|
|
|
|
// resume execution in (returning from) frame 3.)
|
|
|
|
//
|
|
|
|
// During the execution of d2, however, the panic stack is d2 -> d3,
|
|
|
|
// which is inverted. The scan will match d2 to frame 2 but having
|
|
|
|
// d2 on the stack until then means it will not match d3 to frame 3.
|
|
|
|
// This is okay: if we're running d2, then all the defers after d2 have
|
|
|
|
// completed and their corresponding frames are dead. Not finding d3
|
|
|
|
// for frame 3 means we'll set frame 3's continpc == 0, which is correct
|
|
|
|
// (frame 3 is dead). At the end of the walk the panic stack can thus
|
|
|
|
// contain defers (d3 in this case) for dead frames. The inversion here
|
|
|
|
// always indicates a dead frame, and the effect of the inversion on the
|
|
|
|
// scan is to hide those dead frames, so the scan is still okay:
|
|
|
|
// what's left on the panic stack are exactly (and only) the dead frames.
|
|
|
|
//
|
|
|
|
// We require callback != nil here because only when callback != nil
|
|
|
|
// do we know that gentraceback is being called in a "must be correct"
|
|
|
|
// context as opposed to a "best effort" context. The tracebacks with
|
|
|
|
// callbacks only happen when everything is stopped nicely.
|
|
|
|
// At other times, such as when gathering a stack for a profiling signal
|
|
|
|
// or when printing a traceback during a crash, everything may not be
|
|
|
|
// stopped nicely, and the stack walk may not be able to complete.
|
|
|
|
// It's okay in those situations not to use up the entire defer stack:
|
|
|
|
// incomplete information then is still better than nothing.
|
|
|
|
if callback != nil && n < max && _defer != nil {
|
|
|
|
if _defer != nil {
|
2014-12-08 15:18:58 -07:00
|
|
|
print("runtime: g", gp.goid, ": leftover defer sp=", hex(_defer.sp), " pc=", hex(_defer.pc), "\n")
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
for _defer = gp._defer; _defer != nil; _defer = _defer.link {
|
2014-12-08 15:18:58 -07:00
|
|
|
print("\tdefer ", _defer, " sp=", hex(_defer.sp), " pc=", hex(_defer.pc), "\n")
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("traceback has leftover defers")
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
|
2015-08-26 09:39:10 -06:00
|
|
|
if callback != nil && n < max && frame.sp != gp.stktopsp {
|
|
|
|
print("runtime: g", gp.goid, ": frame.sp=", hex(frame.sp), " top=", hex(gp.stktopsp), "\n")
|
|
|
|
print("\tstack=[", hex(gp.stack.lo), "-", hex(gp.stack.hi), "] n=", n, " max=", max, "\n")
|
|
|
|
throw("traceback did not unwind completely")
|
|
|
|
}
|
|
|
|
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2016-12-16 09:57:25 -07:00
|
|
|
// reflectMethodValue is a partial duplicate of reflect.makeFuncImpl
|
|
|
|
// and reflect.methodValue.
|
runtime: fix getArgInfo for deferred reflection calls
getArgInfo for reflect.makeFuncStub and reflect.methodValueCall is
necessarily special. These have dynamically determined argument maps
that are stored in their context (that is, their *funcval). These
functions are written to store this context at 0(SP) when called, and
getArgInfo retrieves it from there.
This technique works if getArgInfo is passed an active call frame for
one of these functions. However, getArgInfo is also used in
tracebackdefers, where the "call" is not a true call with an active
stack frame, but a deferred call. In this situation, getArgInfo
currently crashes because tracebackdefers passes a frame with sp set
to 0. However, the entire approach used by getArgInfo is flawed in
this situation because the wrapper has not actually executed, and
hence hasn't saved this metadata to any stack frame.
In the defer case, we know the *funcval from the _defer itself, so we
can fix this by teaching getArgInfo to use the *funcval context
directly when its available, and otherwise get it from the active call
frame.
While we're here, this commit simplifies getArgInfo a bit by making it
play more nicely with the type system. Rather than decoding the
*reflect.methodValue that is the wrapper's context as a *[2]uintptr,
just write out a copy of the reflect.methodValue type in the runtime.
Fixes #16331. Fixes #17471.
Change-Id: I81db4d985179b4a81c68c490cceeccbfc675456a
Reviewed-on: https://go-review.googlesource.com/31138
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2016-10-16 16:23:39 -06:00
|
|
|
type reflectMethodValue struct {
|
|
|
|
fn uintptr
|
|
|
|
stack *bitvector // args bitmap
|
|
|
|
}
|
|
|
|
|
|
|
|
// getArgInfo returns the argument frame information for a call to f
|
|
|
|
// with call frame frame.
|
|
|
|
//
|
|
|
|
// This is used for both actual calls with active stack frames and for
|
|
|
|
// deferred calls that are not yet executing. If this is an actual
|
|
|
|
// call, ctxt must be nil (getArgInfo will retrieve what it needs from
|
|
|
|
// the active stack frame). If this is a deferred call, ctxt must be
|
|
|
|
// the function object that was deferred.
|
2017-02-20 20:37:07 -07:00
|
|
|
func getArgInfo(frame *stkframe, f funcInfo, needArgMap bool, ctxt *funcval) (arglen uintptr, argmap *bitvector) {
|
2015-11-17 15:14:32 -07:00
|
|
|
arglen = uintptr(f.args)
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
if needArgMap && f.args == _ArgsSizeUnknown {
|
|
|
|
// Extract argument bitmaps for reflect stubs from the calls they made to reflect.
|
2014-12-29 00:16:32 -07:00
|
|
|
switch funcname(f) {
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
case "reflect.makeFuncStub", "reflect.methodValueCall":
|
runtime: fix getArgInfo for deferred reflection calls
getArgInfo for reflect.makeFuncStub and reflect.methodValueCall is
necessarily special. These have dynamically determined argument maps
that are stored in their context (that is, their *funcval). These
functions are written to store this context at 0(SP) when called, and
getArgInfo retrieves it from there.
This technique works if getArgInfo is passed an active call frame for
one of these functions. However, getArgInfo is also used in
tracebackdefers, where the "call" is not a true call with an active
stack frame, but a deferred call. In this situation, getArgInfo
currently crashes because tracebackdefers passes a frame with sp set
to 0. However, the entire approach used by getArgInfo is flawed in
this situation because the wrapper has not actually executed, and
hence hasn't saved this metadata to any stack frame.
In the defer case, we know the *funcval from the _defer itself, so we
can fix this by teaching getArgInfo to use the *funcval context
directly when its available, and otherwise get it from the active call
frame.
While we're here, this commit simplifies getArgInfo a bit by making it
play more nicely with the type system. Rather than decoding the
*reflect.methodValue that is the wrapper's context as a *[2]uintptr,
just write out a copy of the reflect.methodValue type in the runtime.
Fixes #16331. Fixes #17471.
Change-Id: I81db4d985179b4a81c68c490cceeccbfc675456a
Reviewed-on: https://go-review.googlesource.com/31138
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2016-10-16 16:23:39 -06:00
|
|
|
// These take a *reflect.methodValue as their
|
|
|
|
// context register.
|
|
|
|
var mv *reflectMethodValue
|
|
|
|
if ctxt != nil {
|
|
|
|
// This is not an actual call, but a
|
|
|
|
// deferred call. The function value
|
|
|
|
// is itself the *reflect.methodValue.
|
|
|
|
mv = (*reflectMethodValue)(unsafe.Pointer(ctxt))
|
|
|
|
} else {
|
|
|
|
// This is a real call that took the
|
|
|
|
// *reflect.methodValue as its context
|
|
|
|
// register and immediately saved it
|
|
|
|
// to 0(SP). Get the methodValue from
|
|
|
|
// 0(SP).
|
|
|
|
arg0 := frame.sp + sys.MinFrameSize
|
|
|
|
mv = *(**reflectMethodValue)(unsafe.Pointer(arg0))
|
|
|
|
}
|
|
|
|
if mv.fn != f.entry {
|
2014-12-29 00:16:32 -07:00
|
|
|
print("runtime: confused by ", funcname(f), "\n")
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("reflect mismatch")
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
}
|
runtime: fix getArgInfo for deferred reflection calls
getArgInfo for reflect.makeFuncStub and reflect.methodValueCall is
necessarily special. These have dynamically determined argument maps
that are stored in their context (that is, their *funcval). These
functions are written to store this context at 0(SP) when called, and
getArgInfo retrieves it from there.
This technique works if getArgInfo is passed an active call frame for
one of these functions. However, getArgInfo is also used in
tracebackdefers, where the "call" is not a true call with an active
stack frame, but a deferred call. In this situation, getArgInfo
currently crashes because tracebackdefers passes a frame with sp set
to 0. However, the entire approach used by getArgInfo is flawed in
this situation because the wrapper has not actually executed, and
hence hasn't saved this metadata to any stack frame.
In the defer case, we know the *funcval from the _defer itself, so we
can fix this by teaching getArgInfo to use the *funcval context
directly when its available, and otherwise get it from the active call
frame.
While we're here, this commit simplifies getArgInfo a bit by making it
play more nicely with the type system. Rather than decoding the
*reflect.methodValue that is the wrapper's context as a *[2]uintptr,
just write out a copy of the reflect.methodValue type in the runtime.
Fixes #16331. Fixes #17471.
Change-Id: I81db4d985179b4a81c68c490cceeccbfc675456a
Reviewed-on: https://go-review.googlesource.com/31138
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2016-10-16 16:23:39 -06:00
|
|
|
bv := mv.stack
|
2015-11-17 15:14:32 -07:00
|
|
|
arglen = uintptr(bv.n * sys.PtrSize)
|
|
|
|
argmap = bv
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
}
|
|
|
|
}
|
2015-11-17 15:14:32 -07:00
|
|
|
return
|
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how
to interpret the defer structures. Previously, only the stack
copying treated them precisely.
This removes an untyped memory allocation and fixes
at least three copystack bugs.
To make sure the GC can find the deferred argument
frame until it has been copied, keep a Defer on the defer list
during its execution.
In addition to making it possible to remove the untyped
memory allocation, keeping the Defer on the list fixes
two races between copystack and execution of defers
(in both gopanic and Goexit). The problem is that once
the defer has been taken off the list, a stack copy that
happens before the deferred arguments have been copied
back to the stack will not update the arguments correctly.
The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit
(variations on the existing TestDeferPtrs) pass now but
failed before this CL.
In addition to those fixes, keeping the Defer on the list
helps correct a dangling pointer error during copystack.
The traceback routines walk the Defer chain to provide
information about where a panic may resume execution.
When the executing Defer was not on the Defer chain
but instead linked from the Panic chain, the traceback
had to walk the Panic chain too. But Panic structs are
on the stack and being updated by copystack.
Traceback's use of the Panic chain while copystack is
updating those structs means that it can follow an
updated pointer and find itself reading from the new stack.
The new stack is usually all zeros, so it sees an incorrect
early end to the chain. The new TestPanicUseStack makes
this happen at tip and dies when adjustdefers finds an
unexpected argp. The new StackCopyPoison mode
causes an earlier bad dereference instead.
By keeping the Defer on the list, traceback can avoid
walking the Panic chain at all, making it okay for copystack
to update the Panics.
We'd have the same problem for any Defers on the stack.
There was only one: gopanic's dabort. Since we are not
taking the executing Defer off the chain, we can use it
to do what dabort was doing, and then there are no
Defers on the stack ever, so it is okay for traceback to use
the Defer chain even while copystack is executing:
copystack cannot modify the Defer chain.
LGTM=khr
R=khr
CC=dvyukov, golang-codereviews, iant, rlh
https://golang.org/cl/141490043
2014-09-16 08:36:38 -06:00
|
|
|
}
|
|
|
|
|
2016-04-27 15:18:29 -06:00
|
|
|
// tracebackCgoContext handles tracing back a cgo context value, from
|
|
|
|
// the context argument to setCgoTraceback, for the gentraceback
|
|
|
|
// function. It returns the new value of n.
|
|
|
|
func tracebackCgoContext(pcbuf *uintptr, printing bool, ctxt uintptr, n, max int) int {
|
|
|
|
var cgoPCs [32]uintptr
|
|
|
|
cgoContextPCs(ctxt, cgoPCs[:])
|
|
|
|
var arg cgoSymbolizerArg
|
|
|
|
anySymbolized := false
|
|
|
|
for _, pc := range cgoPCs {
|
|
|
|
if pc == 0 || n >= max {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if pcbuf != nil {
|
|
|
|
(*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = pc
|
|
|
|
}
|
|
|
|
if printing {
|
|
|
|
if cgoSymbolizer == nil {
|
|
|
|
print("non-Go function at pc=", hex(pc), "\n")
|
|
|
|
} else {
|
|
|
|
c := printOneCgoTraceback(pc, max-n, &arg)
|
|
|
|
n += c - 1 // +1 a few lines down
|
|
|
|
anySymbolized = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
n++
|
|
|
|
}
|
|
|
|
if anySymbolized {
|
|
|
|
arg.pc = 0
|
|
|
|
callCgoSymbolizer(&arg)
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
func printcreatedby(gp *g) {
|
|
|
|
// Show what created goroutine, except main goroutine (goid 1).
|
|
|
|
pc := gp.gopc
|
|
|
|
f := findfunc(pc)
|
2017-11-09 15:55:45 -07:00
|
|
|
if f.valid() && showframe(f, gp, false, false) && gp.goid != 1 {
|
2018-04-03 19:35:46 -06:00
|
|
|
printcreatedby1(f, pc)
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-03 19:35:46 -06:00
|
|
|
func printcreatedby1(f funcInfo, pc uintptr) {
|
|
|
|
print("created by ", funcname(f), "\n")
|
|
|
|
tracepc := pc // back up to CALL instruction for funcline.
|
|
|
|
if pc > f.entry {
|
|
|
|
tracepc -= sys.PCQuantum
|
|
|
|
}
|
|
|
|
file, line := funcline(f, tracepc)
|
|
|
|
print("\t", file, ":", line)
|
|
|
|
if pc > f.entry {
|
|
|
|
print(" +", hex(pc-f.entry))
|
|
|
|
}
|
|
|
|
print("\n")
|
|
|
|
}
|
|
|
|
|
2015-02-24 22:41:21 -07:00
|
|
|
func traceback(pc, sp, lr uintptr, gp *g) {
|
runtime: fix line number in first stack frame in printed stack trace
Originally traceback was only used for printing the stack
when an unexpected signal came in. In that case, the
initial PC is taken from the signal and should be used
unaltered. For the callers, the PC is the return address,
which might be on the line after the call; we subtract 1
to get to the CALL instruction.
Traceback is now used for a variety of things, and for
almost all of those the initial PC is a return address,
whether from getcallerpc, or gp->sched.pc, or gp->syscallpc.
In those cases, we need to subtract 1 from this initial PC,
but the traceback code had a hard rule "never subtract 1
from the initial PC", left over from the signal handling days.
Change gentraceback to take a flag that specifies whether
we are tracing a trap.
Change traceback to default to "starting with a return PC",
which is the overwhelmingly common case.
Add tracebacktrap, like traceback but starting with a trap PC.
Use tracebacktrap in signal handlers.
Fixes #7690.
LGTM=iant, r
R=r, iant
CC=golang-codereviews
https://golang.org/cl/167810044
2014-10-29 13:14:24 -06:00
|
|
|
traceback1(pc, sp, lr, gp, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// tracebacktrap is like traceback but expects that the PC and SP were obtained
|
|
|
|
// from a trap, not from gp->sched or gp->syscallpc/gp->syscallsp or getcallerpc/getcallersp.
|
|
|
|
// Because they are from a trap instead of from a saved pair,
|
|
|
|
// the initial PC must not be rewound to the previous instruction.
|
|
|
|
// (All the saved pairs record a PC that is a return address, so we
|
|
|
|
// rewind it into the CALL instruction.)
|
2015-02-24 22:41:21 -07:00
|
|
|
func tracebacktrap(pc, sp, lr uintptr, gp *g) {
|
runtime: fix line number in first stack frame in printed stack trace
Originally traceback was only used for printing the stack
when an unexpected signal came in. In that case, the
initial PC is taken from the signal and should be used
unaltered. For the callers, the PC is the return address,
which might be on the line after the call; we subtract 1
to get to the CALL instruction.
Traceback is now used for a variety of things, and for
almost all of those the initial PC is a return address,
whether from getcallerpc, or gp->sched.pc, or gp->syscallpc.
In those cases, we need to subtract 1 from this initial PC,
but the traceback code had a hard rule "never subtract 1
from the initial PC", left over from the signal handling days.
Change gentraceback to take a flag that specifies whether
we are tracing a trap.
Change traceback to default to "starting with a return PC",
which is the overwhelmingly common case.
Add tracebacktrap, like traceback but starting with a trap PC.
Use tracebacktrap in signal handlers.
Fixes #7690.
LGTM=iant, r
R=r, iant
CC=golang-codereviews
https://golang.org/cl/167810044
2014-10-29 13:14:24 -06:00
|
|
|
traceback1(pc, sp, lr, gp, _TraceTrap)
|
|
|
|
}
|
|
|
|
|
2015-02-24 22:41:21 -07:00
|
|
|
func traceback1(pc, sp, lr uintptr, gp *g, flags uint) {
|
2015-12-11 18:16:48 -07:00
|
|
|
// If the goroutine is in cgo, and we have a cgo traceback, print that.
|
|
|
|
if iscgo && gp.m != nil && gp.m.ncgo > 0 && gp.syscallsp != 0 && gp.m.cgoCallers != nil && gp.m.cgoCallers[0] != 0 {
|
|
|
|
// Lock cgoCallers so that a signal handler won't
|
|
|
|
// change it, copy the array, reset it, unlock it.
|
|
|
|
// We are locked to the thread and are not running
|
|
|
|
// concurrently with a signal handler.
|
|
|
|
// We just have to stop a signal handler from interrupting
|
|
|
|
// in the middle of our copy.
|
|
|
|
atomic.Store(&gp.m.cgoCallersUse, 1)
|
|
|
|
cgoCallers := *gp.m.cgoCallers
|
|
|
|
gp.m.cgoCallers[0] = 0
|
|
|
|
atomic.Store(&gp.m.cgoCallersUse, 0)
|
|
|
|
|
|
|
|
printCgoTraceback(&cgoCallers)
|
|
|
|
}
|
|
|
|
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
var n int
|
|
|
|
if readgstatus(gp)&^_Gscan == _Gsyscall {
|
runtime: fix line number in first stack frame in printed stack trace
Originally traceback was only used for printing the stack
when an unexpected signal came in. In that case, the
initial PC is taken from the signal and should be used
unaltered. For the callers, the PC is the return address,
which might be on the line after the call; we subtract 1
to get to the CALL instruction.
Traceback is now used for a variety of things, and for
almost all of those the initial PC is a return address,
whether from getcallerpc, or gp->sched.pc, or gp->syscallpc.
In those cases, we need to subtract 1 from this initial PC,
but the traceback code had a hard rule "never subtract 1
from the initial PC", left over from the signal handling days.
Change gentraceback to take a flag that specifies whether
we are tracing a trap.
Change traceback to default to "starting with a return PC",
which is the overwhelmingly common case.
Add tracebacktrap, like traceback but starting with a trap PC.
Use tracebacktrap in signal handlers.
Fixes #7690.
LGTM=iant, r
R=r, iant
CC=golang-codereviews
https://golang.org/cl/167810044
2014-10-29 13:14:24 -06:00
|
|
|
// Override registers if blocked in system call.
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
pc = gp.syscallpc
|
|
|
|
sp = gp.syscallsp
|
runtime: fix line number in first stack frame in printed stack trace
Originally traceback was only used for printing the stack
when an unexpected signal came in. In that case, the
initial PC is taken from the signal and should be used
unaltered. For the callers, the PC is the return address,
which might be on the line after the call; we subtract 1
to get to the CALL instruction.
Traceback is now used for a variety of things, and for
almost all of those the initial PC is a return address,
whether from getcallerpc, or gp->sched.pc, or gp->syscallpc.
In those cases, we need to subtract 1 from this initial PC,
but the traceback code had a hard rule "never subtract 1
from the initial PC", left over from the signal handling days.
Change gentraceback to take a flag that specifies whether
we are tracing a trap.
Change traceback to default to "starting with a return PC",
which is the overwhelmingly common case.
Add tracebacktrap, like traceback but starting with a trap PC.
Use tracebacktrap in signal handlers.
Fixes #7690.
LGTM=iant, r
R=r, iant
CC=golang-codereviews
https://golang.org/cl/167810044
2014-10-29 13:14:24 -06:00
|
|
|
flags &^= _TraceTrap
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
// Print traceback. By default, omits runtime frames.
|
|
|
|
// If that means we print nothing at all, repeat forcing all frames printed.
|
runtime: fix line number in first stack frame in printed stack trace
Originally traceback was only used for printing the stack
when an unexpected signal came in. In that case, the
initial PC is taken from the signal and should be used
unaltered. For the callers, the PC is the return address,
which might be on the line after the call; we subtract 1
to get to the CALL instruction.
Traceback is now used for a variety of things, and for
almost all of those the initial PC is a return address,
whether from getcallerpc, or gp->sched.pc, or gp->syscallpc.
In those cases, we need to subtract 1 from this initial PC,
but the traceback code had a hard rule "never subtract 1
from the initial PC", left over from the signal handling days.
Change gentraceback to take a flag that specifies whether
we are tracing a trap.
Change traceback to default to "starting with a return PC",
which is the overwhelmingly common case.
Add tracebacktrap, like traceback but starting with a trap PC.
Use tracebacktrap in signal handlers.
Fixes #7690.
LGTM=iant, r
R=r, iant
CC=golang-codereviews
https://golang.org/cl/167810044
2014-10-29 13:14:24 -06:00
|
|
|
n = gentraceback(pc, sp, lr, gp, 0, nil, _TracebackMaxFrames, nil, nil, flags)
|
|
|
|
if n == 0 && (flags&_TraceRuntimeFrames) == 0 {
|
|
|
|
n = gentraceback(pc, sp, lr, gp, 0, nil, _TracebackMaxFrames, nil, nil, flags|_TraceRuntimeFrames)
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
if n == _TracebackMaxFrames {
|
|
|
|
print("...additional frames elided...\n")
|
|
|
|
}
|
|
|
|
printcreatedby(gp)
|
2018-04-03 19:35:46 -06:00
|
|
|
|
|
|
|
if gp.ancestors == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, ancestor := range *gp.ancestors {
|
|
|
|
printAncestorTraceback(ancestor)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// printAncestorTraceback prints the traceback of the given ancestor.
|
|
|
|
// TODO: Unify this with gentraceback and CallersFrames.
|
|
|
|
func printAncestorTraceback(ancestor ancestorInfo) {
|
|
|
|
print("[originating from goroutine ", ancestor.goid, "]:\n")
|
|
|
|
elideWrapper := false
|
|
|
|
for fidx, pc := range ancestor.pcs {
|
|
|
|
f := findfunc(pc) // f previously validated
|
|
|
|
if showfuncinfo(f, fidx == 0, elideWrapper && fidx != 0) {
|
|
|
|
elideWrapper = printAncestorTracebackFuncInfo(f, pc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(ancestor.pcs) == _TracebackMaxFrames {
|
|
|
|
print("...additional frames elided...\n")
|
|
|
|
}
|
|
|
|
// Show what created goroutine, except main goroutine (goid 1).
|
|
|
|
f := findfunc(ancestor.gopc)
|
|
|
|
if f.valid() && showfuncinfo(f, false, false) && ancestor.goid != 1 {
|
|
|
|
printcreatedby1(f, ancestor.gopc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// printAncestorTraceback prints the given function info at a given pc
|
|
|
|
// within an ancestor traceback. The precision of this info is reduced
|
|
|
|
// due to only have access to the pcs at the time of the caller
|
|
|
|
// goroutine being created.
|
|
|
|
func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) bool {
|
|
|
|
tracepc := pc // back up to CALL instruction for funcline.
|
|
|
|
if pc > f.entry {
|
|
|
|
tracepc -= sys.PCQuantum
|
|
|
|
}
|
|
|
|
file, line := funcline(f, tracepc)
|
|
|
|
inldata := funcdata(f, _FUNCDATA_InlTree)
|
|
|
|
if inldata != nil {
|
|
|
|
inltree := (*[1 << 20]inlinedCall)(inldata)
|
|
|
|
ix := pcdatavalue(f, _PCDATA_InlTreeIndex, tracepc, nil)
|
|
|
|
for ix != -1 {
|
|
|
|
name := funcnameFromNameoff(f, inltree[ix].func_)
|
|
|
|
print(name, "(...)\n")
|
|
|
|
print("\t", file, ":", line, "\n")
|
|
|
|
|
|
|
|
file = funcfile(f, inltree[ix].file)
|
|
|
|
line = inltree[ix].line
|
|
|
|
ix = inltree[ix].parent
|
|
|
|
}
|
|
|
|
}
|
|
|
|
name := funcname(f)
|
|
|
|
if name == "runtime.gopanic" {
|
|
|
|
name = "panic"
|
|
|
|
}
|
|
|
|
print(name, "(...)\n")
|
|
|
|
print("\t", file, ":", line)
|
|
|
|
if pc > f.entry {
|
|
|
|
print(" +", hex(pc-f.entry))
|
|
|
|
}
|
|
|
|
print("\n")
|
|
|
|
return elideWrapperCalling(name)
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
|
2015-02-24 22:41:21 -07:00
|
|
|
func callers(skip int, pcbuf []uintptr) int {
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
sp := getcallersp(unsafe.Pointer(&skip))
|
2017-09-22 13:16:26 -06:00
|
|
|
pc := getcallerpc()
|
2015-05-20 09:50:48 -06:00
|
|
|
gp := getg()
|
runtime: avoid gentraceback of self on user goroutine stack
Gentraceback may grow the stack.
One of the gentraceback wrappers may grow the stack.
One of the gentraceback callback calls may grow the stack.
Various stack pointers are stored in various stack locations
as type uintptr during the execution of these calls.
If the stack does grow, these stack pointers will not be
updated and will start trying to decode stack memory that
is no longer valid.
It may be possible to change the type of the stack pointer
variables to be unsafe.Pointer, but that's pretty subtle and
may still have problems, even if we catch every last one.
An easier, more obviously correct fix is to require that
gentraceback of the currently running goroutine must run
on the g0 stack, not on the goroutine's own stack.
Not doing this causes faults when you set
StackFromSystem = 1
StackFaultOnFree = 1
The new check in gentraceback will catch future lapses.
The more general problem is calling getcallersp but then
calling a function that might relocate the stack, which would
invalidate the result of getcallersp. Add note to stubs.go
declaration of getcallersp explaining the problem, and
check all existing calls to getcallersp. Most needed fixes.
This affects Callers, Stack, and nearly all the runtime
profiling routines. It does not affect stack copying directly
nor garbage collection.
LGTM=khr
R=khr, bradfitz
CC=golang-codereviews, r
https://golang.org/cl/167060043
2014-11-05 21:01:48 -07:00
|
|
|
var n int
|
[dev.cc] runtime: delete scalararg, ptrarg; rename onM to systemstack
Scalararg and ptrarg are not "signal safe".
Go code filling them out can be interrupted by a signal,
and then the signal handler runs, and if it also ends up
in Go code that uses scalararg or ptrarg, now the old
values have been smashed.
For the pieces of code that do need to run in a signal handler,
we introduced onM_signalok, which is really just onM
except that the _signalok is meant to convey that the caller
asserts that scalarg and ptrarg will be restored to their old
values after the call (instead of the usual behavior, zeroing them).
Scalararg and ptrarg are also untyped and therefore error-prone.
Go code can always pass a closure instead of using scalararg
and ptrarg; they were only really necessary for C code.
And there's no more C code.
For all these reasons, delete scalararg and ptrarg, converting
the few remaining references to use closures.
Once those are gone, there is no need for a distinction between
onM and onM_signalok, so replace both with a single function
equivalent to the current onM_signalok (that is, it can be called
on any of the curg, g0, and gsignal stacks).
The name onM and the phrase 'm stack' are misnomers,
because on most system an M has two system stacks:
the main thread stack and the signal handling stack.
Correct the misnomer by naming the replacement function systemstack.
Fix a few references to "M stack" in code.
The main motivation for this change is to eliminate scalararg/ptrarg.
Rick and I have already seen them cause problems because
the calling sequence m.ptrarg[0] = p is a heap pointer assignment,
so it gets a write barrier. The write barrier also uses onM, so it has
all the same problems as if it were being invoked by a signal handler.
We worked around this by saving and restoring the old values
and by calling onM_signalok, but there's no point in keeping this nice
home for bugs around any longer.
This CL also changes funcline to return the file name as a result
instead of filling in a passed-in *string. (The *string signature is
left over from when the code was written in and called from C.)
That's arguably an unrelated change, except that once I had done
the ptrarg/scalararg/onM cleanup I started getting false positives
about the *string argument escaping (not allowed in package runtime).
The compiler is wrong, but the easiest fix is to write the code like
Go code instead of like C code. I am a bit worried that the compiler
is wrong because of some use of uninitialized memory in the escape
analysis. If that's the reason, it will go away when we convert the
compiler to Go. (And if not, we'll debug it the next time.)
LGTM=khr
R=r, khr
CC=austin, golang-codereviews, iant, rlh
https://golang.org/cl/174950043
2014-11-12 12:54:31 -07:00
|
|
|
systemstack(func() {
|
2015-05-20 09:50:48 -06:00
|
|
|
n = gentraceback(pc, sp, 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
|
runtime: avoid gentraceback of self on user goroutine stack
Gentraceback may grow the stack.
One of the gentraceback wrappers may grow the stack.
One of the gentraceback callback calls may grow the stack.
Various stack pointers are stored in various stack locations
as type uintptr during the execution of these calls.
If the stack does grow, these stack pointers will not be
updated and will start trying to decode stack memory that
is no longer valid.
It may be possible to change the type of the stack pointer
variables to be unsafe.Pointer, but that's pretty subtle and
may still have problems, even if we catch every last one.
An easier, more obviously correct fix is to require that
gentraceback of the currently running goroutine must run
on the g0 stack, not on the goroutine's own stack.
Not doing this causes faults when you set
StackFromSystem = 1
StackFaultOnFree = 1
The new check in gentraceback will catch future lapses.
The more general problem is calling getcallersp but then
calling a function that might relocate the stack, which would
invalidate the result of getcallersp. Add note to stubs.go
declaration of getcallersp explaining the problem, and
check all existing calls to getcallersp. Most needed fixes.
This affects Callers, Stack, and nearly all the runtime
profiling routines. It does not affect stack copying directly
nor garbage collection.
LGTM=khr
R=khr, bradfitz
CC=golang-codereviews, r
https://golang.org/cl/167060043
2014-11-05 21:01:48 -07:00
|
|
|
})
|
|
|
|
return n
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
|
|
|
|
2015-02-24 22:41:21 -07:00
|
|
|
func gcallers(gp *g, skip int, pcbuf []uintptr) int {
|
|
|
|
return gentraceback(^uintptr(0), ^uintptr(0), 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
|
runtime: convert traceback*.c to Go
The two converted files were nearly identical.
Instead of continuing that duplication, I merged them
into a single traceback.go.
Tested on arm, amd64, amd64p32, and 386.
LGTM=r
R=golang-codereviews, remyoudompheng, dave, r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/134200044
2014-09-02 13:12:53 -06:00
|
|
|
}
|
2014-09-03 09:11:16 -06:00
|
|
|
|
2017-11-09 15:55:45 -07:00
|
|
|
func showframe(f funcInfo, gp *g, firstFrame, elideWrapper bool) bool {
|
2014-09-03 11:02:48 -06:00
|
|
|
g := getg()
|
2015-04-16 22:21:30 -06:00
|
|
|
if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) {
|
2014-09-03 11:02:48 -06:00
|
|
|
return true
|
|
|
|
}
|
2018-04-03 19:35:46 -06:00
|
|
|
return showfuncinfo(f, firstFrame, elideWrapper)
|
|
|
|
}
|
|
|
|
|
|
|
|
func showfuncinfo(f funcInfo, firstFrame, elideWrapper bool) bool {
|
2015-10-30 09:03:02 -06:00
|
|
|
level, _, _ := gotraceback()
|
2017-06-12 09:12:12 -06:00
|
|
|
if level > 1 {
|
|
|
|
// Show all frames.
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2017-11-09 15:55:45 -07:00
|
|
|
if !f.valid() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if elideWrapper {
|
|
|
|
file, _ := funcline(f, f.entry)
|
|
|
|
if file == "<autogenerated>" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-29 00:16:32 -07:00
|
|
|
name := funcname(f)
|
2014-09-03 11:02:48 -06:00
|
|
|
|
runtime: do not print runtime panic frame at top of user stack
The expected default behavior (no explicit GOTRACEBACK setting)
is for the stack trace to start in user code, eliding unnecessary runtime
frames that led up to the actual trace printing code. The idea was that
the first line number printed was the one that crashed.
For #5832 we added code to show 'panic' frames so that if code panics
and then starts running defers and then we trace from there, the panic
frame can help explain why the code seems to have made a call not
present in the code. But that's only needed for panics between two different
call frames, not the panic at the very top of the stack trace.
Fix the fix to again elide the runtime code at the very top of the stack trace.
Simple panic:
package main
func main() {
var x []int
println(x[1])
}
Before this CL:
panic: runtime error: index out of range
goroutine 1 [running]:
panic(0x1056980, 0x1091bf0)
/Users/rsc/go/src/runtime/panic.go:531 +0x1cf
main.main()
/tmp/x.go:5 +0x5
After this CL:
panic: runtime error: index out of range
goroutine 1 [running]:
main.main()
/tmp/x.go:5 +0x5
Panic inside defer triggered by panic:
package main
func main() {
var x []int
defer func() {
println(x[1])
}()
println(x[2])
}
Before this CL:
panic: runtime error: index out of range
panic: runtime error: index out of range
goroutine 1 [running]:
panic(0x1056aa0, 0x1091bf0)
/Users/rsc/go/src/runtime/panic.go:531 +0x1cf
main.main.func1(0x0, 0x0, 0x0)
/tmp/y.go:6 +0x62
panic(0x1056aa0, 0x1091bf0)
/Users/rsc/go/src/runtime/panic.go:489 +0x2cf
main.main()
/tmp/y.go:8 +0x59
The middle panic is important: it explains why main.main ended up calling main.main.func1 on a line that looks like a call to println. The top panic is noise.
After this CL:
panic: runtime error: index out of range
panic: runtime error: index out of range
goroutine 1 [running]:
main.main.func1(0x0, 0x0, 0x0)
/tmp/y.go:6 +0x62
panic(0x1056ac0, 0x1091bf0)
/Users/rsc/go/src/runtime/panic.go:489 +0x2cf
main.main()
/tmp/y.go:8 +0x59
Fixes #17901.
Change-Id: Id6d7c76373f7a658a537a39ca32b7dc23e1e76aa
Reviewed-on: https://go-review.googlesource.com/33165
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-11-12 21:01:37 -07:00
|
|
|
// Special case: always show runtime.gopanic frame
|
|
|
|
// in the middle of a stack trace, so that we can
|
|
|
|
// see the boundary between ordinary code and
|
|
|
|
// panic-induced deferred code.
|
2014-09-03 11:02:48 -06:00
|
|
|
// See golang.org/issue/5832.
|
runtime: do not print runtime panic frame at top of user stack
The expected default behavior (no explicit GOTRACEBACK setting)
is for the stack trace to start in user code, eliding unnecessary runtime
frames that led up to the actual trace printing code. The idea was that
the first line number printed was the one that crashed.
For #5832 we added code to show 'panic' frames so that if code panics
and then starts running defers and then we trace from there, the panic
frame can help explain why the code seems to have made a call not
present in the code. But that's only needed for panics between two different
call frames, not the panic at the very top of the stack trace.
Fix the fix to again elide the runtime code at the very top of the stack trace.
Simple panic:
package main
func main() {
var x []int
println(x[1])
}
Before this CL:
panic: runtime error: index out of range
goroutine 1 [running]:
panic(0x1056980, 0x1091bf0)
/Users/rsc/go/src/runtime/panic.go:531 +0x1cf
main.main()
/tmp/x.go:5 +0x5
After this CL:
panic: runtime error: index out of range
goroutine 1 [running]:
main.main()
/tmp/x.go:5 +0x5
Panic inside defer triggered by panic:
package main
func main() {
var x []int
defer func() {
println(x[1])
}()
println(x[2])
}
Before this CL:
panic: runtime error: index out of range
panic: runtime error: index out of range
goroutine 1 [running]:
panic(0x1056aa0, 0x1091bf0)
/Users/rsc/go/src/runtime/panic.go:531 +0x1cf
main.main.func1(0x0, 0x0, 0x0)
/tmp/y.go:6 +0x62
panic(0x1056aa0, 0x1091bf0)
/Users/rsc/go/src/runtime/panic.go:489 +0x2cf
main.main()
/tmp/y.go:8 +0x59
The middle panic is important: it explains why main.main ended up calling main.main.func1 on a line that looks like a call to println. The top panic is noise.
After this CL:
panic: runtime error: index out of range
panic: runtime error: index out of range
goroutine 1 [running]:
main.main.func1(0x0, 0x0, 0x0)
/tmp/y.go:6 +0x62
panic(0x1056ac0, 0x1091bf0)
/Users/rsc/go/src/runtime/panic.go:489 +0x2cf
main.main()
/tmp/y.go:8 +0x59
Fixes #17901.
Change-Id: Id6d7c76373f7a658a537a39ca32b7dc23e1e76aa
Reviewed-on: https://go-review.googlesource.com/33165
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-11-12 21:01:37 -07:00
|
|
|
if name == "runtime.gopanic" && !firstFrame {
|
2014-09-03 11:02:48 -06:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2017-11-09 15:55:45 -07:00
|
|
|
return contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name))
|
2014-09-18 18:35:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// isExportedRuntime reports whether name is an exported runtime function.
|
|
|
|
// It is only for runtime functions, so ASCII A-Z is fine.
|
|
|
|
func isExportedRuntime(name string) bool {
|
|
|
|
const n = len("runtime.")
|
|
|
|
return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z'
|
2014-09-03 11:02:48 -06:00
|
|
|
}
|
|
|
|
|
2017-11-09 15:55:45 -07:00
|
|
|
// elideWrapperCalling returns whether a wrapper function that called
|
|
|
|
// function "name" should be elided from stack traces.
|
|
|
|
func elideWrapperCalling(name string) bool {
|
|
|
|
// If the wrapper called a panic function instead of the
|
|
|
|
// wrapped function, we want to include it in stacks.
|
|
|
|
return !(name == "runtime.gopanic" || name == "runtime.sigpanic" || name == "runtime.panicwrap")
|
|
|
|
}
|
|
|
|
|
2014-09-03 09:11:16 -06:00
|
|
|
var gStatusStrings = [...]string{
|
|
|
|
_Gidle: "idle",
|
|
|
|
_Grunnable: "runnable",
|
|
|
|
_Grunning: "running",
|
|
|
|
_Gsyscall: "syscall",
|
|
|
|
_Gwaiting: "waiting",
|
|
|
|
_Gdead: "dead",
|
|
|
|
_Gcopystack: "copystack",
|
|
|
|
}
|
|
|
|
|
|
|
|
func goroutineheader(gp *g) {
|
|
|
|
gpstatus := readgstatus(gp)
|
|
|
|
|
runtime: fix, simplify, and improve scan state in goroutine header
Currently goroutineheader goes through some convolutions to *almost*
print the scan state of a G. However, the code path that would print
the scan state of the G refers to gStatusStrings where it almost
certainly meant to refer to gScanStatusStrings (which is unused), so
it winds up printing the regular status string without the scan state
either way. Furthermore, if the G is in _Gwaiting, we override the
status string and lose where this would indicate the scan state if it
worked.
This commit fixes this so the runtime prints the scan state. However,
rather than using a parallel list of status strings, this simply adds
a conditional print if the scan bit is set. This lets us remove the
string list, prints the scan state even in _Gwaiting, and lets us
strip off the scan bit at the beginning of the function, which
simplifies the rest of it.
Change-Id: Ic0adbe5c05abf4adda93da59f93b578172b28e3d
Reviewed-on: https://go-review.googlesource.com/18092
Reviewed-by: Keith Randall <khr@golang.org>
2015-12-21 12:26:33 -07:00
|
|
|
isScan := gpstatus&_Gscan != 0
|
|
|
|
gpstatus &^= _Gscan // drop the scan bit
|
|
|
|
|
2014-09-03 09:11:16 -06:00
|
|
|
// Basic string status
|
|
|
|
var status string
|
|
|
|
if 0 <= gpstatus && gpstatus < uint32(len(gStatusStrings)) {
|
|
|
|
status = gStatusStrings[gpstatus]
|
|
|
|
} else {
|
|
|
|
status = "???"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Override.
|
2018-03-13 09:20:33 -06:00
|
|
|
if gpstatus == _Gwaiting && gp.waitreason != "" {
|
|
|
|
status = gp.waitreason
|
2014-09-03 09:11:16 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// approx time the G is blocked, in minutes
|
|
|
|
var waitfor int64
|
|
|
|
if (gpstatus == _Gwaiting || gpstatus == _Gsyscall) && gp.waitsince != 0 {
|
|
|
|
waitfor = (nanotime() - gp.waitsince) / 60e9
|
|
|
|
}
|
|
|
|
print("goroutine ", gp.goid, " [", status)
|
runtime: fix, simplify, and improve scan state in goroutine header
Currently goroutineheader goes through some convolutions to *almost*
print the scan state of a G. However, the code path that would print
the scan state of the G refers to gStatusStrings where it almost
certainly meant to refer to gScanStatusStrings (which is unused), so
it winds up printing the regular status string without the scan state
either way. Furthermore, if the G is in _Gwaiting, we override the
status string and lose where this would indicate the scan state if it
worked.
This commit fixes this so the runtime prints the scan state. However,
rather than using a parallel list of status strings, this simply adds
a conditional print if the scan bit is set. This lets us remove the
string list, prints the scan state even in _Gwaiting, and lets us
strip off the scan bit at the beginning of the function, which
simplifies the rest of it.
Change-Id: Ic0adbe5c05abf4adda93da59f93b578172b28e3d
Reviewed-on: https://go-review.googlesource.com/18092
Reviewed-by: Keith Randall <khr@golang.org>
2015-12-21 12:26:33 -07:00
|
|
|
if isScan {
|
|
|
|
print(" (scan)")
|
|
|
|
}
|
2014-09-03 09:11:16 -06:00
|
|
|
if waitfor >= 1 {
|
|
|
|
print(", ", waitfor, " minutes")
|
|
|
|
}
|
2017-09-13 11:14:02 -06:00
|
|
|
if gp.lockedm != 0 {
|
2014-09-03 09:11:16 -06:00
|
|
|
print(", locked to thread")
|
|
|
|
}
|
|
|
|
print("]:\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
func tracebackothers(me *g) {
|
2015-10-30 09:03:02 -06:00
|
|
|
level, _, _ := gotraceback()
|
2014-09-03 09:11:16 -06:00
|
|
|
|
|
|
|
// Show the current goroutine first, if we haven't already.
|
|
|
|
g := getg()
|
|
|
|
gp := g.m.curg
|
|
|
|
if gp != nil && gp != me {
|
|
|
|
print("\n")
|
|
|
|
goroutineheader(gp)
|
|
|
|
traceback(^uintptr(0), ^uintptr(0), 0, gp)
|
|
|
|
}
|
|
|
|
|
|
|
|
lock(&allglock)
|
|
|
|
for _, gp := range allgs {
|
2015-02-07 05:31:18 -07:00
|
|
|
if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || isSystemGoroutine(gp) && level < 2 {
|
2014-09-03 09:11:16 -06:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
print("\n")
|
|
|
|
goroutineheader(gp)
|
2015-04-28 15:53:19 -06:00
|
|
|
// Note: gp.m == g.m occurs when tracebackothers is
|
|
|
|
// called from a signal handler initiated during a
|
2016-03-01 16:21:55 -07:00
|
|
|
// systemstack call. The original G is still in the
|
2015-04-28 15:53:19 -06:00
|
|
|
// running state, and we want to print its stack.
|
|
|
|
if gp.m != g.m && readgstatus(gp)&^_Gscan == _Grunning {
|
2014-09-03 09:11:16 -06:00
|
|
|
print("\tgoroutine running on other thread; stack unavailable\n")
|
|
|
|
printcreatedby(gp)
|
|
|
|
} else {
|
|
|
|
traceback(^uintptr(0), ^uintptr(0), 0, gp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unlock(&allglock)
|
|
|
|
}
|
|
|
|
|
2018-01-22 12:53:36 -07:00
|
|
|
// tracebackHexdump hexdumps part of stk around frame.sp and frame.fp
|
|
|
|
// for debugging purposes. If the address bad is included in the
|
|
|
|
// hexdumped range, it will mark it as well.
|
|
|
|
func tracebackHexdump(stk stack, frame *stkframe, bad uintptr) {
|
|
|
|
const expand = 32 * sys.PtrSize
|
|
|
|
const maxExpand = 256 * sys.PtrSize
|
|
|
|
// Start around frame.sp.
|
|
|
|
lo, hi := frame.sp, frame.sp
|
|
|
|
// Expand to include frame.fp.
|
|
|
|
if frame.fp != 0 && frame.fp < lo {
|
|
|
|
lo = frame.fp
|
|
|
|
}
|
|
|
|
if frame.fp != 0 && frame.fp > hi {
|
|
|
|
hi = frame.fp
|
|
|
|
}
|
|
|
|
// Expand a bit more.
|
|
|
|
lo, hi = lo-expand, hi+expand
|
|
|
|
// But don't go too far from frame.sp.
|
|
|
|
if lo < frame.sp-maxExpand {
|
|
|
|
lo = frame.sp - maxExpand
|
|
|
|
}
|
|
|
|
if hi > frame.sp+maxExpand {
|
|
|
|
hi = frame.sp + maxExpand
|
|
|
|
}
|
|
|
|
// And don't go outside the stack bounds.
|
|
|
|
if lo < stk.lo {
|
|
|
|
lo = stk.lo
|
|
|
|
}
|
|
|
|
if hi > stk.hi {
|
|
|
|
hi = stk.hi
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print the hex dump.
|
|
|
|
print("stack: frame={sp:", hex(frame.sp), ", fp:", hex(frame.fp), "} stack=[", hex(stk.lo), ",", hex(stk.hi), ")\n")
|
|
|
|
hexdumpWords(lo, hi, func(p uintptr) byte {
|
|
|
|
switch p {
|
|
|
|
case frame.fp:
|
|
|
|
return '>'
|
|
|
|
case frame.sp:
|
|
|
|
return '<'
|
|
|
|
case bad:
|
|
|
|
return '!'
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-09-03 09:11:16 -06:00
|
|
|
// Does f mark the top of a goroutine stack?
|
2018-01-30 14:01:33 -07:00
|
|
|
func topofstack(f funcInfo, g0 bool) bool {
|
2018-03-14 16:21:37 -06:00
|
|
|
return f.funcID == funcID_goexit ||
|
|
|
|
f.funcID == funcID_mstart ||
|
|
|
|
f.funcID == funcID_mcall ||
|
|
|
|
f.funcID == funcID_morestack ||
|
|
|
|
f.funcID == funcID_rt0_go ||
|
|
|
|
f.funcID == funcID_externalthreadhandler ||
|
2018-01-30 14:01:33 -07:00
|
|
|
// asmcgocall is TOS on the system stack because it
|
|
|
|
// switches to the system stack, but in this case we
|
|
|
|
// can come back to the regular stack and still want
|
|
|
|
// to be able to unwind through the call that appeared
|
|
|
|
// on the regular stack.
|
2018-03-14 16:21:37 -06:00
|
|
|
(g0 && f.funcID == funcID_asmcgocall)
|
2014-09-03 09:11:16 -06:00
|
|
|
}
|
2015-02-07 05:31:18 -07:00
|
|
|
|
2015-02-17 16:44:42 -07:00
|
|
|
// isSystemGoroutine reports whether the goroutine g must be omitted in
|
2015-02-07 05:31:18 -07:00
|
|
|
// stack dumps and deadlock detector.
|
|
|
|
func isSystemGoroutine(gp *g) bool {
|
2018-03-14 16:21:37 -06:00
|
|
|
f := findfunc(gp.startpc)
|
|
|
|
if !f.valid() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return f.funcID == funcID_runfinq && !fingRunning ||
|
|
|
|
f.funcID == funcID_bgsweep ||
|
|
|
|
f.funcID == funcID_forcegchelper ||
|
|
|
|
f.funcID == funcID_timerproc ||
|
|
|
|
f.funcID == funcID_gcBgMarkWorker
|
2015-02-07 05:31:18 -07:00
|
|
|
}
|
2015-12-11 18:16:48 -07:00
|
|
|
|
|
|
|
// SetCgoTraceback records three C functions to use to gather
|
|
|
|
// traceback information from C code and to convert that traceback
|
|
|
|
// information into symbolic information. These are used when printing
|
|
|
|
// stack traces for a program that uses cgo.
|
|
|
|
//
|
|
|
|
// The traceback and context functions may be called from a signal
|
|
|
|
// handler, and must therefore use only async-signal safe functions.
|
|
|
|
// The symbolizer function may be called while the program is
|
|
|
|
// crashing, and so must be cautious about using memory. None of the
|
|
|
|
// functions may call back into Go.
|
|
|
|
//
|
|
|
|
// The context function will be called with a single argument, a
|
|
|
|
// pointer to a struct:
|
|
|
|
//
|
2016-04-27 15:56:13 -06:00
|
|
|
// struct {
|
|
|
|
// Context uintptr
|
|
|
|
// }
|
2015-12-11 18:16:48 -07:00
|
|
|
//
|
|
|
|
// In C syntax, this struct will be
|
|
|
|
//
|
2016-04-27 15:56:13 -06:00
|
|
|
// struct {
|
|
|
|
// uintptr_t Context;
|
|
|
|
// };
|
2015-12-11 18:16:48 -07:00
|
|
|
//
|
|
|
|
// If the Context field is 0, the context function is being called to
|
2016-04-27 15:18:29 -06:00
|
|
|
// record the current traceback context. It should record in the
|
|
|
|
// Context field whatever information is needed about the current
|
|
|
|
// point of execution to later produce a stack trace, probably the
|
|
|
|
// stack pointer and PC. In this case the context function will be
|
|
|
|
// called from C code.
|
2015-12-11 18:16:48 -07:00
|
|
|
//
|
|
|
|
// If the Context field is not 0, then it is a value returned by a
|
|
|
|
// previous call to the context function. This case is called when the
|
|
|
|
// context is no longer needed; that is, when the Go code is returning
|
2016-06-01 23:43:21 -06:00
|
|
|
// to its C code caller. This permits the context function to release
|
|
|
|
// any associated resources.
|
2015-12-11 18:16:48 -07:00
|
|
|
//
|
|
|
|
// While it would be correct for the context function to record a
|
|
|
|
// complete a stack trace whenever it is called, and simply copy that
|
|
|
|
// out in the traceback function, in a typical program the context
|
|
|
|
// function will be called many times without ever recording a
|
|
|
|
// traceback for that context. Recording a complete stack trace in a
|
|
|
|
// call to the context function is likely to be inefficient.
|
|
|
|
//
|
|
|
|
// The traceback function will be called with a single argument, a
|
|
|
|
// pointer to a struct:
|
|
|
|
//
|
2016-04-27 15:56:13 -06:00
|
|
|
// struct {
|
2016-05-27 11:05:52 -06:00
|
|
|
// Context uintptr
|
|
|
|
// SigContext uintptr
|
|
|
|
// Buf *uintptr
|
|
|
|
// Max uintptr
|
2016-04-27 15:56:13 -06:00
|
|
|
// }
|
2015-12-11 18:16:48 -07:00
|
|
|
//
|
|
|
|
// In C syntax, this struct will be
|
|
|
|
//
|
2016-04-27 15:56:13 -06:00
|
|
|
// struct {
|
|
|
|
// uintptr_t Context;
|
2016-05-27 11:05:52 -06:00
|
|
|
// uintptr_t SigContext;
|
2016-04-27 15:56:13 -06:00
|
|
|
// uintptr_t* Buf;
|
|
|
|
// uintptr_t Max;
|
|
|
|
// };
|
2015-12-11 18:16:48 -07:00
|
|
|
//
|
|
|
|
// The Context field will be zero to gather a traceback from the
|
|
|
|
// current program execution point. In this case, the traceback
|
|
|
|
// function will be called from C code.
|
|
|
|
//
|
|
|
|
// Otherwise Context will be a value previously returned by a call to
|
|
|
|
// the context function. The traceback function should gather a stack
|
|
|
|
// trace from that saved point in the program execution. The traceback
|
|
|
|
// function may be called from an execution thread other than the one
|
|
|
|
// that recorded the context, but only when the context is known to be
|
|
|
|
// valid and unchanging. The traceback function may also be called
|
|
|
|
// deeper in the call stack on the same thread that recorded the
|
|
|
|
// context. The traceback function may be called multiple times with
|
|
|
|
// the same Context value; it will usually be appropriate to cache the
|
|
|
|
// result, if possible, the first time this is called for a specific
|
|
|
|
// context value.
|
|
|
|
//
|
2016-05-27 11:05:52 -06:00
|
|
|
// If the traceback function is called from a signal handler on a Unix
|
|
|
|
// system, SigContext will be the signal context argument passed to
|
|
|
|
// the signal handler (a C ucontext_t* cast to uintptr_t). This may be
|
|
|
|
// used to start tracing at the point where the signal occurred. If
|
|
|
|
// the traceback function is not called from a signal handler,
|
|
|
|
// SigContext will be zero.
|
|
|
|
//
|
2015-12-11 18:16:48 -07:00
|
|
|
// Buf is where the traceback information should be stored. It should
|
|
|
|
// be PC values, such that Buf[0] is the PC of the caller, Buf[1] is
|
|
|
|
// the PC of that function's caller, and so on. Max is the maximum
|
|
|
|
// number of entries to store. The function should store a zero to
|
|
|
|
// indicate the top of the stack, or that the caller is on a different
|
|
|
|
// stack, presumably a Go stack.
|
|
|
|
//
|
|
|
|
// Unlike runtime.Callers, the PC values returned should, when passed
|
|
|
|
// to the symbolizer function, return the file/line of the call
|
|
|
|
// instruction. No additional subtraction is required or appropriate.
|
|
|
|
//
|
|
|
|
// The symbolizer function will be called with a single argument, a
|
|
|
|
// pointer to a struct:
|
|
|
|
//
|
2016-04-27 15:56:13 -06:00
|
|
|
// struct {
|
|
|
|
// PC uintptr // program counter to fetch information for
|
|
|
|
// File *byte // file name (NUL terminated)
|
|
|
|
// Lineno uintptr // line number
|
|
|
|
// Func *byte // function name (NUL terminated)
|
|
|
|
// Entry uintptr // function entry point
|
|
|
|
// More uintptr // set non-zero if more info for this PC
|
|
|
|
// Data uintptr // unused by runtime, available for function
|
|
|
|
// }
|
2015-12-11 18:16:48 -07:00
|
|
|
//
|
|
|
|
// In C syntax, this struct will be
|
|
|
|
//
|
2016-04-27 15:56:13 -06:00
|
|
|
// struct {
|
|
|
|
// uintptr_t PC;
|
|
|
|
// char* File;
|
|
|
|
// uintptr_t Lineno;
|
|
|
|
// char* Func;
|
|
|
|
// uintptr_t Entry;
|
|
|
|
// uintptr_t More;
|
|
|
|
// uintptr_t Data;
|
|
|
|
// };
|
2015-12-11 18:16:48 -07:00
|
|
|
//
|
|
|
|
// The PC field will be a value returned by a call to the traceback
|
|
|
|
// function.
|
|
|
|
//
|
|
|
|
// The first time the function is called for a particular traceback,
|
|
|
|
// all the fields except PC will be 0. The function should fill in the
|
|
|
|
// other fields if possible, setting them to 0/nil if the information
|
|
|
|
// is not available. The Data field may be used to store any useful
|
|
|
|
// information across calls. The More field should be set to non-zero
|
|
|
|
// if there is more information for this PC, zero otherwise. If More
|
|
|
|
// is set non-zero, the function will be called again with the same
|
|
|
|
// PC, and may return different information (this is intended for use
|
|
|
|
// with inlined functions). If More is zero, the function will be
|
|
|
|
// called with the next PC value in the traceback. When the traceback
|
|
|
|
// is complete, the function will be called once more with PC set to
|
|
|
|
// zero; this may be used to free any information. Each call will
|
|
|
|
// leave the fields of the struct set to the same values they had upon
|
|
|
|
// return, except for the PC field when the More field is zero. The
|
|
|
|
// function must not keep a copy of the struct pointer between calls.
|
|
|
|
//
|
|
|
|
// When calling SetCgoTraceback, the version argument is the version
|
|
|
|
// number of the structs that the functions expect to receive.
|
|
|
|
// Currently this must be zero.
|
|
|
|
//
|
|
|
|
// The symbolizer function may be nil, in which case the results of
|
|
|
|
// the traceback function will be displayed as numbers. If the
|
|
|
|
// traceback function is nil, the symbolizer function will never be
|
|
|
|
// called. The context function may be nil, in which case the
|
|
|
|
// traceback function will only be called with the context field set
|
|
|
|
// to zero. If the context function is nil, then calls from Go to C
|
|
|
|
// to Go will not show a traceback for the C portion of the call stack.
|
2016-06-02 13:01:03 -06:00
|
|
|
//
|
|
|
|
// SetCgoTraceback should be called only once, ideally from an init function.
|
2015-12-11 18:16:48 -07:00
|
|
|
func SetCgoTraceback(version int, traceback, context, symbolizer unsafe.Pointer) {
|
|
|
|
if version != 0 {
|
|
|
|
panic("unsupported version")
|
|
|
|
}
|
2016-04-27 15:18:29 -06:00
|
|
|
|
2016-06-02 13:01:03 -06:00
|
|
|
if cgoTraceback != nil && cgoTraceback != traceback ||
|
|
|
|
cgoContext != nil && cgoContext != context ||
|
|
|
|
cgoSymbolizer != nil && cgoSymbolizer != symbolizer {
|
|
|
|
panic("call SetCgoTraceback only once")
|
|
|
|
}
|
|
|
|
|
2015-12-11 18:16:48 -07:00
|
|
|
cgoTraceback = traceback
|
2016-06-02 13:01:03 -06:00
|
|
|
cgoContext = context
|
2015-12-11 18:16:48 -07:00
|
|
|
cgoSymbolizer = symbolizer
|
2016-04-27 15:18:29 -06:00
|
|
|
|
|
|
|
// The context function is called when a C function calls a Go
|
|
|
|
// function. As such it is only called by C code in runtime/cgo.
|
|
|
|
if _cgo_set_context_function != nil {
|
|
|
|
cgocall(_cgo_set_context_function, context)
|
|
|
|
}
|
2015-12-11 18:16:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
var cgoTraceback unsafe.Pointer
|
2016-06-02 13:01:03 -06:00
|
|
|
var cgoContext unsafe.Pointer
|
2015-12-11 18:16:48 -07:00
|
|
|
var cgoSymbolizer unsafe.Pointer
|
|
|
|
|
|
|
|
// cgoTracebackArg is the type passed to cgoTraceback.
|
|
|
|
type cgoTracebackArg struct {
|
2016-05-27 11:05:52 -06:00
|
|
|
context uintptr
|
|
|
|
sigContext uintptr
|
|
|
|
buf *uintptr
|
|
|
|
max uintptr
|
2015-12-11 18:16:48 -07:00
|
|
|
}
|
|
|
|
|
2016-04-27 15:18:29 -06:00
|
|
|
// cgoContextArg is the type passed to the context function.
|
2015-12-11 18:16:48 -07:00
|
|
|
type cgoContextArg struct {
|
|
|
|
context uintptr
|
|
|
|
}
|
|
|
|
|
|
|
|
// cgoSymbolizerArg is the type passed to cgoSymbolizer.
|
|
|
|
type cgoSymbolizerArg struct {
|
|
|
|
pc uintptr
|
|
|
|
file *byte
|
|
|
|
lineno uintptr
|
|
|
|
funcName *byte
|
|
|
|
entry uintptr
|
|
|
|
more uintptr
|
|
|
|
data uintptr
|
|
|
|
}
|
|
|
|
|
|
|
|
// cgoTraceback prints a traceback of callers.
|
|
|
|
func printCgoTraceback(callers *cgoCallers) {
|
|
|
|
if cgoSymbolizer == nil {
|
|
|
|
for _, c := range callers {
|
|
|
|
if c == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
print("non-Go function at pc=", hex(c), "\n")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var arg cgoSymbolizerArg
|
|
|
|
for _, c := range callers {
|
|
|
|
if c == 0 {
|
|
|
|
break
|
|
|
|
}
|
2016-04-27 15:18:29 -06:00
|
|
|
printOneCgoTraceback(c, 0x7fffffff, &arg)
|
2015-12-11 18:16:48 -07:00
|
|
|
}
|
|
|
|
arg.pc = 0
|
2016-04-27 15:18:29 -06:00
|
|
|
callCgoSymbolizer(&arg)
|
|
|
|
}
|
|
|
|
|
|
|
|
// printOneCgoTraceback prints the traceback of a single cgo caller.
|
|
|
|
// This can print more than one line because of inlining.
|
|
|
|
// Returns the number of frames printed.
|
|
|
|
func printOneCgoTraceback(pc uintptr, max int, arg *cgoSymbolizerArg) int {
|
|
|
|
c := 0
|
|
|
|
arg.pc = pc
|
|
|
|
for {
|
|
|
|
if c > max {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
callCgoSymbolizer(arg)
|
|
|
|
if arg.funcName != nil {
|
|
|
|
// Note that we don't print any argument
|
|
|
|
// information here, not even parentheses.
|
|
|
|
// The symbolizer must add that if appropriate.
|
|
|
|
println(gostringnocopy(arg.funcName))
|
|
|
|
} else {
|
|
|
|
println("non-Go function")
|
|
|
|
}
|
|
|
|
print("\t")
|
|
|
|
if arg.file != nil {
|
|
|
|
print(gostringnocopy(arg.file), ":", arg.lineno, " ")
|
|
|
|
}
|
2016-05-18 16:20:56 -06:00
|
|
|
print("pc=", hex(pc), "\n")
|
2016-04-27 15:18:29 -06:00
|
|
|
c++
|
|
|
|
if arg.more == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// callCgoSymbolizer calls the cgoSymbolizer function.
|
|
|
|
func callCgoSymbolizer(arg *cgoSymbolizerArg) {
|
|
|
|
call := cgocall
|
|
|
|
if panicking > 0 || getg().m.curg != getg() {
|
|
|
|
// We do not want to call into the scheduler when panicking
|
|
|
|
// or when on the system stack.
|
|
|
|
call = asmcgocall
|
|
|
|
}
|
2016-07-10 19:44:09 -06:00
|
|
|
if msanenabled {
|
|
|
|
msanwrite(unsafe.Pointer(arg), unsafe.Sizeof(cgoSymbolizerArg{}))
|
|
|
|
}
|
2016-04-27 15:18:29 -06:00
|
|
|
call(cgoSymbolizer, noescape(unsafe.Pointer(arg)))
|
|
|
|
}
|
|
|
|
|
|
|
|
// cgoContextPCs gets the PC values from a cgo traceback.
|
|
|
|
func cgoContextPCs(ctxt uintptr, buf []uintptr) {
|
|
|
|
if cgoTraceback == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
call := cgocall
|
|
|
|
if panicking > 0 || getg().m.curg != getg() {
|
|
|
|
// We do not want to call into the scheduler when panicking
|
|
|
|
// or when on the system stack.
|
|
|
|
call = asmcgocall
|
|
|
|
}
|
|
|
|
arg := cgoTracebackArg{
|
|
|
|
context: ctxt,
|
|
|
|
buf: (*uintptr)(noescape(unsafe.Pointer(&buf[0]))),
|
|
|
|
max: uintptr(len(buf)),
|
|
|
|
}
|
2016-07-10 19:44:09 -06:00
|
|
|
if msanenabled {
|
|
|
|
msanwrite(unsafe.Pointer(&arg), unsafe.Sizeof(arg))
|
|
|
|
}
|
2016-04-27 15:18:29 -06:00
|
|
|
call(cgoTraceback, noescape(unsafe.Pointer(&arg)))
|
2015-12-11 18:16:48 -07:00
|
|
|
}
|