|  | // Copyright 2009 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 runtime | 
|  |  | 
|  | import ( | 
|  | "runtime/internal/atomic" | 
|  | "runtime/internal/sys" | 
|  | "unsafe" | 
|  | ) | 
|  |  | 
|  | // The compiler knows that a print of a value of this type | 
|  | // should use printhex instead of printuint (decimal). | 
|  | type hex uint64 | 
|  |  | 
|  | func bytes(s string) (ret []byte) { | 
|  | rp := (*slice)(unsafe.Pointer(&ret)) | 
|  | sp := stringStructOf(&s) | 
|  | rp.array = sp.str | 
|  | rp.len = sp.len | 
|  | rp.cap = sp.len | 
|  | return | 
|  | } | 
|  |  | 
|  | var ( | 
|  | // printBacklog is a circular buffer of messages written with the builtin | 
|  | // print* functions, for use in postmortem analysis of core dumps. | 
|  | printBacklog      [512]byte | 
|  | printBacklogIndex int | 
|  | ) | 
|  |  | 
|  | // recordForPanic maintains a circular buffer of messages written by the | 
|  | // runtime leading up to a process crash, allowing the messages to be | 
|  | // extracted from a core dump. | 
|  | // | 
|  | // The text written during a process crash (following "panic" or "fatal | 
|  | // error") is not saved, since the goroutine stacks will generally be readable | 
|  | // from the runtime datastructures in the core file. | 
|  | func recordForPanic(b []byte) { | 
|  | printlock() | 
|  |  | 
|  | if atomic.Load(&panicking) == 0 { | 
|  | // Not actively crashing: maintain circular buffer of print output. | 
|  | for i := 0; i < len(b); { | 
|  | n := copy(printBacklog[printBacklogIndex:], b[i:]) | 
|  | i += n | 
|  | printBacklogIndex += n | 
|  | printBacklogIndex %= len(printBacklog) | 
|  | } | 
|  | } | 
|  |  | 
|  | printunlock() | 
|  | } | 
|  |  | 
|  | var debuglock mutex | 
|  |  | 
|  | // The compiler emits calls to printlock and printunlock around | 
|  | // the multiple calls that implement a single Go print or println | 
|  | // statement. Some of the print helpers (printslice, for example) | 
|  | // call print recursively. There is also the problem of a crash | 
|  | // happening during the print routines and needing to acquire | 
|  | // the print lock to print information about the crash. | 
|  | // For both these reasons, let a thread acquire the printlock 'recursively'. | 
|  |  | 
|  | func printlock() { | 
|  | mp := getg().m | 
|  | mp.locks++ // do not reschedule between printlock++ and lock(&debuglock). | 
|  | mp.printlock++ | 
|  | if mp.printlock == 1 { | 
|  | lock(&debuglock) | 
|  | } | 
|  | mp.locks-- // now we know debuglock is held and holding up mp.locks for us. | 
|  | } | 
|  |  | 
|  | func printunlock() { | 
|  | mp := getg().m | 
|  | mp.printlock-- | 
|  | if mp.printlock == 0 { | 
|  | unlock(&debuglock) | 
|  | } | 
|  | } | 
|  |  | 
|  | // write to goroutine-local buffer if diverting output, | 
|  | // or else standard error. | 
|  | func gwrite(b []byte) { | 
|  | if len(b) == 0 { | 
|  | return | 
|  | } | 
|  | recordForPanic(b) | 
|  | gp := getg() | 
|  | // Don't use the writebuf if gp.m is dying. We want anything | 
|  | // written through gwrite to appear in the terminal rather | 
|  | // than be written to in some buffer, if we're in a panicking state. | 
|  | // Note that we can't just clear writebuf in the gp.m.dying case | 
|  | // because a panic isn't allowed to have any write barriers. | 
|  | if gp == nil || gp.writebuf == nil || gp.m.dying > 0 { | 
|  | writeErr(b) | 
|  | return | 
|  | } | 
|  |  | 
|  | n := copy(gp.writebuf[len(gp.writebuf):cap(gp.writebuf)], b) | 
|  | gp.writebuf = gp.writebuf[:len(gp.writebuf)+n] | 
|  | } | 
|  |  | 
|  | func printsp() { | 
|  | printstring(" ") | 
|  | } | 
|  |  | 
|  | func printnl() { | 
|  | printstring("\n") | 
|  | } | 
|  |  | 
|  | func printbool(v bool) { | 
|  | if v { | 
|  | printstring("true") | 
|  | } else { | 
|  | printstring("false") | 
|  | } | 
|  | } | 
|  |  | 
|  | func printfloat(v float64) { | 
|  | switch { | 
|  | case v != v: | 
|  | printstring("NaN") | 
|  | return | 
|  | case v+v == v && v > 0: | 
|  | printstring("+Inf") | 
|  | return | 
|  | case v+v == v && v < 0: | 
|  | printstring("-Inf") | 
|  | return | 
|  | } | 
|  |  | 
|  | const n = 7 // digits printed | 
|  | var buf [n + 7]byte | 
|  | buf[0] = '+' | 
|  | e := 0 // exp | 
|  | if v == 0 { | 
|  | if 1/v < 0 { | 
|  | buf[0] = '-' | 
|  | } | 
|  | } else { | 
|  | if v < 0 { | 
|  | v = -v | 
|  | buf[0] = '-' | 
|  | } | 
|  |  | 
|  | // normalize | 
|  | for v >= 10 { | 
|  | e++ | 
|  | v /= 10 | 
|  | } | 
|  | for v < 1 { | 
|  | e-- | 
|  | v *= 10 | 
|  | } | 
|  |  | 
|  | // round | 
|  | h := 5.0 | 
|  | for i := 0; i < n; i++ { | 
|  | h /= 10 | 
|  | } | 
|  | v += h | 
|  | if v >= 10 { | 
|  | e++ | 
|  | v /= 10 | 
|  | } | 
|  | } | 
|  |  | 
|  | // format +d.dddd+edd | 
|  | for i := 0; i < n; i++ { | 
|  | s := int(v) | 
|  | buf[i+2] = byte(s + '0') | 
|  | v -= float64(s) | 
|  | v *= 10 | 
|  | } | 
|  | buf[1] = buf[2] | 
|  | buf[2] = '.' | 
|  |  | 
|  | buf[n+2] = 'e' | 
|  | buf[n+3] = '+' | 
|  | if e < 0 { | 
|  | e = -e | 
|  | buf[n+3] = '-' | 
|  | } | 
|  |  | 
|  | buf[n+4] = byte(e/100) + '0' | 
|  | buf[n+5] = byte(e/10)%10 + '0' | 
|  | buf[n+6] = byte(e%10) + '0' | 
|  | gwrite(buf[:]) | 
|  | } | 
|  |  | 
|  | func printcomplex(c complex128) { | 
|  | print("(", real(c), imag(c), "i)") | 
|  | } | 
|  |  | 
|  | func printuint(v uint64) { | 
|  | var buf [100]byte | 
|  | i := len(buf) | 
|  | for i--; i > 0; i-- { | 
|  | buf[i] = byte(v%10 + '0') | 
|  | if v < 10 { | 
|  | break | 
|  | } | 
|  | v /= 10 | 
|  | } | 
|  | gwrite(buf[i:]) | 
|  | } | 
|  |  | 
|  | func printint(v int64) { | 
|  | if v < 0 { | 
|  | printstring("-") | 
|  | v = -v | 
|  | } | 
|  | printuint(uint64(v)) | 
|  | } | 
|  |  | 
|  | func printhex(v uint64) { | 
|  | const dig = "0123456789abcdef" | 
|  | var buf [100]byte | 
|  | i := len(buf) | 
|  | for i--; i > 0; i-- { | 
|  | buf[i] = dig[v%16] | 
|  | if v < 16 { | 
|  | break | 
|  | } | 
|  | v /= 16 | 
|  | } | 
|  | i-- | 
|  | buf[i] = 'x' | 
|  | i-- | 
|  | buf[i] = '0' | 
|  | gwrite(buf[i:]) | 
|  | } | 
|  |  | 
|  | func printpointer(p unsafe.Pointer) { | 
|  | printhex(uint64(uintptr(p))) | 
|  | } | 
|  |  | 
|  | func printstring(s string) { | 
|  | gwrite(bytes(s)) | 
|  | } | 
|  |  | 
|  | func printslice(s []byte) { | 
|  | sp := (*slice)(unsafe.Pointer(&s)) | 
|  | print("[", len(s), "/", cap(s), "]") | 
|  | printpointer(sp.array) | 
|  | } | 
|  |  | 
|  | func printeface(e eface) { | 
|  | print("(", e._type, ",", e.data, ")") | 
|  | } | 
|  |  | 
|  | func printiface(i iface) { | 
|  | print("(", i.tab, ",", i.data, ")") | 
|  | } | 
|  |  | 
|  | // hexdumpWords prints a word-oriented hex dump of [p, end). | 
|  | // | 
|  | // If mark != nil, it will be called with each printed word's address | 
|  | // and should return a character mark to appear just before that | 
|  | // word's value. It can return 0 to indicate no mark. | 
|  | func hexdumpWords(p, end uintptr, mark func(uintptr) byte) { | 
|  | p1 := func(x uintptr) { | 
|  | var buf [2 * sys.PtrSize]byte | 
|  | for i := len(buf) - 1; i >= 0; i-- { | 
|  | if x&0xF < 10 { | 
|  | buf[i] = byte(x&0xF) + '0' | 
|  | } else { | 
|  | buf[i] = byte(x&0xF) - 10 + 'a' | 
|  | } | 
|  | x >>= 4 | 
|  | } | 
|  | gwrite(buf[:]) | 
|  | } | 
|  |  | 
|  | printlock() | 
|  | var markbuf [1]byte | 
|  | markbuf[0] = ' ' | 
|  | for i := uintptr(0); p+i < end; i += sys.PtrSize { | 
|  | if i%16 == 0 { | 
|  | if i != 0 { | 
|  | println() | 
|  | } | 
|  | p1(p + i) | 
|  | print(": ") | 
|  | } | 
|  |  | 
|  | if mark != nil { | 
|  | markbuf[0] = mark(p + i) | 
|  | if markbuf[0] == 0 { | 
|  | markbuf[0] = ' ' | 
|  | } | 
|  | } | 
|  | gwrite(markbuf[:]) | 
|  | val := *(*uintptr)(unsafe.Pointer(p + i)) | 
|  | p1(val) | 
|  | print(" ") | 
|  |  | 
|  | // Can we symbolize val? | 
|  | fn := findfunc(val) | 
|  | if fn.valid() { | 
|  | print("<", funcname(fn), "+", val-fn.entry, "> ") | 
|  | } | 
|  | } | 
|  | println() | 
|  | printunlock() | 
|  | } |