...

Source file src/runtime/debuglog.go

     1	// Copyright 2019 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	// This file provides an internal debug logging facility. The debug
     6	// log is a lightweight, in-memory, per-M ring buffer. By default, the
     7	// runtime prints the debug log on panic.
     8	//
     9	// To print something to the debug log, call dlog to obtain a dlogger
    10	// and use the methods on that to add values. The values will be
    11	// space-separated in the output (much like println).
    12	//
    13	// This facility can be enabled by passing -tags debuglog when
    14	// building. Without this tag, dlog calls compile to nothing.
    15	
    16	package runtime
    17	
    18	import (
    19		"runtime/internal/atomic"
    20		"unsafe"
    21	)
    22	
    23	// debugLogBytes is the size of each per-M ring buffer. This is
    24	// allocated off-heap to avoid blowing up the M and hence the GC'd
    25	// heap size.
    26	const debugLogBytes = 16 << 10
    27	
    28	// debugLogStringLimit is the maximum number of bytes in a string.
    29	// Above this, the string will be truncated with "..(n more bytes).."
    30	const debugLogStringLimit = debugLogBytes / 8
    31	
    32	// dlog returns a debug logger. The caller can use methods on the
    33	// returned logger to add values, which will be space-separated in the
    34	// final output, much like println. The caller must call end() to
    35	// finish the message.
    36	//
    37	// dlog can be used from highly-constrained corners of the runtime: it
    38	// is safe to use in the signal handler, from within the write
    39	// barrier, from within the stack implementation, and in places that
    40	// must be recursively nosplit.
    41	//
    42	// This will be compiled away if built without the debuglog build tag.
    43	// However, argument construction may not be. If any of the arguments
    44	// are not literals or trivial expressions, consider protecting the
    45	// call with "if dlogEnabled".
    46	//
    47	//go:nosplit
    48	//go:nowritebarrierrec
    49	func dlog() *dlogger {
    50		if !dlogEnabled {
    51			return nil
    52		}
    53	
    54		// Get the time.
    55		tick, nano := uint64(cputicks()), uint64(nanotime())
    56	
    57		// Try to get a cached logger.
    58		l := getCachedDlogger()
    59	
    60		// If we couldn't get a cached logger, try to get one from the
    61		// global pool.
    62		if l == nil {
    63			allp := (*uintptr)(unsafe.Pointer(&allDloggers))
    64			all := (*dlogger)(unsafe.Pointer(atomic.Loaduintptr(allp)))
    65			for l1 := all; l1 != nil; l1 = l1.allLink {
    66				if atomic.Load(&l1.owned) == 0 && atomic.Cas(&l1.owned, 0, 1) {
    67					l = l1
    68					break
    69				}
    70			}
    71		}
    72	
    73		// If that failed, allocate a new logger.
    74		if l == nil {
    75			l = (*dlogger)(sysAlloc(unsafe.Sizeof(dlogger{}), nil))
    76			if l == nil {
    77				throw("failed to allocate debug log")
    78			}
    79			l.w.r.data = &l.w.data
    80			l.owned = 1
    81	
    82			// Prepend to allDloggers list.
    83			headp := (*uintptr)(unsafe.Pointer(&allDloggers))
    84			for {
    85				head := atomic.Loaduintptr(headp)
    86				l.allLink = (*dlogger)(unsafe.Pointer(head))
    87				if atomic.Casuintptr(headp, head, uintptr(unsafe.Pointer(l))) {
    88					break
    89				}
    90			}
    91		}
    92	
    93		// If the time delta is getting too high, write a new sync
    94		// packet. We set the limit so we don't write more than 6
    95		// bytes of delta in the record header.
    96		const deltaLimit = 1<<(3*7) - 1 // ~2ms between sync packets
    97		if tick-l.w.tick > deltaLimit || nano-l.w.nano > deltaLimit {
    98			l.w.writeSync(tick, nano)
    99		}
   100	
   101		// Reserve space for framing header.
   102		l.w.ensure(debugLogHeaderSize)
   103		l.w.write += debugLogHeaderSize
   104	
   105		// Write record header.
   106		l.w.uvarint(tick - l.w.tick)
   107		l.w.uvarint(nano - l.w.nano)
   108		gp := getg()
   109		if gp != nil && gp.m != nil && gp.m.p != 0 {
   110			l.w.varint(int64(gp.m.p.ptr().id))
   111		} else {
   112			l.w.varint(-1)
   113		}
   114	
   115		return l
   116	}
   117	
   118	// A dlogger writes to the debug log.
   119	//
   120	// To obtain a dlogger, call dlog(). When done with the dlogger, call
   121	// end().
   122	//
   123	//go:notinheap
   124	type dlogger struct {
   125		w debugLogWriter
   126	
   127		// allLink is the next dlogger in the allDloggers list.
   128		allLink *dlogger
   129	
   130		// owned indicates that this dlogger is owned by an M. This is
   131		// accessed atomically.
   132		owned uint32
   133	}
   134	
   135	// allDloggers is a list of all dloggers, linked through
   136	// dlogger.allLink. This is accessed atomically. This is prepend only,
   137	// so it doesn't need to protect against ABA races.
   138	var allDloggers *dlogger
   139	
   140	//go:nosplit
   141	func (l *dlogger) end() {
   142		if !dlogEnabled {
   143			return
   144		}
   145	
   146		// Fill in framing header.
   147		size := l.w.write - l.w.r.end
   148		if !l.w.writeFrameAt(l.w.r.end, size) {
   149			throw("record too large")
   150		}
   151	
   152		// Commit the record.
   153		l.w.r.end = l.w.write
   154	
   155		// Attempt to return this logger to the cache.
   156		if putCachedDlogger(l) {
   157			return
   158		}
   159	
   160		// Return the logger to the global pool.
   161		atomic.Store(&l.owned, 0)
   162	}
   163	
   164	const (
   165		debugLogUnknown = 1 + iota
   166		debugLogBoolTrue
   167		debugLogBoolFalse
   168		debugLogInt
   169		debugLogUint
   170		debugLogHex
   171		debugLogPtr
   172		debugLogString
   173		debugLogConstString
   174		debugLogStringOverflow
   175	
   176		debugLogPC
   177		debugLogTraceback
   178	)
   179	
   180	//go:nosplit
   181	func (l *dlogger) b(x bool) *dlogger {
   182		if !dlogEnabled {
   183			return l
   184		}
   185		if x {
   186			l.w.byte(debugLogBoolTrue)
   187		} else {
   188			l.w.byte(debugLogBoolFalse)
   189		}
   190		return l
   191	}
   192	
   193	//go:nosplit
   194	func (l *dlogger) i(x int) *dlogger {
   195		return l.i64(int64(x))
   196	}
   197	
   198	//go:nosplit
   199	func (l *dlogger) i8(x int8) *dlogger {
   200		return l.i64(int64(x))
   201	}
   202	
   203	//go:nosplit
   204	func (l *dlogger) i16(x int16) *dlogger {
   205		return l.i64(int64(x))
   206	}
   207	
   208	//go:nosplit
   209	func (l *dlogger) i32(x int32) *dlogger {
   210		return l.i64(int64(x))
   211	}
   212	
   213	//go:nosplit
   214	func (l *dlogger) i64(x int64) *dlogger {
   215		if !dlogEnabled {
   216			return l
   217		}
   218		l.w.byte(debugLogInt)
   219		l.w.varint(x)
   220		return l
   221	}
   222	
   223	//go:nosplit
   224	func (l *dlogger) u(x uint) *dlogger {
   225		return l.u64(uint64(x))
   226	}
   227	
   228	//go:nosplit
   229	func (l *dlogger) uptr(x uintptr) *dlogger {
   230		return l.u64(uint64(x))
   231	}
   232	
   233	//go:nosplit
   234	func (l *dlogger) u8(x uint8) *dlogger {
   235		return l.u64(uint64(x))
   236	}
   237	
   238	//go:nosplit
   239	func (l *dlogger) u16(x uint16) *dlogger {
   240		return l.u64(uint64(x))
   241	}
   242	
   243	//go:nosplit
   244	func (l *dlogger) u32(x uint32) *dlogger {
   245		return l.u64(uint64(x))
   246	}
   247	
   248	//go:nosplit
   249	func (l *dlogger) u64(x uint64) *dlogger {
   250		if !dlogEnabled {
   251			return l
   252		}
   253		l.w.byte(debugLogUint)
   254		l.w.uvarint(x)
   255		return l
   256	}
   257	
   258	//go:nosplit
   259	func (l *dlogger) hex(x uint64) *dlogger {
   260		if !dlogEnabled {
   261			return l
   262		}
   263		l.w.byte(debugLogHex)
   264		l.w.uvarint(x)
   265		return l
   266	}
   267	
   268	//go:nosplit
   269	func (l *dlogger) p(x interface{}) *dlogger {
   270		if !dlogEnabled {
   271			return l
   272		}
   273		l.w.byte(debugLogPtr)
   274		if x == nil {
   275			l.w.uvarint(0)
   276		} else {
   277			v := efaceOf(&x)
   278			switch v._type.kind & kindMask {
   279			case kindChan, kindFunc, kindMap, kindPtr, kindUnsafePointer:
   280				l.w.uvarint(uint64(uintptr(v.data)))
   281			default:
   282				throw("not a pointer type")
   283			}
   284		}
   285		return l
   286	}
   287	
   288	//go:nosplit
   289	func (l *dlogger) s(x string) *dlogger {
   290		if !dlogEnabled {
   291			return l
   292		}
   293		str := stringStructOf(&x)
   294		datap := &firstmoduledata
   295		if len(x) > 4 && datap.etext <= uintptr(str.str) && uintptr(str.str) < datap.end {
   296			// String constants are in the rodata section, which
   297			// isn't recorded in moduledata. But it has to be
   298			// somewhere between etext and end.
   299			l.w.byte(debugLogConstString)
   300			l.w.uvarint(uint64(str.len))
   301			l.w.uvarint(uint64(uintptr(str.str) - datap.etext))
   302		} else {
   303			l.w.byte(debugLogString)
   304			var b []byte
   305			bb := (*slice)(unsafe.Pointer(&b))
   306			bb.array = str.str
   307			bb.len, bb.cap = str.len, str.len
   308			if len(b) > debugLogStringLimit {
   309				b = b[:debugLogStringLimit]
   310			}
   311			l.w.uvarint(uint64(len(b)))
   312			l.w.bytes(b)
   313			if len(b) != len(x) {
   314				l.w.byte(debugLogStringOverflow)
   315				l.w.uvarint(uint64(len(x) - len(b)))
   316			}
   317		}
   318		return l
   319	}
   320	
   321	//go:nosplit
   322	func (l *dlogger) pc(x uintptr) *dlogger {
   323		if !dlogEnabled {
   324			return l
   325		}
   326		l.w.byte(debugLogPC)
   327		l.w.uvarint(uint64(x))
   328		return l
   329	}
   330	
   331	//go:nosplit
   332	func (l *dlogger) traceback(x []uintptr) *dlogger {
   333		if !dlogEnabled {
   334			return l
   335		}
   336		l.w.byte(debugLogTraceback)
   337		l.w.uvarint(uint64(len(x)))
   338		for _, pc := range x {
   339			l.w.uvarint(uint64(pc))
   340		}
   341		return l
   342	}
   343	
   344	// A debugLogWriter is a ring buffer of binary debug log records.
   345	//
   346	// A log record consists of a 2-byte framing header and a sequence of
   347	// fields. The framing header gives the size of the record as a little
   348	// endian 16-bit value. Each field starts with a byte indicating its
   349	// type, followed by type-specific data. If the size in the framing
   350	// header is 0, it's a sync record consisting of two little endian
   351	// 64-bit values giving a new time base.
   352	//
   353	// Because this is a ring buffer, new records will eventually
   354	// overwrite old records. Hence, it maintains a reader that consumes
   355	// the log as it gets overwritten. That reader state is where an
   356	// actual log reader would start.
   357	//
   358	//go:notinheap
   359	type debugLogWriter struct {
   360		write uint64
   361		data  debugLogBuf
   362	
   363		// tick and nano are the time bases from the most recently
   364		// written sync record.
   365		tick, nano uint64
   366	
   367		// r is a reader that consumes records as they get overwritten
   368		// by the writer. It also acts as the initial reader state
   369		// when printing the log.
   370		r debugLogReader
   371	
   372		// buf is a scratch buffer for encoding. This is here to
   373		// reduce stack usage.
   374		buf [10]byte
   375	}
   376	
   377	//go:notinheap
   378	type debugLogBuf [debugLogBytes]byte
   379	
   380	const (
   381		// debugLogHeaderSize is the number of bytes in the framing
   382		// header of every dlog record.
   383		debugLogHeaderSize = 2
   384	
   385		// debugLogSyncSize is the number of bytes in a sync record.
   386		debugLogSyncSize = debugLogHeaderSize + 2*8
   387	)
   388	
   389	//go:nosplit
   390	func (l *debugLogWriter) ensure(n uint64) {
   391		for l.write+n >= l.r.begin+uint64(len(l.data)) {
   392			// Consume record at begin.
   393			if l.r.skip() == ^uint64(0) {
   394				// Wrapped around within a record.
   395				//
   396				// TODO(austin): It would be better to just
   397				// eat the whole buffer at this point, but we
   398				// have to communicate that to the reader
   399				// somehow.
   400				throw("record wrapped around")
   401			}
   402		}
   403	}
   404	
   405	//go:nosplit
   406	func (l *debugLogWriter) writeFrameAt(pos, size uint64) bool {
   407		l.data[pos%uint64(len(l.data))] = uint8(size)
   408		l.data[(pos+1)%uint64(len(l.data))] = uint8(size >> 8)
   409		return size <= 0xFFFF
   410	}
   411	
   412	//go:nosplit
   413	func (l *debugLogWriter) writeSync(tick, nano uint64) {
   414		l.tick, l.nano = tick, nano
   415		l.ensure(debugLogHeaderSize)
   416		l.writeFrameAt(l.write, 0)
   417		l.write += debugLogHeaderSize
   418		l.writeUint64LE(tick)
   419		l.writeUint64LE(nano)
   420		l.r.end = l.write
   421	}
   422	
   423	//go:nosplit
   424	func (l *debugLogWriter) writeUint64LE(x uint64) {
   425		var b [8]byte
   426		b[0] = byte(x)
   427		b[1] = byte(x >> 8)
   428		b[2] = byte(x >> 16)
   429		b[3] = byte(x >> 24)
   430		b[4] = byte(x >> 32)
   431		b[5] = byte(x >> 40)
   432		b[6] = byte(x >> 48)
   433		b[7] = byte(x >> 56)
   434		l.bytes(b[:])
   435	}
   436	
   437	//go:nosplit
   438	func (l *debugLogWriter) byte(x byte) {
   439		l.ensure(1)
   440		pos := l.write
   441		l.write++
   442		l.data[pos%uint64(len(l.data))] = x
   443	}
   444	
   445	//go:nosplit
   446	func (l *debugLogWriter) bytes(x []byte) {
   447		l.ensure(uint64(len(x)))
   448		pos := l.write
   449		l.write += uint64(len(x))
   450		for len(x) > 0 {
   451			n := copy(l.data[pos%uint64(len(l.data)):], x)
   452			pos += uint64(n)
   453			x = x[n:]
   454		}
   455	}
   456	
   457	//go:nosplit
   458	func (l *debugLogWriter) varint(x int64) {
   459		var u uint64
   460		if x < 0 {
   461			u = (^uint64(x) << 1) | 1 // complement i, bit 0 is 1
   462		} else {
   463			u = (uint64(x) << 1) // do not complement i, bit 0 is 0
   464		}
   465		l.uvarint(u)
   466	}
   467	
   468	//go:nosplit
   469	func (l *debugLogWriter) uvarint(u uint64) {
   470		i := 0
   471		for u >= 0x80 {
   472			l.buf[i] = byte(u) | 0x80
   473			u >>= 7
   474			i++
   475		}
   476		l.buf[i] = byte(u)
   477		i++
   478		l.bytes(l.buf[:i])
   479	}
   480	
   481	type debugLogReader struct {
   482		data *debugLogBuf
   483	
   484		// begin and end are the positions in the log of the beginning
   485		// and end of the log data, modulo len(data).
   486		begin, end uint64
   487	
   488		// tick and nano are the current time base at begin.
   489		tick, nano uint64
   490	}
   491	
   492	//go:nosplit
   493	func (r *debugLogReader) skip() uint64 {
   494		// Read size at pos.
   495		if r.begin+debugLogHeaderSize > r.end {
   496			return ^uint64(0)
   497		}
   498		size := uint64(r.readUint16LEAt(r.begin))
   499		if size == 0 {
   500			// Sync packet.
   501			r.tick = r.readUint64LEAt(r.begin + debugLogHeaderSize)
   502			r.nano = r.readUint64LEAt(r.begin + debugLogHeaderSize + 8)
   503			size = debugLogSyncSize
   504		}
   505		if r.begin+size > r.end {
   506			return ^uint64(0)
   507		}
   508		r.begin += size
   509		return size
   510	}
   511	
   512	//go:nosplit
   513	func (r *debugLogReader) readUint16LEAt(pos uint64) uint16 {
   514		return uint16(r.data[pos%uint64(len(r.data))]) |
   515			uint16(r.data[(pos+1)%uint64(len(r.data))])<<8
   516	}
   517	
   518	//go:nosplit
   519	func (r *debugLogReader) readUint64LEAt(pos uint64) uint64 {
   520		var b [8]byte
   521		for i := range b {
   522			b[i] = r.data[pos%uint64(len(r.data))]
   523			pos++
   524		}
   525		return uint64(b[0]) | uint64(b[1])<<8 |
   526			uint64(b[2])<<16 | uint64(b[3])<<24 |
   527			uint64(b[4])<<32 | uint64(b[5])<<40 |
   528			uint64(b[6])<<48 | uint64(b[7])<<56
   529	}
   530	
   531	func (r *debugLogReader) peek() (tick uint64) {
   532		// Consume any sync records.
   533		size := uint64(0)
   534		for size == 0 {
   535			if r.begin+debugLogHeaderSize > r.end {
   536				return ^uint64(0)
   537			}
   538			size = uint64(r.readUint16LEAt(r.begin))
   539			if size != 0 {
   540				break
   541			}
   542			if r.begin+debugLogSyncSize > r.end {
   543				return ^uint64(0)
   544			}
   545			// Sync packet.
   546			r.tick = r.readUint64LEAt(r.begin + debugLogHeaderSize)
   547			r.nano = r.readUint64LEAt(r.begin + debugLogHeaderSize + 8)
   548			r.begin += debugLogSyncSize
   549		}
   550	
   551		// Peek tick delta.
   552		if r.begin+size > r.end {
   553			return ^uint64(0)
   554		}
   555		pos := r.begin + debugLogHeaderSize
   556		var u uint64
   557		for i := uint(0); ; i += 7 {
   558			b := r.data[pos%uint64(len(r.data))]
   559			pos++
   560			u |= uint64(b&^0x80) << i
   561			if b&0x80 == 0 {
   562				break
   563			}
   564		}
   565		if pos > r.begin+size {
   566			return ^uint64(0)
   567		}
   568		return r.tick + u
   569	}
   570	
   571	func (r *debugLogReader) header() (end, tick, nano uint64, p int) {
   572		// Read size. We've already skipped sync packets and checked
   573		// bounds in peek.
   574		size := uint64(r.readUint16LEAt(r.begin))
   575		end = r.begin + size
   576		r.begin += debugLogHeaderSize
   577	
   578		// Read tick, nano, and p.
   579		tick = r.uvarint() + r.tick
   580		nano = r.uvarint() + r.nano
   581		p = int(r.varint())
   582	
   583		return
   584	}
   585	
   586	func (r *debugLogReader) uvarint() uint64 {
   587		var u uint64
   588		for i := uint(0); ; i += 7 {
   589			b := r.data[r.begin%uint64(len(r.data))]
   590			r.begin++
   591			u |= uint64(b&^0x80) << i
   592			if b&0x80 == 0 {
   593				break
   594			}
   595		}
   596		return u
   597	}
   598	
   599	func (r *debugLogReader) varint() int64 {
   600		u := r.uvarint()
   601		var v int64
   602		if u&1 == 0 {
   603			v = int64(u >> 1)
   604		} else {
   605			v = ^int64(u >> 1)
   606		}
   607		return v
   608	}
   609	
   610	func (r *debugLogReader) printVal() bool {
   611		typ := r.data[r.begin%uint64(len(r.data))]
   612		r.begin++
   613	
   614		switch typ {
   615		default:
   616			print("<unknown field type ", hex(typ), " pos ", r.begin-1, " end ", r.end, ">\n")
   617			return false
   618	
   619		case debugLogUnknown:
   620			print("<unknown kind>")
   621	
   622		case debugLogBoolTrue:
   623			print(true)
   624	
   625		case debugLogBoolFalse:
   626			print(false)
   627	
   628		case debugLogInt:
   629			print(r.varint())
   630	
   631		case debugLogUint:
   632			print(r.uvarint())
   633	
   634		case debugLogHex, debugLogPtr:
   635			print(hex(r.uvarint()))
   636	
   637		case debugLogString:
   638			sl := r.uvarint()
   639			if r.begin+sl > r.end {
   640				r.begin = r.end
   641				print("<string length corrupted>")
   642				break
   643			}
   644			for sl > 0 {
   645				b := r.data[r.begin%uint64(len(r.data)):]
   646				if uint64(len(b)) > sl {
   647					b = b[:sl]
   648				}
   649				r.begin += uint64(len(b))
   650				sl -= uint64(len(b))
   651				gwrite(b)
   652			}
   653	
   654		case debugLogConstString:
   655			len, ptr := int(r.uvarint()), uintptr(r.uvarint())
   656			ptr += firstmoduledata.etext
   657			str := stringStruct{
   658				str: unsafe.Pointer(ptr),
   659				len: len,
   660			}
   661			s := *(*string)(unsafe.Pointer(&str))
   662			print(s)
   663	
   664		case debugLogStringOverflow:
   665			print("..(", r.uvarint(), " more bytes)..")
   666	
   667		case debugLogPC:
   668			printDebugLogPC(uintptr(r.uvarint()))
   669	
   670		case debugLogTraceback:
   671			n := int(r.uvarint())
   672			for i := 0; i < n; i++ {
   673				print("\n\t")
   674				printDebugLogPC(uintptr(r.uvarint()))
   675			}
   676		}
   677	
   678		return true
   679	}
   680	
   681	// printDebugLog prints the debug log.
   682	func printDebugLog() {
   683		if !dlogEnabled {
   684			return
   685		}
   686	
   687		// This function should not panic or throw since it is used in
   688		// the fatal panic path and this may deadlock.
   689	
   690		printlock()
   691	
   692		// Get the list of all debug logs.
   693		allp := (*uintptr)(unsafe.Pointer(&allDloggers))
   694		all := (*dlogger)(unsafe.Pointer(atomic.Loaduintptr(allp)))
   695	
   696		// Count the logs.
   697		n := 0
   698		for l := all; l != nil; l = l.allLink {
   699			n++
   700		}
   701		if n == 0 {
   702			printunlock()
   703			return
   704		}
   705	
   706		// Prepare read state for all logs.
   707		type readState struct {
   708			debugLogReader
   709			first    bool
   710			lost     uint64
   711			nextTick uint64
   712		}
   713		state1 := sysAlloc(unsafe.Sizeof(readState{})*uintptr(n), nil)
   714		if state1 == nil {
   715			println("failed to allocate read state for", n, "logs")
   716			printunlock()
   717			return
   718		}
   719		state := (*[1 << 20]readState)(state1)[:n]
   720		{
   721			l := all
   722			for i := range state {
   723				s := &state[i]
   724				s.debugLogReader = l.w.r
   725				s.first = true
   726				s.lost = l.w.r.begin
   727				s.nextTick = s.peek()
   728				l = l.allLink
   729			}
   730		}
   731	
   732		// Print records.
   733		for {
   734			// Find the next record.
   735			var best struct {
   736				tick uint64
   737				i    int
   738			}
   739			best.tick = ^uint64(0)
   740			for i := range state {
   741				if state[i].nextTick < best.tick {
   742					best.tick = state[i].nextTick
   743					best.i = i
   744				}
   745			}
   746			if best.tick == ^uint64(0) {
   747				break
   748			}
   749	
   750			// Print record.
   751			s := &state[best.i]
   752			if s.first {
   753				print(">> begin log ", best.i)
   754				if s.lost != 0 {
   755					print("; lost first ", s.lost>>10, "KB")
   756				}
   757				print(" <<\n")
   758				s.first = false
   759			}
   760	
   761			end, _, nano, p := s.header()
   762			oldEnd := s.end
   763			s.end = end
   764	
   765			print("[")
   766			var tmpbuf [21]byte
   767			pnano := int64(nano) - runtimeInitTime
   768			if pnano < 0 {
   769				// Logged before runtimeInitTime was set.
   770				pnano = 0
   771			}
   772			print(string(itoaDiv(tmpbuf[:], uint64(pnano), 9)))
   773			print(" P ", p, "] ")
   774	
   775			for i := 0; s.begin < s.end; i++ {
   776				if i > 0 {
   777					print(" ")
   778				}
   779				if !s.printVal() {
   780					// Abort this P log.
   781					print("<aborting P log>")
   782					end = oldEnd
   783					break
   784				}
   785			}
   786			println()
   787	
   788			// Move on to the next record.
   789			s.begin = end
   790			s.end = oldEnd
   791			s.nextTick = s.peek()
   792		}
   793	
   794		printunlock()
   795	}
   796	
   797	func printDebugLogPC(pc uintptr) {
   798		print(hex(pc))
   799		fn := findfunc(pc)
   800		if !fn.valid() {
   801			print(" [unknown PC]")
   802		} else {
   803			name := funcname(fn)
   804			file, line := funcline(fn, pc)
   805			print(" [", name, "+", hex(pc-fn.entry),
   806				" ", file, ":", line, "]")
   807		}
   808	}
   809	

View as plain text