2015-10-12 08:16:57 +00:00
|
|
|
// Copyright 2015 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 (
|
|
|
|
"fmt"
|
|
|
|
"html/template"
|
|
|
|
"net/http"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
2015-11-19 18:02:30 +00:00
|
|
|
"time"
|
2015-10-12 08:16:57 +00:00
|
|
|
|
|
|
|
"github.com/google/syzkaller/cover"
|
|
|
|
"github.com/google/syzkaller/prog"
|
2015-10-14 14:55:09 +00:00
|
|
|
"github.com/google/syzkaller/sys"
|
2015-10-12 08:16:57 +00:00
|
|
|
)
|
|
|
|
|
2015-10-14 14:55:09 +00:00
|
|
|
func (mgr *Manager) initHttp() {
|
|
|
|
http.HandleFunc("/", mgr.httpInfo)
|
|
|
|
http.HandleFunc("/corpus", mgr.httpCorpus)
|
|
|
|
http.HandleFunc("/cover", mgr.httpCover)
|
|
|
|
http.HandleFunc("/prio", mgr.httpPrio)
|
2015-12-17 15:06:33 +00:00
|
|
|
logf(0, "serving http on http://%v", mgr.cfg.Http)
|
|
|
|
go http.ListenAndServe(mgr.cfg.Http, nil)
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
|
|
|
|
2015-10-12 08:16:57 +00:00
|
|
|
func (mgr *Manager) httpInfo(w http.ResponseWriter, r *http.Request) {
|
|
|
|
mgr.mu.Lock()
|
|
|
|
defer mgr.mu.Unlock()
|
|
|
|
|
|
|
|
type CallCov struct {
|
|
|
|
count int
|
|
|
|
cov cover.Cover
|
|
|
|
}
|
|
|
|
calls := make(map[string]*CallCov)
|
|
|
|
for _, inp := range mgr.corpus {
|
|
|
|
if calls[inp.Call] == nil {
|
|
|
|
calls[inp.Call] = new(CallCov)
|
|
|
|
}
|
|
|
|
cc := calls[inp.Call]
|
|
|
|
cc.count++
|
|
|
|
cc.cov = cover.Union(cc.cov, cover.Cover(inp.Cover))
|
|
|
|
}
|
|
|
|
|
2015-11-19 18:02:30 +00:00
|
|
|
uptime := time.Since(mgr.startTime)
|
2015-10-12 08:16:57 +00:00
|
|
|
data := &UIData{
|
2015-12-17 15:06:33 +00:00
|
|
|
CorpusSize: len(mgr.corpus),
|
|
|
|
TriageQueue: len(mgr.candidates),
|
|
|
|
Uptime: fmt.Sprintf("%v", uptime),
|
2015-10-12 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2015-11-19 18:02:30 +00:00
|
|
|
secs := uint64(uptime) / 1e9
|
|
|
|
for k, v := range mgr.stats {
|
|
|
|
val := ""
|
|
|
|
if x := v / secs; x >= 10 {
|
|
|
|
val = fmt.Sprintf("%v/sec", x)
|
|
|
|
} else if x := v * 60 / secs; x >= 10 {
|
|
|
|
val = fmt.Sprintf("%v/min", x)
|
|
|
|
} else {
|
|
|
|
x := v * 60 * 60 / secs
|
|
|
|
val = fmt.Sprintf("%v/hour", x)
|
|
|
|
}
|
|
|
|
data.Stats = append(data.Stats, UIStat{Name: k, Value: val})
|
|
|
|
}
|
|
|
|
sort.Sort(UIStatArray(data.Stats))
|
|
|
|
|
2015-10-12 08:16:57 +00:00
|
|
|
var cov cover.Cover
|
|
|
|
for c, cc := range calls {
|
|
|
|
cov = cover.Union(cov, cc.cov)
|
|
|
|
data.Calls = append(data.Calls, UICallType{c, cc.count, len(cc.cov)})
|
|
|
|
}
|
|
|
|
sort.Sort(UICallTypeArray(data.Calls))
|
|
|
|
data.CoverSize = len(cov)
|
|
|
|
|
|
|
|
if err := htmlTemplate.Execute(w, data); err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mgr *Manager) httpCorpus(w http.ResponseWriter, r *http.Request) {
|
|
|
|
mgr.mu.Lock()
|
|
|
|
defer mgr.mu.Unlock()
|
|
|
|
|
|
|
|
var data []UIInput
|
|
|
|
call := r.FormValue("call")
|
|
|
|
for i, inp := range mgr.corpus {
|
|
|
|
if call != inp.Call {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
p, err := prog.Deserialize(inp.Prog)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to deserialize program: %v", err), http.StatusInternalServerError)
|
|
|
|
}
|
|
|
|
data = append(data, UIInput{
|
|
|
|
Short: p.String(),
|
|
|
|
Full: string(inp.Prog),
|
|
|
|
Cover: len(inp.Cover),
|
|
|
|
N: i,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
sort.Sort(UIInputArray(data))
|
|
|
|
|
|
|
|
if err := corpusTemplate.Execute(w, data); err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mgr *Manager) httpCover(w http.ResponseWriter, r *http.Request) {
|
|
|
|
mgr.mu.Lock()
|
|
|
|
defer mgr.mu.Unlock()
|
|
|
|
|
|
|
|
var cov cover.Cover
|
|
|
|
call := r.FormValue("call")
|
|
|
|
if n, err := strconv.Atoi(call); err == nil && n < len(mgr.corpus) {
|
|
|
|
cov = mgr.corpus[n].Cover
|
|
|
|
} else {
|
|
|
|
for _, inp := range mgr.corpus {
|
|
|
|
if call == "" || call == inp.Call {
|
|
|
|
cov = cover.Union(cov, cover.Cover(inp.Cover))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := generateCoverHtml(w, mgr.cfg.Vmlinux, cov); err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to generate coverage profile: %v", err), http.StatusInternalServerError)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-14 14:55:09 +00:00
|
|
|
func (mgr *Manager) httpPrio(w http.ResponseWriter, r *http.Request) {
|
|
|
|
mgr.mu.Lock()
|
|
|
|
defer mgr.mu.Unlock()
|
|
|
|
|
|
|
|
mgr.minimizeCorpus()
|
|
|
|
call := r.FormValue("call")
|
|
|
|
idx := -1
|
|
|
|
for i, c := range sys.Calls {
|
|
|
|
if c.CallName == call {
|
|
|
|
idx = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if idx == -1 {
|
|
|
|
http.Error(w, fmt.Sprintf("unknown call: %v", call), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
data := &UIPrioData{Call: call}
|
|
|
|
for i, p := range mgr.prios[idx] {
|
|
|
|
data.Prios = append(data.Prios, UIPrio{sys.Calls[i].Name, p})
|
|
|
|
}
|
|
|
|
sort.Sort(UIPrioArray(data.Prios))
|
|
|
|
|
|
|
|
if err := prioTemplate.Execute(w, data); err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-12 08:16:57 +00:00
|
|
|
type UIData struct {
|
2015-12-17 15:06:33 +00:00
|
|
|
CorpusSize int
|
|
|
|
TriageQueue int
|
|
|
|
CoverSize int
|
|
|
|
Uptime string
|
|
|
|
Stats []UIStat
|
|
|
|
Calls []UICallType
|
2015-10-12 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2015-11-19 18:02:30 +00:00
|
|
|
type UIStat struct {
|
|
|
|
Name string
|
|
|
|
Value string
|
|
|
|
}
|
|
|
|
|
2015-10-12 08:16:57 +00:00
|
|
|
type UICallType struct {
|
|
|
|
Name string
|
|
|
|
Inputs int
|
|
|
|
Cover int
|
|
|
|
}
|
|
|
|
|
|
|
|
type UIInput struct {
|
|
|
|
Short string
|
|
|
|
Full string
|
|
|
|
Calls int
|
|
|
|
Cover int
|
|
|
|
N int
|
|
|
|
}
|
|
|
|
|
|
|
|
type UICallTypeArray []UICallType
|
|
|
|
|
|
|
|
func (a UICallTypeArray) Len() int { return len(a) }
|
|
|
|
func (a UICallTypeArray) Less(i, j int) bool { return a[i].Name < a[j].Name }
|
|
|
|
func (a UICallTypeArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
|
|
|
|
type UIInputArray []UIInput
|
|
|
|
|
|
|
|
func (a UIInputArray) Len() int { return len(a) }
|
|
|
|
func (a UIInputArray) Less(i, j int) bool { return a[i].Cover > a[j].Cover }
|
|
|
|
func (a UIInputArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
|
2015-11-19 18:02:30 +00:00
|
|
|
type UIStatArray []UIStat
|
|
|
|
|
|
|
|
func (a UIStatArray) Len() int { return len(a) }
|
|
|
|
func (a UIStatArray) Less(i, j int) bool { return a[i].Name < a[j].Name }
|
|
|
|
func (a UIStatArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
|
2015-10-12 08:16:57 +00:00
|
|
|
var htmlTemplate = template.Must(template.New("").Parse(`
|
|
|
|
<!doctype html>
|
|
|
|
<html>
|
|
|
|
<head>
|
2015-12-17 15:06:33 +00:00
|
|
|
<title>syzkaller</title>
|
2015-10-12 08:16:57 +00:00
|
|
|
</head>
|
|
|
|
<body>
|
2015-11-19 18:02:30 +00:00
|
|
|
Uptime: {{.Uptime}}<br>
|
2015-10-12 08:16:57 +00:00
|
|
|
Corpus: {{.CorpusSize}}<br>
|
2015-10-14 14:55:09 +00:00
|
|
|
Triage queue len: {{.TriageQueue}}<br>
|
2015-10-12 08:16:57 +00:00
|
|
|
<a href='/cover'>Cover: {{.CoverSize}}</a> <br>
|
|
|
|
<br>
|
2015-11-19 18:02:30 +00:00
|
|
|
Stats: <br>
|
|
|
|
{{range $stat := $.Stats}}
|
|
|
|
{{$stat.Name}}: {{$stat.Value}}<br>
|
|
|
|
{{end}}
|
|
|
|
<br>
|
2015-10-12 08:16:57 +00:00
|
|
|
{{range $c := $.Calls}}
|
2015-10-14 14:55:09 +00:00
|
|
|
{{$c.Name}} <a href='/corpus?call={{$c.Name}}'>inputs:{{$c.Inputs}}</a> <a href='/cover?call={{$c.Name}}'>cover:{{$c.Cover}}</a> <a href='/prio?call={{$c.Name}}'>prio</a> <br>
|
2015-10-12 08:16:57 +00:00
|
|
|
{{end}}
|
|
|
|
</body></html>
|
|
|
|
`))
|
|
|
|
|
|
|
|
var corpusTemplate = template.Must(template.New("").Parse(`
|
|
|
|
<!doctype html>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>syzkaller corpus</title>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
{{range $c := $}}
|
|
|
|
<span title="{{$c.Full}}">{{$c.Short}}</span> <a href='/cover?call={{$c.N}}'>cover:{{$c.Cover}}</a> <br>
|
|
|
|
{{end}}
|
|
|
|
</body></html>
|
|
|
|
`))
|
2015-10-14 14:55:09 +00:00
|
|
|
|
|
|
|
type UIPrioData struct {
|
|
|
|
Call string
|
|
|
|
Prios []UIPrio
|
|
|
|
}
|
|
|
|
|
|
|
|
type UIPrio struct {
|
|
|
|
Call string
|
|
|
|
Prio float32
|
|
|
|
}
|
|
|
|
|
|
|
|
type UIPrioArray []UIPrio
|
|
|
|
|
|
|
|
func (a UIPrioArray) Len() int { return len(a) }
|
|
|
|
func (a UIPrioArray) Less(i, j int) bool { return a[i].Prio > a[j].Prio }
|
|
|
|
func (a UIPrioArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
|
|
|
|
var prioTemplate = template.Must(template.New("").Parse(`
|
|
|
|
<!doctype html>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>syzkaller priorities</title>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
Priorities for {{$.Call}} <br> <br>
|
|
|
|
{{range $p := $.Prios}}
|
|
|
|
{{printf "%.4f\t%s" $p.Prio $p.Call}} <br>
|
|
|
|
{{end}}
|
|
|
|
</body></html>
|
|
|
|
`))
|