aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-04-02 12:04:54 +0200
committerDmitry Vyukov <dvyukov@google.com>2024-04-02 13:00:15 +0000
commitfb6fb6ed7e5734bffbff0af8d67a177ee7640f39 (patch)
tree68551c118c59b37a8a9cea81ae4c5b77b901e561
parenteb2966c4fa240b7304c49e53b51867d8a57eb327 (diff)
pkg/symbolizer: add Cache type
When the same crash happens all over again, we repeatedly symbolize the same PCs. This is slow and blocks VM loop in the manager. Cache PCs we already symbolize, we are likely to symbolize them again.
-rw-r--r--pkg/report/linux.go6
-rw-r--r--pkg/symbolizer/cache.go42
-rw-r--r--pkg/symbolizer/cache_test.go38
3 files changed, 85 insertions, 1 deletions
diff --git a/pkg/report/linux.go b/pkg/report/linux.go
index a1dafccfd..43f55d918 100644
--- a/pkg/report/linux.go
+++ b/pkg/report/linux.go
@@ -34,6 +34,7 @@ type linux struct {
reportStartIgnores []*regexp.Regexp
infoMessagesWithStack [][]byte
eoi []byte
+ symbolizerCache symbolizer.Cache
}
func ctorLinux(cfg *config) (reporterImpl, []string, error) {
@@ -399,13 +400,16 @@ func (ctx *linux) Symbolize(rep *Report) error {
func (ctx *linux) symbolize(rep *Report) error {
symb := symbolizer.NewSymbolizer(ctx.config.target)
defer symb.Close()
+ symbFunc := func(bin string, pc uint64) ([]symbolizer.Frame, error) {
+ return ctx.symbolizerCache.Symbolize(symb.Symbolize, bin, pc)
+ }
var symbolized []byte
s := bufio.NewScanner(bytes.NewReader(rep.Report))
prefix := rep.reportPrefixLen
for s.Scan() {
line := append([]byte{}, s.Bytes()...)
line = append(line, '\n')
- newLine := symbolizeLine(symb.Symbolize, ctx.symbols, ctx.vmlinux, ctx.kernelBuildSrc, line)
+ newLine := symbolizeLine(symbFunc, ctx.symbols, ctx.vmlinux, ctx.kernelBuildSrc, line)
if prefix > len(symbolized) {
prefix += len(newLine) - len(line)
}
diff --git a/pkg/symbolizer/cache.go b/pkg/symbolizer/cache.go
new file mode 100644
index 000000000..dcade3929
--- /dev/null
+++ b/pkg/symbolizer/cache.go
@@ -0,0 +1,42 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package symbolizer
+
+import (
+ "sync"
+)
+
+// Cache caches symbolization results from Symbolizer in a thread-safe way.
+type Cache struct {
+ mu sync.RWMutex
+ cache map[cacheKey]cacheVal
+}
+
+type cacheKey struct {
+ bin string
+ pc uint64
+}
+
+type cacheVal struct {
+ frames []Frame
+ err error
+}
+
+func (c *Cache) Symbolize(inner func(string, uint64) ([]Frame, error), bin string, pc uint64) ([]Frame, error) {
+ key := cacheKey{bin, pc}
+ c.mu.RLock()
+ val, ok := c.cache[key]
+ c.mu.RUnlock()
+ if ok {
+ return val.frames, val.err
+ }
+ frames, err := inner(bin, pc)
+ c.mu.Lock()
+ if c.cache == nil {
+ c.cache = make(map[cacheKey]cacheVal)
+ }
+ c.cache[key] = cacheVal{frames, err}
+ c.mu.Unlock()
+ return frames, err
+}
diff --git a/pkg/symbolizer/cache_test.go b/pkg/symbolizer/cache_test.go
new file mode 100644
index 000000000..eceb61f30
--- /dev/null
+++ b/pkg/symbolizer/cache_test.go
@@ -0,0 +1,38 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package symbolizer
+
+import (
+ "errors"
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCache(t *testing.T) {
+ called := make(map[cacheKey]bool)
+ inner := func(bin string, pc uint64) ([]Frame, error) {
+ key := cacheKey{bin, pc}
+ assert.False(t, called[key])
+ called[key] = true
+ if bin == "error" {
+ return nil, fmt.Errorf("error %v", pc)
+ }
+ return []Frame{{PC: pc, Func: bin + "_func"}}, nil
+ }
+ var cache Cache
+ check := func(bin string, pc uint64, frames []Frame, err error) {
+ gotFrames, gotErr := cache.Symbolize(inner, bin, pc)
+ assert.Equal(t, gotFrames, frames)
+ assert.Equal(t, gotErr, err)
+ }
+ check("foo", 1, []Frame{{PC: 1, Func: "foo_func"}}, nil)
+ check("foo", 1, []Frame{{PC: 1, Func: "foo_func"}}, nil)
+ check("foo", 2, []Frame{{PC: 2, Func: "foo_func"}}, nil)
+ check("foo", 1, []Frame{{PC: 1, Func: "foo_func"}}, nil)
+ check("error", 10, nil, errors.New("error 10"))
+ check("error", 10, nil, errors.New("error 10"))
+ check("error", 11, nil, errors.New("error 11"))
+}