Source file
src/runtime/heapdump.go
1
2
3
4
5
6
7
8
9
10
11
12 package runtime
13
14 import (
15 "internal/goarch"
16 "unsafe"
17 )
18
19
20 func runtime_debug_WriteHeapDump(fd uintptr) {
21 stopTheWorld("write heap dump")
22
23
24
25
26
27
28
29 var m MemStats
30 systemstack(func() {
31
32
33
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
67 var tmpbuf []byte
68
69
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
107
108
109
110
111
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
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
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
164 func dumptype(t *_type) {
165 if t == nil {
166 return
167 }
168
169
170
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
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
187
188 for j := typeCacheAssoc - 1; j > 0; j-- {
189 b.t[j] = b.t[j-1]
190 }
191 b.t[0] = t
192
193
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
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
237
238 argoff uintptr
239 arglen uintptr
240 args bitvector
241 sp *uint8
242 depth uintptr
243 }
244
245
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
260 pc := s.pc
261 pcdata := int32(-1)
262 if pc != f.entry() {
263 pc--
264 pcdata = pcdatavalue(f, _PCDATA_StackMapIndex, pc, nil)
265 }
266 if pcdata == -1 {
267
268
269
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
282 dumpint(tagStackFrame)
283 dumpint(uint64(s.sp))
284 dumpint(uint64(child.depth))
285 dumpint(uint64(uintptr(unsafe.Pointer(child.sp))))
286 dumpmemrange(unsafe.Pointer(s.sp), s.fp-s.sp)
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
297 if child.args.n >= 0 {
298 dumpbv(&child.args, child.argoff)
299 } else {
300
301 for off := child.argoff; off < child.argoff+child.arglen; off += goarch.PtrSize {
302 dumpint(fieldKindPtr)
303 dumpint(uint64(off))
304 }
305 }
306
307
308 if stkmap == nil {
309
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
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
323
324 dumpbv(&bv, s.varp-uintptr(bv.n)*goarch.PtrSize-s.sp)
325 }
326 dumpint(fieldKindEol)
327
328
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)
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
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
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
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)
402 dumpint(uint64(uintptr(unsafe.Pointer(p.link))))
403 }
404 }
405
406 func dumpgs() {
407 assertWorldStopped()
408
409
410 forEachG(func(gp *g) {
411 status := readgstatus(gp)
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
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
437 assertWorldStopped()
438
439
440
441 dumpint(tagData)
442 dumpint(uint64(firstmoduledata.data))
443 dumpmemrange(unsafe.Pointer(firstmoduledata.data), firstmoduledata.edata-firstmoduledata.data)
444 dumpfields(firstmoduledata.gcdatamask)
445
446
447 dumpint(tagBSS)
448 dumpint(uint64(firstmoduledata.bss))
449 dumpmemrange(unsafe.Pointer(firstmoduledata.bss), firstmoduledata.ebss-firstmoduledata.bss)
450 dumpfields(firstmoduledata.gcbssmask)
451
452
453 for _, s := range mheap_.allspans {
454 if s.state.get() == mSpanInUse {
455
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
468 iterate_finq(finq_callback)
469 }
470
471
472
473 var freemark [_PageSize / 8]bool
474
475 func dumpobjs() {
476
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)
511 } else {
512 dumpbool(true)
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
562 func dumpmemstats(m *MemStats) {
563 assertWorldStopped()
564
565
566
567
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
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
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
701
702
703 updatememstats()
704
705
706 dumpfd = fd
707
708
709 mdump(m)
710
711
712 dumpfd = 0
713 if tmpbuf != nil {
714 sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys)
715 tmpbuf = nil
716 }
717
718 casgstatus(_g_.m.curg, _Gwaiting, _Grunning)
719 }
720
721
722 func dumpfields(bv bitvector) {
723 dumpbv(&bv, 0)
724 dumpint(fieldKindEol)
725 }
726
727 func makeheapobjbv(p uintptr, size uintptr) bitvector {
728
729 nptr := size / goarch.PtrSize
730 if uintptr(len(tmpbuf)) < nptr/8+1 {
731 if tmpbuf != nil {
732 sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys)
733 }
734 n := nptr/8 + 1
735 p := sysAlloc(n, &memstats.other_sys)
736 if p == nil {
737 throw("heapdump: out of memory")
738 }
739 tmpbuf = (*[1 << 30]byte)(p)[:n]
740 }
741
742 for i := uintptr(0); i < nptr/8+1; i++ {
743 tmpbuf[i] = 0
744 }
745 i := uintptr(0)
746 hbits := heapBitsForAddr(p)
747 for ; i < nptr; i++ {
748 if !hbits.morePointers() {
749 break
750 }
751 if hbits.isPointer() {
752 tmpbuf[i/8] |= 1 << (i % 8)
753 }
754 hbits = hbits.next()
755 }
756 return bitvector{int32(i), &tmpbuf[0]}
757 }
758
View as plain text