| // Copyright 2016 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package core provides functions for reading core dumps |
| // and examining their contained heaps. |
| package core |
| |
| import ( |
| "bytes" |
| "encoding/binary" |
| "fmt" |
| "runtime" |
| "sort" |
| ) |
| |
| // RawDump provides raw access to the heap records in a core file. |
| // The raw records in this file are described by other structs named Raw{*}. |
| // All []byte slices are direct references to the underlying mmap'd file. |
| // These references will become invalid as soon as the RawDump is closed. |
| type RawDump struct { |
| Params *RawParams |
| MemStats *runtime.MemStats |
| |
| HeapObjects []RawSegment // heap objects sorted by Addr, low-to-high |
| GlobalSegments []RawSegment // data, bss, and noptrbss segments |
| |
| OSThreads []*RawOSThread |
| Goroutines []*RawGoroutine |
| StackFrames []*RawStackFrame |
| OtherRoots []*RawOtherRoot |
| Finalizers []*RawFinalizer |
| Defers []*RawDefer |
| Panics []*RawPanic |
| |
| TypeFromItab map[uint64]uint64 // map from itab address to the type address that itab represents |
| TypeFromAddr map[uint64]*RawType // map from RawType.Addr to RawType |
| |
| MemProfMap map[uint64]*RawMemProfEntry |
| AllocSamples []*RawAllocSample |
| |
| fmap *mmapFile |
| } |
| |
| // RawParams holds metadata about the program that generated the dump. |
| type RawParams struct { |
| // Info about the memory space |
| |
| ByteOrder binary.ByteOrder // byte order of all memory in this dump |
| PtrSize uint64 // in bytes |
| HeapStart uint64 // heap start address |
| HeapEnd uint64 // heap end address (this is the last byte in the heap + 1) |
| |
| // Info about the program that generated this heapdump |
| |
| GoArch string // GOARCH of the runtime library that generated this dump |
| GoExperiment string // GOEXPERIMENT of the toolchain that build the runtime library |
| NCPU uint64 // number of physical cpus available to the program |
| } |
| |
| // RawSegment represents a segment of memory. |
| type RawSegment struct { |
| Addr uint64 // base address of the segment |
| Data []byte // data for this segment |
| PtrFields RawPtrFields // offsets of ptr fields within this segment |
| } |
| |
| // RawPtrFields represents a pointer field. |
| type RawPtrFields struct { |
| encoded []byte // list of uvarint-encoded offsets, or nil if none |
| startOff, endOff uint64 // decoded offsets are translated and clipped to [startOff,endOff) |
| } |
| |
| // RawOSThread represents an OS thread. |
| type RawOSThread struct { |
| MAddr uint64 // address of the OS thread descriptor (M) |
| GoID uint64 // go's internal ID for the thread |
| ProcID uint64 // kernel's ID for the thread |
| } |
| |
| // RawGoroutine represents a goroutine structure. |
| type RawGoroutine struct { |
| GAddr uint64 // address of the goroutine descriptor |
| SP uint64 // current stack pointer (lowest address in the currently running frame) |
| GoID uint64 // goroutine ID |
| GoPC uint64 // PC of the go statement that created this goroutine |
| Status uint64 |
| IsSystem bool // true if started by the system |
| IsBackground bool // always false in go1.7 |
| WaitSince uint64 // time the goroutine started waiting, in nanoseconds since the Unix epoch |
| WaitReason string |
| CtxtAddr uint64 // address of the scheduling ctxt |
| MAddr uint64 // address of the OS thread descriptor (M) |
| TopDeferAddr uint64 // address of the top defer record |
| TopPanicAddr uint64 // address of the top panic record |
| } |
| |
| // RawStackFrame represents a stack frame. |
| type RawStackFrame struct { |
| Name string |
| Depth uint64 // 0 = bottom of stack (currently running frame) |
| CalleeSP uint64 // stack pointer of the child frame (or 0 for the bottom-most frame) |
| EntryPC uint64 // entry PC for the function |
| PC uint64 // current PC being executed |
| NextPC uint64 // for callers, where the function resumes (if anywhere) after the callee is done |
| Segment RawSegment // local vars (Segment.Addr is the stack pointer, i.e., lowest address in the frame) |
| } |
| |
| // RawOtherRoot represents the other roots not in RawDump's other fields. |
| type RawOtherRoot struct { |
| Description string |
| Addr uint64 // address pointed to by this root |
| } |
| |
| // RawFinalizer represents a finalizer. |
| type RawFinalizer struct { |
| IsQueued bool // if true, the object is unreachable and the finalizer is ready to run |
| ObjAddr uint64 // address of the object to finalize |
| ObjTypeAddr uint64 // address of the descriptor for typeof(obj) |
| FnAddr uint64 // function to be run (a FuncVal*) |
| FnArgTypeAddr uint64 // address of the descriptor for the type of the function argument |
| FnPC uint64 // PC of finalizer entry point |
| } |
| |
| // RawDefer represents a defer. |
| type RawDefer struct { |
| Addr uint64 // address of the defer record |
| GAddr uint64 // address of the containing goroutine's descriptor |
| ArgP uint64 // stack pointer giving the args for defer (TODO: is this right?) |
| PC uint64 // PC of the defer instruction |
| FnAddr uint64 // function to be run (a FuncVal*) |
| FnPC uint64 // PC of the defered function's entry point |
| LinkAddr uint64 // address of the next defer record in this chain |
| } |
| |
| // RawPanic represents a panic. |
| type RawPanic struct { |
| Addr uint64 // address of the panic record |
| GAddr uint64 // address of the containing goroutine's descriptor |
| ArgTypeAddr uint64 // type of the panic arg |
| ArgAddr uint64 // address of the panic arg |
| DeferAddr uint64 // address of the defer record that is currently running |
| LinkAddr uint64 // address of the next panic record in this chain |
| } |
| |
| // RawType repesents the Go runtime's representation of a type. |
| type RawType struct { |
| Addr uint64 // address of the type descriptor |
| Size uint64 // in bytes |
| Name string // not necessarily unique |
| // If true, this type is equivalent to a single pointer, so ifaces can store |
| // this type directly in the data field (without indirection). |
| DirectIFace bool |
| } |
| |
| // RawMemProfEntry represents a memory profiler entry. |
| type RawMemProfEntry struct { |
| Size uint64 // size of the allocated object |
| NumAllocs uint64 // number of allocations |
| NumFrees uint64 // number of frees |
| Stacks []RawMemProfFrame // call stacks |
| } |
| |
| // RawMemProfFrame represents a memory profiler frame. |
| type RawMemProfFrame struct { |
| Func []byte // string left as []byte reference to save memory |
| File []byte // string left as []byte reference to save memory |
| Line uint64 |
| } |
| |
| // RawAllocSample represents a memory profiler allocation sample. |
| type RawAllocSample struct { |
| Addr uint64 // address of object |
| Prof *RawMemProfEntry // record of allocation site |
| } |
| |
| // Close closes the file. |
| func (r *RawDump) Close() error { |
| return r.fmap.Close() |
| } |
| |
| // FindSegment returns the segment that contains the given address, or |
| // nil of no segment contains the address. |
| func (r *RawDump) FindSegment(addr uint64) *RawSegment { |
| // Binary search for an upper-bound heap object, then check |
| // if the previous object contains addr. |
| k := sort.Search(len(r.HeapObjects), func(k int) bool { |
| return addr < r.HeapObjects[k].Addr |
| }) |
| k-- |
| if k >= 0 && r.HeapObjects[k].Contains(addr) { |
| return &r.HeapObjects[k] |
| } |
| |
| // Check all global segments. |
| for k := range r.GlobalSegments { |
| if r.GlobalSegments[k].Contains(addr) { |
| return &r.GlobalSegments[k] |
| } |
| } |
| |
| // NB: Stack-local vars are technically allocated in the heap, since stack frames are |
| // allocated in the heap space, however, stack frames don't show up in r.HeapObjects. |
| for _, f := range r.StackFrames { |
| if f.Segment.Contains(addr) { |
| return &f.Segment |
| } |
| } |
| |
| return nil |
| } |
| |
| // Contains returns true if the segment contains the given address. |
| func (r RawSegment) Contains(addr uint64) bool { |
| return r.Addr <= addr && addr < r.Addr+r.Size() |
| } |
| |
| // ContainsRange returns true if the segment contains the range [addr, addr+size). |
| func (r RawSegment) ContainsRange(addr, size uint64) bool { |
| if !r.Contains(addr) { |
| return false |
| } |
| if size > 0 && !r.Contains(addr+size-1) { |
| return false |
| } |
| return true |
| } |
| |
| // Size returns the size of the segment in bytes. |
| func (r RawSegment) Size() uint64 { |
| return uint64(len(r.Data)) |
| } |
| |
| // Slice takes a slice of the given segment. Panics if [offset,offset+size) |
| // is out-of-bounds. The resulting RawSegment.PtrOffsets will clipped and |
| // translated into the new segment. |
| func (r RawSegment) Slice(offset, size uint64) *RawSegment { |
| if offset+size > uint64(len(r.Data)) { |
| panic(fmt.Errorf("slice(%d,%d) out-of-bounds of segment @%x sz=%d", offset, size, r.Addr, len(r.Data))) |
| } |
| return &RawSegment{ |
| Addr: r.Addr + offset, |
| Data: r.Data[offset : offset+size : offset+size], |
| PtrFields: RawPtrFields{ |
| encoded: r.PtrFields.encoded, |
| startOff: r.PtrFields.startOff + offset, |
| endOff: r.PtrFields.startOff + offset + size, |
| }, |
| } |
| } |
| |
| // Offsets decodes the list of ptr field offsets. |
| func (r RawPtrFields) Offsets() []uint64 { |
| if r.encoded == nil { |
| return nil |
| } |
| |
| // NB: This should never fail since we already decoded the varints once |
| // when parsing the file originally. Hence we panic on failure. |
| reader := bytes.NewReader(r.encoded) |
| readUint64 := func() uint64 { |
| x, err := binary.ReadUvarint(reader) |
| if err != nil { |
| panic(fmt.Errorf("unexpected failure decoding uvarint: %v", err)) |
| } |
| return x |
| } |
| |
| var out []uint64 |
| for { |
| k := readUint64() |
| switch k { |
| case 0: // end |
| return out |
| case 1: // ptr |
| x := readUint64() |
| if r.startOff <= x && x < r.endOff { |
| out = append(out, x-r.startOff) |
| } |
| default: |
| panic(fmt.Errorf("unexpected FieldKind %d", k)) |
| } |
| } |
| } |
| |
| // ReadPtr decodes a ptr from the given byte slice. |
| func (r *RawParams) ReadPtr(b []byte) uint64 { |
| switch r.PtrSize { |
| case 4: |
| return uint64(r.ByteOrder.Uint32(b)) |
| case 8: |
| return r.ByteOrder.Uint64(b) |
| default: |
| panic(fmt.Errorf("unsupported PtrSize=%d", r.PtrSize)) |
| } |
| } |
| |
| // WritePtr encodes a ptr into the given byte slice. |
| func (r *RawParams) WritePtr(b []byte, addr uint64) { |
| switch r.PtrSize { |
| case 4: |
| r.ByteOrder.PutUint32(b, uint32(addr)) |
| case 8: |
| r.ByteOrder.PutUint64(b, addr) |
| default: |
| panic(fmt.Errorf("unsupported PtrSize=%d", r.PtrSize)) |
| } |
| } |