From 3d54a78389a7ab2ce6588d7a6fade681c22626ab Mon Sep 17 00:00:00 2001 From: Taras Madan Date: Thu, 30 Jan 2025 11:01:00 +0100 Subject: dashboard/app: export coverage json --- pkg/coveragedb/coveragedb.go | 27 +++++++++----- pkg/coveragedb/functions.go | 85 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 pkg/coveragedb/functions.go (limited to 'pkg') 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 +} -- cgit mrf-deployment