Black Lives Matter. Support the Equal Justice Initiative.

Source file src/runtime/heapdump.go

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

View as plain text