diff options
| author | Mara Mihali <maramihali@google.com> | 2021-07-14 08:13:56 +0000 |
|---|---|---|
| committer | maramihali <maramihali@google.com> | 2021-07-19 14:47:11 +0300 |
| commit | 0a9d2a41c103de0b2283c9c0ec8a4740d577f820 (patch) | |
| tree | 36d825b9710daa8c54b79660ebcd8e3deafae640 /syz-verifier | |
| parent | e6a175800f1d9e20aeb7ed35ea2b3fc627049e8f (diff) | |
syz-verifier/stats: create utility to gather statistics
Diffstat (limited to 'syz-verifier')
| -rw-r--r-- | syz-verifier/stats/stats.go | 111 | ||||
| -rw-r--r-- | syz-verifier/stats/stats_test.go | 74 |
2 files changed, 185 insertions, 0 deletions
diff --git a/syz-verifier/stats/stats.go b/syz-verifier/stats/stats.go new file mode 100644 index 000000000..0a96b7f71 --- /dev/null +++ b/syz-verifier/stats/stats.go @@ -0,0 +1,111 @@ +// Copyright 2021 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 stats contains utilities that aid gathering statistics of +// system call mismatches in the verified programs. +package stats + +import ( + "fmt" + "io" + "os" + "os/signal" + "sort" + + "github.com/google/syzkaller/prog" +) + +// Stats encapsulates data for creating statistics related to the results +// of the verification process. +type Stats struct { + // Calls stores statistics for all supported system calls. + Calls map[string]*CallStats + TotalMismatches int +} + +// CallStats stores information used to generate statistics for the +// system call. +type CallStats struct { + // Name is the system call name. + Name string + // Mismatches stores the number of errno mismatches identifed in the + // verified programs for this system call. + Mismatches int + // Occurrences is the number of times the system call appeared in a + // verified program. + Occurrences int +} + +// InitStats creates a stats object that will report verification +// statistics when an os.Interrupt occurs. +func InitStats(calls map[*prog.Syscall]bool, w io.Writer) *Stats { + s := &Stats{Calls: make(map[string]*CallStats)} + for c := range calls { + s.Calls[c.Name] = &CallStats{Name: c.Name} + } + + c := make(chan os.Signal) + signal.Notify(c, os.Interrupt) + go func() { + <-c + s.ReportGlobalStats(w) + os.Exit(0) + }() + + return s +} + +// ReportCallStats creates a report with the current statistics for call. +func (s *Stats) ReportCallStats(call string) string { + cs, ok := s.Calls[call] + if !ok { + return "" + } + name, m, o := cs.Name, cs.Mismatches, cs.Occurrences + data := fmt.Sprintf("statistics for %s:\n"+ + "\t↳ mismatches of %s / occurrences of %s: %d / %d (%0.2f %%)\n"+ + "\t↳ mismatches of %s / total number of mismatches: "+ + "%d / %d (%0.2f %%)\n", name, name, name, m, o, + getPercentage(m, o), name, m, s.TotalMismatches, getPercentage(m, s.TotalMismatches)) + return data +} + +func getPercentage(value, total int) float64 { + return float64(value) / float64(total) * 100 +} + +// ReportGlobalStats creates a report with statistics about all the +// supported system calls for which errno mismatches were identified in +// the verified programs, shown in decreasing order. +func (s *Stats) ReportGlobalStats(w io.Writer) { + tc := s.totalCallsExecuted() + fmt.Fprintf(w, "total number of mismatches / total number of calls "+ + "executed: %d / %d (%0.2f %%)\n\n", s.TotalMismatches, tc, getPercentage(s.TotalMismatches, tc)) + cs := s.getOrderedStats() + for _, c := range cs { + fmt.Fprintf(w, "%s\n", s.ReportCallStats(c.Name)) + } +} + +func (s *Stats) totalCallsExecuted() int { + t := 0 + for _, cs := range s.Calls { + t += cs.Occurrences + } + return t +} + +func (s *Stats) getOrderedStats() []*CallStats { + css := make([]*CallStats, 0) + for _, cs := range s.Calls { + if cs.Mismatches > 0 { + css = append(css, cs) + } + } + + sort.Slice(css, func(i, j int) bool { + return css[i].Mismatches > css[j].Mismatches + }) + + return css +} diff --git a/syz-verifier/stats/stats_test.go b/syz-verifier/stats/stats_test.go new file mode 100644 index 000000000..4bc3f9149 --- /dev/null +++ b/syz-verifier/stats/stats_test.go @@ -0,0 +1,74 @@ +// Copyright 2021 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 stats + +import ( + "bytes" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func getTestStats() *Stats { + return &Stats{ + TotalMismatches: 11, + Calls: map[string]*CallStats{ + "foo": {"foo", 2, 8}, + "bar": {"bar", 5, 6}, + "tar": {"tar", 3, 4}, + "biz": {"biz", 0, 2}, + }, + } +} + +func TestReportCallStats(t *testing.T) { + tests := []struct { + name, call, report string + }{ + { + name: "report for unsupported call", + call: "read", + report: "", + }, + { + name: "report for supported call", + call: "foo", + report: "statistics for foo:\n" + + "\t↳ mismatches of foo / occurrences of foo: 2 / 8 (25.00 %)\n" + + "\t↳ mismatches of foo / total number of mismatches: 2 / 11 (18.18 %)\n", + }, + } + + for _, test := range tests { + s := getTestStats() + t.Run(test.name, func(t *testing.T) { + got, want := s.ReportCallStats(test.call), test.report + if diff := cmp.Diff(got, want); diff != "" { + t.Errorf("s.ReportCallStats mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestReportGlobalStats(t *testing.T) { + s := getTestStats() + out := bytes.Buffer{} + s.ReportGlobalStats(&out) + got, want := out.String(), + "total number of mismatches / total number of calls "+ + "executed: 11 / 20 (55.00 %)\n\n"+ + "statistics for bar:\n"+ + "\t↳ mismatches of bar / occurrences of bar: 5 / 6 (83.33 %)\n"+ + "\t↳ mismatches of bar / total number of mismatches: 5 / 11 (45.45 %)\n\n"+ + "statistics for tar:\n"+ + "\t↳ mismatches of tar / occurrences of tar: 3 / 4 (75.00 %)\n"+ + "\t↳ mismatches of tar / total number of mismatches: 3 / 11 (27.27 %)\n\n"+ + "statistics for foo:\n"+ + "\t↳ mismatches of foo / occurrences of foo: 2 / 8 (25.00 %)\n"+ + "\t↳ mismatches of foo / total number of mismatches: 2 / 11 (18.18 %)\n\n" + + if diff := cmp.Diff(got, want); diff != "" { + t.Errorf("s.ReportGlobalStats mismatch (-want +got):\n%s", diff) + } +} |
