blob: 5ca8858058f634d64071868b5d1524e20f86abac [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"flag"
"fmt"
"io"
"os"
"os/exec"
"text/template"
"android.googlesource.com/platform/tools/gpu/framework/app"
"android.googlesource.com/platform/tools/gpu/framework/log"
)
const plotTemplate = `#!/usr/bin/env gnuplot
$dataset1 << EOD
#index average median min max
{{range $idx, $s := .First}}
{{$sm := $s.Values}}{{$s.Index}} {{$sm.Average.Seconds}} {{$sm.Median.Seconds}} {{$sm.Min.Seconds}} {{$sm.Max.Seconds}}{{end}}
EOD
{{if .Second}}$dataset2 << EOD
#index average median min max
{{range $idx, $s := .Second}}
{{$sm := $s.Values}}{{$s.Index}} {{$sm.Average.Seconds}} {{$sm.Median.Seconds}} {{$sm.Min.Seconds}} {{$sm.Max.Seconds}}{{end}}
EOD{{end}}
reset
set terminal svg size 1600, 900
{{.Extra}}
set title "{/=20 {{.BenchName}}}\n{/=16 {{.Name1}}{{if .Second}} vs {{.Name2}}{{end}}}"
set xlabel "Index"
set ylabel "Seconds"
set style fill transparent solid 0.2 noborder
plot {{if .ShowMinMax}}'$dataset1' using 1:4:5 with filledcurves title 'min..max:{{.Name1}}', \
{{end}}{{if .ShowAverage}}'$dataset1' using 1:2 with lp lt 3 pt 7 ps 0.5 lw 1 title 'avg:{{.Name1}}', \
{{end}}'$dataset1' using 1:3 with lp lt 1 pt 7 ps 0.5 lw 1 title 'median:{{.Name1}}'{{if .Second}}, \
{{if .ShowMinMax}}'$dataset2' using 1:4:5 with filledcurves title 'min..max:{{.Name2}}', \
{{end}}{{if .ShowAverage}}'$dataset2' using 1:2 with lp lt 4 pt 7 ps 0.5 lw 1 title 'avg:{{.Name2}}', \
{{end}}'$dataset2' using 1:3 with lp lt 5 pt 7 ps 0.5 lw 1 title 'median:{{.Name2}}'{{end}}
`
var (
flagShowMinMax bool
flagShowAverage bool
flagRunGnuplot bool
flagBenchmarkNameToPlot string
)
func init() {
verb := &app.Verb{
Name: "plot",
ShortHelp: "Plots samples from a benchmark out of one or two perfz files",
Run: plotVerb,
ShortUsage: "<perfz> [perfz]",
}
verb.Flags.StringVar(&flagBenchmarkNameToPlot, "b", "", "benchmark name")
verb.Flags.StringVar(&flagTextualOutput, "o", "-", "output file")
verb.Flags.BoolVar(&flagShowMinMax, "mm", true, "show min and max")
verb.Flags.BoolVar(&flagShowAverage, "avg", true, "show average")
verb.Flags.BoolVar(&flagRunGnuplot, "run-gnuplot", true, "run gnuplot")
app.AddVerb(verb)
}
func getPlotData(ctx log.Context, perfzFile string, benchmarkName string) (IndexedMultisamples, string, error) {
perfz, err := LoadPerfz(ctx, perfzFile, flagVerifyHashes)
if err != nil {
return IndexedMultisamples{}, "", err
}
bench, err := selectBenchmark(perfz, benchmarkName)
if err != nil {
return IndexedMultisamples{}, "", err
}
return bench.Samples.IndexedMultisamples(), bench.Input.Name, nil
}
func plotVerb(ctx log.Context, flags flag.FlagSet) error {
if flags.NArg() < 1 {
app.Usage(ctx, "At least one argument expected.")
return nil
}
args := struct {
First IndexedMultisamples
Second IndexedMultisamples
Name1 string
Name2 string
BenchName string
ShowAverage bool
ShowMinMax bool
Extra string
}{
Name1: flags.Arg(0),
Name2: flags.Arg(1),
ShowAverage: flagShowAverage,
ShowMinMax: flagShowMinMax,
Extra: func() string {
if flagRunGnuplot && flagTextualOutput != "-" {
return fmt.Sprintf(`set output "%s"`, flagTextualOutput)
}
return ""
}(),
}
var err error
args.First, args.BenchName, err = getPlotData(ctx, args.Name1, flagBenchmarkNameToPlot)
if err != nil {
return err
}
if flags.NArg() >= 2 {
args.Second, _, err = getPlotData(ctx, args.Name2, args.BenchName)
if err != nil {
return err
}
}
tmpl, err := template.New("plot").Parse(plotTemplate)
if err != nil {
return err
}
writeScript := func(w io.Writer) error {
return tmpl.Execute(w, args)
}
if flagRunGnuplot {
fn, _, err := FuncDataSource(writeScript).DiskFile()
if err != nil {
return err
}
defer os.Remove(fn)
cmd := exec.Command("gnuplot", fn)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
} else {
return writeAllFn(flagTextualOutput, writeScript)
}
}