diff options
| -rwxr-xr-x | syz-verifier/main.go | 7 | ||||
| -rw-r--r-- | syz-verifier/main_test.go | 1 | ||||
| -rw-r--r-- | syz-verifier/monitoring_api.go | 85 | ||||
| -rw-r--r-- | syz-verifier/rpcserver.go | 2 | ||||
| -rw-r--r-- | syz-verifier/stats.go | 16 |
5 files changed, 103 insertions, 8 deletions
diff --git a/syz-verifier/main.go b/syz-verifier/main.go index 138adc175..d17d01437 100755 --- a/syz-verifier/main.go +++ b/syz-verifier/main.go @@ -177,6 +177,7 @@ func main() { executorBin: execBin, addr: addr, reportReasons: len(cfg.EnabledSyscalls) != 0 || len(cfg.DisabledSyscalls) != 0, + stats: MakeStats(), statsWrite: sw, newEnv: *flagEnv, reruns: *flagReruns, @@ -189,5 +190,11 @@ func main() { vrf.startInstances() + monitor := MakeMonitor() + monitor.SetStatsTracking(vrf.stats) + // TODO: move binding address to configuration + log.Logf(0, "run the Monitor at http://127.0.0.1:8080/") + go monitor.ListenAndServe("127.0.0.1:8080") + select {} } diff --git a/syz-verifier/main_test.go b/syz-verifier/main_test.go index b083b1a9b..734985bcc 100644 --- a/syz-verifier/main_test.go +++ b/syz-verifier/main_test.go @@ -240,6 +240,7 @@ func TestUpdateUnsupported(t *testing.T) { target.SyscallMap["test$res0"]: true, target.SyscallMap["test$union0"]: true, }, + stats: MakeStats(), } srv, err := startRPCServer(&vrf) if err != nil { diff --git a/syz-verifier/monitoring_api.go b/syz-verifier/monitoring_api.go new file mode 100644 index 000000000..8c67cbe40 --- /dev/null +++ b/syz-verifier/monitoring_api.go @@ -0,0 +1,85 @@ +// 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 main + +import ( + "encoding/json" + "net/http" + "time" +) + +// Monitor provides http based data for the syz-verifier monitoring. +// TODO: Add tests to monitoring_api. +type Monitor struct { + externalStats *Stats +} + +// MakeMonitor creates the Monitor instance. +func MakeMonitor() *Monitor { + instance := &Monitor{} + instance.initHTTPHandlers() + return instance +} + +// ListenAndServe starts the server. +func (monitor *Monitor) ListenAndServe(addr string) error { + return http.ListenAndServe(addr, nil) +} + +// SetStatsTracking points Monitor to the Stats object to monitor. +func (monitor *Monitor) SetStatsTracking(s *Stats) { + monitor.externalStats = s +} + +// InitHTTPHandlers initializes the API routing. +func (monitor *Monitor) initHTTPHandlers() { + http.Handle("/api/stats.json", jsonResponse(monitor.renderStats)) + + http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { + writer.Write([]byte("<a href='api/stats.json'>stats_json</a>")) + }) +} + +// statsJSON provides information for the "/api/stats.json" render. +type statsJSON struct { + StartTime time.Time + TotalMismatches int + TotalProgs int + FlakyProgs int + MismatchingProgs int + AverExecSpeed int +} + +// handleStats renders the statsJSON object. +func (monitor *Monitor) renderStats() interface{} { + stats := monitor.externalStats + return &statsJSON{ + StartTime: stats.StartTime, + TotalMismatches: stats.TotalMismatches, + TotalProgs: stats.TotalProgs, + FlakyProgs: stats.FlakyProgs, + MismatchingProgs: stats.MismatchingProgs, + AverExecSpeed: 60 * stats.TotalProgs / int(1+time.Since(stats.StartTime).Seconds()), + } +} + +// jsonResponse provides general response forming logic. +func jsonResponse(getData func() interface{}) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + + data := getData() + json, err := json.MarshalIndent( + data, + "", + "\t", + ) + if err != nil { + http.Error(w, err.Error(), 500) // Internal Server Error. + return + } + + w.Write(json) + }) +} diff --git a/syz-verifier/rpcserver.go b/syz-verifier/rpcserver.go index aef89a135..8757b22cc 100644 --- a/syz-verifier/rpcserver.go +++ b/syz-verifier/rpcserver.go @@ -82,7 +82,7 @@ func (srv *RPCServer) UpdateUnsupported(a *rpctype.UpdateUnsupportedArgs, r *int if srv.notChecked == 0 { vrf.finalizeCallSet(os.Stdout) - vrf.stats = InitStats(vrf.calls) + vrf.stats.SetSyscallMask(vrf.calls) vrf.SetPrintStatAtSIGINT() vrf.choiceTable = vrf.target.BuildChoiceTable(nil, vrf.calls) diff --git a/syz-verifier/stats.go b/syz-verifier/stats.go index a6d00a0d2..e06ead8c0 100644 --- a/syz-verifier/stats.go +++ b/syz-verifier/stats.go @@ -39,20 +39,22 @@ type CallStats struct { States map[ReturnState]bool } -// InitStats creates a stats object. -func InitStats(calls map[*prog.Syscall]bool) *Stats { - stats := &Stats{ - Calls: make(map[string]*CallStats), - StartTime: time.Now(), +// MakeStats creates a stats object. +func MakeStats() *Stats { + return &Stats{ + Calls: make(map[string]*CallStats), } +} + +// SetSyscallMask initializes the allowed syscall list. +func (stats *Stats) SetSyscallMask(calls map[*prog.Syscall]bool) { + stats.StartTime = time.Now() for syscall := range calls { stats.Calls[syscall.Name] = &CallStats{ Name: syscall.Name, States: make(map[ReturnState]bool)} } - - return stats } // ReportGlobalStats creates a report with statistics about all the |
