github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/report/title_stat.go (about)

     1  // Copyright 2025 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package report
     5  
     6  import (
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"maps"
    11  	"os"
    12  )
    13  
    14  func AddTitleStat(file string, reps []*Report) error {
    15  	var titles []string
    16  	for _, rep := range reps {
    17  		titles = append(titles, rep.Title)
    18  	}
    19  	stat, err := ReadStatFile(file)
    20  	if err != nil {
    21  		return fmt.Errorf("report.ReadStatFile: %w", err)
    22  	}
    23  	stat.add(titles)
    24  	if err := writeStatFile(file, stat); err != nil {
    25  		return fmt.Errorf("writeStatFile: %w", err)
    26  	}
    27  	return nil
    28  }
    29  
    30  func ReadStatFile(file string) (*titleStat, error) {
    31  	stat := &titleStat{}
    32  	if _, err := os.Stat(file); errors.Is(err, os.ErrNotExist) {
    33  		return stat, nil
    34  	}
    35  	data, err := os.ReadFile(file)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	if len(data) == 0 {
    40  		return stat, nil
    41  	}
    42  	if err := json.Unmarshal(data, stat); err != nil {
    43  		return nil, err
    44  	}
    45  	return stat, nil
    46  }
    47  
    48  func writeStatFile(file string, stat *titleStat) error {
    49  	data, err := json.MarshalIndent(stat, "", "\t")
    50  	if err != nil {
    51  		return err
    52  	}
    53  	if err := os.WriteFile(file, data, 0644); err != nil {
    54  		return err
    55  	}
    56  	return nil
    57  }
    58  
    59  type titleStatNodes map[string]*titleStat
    60  
    61  type titleStat struct {
    62  	Count int
    63  	Nodes titleStatNodes
    64  }
    65  
    66  func (ts *titleStat) add(reps []string) {
    67  	if len(reps) == 0 {
    68  		return
    69  	}
    70  	if ts.Nodes == nil {
    71  		ts.Nodes = make(titleStatNodes)
    72  	}
    73  	if ts.Nodes[reps[0]] == nil {
    74  		ts.Nodes[reps[0]] = &titleStat{}
    75  	}
    76  	ts.Nodes[reps[0]].Count++
    77  	ts.Nodes[reps[0]].add(reps[1:])
    78  }
    79  
    80  func (ts *titleStat) visit(cb func(int, ...string), titles ...string) {
    81  	if len(ts.Nodes) == 0 {
    82  		cb(ts.Count, titles...)
    83  		return
    84  	}
    85  	for title := range maps.Keys(ts.Nodes) {
    86  		ts.Nodes[title].visit(cb, append(titles, title)...)
    87  	}
    88  }