aboutsummaryrefslogtreecommitdiffstats
path: root/syz-verifier
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@gmail.com>2021-11-09 17:32:18 +0100
committerGitHub <noreply@github.com>2021-11-09 17:32:18 +0100
commitf549c8707e71276f8ce5396b0d64b4f2ec536ff4 (patch)
tree3320aec8972b3806f2f79ac17d36b3f7620dbfc0 /syz-verifier
parent59bcaf9a32d049b180bfc3e2ff2b80fb0ac2d5b1 (diff)
syz-verifier: add monitoring api (#2869)
Diffstat (limited to 'syz-verifier')
-rwxr-xr-xsyz-verifier/main.go7
-rw-r--r--syz-verifier/main_test.go1
-rw-r--r--syz-verifier/monitoring_api.go85
-rw-r--r--syz-verifier/rpcserver.go2
-rw-r--r--syz-verifier/stats.go16
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