Copyright 2019 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.
This file provides an internal debug logging facility. The debug log is a lightweight, in-memory, per-M ring buffer. By default, the runtime prints the debug log on panic. To print something to the debug log, call dlog to obtain a dlogger and use the methods on that to add values. The values will be space-separated in the output (much like println). This facility can be enabled by passing -tags debuglog when building. Without this tag, dlog calls compile to nothing.

package runtime

import (
	
	
)
debugLogBytes is the size of each per-M ring buffer. This is allocated off-heap to avoid blowing up the M and hence the GC'd heap size.
const debugLogBytes = 16 << 10
debugLogStringLimit is the maximum number of bytes in a string. Above this, the string will be truncated with "..(n more bytes).."
dlog returns a debug logger. The caller can use methods on the returned logger to add values, which will be space-separated in the final output, much like println. The caller must call end() to finish the message. dlog can be used from highly-constrained corners of the runtime: it is safe to use in the signal handler, from within the write barrier, from within the stack implementation, and in places that must be recursively nosplit. This will be compiled away if built without the debuglog build tag. However, argument construction may not be. If any of the arguments are not literals or trivial expressions, consider protecting the call with "if dlogEnabled".go:nosplitgo:nowritebarrierrec
func () *dlogger {
	if !dlogEnabled {
		return nil
	}
Get the time.
	,  := uint64(cputicks()), uint64(nanotime())
Try to get a cached logger.
If we couldn't get a cached logger, try to get one from the global pool.
	if  == nil {
		 := (*uintptr)(unsafe.Pointer(&allDloggers))
		 := (*dlogger)(unsafe.Pointer(atomic.Loaduintptr()))
		for  := ;  != nil;  = .allLink {
			if atomic.Load(&.owned) == 0 && atomic.Cas(&.owned, 0, 1) {
				 = 
				break
			}
		}
	}
If that failed, allocate a new logger.
	if  == nil {
		 = (*dlogger)(sysAlloc(unsafe.Sizeof(dlogger{}), nil))
		if  == nil {
			throw("failed to allocate debug log")
		}
		.w.r.data = &.w.data
		.owned = 1
Prepend to allDloggers list.
		 := (*uintptr)(unsafe.Pointer(&allDloggers))
		for {
			 := atomic.Loaduintptr()
			.allLink = (*dlogger)(unsafe.Pointer())
			if atomic.Casuintptr(, , uintptr(unsafe.Pointer())) {
				break
			}
		}
	}
If the time delta is getting too high, write a new sync packet. We set the limit so we don't write more than 6 bytes of delta in the record header.
	const  = 1<<(3*7) - 1 // ~2ms between sync packets
	if -.w.tick >  || -.w.nano >  {
		.w.writeSync(, )
	}
Reserve space for framing header.
Write record header.
	.w.uvarint( - .w.tick)
	.w.uvarint( - .w.nano)
	 := getg()
	if  != nil && .m != nil && .m.p != 0 {
		.w.varint(int64(.m.p.ptr().id))
	} else {
		.w.varint(-1)
	}

	return 
}
A dlogger writes to the debug log. To obtain a dlogger, call dlog(). When done with the dlogger, call end().go:notinheap
type dlogger struct {
	w debugLogWriter
allLink is the next dlogger in the allDloggers list.
owned indicates that this dlogger is owned by an M. This is accessed atomically.
allDloggers is a list of all dloggers, linked through dlogger.allLink. This is accessed atomically. This is prepend only, so it doesn't need to protect against ABA races.
go:nosplit
func ( *dlogger) () {
	if !dlogEnabled {
		return
	}
Fill in framing header.
	 := .w.write - .w.r.end
	if !.w.writeFrameAt(.w.r.end, ) {
		throw("record too large")
	}
Commit the record.
	.w.r.end = .w.write
Attempt to return this logger to the cache.
	if putCachedDlogger() {
		return
	}
go:nosplit
func ( *dlogger) ( bool) *dlogger {
	if !dlogEnabled {
		return 
	}
	if  {
		.w.byte(debugLogBoolTrue)
	} else {
		.w.byte(debugLogBoolFalse)
	}
	return 
}
go:nosplit
func ( *dlogger) ( int) *dlogger {
	return .i64(int64())
}
go:nosplit
func ( *dlogger) ( int8) *dlogger {
	return .i64(int64())
}
go:nosplit
func ( *dlogger) ( int16) *dlogger {
	return .i64(int64())
}
go:nosplit
func ( *dlogger) ( int32) *dlogger {
	return .i64(int64())
}
go:nosplit
func ( *dlogger) ( int64) *dlogger {
	if !dlogEnabled {
		return 
	}
	.w.byte(debugLogInt)
	.w.varint()
	return 
}
go:nosplit
func ( *dlogger) ( uint) *dlogger {
	return .u64(uint64())
}
go:nosplit
func ( *dlogger) ( uintptr) *dlogger {
	return .u64(uint64())
}
go:nosplit
func ( *dlogger) ( uint8) *dlogger {
	return .u64(uint64())
}
go:nosplit
func ( *dlogger) ( uint16) *dlogger {
	return .u64(uint64())
}
go:nosplit
func ( *dlogger) ( uint32) *dlogger {
	return .u64(uint64())
}
go:nosplit
func ( *dlogger) ( uint64) *dlogger {
	if !dlogEnabled {
		return 
	}
	.w.byte(debugLogUint)
	.w.uvarint()
	return 
}
go:nosplit
func ( *dlogger) ( uint64) *dlogger {
	if !dlogEnabled {
		return 
	}
	.w.byte(debugLogHex)
	.w.uvarint()
	return 
}
go:nosplit
func ( *dlogger) ( interface{}) *dlogger {
	if !dlogEnabled {
		return 
	}
	.w.byte(debugLogPtr)
	if  == nil {
		.w.uvarint(0)
	} else {
		 := efaceOf(&)
		switch ._type.kind & kindMask {
		case kindChan, kindFunc, kindMap, kindPtr, kindUnsafePointer:
			.w.uvarint(uint64(uintptr(.data)))
		default:
			throw("not a pointer type")
		}
	}
	return 
}
go:nosplit
func ( *dlogger) ( string) *dlogger {
	if !dlogEnabled {
		return 
	}
	 := stringStructOf(&)
	 := &firstmoduledata
String constants are in the rodata section, which isn't recorded in moduledata. But it has to be somewhere between etext and end.
		.w.byte(debugLogConstString)
		.w.uvarint(uint64(.len))
		.w.uvarint(uint64(uintptr(.str) - .etext))
	} else {
		.w.byte(debugLogString)
		var  []byte
		 := (*slice)(unsafe.Pointer(&))
		.array = .str
		.len, .cap = .len, .len
		if len() > debugLogStringLimit {
			 = [:debugLogStringLimit]
		}
		.w.uvarint(uint64(len()))
		.w.bytes()
		if len() != len() {
			.w.byte(debugLogStringOverflow)
			.w.uvarint(uint64(len() - len()))
		}
	}
	return 
}
go:nosplit
func ( *dlogger) ( uintptr) *dlogger {
	if !dlogEnabled {
		return 
	}
	.w.byte(debugLogPC)
	.w.uvarint(uint64())
	return 
}
go:nosplit
func ( *dlogger) ( []uintptr) *dlogger {
	if !dlogEnabled {
		return 
	}
	.w.byte(debugLogTraceback)
	.w.uvarint(uint64(len()))
	for ,  := range  {
		.w.uvarint(uint64())
	}
	return 
}
A debugLogWriter is a ring buffer of binary debug log records. A log record consists of a 2-byte framing header and a sequence of fields. The framing header gives the size of the record as a little endian 16-bit value. Each field starts with a byte indicating its type, followed by type-specific data. If the size in the framing header is 0, it's a sync record consisting of two little endian 64-bit values giving a new time base. Because this is a ring buffer, new records will eventually overwrite old records. Hence, it maintains a reader that consumes the log as it gets overwritten. That reader state is where an actual log reader would start.go:notinheap
tick and nano are the time bases from the most recently written sync record.
r is a reader that consumes records as they get overwritten by the writer. It also acts as the initial reader state when printing the log.
buf is a scratch buffer for encoding. This is here to reduce stack usage.
	buf [10]byte
}
go:notinheap
debugLogHeaderSize is the number of bytes in the framing header of every dlog record.
debugLogSyncSize is the number of bytes in a sync record.
go:nosplit
func ( *debugLogWriter) ( uint64) {
Consume record at begin.
Wrapped around within a record. TODO(austin): It would be better to just eat the whole buffer at this point, but we have to communicate that to the reader somehow.
			throw("record wrapped around")
		}
	}
}
go:nosplit
func ( *debugLogWriter) (,  uint64) bool {
	.data[%uint64(len(.data))] = uint8()
	.data[(+1)%uint64(len(.data))] = uint8( >> 8)
	return  <= 0xFFFF
}
go:nosplit
func ( *debugLogWriter) (,  uint64) {
	.tick, .nano = , 
	.ensure(debugLogHeaderSize)
	.writeFrameAt(.write, 0)
	.write += debugLogHeaderSize
	.writeUint64LE()
	.writeUint64LE()
	.r.end = .write
}
go:nosplit
func ( *debugLogWriter) ( uint64) {
	var  [8]byte
	[0] = byte()
	[1] = byte( >> 8)
	[2] = byte( >> 16)
	[3] = byte( >> 24)
	[4] = byte( >> 32)
	[5] = byte( >> 40)
	[6] = byte( >> 48)
	[7] = byte( >> 56)
	.bytes([:])
}
go:nosplit
func ( *debugLogWriter) ( byte) {
	.ensure(1)
	 := .write
	.write++
	.data[%uint64(len(.data))] = 
}
go:nosplit
func ( *debugLogWriter) ( []byte) {
	.ensure(uint64(len()))
	 := .write
	.write += uint64(len())
	for len() > 0 {
		 := copy(.data[%uint64(len(.data)):], )
		 += uint64()
		 = [:]
	}
}
go:nosplit
func ( *debugLogWriter) ( int64) {
	var  uint64
	if  < 0 {
		 = (^uint64() << 1) | 1 // complement i, bit 0 is 1
	} else {
		 = (uint64() << 1) // do not complement i, bit 0 is 0
	}
	.uvarint()
}
go:nosplit
func ( *debugLogWriter) ( uint64) {
	 := 0
	for  >= 0x80 {
		.buf[] = byte() | 0x80
		 >>= 7
		++
	}
	.buf[] = byte()
	++
	.bytes(.buf[:])
}

type debugLogReader struct {
	data *debugLogBuf
begin and end are the positions in the log of the beginning and end of the log data, modulo len(data).
tick and nano are the current time base at begin.
go:nosplit
Read size at pos.
	if .begin+debugLogHeaderSize > .end {
		return ^uint64(0)
	}
	 := uint64(.readUint16LEAt(.begin))
Sync packet.
		.tick = .readUint64LEAt(.begin + debugLogHeaderSize)
		.nano = .readUint64LEAt(.begin + debugLogHeaderSize + 8)
		 = debugLogSyncSize
	}
	if .begin+ > .end {
		return ^uint64(0)
	}
	.begin += 
	return 
}
go:nosplit
func ( *debugLogReader) ( uint64) uint16 {
	return uint16(.data[%uint64(len(.data))]) |
		uint16(.data[(+1)%uint64(len(.data))])<<8
}
go:nosplit
func ( *debugLogReader) ( uint64) uint64 {
	var  [8]byte
	for  := range  {
		[] = .data[%uint64(len(.data))]
		++
	}
	return uint64([0]) | uint64([1])<<8 |
		uint64([2])<<16 | uint64([3])<<24 |
		uint64([4])<<32 | uint64([5])<<40 |
		uint64([6])<<48 | uint64([7])<<56
}

Consume any sync records.
	 := uint64(0)
	for  == 0 {
		if .begin+debugLogHeaderSize > .end {
			return ^uint64(0)
		}
		 = uint64(.readUint16LEAt(.begin))
		if  != 0 {
			break
		}
		if .begin+debugLogSyncSize > .end {
			return ^uint64(0)
Peek tick delta.
	if .begin+ > .end {
		return ^uint64(0)
	}
	 := .begin + debugLogHeaderSize
	var  uint64
	for  := uint(0); ;  += 7 {
		 := .data[%uint64(len(.data))]
		++
		 |= uint64(&^0x80) << 
		if &0x80 == 0 {
			break
		}
	}
	if  > .begin+ {
		return ^uint64(0)
	}
	return .tick + 
}

Read size. We've already skipped sync packets and checked bounds in peek.
	 := uint64(.readUint16LEAt(.begin))
	 = .begin + 
	.begin += debugLogHeaderSize
Read tick, nano, and p.
	 = .uvarint() + .tick
	 = .uvarint() + .nano
	 = int(.varint())

	return
}

func ( *debugLogReader) () uint64 {
	var  uint64
	for  := uint(0); ;  += 7 {
		 := .data[.begin%uint64(len(.data))]
		.begin++
		 |= uint64(&^0x80) << 
		if &0x80 == 0 {
			break
		}
	}
	return 
}

func ( *debugLogReader) () int64 {
	 := .uvarint()
	var  int64
	if &1 == 0 {
		 = int64( >> 1)
	} else {
		 = ^int64( >> 1)
	}
	return 
}

func ( *debugLogReader) () bool {
	 := .data[.begin%uint64(len(.data))]
	.begin++

	switch  {
	default:
		print("<unknown field type ", hex(), " pos ", .begin-1, " end ", .end, ">\n")
		return false

	case debugLogUnknown:
		print("<unknown kind>")

	case debugLogBoolTrue:
		print(true)

	case debugLogBoolFalse:
		print(false)

	case debugLogInt:
		print(.varint())

	case debugLogUint:
		print(.uvarint())

	case debugLogHex, debugLogPtr:
		print(hex(.uvarint()))

	case debugLogString:
		 := .uvarint()
		if .begin+ > .end {
			.begin = .end
			print("<string length corrupted>")
			break
		}
		for  > 0 {
			 := .data[.begin%uint64(len(.data)):]
			if uint64(len()) >  {
				 = [:]
			}
			.begin += uint64(len())
			 -= uint64(len())
			gwrite()
		}

	case debugLogConstString:
		,  := int(.uvarint()), uintptr(.uvarint())
		 += firstmoduledata.etext
		 := stringStruct{
			str: unsafe.Pointer(),
			len: ,
		}
		 := *(*string)(unsafe.Pointer(&))
		print()

	case debugLogStringOverflow:
		print("..(", .uvarint(), " more bytes)..")

	case debugLogPC:
		printDebugLogPC(uintptr(.uvarint()), false)

	case debugLogTraceback:
		 := int(.uvarint())
		for  := 0;  < ; ++ {
gentraceback PCs are always return PCs. Convert them to call PCs. TODO(austin): Expand inlined frames.
			printDebugLogPC(uintptr(.uvarint()), true)
		}
	}

	return true
}
printDebugLog prints the debug log.
func () {
	if !dlogEnabled {
		return
	}
This function should not panic or throw since it is used in the fatal panic path and this may deadlock.
Get the list of all debug logs.
Count the logs.
	 := 0
	for  := ;  != nil;  = .allLink {
		++
	}
	if  == 0 {
		printunlock()
		return
	}
Prepare read state for all logs.
	type  struct {
		debugLogReader
		    bool
		     uint64
		 uint64
	}
	 := sysAlloc(unsafe.Sizeof({})*uintptr(), nil)
	if  == nil {
		println("failed to allocate read state for", , "logs")
		printunlock()
		return
	}
	 := (*[1 << 20])()[:]
	{
		 := 
		for  := range  {
			 := &[]
			. = .w.r
			. = true
			. = .w.r.begin
			. = .peek()
			 = .allLink
		}
	}
Print records.
Find the next record.
		var  struct {
			 uint64
			    int
		}
		. = ^uint64(0)
		for  := range  {
			if []. < . {
				. = [].
				. = 
			}
		}
		if . == ^uint64(0) {
			break
		}
Print record.
		 := &[.]
		if . {
			print(">> begin log ", .)
			if . != 0 {
				print("; lost first ", .>>10, "KB")
			}
			print(" <<\n")
			. = false
		}

		, , ,  := .header()
		 := .end
		.end = 

		print("[")
		var  [21]byte
		 := int64() - runtimeInitTime
Logged before runtimeInitTime was set.
			 = 0
		}
		print(string(itoaDiv([:], uint64(), 9)))
		print(" P ", , "] ")

		for  := 0; .begin < .end; ++ {
			if  > 0 {
				print(" ")
			}
Abort this P log.
				print("<aborting P log>")
				 = 
				break
			}
		}
		println()
Move on to the next record.
		.begin = 
		.end = 
		. = .peek()
	}

	printunlock()
}
printDebugLogPC prints a single symbolized PC. If returnPC is true, pc is a return PC that must first be converted to a call PC.
func ( uintptr,  bool) {
	 := findfunc()
TODO(austin): Don't back up if the previous frame was a sigpanic.
		--
	}

	print(hex())
	if !.valid() {
		print(" [unknown PC]")
	} else {
		 := funcname()
		,  := funcline(, )
		print(" [", , "+", hex(-.entry),
			" ", , ":", , "]")
	}