// 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).."
const debugLogStringLimit = debugLogBytes / 8

// 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:nosplit
//go:nowritebarrierrec
func dlog() *dlogger {
	if !dlogEnabled {
		return nil
	}

	// Get the time.
	,  := uint64(cputicks()), uint64(nanotime())

	// Try to get a cached logger.
	 := getCachedDlogger()

	// 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.
	.w.ensure(debugLogHeaderSize)
	.w.write += debugLogHeaderSize

	// 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.
	allLink *dlogger

	// owned indicates that this dlogger is owned by an M. This is
	// accessed atomically.
	owned uint32
}

// 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.
var allDloggers *dlogger

//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
	}

	// Return the logger to the global pool.
	atomic.Store(&.owned, 0)
}

const (
	debugLogUnknown = 1 + iota
	debugLogBoolTrue
	debugLogBoolFalse
	debugLogInt
	debugLogUint
	debugLogHex
	debugLogPtr
	debugLogString
	debugLogConstString
	debugLogStringOverflow

	debugLogPC
	debugLogTraceback
)

//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
	if len() > 4 && .etext <= uintptr(.str) && uintptr(.str) < .end {
		// 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
type debugLogWriter struct {
	write uint64
	data  debugLogBuf

	// tick and nano are the time bases from the most recently
	// written sync record.
	tick, nano uint64

	// 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.
	r debugLogReader

	// buf is a scratch buffer for encoding. This is here to
	// reduce stack usage.
	buf [10]byte
}

//go:notinheap
type debugLogBuf [debugLogBytes]byte

const (
	// debugLogHeaderSize is the number of bytes in the framing
	// header of every dlog record.
	debugLogHeaderSize = 2

	// debugLogSyncSize is the number of bytes in a sync record.
	debugLogSyncSize = debugLogHeaderSize + 2*8
)

//go:nosplit
func ( *debugLogWriter) ( uint64) {
	for .write+ >= .r.begin+uint64(len(.data)) {
		// Consume record at begin.
		if .r.skip() == ^uint64(0) {
			// 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).
	begin, end uint64

	// tick and nano are the current time base at begin.
	tick, nano uint64
}

//go:nosplit
func ( *debugLogReader) () uint64 {
	// Read size at pos.
	if .begin+debugLogHeaderSize > .end {
		return ^uint64(0)
	}
	 := uint64(.readUint16LEAt(.begin))
	if  == 0 {
		// 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
}

func ( *debugLogReader) () ( uint64) {
	// 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)
		}
		// Sync packet.
		.tick = .readUint64LEAt(.begin + debugLogHeaderSize)
		.nano = .readUint64LEAt(.begin + debugLogHeaderSize + 8)
		.begin += debugLogSyncSize
	}

	// 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 + 
}

func ( *debugLogReader) () (, ,  uint64,  int) {
	// 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;  < ; ++ {
			print("\n\t")
			// 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 printDebugLog() {
	if !dlogEnabled {
		return
	}

	// This function should not panic or throw since it is used in
	// the fatal panic path and this may deadlock.

	printlock()

	// Get the list of all debug logs.
	 := (*uintptr)(unsafe.Pointer(&allDloggers))
	 := (*dlogger)(unsafe.Pointer(atomic.Loaduintptr()))

	// 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.
	for {
		// 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
		if  < 0 {
			// Logged before runtimeInitTime was set.
			 = 0
		}
		print(string(itoaDiv([:], uint64(), 9)))
		print(" P ", , "] ")

		for  := 0; .begin < .end; ++ {
			if  > 0 {
				print(" ")
			}
			if !.printVal() {
				// 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 printDebugLogPC( uintptr,  bool) {
	 := findfunc()
	if  && (!.valid() ||  > .entry) {
		// 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),
			" ", , ":", , "]")
	}
}