aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2025-01-30 11:01:00 +0100
committerTaras Madan <tarasmadan@google.com>2025-02-06 16:06:49 +0000
commit3d54a78389a7ab2ce6588d7a6fade681c22626ab (patch)
tree576ce35472663b01d6bb16b70d53823e9ace2c1c /pkg
parent80f71f68ba84a33848ff78da0f62cb472f657a28 (diff)
dashboard/app: export coverage json
Diffstat (limited to 'pkg')
-rw-r--r--pkg/coveragedb/coveragedb.go27
-rw-r--r--pkg/coveragedb/functions.go85
2 files changed, 104 insertions, 8 deletions
diff --git a/pkg/coveragedb/coveragedb.go b/pkg/coveragedb/coveragedb.go
index 4c51df515..cb847339d 100644
--- a/pkg/coveragedb/coveragedb.go
+++ b/pkg/coveragedb/coveragedb.go
@@ -40,14 +40,6 @@ type MergedCoverageRecord struct {
FileData *Coverage
}
-// FuncLines represents the 'functions' table records.
-// It could be used to maps 'hitcounts' from 'files' table to the function names.
-type FuncLines struct {
- FilePath string
- FuncName string
- Lines []int64 // List of lines we know belong to this function name according to the addr2line output.
-}
-
type JSONLWrapper struct {
MCR *MergedCoverageRecord
FL *FuncLines
@@ -415,6 +407,25 @@ type SelectScope struct {
Periods []TimePeriod
}
+// FilesCoverageStream streams information about all the line coverage.
+// It is expensive and better to be used for time insensitive operations.
+func FilesCoverageStream(ctx context.Context, client spannerclient.SpannerClient, ns string, timePeriod TimePeriod,
+) (<-chan *FileCoverageWithLineInfo, <-chan error) {
+ iter := client.Single().Query(ctx,
+ filesCoverageWithDetailsStmt(ns, "", "", timePeriod, true))
+ resCh := make(chan *FileCoverageWithLineInfo)
+ errCh := make(chan error)
+ go func() {
+ defer iter.Stop()
+ defer close(resCh)
+ defer close(errCh)
+ if err := readIterToChan(context.Background(), iter, resCh); err != nil {
+ errCh <- err
+ }
+ }()
+ return resCh, errCh
+}
+
// FilesCoverageWithDetails fetches the data directly from DB. No caching.
// Flag onlyUnique is quite expensive.
func FilesCoverageWithDetails(
diff --git a/pkg/coveragedb/functions.go b/pkg/coveragedb/functions.go
new file mode 100644
index 000000000..6e1240881
--- /dev/null
+++ b/pkg/coveragedb/functions.go
@@ -0,0 +1,85 @@
+// Copyright 2025 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 coveragedb
+
+import (
+ "context"
+ "fmt"
+
+ "cloud.google.com/go/spanner"
+ "github.com/google/syzkaller/pkg/coveragedb/spannerclient"
+ "google.golang.org/api/iterator"
+)
+
+// FuncLines represents the 'functions' table records.
+// It could be used to maps 'hitcounts' from 'files' table to the function names.
+type FuncLines struct {
+ FilePath string
+ FuncName string
+ Lines []int64 // List of lines we know belong to this function name according to the addr2line output.
+}
+
+func MakeFuncFinder(ctx context.Context, client spannerclient.SpannerClient, ns string, timePeriod TimePeriod,
+) (*FunctionFinder, error) {
+ stmt := spanner.Statement{
+ SQL: `select
+ filepath, funcname, lines
+from merge_history
+ join functions
+ on merge_history.session = functions.session
+where
+ merge_history.namespace=$1 and dateto=$2 and duration=$3`,
+ Params: map[string]interface{}{
+ "p1": ns,
+ "p2": timePeriod.DateTo,
+ "p3": timePeriod.Days,
+ },
+ }
+ iter := client.Single().Query(ctx, stmt)
+ defer iter.Stop()
+
+ ff := &FunctionFinder{}
+ for {
+ row, err := iter.Next()
+ if err == iterator.Done {
+ break
+ }
+ if err != nil {
+ return nil, fmt.Errorf("iter.Next(): %w", err)
+ }
+ var r FuncLines
+ if err = row.ToStruct(&r); err != nil {
+ return nil, fmt.Errorf("row.ToStruct(): %w", err)
+ }
+
+ for _, val := range r.Lines {
+ ff.addLine(r.FilePath, r.FuncName, int(val))
+ }
+ }
+ return ff, nil
+}
+
+type FunctionFinder struct {
+ fileLineToFuncName map[string]map[int]string
+}
+
+func (ff *FunctionFinder) addLine(fileName, funcName string, line int) {
+ if ff.fileLineToFuncName == nil {
+ ff.fileLineToFuncName = map[string]map[int]string{}
+ }
+ if ff.fileLineToFuncName[fileName] == nil {
+ ff.fileLineToFuncName[fileName] = map[int]string{}
+ }
+ ff.fileLineToFuncName[fileName][line] = funcName
+}
+
+func (ff *FunctionFinder) FileLineToFuncName(filePath string, line int) (string, error) {
+ if _, ok := ff.fileLineToFuncName[filePath]; !ok {
+ return "", fmt.Errorf("file %s not found", filePath)
+ }
+ if _, ok := ff.fileLineToFuncName[filePath][line]; !ok {
+ return "", fmt.Errorf("file:line %s:%d function not found", filePath, line)
+ }
+ return ff.fileLineToFuncName[filePath][line], nil
+}