github.com/c0deoo1/golang1.5@v0.0.0-20220525150107-c87c805d4593/src/runtime/heapdump.go (about)

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