Source file
src/runtime/debuglog.go
Documentation: runtime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package runtime
17
18 import (
19 "runtime/internal/atomic"
20 "runtime/internal/sys"
21 "unsafe"
22 )
23
24
25
26
27 const debugLogBytes = 16 << 10
28
29
30
31 const debugLogStringLimit = debugLogBytes / 8
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 func dlog() *dlogger {
51 if !dlogEnabled {
52 return nil
53 }
54
55
56 tick, nano := uint64(cputicks()), uint64(nanotime())
57
58
59 l := getCachedDlogger()
60
61
62
63 if l == nil {
64 allp := (*uintptr)(unsafe.Pointer(&allDloggers))
65 all := (*dlogger)(unsafe.Pointer(atomic.Loaduintptr(allp)))
66 for l1 := all; l1 != nil; l1 = l1.allLink {
67 if l1.owned.Load() == 0 && l1.owned.CompareAndSwap(0, 1) {
68 l = l1
69 break
70 }
71 }
72 }
73
74
75 if l == nil {
76
77
78 l = (*dlogger)(sysAllocOS(unsafe.Sizeof(dlogger{})))
79 if l == nil {
80 throw("failed to allocate debug log")
81 }
82 l.w.r.data = &l.w.data
83 l.owned.Store(1)
84
85
86 headp := (*uintptr)(unsafe.Pointer(&allDloggers))
87 for {
88 head := atomic.Loaduintptr(headp)
89 l.allLink = (*dlogger)(unsafe.Pointer(head))
90 if atomic.Casuintptr(headp, head, uintptr(unsafe.Pointer(l))) {
91 break
92 }
93 }
94 }
95
96
97
98
99 const deltaLimit = 1<<(3*7) - 1
100 if tick-l.w.tick > deltaLimit || nano-l.w.nano > deltaLimit {
101 l.w.writeSync(tick, nano)
102 }
103
104
105 l.w.ensure(debugLogHeaderSize)
106 l.w.write += debugLogHeaderSize
107
108
109 l.w.uvarint(tick - l.w.tick)
110 l.w.uvarint(nano - l.w.nano)
111 gp := getg()
112 if gp != nil && gp.m != nil && gp.m.p != 0 {
113 l.w.varint(int64(gp.m.p.ptr().id))
114 } else {
115 l.w.varint(-1)
116 }
117
118 return l
119 }
120
121
122
123
124
125 type dlogger struct {
126 _ sys.NotInHeap
127 w debugLogWriter
128
129
130 allLink *dlogger
131
132
133
134 owned atomic.Uint32
135 }
136
137
138
139
140 var allDloggers *dlogger
141
142
143 func (l *dlogger) end() {
144 if !dlogEnabled {
145 return
146 }
147
148
149 size := l.w.write - l.w.r.end
150 if !l.w.writeFrameAt(l.w.r.end, size) {
151 throw("record too large")
152 }
153
154
155 l.w.r.end = l.w.write
156
157
158 if putCachedDlogger(l) {
159 return
160 }
161
162
163 l.owned.Store(0)
164 }
165
166 const (
167 debugLogUnknown = 1 + iota
168 debugLogBoolTrue
169 debugLogBoolFalse
170 debugLogInt
171 debugLogUint
172 debugLogHex
173 debugLogPtr
174 debugLogString
175 debugLogConstString
176 debugLogStringOverflow
177
178 debugLogPC
179 debugLogTraceback
180 )
181
182
183 func (l *dlogger) b(x bool) *dlogger {
184 if !dlogEnabled {
185 return l
186 }
187 if x {
188 l.w.byte(debugLogBoolTrue)
189 } else {
190 l.w.byte(debugLogBoolFalse)
191 }
192 return l
193 }
194
195
196 func (l *dlogger) i(x int) *dlogger {
197 return l.i64(int64(x))
198 }
199
200
201 func (l *dlogger) i8(x int8) *dlogger {
202 return l.i64(int64(x))
203 }
204
205
206 func (l *dlogger) i16(x int16) *dlogger {
207 return l.i64(int64(x))
208 }
209
210
211 func (l *dlogger) i32(x int32) *dlogger {
212 return l.i64(int64(x))
213 }
214
215
216 func (l *dlogger) i64(x int64) *dlogger {
217 if !dlogEnabled {
218 return l
219 }
220 l.w.byte(debugLogInt)
221 l.w.varint(x)
222 return l
223 }
224
225
226 func (l *dlogger) u(x uint) *dlogger {
227 return l.u64(uint64(x))
228 }
229
230
231 func (l *dlogger) uptr(x uintptr) *dlogger {
232 return l.u64(uint64(x))
233 }
234
235
236 func (l *dlogger) u8(x uint8) *dlogger {
237 return l.u64(uint64(x))
238 }
239
240
241 func (l *dlogger) u16(x uint16) *dlogger {
242 return l.u64(uint64(x))
243 }
244
245
246 func (l *dlogger) u32(x uint32) *dlogger {
247 return l.u64(uint64(x))
248 }
249
250
251 func (l *dlogger) u64(x uint64) *dlogger {
252 if !dlogEnabled {
253 return l
254 }
255 l.w.byte(debugLogUint)
256 l.w.uvarint(x)
257 return l
258 }
259
260
261 func (l *dlogger) hex(x uint64) *dlogger {
262 if !dlogEnabled {
263 return l
264 }
265 l.w.byte(debugLogHex)
266 l.w.uvarint(x)
267 return l
268 }
269
270
271 func (l *dlogger) p(x any) *dlogger {
272 if !dlogEnabled {
273 return l
274 }
275 l.w.byte(debugLogPtr)
276 if x == nil {
277 l.w.uvarint(0)
278 } else {
279 v := efaceOf(&x)
280 switch v._type.Kind_ & kindMask {
281 case kindChan, kindFunc, kindMap, kindPtr, kindUnsafePointer:
282 l.w.uvarint(uint64(uintptr(v.data)))
283 default:
284 throw("not a pointer type")
285 }
286 }
287 return l
288 }
289
290
291 func (l *dlogger) s(x string) *dlogger {
292 if !dlogEnabled {
293 return l
294 }
295
296 strData := unsafe.StringData(x)
297 datap := &firstmoduledata
298 if len(x) > 4 && datap.etext <= uintptr(unsafe.Pointer(strData)) && uintptr(unsafe.Pointer(strData)) < datap.end {
299
300
301
302 l.w.byte(debugLogConstString)
303 l.w.uvarint(uint64(len(x)))
304 l.w.uvarint(uint64(uintptr(unsafe.Pointer(strData)) - datap.etext))
305 } else {
306 l.w.byte(debugLogString)
307
308
309 var b []byte
310 bb := (*slice)(unsafe.Pointer(&b))
311 bb.array = unsafe.Pointer(strData)
312 bb.len, bb.cap = len(x), len(x)
313 if len(b) > debugLogStringLimit {
314 b = b[:debugLogStringLimit]
315 }
316 l.w.uvarint(uint64(len(b)))
317 l.w.bytes(b)
318 if len(b) != len(x) {
319 l.w.byte(debugLogStringOverflow)
320 l.w.uvarint(uint64(len(x) - len(b)))
321 }
322 }
323 return l
324 }
325
326
327 func (l *dlogger) pc(x uintptr) *dlogger {
328 if !dlogEnabled {
329 return l
330 }
331 l.w.byte(debugLogPC)
332 l.w.uvarint(uint64(x))
333 return l
334 }
335
336
337 func (l *dlogger) traceback(x []uintptr) *dlogger {
338 if !dlogEnabled {
339 return l
340 }
341 l.w.byte(debugLogTraceback)
342 l.w.uvarint(uint64(len(x)))
343 for _, pc := range x {
344 l.w.uvarint(uint64(pc))
345 }
346 return l
347 }
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362 type debugLogWriter struct {
363 _ sys.NotInHeap
364 write uint64
365 data debugLogBuf
366
367
368
369 tick, nano uint64
370
371
372
373
374 r debugLogReader
375
376
377
378 buf [10]byte
379 }
380
381 type debugLogBuf struct {
382 _ sys.NotInHeap
383 b [debugLogBytes]byte
384 }
385
386 const (
387
388
389 debugLogHeaderSize = 2
390
391
392 debugLogSyncSize = debugLogHeaderSize + 2*8
393 )
394
395
396 func (l *debugLogWriter) ensure(n uint64) {
397 for l.write+n >= l.r.begin+uint64(len(l.data.b)) {
398
399 if l.r.skip() == ^uint64(0) {
400
401
402
403
404
405
406 throw("record wrapped around")
407 }
408 }
409 }
410
411
412 func (l *debugLogWriter) writeFrameAt(pos, size uint64) bool {
413 l.data.b[pos%uint64(len(l.data.b))] = uint8(size)
414 l.data.b[(pos+1)%uint64(len(l.data.b))] = uint8(size >> 8)
415 return size <= 0xFFFF
416 }
417
418
419 func (l *debugLogWriter) writeSync(tick, nano uint64) {
420 l.tick, l.nano = tick, nano
421 l.ensure(debugLogHeaderSize)
422 l.writeFrameAt(l.write, 0)
423 l.write += debugLogHeaderSize
424 l.writeUint64LE(tick)
425 l.writeUint64LE(nano)
426 l.r.end = l.write
427 }
428
429
430 func (l *debugLogWriter) writeUint64LE(x uint64) {
431 var b [8]byte
432 b[0] = byte(x)
433 b[1] = byte(x >> 8)
434 b[2] = byte(x >> 16)
435 b[3] = byte(x >> 24)
436 b[4] = byte(x >> 32)
437 b[5] = byte(x >> 40)
438 b[6] = byte(x >> 48)
439 b[7] = byte(x >> 56)
440 l.bytes(b[:])
441 }
442
443
444 func (l *debugLogWriter) byte(x byte) {
445 l.ensure(1)
446 pos := l.write
447 l.write++
448 l.data.b[pos%uint64(len(l.data.b))] = x
449 }
450
451
452 func (l *debugLogWriter) bytes(x []byte) {
453 l.ensure(uint64(len(x)))
454 pos := l.write
455 l.write += uint64(len(x))
456 for len(x) > 0 {
457 n := copy(l.data.b[pos%uint64(len(l.data.b)):], x)
458 pos += uint64(n)
459 x = x[n:]
460 }
461 }
462
463
464 func (l *debugLogWriter) varint(x int64) {
465 var u uint64
466 if x < 0 {
467 u = (^uint64(x) << 1) | 1
468 } else {
469 u = (uint64(x) << 1)
470 }
471 l.uvarint(u)
472 }
473
474
475 func (l *debugLogWriter) uvarint(u uint64) {
476 i := 0
477 for u >= 0x80 {
478 l.buf[i] = byte(u) | 0x80
479 u >>= 7
480 i++
481 }
482 l.buf[i] = byte(u)
483 i++
484 l.bytes(l.buf[:i])
485 }
486
487 type debugLogReader struct {
488 data *debugLogBuf
489
490
491
492 begin, end uint64
493
494
495 tick, nano uint64
496 }
497
498
499 func (r *debugLogReader) skip() uint64 {
500
501 if r.begin+debugLogHeaderSize > r.end {
502 return ^uint64(0)
503 }
504 size := uint64(r.readUint16LEAt(r.begin))
505 if size == 0 {
506
507 r.tick = r.readUint64LEAt(r.begin + debugLogHeaderSize)
508 r.nano = r.readUint64LEAt(r.begin + debugLogHeaderSize + 8)
509 size = debugLogSyncSize
510 }
511 if r.begin+size > r.end {
512 return ^uint64(0)
513 }
514 r.begin += size
515 return size
516 }
517
518
519 func (r *debugLogReader) readUint16LEAt(pos uint64) uint16 {
520 return uint16(r.data.b[pos%uint64(len(r.data.b))]) |
521 uint16(r.data.b[(pos+1)%uint64(len(r.data.b))])<<8
522 }
523
524
525 func (r *debugLogReader) readUint64LEAt(pos uint64) uint64 {
526 var b [8]byte
527 for i := range b {
528 b[i] = r.data.b[pos%uint64(len(r.data.b))]
529 pos++
530 }
531 return uint64(b[0]) | uint64(b[1])<<8 |
532 uint64(b[2])<<16 | uint64(b[3])<<24 |
533 uint64(b[4])<<32 | uint64(b[5])<<40 |
534 uint64(b[6])<<48 | uint64(b[7])<<56
535 }
536
537 func (r *debugLogReader) peek() (tick uint64) {
538
539 size := uint64(0)
540 for size == 0 {
541 if r.begin+debugLogHeaderSize > r.end {
542 return ^uint64(0)
543 }
544 size = uint64(r.readUint16LEAt(r.begin))
545 if size != 0 {
546 break
547 }
548 if r.begin+debugLogSyncSize > r.end {
549 return ^uint64(0)
550 }
551
552 r.tick = r.readUint64LEAt(r.begin + debugLogHeaderSize)
553 r.nano = r.readUint64LEAt(r.begin + debugLogHeaderSize + 8)
554 r.begin += debugLogSyncSize
555 }
556
557
558 if r.begin+size > r.end {
559 return ^uint64(0)
560 }
561 pos := r.begin + debugLogHeaderSize
562 var u uint64
563 for i := uint(0); ; i += 7 {
564 b := r.data.b[pos%uint64(len(r.data.b))]
565 pos++
566 u |= uint64(b&^0x80) << i
567 if b&0x80 == 0 {
568 break
569 }
570 }
571 if pos > r.begin+size {
572 return ^uint64(0)
573 }
574 return r.tick + u
575 }
576
577 func (r *debugLogReader) header() (end, tick, nano uint64, p int) {
578
579
580 size := uint64(r.readUint16LEAt(r.begin))
581 end = r.begin + size
582 r.begin += debugLogHeaderSize
583
584
585 tick = r.uvarint() + r.tick
586 nano = r.uvarint() + r.nano
587 p = int(r.varint())
588
589 return
590 }
591
592 func (r *debugLogReader) uvarint() uint64 {
593 var u uint64
594 for i := uint(0); ; i += 7 {
595 b := r.data.b[r.begin%uint64(len(r.data.b))]
596 r.begin++
597 u |= uint64(b&^0x80) << i
598 if b&0x80 == 0 {
599 break
600 }
601 }
602 return u
603 }
604
605 func (r *debugLogReader) varint() int64 {
606 u := r.uvarint()
607 var v int64
608 if u&1 == 0 {
609 v = int64(u >> 1)
610 } else {
611 v = ^int64(u >> 1)
612 }
613 return v
614 }
615
616 func (r *debugLogReader) printVal() bool {
617 typ := r.data.b[r.begin%uint64(len(r.data.b))]
618 r.begin++
619
620 switch typ {
621 default:
622 print("<unknown field type ", hex(typ), " pos ", r.begin-1, " end ", r.end, ">\n")
623 return false
624
625 case debugLogUnknown:
626 print("<unknown kind>")
627
628 case debugLogBoolTrue:
629 print(true)
630
631 case debugLogBoolFalse:
632 print(false)
633
634 case debugLogInt:
635 print(r.varint())
636
637 case debugLogUint:
638 print(r.uvarint())
639
640 case debugLogHex, debugLogPtr:
641 print(hex(r.uvarint()))
642
643 case debugLogString:
644 sl := r.uvarint()
645 if r.begin+sl > r.end {
646 r.begin = r.end
647 print("<string length corrupted>")
648 break
649 }
650 for sl > 0 {
651 b := r.data.b[r.begin%uint64(len(r.data.b)):]
652 if uint64(len(b)) > sl {
653 b = b[:sl]
654 }
655 r.begin += uint64(len(b))
656 sl -= uint64(len(b))
657 gwrite(b)
658 }
659
660 case debugLogConstString:
661 len, ptr := int(r.uvarint()), uintptr(r.uvarint())
662 ptr += firstmoduledata.etext
663
664
665 str := stringStruct{
666 str: unsafe.Pointer(ptr),
667 len: len,
668 }
669 s := *(*string)(unsafe.Pointer(&str))
670 print(s)
671
672 case debugLogStringOverflow:
673 print("..(", r.uvarint(), " more bytes)..")
674
675 case debugLogPC:
676 printDebugLogPC(uintptr(r.uvarint()), false)
677
678 case debugLogTraceback:
679 n := int(r.uvarint())
680 for i := 0; i < n; i++ {
681 print("\n\t")
682
683
684
685
686 printDebugLogPC(uintptr(r.uvarint()), true)
687 }
688 }
689
690 return true
691 }
692
693
694 func printDebugLog() {
695 if !dlogEnabled {
696 return
697 }
698
699
700
701
702 printlock()
703
704
705 allp := (*uintptr)(unsafe.Pointer(&allDloggers))
706 all := (*dlogger)(unsafe.Pointer(atomic.Loaduintptr(allp)))
707
708
709 n := 0
710 for l := all; l != nil; l = l.allLink {
711 n++
712 }
713 if n == 0 {
714 printunlock()
715 return
716 }
717
718
719 type readState struct {
720 debugLogReader
721 first bool
722 lost uint64
723 nextTick uint64
724 }
725
726
727 state1 := sysAllocOS(unsafe.Sizeof(readState{}) * uintptr(n))
728 if state1 == nil {
729 println("failed to allocate read state for", n, "logs")
730 printunlock()
731 return
732 }
733 state := (*[1 << 20]readState)(state1)[:n]
734 {
735 l := all
736 for i := range state {
737 s := &state[i]
738 s.debugLogReader = l.w.r
739 s.first = true
740 s.lost = l.w.r.begin
741 s.nextTick = s.peek()
742 l = l.allLink
743 }
744 }
745
746
747 for {
748
749 var best struct {
750 tick uint64
751 i int
752 }
753 best.tick = ^uint64(0)
754 for i := range state {
755 if state[i].nextTick < best.tick {
756 best.tick = state[i].nextTick
757 best.i = i
758 }
759 }
760 if best.tick == ^uint64(0) {
761 break
762 }
763
764
765 s := &state[best.i]
766 if s.first {
767 print(">> begin log ", best.i)
768 if s.lost != 0 {
769 print("; lost first ", s.lost>>10, "KB")
770 }
771 print(" <<\n")
772 s.first = false
773 }
774
775 end, _, nano, p := s.header()
776 oldEnd := s.end
777 s.end = end
778
779 print("[")
780 var tmpbuf [21]byte
781 pnano := int64(nano) - runtimeInitTime
782 if pnano < 0 {
783
784 pnano = 0
785 }
786 pnanoBytes := itoaDiv(tmpbuf[:], uint64(pnano), 9)
787 print(slicebytetostringtmp((*byte)(noescape(unsafe.Pointer(&pnanoBytes[0]))), len(pnanoBytes)))
788 print(" P ", p, "] ")
789
790 for i := 0; s.begin < s.end; i++ {
791 if i > 0 {
792 print(" ")
793 }
794 if !s.printVal() {
795
796 print("<aborting P log>")
797 end = oldEnd
798 break
799 }
800 }
801 println()
802
803
804 s.begin = end
805 s.end = oldEnd
806 s.nextTick = s.peek()
807 }
808
809 printunlock()
810 }
811
812
813
814 func printDebugLogPC(pc uintptr, returnPC bool) {
815 fn := findfunc(pc)
816 if returnPC && (!fn.valid() || pc > fn.entry()) {
817
818
819 pc--
820 }
821
822 print(hex(pc))
823 if !fn.valid() {
824 print(" [unknown PC]")
825 } else {
826 name := funcname(fn)
827 file, line := funcline(fn, pc)
828 print(" [", name, "+", hex(pc-fn.entry()),
829 " ", file, ":", line, "]")
830 }
831 }
832
View as plain text