aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-04-15 11:13:06 +0200
committerDmitry Vyukov <dvyukov@google.com>2024-04-15 09:51:16 +0000
commit459f4b00beb4b01fcc508eeafbdddcd18d61f6c2 (patch)
treedf95e00aa883114432ff32065ae9836d67c3689b
parentc8349e48534ea6d8f01515335d95de8ebf5da8df (diff)
pkg/log: restore lazy formatting
For a while we eagerly format log messages even if they are discarded. This may be expensive. If the fuzzer does not deserialize programs, logging consumes 8.7% of CPU and constantly generates garbage. Restore lazy formatting and don't lock the mutex if caching is not enabled.
-rw-r--r--pkg/log/log.go84
-rw-r--r--pkg/log/log_test.go12
2 files changed, 58 insertions, 38 deletions
diff --git a/pkg/log/log.go b/pkg/log/log.go
index 95cdb7fc8..dc0bb4ceb 100644
--- a/pkg/log/log.go
+++ b/pkg/log/log.go
@@ -15,18 +15,20 @@ import (
golog "log"
"strings"
"sync"
+ "sync/atomic"
"time"
)
var (
- flagV = flag.Int("vv", 0, "verbosity")
- mu sync.Mutex
- cacheMem int
- cacheMaxMem int
- cachePos int
- cacheEntries []string
- instanceName string
- prependTime = true // for testing
+ flagV = flag.Int("vv", 0, "verbosity")
+ mu sync.Mutex
+ cacheMem int
+ cacheMaxMem int
+ cachePos int
+ cacheEntries []string
+ cachingEnabled atomic.Bool
+ instanceName string
+ prependTime = true // for testing
)
// EnableLogCaching enables in memory caching of log output.
@@ -43,6 +45,7 @@ func EnableLogCaching(maxLines, maxMem int) {
}
cacheMaxMem = maxMem
cacheEntries = make([]string, maxLines)
+ cachingEnabled.Store(true)
}
// Retrieves cached log output.
@@ -73,11 +76,11 @@ func V(level int) bool {
}
func Logf(v int, msg string, args ...interface{}) {
- writeMessage(v, message("", msg, args...))
+ writeMessage(v, "", msg, args...)
}
func Errorf(msg string, args ...interface{}) {
- writeMessage(0, message("ERROR", msg, args...))
+ writeMessage(0, "ERROR", msg, args...)
}
func Fatal(err error) {
@@ -105,36 +108,41 @@ func message(severity, msg string, args ...interface{}) string {
return sb.String()
}
-func writeMessage(v int, msg string) {
- mu.Lock()
- if cacheEntries != nil && v <= 1 {
- cacheMem -= len(cacheEntries[cachePos])
- if cacheMem < 0 {
- panic("log cache size underflow")
- }
- timeStr := ""
- if prependTime {
- timeStr = time.Now().Format("2006/01/02 15:04:05 ")
- }
- cacheEntries[cachePos] = timeStr + msg
- cacheMem += len(cacheEntries[cachePos])
- cachePos++
- if cachePos == len(cacheEntries) {
- cachePos = 0
- }
- for i := 0; i < len(cacheEntries)-1 && cacheMem > cacheMaxMem; i++ {
- pos := (cachePos + i) % len(cacheEntries)
- cacheMem -= len(cacheEntries[pos])
- cacheEntries[pos] = ""
- }
- if cacheMem < 0 {
- panic("log cache size underflow")
- }
+func writeMessage(v int, severity, msg string, args ...interface{}) {
+ cache := v <= 1 && cachingEnabled.Load()
+ if !V(v) && !cache {
+ return
}
- mu.Unlock()
-
+ text := message(severity, msg, args...)
if V(v) {
- golog.Print(msg)
+ golog.Print(text)
+ }
+ if !cache {
+ return
+ }
+ mu.Lock()
+ defer mu.Unlock()
+ cacheMem -= len(cacheEntries[cachePos])
+ if cacheMem < 0 {
+ panic("log cache size underflow")
+ }
+ timeStr := ""
+ if prependTime {
+ timeStr = time.Now().Format("2006/01/02 15:04:05 ")
+ }
+ cacheEntries[cachePos] = timeStr + text
+ cacheMem += len(cacheEntries[cachePos])
+ cachePos++
+ if cachePos == len(cacheEntries) {
+ cachePos = 0
+ }
+ for i := 0; i < len(cacheEntries)-1 && cacheMem > cacheMaxMem; i++ {
+ pos := (cachePos + i) % len(cacheEntries)
+ cacheMem -= len(cacheEntries[pos])
+ cacheEntries[pos] = ""
+ }
+ if cacheMem < 0 {
+ panic("log cache size underflow")
}
}
diff --git a/pkg/log/log_test.go b/pkg/log/log_test.go
index 3cbbfc655..c1be24a9f 100644
--- a/pkg/log/log_test.go
+++ b/pkg/log/log_test.go
@@ -33,3 +33,15 @@ func TestCaching(t *testing.T) {
}
}
}
+
+func TestLazy(t *testing.T) {
+ // Ensure that the format message is formatted lazily only when logging enabled.
+ Logf(1e6, "%v", noFormat{t})
+}
+
+type noFormat struct{ *testing.T }
+
+func (nf noFormat) String() string {
+ nf.T.Fatalf("must not be formatted")
+ return ""
+}