...

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