mirror of
https://github.com/maride/afl-prom.git
synced 2024-11-24 20:34:25 +00:00
Init commit
This commit is contained in:
commit
49d9be65b6
200
fuzzer.go
Normal file
200
fuzzer.go
Normal file
@ -0,0 +1,200 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Represents a Fuzzer and its state, as reported in the "fuzzer_stats" file
|
||||
type Fuzzer struct {
|
||||
fuzzer_pid prometheus.Gauge
|
||||
cycles_done prometheus.Gauge
|
||||
execs_done prometheus.Gauge
|
||||
execs_per_sec prometheus.Gauge
|
||||
paths_total prometheus.Gauge
|
||||
paths_favored prometheus.Gauge
|
||||
paths_found prometheus.Gauge
|
||||
paths_imported prometheus.Gauge
|
||||
max_depth prometheus.Gauge
|
||||
cur_path prometheus.Gauge
|
||||
pending_favs prometheus.Gauge
|
||||
pending_total prometheus.Gauge
|
||||
variable_paths prometheus.Gauge
|
||||
stability prometheus.Gauge
|
||||
bitmap_cvg prometheus.Gauge
|
||||
unique_crashes prometheus.Gauge
|
||||
unique_hangs prometheus.Gauge
|
||||
last_path prometheus.Gauge
|
||||
last_crash prometheus.Gauge
|
||||
last_hang prometheus.Gauge
|
||||
execs_since_crash prometheus.Gauge
|
||||
exec_timeout prometheus.Gauge
|
||||
slowest_exec_ms prometheus.Gauge
|
||||
peak_rss_mb prometheus.Gauge
|
||||
afl_banner string
|
||||
afl_directory string
|
||||
// missing start_time, last_update, afl_version, target_mode and command_line, and to be honest, I don't see a reason to export these values via prometheus
|
||||
}
|
||||
|
||||
// Initialises all gauges. Needs afl_banner to be set properly before (and doesn't check that on its own)
|
||||
func (f *Fuzzer) initGauges() {
|
||||
log.Printf("%s %s", f.afl_directory, f.afl_banner)
|
||||
f.fuzzer_pid = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_fuzzer_pid"})
|
||||
f.cycles_done = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_cycles_done"})
|
||||
f.execs_done = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_execs_done"})
|
||||
f.execs_per_sec = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_execs_per_sec"})
|
||||
f.paths_total = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_paths_total"})
|
||||
f.paths_favored = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_paths_favored"})
|
||||
f.paths_found = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_paths_found"})
|
||||
f.paths_imported = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_paths_imported"})
|
||||
f.max_depth = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_max_depth"})
|
||||
f.cur_path = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_cur_path"})
|
||||
f.pending_favs = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_pending_favs"})
|
||||
f.pending_total = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_pending_total"})
|
||||
f.variable_paths = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_variable_paths"})
|
||||
f.stability = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_stability"})
|
||||
f.bitmap_cvg = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_bitmap_cvg"})
|
||||
f.unique_crashes = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_unique_crashes"})
|
||||
f.unique_hangs = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_unique_hangs"})
|
||||
f.last_path = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_last_path"})
|
||||
f.last_crash = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_last_crash"})
|
||||
f.last_hang = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_last_hang"})
|
||||
f.execs_since_crash = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_execs_since_crash"})
|
||||
f.exec_timeout = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_exec_timeout"})
|
||||
f.slowest_exec_ms = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_slowest_exec_ms"})
|
||||
f.peak_rss_mb = promauto.NewGauge(prometheus.GaugeOpts{Name: f.afl_banner + "_peak_rss_mb"})
|
||||
}
|
||||
|
||||
// Parses the "fuzzer_stats" file present in the given directory, and updates the gauges of the Fuzzer instance accordingly
|
||||
func (f *Fuzzer) ParseStatsFile() error {
|
||||
// Open file and read it
|
||||
path := fmt.Sprintf("%s%cfuzzer_stats", f.afl_directory, os.PathSeparator)
|
||||
fileBytes, fileReadErr := ioutil.ReadFile(path)
|
||||
if fileReadErr != nil {
|
||||
return fileReadErr
|
||||
}
|
||||
|
||||
// Split file contents into lines
|
||||
fileLines := strings.Split(string(fileBytes), "\n")
|
||||
|
||||
// So we want to walk over the file two times.
|
||||
// 1) If it is unset: read "afl_banner", set it, and run initGauges()
|
||||
// 2) Set every other value to its corresponding gauge.
|
||||
bannerSet := f.afl_banner != "" // 1)
|
||||
gaugesSet := false // 2)
|
||||
for !(bannerSet && gaugesSet) {
|
||||
// Iterate over every line
|
||||
for _, l := range fileLines {
|
||||
// Skip empty lines
|
||||
if l == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Convert line to key and value
|
||||
parts := strings.SplitN(l, ":", 2)
|
||||
key := strings.TrimSpace(parts[0])
|
||||
val := strings.TrimSpace(parts[1])
|
||||
|
||||
// 1)
|
||||
if !bannerSet {
|
||||
if key == "afl_banner" {
|
||||
f.afl_banner = val
|
||||
bannerSet = true
|
||||
f.initGauges()
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 2)
|
||||
switch key {
|
||||
case "fuzzer_pid":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.fuzzer_pid.Set(float64(convVal))
|
||||
case "cycles_done":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.cycles_done.Set(float64(convVal))
|
||||
case "execs_done":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.execs_done.Set(float64(convVal))
|
||||
case "execs_per_sec":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.execs_per_sec.Set(float64(convVal))
|
||||
case "paths_total":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.paths_total.Set(float64(convVal))
|
||||
case "paths_favored":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.paths_favored.Set(float64(convVal))
|
||||
case "paths_found":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.paths_found.Set(float64(convVal))
|
||||
case "paths_imported":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.paths_imported.Set(float64(convVal))
|
||||
case "max_depth":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.max_depth.Set(float64(convVal))
|
||||
case "cur_path":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.cur_path.Set(float64(convVal))
|
||||
case "pending_favs":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.pending_favs.Set(float64(convVal))
|
||||
case "pending_total":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.pending_total.Set(float64(convVal))
|
||||
case "variable_paths":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.variable_paths.Set(float64(convVal))
|
||||
case "stability":
|
||||
val = strings.Replace(val, "%", "", 1)
|
||||
convVal, _ := strconv.ParseFloat(val, 64)
|
||||
f.stability.Set(convVal)
|
||||
case "bitmap_cvg":
|
||||
val = strings.Replace(val, "%", "", 1)
|
||||
convVal, _ := strconv.ParseFloat(val, 64)
|
||||
f.bitmap_cvg.Set(convVal)
|
||||
case "unique_crashes":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.unique_crashes.Set(float64(convVal))
|
||||
case "unique_hangs":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.unique_hangs.Set(float64(convVal))
|
||||
case "last_path":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.last_path.Set(float64(convVal))
|
||||
case "last_crash":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.last_crash.Set(float64(convVal))
|
||||
case "last_hang":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.last_hang.Set(float64(convVal))
|
||||
case "execs_since_crash":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.execs_since_crash.Set(float64(convVal))
|
||||
case "exec_timeout":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.exec_timeout.Set(float64(convVal))
|
||||
case "slowest_exec_ms":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.slowest_exec_ms.Set(float64(convVal))
|
||||
case "peak_rss_mb":
|
||||
convVal, _ := strconv.ParseInt(val, 10, 64)
|
||||
f.peak_rss_mb.Set(float64(convVal))
|
||||
}
|
||||
|
||||
gaugesSet = true
|
||||
}
|
||||
}
|
||||
|
||||
// [+] Done parsing. Have a nice day.
|
||||
return nil
|
||||
}
|
26
main.go
Normal file
26
main.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// Main function
|
||||
func main() {
|
||||
// Check args
|
||||
targetFuzzers, targetErr := getFuzzersToWatch()
|
||||
if targetErr != nil {
|
||||
log.Println(targetErr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Start thread to watch the fuzzer(s)
|
||||
registerFuzzers(targetFuzzers)
|
||||
go watchFuzzers()
|
||||
|
||||
// Start HTTP handler exposing the metrics
|
||||
http.Handle("/metrics", promhttp.Handler())
|
||||
http.ListenAndServe(":2112", nil)
|
||||
}
|
60
watch.go
Normal file
60
watch.go
Normal file
@ -0,0 +1,60 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
registeredFuzzers []Fuzzer
|
||||
)
|
||||
|
||||
// Returns the path to every fuzzer to watch
|
||||
func getFuzzersToWatch() ([]string, error) {
|
||||
for i, a := range os.Args {
|
||||
if a == "--" {
|
||||
// Choose arguments after that one as the target fuzzer directories
|
||||
return os.Args[i+1:], nil
|
||||
}
|
||||
}
|
||||
|
||||
// Wrong usage - construct a helpful error message
|
||||
return []string{}, fmt.Errorf("Please give at least one fuzzer directory to watch.\n%s [options...] -- /path/to/fuzzer1 /path/to/fuzzer2", os.Args[0])
|
||||
}
|
||||
|
||||
// Registers the given paths as fuzzer directories which should be monitored
|
||||
func registerFuzzers(targets []string) {
|
||||
for _, f := range targets {
|
||||
var tmpFuzzer Fuzzer
|
||||
tmpFuzzer.afl_directory = f
|
||||
parseErr := tmpFuzzer.ParseStatsFile()
|
||||
|
||||
if parseErr != nil {
|
||||
log.Printf("Encountered error while parsing %s: %s", f, parseErr.Error())
|
||||
break
|
||||
}
|
||||
|
||||
// Append fuzzer to our list of registered fuzzers
|
||||
registeredFuzzers = append(registeredFuzzers, tmpFuzzer)
|
||||
}
|
||||
}
|
||||
|
||||
// Watch over the fuzzer(s)
|
||||
func watchFuzzers() {
|
||||
// Loop forever
|
||||
for {
|
||||
// Loop over every registered fuzzer
|
||||
for _, f := range registeredFuzzers {
|
||||
parseErr := f.ParseStatsFile()
|
||||
|
||||
if parseErr != nil {
|
||||
log.Printf("Encountered error while parsing %s: %s", f, parseErr.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// and sleep
|
||||
time.Sleep(30 * time.Second)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user