Source file
src/runtime/heapdump.go
Documentation: runtime
1
2
3
4
5
6
7
8
9
10
11
12 package runtime
13
14 import (
15 "runtime/internal/sys"
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*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
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 += sys.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 += sys.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 += sys.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)*sys.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 dumpint(uint64(uintptr(unsafe.Pointer(d.fn))))
385 if d.fn == nil {
386
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)
401 dumpint(uint64(uintptr(unsafe.Pointer(p.link))))
402 }
403 }
404
405 func dumpgs() {
406 assertWorldStopped()
407
408
409 forEachG(func(gp *g) {
410 status := readgstatus(gp)
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
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
436 assertWorldStopped()
437
438
439
440 dumpint(tagData)
441 dumpint(uint64(firstmoduledata.data))
442 dumpmemrange(unsafe.Pointer(firstmoduledata.data), firstmoduledata.edata-firstmoduledata.data)
443 dumpfields(firstmoduledata.gcdatamask)
444
445
446 dumpint(tagBSS)
447 dumpint(uint64(firstmoduledata.bss))
448 dumpmemrange(unsafe.Pointer(firstmoduledata.bss), firstmoduledata.ebss-firstmoduledata.bss)
449 dumpfields(firstmoduledata.gcbssmask)
450
451
452 for _, s := range mheap_.allspans {
453 if s.state.get() == mSpanInUse {
454
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
467 iterate_finq(finq_callback)
468 }
469
470
471
472 var freemark [_PageSize / 8]bool
473
474 func dumpobjs() {
475
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)
510 } else {
511 dumpbool(true)
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
561 func dumpmemstats(m *MemStats) {
562 assertWorldStopped()
563
564
565
566
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
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
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
700
701
702 updatememstats()
703
704
705 dumpfd = fd
706
707
708 mdump(m)
709
710
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
721 func dumpfields(bv bitvector) {
722 dumpbv(&bv, 0)
723 dumpint(fieldKindEol)
724 }
725
726 func makeheapobjbv(p uintptr, size uintptr) bitvector {
727
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
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
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