github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/log/log.go (about) 1 // Copyright 2016 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 // Package log provides functionality similar to standard log package with some extensions: 5 // - verbosity levels 6 // - global verbosity setting that can be used by multiple packages 7 // - ability to disable all output 8 // - ability to cache recent output in memory 9 package log 10 11 import ( 12 "bytes" 13 "flag" 14 "fmt" 15 golog "log" 16 "strings" 17 "sync" 18 "sync/atomic" 19 "time" 20 ) 21 22 var ( 23 flagV = flag.Int("vv", 0, "verbosity") 24 mu sync.Mutex 25 cacheMem int 26 cacheMaxMem int 27 cachePos int 28 cacheEntries []string 29 cachingEnabled atomic.Bool 30 instanceName string 31 prependTime = true // for testing 32 ) 33 34 // EnableLogCaching enables in memory caching of log output. 35 // Caches up to maxLines, but no more than maxMem bytes. 36 // Cached output can later be queried with CachedOutput. 37 func EnableLogCaching(maxLines, maxMem int) { 38 mu.Lock() 39 defer mu.Unlock() 40 if cacheEntries != nil { 41 Fatalf("log caching is already enabled") 42 } 43 if maxLines < 1 || maxMem < 1 { 44 panic("invalid maxLines/maxMem") 45 } 46 cacheMaxMem = maxMem 47 cacheEntries = make([]string, maxLines) 48 cachingEnabled.Store(true) 49 } 50 51 // Retrieves cached log output. 52 func CachedLogOutput() string { 53 mu.Lock() 54 defer mu.Unlock() 55 buf := new(bytes.Buffer) 56 for i := range cacheEntries { 57 pos := (cachePos + i) % len(cacheEntries) 58 if cacheEntries[pos] == "" { 59 continue 60 } 61 buf.WriteString(cacheEntries[pos]) 62 buf.Write([]byte{'\n'}) 63 } 64 return buf.String() 65 } 66 67 // If the name is set, it will be displayed for all logs. 68 func SetName(name string) { 69 instanceName = name 70 } 71 72 // V reports whether verbosity at the call site is at least the requested level. 73 // See https://pkg.go.dev/github.com/golang/glog#V for details. 74 func V(level int) bool { 75 return level <= *flagV 76 } 77 78 func Logf(v int, msg string, args ...interface{}) { 79 writeMessage(v, "", msg, args...) 80 } 81 82 func Errorf(msg string, args ...interface{}) { 83 writeMessage(0, "ERROR", msg, args...) 84 } 85 86 func Fatal(err error) { 87 Fatalf("%v", err) 88 } 89 90 func Fatalf(msg string, args ...interface{}) { 91 golog.Fatalf(message("FATAL", msg, args...)) 92 } 93 94 // SyzFatalf-reported errors are parsed by syzkaller as if they were kernel bugs. 95 func SyzFatalf(msg string, args ...interface{}) { 96 golog.Fatalf("SYZFATAL: "+msg, args...) 97 } 98 99 func SyzFatal(err error) { 100 SyzFatalf("%v", err) 101 } 102 103 func message(severity, msg string, args ...interface{}) string { 104 var sb strings.Builder 105 if severity != "" { 106 fmt.Fprintf(&sb, "[%s] ", severity) 107 } 108 if instanceName != "" { 109 fmt.Fprintf(&sb, "%s: ", instanceName) 110 } 111 fmt.Fprintf(&sb, msg, args...) 112 return sb.String() 113 } 114 115 func writeMessage(v int, severity, msg string, args ...interface{}) { 116 cache := v <= 1 && cachingEnabled.Load() 117 if !V(v) && !cache { 118 return 119 } 120 text := message(severity, msg, args...) 121 if V(v) { 122 golog.Print(text) 123 } 124 if !cache { 125 return 126 } 127 mu.Lock() 128 defer mu.Unlock() 129 cacheMem -= len(cacheEntries[cachePos]) 130 if cacheMem < 0 { 131 panic("log cache size underflow") 132 } 133 timeStr := "" 134 if prependTime { 135 timeStr = time.Now().Format("2006/01/02 15:04:05 ") 136 } 137 cacheEntries[cachePos] = timeStr + text 138 cacheMem += len(cacheEntries[cachePos]) 139 cachePos++ 140 if cachePos == len(cacheEntries) { 141 cachePos = 0 142 } 143 for i := 0; i < len(cacheEntries)-1 && cacheMem > cacheMaxMem; i++ { 144 pos := (cachePos + i) % len(cacheEntries) 145 cacheMem -= len(cacheEntries[pos]) 146 cacheEntries[pos] = "" 147 } 148 if cacheMem < 0 { 149 panic("log cache size underflow") 150 } 151 } 152 153 type VerboseWriter int 154 155 func (w VerboseWriter) Write(data []byte) (int, error) { 156 Logf(int(w), "%s", data) 157 return len(data), nil 158 }