github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/runtime/heapdump.go (about) 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 "runtime/internal/sys" 16 "unsafe" 17 ) 18 19 //go:linkname runtime_debug_WriteHeapDump runtime/debug.WriteHeapDump 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 // fd to write the dump to. 56 var tmpbuf []byte 57 58 // buffer of pending write data 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 // Cache of types that have been serialized already. 96 // We use a type's hash field to pick a bucket. 97 // Inside a bucket, we keep a list of types that 98 // have been serialized so far, most recently used first. 99 // Note: when a bucket overflows we may end up 100 // serializing a type more than once. That's ok. 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 // dump a uint64 in a varint format parseable by encoding/binary 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 // dump varint uint64 length followed by memory contents 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 // dump information for a type 153 func dumptype(t *_type) { 154 if t == nil { 155 return 156 } 157 158 // If we've definitely serialized the type before, 159 // no need to do it again. 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 // Move-to-front 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 // Might not have been dumped yet. Dump it and 176 // remember we did so. 177 for j := typeCacheAssoc - 1; j > 0; j-- { 178 b.t[j] = b.t[j-1] 179 } 180 b.t[0] = t 181 182 // dump the type 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.kind&kindNoPointers == 0) 199 } 200 201 // dump an object 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 // Information passed up from the callee frame about 226 // the layout of the outargs region. 227 argoff uintptr // where the arguments start in the frame 228 arglen uintptr // size of args region 229 args bitvector // if args.n >= 0, pointer map of args region 230 sp *uint8 // callee sp 231 depth uintptr // depth in call stack (0 == most recent) 232 } 233 234 // dump kinds & offsets of interesting fields in bv 235 func dumpbv(cbv *bitvector, offset uintptr) { 236 bv := gobv(*cbv) 237 for i := uintptr(0); i < bv.n; i++ { 238 if bv.bytedata[i/8]>>(i%8)&1 == 1 { 239 dumpint(fieldKindPtr) 240 dumpint(uint64(offset + i*sys.PtrSize)) 241 } 242 } 243 } 244 245 func dumpframe(s *stkframe, arg unsafe.Pointer) bool { 246 child := (*childInfo)(arg) 247 f := s.fn 248 249 // Figure out what we can about our stack map 250 pc := s.pc 251 if pc != f.entry { 252 pc-- 253 } 254 pcdata := pcdatavalue(f, _PCDATA_StackMapIndex, pc, nil) 255 if pcdata == -1 { 256 // We do not have a valid pcdata value but there might be a 257 // stackmap for this function. It is likely that we are looking 258 // at the function prologue, assume so and hope for the best. 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 // Dump main body of stack frame. 271 dumpint(tagStackFrame) 272 dumpint(uint64(s.sp)) // lowest address in frame 273 dumpint(uint64(child.depth)) // # of frames deep on the stack 274 dumpint(uint64(uintptr(unsafe.Pointer(child.sp)))) // sp of child, or 0 if bottom of stack 275 dumpmemrange(unsafe.Pointer(s.sp), s.fp-s.sp) // frame contents 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 // Dump fields in the outargs section 286 if child.args.n >= 0 { 287 dumpbv(&child.args, child.argoff) 288 } else { 289 // conservative - everything might be a pointer 290 for off := child.argoff; off < child.argoff+child.arglen; off += sys.PtrSize { 291 dumpint(fieldKindPtr) 292 dumpint(uint64(off)) 293 } 294 } 295 296 // Dump fields in the local vars section 297 if stkmap == nil { 298 // No locals information, dump everything. 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 // Locals size information, dump just the locals. 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 // Locals bitmap information, scan just the pointers in 312 // locals. 313 dumpbv(&bv, s.varp-uintptr(bv.n)*sys.PtrSize-s.sp) 314 } 315 dumpint(fieldKindEol) 316 317 // Record arg info for parent. 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)) 350 dumpbool(false) // isbackground 351 dumpint(uint64(gp.waitsince)) 352 dumpstr(gp.waitreason) 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 // dump stack 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 // dump defer & panic records 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) // was p->defer, no longer recorded 385 dumpint(uint64(uintptr(unsafe.Pointer(p.link)))) 386 } 387 } 388 389 func dumpgs() { 390 // goroutines & stacks 391 for i := 0; uintptr(i) < allglen; i++ { 392 gp := allgs[i] 393 status := readgstatus(gp) // The world is stopped so gp will not be in a scan state. 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 // ok 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 // TODO(mwhudson): dump datamask etc from all objects 419 // data segment 420 dumpint(tagData) 421 dumpint(uint64(firstmoduledata.data)) 422 dumpmemrange(unsafe.Pointer(firstmoduledata.data), firstmoduledata.edata-firstmoduledata.data) 423 dumpfields(firstmoduledata.gcdatamask) 424 425 // bss segment 426 dumpint(tagBSS) 427 dumpint(uint64(firstmoduledata.bss)) 428 dumpmemrange(unsafe.Pointer(firstmoduledata.bss), firstmoduledata.ebss-firstmoduledata.bss) 429 dumpfields(firstmoduledata.gcbssmask) 430 431 // MSpan.types 432 for _, s := range mheap_.allspans { 433 if s.state == _MSpanInUse { 434 // Finalizers 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 // Finalizer queue 447 iterate_finq(finq_callback) 448 } 449 450 // Bit vector of free marks. 451 // Needs to be as big as the largest number of objects per span. 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) // little-endian ptrs 487 } else { 488 dumpbool(true) // big-endian ptrs 489 } 490 dumpint(sys.PtrSize) 491 dumpint(uint64(mheap_.arena_start)) 492 dumpint(uint64(mheap_.arena_used)) 493 dumpstr(sys.GOARCH) 494 dumpstr(sys.Goexperiment) 495 dumpint(uint64(ncpu)) 496 } 497 498 func itab_callback(tab *itab) { 499 t := tab._type 500 dumptype(t) 501 dumpint(tagItab) 502 dumpint(uint64(uintptr(unsafe.Pointer(tab)))) 503 dumpint(uint64(uintptr(unsafe.Pointer(t)))) 504 } 505 506 func dumpitabs() { 507 iterate_itabs(itab_callback) 508 } 509 510 func dumpms() { 511 for mp := allm; mp != nil; mp = mp.alllink { 512 dumpint(tagOSThread) 513 dumpint(uint64(uintptr(unsafe.Pointer(mp)))) 514 dumpint(uint64(mp.id)) 515 dumpint(mp.procid) 516 } 517 } 518 519 func dumpmemstats() { 520 dumpint(tagMemStats) 521 dumpint(memstats.alloc) 522 dumpint(memstats.total_alloc) 523 dumpint(memstats.sys) 524 dumpint(memstats.nlookup) 525 dumpint(memstats.nmalloc) 526 dumpint(memstats.nfree) 527 dumpint(memstats.heap_alloc) 528 dumpint(memstats.heap_sys) 529 dumpint(memstats.heap_idle) 530 dumpint(memstats.heap_inuse) 531 dumpint(memstats.heap_released) 532 dumpint(memstats.heap_objects) 533 dumpint(memstats.stacks_inuse) 534 dumpint(memstats.stacks_sys) 535 dumpint(memstats.mspan_inuse) 536 dumpint(memstats.mspan_sys) 537 dumpint(memstats.mcache_inuse) 538 dumpint(memstats.mcache_sys) 539 dumpint(memstats.buckhash_sys) 540 dumpint(memstats.gc_sys) 541 dumpint(memstats.other_sys) 542 dumpint(memstats.next_gc) 543 dumpint(memstats.last_gc_unix) 544 dumpint(memstats.pause_total_ns) 545 for i := 0; i < 256; i++ { 546 dumpint(memstats.pause_ns[i]) 547 } 548 dumpint(uint64(memstats.numgc)) 549 } 550 551 func dumpmemprof_callback(b *bucket, nstk uintptr, pstk *uintptr, size, allocs, frees uintptr) { 552 stk := (*[100000]uintptr)(unsafe.Pointer(pstk)) 553 dumpint(tagMemProf) 554 dumpint(uint64(uintptr(unsafe.Pointer(b)))) 555 dumpint(uint64(size)) 556 dumpint(uint64(nstk)) 557 for i := uintptr(0); i < nstk; i++ { 558 pc := stk[i] 559 f := findfunc(pc) 560 if !f.valid() { 561 var buf [64]byte 562 n := len(buf) 563 n-- 564 buf[n] = ')' 565 if pc == 0 { 566 n-- 567 buf[n] = '0' 568 } else { 569 for pc > 0 { 570 n-- 571 buf[n] = "0123456789abcdef"[pc&15] 572 pc >>= 4 573 } 574 } 575 n-- 576 buf[n] = 'x' 577 n-- 578 buf[n] = '0' 579 n-- 580 buf[n] = '(' 581 dumpslice(buf[n:]) 582 dumpstr("?") 583 dumpint(0) 584 } else { 585 dumpstr(funcname(f)) 586 if i > 0 && pc > f.entry { 587 pc-- 588 } 589 file, line := funcline(f, pc) 590 dumpstr(file) 591 dumpint(uint64(line)) 592 } 593 } 594 dumpint(uint64(allocs)) 595 dumpint(uint64(frees)) 596 } 597 598 func dumpmemprof() { 599 iterate_memprof(dumpmemprof_callback) 600 for _, s := range mheap_.allspans { 601 if s.state != _MSpanInUse { 602 continue 603 } 604 for sp := s.specials; sp != nil; sp = sp.next { 605 if sp.kind != _KindSpecialProfile { 606 continue 607 } 608 spp := (*specialprofile)(unsafe.Pointer(sp)) 609 p := s.base() + uintptr(spp.special.offset) 610 dumpint(tagAllocSample) 611 dumpint(uint64(p)) 612 dumpint(uint64(uintptr(unsafe.Pointer(spp.b)))) 613 } 614 } 615 } 616 617 var dumphdr = []byte("go1.7 heap dump\n") 618 619 func mdump() { 620 // make sure we're done sweeping 621 for _, s := range mheap_.allspans { 622 if s.state == _MSpanInUse { 623 s.ensureSwept() 624 } 625 } 626 memclrNoHeapPointers(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache)) 627 dwrite(unsafe.Pointer(&dumphdr[0]), uintptr(len(dumphdr))) 628 dumpparams() 629 dumpitabs() 630 dumpobjs() 631 dumpgs() 632 dumpms() 633 dumproots() 634 dumpmemstats() 635 dumpmemprof() 636 dumpint(tagEOF) 637 flush() 638 } 639 640 func writeheapdump_m(fd uintptr) { 641 _g_ := getg() 642 casgstatus(_g_.m.curg, _Grunning, _Gwaiting) 643 _g_.waitreason = "dumping heap" 644 645 // Update stats so we can dump them. 646 // As a side effect, flushes all the MCaches so the MSpan.freelist 647 // lists contain all the free objects. 648 updatememstats() 649 650 // Set dump file. 651 dumpfd = fd 652 653 // Call dump routine. 654 mdump() 655 656 // Reset dump file. 657 dumpfd = 0 658 if tmpbuf != nil { 659 sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys) 660 tmpbuf = nil 661 } 662 663 casgstatus(_g_.m.curg, _Gwaiting, _Grunning) 664 } 665 666 // dumpint() the kind & offset of each field in an object. 667 func dumpfields(bv bitvector) { 668 dumpbv(&bv, 0) 669 dumpint(fieldKindEol) 670 } 671 672 func makeheapobjbv(p uintptr, size uintptr) bitvector { 673 // Extend the temp buffer if necessary. 674 nptr := size / sys.PtrSize 675 if uintptr(len(tmpbuf)) < nptr/8+1 { 676 if tmpbuf != nil { 677 sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys) 678 } 679 n := nptr/8 + 1 680 p := sysAlloc(n, &memstats.other_sys) 681 if p == nil { 682 throw("heapdump: out of memory") 683 } 684 tmpbuf = (*[1 << 30]byte)(p)[:n] 685 } 686 // Convert heap bitmap to pointer bitmap. 687 for i := uintptr(0); i < nptr/8+1; i++ { 688 tmpbuf[i] = 0 689 } 690 i := uintptr(0) 691 hbits := heapBitsForAddr(p) 692 for ; i < nptr; i++ { 693 if i != 1 && !hbits.morePointers() { 694 break // end of object 695 } 696 if hbits.isPointer() { 697 tmpbuf[i/8] |= 1 << (i % 8) 698 } 699 hbits = hbits.next() 700 } 701 return bitvector{int32(i), &tmpbuf[0]} 702 }