1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Implementation of runtime/debug.WriteHeapDump. Writes all
6 // objects in the heap plus additional info (roots, threads,
7 // finalizers, etc.) to a file.
9 // The format of the dumped file is described at
10 // https://golang.org/s/go15heapdump.
15 "runtime/internal/sys"
19 //go:linkname runtime_debug_WriteHeapDump runtime_debug.WriteHeapDump
20 func runtime_debug_WriteHeapDump(fd
uintptr) {
21 stopTheWorld("write heap dump")
46 tagQueuedFinalizer
= 11
55 var dumpfd
uintptr // fd to write the dump to.
58 // buffer of pending write data
66 func dwrite(data unsafe
.Pointer
, len uintptr) {
70 if nbuf
+len <= bufSize
{
71 copy(buf
[nbuf
:], (*[bufSize
]byte)(data
)[:len])
76 write(dumpfd
, unsafe
.Pointer(&buf
), int32(nbuf
))
78 write(dumpfd
, data
, int32(len))
81 copy(buf
[:], (*[bufSize
]byte)(data
)[:len])
86 func dwritebyte(b
byte) {
87 dwrite(unsafe
.Pointer(&b
), 1)
91 write(dumpfd
, unsafe
.Pointer(&buf
), int32(nbuf
))
95 // Cache of types that have been serialized already.
96 // We use a type's hash field to pick a bucket.
97 // Inside a bucket, we keep a list of types that
98 // have been serialized so far, most recently used first.
99 // Note: when a bucket overflows we may end up
100 // serializing a type more than once. That's ok.
102 typeCacheBuckets
= 256
106 type typeCacheBucket
struct {
107 t
[typeCacheAssoc
]*_type
110 var typecache
[typeCacheBuckets
]typeCacheBucket
112 // dump a uint64 in a varint format parseable by encoding/binary
113 func dumpint(v
uint64) {
117 buf
[n
] = byte(v |
0x80)
123 dwrite(unsafe
.Pointer(&buf
), uintptr(n
))
126 func dumpbool(b
bool) {
134 // dump varint uint64 length followed by memory contents
135 func dumpmemrange(data unsafe
.Pointer
, len uintptr) {
140 func dumpslice(b
[]byte) {
141 dumpint(uint64(len(b
)))
143 dwrite(unsafe
.Pointer(&b
[0]), uintptr(len(b
)))
147 func dumpstr(s
string) {
148 sp
:= stringStructOf(&s
)
149 dumpmemrange(sp
.str
, uintptr(sp
.len))
152 // dump information for a type
153 func dumptype(t
*_type
) {
158 // If we've definitely serialized the type before,
159 // no need to do it again.
160 b
:= &typecache
[t
.hash
&(typeCacheBuckets
-1)]
164 for i
:= 1; i
< typeCacheAssoc
; i
++ {
167 for j
:= i
; j
> 0; j
-- {
175 // Might not have been dumped yet. Dump it and
176 // remember we did so.
177 for j
:= typeCacheAssoc
- 1; j
> 0; j
-- {
184 dumpint(uint64(uintptr(unsafe
.Pointer(t
))))
185 dumpint(uint64(t
.size
))
186 if x
:= t
.uncommontype
; x
== nil || t
.pkgPath
== nil ||
*t
.pkgPath
== "" {
189 pkgpathstr
:= *t
.pkgPath
190 pkgpath
:= stringStructOf(&pkgpathstr
)
192 name
:= stringStructOf(&namestr
)
193 dumpint(uint64(uintptr(pkgpath
.len) + 1 + uintptr(name
.len)))
194 dwrite(pkgpath
.str
, uintptr(pkgpath
.len))
196 dwrite(name
.str
, uintptr(name
.len))
198 dumpbool(t
.kind
&kindDirectIface
== 0 || t
.kind
&kindNoPointers
== 0)
202 func dumpobj(obj unsafe
.Pointer
, size
uintptr, bv bitvector
) {
204 dumpint(uint64(uintptr(obj
)))
205 dumpmemrange(obj
, size
)
209 func dumpotherroot(description
string, to unsafe
.Pointer
) {
210 dumpint(tagOtherRoot
)
212 dumpint(uint64(uintptr(to
)))
215 func dumpfinalizer(obj unsafe
.Pointer
, fn
*funcval
, ft
*functype
, ot
*ptrtype
) {
216 dumpint(tagFinalizer
)
217 dumpint(uint64(uintptr(obj
)))
218 dumpint(uint64(uintptr(unsafe
.Pointer(fn
))))
219 dumpint(uint64(uintptr(unsafe
.Pointer(fn
.fn
))))
220 dumpint(uint64(uintptr(unsafe
.Pointer(ft
))))
221 dumpint(uint64(uintptr(unsafe
.Pointer(ot
))))
224 type childInfo
struct {
225 // Information passed up from the callee frame about
226 // the layout of the outargs region.
227 argoff
uintptr // where the arguments start in the frame
228 arglen
uintptr // size of args region
229 args bitvector
// if args.n >= 0, pointer map of args region
230 sp
*uint8 // callee sp
231 depth
uintptr // depth in call stack (0 == most recent)
234 // dump kinds & offsets of interesting fields in bv
235 func dumpbv(cbv
*bitvector
, offset
uintptr) {
237 for i
:= uintptr(0); i
< bv
.n
; i
++ {
238 if bv
.bytedata
[i
/8]>>(i%8
)&1 == 1 {
239 dumpint(fieldKindPtr
)
240 dumpint(uint64(offset
+ i
*sys
.PtrSize
))
245 func dumpgoroutine(gp
*g
) {
248 dumpint(tagGoroutine
)
249 dumpint(uint64(uintptr(unsafe
.Pointer(gp
))))
251 dumpint(uint64(gp
.goid
))
252 dumpint(uint64(gp
.gopc
))
253 dumpint(uint64(readgstatus(gp
)))
254 dumpbool(isSystemGoroutine(gp
))
255 dumpbool(false) // isbackground
256 dumpint(uint64(gp
.waitsince
))
257 dumpstr(gp
.waitreason
)
259 dumpint(uint64(uintptr(unsafe
.Pointer(gp
.m
))))
260 dumpint(uint64(uintptr(unsafe
.Pointer(gp
._defer
))))
261 dumpint(uint64(uintptr(unsafe
.Pointer(gp
._panic
))))
263 // dump defer & panic records
264 for d
:= gp
._defer
; d
!= nil; d
= d
.link
{
266 dumpint(uint64(uintptr(unsafe
.Pointer(d
))))
267 dumpint(uint64(uintptr(unsafe
.Pointer(gp
))))
270 dumpint(uint64(uintptr(unsafe
.Pointer(d
.pfn
))))
272 dumpint(uint64(uintptr(unsafe
.Pointer(d
.link
))))
274 for p
:= gp
._panic
; p
!= nil; p
= p
.link
{
276 dumpint(uint64(uintptr(unsafe
.Pointer(p
))))
277 dumpint(uint64(uintptr(unsafe
.Pointer(gp
))))
278 eface
:= efaceOf(&p
.arg
)
279 dumpint(uint64(uintptr(unsafe
.Pointer(eface
._type
))))
280 dumpint(uint64(uintptr(unsafe
.Pointer(eface
.data
))))
281 dumpint(0) // was p->defer, no longer recorded
282 dumpint(uint64(uintptr(unsafe
.Pointer(p
.link
))))
287 // goroutines & stacks
288 for i
:= 0; uintptr(i
) < allglen
; i
++ {
290 status
:= readgstatus(gp
) // The world is stopped so gp will not be in a scan state.
293 print("runtime: unexpected G.status ", hex(status
), "\n")
294 throw("dumpgs in STW - bad status")
305 func finq_callback(fn
*funcval
, obj unsafe
.Pointer
, ft
*functype
, ot
*ptrtype
) {
306 dumpint(tagQueuedFinalizer
)
307 dumpint(uint64(uintptr(obj
)))
308 dumpint(uint64(uintptr(unsafe
.Pointer(fn
))))
309 dumpint(uint64(uintptr(unsafe
.Pointer(fn
.fn
))))
310 dumpint(uint64(uintptr(unsafe
.Pointer(ft
))))
311 dumpint(uint64(uintptr(unsafe
.Pointer(ot
))))
316 for _
, s
:= range mheap_
.allspans
{
317 if s
.state
== _MSpanInUse
{
319 for sp
:= s
.specials
; sp
!= nil; sp
= sp
.next
{
320 if sp
.kind
!= _KindSpecialFinalizer
{
323 spf
:= (*specialfinalizer
)(unsafe
.Pointer(sp
))
324 p
:= unsafe
.Pointer(s
.base() + uintptr(spf
.special
.offset
))
325 dumpfinalizer(p
, spf
.fn
, spf
.ft
, spf
.ot
)
331 iterate_finq(finq_callback
)
334 // Bit vector of free marks.
335 // Needs to be as big as the largest number of objects per span.
336 var freemark
[_PageSize
/ 8]bool
339 for _
, s
:= range mheap_
.allspans
{
340 if s
.state
!= _MSpanInUse
{
345 n
:= (s
.npages
<< _PageShift
) / size
346 if n
> uintptr(len(freemark
)) {
347 throw("freemark array doesn't have enough entries")
350 for freeIndex
:= uintptr(0); freeIndex
< s
.nelems
; freeIndex
++ {
351 if s
.isFree(freeIndex
) {
352 freemark
[freeIndex
] = true
356 for j
:= uintptr(0); j
< n
; j
, p
= j
+1, p
+size
{
361 dumpobj(unsafe
.Pointer(p
), size
, makeheapobjbv(p
, size
))
369 if *(*byte)(unsafe
.Pointer(&x
)) == 1 {
370 dumpbool(false) // little-endian ptrs
372 dumpbool(true) // big-endian ptrs
375 dumpint(uint64(mheap_
.arena_start
))
376 dumpint(uint64(mheap_
.arena_used
))
378 dumpstr(sys
.Goexperiment
)
379 dumpint(uint64(ncpu
))
383 for mp
:= allm
; mp
!= nil; mp
= mp
.alllink
{
385 dumpint(uint64(uintptr(unsafe
.Pointer(mp
))))
386 dumpint(uint64(mp
.id
))
391 func dumpmemstats() {
393 dumpint(memstats
.alloc
)
394 dumpint(memstats
.total_alloc
)
395 dumpint(memstats
.sys
)
396 dumpint(memstats
.nlookup
)
397 dumpint(memstats
.nmalloc
)
398 dumpint(memstats
.nfree
)
399 dumpint(memstats
.heap_alloc
)
400 dumpint(memstats
.heap_sys
)
401 dumpint(memstats
.heap_idle
)
402 dumpint(memstats
.heap_inuse
)
403 dumpint(memstats
.heap_released
)
404 dumpint(memstats
.heap_objects
)
405 dumpint(memstats
.stacks_inuse
)
406 dumpint(memstats
.stacks_sys
)
407 dumpint(memstats
.mspan_inuse
)
408 dumpint(memstats
.mspan_sys
)
409 dumpint(memstats
.mcache_inuse
)
410 dumpint(memstats
.mcache_sys
)
411 dumpint(memstats
.buckhash_sys
)
412 dumpint(memstats
.gc_sys
)
413 dumpint(memstats
.other_sys
)
414 dumpint(memstats
.next_gc
)
415 dumpint(memstats
.last_gc_unix
)
416 dumpint(memstats
.pause_total_ns
)
417 for i
:= 0; i
< 256; i
++ {
418 dumpint(memstats
.pause_ns
[i
])
420 dumpint(uint64(memstats
.numgc
))
423 func dumpmemprof_callback(b
*bucket
, nstk
uintptr, pstk
*location
, size
, allocs
, frees
uintptr) {
424 stk
:= (*[100000]location
)(unsafe
.Pointer(pstk
))
426 dumpint(uint64(uintptr(unsafe
.Pointer(b
))))
427 dumpint(uint64(size
))
428 dumpint(uint64(nstk
))
429 for i
:= uintptr(0); i
< nstk
; i
++ {
431 fn
:= stk
[i
].function
432 file
:= stk
[i
].filename
433 line
:= stk
[i
].lineno
445 buf
[n
] = "0123456789abcdef"[pc
&15]
461 dumpint(uint64(line
))
464 dumpint(uint64(allocs
))
465 dumpint(uint64(frees
))
469 iterate_memprof(dumpmemprof_callback
)
470 for _
, s
:= range mheap_
.allspans
{
471 if s
.state
!= _MSpanInUse
{
474 for sp
:= s
.specials
; sp
!= nil; sp
= sp
.next
{
475 if sp
.kind
!= _KindSpecialProfile
{
478 spp
:= (*specialprofile
)(unsafe
.Pointer(sp
))
479 p
:= s
.base() + uintptr(spp
.special
.offset
)
480 dumpint(tagAllocSample
)
482 dumpint(uint64(uintptr(unsafe
.Pointer(spp
.b
))))
487 var dumphdr
= []byte("go1.7 heap dump\n")
490 // make sure we're done sweeping
491 for _
, s
:= range mheap_
.allspans
{
492 if s
.state
== _MSpanInUse
{
496 memclrNoHeapPointers(unsafe
.Pointer(&typecache
), unsafe
.Sizeof(typecache
))
497 dwrite(unsafe
.Pointer(&dumphdr
[0]), uintptr(len(dumphdr
)))
509 func writeheapdump_m(fd
uintptr) {
511 casgstatus(_g_
.m
.curg
, _Grunning
, _Gwaiting
)
512 _g_
.waitreason
= "dumping heap"
514 // Update stats so we can dump them.
515 // As a side effect, flushes all the MCaches so the MSpan.freelist
516 // lists contain all the free objects.
522 // Call dump routine.
528 sysFree(unsafe
.Pointer(&tmpbuf
[0]), uintptr(len(tmpbuf
)), &memstats
.other_sys
)
532 casgstatus(_g_
.m
.curg
, _Gwaiting
, _Grunning
)
535 // dumpint() the kind & offset of each field in an object.
536 func dumpfields(bv bitvector
) {
538 dumpint(fieldKindEol
)
541 func makeheapobjbv(p
uintptr, size
uintptr) bitvector
{
542 // Extend the temp buffer if necessary.
543 nptr
:= size
/ sys
.PtrSize
544 if uintptr(len(tmpbuf
)) < nptr
/8+1 {
546 sysFree(unsafe
.Pointer(&tmpbuf
[0]), uintptr(len(tmpbuf
)), &memstats
.other_sys
)
549 p
:= sysAlloc(n
, &memstats
.other_sys
)
551 throw("heapdump: out of memory")
553 tmpbuf
= (*[1 << 30]byte)(p
)[:n
]
555 // Convert heap bitmap to pointer bitmap.
556 for i
:= uintptr(0); i
< nptr
/8+1; i
++ {
560 hbits
:= heapBitsForAddr(p
)
561 for ; i
< nptr
; i
++ {
562 if i
!= 1 && !hbits
.morePointers() {
563 break // end of object
565 if hbits
.isPointer() {
566 tmpbuf
[i
/8] |
= 1 << (i
% 8)
570 return bitvector
{int32(i
), &tmpbuf
[0]}
573 type gobitvector
struct {
578 func gobv(bv bitvector
) gobitvector
{
581 (*[1 << 30]byte)(unsafe
.Pointer(bv
.bytedata
))[:(bv
.n
+7)/8],