diff options
| author | Taras Madan <tarasmadan@google.com> | 2025-01-30 11:01:00 +0100 |
|---|---|---|
| committer | Taras Madan <tarasmadan@google.com> | 2025-02-06 16:06:49 +0000 |
| commit | 3d54a78389a7ab2ce6588d7a6fade681c22626ab (patch) | |
| tree | 576ce35472663b01d6bb16b70d53823e9ace2c1c /pkg | |
| parent | 80f71f68ba84a33848ff78da0f62cb472f657a28 (diff) | |
dashboard/app: export coverage json
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/coveragedb/coveragedb.go | 27 | ||||
| -rw-r--r-- | pkg/coveragedb/functions.go | 85 |
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 +} |
