Skip to content

Commit

Permalink
Use multi-widgets to show metrics (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
nakabonne authored Sep 20, 2020
1 parent 90c64bf commit 8788b7c
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 23 deletions.
83 changes: 77 additions & 6 deletions gui/drawer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gui

import (
"context"
"fmt"
"time"

"github.com/mum4k/termdash/cell"
Expand All @@ -12,10 +13,11 @@ import (
)

type drawer struct {
widgets *widgets
chartCh chan *attacker.Result
gaugeCh chan bool
reportCh chan string
widgets *widgets
chartCh chan *attacker.Result
gaugeCh chan bool
metricsCh chan *attacker.Metrics
messageCh chan string

// aims to avoid to perform multiple `redrawChart`.
chartDrawing bool
Expand Down Expand Up @@ -68,13 +70,82 @@ func (d *drawer) redrawGauge(ctx context.Context, maxSize int) {
}
}

const (
latenciesTextFormat = `Total: %v
Mean: %v
P50: %v
P90: %v
P95: %v
P99: %v
Max: %v
Min: %v`

bytesTextFormat = `In:
Total: %v
Mean: %v
Out:
Total: %v
Mean: %v`

othersTextFormat = `Earliest: %v
Latest: %v
End: %v
Duration: %v
Wait: %v
Requests: %d
Rate: %f
Throughput: %f
Success: %f
StatusCodes:`
)

func (d *drawer) redrawReport(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case report := <-d.reportCh:
d.widgets.reportText.Write(report, text.WriteReplace())
case metrics := <-d.metricsCh:
d.widgets.latenciesText.Write(
fmt.Sprintf(latenciesTextFormat,
metrics.Latencies.Total,
metrics.Latencies.Mean,
metrics.Latencies.P50,
metrics.Latencies.P90,
metrics.Latencies.P95,
metrics.Latencies.P99,
metrics.Latencies.Max,
metrics.Latencies.Min,
), text.WriteReplace())

d.widgets.bytesText.Write(
fmt.Sprintf(bytesTextFormat,
metrics.BytesIn.Total,
metrics.BytesIn.Mean,
metrics.BytesOut.Total,
metrics.BytesOut.Mean,
), text.WriteReplace())

d.widgets.othersText.Write(
fmt.Sprintf(othersTextFormat,
metrics.Earliest,
metrics.Latest,
metrics.End,
metrics.Duration,
metrics.Wait,
metrics.Requests,
metrics.Rate,
metrics.Throughput,
metrics.Success,
), text.WriteReplace())

for code, n := range metrics.StatusCodes {
d.widgets.othersText.Write(fmt.Sprintf(`
%s: %d`, code, n))
}
for i, e := range metrics.Errors {
d.widgets.othersText.Write(fmt.Sprintf(`
%d: %s`, i, e))
}
}
}
}
19 changes: 13 additions & 6 deletions gui/gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ func Run() error {
}

d := &drawer{
widgets: w,
chartCh: make(chan *attacker.Result),
gaugeCh: make(chan bool),
reportCh: make(chan string),
widgets: w,
chartCh: make(chan *attacker.Result),
gaugeCh: make(chan bool),
metricsCh: make(chan *attacker.Metrics),
}
go d.redrawReport(ctx)

Expand All @@ -59,7 +59,7 @@ func Run() error {
func gridLayout(w *widgets) ([]container.Option, error) {
raw1 := grid.RowHeightPerc(65, grid.Widget(w.latencyChart, container.Border(linestyle.Light), container.BorderTitle("Latency (ms)")))
raw2 := grid.RowHeightPerc(30,
grid.ColWidthPerc(64,
grid.ColWidthPerc(50,
grid.RowHeightPerc(34, grid.Widget(w.urlInput, container.Border(linestyle.Light), container.BorderTitle("Target URL"))),
grid.RowHeightPerc(33,
grid.ColWidthPerc(20, grid.Widget(w.rateLimitInput, container.Border(linestyle.Light), container.BorderTitle("Rate Limit"))),
Expand All @@ -70,7 +70,14 @@ func gridLayout(w *widgets) ([]container.Option, error) {
),
grid.RowHeightPerc(33, grid.Widget(w.bodyInput, container.Border(linestyle.Light), container.BorderTitle("Body"))),
),
grid.ColWidthPerc(35, grid.Widget(w.reportText, container.Border(linestyle.Light), container.BorderTitle("Report"))),
grid.ColWidthPerc(50,
grid.RowHeightPerc(85,
grid.ColWidthPerc(25, grid.Widget(w.latenciesText, container.Border(linestyle.Light), container.BorderTitle("Latencies"))),
grid.ColWidthPerc(25, grid.Widget(w.bytesText, container.Border(linestyle.Light), container.BorderTitle("Bytes"))),
grid.ColWidthPerc(50, grid.Widget(w.othersText, container.Border(linestyle.Light), container.BorderTitle("Others"))),
),
grid.RowHeightPerc(15, grid.Widget(w.messageText, container.Border(linestyle.Light), container.BorderTitle("Message"))),
),
)
raw3 := grid.RowHeightPerc(4,
grid.ColWidthPerc(64, grid.Widget(w.progressGauge, container.Border(linestyle.None))),
Expand Down
6 changes: 3 additions & 3 deletions gui/keybinds.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ func attack(ctx context.Context, d *drawer) {
}
target := d.widgets.urlInput.Read()
if _, err := url.ParseRequestURI(target); err != nil {
d.reportCh <- fmt.Sprintf("Bad URL: %v", err)
d.messageCh <- fmt.Sprintf("Bad URL: %v", err)
return
}
opts, err := makeOptions(d.widgets)
if err != nil {
d.reportCh <- err.Error()
d.messageCh <- err.Error()
return
}
requestNum := opts.Rate * int(opts.Duration/time.Second)
Expand All @@ -47,7 +47,7 @@ func attack(ctx context.Context, d *drawer) {
go d.redrawGauge(ctx, requestNum)
go func(ctx context.Context, d *drawer, t string, o attacker.Options) {
metrics := attacker.Attack(ctx, t, d.chartCh, o)
d.reportCh <- metrics.String()
d.metricsCh <- metrics
d.chartCh <- &attacker.Result{End: true}
}(ctx, d, target, opts)
}
Expand Down
39 changes: 31 additions & 8 deletions gui/widgets.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,36 @@ type widgets struct {
bodyInput *textinput.TextInput
headerInput *textinput.TextInput
timeoutInput *textinput.TextInput
latencyChart *linechart.LineChart
reportText *text.Text
progressGauge *gauge.Gauge
navi *text.Text

latencyChart *linechart.LineChart

messageText *text.Text
latenciesText *text.Text
bytesText *text.Text
othersText *text.Text

progressGauge *gauge.Gauge
navi *text.Text
}

func newWidgets() (*widgets, error) {
latencyChart, err := newLineChart()
if err != nil {
return nil, err
}
reportText, err := newText("Give the target URL and press Enter, then the attack will be launched.")
messageText, err := newText("Give the target URL and press Enter.")
if err != nil {
return nil, err
}
latenciesText, err := newText("")
if err != nil {
return nil, err
}
bytesText, err := newText("")
if err != nil {
return nil, err
}
othersText, err := newText("")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -87,7 +105,10 @@ func newWidgets() (*widgets, error) {
headerInput: headerInput,
timeoutInput: timeoutInput,
latencyChart: latencyChart,
reportText: reportText,
messageText: messageText,
latenciesText: latenciesText,
bytesText: bytesText,
othersText: othersText,
progressGauge: progressGauge,
navi: navi,
}, nil
Expand All @@ -106,8 +127,10 @@ func newText(s string) (*text.Text, error) {
if err != nil {
return nil, err
}
if err := t.Write(s); err != nil {
return nil, err
if s != "" {
if err := t.Write(s); err != nil {
return nil, err
}
}
return t, nil
}
Expand Down

0 comments on commit 8788b7c

Please sign in to comment.