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