Source file src/pkg/runtime/heapdump.go
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 systemstack(func() {
24 writeheapdump_m(fd)
25 })
26
27 startTheWorld()
28 }
29
30 const (
31 fieldKindEol = 0
32 fieldKindPtr = 1
33 fieldKindIface = 2
34 fieldKindEface = 3
35 tagEOF = 0
36 tagObject = 1
37 tagOtherRoot = 2
38 tagType = 3
39 tagGoroutine = 4
40 tagStackFrame = 5
41 tagParams = 6
42 tagFinalizer = 7
43 tagItab = 8
44 tagOSThread = 9
45 tagMemStats = 10
46 tagQueuedFinalizer = 11
47 tagData = 12
48 tagBSS = 13
49 tagDefer = 14
50 tagPanic = 15
51 tagMemProf = 16
52 tagAllocSample = 17
53 )
54
55 var dumpfd uintptr
56 var tmpbuf []byte
57
58
59 const (
60 bufSize = 4096
61 )
62
63 var buf [bufSize]byte
64 var nbuf uintptr
65
66 func dwrite(data unsafe.Pointer, len uintptr) {
67 if len == 0 {
68 return
69 }
70 if nbuf+len <= bufSize {
71 copy(buf[nbuf:], (*[bufSize]byte)(data)[:len])
72 nbuf += len
73 return
74 }
75
76 write(dumpfd, unsafe.Pointer(&buf), int32(nbuf))
77 if len >= bufSize {
78 write(dumpfd, data, int32(len))
79 nbuf = 0
80 } else {
81 copy(buf[:], (*[bufSize]byte)(data)[:len])
82 nbuf = len
83 }
84 }
85
86 func dwritebyte(b byte) {
87 dwrite(unsafe.Pointer(&b), 1)
88 }
89
90 func flush() {
91 write(dumpfd, unsafe.Pointer(&buf), int32(nbuf))
92 nbuf = 0
93 }
94
95
96
97
98
99
100
101 const (
102 typeCacheBuckets = 256
103 typeCacheAssoc = 4
104 )
105
106 type typeCacheBucket struct {
107 t [typeCacheAssoc]*_type
108 }
109
110 var typecache [typeCacheBuckets]typeCacheBucket
111
112
113 func dumpint(v uint64) {
114 var buf [10]byte
115 var n int
116 for v >= 0x80 {
117 buf[n] = byte(v | 0x80)
118 n++
119 v >>= 7
120 }
121 buf[n] = byte(v)
122 n++
123 dwrite(unsafe.Pointer(&buf), uintptr(n))
124 }
125
126 func dumpbool(b bool) {
127 if b {
128 dumpint(1)
129 } else {
130 dumpint(0)
131 }
132 }
133
134
135 func dumpmemrange(data unsafe.Pointer, len uintptr) {
136 dumpint(uint64(len))
137 dwrite(data, len)
138 }
139
140 func dumpslice(b []byte) {
141 dumpint(uint64(len(b)))
142 if len(b) > 0 {
143 dwrite(unsafe.Pointer(&b[0]), uintptr(len(b)))
144 }
145 }
146
147 func dumpstr(s string) {
148 sp := stringStructOf(&s)
149 dumpmemrange(sp.str, uintptr(sp.len))
150 }
151
152
153 func dumptype(t *_type) {
154 if t == nil {
155 return
156 }
157
158
159
160 b := &typecache[t.hash&(typeCacheBuckets-1)]
161 if t == b.t[0] {
162 return
163 }
164 for i := 1; i < typeCacheAssoc; i++ {
165 if t == b.t[i] {
166
167 for j := i; j > 0; j-- {
168 b.t[j] = b.t[j-1]
169 }
170 b.t[0] = t
171 return
172 }
173 }
174
175
176
177 for j := typeCacheAssoc - 1; j > 0; j-- {
178 b.t[j] = b.t[j-1]
179 }
180 b.t[0] = t
181
182
183 dumpint(tagType)
184 dumpint(uint64(uintptr(unsafe.Pointer(t))))
185 dumpint(uint64(t.size))
186 if x := t.uncommon(); x == nil || t.nameOff(x.pkgpath).name() == "" {
187 dumpstr(t.string())
188 } else {
189 pkgpathstr := t.nameOff(x.pkgpath).name()
190 pkgpath := stringStructOf(&pkgpathstr)
191 namestr := t.name()
192 name := stringStructOf(&namestr)
193 dumpint(uint64(uintptr(pkgpath.len) + 1 + uintptr(name.len)))
194 dwrite(pkgpath.str, uintptr(pkgpath.len))
195 dwritebyte('.')
196 dwrite(name.str, uintptr(name.len))
197 }
198 dumpbool(t.kind&kindDirectIface == 0 || t.ptrdata != 0)
199 }
200
201
202 func dumpobj(obj unsafe.Pointer, size uintptr, bv bitvector) {
203 dumpint(tagObject)
204 dumpint(uint64(uintptr(obj)))
205 dumpmemrange(obj, size)
206 dumpfields(bv)
207 }
208
209 func dumpotherroot(description string, to unsafe.Pointer) {
210 dumpint(tagOtherRoot)
211 dumpstr(description)
212 dumpint(uint64(uintptr(to)))
213 }
214
215 func dumpfinalizer(obj unsafe.Pointer, fn *funcval, fint *_type, ot *ptrtype) {
216 dumpint(tagFinalizer)
217 dumpint(uint64(uintptr(obj)))
218 dumpint(uint64(uintptr(unsafe.Pointer(fn))))
219 dumpint(uint64(uintptr(unsafe.Pointer(fn.fn))))
220 dumpint(uint64(uintptr(unsafe.Pointer(fint))))
221 dumpint(uint64(uintptr(unsafe.Pointer(ot))))
222 }
223
224 type childInfo struct {
225
226
227 argoff uintptr
228 arglen uintptr
229 args bitvector
230 sp *uint8
231 depth uintptr
232 }
233
234
235 func dumpbv(cbv *bitvector, offset uintptr) {
236 for i := uintptr(0); i < uintptr(cbv.n); i++ {
237 if cbv.ptrbit(i) == 1 {
238 dumpint(fieldKindPtr)
239 dumpint(uint64(offset + i*sys.PtrSize))
240 }
241 }
242 }
243
244 func dumpframe(s *stkframe, arg unsafe.Pointer) bool {
245 child := (*childInfo)(arg)
246 f := s.fn
247
248
249 pc := s.pc
250 pcdata := int32(-1)
251 if pc != f.entry {
252 pc--
253 pcdata = pcdatavalue(f, _PCDATA_StackMapIndex, pc, nil)
254 }
255 if pcdata == -1 {
256
257
258
259 pcdata = 0
260 }
261 stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
262
263 var bv bitvector
264 if stkmap != nil && stkmap.n > 0 {
265 bv = stackmapdata(stkmap, pcdata)
266 } else {
267 bv.n = -1
268 }
269
270
271 dumpint(tagStackFrame)
272 dumpint(uint64(s.sp))
273 dumpint(uint64(child.depth))
274 dumpint(uint64(uintptr(unsafe.Pointer(child.sp))))
275 dumpmemrange(unsafe.Pointer(s.sp), s.fp-s.sp)
276 dumpint(uint64(f.entry))
277 dumpint(uint64(s.pc))
278 dumpint(uint64(s.continpc))
279 name := funcname(f)
280 if name == "" {
281 name = "unknown function"
282 }
283 dumpstr(name)
284
285
286 if child.args.n >= 0 {
287 dumpbv(&child.args, child.argoff)
288 } else {
289
290 for off := child.argoff; off < child.argoff+child.arglen; off += sys.PtrSize {
291 dumpint(fieldKindPtr)
292 dumpint(uint64(off))
293 }
294 }
295
296
297 if stkmap == nil {
298
299 for off := child.arglen; off < s.varp-s.sp; off += sys.PtrSize {
300 dumpint(fieldKindPtr)
301 dumpint(uint64(off))
302 }
303 } else if stkmap.n < 0 {
304
305 size := uintptr(-stkmap.n)
306 for off := s.varp - size - s.sp; off < s.varp-s.sp; off += sys.PtrSize {
307 dumpint(fieldKindPtr)
308 dumpint(uint64(off))
309 }
310 } else if stkmap.n > 0 {
311
312
313 dumpbv(&bv, s.varp-uintptr(bv.n)*sys.PtrSize-s.sp)
314 }
315 dumpint(fieldKindEol)
316
317
318 child.argoff = s.argp - s.fp
319 child.arglen = s.arglen
320 child.sp = (*uint8)(unsafe.Pointer(s.sp))
321 child.depth++
322 stkmap = (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
323 if stkmap != nil {
324 child.args = stackmapdata(stkmap, pcdata)
325 } else {
326 child.args.n = -1
327 }
328 return true
329 }
330
331 func dumpgoroutine(gp *g) {
332 var sp, pc, lr uintptr
333 if gp.syscallsp != 0 {
334 sp = gp.syscallsp
335 pc = gp.syscallpc
336 lr = 0
337 } else {
338 sp = gp.sched.sp
339 pc = gp.sched.pc
340 lr = gp.sched.lr
341 }
342
343 dumpint(tagGoroutine)
344 dumpint(uint64(uintptr(unsafe.Pointer(gp))))
345 dumpint(uint64(sp))
346 dumpint(uint64(gp.goid))
347 dumpint(uint64(gp.gopc))
348 dumpint(uint64(readgstatus(gp)))
349 dumpbool(isSystemGoroutine(gp, false))
350 dumpbool(false)
351 dumpint(uint64(gp.waitsince))
352 dumpstr(gp.waitreason.String())
353 dumpint(uint64(uintptr(gp.sched.ctxt)))
354 dumpint(uint64(uintptr(unsafe.Pointer(gp.m))))
355 dumpint(uint64(uintptr(unsafe.Pointer(gp._defer))))
356 dumpint(uint64(uintptr(unsafe.Pointer(gp._panic))))
357
358
359 var child childInfo
360 child.args.n = -1
361 child.arglen = 0
362 child.sp = nil
363 child.depth = 0
364 gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, dumpframe, noescape(unsafe.Pointer(&child)), 0)
365
366
367 for d := gp._defer; d != nil; d = d.link {
368 dumpint(tagDefer)
369 dumpint(uint64(uintptr(unsafe.Pointer(d))))
370 dumpint(uint64(uintptr(unsafe.Pointer(gp))))
371 dumpint(uint64(d.sp))
372 dumpint(uint64(d.pc))
373 dumpint(uint64(uintptr(unsafe.Pointer(d.fn))))
374 dumpint(uint64(uintptr(unsafe.Pointer(d.fn.fn))))
375 dumpint(uint64(uintptr(unsafe.Pointer(d.link))))
376 }
377 for p := gp._panic; p != nil; p = p.link {
378 dumpint(tagPanic)
379 dumpint(uint64(uintptr(unsafe.Pointer(p))))
380 dumpint(uint64(uintptr(unsafe.Pointer(gp))))
381 eface := efaceOf(&p.arg)
382 dumpint(uint64(uintptr(unsafe.Pointer(eface._type))))
383 dumpint(uint64(uintptr(unsafe.Pointer(eface.data))))
384 dumpint(0)
385 dumpint(uint64(uintptr(unsafe.Pointer(p.link))))
386 }
387 }
388
389 func dumpgs() {
390
391 for i := 0; uintptr(i) < allglen; i++ {
392 gp := allgs[i]
393 status := readgstatus(gp)
394 switch status {
395 default:
396 print("runtime: unexpected G.status ", hex(status), "\n")
397 throw("dumpgs in STW - bad status")
398 case _Gdead:
399
400 case _Grunnable,
401 _Gsyscall,
402 _Gwaiting:
403 dumpgoroutine(gp)
404 }
405 }
406 }
407
408 func finq_callback(fn *funcval, obj unsafe.Pointer, nret uintptr, fint *_type, ot *ptrtype) {
409 dumpint(tagQueuedFinalizer)
410 dumpint(uint64(uintptr(obj)))
411 dumpint(uint64(uintptr(unsafe.Pointer(fn))))
412 dumpint(uint64(uintptr(unsafe.Pointer(fn.fn))))
413 dumpint(uint64(uintptr(unsafe.Pointer(fint))))
414 dumpint(uint64(uintptr(unsafe.Pointer(ot))))
415 }
416
417 func dumproots() {
418
419
420 dumpint(tagData)
421 dumpint(uint64(firstmoduledata.data))
422 dumpmemrange(unsafe.Pointer(firstmoduledata.data), firstmoduledata.edata-firstmoduledata.data)
423 dumpfields(firstmoduledata.gcdatamask)
424
425
426 dumpint(tagBSS)
427 dumpint(uint64(firstmoduledata.bss))
428 dumpmemrange(unsafe.Pointer(firstmoduledata.bss), firstmoduledata.ebss-firstmoduledata.bss)
429 dumpfields(firstmoduledata.gcbssmask)
430
431
432 for _, s := range mheap_.allspans {
433 if s.state == mSpanInUse {
434
435 for sp := s.specials; sp != nil; sp = sp.next {
436 if sp.kind != _KindSpecialFinalizer {
437 continue
438 }
439 spf := (*specialfinalizer)(unsafe.Pointer(sp))
440 p := unsafe.Pointer(s.base() + uintptr(spf.special.offset))
441 dumpfinalizer(p, spf.fn, spf.fint, spf.ot)
442 }
443 }
444 }
445
446
447 iterate_finq(finq_callback)
448 }
449
450
451
452 var freemark [_PageSize / 8]bool
453
454 func dumpobjs() {
455 for _, s := range mheap_.allspans {
456 if s.state != mSpanInUse {
457 continue
458 }
459 p := s.base()
460 size := s.elemsize
461 n := (s.npages << _PageShift) / size
462 if n > uintptr(len(freemark)) {
463 throw("freemark array doesn't have enough entries")
464 }
465
466 for freeIndex := uintptr(0); freeIndex < s.nelems; freeIndex++ {
467 if s.isFree(freeIndex) {
468 freemark[freeIndex] = true
469 }
470 }
471
472 for j := uintptr(0); j < n; j, p = j+1, p+size {
473 if freemark[j] {
474 freemark[j] = false
475 continue
476 }
477 dumpobj(unsafe.Pointer(p), size, makeheapobjbv(p, size))
478 }
479 }
480 }
481
482 func dumpparams() {
483 dumpint(tagParams)
484 x := uintptr(1)
485 if *(*byte)(unsafe.Pointer(&x)) == 1 {
486 dumpbool(false)
487 } else {
488 dumpbool(true)
489 }
490 dumpint(sys.PtrSize)
491 var arenaStart, arenaEnd uintptr
492 for i1 := range mheap_.arenas {
493 if mheap_.arenas[i1] == nil {
494 continue
495 }
496 for i, ha := range mheap_.arenas[i1] {
497 if ha == nil {
498 continue
499 }
500 base := arenaBase(arenaIdx(i1)<<arenaL1Shift | arenaIdx(i))
501 if arenaStart == 0 || base < arenaStart {
502 arenaStart = base
503 }
504 if base+heapArenaBytes > arenaEnd {
505 arenaEnd = base + heapArenaBytes
506 }
507 }
508 }
509 dumpint(uint64(arenaStart))
510 dumpint(uint64(arenaEnd))
511 dumpstr(sys.GOARCH)
512 dumpstr(sys.Goexperiment)
513 dumpint(uint64(ncpu))
514 }
515
516 func itab_callback(tab *itab) {
517 t := tab._type
518 dumptype(t)
519 dumpint(tagItab)
520 dumpint(uint64(uintptr(unsafe.Pointer(tab))))
521 dumpint(uint64(uintptr(unsafe.Pointer(t))))
522 }
523
524 func dumpitabs() {
525 iterate_itabs(itab_callback)
526 }
527
528 func dumpms() {
529 for mp := allm; mp != nil; mp = mp.alllink {
530 dumpint(tagOSThread)
531 dumpint(uint64(uintptr(unsafe.Pointer(mp))))
532 dumpint(uint64(mp.id))
533 dumpint(mp.procid)
534 }
535 }
536
537 func dumpmemstats() {
538 dumpint(tagMemStats)
539 dumpint(memstats.alloc)
540 dumpint(memstats.total_alloc)
541 dumpint(memstats.sys)
542 dumpint(memstats.nlookup)
543 dumpint(memstats.nmalloc)
544 dumpint(memstats.nfree)
545 dumpint(memstats.heap_alloc)
546 dumpint(memstats.heap_sys)
547 dumpint(memstats.heap_idle)
548 dumpint(memstats.heap_inuse)
549 dumpint(memstats.heap_released)
550 dumpint(memstats.heap_objects)
551 dumpint(memstats.stacks_inuse)
552 dumpint(memstats.stacks_sys)
553 dumpint(memstats.mspan_inuse)
554 dumpint(memstats.mspan_sys)
555 dumpint(memstats.mcache_inuse)
556 dumpint(memstats.mcache_sys)
557 dumpint(memstats.buckhash_sys)
558 dumpint(memstats.gc_sys)
559 dumpint(memstats.other_sys)
560 dumpint(memstats.next_gc)
561 dumpint(memstats.last_gc_unix)
562 dumpint(memstats.pause_total_ns)
563 for i := 0; i < 256; i++ {
564 dumpint(memstats.pause_ns[i])
565 }
566 dumpint(uint64(memstats.numgc))
567 }
568
569 func dumpmemprof_callback(b *bucket, nstk uintptr, pstk *uintptr, size, allocs, frees uintptr) {
570 stk := (*[100000]uintptr)(unsafe.Pointer(pstk))
571 dumpint(tagMemProf)
572 dumpint(uint64(uintptr(unsafe.Pointer(b))))
573 dumpint(uint64(size))
574 dumpint(uint64(nstk))
575 for i := uintptr(0); i < nstk; i++ {
576 pc := stk[i]
577 f := findfunc(pc)
578 if !f.valid() {
579 var buf [64]byte
580 n := len(buf)
581 n--
582 buf[n] = ')'
583 if pc == 0 {
584 n--
585 buf[n] = '0'
586 } else {
587 for pc > 0 {
588 n--
589 buf[n] = "0123456789abcdef"[pc&15]
590 pc >>= 4
591 }
592 }
593 n--
594 buf[n] = 'x'
595 n--
596 buf[n] = '0'
597 n--
598 buf[n] = '('
599 dumpslice(buf[n:])
600 dumpstr("?")
601 dumpint(0)
602 } else {
603 dumpstr(funcname(f))
604 if i > 0 && pc > f.entry {
605 pc--
606 }
607 file, line := funcline(f, pc)
608 dumpstr(file)
609 dumpint(uint64(line))
610 }
611 }
612 dumpint(uint64(allocs))
613 dumpint(uint64(frees))
614 }
615
616 func dumpmemprof() {
617 iterate_memprof(dumpmemprof_callback)
618 for _, s := range mheap_.allspans {
619 if s.state != mSpanInUse {
620 continue
621 }
622 for sp := s.specials; sp != nil; sp = sp.next {
623 if sp.kind != _KindSpecialProfile {
624 continue
625 }
626 spp := (*specialprofile)(unsafe.Pointer(sp))
627 p := s.base() + uintptr(spp.special.offset)
628 dumpint(tagAllocSample)
629 dumpint(uint64(p))
630 dumpint(uint64(uintptr(unsafe.Pointer(spp.b))))
631 }
632 }
633 }
634
635 var dumphdr = []byte("go1.7 heap dump\n")
636
637 func mdump() {
638
639 for _, s := range mheap_.allspans {
640 if s.state == mSpanInUse {
641 s.ensureSwept()
642 }
643 }
644 memclrNoHeapPointers(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache))
645 dwrite(unsafe.Pointer(&dumphdr[0]), uintptr(len(dumphdr)))
646 dumpparams()
647 dumpitabs()
648 dumpobjs()
649 dumpgs()
650 dumpms()
651 dumproots()
652 dumpmemstats()
653 dumpmemprof()
654 dumpint(tagEOF)
655 flush()
656 }
657
658 func writeheapdump_m(fd uintptr) {
659 _g_ := getg()
660 casgstatus(_g_.m.curg, _Grunning, _Gwaiting)
661 _g_.waitreason = waitReasonDumpingHeap
662
663
664
665
666 updatememstats()
667
668
669 dumpfd = fd
670
671
672 mdump()
673
674
675 dumpfd = 0
676 if tmpbuf != nil {
677 sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys)
678 tmpbuf = nil
679 }
680
681 casgstatus(_g_.m.curg, _Gwaiting, _Grunning)
682 }
683
684
685 func dumpfields(bv bitvector) {
686 dumpbv(&bv, 0)
687 dumpint(fieldKindEol)
688 }
689
690 func makeheapobjbv(p uintptr, size uintptr) bitvector {
691
692 nptr := size / sys.PtrSize
693 if uintptr(len(tmpbuf)) < nptr/8+1 {
694 if tmpbuf != nil {
695 sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys)
696 }
697 n := nptr/8 + 1
698 p := sysAlloc(n, &memstats.other_sys)
699 if p == nil {
700 throw("heapdump: out of memory")
701 }
702 tmpbuf = (*[1 << 30]byte)(p)[:n]
703 }
704
705 for i := uintptr(0); i < nptr/8+1; i++ {
706 tmpbuf[i] = 0
707 }
708 i := uintptr(0)
709 hbits := heapBitsForAddr(p)
710 for ; i < nptr; i++ {
711 if i != 1 && !hbits.morePointers() {
712 break
713 }
714 if hbits.isPointer() {
715 tmpbuf[i/8] |= 1 << (i % 8)
716 }
717 hbits = hbits.next()
718 }
719 return bitvector{int32(i), &tmpbuf[0]}
720 }
721
View as plain text