blob: f5770958fdac61dcadb1f92b715d0afd4316d92f [file] [log] [blame]
Russ Cox1e2d2f02014-11-11 17:05:02 -05001// 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
Russ Cox50e07492014-11-20 11:48:08 -050010// http://golang.org/s/go14heapdump.
Russ Cox1e2d2f02014-11-11 17:05:02 -050011
12package runtime
13
14import "unsafe"
15
Russ Cox484f8012015-02-19 13:38:46 -050016//go:linkname runtime_debug_WriteHeapDump runtime/debug.WriteHeapDump
17func runtime_debug_WriteHeapDump(fd uintptr) {
18 semacquire(&worldsema, false)
19 gp := getg()
20 gp.m.preemptoff = "write heap dump"
21 systemstack(stoptheworld)
22
23 systemstack(func() {
24 writeheapdump_m(fd)
25 })
26
27 gp.m.preemptoff = ""
28 gp.m.locks++
29 semrelease(&worldsema)
30 systemstack(starttheworld)
31 gp.m.locks--
32}
33
Russ Cox1e2d2f02014-11-11 17:05:02 -050034const (
35 fieldKindEol = 0
36 fieldKindPtr = 1
37 fieldKindIface = 2
38 fieldKindEface = 3
39 tagEOF = 0
40 tagObject = 1
41 tagOtherRoot = 2
42 tagType = 3
43 tagGoroutine = 4
44 tagStackFrame = 5
45 tagParams = 6
46 tagFinalizer = 7
47 tagItab = 8
48 tagOSThread = 9
49 tagMemStats = 10
50 tagQueuedFinalizer = 11
51 tagData = 12
52 tagBSS = 13
53 tagDefer = 14
54 tagPanic = 15
55 tagMemProf = 16
56 tagAllocSample = 17
57)
58
59var dumpfd uintptr // fd to write the dump to.
60var tmpbuf []byte
61
62// buffer of pending write data
63const (
64 bufSize = 4096
65)
66
67var buf [bufSize]byte
68var nbuf uintptr
69
70func dwrite(data unsafe.Pointer, len uintptr) {
71 if len == 0 {
72 return
73 }
74 if nbuf+len <= bufSize {
75 copy(buf[nbuf:], (*[bufSize]byte)(data)[:len])
76 nbuf += len
77 return
78 }
79
80 write(dumpfd, (unsafe.Pointer)(&buf), int32(nbuf))
81 if len >= bufSize {
82 write(dumpfd, data, int32(len))
83 nbuf = 0
84 } else {
85 copy(buf[:], (*[bufSize]byte)(data)[:len])
86 nbuf = len
87 }
88}
89
90func dwritebyte(b byte) {
91 dwrite(unsafe.Pointer(&b), 1)
92}
93
94func flush() {
95 write(dumpfd, (unsafe.Pointer)(&buf), int32(nbuf))
96 nbuf = 0
97}
98
99// Cache of types that have been serialized already.
100// We use a type's hash field to pick a bucket.
101// Inside a bucket, we keep a list of types that
102// have been serialized so far, most recently used first.
103// Note: when a bucket overflows we may end up
104// serializing a type more than once. That's ok.
105const (
106 typeCacheBuckets = 256
107 typeCacheAssoc = 4
108)
109
110type typeCacheBucket struct {
111 t [typeCacheAssoc]*_type
112}
113
114var typecache [typeCacheBuckets]typeCacheBucket
115
116// dump a uint64 in a varint format parseable by encoding/binary
117func dumpint(v uint64) {
118 var buf [10]byte
119 var n int
120 for v >= 0x80 {
121 buf[n] = byte(v | 0x80)
122 n++
123 v >>= 7
124 }
125 buf[n] = byte(v)
126 n++
127 dwrite(unsafe.Pointer(&buf), uintptr(n))
128}
129
130func dumpbool(b bool) {
131 if b {
132 dumpint(1)
133 } else {
134 dumpint(0)
135 }
136}
137
138// dump varint uint64 length followed by memory contents
139func dumpmemrange(data unsafe.Pointer, len uintptr) {
140 dumpint(uint64(len))
141 dwrite(data, len)
142}
143
144func dumpslice(b []byte) {
145 dumpint(uint64(len(b)))
146 if len(b) > 0 {
147 dwrite(unsafe.Pointer(&b[0]), uintptr(len(b)))
148 }
149}
150
151func dumpstr(s string) {
152 sp := (*stringStruct)(unsafe.Pointer(&s))
153 dumpmemrange(sp.str, uintptr(sp.len))
154}
155
156// dump information for a type
157func dumptype(t *_type) {
158 if t == nil {
159 return
160 }
161
162 // If we've definitely serialized the type before,
163 // no need to do it again.
164 b := &typecache[t.hash&(typeCacheBuckets-1)]
165 if t == b.t[0] {
166 return
167 }
168 for i := 1; i < typeCacheAssoc; i++ {
169 if t == b.t[i] {
170 // Move-to-front
171 for j := i; j > 0; j-- {
172 b.t[j] = b.t[j-1]
173 }
174 b.t[0] = t
175 return
176 }
177 }
178
179 // Might not have been dumped yet. Dump it and
180 // remember we did so.
181 for j := typeCacheAssoc - 1; j > 0; j-- {
182 b.t[j] = b.t[j-1]
183 }
184 b.t[0] = t
185
186 // dump the type
187 dumpint(tagType)
188 dumpint(uint64(uintptr(unsafe.Pointer(t))))
189 dumpint(uint64(t.size))
190 if t.x == nil || t.x.pkgpath == nil || t.x.name == nil {
191 dumpstr(*t._string)
192 } else {
193 pkgpath := (*stringStruct)(unsafe.Pointer(&t.x.pkgpath))
194 name := (*stringStruct)(unsafe.Pointer(&t.x.name))
195 dumpint(uint64(uintptr(pkgpath.len) + 1 + uintptr(name.len)))
196 dwrite(pkgpath.str, uintptr(pkgpath.len))
197 dwritebyte('.')
198 dwrite(name.str, uintptr(name.len))
199 }
200 dumpbool(t.kind&kindDirectIface == 0 || t.kind&kindNoPointers == 0)
201}
202
203// dump an object
204func dumpobj(obj unsafe.Pointer, size uintptr, bv bitvector) {
205 dumpbvtypes(&bv, obj)
206 dumpint(tagObject)
207 dumpint(uint64(uintptr(obj)))
208 dumpmemrange(obj, size)
209 dumpfields(bv)
210}
211
212func dumpotherroot(description string, to unsafe.Pointer) {
213 dumpint(tagOtherRoot)
214 dumpstr(description)
215 dumpint(uint64(uintptr(to)))
216}
217
218func dumpfinalizer(obj unsafe.Pointer, fn *funcval, fint *_type, ot *ptrtype) {
219 dumpint(tagFinalizer)
220 dumpint(uint64(uintptr(obj)))
221 dumpint(uint64(uintptr(unsafe.Pointer(fn))))
222 dumpint(uint64(uintptr(unsafe.Pointer(fn.fn))))
223 dumpint(uint64(uintptr(unsafe.Pointer(fint))))
224 dumpint(uint64(uintptr(unsafe.Pointer(ot))))
225}
226
227type childInfo struct {
228 // Information passed up from the callee frame about
229 // the layout of the outargs region.
230 argoff uintptr // where the arguments start in the frame
231 arglen uintptr // size of args region
232 args bitvector // if args.n >= 0, pointer map of args region
233 sp *uint8 // callee sp
234 depth uintptr // depth in call stack (0 == most recent)
235}
236
237// dump kinds & offsets of interesting fields in bv
238func dumpbv(cbv *bitvector, offset uintptr) {
239 bv := gobv(*cbv)
Russ Cox3965d752015-01-16 14:43:38 -0500240 for i := uintptr(0); i < uintptr(bv.n); i += typeBitsWidth {
241 switch bv.bytedata[i/8] >> (i % 8) & typeMask {
Russ Cox1e2d2f02014-11-11 17:05:02 -0500242 default:
Keith Randallb2a950b2014-12-27 20:58:00 -0800243 throw("unexpected pointer bits")
Russ Cox3965d752015-01-16 14:43:38 -0500244 case typeDead:
245 // typeDead has already been processed in makeheapobjbv.
Russ Cox1e2d2f02014-11-11 17:05:02 -0500246 // We should only see it in stack maps, in which case we should continue processing.
Russ Cox3965d752015-01-16 14:43:38 -0500247 case typeScalar:
Russ Cox1e2d2f02014-11-11 17:05:02 -0500248 // ok
Russ Cox3965d752015-01-16 14:43:38 -0500249 case typePointer:
Russ Cox1e2d2f02014-11-11 17:05:02 -0500250 dumpint(fieldKindPtr)
Russ Cox3965d752015-01-16 14:43:38 -0500251 dumpint(uint64(offset + i/typeBitsWidth*ptrSize))
Russ Cox1e2d2f02014-11-11 17:05:02 -0500252 }
253 }
254}
255
256func dumpframe(s *stkframe, arg unsafe.Pointer) bool {
257 child := (*childInfo)(arg)
258 f := s.fn
259
260 // Figure out what we can about our stack map
261 pc := s.pc
262 if pc != f.entry {
263 pc--
264 }
265 pcdata := pcdatavalue(f, _PCDATA_StackMapIndex, pc)
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 // Dump any types we will need to resolve Efaces.
275 if child.args.n >= 0 {
276 dumpbvtypes(&child.args, unsafe.Pointer(s.sp+child.argoff))
277 }
278 var bv bitvector
279 if stkmap != nil && stkmap.n > 0 {
280 bv = stackmapdata(stkmap, pcdata)
Russ Cox3965d752015-01-16 14:43:38 -0500281 dumpbvtypes(&bv, unsafe.Pointer(s.varp-uintptr(bv.n/typeBitsWidth*ptrSize)))
Russ Cox1e2d2f02014-11-11 17:05:02 -0500282 } else {
283 bv.n = -1
284 }
285
286 // Dump main body of stack frame.
287 dumpint(tagStackFrame)
288 dumpint(uint64(s.sp)) // lowest address in frame
289 dumpint(uint64(child.depth)) // # of frames deep on the stack
290 dumpint(uint64(uintptr(unsafe.Pointer(child.sp)))) // sp of child, or 0 if bottom of stack
291 dumpmemrange(unsafe.Pointer(s.sp), s.fp-s.sp) // frame contents
292 dumpint(uint64(f.entry))
293 dumpint(uint64(s.pc))
294 dumpint(uint64(s.continpc))
Keith Randall0bb8fc62014-12-28 23:16:32 -0800295 name := funcname(f)
Russ Cox1e2d2f02014-11-11 17:05:02 -0500296 if name == "" {
297 name = "unknown function"
298 }
299 dumpstr(name)
300
301 // Dump fields in the outargs section
302 if child.args.n >= 0 {
303 dumpbv(&child.args, child.argoff)
304 } else {
305 // conservative - everything might be a pointer
306 for off := child.argoff; off < child.argoff+child.arglen; off += ptrSize {
307 dumpint(fieldKindPtr)
308 dumpint(uint64(off))
309 }
310 }
311
312 // Dump fields in the local vars section
313 if stkmap == nil {
314 // No locals information, dump everything.
315 for off := child.arglen; off < s.varp-s.sp; off += ptrSize {
316 dumpint(fieldKindPtr)
317 dumpint(uint64(off))
318 }
319 } else if stkmap.n < 0 {
320 // Locals size information, dump just the locals.
321 size := uintptr(-stkmap.n)
322 for off := s.varp - size - s.sp; off < s.varp-s.sp; off += ptrSize {
323 dumpint(fieldKindPtr)
324 dumpint(uint64(off))
325 }
326 } else if stkmap.n > 0 {
327 // Locals bitmap information, scan just the pointers in
328 // locals.
Russ Cox3965d752015-01-16 14:43:38 -0500329 dumpbv(&bv, s.varp-uintptr(bv.n)/typeBitsWidth*ptrSize-s.sp)
Russ Cox1e2d2f02014-11-11 17:05:02 -0500330 }
331 dumpint(fieldKindEol)
332
333 // Record arg info for parent.
334 child.argoff = s.argp - s.fp
335 child.arglen = s.arglen
336 child.sp = (*uint8)(unsafe.Pointer(s.sp))
337 child.depth++
338 stkmap = (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
339 if stkmap != nil {
340 child.args = stackmapdata(stkmap, pcdata)
341 } else {
342 child.args.n = -1
343 }
344 return true
345}
346
347func dumpgoroutine(gp *g) {
348 var sp, pc, lr uintptr
349 if gp.syscallsp != 0 {
350 sp = gp.syscallsp
351 pc = gp.syscallpc
352 lr = 0
353 } else {
354 sp = gp.sched.sp
355 pc = gp.sched.pc
356 lr = gp.sched.lr
357 }
358
359 dumpint(tagGoroutine)
360 dumpint(uint64(uintptr(unsafe.Pointer(gp))))
361 dumpint(uint64(sp))
362 dumpint(uint64(gp.goid))
363 dumpint(uint64(gp.gopc))
364 dumpint(uint64(readgstatus(gp)))
Dmitry Vyukov59495e82015-02-07 15:31:18 +0300365 dumpbool(isSystemGoroutine(gp))
Russ Cox1e2d2f02014-11-11 17:05:02 -0500366 dumpbool(false) // isbackground
367 dumpint(uint64(gp.waitsince))
368 dumpstr(gp.waitreason)
369 dumpint(uint64(uintptr(gp.sched.ctxt)))
370 dumpint(uint64(uintptr(unsafe.Pointer(gp.m))))
371 dumpint(uint64(uintptr(unsafe.Pointer(gp._defer))))
372 dumpint(uint64(uintptr(unsafe.Pointer(gp._panic))))
373
374 // dump stack
375 var child childInfo
376 child.args.n = -1
377 child.arglen = 0
378 child.sp = nil
379 child.depth = 0
380 gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, dumpframe, noescape(unsafe.Pointer(&child)), 0)
381
382 // dump defer & panic records
383 for d := gp._defer; d != nil; d = d.link {
384 dumpint(tagDefer)
385 dumpint(uint64(uintptr(unsafe.Pointer(d))))
386 dumpint(uint64(uintptr(unsafe.Pointer(gp))))
Keith Randall53c52262014-12-08 14:18:58 -0800387 dumpint(uint64(d.sp))
Russ Cox1e2d2f02014-11-11 17:05:02 -0500388 dumpint(uint64(d.pc))
389 dumpint(uint64(uintptr(unsafe.Pointer(d.fn))))
390 dumpint(uint64(uintptr(unsafe.Pointer(d.fn.fn))))
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 := (*eface)(unsafe.Pointer(&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
405func dumpgs() {
406 // goroutines & stacks
407 for i := 0; uintptr(i) < allglen; i++ {
408 gp := allgs[i]
409 status := readgstatus(gp) // The world is stopped so gp will not be in a scan state.
410 switch status {
411 default:
412 print("runtime: unexpected G.status ", hex(status), "\n")
Keith Randallb2a950b2014-12-27 20:58:00 -0800413 throw("dumpgs in STW - bad status")
Russ Cox1e2d2f02014-11-11 17:05:02 -0500414 case _Gdead:
415 // ok
416 case _Grunnable,
417 _Gsyscall,
418 _Gwaiting:
419 dumpgoroutine(gp)
420 }
421 }
422}
423
424func finq_callback(fn *funcval, obj unsafe.Pointer, nret uintptr, fint *_type, ot *ptrtype) {
425 dumpint(tagQueuedFinalizer)
426 dumpint(uint64(uintptr(obj)))
427 dumpint(uint64(uintptr(unsafe.Pointer(fn))))
428 dumpint(uint64(uintptr(unsafe.Pointer(fn.fn))))
429 dumpint(uint64(uintptr(unsafe.Pointer(fint))))
430 dumpint(uint64(uintptr(unsafe.Pointer(ot))))
431}
432
433func dumproots() {
434 // data segment
435 dumpbvtypes(&gcdatamask, unsafe.Pointer(&data))
436 dumpint(tagData)
437 dumpint(uint64(uintptr(unsafe.Pointer(&data))))
438 dumpmemrange(unsafe.Pointer(&data), uintptr(unsafe.Pointer(&edata))-uintptr(unsafe.Pointer(&data)))
439 dumpfields(gcdatamask)
440
441 // bss segment
442 dumpbvtypes(&gcbssmask, unsafe.Pointer(&bss))
443 dumpint(tagBSS)
444 dumpint(uint64(uintptr(unsafe.Pointer(&bss))))
445 dumpmemrange(unsafe.Pointer(&bss), uintptr(unsafe.Pointer(&ebss))-uintptr(unsafe.Pointer(&bss)))
446 dumpfields(gcbssmask)
447
448 // MSpan.types
449 allspans := h_allspans
450 for spanidx := uint32(0); spanidx < mheap_.nspan; spanidx++ {
451 s := allspans[spanidx]
452 if s.state == _MSpanInUse {
453 // Finalizers
454 for sp := s.specials; sp != nil; sp = sp.next {
455 if sp.kind != _KindSpecialFinalizer {
456 continue
457 }
458 spf := (*specialfinalizer)(unsafe.Pointer(sp))
459 p := unsafe.Pointer((uintptr(s.start) << _PageShift) + uintptr(spf.special.offset))
460 dumpfinalizer(p, spf.fn, spf.fint, spf.ot)
461 }
462 }
463 }
464
465 // Finalizer queue
466 iterate_finq(finq_callback)
467}
468
469// Bit vector of free marks.
470// Needs to be as big as the largest number of objects per span.
471var freemark [_PageSize / 8]bool
472
473func dumpobjs() {
474 for i := uintptr(0); i < uintptr(mheap_.nspan); i++ {
475 s := h_allspans[i]
476 if s.state != _MSpanInUse {
477 continue
478 }
479 p := uintptr(s.start << _PageShift)
480 size := s.elemsize
481 n := (s.npages << _PageShift) / size
482 if n > uintptr(len(freemark)) {
Keith Randallb2a950b2014-12-27 20:58:00 -0800483 throw("freemark array doesn't have enough entries")
Russ Cox1e2d2f02014-11-11 17:05:02 -0500484 }
Rick Hudson8cfb0842014-11-20 12:08:13 -0500485 for l := s.freelist; l.ptr() != nil; l = l.ptr().next {
486 freemark[(uintptr(l)-p)/size] = true
Russ Cox1e2d2f02014-11-11 17:05:02 -0500487 }
488 for j := uintptr(0); j < n; j, p = j+1, p+size {
489 if freemark[j] {
490 freemark[j] = false
491 continue
492 }
493 dumpobj(unsafe.Pointer(p), size, makeheapobjbv(p, size))
494 }
495 }
496}
497
498func dumpparams() {
499 dumpint(tagParams)
500 x := uintptr(1)
501 if *(*byte)(unsafe.Pointer(&x)) == 1 {
502 dumpbool(false) // little-endian ptrs
503 } else {
504 dumpbool(true) // big-endian ptrs
505 }
506 dumpint(ptrSize)
507 dumpint(uint64(mheap_.arena_start))
508 dumpint(uint64(mheap_.arena_used))
509 dumpint(thechar)
510 dumpstr(goexperiment)
511 dumpint(uint64(ncpu))
512}
513
514func itab_callback(tab *itab) {
515 t := tab._type
516 // Dump a map from itab* to the type of its data field.
517 // We want this map so we can deduce types of interface referents.
518 if t.kind&kindDirectIface == 0 {
519 // indirect - data slot is a pointer to t.
520 dumptype(t.ptrto)
521 dumpint(tagItab)
522 dumpint(uint64(uintptr(unsafe.Pointer(tab))))
523 dumpint(uint64(uintptr(unsafe.Pointer(t.ptrto))))
524 } else if t.kind&kindNoPointers == 0 {
525 // t is pointer-like - data slot is a t.
526 dumptype(t)
527 dumpint(tagItab)
528 dumpint(uint64(uintptr(unsafe.Pointer(tab))))
529 dumpint(uint64(uintptr(unsafe.Pointer(t))))
530 } else {
531 // Data slot is a scalar. Dump type just for fun.
532 // With pointer-only interfaces, this shouldn't happen.
533 dumptype(t)
534 dumpint(tagItab)
535 dumpint(uint64(uintptr(unsafe.Pointer(tab))))
536 dumpint(uint64(uintptr(unsafe.Pointer(t))))
537 }
538}
539
540func dumpitabs() {
541 iterate_itabs(itab_callback)
542}
543
544func dumpms() {
545 for mp := allm; mp != nil; mp = mp.alllink {
546 dumpint(tagOSThread)
547 dumpint(uint64(uintptr(unsafe.Pointer(mp))))
548 dumpint(uint64(mp.id))
549 dumpint(mp.procid)
550 }
551}
552
553func dumpmemstats() {
554 dumpint(tagMemStats)
555 dumpint(memstats.alloc)
556 dumpint(memstats.total_alloc)
557 dumpint(memstats.sys)
558 dumpint(memstats.nlookup)
559 dumpint(memstats.nmalloc)
560 dumpint(memstats.nfree)
561 dumpint(memstats.heap_alloc)
562 dumpint(memstats.heap_sys)
563 dumpint(memstats.heap_idle)
564 dumpint(memstats.heap_inuse)
565 dumpint(memstats.heap_released)
566 dumpint(memstats.heap_objects)
567 dumpint(memstats.stacks_inuse)
568 dumpint(memstats.stacks_sys)
569 dumpint(memstats.mspan_inuse)
570 dumpint(memstats.mspan_sys)
571 dumpint(memstats.mcache_inuse)
572 dumpint(memstats.mcache_sys)
573 dumpint(memstats.buckhash_sys)
574 dumpint(memstats.gc_sys)
575 dumpint(memstats.other_sys)
576 dumpint(memstats.next_gc)
577 dumpint(memstats.last_gc)
578 dumpint(memstats.pause_total_ns)
579 for i := 0; i < 256; i++ {
580 dumpint(memstats.pause_ns[i])
581 }
582 dumpint(uint64(memstats.numgc))
583}
584
585func dumpmemprof_callback(b *bucket, nstk uintptr, pstk *uintptr, size, allocs, frees uintptr) {
586 stk := (*[100000]uintptr)(unsafe.Pointer(pstk))
587 dumpint(tagMemProf)
588 dumpint(uint64(uintptr(unsafe.Pointer(b))))
589 dumpint(uint64(size))
590 dumpint(uint64(nstk))
591 for i := uintptr(0); i < nstk; i++ {
592 pc := stk[i]
593 f := findfunc(pc)
594 if f == nil {
595 var buf [64]byte
596 n := len(buf)
597 n--
598 buf[n] = ')'
599 if pc == 0 {
600 n--
601 buf[n] = '0'
602 } else {
603 for pc > 0 {
604 n--
605 buf[n] = "0123456789abcdef"[pc&15]
606 pc >>= 4
607 }
608 }
609 n--
610 buf[n] = 'x'
611 n--
612 buf[n] = '0'
613 n--
614 buf[n] = '('
615 dumpslice(buf[n:])
616 dumpstr("?")
617 dumpint(0)
618 } else {
Keith Randall0bb8fc62014-12-28 23:16:32 -0800619 dumpstr(funcname(f))
Russ Cox1e2d2f02014-11-11 17:05:02 -0500620 if i > 0 && pc > f.entry {
621 pc--
622 }
Russ Cox656be312014-11-12 14:54:31 -0500623 file, line := funcline(f, pc)
Russ Cox1e2d2f02014-11-11 17:05:02 -0500624 dumpstr(file)
625 dumpint(uint64(line))
626 }
627 }
628 dumpint(uint64(allocs))
629 dumpint(uint64(frees))
630}
631
632func dumpmemprof() {
633 iterate_memprof(dumpmemprof_callback)
634 allspans := h_allspans
635 for spanidx := uint32(0); spanidx < mheap_.nspan; spanidx++ {
636 s := allspans[spanidx]
637 if s.state != _MSpanInUse {
638 continue
639 }
640 for sp := s.specials; sp != nil; sp = sp.next {
641 if sp.kind != _KindSpecialProfile {
642 continue
643 }
644 spp := (*specialprofile)(unsafe.Pointer(sp))
645 p := uintptr(s.start<<_PageShift) + uintptr(spp.special.offset)
646 dumpint(tagAllocSample)
647 dumpint(uint64(p))
648 dumpint(uint64(uintptr(unsafe.Pointer(spp.b))))
649 }
650 }
651}
652
653var dumphdr = []byte("go1.4 heap dump\n")
654
655func mdump() {
656 // make sure we're done sweeping
657 for i := uintptr(0); i < uintptr(mheap_.nspan); i++ {
658 s := h_allspans[i]
659 if s.state == _MSpanInUse {
660 mSpan_EnsureSwept(s)
661 }
662 }
663 memclr(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache))
664 dwrite(unsafe.Pointer(&dumphdr[0]), uintptr(len(dumphdr)))
665 dumpparams()
666 dumpitabs()
667 dumpobjs()
668 dumpgs()
669 dumpms()
670 dumproots()
671 dumpmemstats()
672 dumpmemprof()
673 dumpint(tagEOF)
674 flush()
675}
676
Russ Cox656be312014-11-12 14:54:31 -0500677func writeheapdump_m(fd uintptr) {
Russ Cox1e2d2f02014-11-11 17:05:02 -0500678 _g_ := getg()
Russ Cox1e2d2f02014-11-11 17:05:02 -0500679 casgstatus(_g_.m.curg, _Grunning, _Gwaiting)
680 _g_.waitreason = "dumping heap"
681
682 // Update stats so we can dump them.
683 // As a side effect, flushes all the MCaches so the MSpan.freelist
684 // lists contain all the free objects.
685 updatememstats(nil)
686
687 // Set dump file.
688 dumpfd = fd
689
690 // Call dump routine.
691 mdump()
692
693 // Reset dump file.
694 dumpfd = 0
695 if tmpbuf != nil {
696 sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys)
697 tmpbuf = nil
698 }
699
700 casgstatus(_g_.m.curg, _Gwaiting, _Grunning)
701}
702
703// dumpint() the kind & offset of each field in an object.
704func dumpfields(bv bitvector) {
705 dumpbv(&bv, 0)
706 dumpint(fieldKindEol)
707}
708
709// The heap dump reader needs to be able to disambiguate
710// Eface entries. So it needs to know every type that might
711// appear in such an entry. The following routine accomplishes that.
712// TODO(rsc, khr): Delete - no longer possible.
713
714// Dump all the types that appear in the type field of
715// any Eface described by this bit vector.
716func dumpbvtypes(bv *bitvector, base unsafe.Pointer) {
717}
718
719func makeheapobjbv(p uintptr, size uintptr) bitvector {
720 // Extend the temp buffer if necessary.
721 nptr := size / ptrSize
Russ Cox3965d752015-01-16 14:43:38 -0500722 if uintptr(len(tmpbuf)) < nptr*typeBitsWidth/8+1 {
Russ Cox1e2d2f02014-11-11 17:05:02 -0500723 if tmpbuf != nil {
724 sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys)
725 }
Russ Cox3965d752015-01-16 14:43:38 -0500726 n := nptr*typeBitsWidth/8 + 1
Russ Cox1e2d2f02014-11-11 17:05:02 -0500727 p := sysAlloc(n, &memstats.other_sys)
728 if p == nil {
Keith Randallb2a950b2014-12-27 20:58:00 -0800729 throw("heapdump: out of memory")
Russ Cox1e2d2f02014-11-11 17:05:02 -0500730 }
731 tmpbuf = (*[1 << 30]byte)(p)[:n]
732 }
Russ Cox3965d752015-01-16 14:43:38 -0500733 // Convert heap bitmap to type bitmap.
734 i := uintptr(0)
735 hbits := heapBitsForAddr(p)
736 for ; i < nptr; i++ {
737 bits := hbits.typeBits()
738 if bits == typeDead {
739 break // end of object
Russ Cox1e2d2f02014-11-11 17:05:02 -0500740 }
Russ Cox3965d752015-01-16 14:43:38 -0500741 hbits = hbits.next()
742 tmpbuf[i*typeBitsWidth/8] &^= (typeMask << ((i * typeBitsWidth) % 8))
743 tmpbuf[i*typeBitsWidth/8] |= bits << ((i * typeBitsWidth) % 8)
Russ Cox1e2d2f02014-11-11 17:05:02 -0500744 }
Russ Cox3965d752015-01-16 14:43:38 -0500745 return bitvector{int32(i * typeBitsWidth), &tmpbuf[0]}
Russ Cox1e2d2f02014-11-11 17:05:02 -0500746}