Source file src/runtime/heapdump.go

     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  	"internal/goarch"
    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*goarch.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 += goarch.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 += goarch.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 += goarch.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)*goarch.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  		fn := *(**funcval)(unsafe.Pointer(&d.fn))
   385  		dumpint(uint64(uintptr(unsafe.Pointer(fn))))
   386  		if d.fn == nil {
   387  			// d.fn can be nil for open-coded defers
   388  			dumpint(uint64(0))
   389  		} else {
   390  			dumpint(uint64(uintptr(unsafe.Pointer(fn.fn))))
   391  		}
   392  		dumpint(uint64(uintptr(unsafe.Pointer(d.link))))
   393  	}
   394  	for p := gp._panic; p != nil; p = p.link {
   395  		dumpint(tagPanic)
   396  		dumpint(uint64(uintptr(unsafe.Pointer(p))))
   397  		dumpint(uint64(uintptr(unsafe.Pointer(gp))))
   398  		eface := efaceOf(&p.arg)
   399  		dumpint(uint64(uintptr(unsafe.Pointer(eface._type))))
   400  		dumpint(uint64(uintptr(unsafe.Pointer(eface.data))))
   401  		dumpint(0) // was p->defer, no longer recorded
   402  		dumpint(uint64(uintptr(unsafe.Pointer(p.link))))
   403  	}
   404  }
   405  
   406  func dumpgs() {
   407  	assertWorldStopped()
   408  
   409  	// goroutines & stacks
   410  	forEachG(func(gp *g) {
   411  		status := readgstatus(gp) // The world is stopped so gp will not be in a scan state.
   412  		switch status {
   413  		default:
   414  			print("runtime: unexpected G.status ", hex(status), "\n")
   415  			throw("dumpgs in STW - bad status")
   416  		case _Gdead:
   417  			// ok
   418  		case _Grunnable,
   419  			_Gsyscall,
   420  			_Gwaiting:
   421  			dumpgoroutine(gp)
   422  		}
   423  	})
   424  }
   425  
   426  func finq_callback(fn *funcval, obj unsafe.Pointer, nret uintptr, fint *_type, ot *ptrtype) {
   427  	dumpint(tagQueuedFinalizer)
   428  	dumpint(uint64(uintptr(obj)))
   429  	dumpint(uint64(uintptr(unsafe.Pointer(fn))))
   430  	dumpint(uint64(uintptr(unsafe.Pointer(fn.fn))))
   431  	dumpint(uint64(uintptr(unsafe.Pointer(fint))))
   432  	dumpint(uint64(uintptr(unsafe.Pointer(ot))))
   433  }
   434  
   435  func dumproots() {
   436  	// To protect mheap_.allspans.
   437  	assertWorldStopped()
   438  
   439  	// TODO(mwhudson): dump datamask etc from all objects
   440  	// data segment
   441  	dumpint(tagData)
   442  	dumpint(uint64(firstmoduledata.data))
   443  	dumpmemrange(unsafe.Pointer(firstmoduledata.data), firstmoduledata.edata-firstmoduledata.data)
   444  	dumpfields(firstmoduledata.gcdatamask)
   445  
   446  	// bss segment
   447  	dumpint(tagBSS)
   448  	dumpint(uint64(firstmoduledata.bss))
   449  	dumpmemrange(unsafe.Pointer(firstmoduledata.bss), firstmoduledata.ebss-firstmoduledata.bss)
   450  	dumpfields(firstmoduledata.gcbssmask)
   451  
   452  	// mspan.types
   453  	for _, s := range mheap_.allspans {
   454  		if s.state.get() == mSpanInUse {
   455  			// Finalizers
   456  			for sp := s.specials; sp != nil; sp = sp.next {
   457  				if sp.kind != _KindSpecialFinalizer {
   458  					continue
   459  				}
   460  				spf := (*specialfinalizer)(unsafe.Pointer(sp))
   461  				p := unsafe.Pointer(s.base() + uintptr(spf.special.offset))
   462  				dumpfinalizer(p, spf.fn, spf.fint, spf.ot)
   463  			}
   464  		}
   465  	}
   466  
   467  	// Finalizer queue
   468  	iterate_finq(finq_callback)
   469  }
   470  
   471  // Bit vector of free marks.
   472  // Needs to be as big as the largest number of objects per span.
   473  var freemark [_PageSize / 8]bool
   474  
   475  func dumpobjs() {
   476  	// To protect mheap_.allspans.
   477  	assertWorldStopped()
   478  
   479  	for _, s := range mheap_.allspans {
   480  		if s.state.get() != mSpanInUse {
   481  			continue
   482  		}
   483  		p := s.base()
   484  		size := s.elemsize
   485  		n := (s.npages << _PageShift) / size
   486  		if n > uintptr(len(freemark)) {
   487  			throw("freemark array doesn't have enough entries")
   488  		}
   489  
   490  		for freeIndex := uintptr(0); freeIndex < s.nelems; freeIndex++ {
   491  			if s.isFree(freeIndex) {
   492  				freemark[freeIndex] = true
   493  			}
   494  		}
   495  
   496  		for j := uintptr(0); j < n; j, p = j+1, p+size {
   497  			if freemark[j] {
   498  				freemark[j] = false
   499  				continue
   500  			}
   501  			dumpobj(unsafe.Pointer(p), size, makeheapobjbv(p, size))
   502  		}
   503  	}
   504  }
   505  
   506  func dumpparams() {
   507  	dumpint(tagParams)
   508  	x := uintptr(1)
   509  	if *(*byte)(unsafe.Pointer(&x)) == 1 {
   510  		dumpbool(false) // little-endian ptrs
   511  	} else {
   512  		dumpbool(true) // big-endian ptrs
   513  	}
   514  	dumpint(goarch.PtrSize)
   515  	var arenaStart, arenaEnd uintptr
   516  	for i1 := range mheap_.arenas {
   517  		if mheap_.arenas[i1] == nil {
   518  			continue
   519  		}
   520  		for i, ha := range mheap_.arenas[i1] {
   521  			if ha == nil {
   522  				continue
   523  			}
   524  			base := arenaBase(arenaIdx(i1)<<arenaL1Shift | arenaIdx(i))
   525  			if arenaStart == 0 || base < arenaStart {
   526  				arenaStart = base
   527  			}
   528  			if base+heapArenaBytes > arenaEnd {
   529  				arenaEnd = base + heapArenaBytes
   530  			}
   531  		}
   532  	}
   533  	dumpint(uint64(arenaStart))
   534  	dumpint(uint64(arenaEnd))
   535  	dumpstr(goarch.GOARCH)
   536  	dumpstr(buildVersion)
   537  	dumpint(uint64(ncpu))
   538  }
   539  
   540  func itab_callback(tab *itab) {
   541  	t := tab._type
   542  	dumptype(t)
   543  	dumpint(tagItab)
   544  	dumpint(uint64(uintptr(unsafe.Pointer(tab))))
   545  	dumpint(uint64(uintptr(unsafe.Pointer(t))))
   546  }
   547  
   548  func dumpitabs() {
   549  	iterate_itabs(itab_callback)
   550  }
   551  
   552  func dumpms() {
   553  	for mp := allm; mp != nil; mp = mp.alllink {
   554  		dumpint(tagOSThread)
   555  		dumpint(uint64(uintptr(unsafe.Pointer(mp))))
   556  		dumpint(uint64(mp.id))
   557  		dumpint(mp.procid)
   558  	}
   559  }
   560  
   561  //go:systemstack
   562  func dumpmemstats(m *MemStats) {
   563  	assertWorldStopped()
   564  
   565  	// These ints should be identical to the exported
   566  	// MemStats structure and should be ordered the same
   567  	// way too.
   568  	dumpint(tagMemStats)
   569  	dumpint(m.Alloc)
   570  	dumpint(m.TotalAlloc)
   571  	dumpint(m.Sys)
   572  	dumpint(m.Lookups)
   573  	dumpint(m.Mallocs)
   574  	dumpint(m.Frees)
   575  	dumpint(m.HeapAlloc)
   576  	dumpint(m.HeapSys)
   577  	dumpint(m.HeapIdle)
   578  	dumpint(m.HeapInuse)
   579  	dumpint(m.HeapReleased)
   580  	dumpint(m.HeapObjects)
   581  	dumpint(m.StackInuse)
   582  	dumpint(m.StackSys)
   583  	dumpint(m.MSpanInuse)
   584  	dumpint(m.MSpanSys)
   585  	dumpint(m.MCacheInuse)
   586  	dumpint(m.MCacheSys)
   587  	dumpint(m.BuckHashSys)
   588  	dumpint(m.GCSys)
   589  	dumpint(m.OtherSys)
   590  	dumpint(m.NextGC)
   591  	dumpint(m.LastGC)
   592  	dumpint(m.PauseTotalNs)
   593  	for i := 0; i < 256; i++ {
   594  		dumpint(m.PauseNs[i])
   595  	}
   596  	dumpint(uint64(m.NumGC))
   597  }
   598  
   599  func dumpmemprof_callback(b *bucket, nstk uintptr, pstk *uintptr, size, allocs, frees uintptr) {
   600  	stk := (*[100000]uintptr)(unsafe.Pointer(pstk))
   601  	dumpint(tagMemProf)
   602  	dumpint(uint64(uintptr(unsafe.Pointer(b))))
   603  	dumpint(uint64(size))
   604  	dumpint(uint64(nstk))
   605  	for i := uintptr(0); i < nstk; i++ {
   606  		pc := stk[i]
   607  		f := findfunc(pc)
   608  		if !f.valid() {
   609  			var buf [64]byte
   610  			n := len(buf)
   611  			n--
   612  			buf[n] = ')'
   613  			if pc == 0 {
   614  				n--
   615  				buf[n] = '0'
   616  			} else {
   617  				for pc > 0 {
   618  					n--
   619  					buf[n] = "0123456789abcdef"[pc&15]
   620  					pc >>= 4
   621  				}
   622  			}
   623  			n--
   624  			buf[n] = 'x'
   625  			n--
   626  			buf[n] = '0'
   627  			n--
   628  			buf[n] = '('
   629  			dumpslice(buf[n:])
   630  			dumpstr("?")
   631  			dumpint(0)
   632  		} else {
   633  			dumpstr(funcname(f))
   634  			if i > 0 && pc > f.entry() {
   635  				pc--
   636  			}
   637  			file, line := funcline(f, pc)
   638  			dumpstr(file)
   639  			dumpint(uint64(line))
   640  		}
   641  	}
   642  	dumpint(uint64(allocs))
   643  	dumpint(uint64(frees))
   644  }
   645  
   646  func dumpmemprof() {
   647  	// To protect mheap_.allspans.
   648  	assertWorldStopped()
   649  
   650  	iterate_memprof(dumpmemprof_callback)
   651  	for _, s := range mheap_.allspans {
   652  		if s.state.get() != mSpanInUse {
   653  			continue
   654  		}
   655  		for sp := s.specials; sp != nil; sp = sp.next {
   656  			if sp.kind != _KindSpecialProfile {
   657  				continue
   658  			}
   659  			spp := (*specialprofile)(unsafe.Pointer(sp))
   660  			p := s.base() + uintptr(spp.special.offset)
   661  			dumpint(tagAllocSample)
   662  			dumpint(uint64(p))
   663  			dumpint(uint64(uintptr(unsafe.Pointer(spp.b))))
   664  		}
   665  	}
   666  }
   667  
   668  var dumphdr = []byte("go1.7 heap dump\n")
   669  
   670  func mdump(m *MemStats) {
   671  	assertWorldStopped()
   672  
   673  	// make sure we're done sweeping
   674  	for _, s := range mheap_.allspans {
   675  		if s.state.get() == mSpanInUse {
   676  			s.ensureSwept()
   677  		}
   678  	}
   679  	memclrNoHeapPointers(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache))
   680  	dwrite(unsafe.Pointer(&dumphdr[0]), uintptr(len(dumphdr)))
   681  	dumpparams()
   682  	dumpitabs()
   683  	dumpobjs()
   684  	dumpgs()
   685  	dumpms()
   686  	dumproots()
   687  	dumpmemstats(m)
   688  	dumpmemprof()
   689  	dumpint(tagEOF)
   690  	flush()
   691  }
   692  
   693  func writeheapdump_m(fd uintptr, m *MemStats) {
   694  	assertWorldStopped()
   695  
   696  	_g_ := getg()
   697  	casgstatus(_g_.m.curg, _Grunning, _Gwaiting)
   698  	_g_.waitreason = waitReasonDumpingHeap
   699  
   700  	// Set dump file.
   701  	dumpfd = fd
   702  
   703  	// Call dump routine.
   704  	mdump(m)
   705  
   706  	// Reset dump file.
   707  	dumpfd = 0
   708  	if tmpbuf != nil {
   709  		sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys)
   710  		tmpbuf = nil
   711  	}
   712  
   713  	casgstatus(_g_.m.curg, _Gwaiting, _Grunning)
   714  }
   715  
   716  // dumpint() the kind & offset of each field in an object.
   717  func dumpfields(bv bitvector) {
   718  	dumpbv(&bv, 0)
   719  	dumpint(fieldKindEol)
   720  }
   721  
   722  func makeheapobjbv(p uintptr, size uintptr) bitvector {
   723  	// Extend the temp buffer if necessary.
   724  	nptr := size / goarch.PtrSize
   725  	if uintptr(len(tmpbuf)) < nptr/8+1 {
   726  		if tmpbuf != nil {
   727  			sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys)
   728  		}
   729  		n := nptr/8 + 1
   730  		p := sysAlloc(n, &memstats.other_sys)
   731  		if p == nil {
   732  			throw("heapdump: out of memory")
   733  		}
   734  		tmpbuf = (*[1 << 30]byte)(p)[:n]
   735  	}
   736  	// Convert heap bitmap to pointer bitmap.
   737  	for i := uintptr(0); i < nptr/8+1; i++ {
   738  		tmpbuf[i] = 0
   739  	}
   740  	i := uintptr(0)
   741  	hbits := heapBitsForAddr(p)
   742  	for ; i < nptr; i++ {
   743  		if !hbits.morePointers() {
   744  			break // end of object
   745  		}
   746  		if hbits.isPointer() {
   747  			tmpbuf[i/8] |= 1 << (i % 8)
   748  		}
   749  		hbits = hbits.next()
   750  	}
   751  	return bitvector{int32(i), &tmpbuf[0]}
   752  }
   753  

View as plain text