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