package runtime
import (
"internal/abi"
"internal/goarch"
"unsafe"
)
func runtime_debug_WriteHeapDump (fd uintptr ) {
stopTheWorld (stwWriteHeapDump )
var m MemStats
systemstack (func () {
readmemstats_m (&m )
writeheapdump_m (fd , &m )
})
startTheWorld ()
}
const (
fieldKindEol = 0
fieldKindPtr = 1
fieldKindIface = 2
fieldKindEface = 3
tagEOF = 0
tagObject = 1
tagOtherRoot = 2
tagType = 3
tagGoroutine = 4
tagStackFrame = 5
tagParams = 6
tagFinalizer = 7
tagItab = 8
tagOSThread = 9
tagMemStats = 10
tagQueuedFinalizer = 11
tagData = 12
tagBSS = 13
tagDefer = 14
tagPanic = 15
tagMemProf = 16
tagAllocSample = 17
)
var dumpfd uintptr
var tmpbuf []byte
const (
bufSize = 4096
)
var buf [bufSize ]byte
var nbuf uintptr
func dwrite (data unsafe .Pointer , len uintptr ) {
if len == 0 {
return
}
if nbuf +len <= bufSize {
copy (buf [nbuf :], (*[bufSize ]byte )(data )[:len ])
nbuf += len
return
}
write (dumpfd , unsafe .Pointer (&buf ), int32 (nbuf ))
if len >= bufSize {
write (dumpfd , data , int32 (len ))
nbuf = 0
} else {
copy (buf [:], (*[bufSize ]byte )(data )[:len ])
nbuf = len
}
}
func dwritebyte (b byte ) {
dwrite (unsafe .Pointer (&b ), 1 )
}
func flush () {
write (dumpfd , unsafe .Pointer (&buf ), int32 (nbuf ))
nbuf = 0
}
const (
typeCacheBuckets = 256
typeCacheAssoc = 4
)
type typeCacheBucket struct {
t [typeCacheAssoc ]*_type
}
var typecache [typeCacheBuckets ]typeCacheBucket
func dumpint (v uint64 ) {
var buf [10 ]byte
var n int
for v >= 0x80 {
buf [n ] = byte (v | 0x80 )
n ++
v >>= 7
}
buf [n ] = byte (v )
n ++
dwrite (unsafe .Pointer (&buf ), uintptr (n ))
}
func dumpbool (b bool ) {
if b {
dumpint (1 )
} else {
dumpint (0 )
}
}
func dumpmemrange (data unsafe .Pointer , len uintptr ) {
dumpint (uint64 (len ))
dwrite (data , len )
}
func dumpslice (b []byte ) {
dumpint (uint64 (len (b )))
if len (b ) > 0 {
dwrite (unsafe .Pointer (&b [0 ]), uintptr (len (b )))
}
}
func dumpstr (s string ) {
dumpmemrange (unsafe .Pointer (unsafe .StringData (s )), uintptr (len (s )))
}
func dumptype (t *_type ) {
if t == nil {
return
}
b := &typecache [t .Hash &(typeCacheBuckets -1 )]
if t == b .t [0 ] {
return
}
for i := 1 ; i < typeCacheAssoc ; i ++ {
if t == b .t [i ] {
for j := i ; j > 0 ; j -- {
b .t [j ] = b .t [j -1 ]
}
b .t [0 ] = t
return
}
}
for j := typeCacheAssoc - 1 ; j > 0 ; j -- {
b .t [j ] = b .t [j -1 ]
}
b .t [0 ] = t
dumpint (tagType )
dumpint (uint64 (uintptr (unsafe .Pointer (t ))))
dumpint (uint64 (t .Size_ ))
rt := toRType (t )
if x := t .Uncommon (); x == nil || rt .nameOff (x .PkgPath ).Name () == "" {
dumpstr (rt .string ())
} else {
pkgpath := rt .nameOff (x .PkgPath ).Name ()
name := rt .name ()
dumpint (uint64 (uintptr (len (pkgpath )) + 1 + uintptr (len (name ))))
dwrite (unsafe .Pointer (unsafe .StringData (pkgpath )), uintptr (len (pkgpath )))
dwritebyte ('.' )
dwrite (unsafe .Pointer (unsafe .StringData (name )), uintptr (len (name )))
}
dumpbool (t .Kind_ &kindDirectIface == 0 || t .PtrBytes != 0 )
}
func dumpobj (obj unsafe .Pointer , size uintptr , bv bitvector ) {
dumpint (tagObject )
dumpint (uint64 (uintptr (obj )))
dumpmemrange (obj , size )
dumpfields (bv )
}
func dumpotherroot (description string , to unsafe .Pointer ) {
dumpint (tagOtherRoot )
dumpstr (description )
dumpint (uint64 (uintptr (to )))
}
func dumpfinalizer (obj unsafe .Pointer , fn *funcval , fint *_type , ot *ptrtype ) {
dumpint (tagFinalizer )
dumpint (uint64 (uintptr (obj )))
dumpint (uint64 (uintptr (unsafe .Pointer (fn ))))
dumpint (uint64 (uintptr (unsafe .Pointer (fn .fn ))))
dumpint (uint64 (uintptr (unsafe .Pointer (fint ))))
dumpint (uint64 (uintptr (unsafe .Pointer (ot ))))
}
type childInfo struct {
argoff uintptr
arglen uintptr
args bitvector
sp *uint8
depth uintptr
}
func dumpbv (cbv *bitvector , offset uintptr ) {
for i := uintptr (0 ); i < uintptr (cbv .n ); i ++ {
if cbv .ptrbit (i ) == 1 {
dumpint (fieldKindPtr )
dumpint (uint64 (offset + i *goarch .PtrSize ))
}
}
}
func dumpframe (s *stkframe , child *childInfo ) {
f := s .fn
pc := s .pc
pcdata := int32 (-1 )
if pc != f .entry () {
pc --
pcdata = pcdatavalue (f , abi .PCDATA_StackMapIndex , pc , nil )
}
if pcdata == -1 {
pcdata = 0
}
stkmap := (*stackmap )(funcdata (f , abi .FUNCDATA_LocalsPointerMaps ))
var bv bitvector
if stkmap != nil && stkmap .n > 0 {
bv = stackmapdata (stkmap , pcdata )
} else {
bv .n = -1
}
dumpint (tagStackFrame )
dumpint (uint64 (s .sp ))
dumpint (uint64 (child .depth ))
dumpint (uint64 (uintptr (unsafe .Pointer (child .sp ))))
dumpmemrange (unsafe .Pointer (s .sp ), s .fp -s .sp )
dumpint (uint64 (f .entry ()))
dumpint (uint64 (s .pc ))
dumpint (uint64 (s .continpc ))
name := funcname (f )
if name == "" {
name = "unknown function"
}
dumpstr (name )
if child .args .n >= 0 {
dumpbv (&child .args , child .argoff )
} else {
for off := child .argoff ; off < child .argoff +child .arglen ; off += goarch .PtrSize {
dumpint (fieldKindPtr )
dumpint (uint64 (off ))
}
}
if stkmap == nil {
for off := child .arglen ; off < s .varp -s .sp ; off += goarch .PtrSize {
dumpint (fieldKindPtr )
dumpint (uint64 (off ))
}
} else if stkmap .n < 0 {
size := uintptr (-stkmap .n )
for off := s .varp - size - s .sp ; off < s .varp -s .sp ; off += goarch .PtrSize {
dumpint (fieldKindPtr )
dumpint (uint64 (off ))
}
} else if stkmap .n > 0 {
dumpbv (&bv , s .varp -uintptr (bv .n )*goarch .PtrSize -s .sp )
}
dumpint (fieldKindEol )
child .argoff = s .argp - s .fp
child .arglen = s .argBytes ()
child .sp = (*uint8 )(unsafe .Pointer (s .sp ))
child .depth ++
stkmap = (*stackmap )(funcdata (f , abi .FUNCDATA_ArgsPointerMaps ))
if stkmap != nil {
child .args = stackmapdata (stkmap , pcdata )
} else {
child .args .n = -1
}
return
}
func dumpgoroutine (gp *g ) {
var sp , pc , lr uintptr
if gp .syscallsp != 0 {
sp = gp .syscallsp
pc = gp .syscallpc
lr = 0
} else {
sp = gp .sched .sp
pc = gp .sched .pc
lr = gp .sched .lr
}
dumpint (tagGoroutine )
dumpint (uint64 (uintptr (unsafe .Pointer (gp ))))
dumpint (uint64 (sp ))
dumpint (gp .goid )
dumpint (uint64 (gp .gopc ))
dumpint (uint64 (readgstatus (gp )))
dumpbool (isSystemGoroutine (gp , false ))
dumpbool (false )
dumpint (uint64 (gp .waitsince ))
dumpstr (gp .waitreason .String ())
dumpint (uint64 (uintptr (gp .sched .ctxt )))
dumpint (uint64 (uintptr (unsafe .Pointer (gp .m ))))
dumpint (uint64 (uintptr (unsafe .Pointer (gp ._defer ))))
dumpint (uint64 (uintptr (unsafe .Pointer (gp ._panic ))))
var child childInfo
child .args .n = -1
child .arglen = 0
child .sp = nil
child .depth = 0
var u unwinder
for u .initAt (pc , sp , lr , gp , 0 ); u .valid (); u .next () {
dumpframe (&u .frame , &child )
}
for d := gp ._defer ; d != nil ; d = d .link {
dumpint (tagDefer )
dumpint (uint64 (uintptr (unsafe .Pointer (d ))))
dumpint (uint64 (uintptr (unsafe .Pointer (gp ))))
dumpint (uint64 (d .sp ))
dumpint (uint64 (d .pc ))
fn := *(**funcval )(unsafe .Pointer (&d .fn ))
dumpint (uint64 (uintptr (unsafe .Pointer (fn ))))
if d .fn == nil {
dumpint (uint64 (0 ))
} else {
dumpint (uint64 (uintptr (unsafe .Pointer (fn .fn ))))
}
dumpint (uint64 (uintptr (unsafe .Pointer (d .link ))))
}
for p := gp ._panic ; p != nil ; p = p .link {
dumpint (tagPanic )
dumpint (uint64 (uintptr (unsafe .Pointer (p ))))
dumpint (uint64 (uintptr (unsafe .Pointer (gp ))))
eface := efaceOf (&p .arg )
dumpint (uint64 (uintptr (unsafe .Pointer (eface ._type ))))
dumpint (uint64 (uintptr (unsafe .Pointer (eface .data ))))
dumpint (0 )
dumpint (uint64 (uintptr (unsafe .Pointer (p .link ))))
}
}
func dumpgs () {
assertWorldStopped ()
forEachG (func (gp *g ) {
status := readgstatus (gp )
switch status {
default :
print ("runtime: unexpected G.status " , hex (status ), "\n" )
throw ("dumpgs in STW - bad status" )
case _Gdead :
case _Grunnable ,
_Gsyscall ,
_Gwaiting :
dumpgoroutine (gp )
}
})
}
func finq_callback (fn *funcval , obj unsafe .Pointer , nret uintptr , fint *_type , ot *ptrtype ) {
dumpint (tagQueuedFinalizer )
dumpint (uint64 (uintptr (obj )))
dumpint (uint64 (uintptr (unsafe .Pointer (fn ))))
dumpint (uint64 (uintptr (unsafe .Pointer (fn .fn ))))
dumpint (uint64 (uintptr (unsafe .Pointer (fint ))))
dumpint (uint64 (uintptr (unsafe .Pointer (ot ))))
}
func dumproots () {
assertWorldStopped ()
dumpint (tagData )
dumpint (uint64 (firstmoduledata .data ))
dumpmemrange (unsafe .Pointer (firstmoduledata .data ), firstmoduledata .edata -firstmoduledata .data )
dumpfields (firstmoduledata .gcdatamask )
dumpint (tagBSS )
dumpint (uint64 (firstmoduledata .bss ))
dumpmemrange (unsafe .Pointer (firstmoduledata .bss ), firstmoduledata .ebss -firstmoduledata .bss )
dumpfields (firstmoduledata .gcbssmask )
for _ , s := range mheap_ .allspans {
if s .state .get () == mSpanInUse {
for sp := s .specials ; sp != nil ; sp = sp .next {
if sp .kind != _KindSpecialFinalizer {
continue
}
spf := (*specialfinalizer )(unsafe .Pointer (sp ))
p := unsafe .Pointer (s .base () + uintptr (spf .special .offset ))
dumpfinalizer (p , spf .fn , spf .fint , spf .ot )
}
}
}
iterate_finq (finq_callback )
}
var freemark [_PageSize / 8 ]bool
func dumpobjs () {
assertWorldStopped ()
for _ , s := range mheap_ .allspans {
if s .state .get () != mSpanInUse {
continue
}
p := s .base ()
size := s .elemsize
n := (s .npages << _PageShift ) / size
if n > uintptr (len (freemark )) {
throw ("freemark array doesn't have enough entries" )
}
for freeIndex := uintptr (0 ); freeIndex < s .nelems ; freeIndex ++ {
if s .isFree (freeIndex ) {
freemark [freeIndex ] = true
}
}
for j := uintptr (0 ); j < n ; j , p = j +1 , p +size {
if freemark [j ] {
freemark [j ] = false
continue
}
dumpobj (unsafe .Pointer (p ), size , makeheapobjbv (p , size ))
}
}
}
func dumpparams () {
dumpint (tagParams )
x := uintptr (1 )
if *(*byte )(unsafe .Pointer (&x )) == 1 {
dumpbool (false )
} else {
dumpbool (true )
}
dumpint (goarch .PtrSize )
var arenaStart , arenaEnd uintptr
for i1 := range mheap_ .arenas {
if mheap_ .arenas [i1 ] == nil {
continue
}
for i , ha := range mheap_ .arenas [i1 ] {
if ha == nil {
continue
}
base := arenaBase (arenaIdx (i1 )<<arenaL1Shift | arenaIdx (i ))
if arenaStart == 0 || base < arenaStart {
arenaStart = base
}
if base +heapArenaBytes > arenaEnd {
arenaEnd = base + heapArenaBytes
}
}
}
dumpint (uint64 (arenaStart ))
dumpint (uint64 (arenaEnd ))
dumpstr (goarch .GOARCH )
dumpstr (buildVersion )
dumpint (uint64 (ncpu ))
}
func itab_callback (tab *itab ) {
t := tab ._type
dumptype (t )
dumpint (tagItab )
dumpint (uint64 (uintptr (unsafe .Pointer (tab ))))
dumpint (uint64 (uintptr (unsafe .Pointer (t ))))
}
func dumpitabs () {
iterate_itabs (itab_callback )
}
func dumpms () {
for mp := allm ; mp != nil ; mp = mp .alllink {
dumpint (tagOSThread )
dumpint (uint64 (uintptr (unsafe .Pointer (mp ))))
dumpint (uint64 (mp .id ))
dumpint (mp .procid )
}
}
func dumpmemstats (m *MemStats ) {
assertWorldStopped ()
dumpint (tagMemStats )
dumpint (m .Alloc )
dumpint (m .TotalAlloc )
dumpint (m .Sys )
dumpint (m .Lookups )
dumpint (m .Mallocs )
dumpint (m .Frees )
dumpint (m .HeapAlloc )
dumpint (m .HeapSys )
dumpint (m .HeapIdle )
dumpint (m .HeapInuse )
dumpint (m .HeapReleased )
dumpint (m .HeapObjects )
dumpint (m .StackInuse )
dumpint (m .StackSys )
dumpint (m .MSpanInuse )
dumpint (m .MSpanSys )
dumpint (m .MCacheInuse )
dumpint (m .MCacheSys )
dumpint (m .BuckHashSys )
dumpint (m .GCSys )
dumpint (m .OtherSys )
dumpint (m .NextGC )
dumpint (m .LastGC )
dumpint (m .PauseTotalNs )
for i := 0 ; i < 256 ; i ++ {
dumpint (m .PauseNs [i ])
}
dumpint (uint64 (m .NumGC ))
}
func dumpmemprof_callback (b *bucket , nstk uintptr , pstk *uintptr , size , allocs , frees uintptr ) {
stk := (*[100000 ]uintptr )(unsafe .Pointer (pstk ))
dumpint (tagMemProf )
dumpint (uint64 (uintptr (unsafe .Pointer (b ))))
dumpint (uint64 (size ))
dumpint (uint64 (nstk ))
for i := uintptr (0 ); i < nstk ; i ++ {
pc := stk [i ]
f := findfunc (pc )
if !f .valid () {
var buf [64 ]byte
n := len (buf )
n --
buf [n ] = ')'
if pc == 0 {
n --
buf [n ] = '0'
} else {
for pc > 0 {
n --
buf [n ] = "0123456789abcdef" [pc &15 ]
pc >>= 4
}
}
n --
buf [n ] = 'x'
n --
buf [n ] = '0'
n --
buf [n ] = '('
dumpslice (buf [n :])
dumpstr ("?" )
dumpint (0 )
} else {
dumpstr (funcname (f ))
if i > 0 && pc > f .entry () {
pc --
}
file , line := funcline (f , pc )
dumpstr (file )
dumpint (uint64 (line ))
}
}
dumpint (uint64 (allocs ))
dumpint (uint64 (frees ))
}
func dumpmemprof () {
assertWorldStopped ()
iterate_memprof (dumpmemprof_callback )
for _ , s := range mheap_ .allspans {
if s .state .get () != mSpanInUse {
continue
}
for sp := s .specials ; sp != nil ; sp = sp .next {
if sp .kind != _KindSpecialProfile {
continue
}
spp := (*specialprofile )(unsafe .Pointer (sp ))
p := s .base () + uintptr (spp .special .offset )
dumpint (tagAllocSample )
dumpint (uint64 (p ))
dumpint (uint64 (uintptr (unsafe .Pointer (spp .b ))))
}
}
}
var dumphdr = []byte ("go1.7 heap dump\n" )
func mdump (m *MemStats ) {
assertWorldStopped ()
for _ , s := range mheap_ .allspans {
if s .state .get () == mSpanInUse {
s .ensureSwept ()
}
}
memclrNoHeapPointers (unsafe .Pointer (&typecache ), unsafe .Sizeof (typecache ))
dwrite (unsafe .Pointer (&dumphdr [0 ]), uintptr (len (dumphdr )))
dumpparams ()
dumpitabs ()
dumpobjs ()
dumpgs ()
dumpms ()
dumproots ()
dumpmemstats (m )
dumpmemprof ()
dumpint (tagEOF )
flush ()
}
func writeheapdump_m (fd uintptr , m *MemStats ) {
assertWorldStopped ()
gp := getg ()
casGToWaiting (gp .m .curg , _Grunning , waitReasonDumpingHeap )
dumpfd = fd
mdump (m )
dumpfd = 0
if tmpbuf != nil {
sysFree (unsafe .Pointer (&tmpbuf [0 ]), uintptr (len (tmpbuf )), &memstats .other_sys )
tmpbuf = nil
}
casgstatus (gp .m .curg , _Gwaiting , _Grunning )
}
func dumpfields (bv bitvector ) {
dumpbv (&bv , 0 )
dumpint (fieldKindEol )
}
func makeheapobjbv (p uintptr , size uintptr ) bitvector {
nptr := size / goarch .PtrSize
if uintptr (len (tmpbuf )) < nptr /8 +1 {
if tmpbuf != nil {
sysFree (unsafe .Pointer (&tmpbuf [0 ]), uintptr (len (tmpbuf )), &memstats .other_sys )
}
n := nptr /8 + 1
p := sysAlloc (n , &memstats .other_sys )
if p == nil {
throw ("heapdump: out of memory" )
}
tmpbuf = (*[1 << 30 ]byte )(p )[:n ]
}
for i := uintptr (0 ); i < nptr /8 +1 ; i ++ {
tmpbuf [i ] = 0
}
hbits := heapBitsForAddr (p , size )
for {
var addr uintptr
hbits , addr = hbits .next ()
if addr == 0 {
break
}
i := (addr - p ) / goarch .PtrSize
tmpbuf [i /8 ] |= 1 << (i % 8 )
}
return bitvector {int32 (nptr ), &tmpbuf [0 ]}
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .