1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
|
// 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))
}
}
|