Home | History | Annotate | Download | only in runtime
      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.
      4 
      5 // Implementation of runtime/debug.WriteHeapDump. Writes all
      6 // objects in the heap plus additional info (roots, threads,
      7 // finalizers, etc.) to a file.
      8 
      9 // The format of the dumped file is described at
     10 // https://golang.org/s/go15heapdump.
     11 
     12 package runtime
     13 
     14 import (
     15 	"runtime/internal/sys"
     16 	"unsafe"
     17 )
     18 
     19 //go:linkname runtime_debug_WriteHeapDump runtime/debug.WriteHeapDump
     20 func runtime_debug_WriteHeapDump(fd uintptr) {
     21 	stopTheWorld("write heap dump")
     22 
     23 	systemstack(func() {
     24 		writeheapdump_m(fd)
     25 	})
     26 
     27 	startTheWorld()
     28 }
     29 
     30 const (
     31 	fieldKindEol       = 0
     32 	fieldKindPtr       = 1
     33 	fieldKindIface     = 2
     34 	fieldKindEface     = 3
     35 	tagEOF             = 0
     36 	tagObject          = 1
     37 	tagOtherRoot       = 2
     38 	tagType            = 3
     39 	tagGoroutine       = 4
     40 	tagStackFrame      = 5
     41 	tagParams          = 6
     42 	tagFinalizer       = 7
     43 	tagItab            = 8
     44 	tagOSThread        = 9
     45 	tagMemStats        = 10
     46 	tagQueuedFinalizer = 11
     47 	tagData            = 12
     48 	tagBSS             = 13
     49 	tagDefer           = 14
     50 	tagPanic           = 15
     51 	tagMemProf         = 16
     52 	tagAllocSample     = 17
     53 )
     54 
     55 var dumpfd uintptr // fd to write the dump to.
     56 var tmpbuf []byte
     57 
     58 // buffer of pending write data
     59 const (
     60 	bufSize = 4096
     61 )
     62 
     63 var buf [bufSize]byte
     64 var nbuf uintptr
     65 
     66 func dwrite(data unsafe.Pointer, len uintptr) {
     67 	if len == 0 {
     68 		return
     69 	}
     70 	if nbuf+len <= bufSize {
     71 		copy(buf[nbuf:], (*[bufSize]byte)(data)[:len])
     72 		nbuf += len
     73 		return
     74 	}
     75 
     76 	write(dumpfd, unsafe.Pointer(&buf), int32(nbuf))
     77 	if len >= bufSize {
     78 		write(dumpfd, data, int32(len))
     79 		nbuf = 0
     80 	} else {
     81 		copy(buf[:], (*[bufSize]byte)(data)[:len])
     82 		nbuf = len
     83 	}
     84 }
     85 
     86 func dwritebyte(b byte) {
     87 	dwrite(unsafe.Pointer(&b), 1)
     88 }
     89 
     90 func flush() {
     91 	write(dumpfd, unsafe.Pointer(&buf), int32(nbuf))
     92 	nbuf = 0
     93 }
     94 
     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.
    101 const (
    102 	typeCacheBuckets = 256
    103 	typeCacheAssoc   = 4
    104 )
    105 
    106 type typeCacheBucket struct {
    107 	t [typeCacheAssoc]*_type
    108 }
    109 
    110 var typecache [typeCacheBuckets]typeCacheBucket
    111 
    112 // dump a uint64 in a varint format parseable by encoding/binary
    113 func dumpint(v uint64) {
    114 	var buf [10]byte
    115 	var n int
    116 	for v >= 0x80 {
    117 		buf[n] = byte(v | 0x80)
    118 		n++
    119 		v >>= 7
    120 	}
    121 	buf[n] = byte(v)
    122 	n++
    123 	dwrite(unsafe.Pointer(&buf), uintptr(n))
    124 }
    125 
    126 func dumpbool(b bool) {
    127 	if b {
    128 		dumpint(1)
    129 	} else {
    130 		dumpint(0)
    131 	}
    132 }
    133 
    134 // dump varint uint64 length followed by memory contents
    135 func dumpmemrange(data unsafe.Pointer, len uintptr) {
    136 	dumpint(uint64(len))
    137 	dwrite(data, len)
    138 }
    139 
    140 func dumpslice(b []byte) {
    141 	dumpint(uint64(len(b)))
    142 	if len(b) > 0 {
    143 		dwrite(unsafe.Pointer(&b[0]), uintptr(len(b)))
    144 	}
    145 }
    146 
    147 func dumpstr(s string) {
    148 	sp := stringStructOf(&s)
    149 	dumpmemrange(sp.str, uintptr(sp.len))
    150 }
    151 
    152 // dump information for a type
    153 func dumptype(t *_type) {
    154 	if t == nil {
    155 		return
    156 	}
    157 
    158 	// If we've definitely serialized the type before,
    159 	// no need to do it again.
    160 	b := &typecache[t.hash&(typeCacheBuckets-1)]
    161 	if t == b.t[0] {
    162 		return
    163 	}
    164 	for i := 1; i < typeCacheAssoc; i++ {
    165 		if t == b.t[i] {
    166 			// Move-to-front
    167 			for j := i; j > 0; j-- {
    168 				b.t[j] = b.t[j-1]
    169 			}
    170 			b.t[0] = t
    171 			return
    172 		}
    173 	}
    174 
    175 	// Might not have been dumped yet. Dump it and
    176 	// remember we did so.
    177 	for j := typeCacheAssoc - 1; j > 0; j-- {
    178 		b.t[j] = b.t[j-1]
    179 	}
    180 	b.t[0] = t
    181 
    182 	// dump the type
    183 	dumpint(tagType)
    184 	dumpint(uint64(uintptr(unsafe.Pointer(t))))
    185 	dumpint(uint64(t.size))
    186 	if x := t.uncommon(); x == nil || t.nameOff(x.pkgpath).name() == "" {
    187 		dumpstr(t.string())
    188 	} else {
    189 		pkgpathstr := t.nameOff(x.pkgpath).name()
    190 		pkgpath := stringStructOf(&pkgpathstr)
    191 		namestr := t.name()
    192 		name := stringStructOf(&namestr)
    193 		dumpint(uint64(uintptr(pkgpath.len) + 1 + uintptr(name.len)))
    194 		dwrite(pkgpath.str, uintptr(pkgpath.len))
    195 		dwritebyte('.')
    196 		dwrite(name.str, uintptr(name.len))
    197 	}
    198 	dumpbool(t.kind&kindDirectIface == 0 || t.kind&kindNoPointers == 0)
    199 }
    200 
    201 // dump an object
    202 func dumpobj(obj unsafe.Pointer, size uintptr, bv bitvector) {
    203 	dumpint(tagObject)
    204 	dumpint(uint64(uintptr(obj)))
    205 	dumpmemrange(obj, size)
    206 	dumpfields(bv)
    207 }
    208 
    209 func dumpotherroot(description string, to unsafe.Pointer) {
    210 	dumpint(tagOtherRoot)
    211 	dumpstr(description)
    212 	dumpint(uint64(uintptr(to)))
    213 }
    214 
    215 func dumpfinalizer(obj unsafe.Pointer, fn *funcval, fint *_type, 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(fint))))
    221 	dumpint(uint64(uintptr(unsafe.Pointer(ot))))
    222 }
    223 
    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)
    232 }
    233 
    234 // dump kinds & offsets of interesting fields in bv
    235 func dumpbv(cbv *bitvector, offset uintptr) {
    236 	bv := gobv(*cbv)
    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))
    241 		}
    242 	}
    243 }
    244 
    245 func dumpframe(s *stkframe, arg unsafe.Pointer) bool {
    246 	child := (*childInfo)(arg)
    247 	f := s.fn
    248 
    249 	// Figure out what we can about our stack map
    250 	pc := s.pc
    251 	if pc != f.entry {
    252 		pc--
    253 	}
    254 	pcdata := pcdatavalue(f, _PCDATA_StackMapIndex, pc, nil)
    255 	if pcdata == -1 {
    256 		// We do not have a valid pcdata value but there might be a
    257 		// stackmap for this function. It is likely that we are looking
    258 		// at the function prologue, assume so and hope for the best.
    259 		pcdata = 0
    260 	}
    261 	stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
    262 
    263 	var bv bitvector
    264 	if stkmap != nil && stkmap.n > 0 {
    265 		bv = stackmapdata(stkmap, pcdata)
    266 	} else {
    267 		bv.n = -1
    268 	}
    269 
    270 	// Dump main body of stack frame.
    271 	dumpint(tagStackFrame)
    272 	dumpint(uint64(s.sp))                              // lowest address in frame
    273 	dumpint(uint64(child.depth))                       // # of frames deep on the stack
    274 	dumpint(uint64(uintptr(unsafe.Pointer(child.sp)))) // sp of child, or 0 if bottom of stack
    275 	dumpmemrange(unsafe.Pointer(s.sp), s.fp-s.sp)      // frame contents
    276 	dumpint(uint64(f.entry))
    277 	dumpint(uint64(s.pc))
    278 	dumpint(uint64(s.continpc))
    279 	name := funcname(f)
    280 	if name == "" {
    281 		name = "unknown function"
    282 	}
    283 	dumpstr(name)
    284 
    285 	// Dump fields in the outargs section
    286 	if child.args.n >= 0 {
    287 		dumpbv(&child.args, child.argoff)
    288 	} else {
    289 		// conservative - everything might be a pointer
    290 		for off := child.argoff; off < child.argoff+child.arglen; off += sys.PtrSize {
    291 			dumpint(fieldKindPtr)
    292 			dumpint(uint64(off))
    293 		}
    294 	}
    295 
    296 	// Dump fields in the local vars section
    297 	if stkmap == nil {
    298 		// No locals information, dump everything.
    299 		for off := child.arglen; off < s.varp-s.sp; off += sys.PtrSize {
    300 			dumpint(fieldKindPtr)
    301 			dumpint(uint64(off))
    302 		}
    303 	} else if stkmap.n < 0 {
    304 		// Locals size information, dump just the locals.
    305 		size := uintptr(-stkmap.n)
    306 		for off := s.varp - size - s.sp; off < s.varp-s.sp; off += sys.PtrSize {
    307 			dumpint(fieldKindPtr)
    308 			dumpint(uint64(off))
    309 		}
    310 	} else if stkmap.n > 0 {
    311 		// Locals bitmap information, scan just the pointers in
    312 		// locals.
    313 		dumpbv(&bv, s.varp-uintptr(bv.n)*sys.PtrSize-s.sp)
    314 	}
    315 	dumpint(fieldKindEol)
    316 
    317 	// Record arg info for parent.
    318 	child.argoff = s.argp - s.fp
    319 	child.arglen = s.arglen
    320 	child.sp = (*uint8)(unsafe.Pointer(s.sp))
    321 	child.depth++
    322 	stkmap = (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
    323 	if stkmap != nil {
    324 		child.args = stackmapdata(stkmap, pcdata)
    325 	} else {
    326 		child.args.n = -1
    327 	}
    328 	return true
    329 }
    330 
    331 func dumpgoroutine(gp *g) {
    332 	var sp, pc, lr uintptr
    333 	if gp.syscallsp != 0 {
    334 		sp = gp.syscallsp
    335 		pc = gp.syscallpc
    336 		lr = 0
    337 	} else {
    338 		sp = gp.sched.sp
    339 		pc = gp.sched.pc
    340 		lr = gp.sched.lr
    341 	}
    342 
    343 	dumpint(tagGoroutine)
    344 	dumpint(uint64(uintptr(unsafe.Pointer(gp))))
    345 	dumpint(uint64(sp))
    346 	dumpint(uint64(gp.goid))
    347 	dumpint(uint64(gp.gopc))
    348 	dumpint(uint64(readgstatus(gp)))
    349 	dumpbool(isSystemGoroutine(gp))
    350 	dumpbool(false) // isbackground
    351 	dumpint(uint64(gp.waitsince))
    352 	dumpstr(gp.waitreason)
    353 	dumpint(uint64(uintptr(gp.sched.ctxt)))
    354 	dumpint(uint64(uintptr(unsafe.Pointer(gp.m))))
    355 	dumpint(uint64(uintptr(unsafe.Pointer(gp._defer))))
    356 	dumpint(uint64(uintptr(unsafe.Pointer(gp._panic))))
    357 
    358 	// dump stack
    359 	var child childInfo
    360 	child.args.n = -1
    361 	child.arglen = 0
    362 	child.sp = nil
    363 	child.depth = 0
    364 	gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, dumpframe, noescape(unsafe.Pointer(&child)), 0)
    365 
    366 	// dump defer & panic records
    367 	for d := gp._defer; d != nil; d = d.link {
    368 		dumpint(tagDefer)
    369 		dumpint(uint64(uintptr(unsafe.Pointer(d))))
    370 		dumpint(uint64(uintptr(unsafe.Pointer(gp))))
    371 		dumpint(uint64(d.sp))
    372 		dumpint(uint64(d.pc))
    373 		dumpint(uint64(uintptr(unsafe.Pointer(d.fn))))
    374 		dumpint(uint64(uintptr(unsafe.Pointer(d.fn.fn))))
    375 		dumpint(uint64(uintptr(unsafe.Pointer(d.link))))
    376 	}
    377 	for p := gp._panic; p != nil; p = p.link {
    378 		dumpint(tagPanic)
    379 		dumpint(uint64(uintptr(unsafe.Pointer(p))))
    380 		dumpint(uint64(uintptr(unsafe.Pointer(gp))))
    381 		eface := efaceOf(&p.arg)
    382 		dumpint(uint64(uintptr(unsafe.Pointer(eface._type))))
    383 		dumpint(uint64(uintptr(unsafe.Pointer(eface.data))))
    384 		dumpint(0) // was p->defer, no longer recorded
    385 		dumpint(uint64(uintptr(unsafe.Pointer(p.link))))
    386 	}
    387 }
    388 
    389 func dumpgs() {
    390 	// goroutines & stacks
    391 	for i := 0; uintptr(i) < allglen; i++ {
    392 		gp := allgs[i]
    393 		status := readgstatus(gp) // The world is stopped so gp will not be in a scan state.
    394 		switch status {
    395 		default:
    396 			print("runtime: unexpected G.status ", hex(status), "\n")
    397 			throw("dumpgs in STW - bad status")
    398 		case _Gdead:
    399 			// ok
    400 		case _Grunnable,
    401 			_Gsyscall,
    402 			_Gwaiting:
    403 			dumpgoroutine(gp)
    404 		}
    405 	}
    406 }
    407 
    408 func finq_callback(fn *funcval, obj unsafe.Pointer, nret uintptr, fint *_type, ot *ptrtype) {
    409 	dumpint(tagQueuedFinalizer)
    410 	dumpint(uint64(uintptr(obj)))
    411 	dumpint(uint64(uintptr(unsafe.Pointer(fn))))
    412 	dumpint(uint64(uintptr(unsafe.Pointer(fn.fn))))
    413 	dumpint(uint64(uintptr(unsafe.Pointer(fint))))
    414 	dumpint(uint64(uintptr(unsafe.Pointer(ot))))
    415 }
    416 
    417 func dumproots() {
    418 	// TODO(mwhudson): dump datamask etc from all objects
    419 	// data segment
    420 	dumpint(tagData)
    421 	dumpint(uint64(firstmoduledata.data))
    422 	dumpmemrange(unsafe.Pointer(firstmoduledata.data), firstmoduledata.edata-firstmoduledata.data)
    423 	dumpfields(firstmoduledata.gcdatamask)
    424 
    425 	// bss segment
    426 	dumpint(tagBSS)
    427 	dumpint(uint64(firstmoduledata.bss))
    428 	dumpmemrange(unsafe.Pointer(firstmoduledata.bss), firstmoduledata.ebss-firstmoduledata.bss)
    429 	dumpfields(firstmoduledata.gcbssmask)
    430 
    431 	// MSpan.types
    432 	for _, s := range mheap_.allspans {
    433 		if s.state == _MSpanInUse {
    434 			// Finalizers
    435 			for sp := s.specials; sp != nil; sp = sp.next {
    436 				if sp.kind != _KindSpecialFinalizer {
    437 					continue
    438 				}
    439 				spf := (*specialfinalizer)(unsafe.Pointer(sp))
    440 				p := unsafe.Pointer(s.base() + uintptr(spf.special.offset))
    441 				dumpfinalizer(p, spf.fn, spf.fint, spf.ot)
    442 			}
    443 		}
    444 	}
    445 
    446 	// Finalizer queue
    447 	iterate_finq(finq_callback)
    448 }
    449 
    450 // Bit vector of free marks.
    451 // Needs to be as big as the largest number of objects per span.
    452 var freemark [_PageSize / 8]bool
    453 
    454 func dumpobjs() {
    455 	for _, s := range mheap_.allspans {
    456 		if s.state != _MSpanInUse {
    457 			continue
    458 		}
    459 		p := s.base()
    460 		size := s.elemsize
    461 		n := (s.npages << _PageShift) / size
    462 		if n > uintptr(len(freemark)) {
    463 			throw("freemark array doesn't have enough entries")
    464 		}
    465 
    466 		for freeIndex := uintptr(0); freeIndex < s.nelems; freeIndex++ {
    467 			if s.isFree(freeIndex) {
    468 				freemark[freeIndex] = true
    469 			}
    470 		}
    471 
    472 		for j := uintptr(0); j < n; j, p = j+1, p+size {
    473 			if freemark[j] {
    474 				freemark[j] = false
    475 				continue
    476 			}
    477 			dumpobj(unsafe.Pointer(p), size, makeheapobjbv(p, size))
    478 		}
    479 	}
    480 }
    481 
    482 func dumpparams() {
    483 	dumpint(tagParams)
    484 	x := uintptr(1)
    485 	if *(*byte)(unsafe.Pointer(&x)) == 1 {
    486 		dumpbool(false) // little-endian ptrs
    487 	} else {
    488 		dumpbool(true) // big-endian ptrs
    489 	}
    490 	dumpint(sys.PtrSize)
    491 	dumpint(uint64(mheap_.arena_start))
    492 	dumpint(uint64(mheap_.arena_used))
    493 	dumpstr(sys.GOARCH)
    494 	dumpstr(sys.Goexperiment)
    495 	dumpint(uint64(ncpu))
    496 }
    497 
    498 func itab_callback(tab *itab) {
    499 	t := tab._type
    500 	dumptype(t)
    501 	dumpint(tagItab)
    502 	dumpint(uint64(uintptr(unsafe.Pointer(tab))))
    503 	dumpint(uint64(uintptr(unsafe.Pointer(t))))
    504 }
    505 
    506 func dumpitabs() {
    507 	iterate_itabs(itab_callback)
    508 }
    509 
    510 func dumpms() {
    511 	for mp := allm; mp != nil; mp = mp.alllink {
    512 		dumpint(tagOSThread)
    513 		dumpint(uint64(uintptr(unsafe.Pointer(mp))))
    514 		dumpint(uint64(mp.id))
    515 		dumpint(mp.procid)
    516 	}
    517 }
    518 
    519 func dumpmemstats() {
    520 	dumpint(tagMemStats)
    521 	dumpint(memstats.alloc)
    522 	dumpint(memstats.total_alloc)
    523 	dumpint(memstats.sys)
    524 	dumpint(memstats.nlookup)
    525 	dumpint(memstats.nmalloc)
    526 	dumpint(memstats.nfree)
    527 	dumpint(memstats.heap_alloc)
    528 	dumpint(memstats.heap_sys)
    529 	dumpint(memstats.heap_idle)
    530 	dumpint(memstats.heap_inuse)
    531 	dumpint(memstats.heap_released)
    532 	dumpint(memstats.heap_objects)
    533 	dumpint(memstats.stacks_inuse)
    534 	dumpint(memstats.stacks_sys)
    535 	dumpint(memstats.mspan_inuse)
    536 	dumpint(memstats.mspan_sys)
    537 	dumpint(memstats.mcache_inuse)
    538 	dumpint(memstats.mcache_sys)
    539 	dumpint(memstats.buckhash_sys)
    540 	dumpint(memstats.gc_sys)
    541 	dumpint(memstats.other_sys)
    542 	dumpint(memstats.next_gc)
    543 	dumpint(memstats.last_gc_unix)
    544 	dumpint(memstats.pause_total_ns)
    545 	for i := 0; i < 256; i++ {
    546 		dumpint(memstats.pause_ns[i])
    547 	}
    548 	dumpint(uint64(memstats.numgc))
    549 }
    550 
    551 func dumpmemprof_callback(b *bucket, nstk uintptr, pstk *uintptr, size, allocs, frees uintptr) {
    552 	stk := (*[100000]uintptr)(unsafe.Pointer(pstk))
    553 	dumpint(tagMemProf)
    554 	dumpint(uint64(uintptr(unsafe.Pointer(b))))
    555 	dumpint(uint64(size))
    556 	dumpint(uint64(nstk))
    557 	for i := uintptr(0); i < nstk; i++ {
    558 		pc := stk[i]
    559 		f := findfunc(pc)
    560 		if !f.valid() {
    561 			var buf [64]byte
    562 			n := len(buf)
    563 			n--
    564 			buf[n] = ')'
    565 			if pc == 0 {
    566 				n--
    567 				buf[n] = '0'
    568 			} else {
    569 				for pc > 0 {
    570 					n--
    571 					buf[n] = "0123456789abcdef"[pc&15]
    572 					pc >>= 4
    573 				}
    574 			}
    575 			n--
    576 			buf[n] = 'x'
    577 			n--
    578 			buf[n] = '0'
    579 			n--
    580 			buf[n] = '('
    581 			dumpslice(buf[n:])
    582 			dumpstr("?")
    583 			dumpint(0)
    584 		} else {
    585 			dumpstr(funcname(f))
    586 			if i > 0 && pc > f.entry {
    587 				pc--
    588 			}
    589 			file, line := funcline(f, pc)
    590 			dumpstr(file)
    591 			dumpint(uint64(line))
    592 		}
    593 	}
    594 	dumpint(uint64(allocs))
    595 	dumpint(uint64(frees))
    596 }
    597 
    598 func dumpmemprof() {
    599 	iterate_memprof(dumpmemprof_callback)
    600 	for _, s := range mheap_.allspans {
    601 		if s.state != _MSpanInUse {
    602 			continue
    603 		}
    604 		for sp := s.specials; sp != nil; sp = sp.next {
    605 			if sp.kind != _KindSpecialProfile {
    606 				continue
    607 			}
    608 			spp := (*specialprofile)(unsafe.Pointer(sp))
    609 			p := s.base() + uintptr(spp.special.offset)
    610 			dumpint(tagAllocSample)
    611 			dumpint(uint64(p))
    612 			dumpint(uint64(uintptr(unsafe.Pointer(spp.b))))
    613 		}
    614 	}
    615 }
    616 
    617 var dumphdr = []byte("go1.7 heap dump\n")
    618 
    619 func mdump() {
    620 	// make sure we're done sweeping
    621 	for _, s := range mheap_.allspans {
    622 		if s.state == _MSpanInUse {
    623 			s.ensureSwept()
    624 		}
    625 	}
    626 	memclrNoHeapPointers(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache))
    627 	dwrite(unsafe.Pointer(&dumphdr[0]), uintptr(len(dumphdr)))
    628 	dumpparams()
    629 	dumpitabs()
    630 	dumpobjs()
    631 	dumpgs()
    632 	dumpms()
    633 	dumproots()
    634 	dumpmemstats()
    635 	dumpmemprof()
    636 	dumpint(tagEOF)
    637 	flush()
    638 }
    639 
    640 func writeheapdump_m(fd uintptr) {
    641 	_g_ := getg()
    642 	casgstatus(_g_.m.curg, _Grunning, _Gwaiting)
    643 	_g_.waitreason = "dumping heap"
    644 
    645 	// Update stats so we can dump them.
    646 	// As a side effect, flushes all the MCaches so the MSpan.freelist
    647 	// lists contain all the free objects.
    648 	updatememstats()
    649 
    650 	// Set dump file.
    651 	dumpfd = fd
    652 
    653 	// Call dump routine.
    654 	mdump()
    655 
    656 	// Reset dump file.
    657 	dumpfd = 0
    658 	if tmpbuf != nil {
    659 		sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys)
    660 		tmpbuf = nil
    661 	}
    662 
    663 	casgstatus(_g_.m.curg, _Gwaiting, _Grunning)
    664 }
    665 
    666 // dumpint() the kind & offset of each field in an object.
    667 func dumpfields(bv bitvector) {
    668 	dumpbv(&bv, 0)
    669 	dumpint(fieldKindEol)
    670 }
    671 
    672 func makeheapobjbv(p uintptr, size uintptr) bitvector {
    673 	// Extend the temp buffer if necessary.
    674 	nptr := size / sys.PtrSize
    675 	if uintptr(len(tmpbuf)) < nptr/8+1 {
    676 		if tmpbuf != nil {
    677 			sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys)
    678 		}
    679 		n := nptr/8 + 1
    680 		p := sysAlloc(n, &memstats.other_sys)
    681 		if p == nil {
    682 			throw("heapdump: out of memory")
    683 		}
    684 		tmpbuf = (*[1 << 30]byte)(p)[:n]
    685 	}
    686 	// Convert heap bitmap to pointer bitmap.
    687 	for i := uintptr(0); i < nptr/8+1; i++ {
    688 		tmpbuf[i] = 0
    689 	}
    690 	i := uintptr(0)
    691 	hbits := heapBitsForAddr(p)
    692 	for ; i < nptr; i++ {
    693 		if i != 1 && !hbits.morePointers() {
    694 			break // end of object
    695 		}
    696 		if hbits.isPointer() {
    697 			tmpbuf[i/8] |= 1 << (i % 8)
    698 		}
    699 		hbits = hbits.next()
    700 	}
    701 	return bitvector{int32(i), &tmpbuf[0]}
    702 }
    703