package runtime
import (
"internal/abi"
"internal/runtime/atomic"
"runtime/internal/sys"
"unsafe"
)
const debugLogBytes = 16 << 10
const debugLogStringLimit = debugLogBytes / 8
func dlog() *dlogger {
if !dlogEnabled {
return nil
}
tick , nano := uint64 (cputicks ()), uint64 (nanotime ())
l := getCachedDlogger ()
if l == nil {
allp := (*uintptr )(unsafe .Pointer (&allDloggers ))
all := (*dlogger )(unsafe .Pointer (atomic .Loaduintptr (allp )))
for l1 := all ; l1 != nil ; l1 = l1 .allLink {
if l1 .owned .Load () == 0 && l1 .owned .CompareAndSwap (0 , 1 ) {
l = l1
break
}
}
}
if l == nil {
l = (*dlogger )(sysAllocOS (unsafe .Sizeof (dlogger {})))
if l == nil {
throw ("failed to allocate debug log" )
}
l .w .r .data = &l .w .data
l .owned .Store (1 )
headp := (*uintptr )(unsafe .Pointer (&allDloggers ))
for {
head := atomic .Loaduintptr (headp )
l .allLink = (*dlogger )(unsafe .Pointer (head ))
if atomic .Casuintptr (headp , head , uintptr (unsafe .Pointer (l ))) {
break
}
}
}
const deltaLimit = 1 <<(3 *7 ) - 1
if tick -l .w .tick > deltaLimit || nano -l .w .nano > deltaLimit {
l .w .writeSync (tick , nano )
}
l .w .ensure (debugLogHeaderSize )
l .w .write += debugLogHeaderSize
l .w .uvarint (tick - l .w .tick )
l .w .uvarint (nano - l .w .nano )
gp := getg ()
if gp != nil && gp .m != nil && gp .m .p != 0 {
l .w .varint (int64 (gp .m .p .ptr ().id ))
} else {
l .w .varint (-1 )
}
return l
}
type dlogger struct {
_ sys .NotInHeap
w debugLogWriter
allLink *dlogger
owned atomic .Uint32
}
var allDloggers *dlogger
func (l *dlogger ) end () {
if !dlogEnabled {
return
}
size := l .w .write - l .w .r .end
if !l .w .writeFrameAt (l .w .r .end , size ) {
throw ("record too large" )
}
l .w .r .end = l .w .write
if putCachedDlogger (l ) {
return
}
l .owned .Store (0 )
}
const (
debugLogUnknown = 1 + iota
debugLogBoolTrue
debugLogBoolFalse
debugLogInt
debugLogUint
debugLogHex
debugLogPtr
debugLogString
debugLogConstString
debugLogStringOverflow
debugLogPC
debugLogTraceback
)
func (l *dlogger ) b (x bool ) *dlogger {
if !dlogEnabled {
return l
}
if x {
l .w .byte (debugLogBoolTrue )
} else {
l .w .byte (debugLogBoolFalse )
}
return l
}
func (l *dlogger ) i (x int ) *dlogger {
return l .i64 (int64 (x ))
}
func (l *dlogger ) i8 (x int8 ) *dlogger {
return l .i64 (int64 (x ))
}
func (l *dlogger ) i16 (x int16 ) *dlogger {
return l .i64 (int64 (x ))
}
func (l *dlogger ) i32 (x int32 ) *dlogger {
return l .i64 (int64 (x ))
}
func (l *dlogger ) i64 (x int64 ) *dlogger {
if !dlogEnabled {
return l
}
l .w .byte (debugLogInt )
l .w .varint (x )
return l
}
func (l *dlogger ) u (x uint ) *dlogger {
return l .u64 (uint64 (x ))
}
func (l *dlogger ) uptr (x uintptr ) *dlogger {
return l .u64 (uint64 (x ))
}
func (l *dlogger ) u8 (x uint8 ) *dlogger {
return l .u64 (uint64 (x ))
}
func (l *dlogger ) u16 (x uint16 ) *dlogger {
return l .u64 (uint64 (x ))
}
func (l *dlogger ) u32 (x uint32 ) *dlogger {
return l .u64 (uint64 (x ))
}
func (l *dlogger ) u64 (x uint64 ) *dlogger {
if !dlogEnabled {
return l
}
l .w .byte (debugLogUint )
l .w .uvarint (x )
return l
}
func (l *dlogger ) hex (x uint64 ) *dlogger {
if !dlogEnabled {
return l
}
l .w .byte (debugLogHex )
l .w .uvarint (x )
return l
}
func (l *dlogger ) p (x any ) *dlogger {
if !dlogEnabled {
return l
}
l .w .byte (debugLogPtr )
if x == nil {
l .w .uvarint (0 )
} else {
v := efaceOf (&x )
switch v ._type .Kind_ & abi .KindMask {
case abi .Chan , abi .Func , abi .Map , abi .Pointer , abi .UnsafePointer :
l .w .uvarint (uint64 (uintptr (v .data )))
default :
throw ("not a pointer type" )
}
}
return l
}
func (l *dlogger ) s (x string ) *dlogger {
if !dlogEnabled {
return l
}
strData := unsafe .StringData (x )
datap := &firstmoduledata
if len (x ) > 4 && datap .etext <= uintptr (unsafe .Pointer (strData )) && uintptr (unsafe .Pointer (strData )) < datap .end {
l .w .byte (debugLogConstString )
l .w .uvarint (uint64 (len (x )))
l .w .uvarint (uint64 (uintptr (unsafe .Pointer (strData )) - datap .etext ))
} else {
l .w .byte (debugLogString )
var b []byte
bb := (*slice )(unsafe .Pointer (&b ))
bb .array = unsafe .Pointer (strData )
bb .len , bb .cap = len (x ), len (x )
if len (b ) > debugLogStringLimit {
b = b [:debugLogStringLimit ]
}
l .w .uvarint (uint64 (len (b )))
l .w .bytes (b )
if len (b ) != len (x ) {
l .w .byte (debugLogStringOverflow )
l .w .uvarint (uint64 (len (x ) - len (b )))
}
}
return l
}
func (l *dlogger ) pc (x uintptr ) *dlogger {
if !dlogEnabled {
return l
}
l .w .byte (debugLogPC )
l .w .uvarint (uint64 (x ))
return l
}
func (l *dlogger ) traceback (x []uintptr ) *dlogger {
if !dlogEnabled {
return l
}
l .w .byte (debugLogTraceback )
l .w .uvarint (uint64 (len (x )))
for _ , pc := range x {
l .w .uvarint (uint64 (pc ))
}
return l
}
type debugLogWriter struct {
_ sys .NotInHeap
write uint64
data debugLogBuf
tick, nano uint64
r debugLogReader
buf [10 ]byte
}
type debugLogBuf struct {
_ sys .NotInHeap
b [debugLogBytes ]byte
}
const (
debugLogHeaderSize = 2
debugLogSyncSize = debugLogHeaderSize + 2 *8
)
func (l *debugLogWriter ) ensure (n uint64 ) {
for l .write +n >= l .r .begin +uint64 (len (l .data .b )) {
if l .r .skip () == ^uint64 (0 ) {
throw ("record wrapped around" )
}
}
}
func (l *debugLogWriter ) writeFrameAt (pos , size uint64 ) bool {
l .data .b [pos %uint64 (len (l .data .b ))] = uint8 (size )
l .data .b [(pos +1 )%uint64 (len (l .data .b ))] = uint8 (size >> 8 )
return size <= 0xFFFF
}
func (l *debugLogWriter ) writeSync (tick , nano uint64 ) {
l .tick , l .nano = tick , nano
l .ensure (debugLogHeaderSize )
l .writeFrameAt (l .write , 0 )
l .write += debugLogHeaderSize
l .writeUint64LE (tick )
l .writeUint64LE (nano )
l .r .end = l .write
}
func (l *debugLogWriter ) writeUint64LE (x uint64 ) {
var b [8 ]byte
b [0 ] = byte (x )
b [1 ] = byte (x >> 8 )
b [2 ] = byte (x >> 16 )
b [3 ] = byte (x >> 24 )
b [4 ] = byte (x >> 32 )
b [5 ] = byte (x >> 40 )
b [6 ] = byte (x >> 48 )
b [7 ] = byte (x >> 56 )
l .bytes (b [:])
}
func (l *debugLogWriter ) byte (x byte ) {
l .ensure (1 )
pos := l .write
l .write ++
l .data .b [pos %uint64 (len (l .data .b ))] = x
}
func (l *debugLogWriter ) bytes (x []byte ) {
l .ensure (uint64 (len (x )))
pos := l .write
l .write += uint64 (len (x ))
for len (x ) > 0 {
n := copy (l .data .b [pos %uint64 (len (l .data .b )):], x )
pos += uint64 (n )
x = x [n :]
}
}
func (l *debugLogWriter ) varint (x int64 ) {
var u uint64
if x < 0 {
u = (^uint64 (x ) << 1 ) | 1
} else {
u = (uint64 (x ) << 1 )
}
l .uvarint (u )
}
func (l *debugLogWriter ) uvarint (u uint64 ) {
i := 0
for u >= 0x80 {
l .buf [i ] = byte (u ) | 0x80
u >>= 7
i ++
}
l .buf [i ] = byte (u )
i ++
l .bytes (l .buf [:i ])
}
type debugLogReader struct {
data *debugLogBuf
begin, end uint64
tick, nano uint64
}
func (r *debugLogReader ) skip () uint64 {
if r .begin +debugLogHeaderSize > r .end {
return ^uint64 (0 )
}
size := uint64 (r .readUint16LEAt (r .begin ))
if size == 0 {
r .tick = r .readUint64LEAt (r .begin + debugLogHeaderSize )
r .nano = r .readUint64LEAt (r .begin + debugLogHeaderSize + 8 )
size = debugLogSyncSize
}
if r .begin +size > r .end {
return ^uint64 (0 )
}
r .begin += size
return size
}
func (r *debugLogReader ) readUint16LEAt (pos uint64 ) uint16 {
return uint16 (r .data .b [pos %uint64 (len (r .data .b ))]) |
uint16 (r .data .b [(pos +1 )%uint64 (len (r .data .b ))])<<8
}
func (r *debugLogReader ) readUint64LEAt (pos uint64 ) uint64 {
var b [8 ]byte
for i := range b {
b [i ] = r .data .b [pos %uint64 (len (r .data .b ))]
pos ++
}
return uint64 (b [0 ]) | uint64 (b [1 ])<<8 |
uint64 (b [2 ])<<16 | uint64 (b [3 ])<<24 |
uint64 (b [4 ])<<32 | uint64 (b [5 ])<<40 |
uint64 (b [6 ])<<48 | uint64 (b [7 ])<<56
}
func (r *debugLogReader ) peek () (tick uint64 ) {
size := uint64 (0 )
for size == 0 {
if r .begin +debugLogHeaderSize > r .end {
return ^uint64 (0 )
}
size = uint64 (r .readUint16LEAt (r .begin ))
if size != 0 {
break
}
if r .begin +debugLogSyncSize > r .end {
return ^uint64 (0 )
}
r .tick = r .readUint64LEAt (r .begin + debugLogHeaderSize )
r .nano = r .readUint64LEAt (r .begin + debugLogHeaderSize + 8 )
r .begin += debugLogSyncSize
}
if r .begin +size > r .end {
return ^uint64 (0 )
}
pos := r .begin + debugLogHeaderSize
var u uint64
for i := uint (0 ); ; i += 7 {
b := r .data .b [pos %uint64 (len (r .data .b ))]
pos ++
u |= uint64 (b &^0x80 ) << i
if b &0x80 == 0 {
break
}
}
if pos > r .begin +size {
return ^uint64 (0 )
}
return r .tick + u
}
func (r *debugLogReader ) header () (end , tick , nano uint64 , p int ) {
size := uint64 (r .readUint16LEAt (r .begin ))
end = r .begin + size
r .begin += debugLogHeaderSize
tick = r .uvarint () + r .tick
nano = r .uvarint () + r .nano
p = int (r .varint ())
return
}
func (r *debugLogReader ) uvarint () uint64 {
var u uint64
for i := uint (0 ); ; i += 7 {
b := r .data .b [r .begin %uint64 (len (r .data .b ))]
r .begin ++
u |= uint64 (b &^0x80 ) << i
if b &0x80 == 0 {
break
}
}
return u
}
func (r *debugLogReader ) varint () int64 {
u := r .uvarint ()
var v int64
if u &1 == 0 {
v = int64 (u >> 1 )
} else {
v = ^int64 (u >> 1 )
}
return v
}
func (r *debugLogReader ) printVal () bool {
typ := r .data .b [r .begin %uint64 (len (r .data .b ))]
r .begin ++
switch typ {
default :
print ("<unknown field type " , hex (typ ), " pos " , r .begin -1 , " end " , r .end , ">\n" )
return false
case debugLogUnknown :
print ("<unknown kind>" )
case debugLogBoolTrue :
print (true )
case debugLogBoolFalse :
print (false )
case debugLogInt :
print (r .varint ())
case debugLogUint :
print (r .uvarint ())
case debugLogHex , debugLogPtr :
print (hex (r .uvarint ()))
case debugLogString :
sl := r .uvarint ()
if r .begin +sl > r .end {
r .begin = r .end
print ("<string length corrupted>" )
break
}
for sl > 0 {
b := r .data .b [r .begin %uint64 (len (r .data .b )):]
if uint64 (len (b )) > sl {
b = b [:sl ]
}
r .begin += uint64 (len (b ))
sl -= uint64 (len (b ))
gwrite (b )
}
case debugLogConstString :
len , ptr := int (r .uvarint ()), uintptr (r .uvarint ())
ptr += firstmoduledata .etext
str := stringStruct {
str : unsafe .Pointer (ptr ),
len : len ,
}
s := *(*string )(unsafe .Pointer (&str ))
print (s )
case debugLogStringOverflow :
print ("..(" , r .uvarint (), " more bytes).." )
case debugLogPC :
printDebugLogPC (uintptr (r .uvarint ()), false )
case debugLogTraceback :
n := int (r .uvarint ())
for i := 0 ; i < n ; i ++ {
print ("\n\t" )
printDebugLogPC (uintptr (r .uvarint ()), true )
}
}
return true
}
func printDebugLog() {
if !dlogEnabled {
return
}
printlock ()
allp := (*uintptr )(unsafe .Pointer (&allDloggers ))
all := (*dlogger )(unsafe .Pointer (atomic .Loaduintptr (allp )))
n := 0
for l := all ; l != nil ; l = l .allLink {
n ++
}
if n == 0 {
printunlock ()
return
}
type readState struct {
debugLogReader
first bool
lost uint64
nextTick uint64
}
state1 := sysAllocOS (unsafe .Sizeof (readState {}) * uintptr (n ))
if state1 == nil {
println ("failed to allocate read state for" , n , "logs" )
printunlock ()
return
}
state := (*[1 << 20 ]readState )(state1 )[:n ]
{
l := all
for i := range state {
s := &state [i ]
s .debugLogReader = l .w .r
s .first = true
s .lost = l .w .r .begin
s .nextTick = s .peek ()
l = l .allLink
}
}
for {
var best struct {
tick uint64
i int
}
best .tick = ^uint64 (0 )
for i := range state {
if state [i ].nextTick < best .tick {
best .tick = state [i ].nextTick
best .i = i
}
}
if best .tick == ^uint64 (0 ) {
break
}
s := &state [best .i ]
if s .first {
print (">> begin log " , best .i )
if s .lost != 0 {
print ("; lost first " , s .lost >>10 , "KB" )
}
print (" <<\n" )
s .first = false
}
end , _ , nano , p := s .header ()
oldEnd := s .end
s .end = end
print ("[" )
var tmpbuf [21 ]byte
pnano := int64 (nano ) - runtimeInitTime
if pnano < 0 {
pnano = 0
}
pnanoBytes := itoaDiv (tmpbuf [:], uint64 (pnano ), 9 )
print (slicebytetostringtmp ((*byte )(noescape (unsafe .Pointer (&pnanoBytes [0 ]))), len (pnanoBytes )))
print (" P " , p , "] " )
for i := 0 ; s .begin < s .end ; i ++ {
if i > 0 {
print (" " )
}
if !s .printVal () {
print ("<aborting P log>" )
end = oldEnd
break
}
}
println ()
s .begin = end
s .end = oldEnd
s .nextTick = s .peek ()
}
printunlock ()
}
func printDebugLogPC(pc uintptr , returnPC bool ) {
fn := findfunc (pc )
if returnPC && (!fn .valid () || pc > fn .entry ()) {
pc --
}
print (hex (pc ))
if !fn .valid () {
print (" [unknown PC]" )
} else {
name := funcname (fn )
file , line := funcline (fn , pc )
print (" [" , name , "+" , hex (pc -fn .entry ()),
" " , file , ":" , line , "]" )
}
}
The pages are generated with Golds v0.6.9-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .