| // 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 ( | |
| "internal/strconv" | |
| "unsafe" | |
| ) | |
| // The compiler knows that a print of a value of this type | |
| // should use printhex instead of printuint (decimal). | |
| type hex uint64 | |
| // The compiler knows that a print of a value of this type should use | |
| // printquoted instead of printstring. | |
| type quoted string | |
| 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 data structures in the core file. | |
| func recordForPanic(b []byte) { | |
| printlock() | |
| if panicking.Load() == 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") | |
| } | |
| } | |
| // float64 requires 1+17+1+1+1+3 = 24 bytes max (sign+digits+decimal point+e+sign+exponent digits). | |
| const float64Bytes = 24 | |
| func printfloat64(v float64) { | |
| var buf [float64Bytes]byte | |
| gwrite(strconv.AppendFloat(buf[:0], v, 'g', -1, 64)) | |
| } | |
| // float32 requires 1+9+1+1+1+2 = 15 bytes max (sign+digits+decimal point+e+sign+exponent digits). | |
| const float32Bytes = 15 | |
| func printfloat32(v float32) { | |
| var buf [float32Bytes]byte | |
| gwrite(strconv.AppendFloat(buf[:0], float64(v), 'g', -1, 32)) | |
| } | |
| // complex128 requires 24+24+1+1+1 = 51 bytes max (paren+float64+float64+i+paren). | |
| const complex128Bytes = 2*float64Bytes + 3 | |
| func printcomplex128(c complex128) { | |
| var buf [complex128Bytes]byte | |
| gwrite(strconv.AppendComplex(buf[:0], c, 'g', -1, 128)) | |
| } | |
| // complex64 requires 15+15+1+1+1 = 33 bytes max (paren+float32+float32+i+paren). | |
| const complex64Bytes = 2*float32Bytes + 3 | |
| func printcomplex64(c complex64) { | |
| var buf [complex64Bytes]byte | |
| gwrite(strconv.AppendComplex(buf[:0], complex128(c), 'g', -1, 64)) | |
| } | |
| func printuint(v uint64) { | |
| // Note: Avoiding strconv.AppendUint so that it's clearer | |
| // that there are no allocations in this routine. | |
| // cmd/link/internal/ld.TestAbstractOriginSanity | |
| // sees the append and doesn't realize it doesn't allocate. | |
| var buf [20]byte | |
| i := strconv.RuntimeFormatBase10(buf[:], v) | |
| gwrite(buf[i:]) | |
| } | |
| func printint(v int64) { | |
| // Note: Avoiding strconv.AppendUint so that it's clearer | |
| // that there are no allocations in this routine. | |
| // cmd/link/internal/ld.TestAbstractOriginSanity | |
| // sees the append and doesn't realize it doesn't allocate. | |
| neg := v < 0 | |
| u := uint64(v) | |
| if neg { | |
| u = -u | |
| } | |
| var buf [20]byte | |
| i := strconv.RuntimeFormatBase10(buf[:], u) | |
| if neg { | |
| i-- | |
| buf[i] = '-' | |
| } | |
| gwrite(buf[i:]) | |
| } | |
| var minhexdigits = 0 // protected by printlock | |
| func printhexopts(include0x bool, mindigits int, 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 && len(buf)-i >= mindigits { | |
| break | |
| } | |
| v /= 16 | |
| } | |
| if include0x { | |
| i-- | |
| buf[i] = 'x' | |
| i-- | |
| buf[i] = '0' | |
| } | |
| gwrite(buf[i:]) | |
| } | |
| func printhex(v uint64) { | |
| printhexopts(true, minhexdigits, v) | |
| } | |
| func printquoted(s string) { | |
| printlock() | |
| gwrite([]byte(`"`)) | |
| for i, r := range s { | |
| switch r { | |
| case '\n': | |
| gwrite([]byte(`\n`)) | |
| continue | |
| case '\r': | |
| gwrite([]byte(`\r`)) | |
| continue | |
| case '\t': | |
| gwrite([]byte(`\t`)) | |
| print() | |
| continue | |
| case '\\', '"': | |
| gwrite([]byte{byte('\\'), byte(r)}) | |
| continue | |
| case runeError: | |
| // Distinguish errors from a valid encoding of U+FFFD. | |
| if _, j := decoderune(s, uint(i)); j == uint(i+1) { | |
| gwrite(bytes(`\x`)) | |
| printhexopts(false, 2, uint64(s[i])) | |
| continue | |
| } | |
| // Fall through to quoting. | |
| } | |
| // For now, only allow basic printable ascii through unescaped | |
| if r >= ' ' && r <= '~' { | |
| gwrite([]byte{byte(r)}) | |
| } else if r < 127 { | |
| gwrite(bytes(`\x`)) | |
| printhexopts(false, 2, uint64(r)) | |
| } else if r < 0x1_0000 { | |
| gwrite(bytes(`\u`)) | |
| printhexopts(false, 4, uint64(r)) | |
| } else { | |
| gwrite(bytes(`\U`)) | |
| printhexopts(false, 8, uint64(r)) | |
| } | |
| } | |
| gwrite([]byte{byte('"')}) | |
| printunlock() | |
| } | |
| func printpointer(p unsafe.Pointer) { | |
| printhex(uint64(uintptr(p))) | |
| } | |
| func printuintptr(p uintptr) { | |
| printhex(uint64(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, ")") | |
| } |