]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/heapdump.go
2394a3e9eb3a1522f2c6021afff7aff59fe00b7c
[gostls13.git] / 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/abi"
16         "internal/goarch"
17         "unsafe"
18 )
19
20 //go:linkname runtime_debug_WriteHeapDump runtime/debug.WriteHeapDump
21 func runtime_debug_WriteHeapDump(fd uintptr) {
22         stopTheWorld(stwWriteHeapDump)
23
24         // Keep m on this G's stack instead of the system stack.
25         // Both readmemstats_m and writeheapdump_m have pretty large
26         // peak stack depths and we risk blowing the system stack.
27         // This is safe because the world is stopped, so we don't
28         // need to worry about anyone shrinking and therefore moving
29         // our stack.
30         var m MemStats
31         systemstack(func() {
32                 // Call readmemstats_m here instead of deeper in
33                 // writeheapdump_m because we might blow the system stack
34                 // otherwise.
35                 readmemstats_m(&m)
36                 writeheapdump_m(fd, &m)
37         })
38
39         startTheWorld()
40 }
41
42 const (
43         fieldKindEol       = 0
44         fieldKindPtr       = 1
45         fieldKindIface     = 2
46         fieldKindEface     = 3
47         tagEOF             = 0
48         tagObject          = 1
49         tagOtherRoot       = 2
50         tagType            = 3
51         tagGoroutine       = 4
52         tagStackFrame      = 5
53         tagParams          = 6
54         tagFinalizer       = 7
55         tagItab            = 8
56         tagOSThread        = 9
57         tagMemStats        = 10
58         tagQueuedFinalizer = 11
59         tagData            = 12
60         tagBSS             = 13
61         tagDefer           = 14
62         tagPanic           = 15
63         tagMemProf         = 16
64         tagAllocSample     = 17
65 )
66
67 var dumpfd uintptr // fd to write the dump to.
68 var tmpbuf []byte
69
70 // buffer of pending write data
71 const (
72         bufSize = 4096
73 )
74
75 var buf [bufSize]byte
76 var nbuf uintptr
77
78 func dwrite(data unsafe.Pointer, len uintptr) {
79         if len == 0 {
80                 return
81         }
82         if nbuf+len <= bufSize {
83                 copy(buf[nbuf:], (*[bufSize]byte)(data)[:len])
84                 nbuf += len
85                 return
86         }
87
88         write(dumpfd, unsafe.Pointer(&buf), int32(nbuf))
89         if len >= bufSize {
90                 write(dumpfd, data, int32(len))
91                 nbuf = 0
92         } else {
93                 copy(buf[:], (*[bufSize]byte)(data)[:len])
94                 nbuf = len
95         }
96 }
97
98 func dwritebyte(b byte) {
99         dwrite(unsafe.Pointer(&b), 1)
100 }
101
102 func flush() {
103         write(dumpfd, unsafe.Pointer(&buf), int32(nbuf))
104         nbuf = 0
105 }
106
107 // Cache of types that have been serialized already.
108 // We use a type's hash field to pick a bucket.
109 // Inside a bucket, we keep a list of types that
110 // have been serialized so far, most recently used first.
111 // Note: when a bucket overflows we may end up
112 // serializing a type more than once. That's ok.
113 const (
114         typeCacheBuckets = 256
115         typeCacheAssoc   = 4
116 )
117
118 type typeCacheBucket struct {
119         t [typeCacheAssoc]*_type
120 }
121
122 var typecache [typeCacheBuckets]typeCacheBucket
123
124 // dump a uint64 in a varint format parseable by encoding/binary.
125 func dumpint(v uint64) {
126         var buf [10]byte
127         var n int
128         for v >= 0x80 {
129                 buf[n] = byte(v | 0x80)
130                 n++
131                 v >>= 7
132         }
133         buf[n] = byte(v)
134         n++
135         dwrite(unsafe.Pointer(&buf), uintptr(n))
136 }
137
138 func dumpbool(b bool) {
139         if b {
140                 dumpint(1)
141         } else {
142                 dumpint(0)
143         }
144 }
145
146 // dump varint uint64 length followed by memory contents.
147 func dumpmemrange(data unsafe.Pointer, len uintptr) {
148         dumpint(uint64(len))
149         dwrite(data, len)
150 }
151
152 func dumpslice(b []byte) {
153         dumpint(uint64(len(b)))
154         if len(b) > 0 {
155                 dwrite(unsafe.Pointer(&b[0]), uintptr(len(b)))
156         }
157 }
158
159 func dumpstr(s string) {
160         dumpmemrange(unsafe.Pointer(unsafe.StringData(s)), uintptr(len(s)))
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         rt := toRType(t)
198         if x := t.Uncommon(); x == nil || rt.nameOff(x.PkgPath).Name() == "" {
199                 dumpstr(rt.string())
200         } else {
201                 pkgpath := rt.nameOff(x.PkgPath).Name()
202                 name := rt.name()
203                 dumpint(uint64(uintptr(len(pkgpath)) + 1 + uintptr(len(name))))
204                 dwrite(unsafe.Pointer(unsafe.StringData(pkgpath)), uintptr(len(pkgpath)))
205                 dwritebyte('.')
206                 dwrite(unsafe.Pointer(unsafe.StringData(name)), uintptr(len(name)))
207         }
208         dumpbool(t.Kind_&kindDirectIface == 0 || t.PtrBytes != 0)
209 }
210
211 // dump an object.
212 func dumpobj(obj unsafe.Pointer, size uintptr, bv bitvector) {
213         dumpint(tagObject)
214         dumpint(uint64(uintptr(obj)))
215         dumpmemrange(obj, size)
216         dumpfields(bv)
217 }
218
219 func dumpotherroot(description string, to unsafe.Pointer) {
220         dumpint(tagOtherRoot)
221         dumpstr(description)
222         dumpint(uint64(uintptr(to)))
223 }
224
225 func dumpfinalizer(obj unsafe.Pointer, fn *funcval, fint *_type, ot *ptrtype) {
226         dumpint(tagFinalizer)
227         dumpint(uint64(uintptr(obj)))
228         dumpint(uint64(uintptr(unsafe.Pointer(fn))))
229         dumpint(uint64(uintptr(unsafe.Pointer(fn.fn))))
230         dumpint(uint64(uintptr(unsafe.Pointer(fint))))
231         dumpint(uint64(uintptr(unsafe.Pointer(ot))))
232 }
233
234 type childInfo struct {
235         // Information passed up from the callee frame about
236         // the layout of the outargs region.
237         argoff uintptr   // where the arguments start in the frame
238         arglen uintptr   // size of args region
239         args   bitvector // if args.n >= 0, pointer map of args region
240         sp     *uint8    // callee sp
241         depth  uintptr   // depth in call stack (0 == most recent)
242 }
243
244 // dump kinds & offsets of interesting fields in bv.
245 func dumpbv(cbv *bitvector, offset uintptr) {
246         for i := uintptr(0); i < uintptr(cbv.n); i++ {
247                 if cbv.ptrbit(i) == 1 {
248                         dumpint(fieldKindPtr)
249                         dumpint(uint64(offset + i*goarch.PtrSize))
250                 }
251         }
252 }
253
254 func dumpframe(s *stkframe, child *childInfo) {
255         f := s.fn
256
257         // Figure out what we can about our stack map
258         pc := s.pc
259         pcdata := int32(-1) // Use the entry map at function entry
260         if pc != f.entry() {
261                 pc--
262                 pcdata = pcdatavalue(f, abi.PCDATA_StackMapIndex, pc)
263         }
264         if pcdata == -1 {
265                 // We do not have a valid pcdata value but there might be a
266                 // stackmap for this function. It is likely that we are looking
267                 // at the function prologue, assume so and hope for the best.
268                 pcdata = 0
269         }
270         stkmap := (*stackmap)(funcdata(f, abi.FUNCDATA_LocalsPointerMaps))
271
272         var bv bitvector
273         if stkmap != nil && stkmap.n > 0 {
274                 bv = stackmapdata(stkmap, pcdata)
275         } else {
276                 bv.n = -1
277         }
278
279         // Dump main body of stack frame.
280         dumpint(tagStackFrame)
281         dumpint(uint64(s.sp))                              // lowest address in frame
282         dumpint(uint64(child.depth))                       // # of frames deep on the stack
283         dumpint(uint64(uintptr(unsafe.Pointer(child.sp)))) // sp of child, or 0 if bottom of stack
284         dumpmemrange(unsafe.Pointer(s.sp), s.fp-s.sp)      // frame contents
285         dumpint(uint64(f.entry()))
286         dumpint(uint64(s.pc))
287         dumpint(uint64(s.continpc))
288         name := funcname(f)
289         if name == "" {
290                 name = "unknown function"
291         }
292         dumpstr(name)
293
294         // Dump fields in the outargs section
295         if child.args.n >= 0 {
296                 dumpbv(&child.args, child.argoff)
297         } else {
298                 // conservative - everything might be a pointer
299                 for off := child.argoff; off < child.argoff+child.arglen; off += goarch.PtrSize {
300                         dumpint(fieldKindPtr)
301                         dumpint(uint64(off))
302                 }
303         }
304
305         // Dump fields in the local vars section
306         if stkmap == nil {
307                 // No locals information, dump everything.
308                 for off := child.arglen; off < s.varp-s.sp; off += goarch.PtrSize {
309                         dumpint(fieldKindPtr)
310                         dumpint(uint64(off))
311                 }
312         } else if stkmap.n < 0 {
313                 // Locals size information, dump just the locals.
314                 size := uintptr(-stkmap.n)
315                 for off := s.varp - size - s.sp; off < s.varp-s.sp; off += goarch.PtrSize {
316                         dumpint(fieldKindPtr)
317                         dumpint(uint64(off))
318                 }
319         } else if stkmap.n > 0 {
320                 // Locals bitmap information, scan just the pointers in
321                 // locals.
322                 dumpbv(&bv, s.varp-uintptr(bv.n)*goarch.PtrSize-s.sp)
323         }
324         dumpint(fieldKindEol)
325
326         // Record arg info for parent.
327         child.argoff = s.argp - s.fp
328         child.arglen = s.argBytes()
329         child.sp = (*uint8)(unsafe.Pointer(s.sp))
330         child.depth++
331         stkmap = (*stackmap)(funcdata(f, abi.FUNCDATA_ArgsPointerMaps))
332         if stkmap != nil {
333                 child.args = stackmapdata(stkmap, pcdata)
334         } else {
335                 child.args.n = -1
336         }
337         return
338 }
339
340 func dumpgoroutine(gp *g) {
341         var sp, pc, lr uintptr
342         if gp.syscallsp != 0 {
343                 sp = gp.syscallsp
344                 pc = gp.syscallpc
345                 lr = 0
346         } else {
347                 sp = gp.sched.sp
348                 pc = gp.sched.pc
349                 lr = gp.sched.lr
350         }
351
352         dumpint(tagGoroutine)
353         dumpint(uint64(uintptr(unsafe.Pointer(gp))))
354         dumpint(uint64(sp))
355         dumpint(gp.goid)
356         dumpint(uint64(gp.gopc))
357         dumpint(uint64(readgstatus(gp)))
358         dumpbool(isSystemGoroutine(gp, false))
359         dumpbool(false) // isbackground
360         dumpint(uint64(gp.waitsince))
361         dumpstr(gp.waitreason.String())
362         dumpint(uint64(uintptr(gp.sched.ctxt)))
363         dumpint(uint64(uintptr(unsafe.Pointer(gp.m))))
364         dumpint(uint64(uintptr(unsafe.Pointer(gp._defer))))
365         dumpint(uint64(uintptr(unsafe.Pointer(gp._panic))))
366
367         // dump stack
368         var child childInfo
369         child.args.n = -1
370         child.arglen = 0
371         child.sp = nil
372         child.depth = 0
373         var u unwinder
374         for u.initAt(pc, sp, lr, gp, 0); u.valid(); u.next() {
375                 dumpframe(&u.frame, &child)
376         }
377
378         // dump defer & panic records
379         for d := gp._defer; d != nil; d = d.link {
380                 dumpint(tagDefer)
381                 dumpint(uint64(uintptr(unsafe.Pointer(d))))
382                 dumpint(uint64(uintptr(unsafe.Pointer(gp))))
383                 dumpint(uint64(d.sp))
384                 dumpint(uint64(d.pc))
385                 fn := *(**funcval)(unsafe.Pointer(&d.fn))
386                 dumpint(uint64(uintptr(unsafe.Pointer(fn))))
387                 if d.fn == nil {
388                         // d.fn can be nil for open-coded defers
389                         dumpint(uint64(0))
390                 } else {
391                         dumpint(uint64(uintptr(unsafe.Pointer(fn.fn))))
392                 }
393                 dumpint(uint64(uintptr(unsafe.Pointer(d.link))))
394         }
395         for p := gp._panic; p != nil; p = p.link {
396                 dumpint(tagPanic)
397                 dumpint(uint64(uintptr(unsafe.Pointer(p))))
398                 dumpint(uint64(uintptr(unsafe.Pointer(gp))))
399                 eface := efaceOf(&p.arg)
400                 dumpint(uint64(uintptr(unsafe.Pointer(eface._type))))
401                 dumpint(uint64(uintptr(eface.data)))
402                 dumpint(0) // was p->defer, no longer recorded
403                 dumpint(uint64(uintptr(unsafe.Pointer(p.link))))
404         }
405 }
406
407 func dumpgs() {
408         assertWorldStopped()
409
410         // goroutines & stacks
411         forEachG(func(gp *g) {
412                 status := readgstatus(gp) // The world is stopped so gp will not be in a scan state.
413                 switch status {
414                 default:
415                         print("runtime: unexpected G.status ", hex(status), "\n")
416                         throw("dumpgs in STW - bad status")
417                 case _Gdead:
418                         // ok
419                 case _Grunnable,
420                         _Gsyscall,
421                         _Gwaiting:
422                         dumpgoroutine(gp)
423                 }
424         })
425 }
426
427 func finq_callback(fn *funcval, obj unsafe.Pointer, nret uintptr, fint *_type, ot *ptrtype) {
428         dumpint(tagQueuedFinalizer)
429         dumpint(uint64(uintptr(obj)))
430         dumpint(uint64(uintptr(unsafe.Pointer(fn))))
431         dumpint(uint64(uintptr(unsafe.Pointer(fn.fn))))
432         dumpint(uint64(uintptr(unsafe.Pointer(fint))))
433         dumpint(uint64(uintptr(unsafe.Pointer(ot))))
434 }
435
436 func dumproots() {
437         // To protect mheap_.allspans.
438         assertWorldStopped()
439
440         // TODO(mwhudson): dump datamask etc from all objects
441         // data segment
442         dumpint(tagData)
443         dumpint(uint64(firstmoduledata.data))
444         dumpmemrange(unsafe.Pointer(firstmoduledata.data), firstmoduledata.edata-firstmoduledata.data)
445         dumpfields(firstmoduledata.gcdatamask)
446
447         // bss segment
448         dumpint(tagBSS)
449         dumpint(uint64(firstmoduledata.bss))
450         dumpmemrange(unsafe.Pointer(firstmoduledata.bss), firstmoduledata.ebss-firstmoduledata.bss)
451         dumpfields(firstmoduledata.gcbssmask)
452
453         // mspan.types
454         for _, s := range mheap_.allspans {
455                 if s.state.get() == mSpanInUse {
456                         // Finalizers
457                         for sp := s.specials; sp != nil; sp = sp.next {
458                                 if sp.kind != _KindSpecialFinalizer {
459                                         continue
460                                 }
461                                 spf := (*specialfinalizer)(unsafe.Pointer(sp))
462                                 p := unsafe.Pointer(s.base() + uintptr(spf.special.offset))
463                                 dumpfinalizer(p, spf.fn, spf.fint, spf.ot)
464                         }
465                 }
466         }
467
468         // Finalizer queue
469         iterate_finq(finq_callback)
470 }
471
472 // Bit vector of free marks.
473 // Needs to be as big as the largest number of objects per span.
474 var freemark [_PageSize / 8]bool
475
476 func dumpobjs() {
477         // To protect mheap_.allspans.
478         assertWorldStopped()
479
480         for _, s := range mheap_.allspans {
481                 if s.state.get() != mSpanInUse {
482                         continue
483                 }
484                 p := s.base()
485                 size := s.elemsize
486                 n := (s.npages << _PageShift) / size
487                 if n > uintptr(len(freemark)) {
488                         throw("freemark array doesn't have enough entries")
489                 }
490
491                 for freeIndex := uint16(0); freeIndex < s.nelems; freeIndex++ {
492                         if s.isFree(uintptr(freeIndex)) {
493                                 freemark[freeIndex] = true
494                         }
495                 }
496
497                 for j := uintptr(0); j < n; j, p = j+1, p+size {
498                         if freemark[j] {
499                                 freemark[j] = false
500                                 continue
501                         }
502                         dumpobj(unsafe.Pointer(p), size, makeheapobjbv(p, size))
503                 }
504         }
505 }
506
507 func dumpparams() {
508         dumpint(tagParams)
509         x := uintptr(1)
510         if *(*byte)(unsafe.Pointer(&x)) == 1 {
511                 dumpbool(false) // little-endian ptrs
512         } else {
513                 dumpbool(true) // big-endian ptrs
514         }
515         dumpint(goarch.PtrSize)
516         var arenaStart, arenaEnd uintptr
517         for i1 := range mheap_.arenas {
518                 if mheap_.arenas[i1] == nil {
519                         continue
520                 }
521                 for i, ha := range mheap_.arenas[i1] {
522                         if ha == nil {
523                                 continue
524                         }
525                         base := arenaBase(arenaIdx(i1)<<arenaL1Shift | arenaIdx(i))
526                         if arenaStart == 0 || base < arenaStart {
527                                 arenaStart = base
528                         }
529                         if base+heapArenaBytes > arenaEnd {
530                                 arenaEnd = base + heapArenaBytes
531                         }
532                 }
533         }
534         dumpint(uint64(arenaStart))
535         dumpint(uint64(arenaEnd))
536         dumpstr(goarch.GOARCH)
537         dumpstr(buildVersion)
538         dumpint(uint64(ncpu))
539 }
540
541 func itab_callback(tab *itab) {
542         t := tab._type
543         dumptype(t)
544         dumpint(tagItab)
545         dumpint(uint64(uintptr(unsafe.Pointer(tab))))
546         dumpint(uint64(uintptr(unsafe.Pointer(t))))
547 }
548
549 func dumpitabs() {
550         iterate_itabs(itab_callback)
551 }
552
553 func dumpms() {
554         for mp := allm; mp != nil; mp = mp.alllink {
555                 dumpint(tagOSThread)
556                 dumpint(uint64(uintptr(unsafe.Pointer(mp))))
557                 dumpint(uint64(mp.id))
558                 dumpint(mp.procid)
559         }
560 }
561
562 //go:systemstack
563 func dumpmemstats(m *MemStats) {
564         assertWorldStopped()
565
566         // These ints should be identical to the exported
567         // MemStats structure and should be ordered the same
568         // way too.
569         dumpint(tagMemStats)
570         dumpint(m.Alloc)
571         dumpint(m.TotalAlloc)
572         dumpint(m.Sys)
573         dumpint(m.Lookups)
574         dumpint(m.Mallocs)
575         dumpint(m.Frees)
576         dumpint(m.HeapAlloc)
577         dumpint(m.HeapSys)
578         dumpint(m.HeapIdle)
579         dumpint(m.HeapInuse)
580         dumpint(m.HeapReleased)
581         dumpint(m.HeapObjects)
582         dumpint(m.StackInuse)
583         dumpint(m.StackSys)
584         dumpint(m.MSpanInuse)
585         dumpint(m.MSpanSys)
586         dumpint(m.MCacheInuse)
587         dumpint(m.MCacheSys)
588         dumpint(m.BuckHashSys)
589         dumpint(m.GCSys)
590         dumpint(m.OtherSys)
591         dumpint(m.NextGC)
592         dumpint(m.LastGC)
593         dumpint(m.PauseTotalNs)
594         for i := 0; i < 256; i++ {
595                 dumpint(m.PauseNs[i])
596         }
597         dumpint(uint64(m.NumGC))
598 }
599
600 func dumpmemprof_callback(b *bucket, nstk uintptr, pstk *uintptr, size, allocs, frees uintptr) {
601         stk := (*[100000]uintptr)(unsafe.Pointer(pstk))
602         dumpint(tagMemProf)
603         dumpint(uint64(uintptr(unsafe.Pointer(b))))
604         dumpint(uint64(size))
605         dumpint(uint64(nstk))
606         for i := uintptr(0); i < nstk; i++ {
607                 pc := stk[i]
608                 f := findfunc(pc)
609                 if !f.valid() {
610                         var buf [64]byte
611                         n := len(buf)
612                         n--
613                         buf[n] = ')'
614                         if pc == 0 {
615                                 n--
616                                 buf[n] = '0'
617                         } else {
618                                 for pc > 0 {
619                                         n--
620                                         buf[n] = "0123456789abcdef"[pc&15]
621                                         pc >>= 4
622                                 }
623                         }
624                         n--
625                         buf[n] = 'x'
626                         n--
627                         buf[n] = '0'
628                         n--
629                         buf[n] = '('
630                         dumpslice(buf[n:])
631                         dumpstr("?")
632                         dumpint(0)
633                 } else {
634                         dumpstr(funcname(f))
635                         if i > 0 && pc > f.entry() {
636                                 pc--
637                         }
638                         file, line := funcline(f, pc)
639                         dumpstr(file)
640                         dumpint(uint64(line))
641                 }
642         }
643         dumpint(uint64(allocs))
644         dumpint(uint64(frees))
645 }
646
647 func dumpmemprof() {
648         // To protect mheap_.allspans.
649         assertWorldStopped()
650
651         iterate_memprof(dumpmemprof_callback)
652         for _, s := range mheap_.allspans {
653                 if s.state.get() != mSpanInUse {
654                         continue
655                 }
656                 for sp := s.specials; sp != nil; sp = sp.next {
657                         if sp.kind != _KindSpecialProfile {
658                                 continue
659                         }
660                         spp := (*specialprofile)(unsafe.Pointer(sp))
661                         p := s.base() + uintptr(spp.special.offset)
662                         dumpint(tagAllocSample)
663                         dumpint(uint64(p))
664                         dumpint(uint64(uintptr(unsafe.Pointer(spp.b))))
665                 }
666         }
667 }
668
669 var dumphdr = []byte("go1.7 heap dump\n")
670
671 func mdump(m *MemStats) {
672         assertWorldStopped()
673
674         // make sure we're done sweeping
675         for _, s := range mheap_.allspans {
676                 if s.state.get() == mSpanInUse {
677                         s.ensureSwept()
678                 }
679         }
680         memclrNoHeapPointers(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache))
681         dwrite(unsafe.Pointer(&dumphdr[0]), uintptr(len(dumphdr)))
682         dumpparams()
683         dumpitabs()
684         dumpobjs()
685         dumpgs()
686         dumpms()
687         dumproots()
688         dumpmemstats(m)
689         dumpmemprof()
690         dumpint(tagEOF)
691         flush()
692 }
693
694 func writeheapdump_m(fd uintptr, m *MemStats) {
695         assertWorldStopped()
696
697         gp := getg()
698         casGToWaiting(gp.m.curg, _Grunning, 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(gp.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
741         hbits := heapBitsForAddr(p, size)
742         for {
743                 var addr uintptr
744                 hbits, addr = hbits.next()
745                 if addr == 0 {
746                         break
747                 }
748                 i := (addr - p) / goarch.PtrSize
749                 tmpbuf[i/8] |= 1 << (i % 8)
750         }
751         return bitvector{int32(nptr), &tmpbuf[0]}
752 }