o gopher2600

- debugger can now be run in a cpu profiling loop
This commit is contained in:
steve 2019-09-05 20:30:55 +01:00
parent f334e6e94d
commit 9e32748b0a
5 changed files with 73 additions and 46 deletions

View file

@ -16,7 +16,7 @@ const (
PlayError
DebuggerError
DisasmError
FPSError
PerformanceError
// debugger
ParserError

View file

@ -12,10 +12,10 @@ var messages = map[Errno]string{
TVOutOfSpec: "tv out of spec: %s", // sentinal
// program modes
PlayError: "error emulating vcs: %s",
DebuggerError: "error debugging vcs: %s",
FPSError: "error during fps profiling: %s",
DisasmError: "error during disassembly: %s",
PlayError: "error emulating vcs: %s",
DebuggerError: "error debugging vcs: %s",
PerformanceError: "error during performance profiling: %s",
DisasmError: "error during disassembly: %s",
// debugger
ParserError: "parser error: %s: %s (char %d)", // first placeholder is the command definition

View file

@ -169,6 +169,7 @@ func main() {
tvType := modeFlags.String("tv", "AUTO", "television specification: NTSC, PAL")
termType := modeFlags.String("term", "COLOR", "terminal type to use in debug mode: COLOR, PLAIN")
initScript := modeFlags.String("initscript", defaultInitScript, "terminal type to use in debug mode: COLOR, PLAIN")
profile := modeFlags.Bool("profile", false, "run debugger through cpu profiler")
modeFlagsParse()
dbg, err := debugger.NewDebugger(*tvType)
@ -195,10 +196,27 @@ func main() {
// it's okay if DEBUG mode is started with no cartridges
fallthrough
case 1:
err := dbg.Start(term, *initScript, modeFlags.Arg(0))
if err != nil {
fmt.Printf("* %s\n", err)
os.Exit(2)
runner := func() error {
err := dbg.Start(term, *initScript, modeFlags.Arg(0))
if err != nil {
return errors.NewFormattedError(errors.PerformanceError, err)
}
return nil
}
if *profile {
err := performance.ProfileCPU("debug.cpu.profile", runner)
if err != nil {
fmt.Printf("* %s\n", err)
os.Exit(2)
}
err = performance.ProfileMem("debug.mem.profile")
if err != nil {
fmt.Printf("* %s\n", err)
os.Exit(2)
}
} else {
runner()
}
default:
fmt.Printf("* too many arguments for %s mode\n", mode)

View file

@ -21,46 +21,46 @@ func Check(output io.Writer, profile bool, cartridgeFile string, display bool, t
if display {
ftv, err = sdl.NewGUI(tvType, scaling, nil)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
return errors.NewFormattedError(errors.PerformanceError, err)
}
err = ftv.(gui.GUI).SetFeature(gui.ReqSetVisibility, true)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
return errors.NewFormattedError(errors.PerformanceError, err)
}
} else {
ftv, err = television.NewStellaTelevision(tvType)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
return errors.NewFormattedError(errors.PerformanceError, err)
}
}
// create vcs using the tv created above
vcs, err := hardware.NewVCS(ftv)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
return errors.NewFormattedError(errors.PerformanceError, err)
}
// attach cartridge to te vcs
err = vcs.AttachCartridge(cartridgeFile)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
return errors.NewFormattedError(errors.PerformanceError, err)
}
// parse supplied duration
duration, err := time.ParseDuration(runTime)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
return errors.NewFormattedError(errors.PerformanceError, err)
}
// get starting frame number (should be 0)
startFrame, err := ftv.GetState(television.ReqFramenum)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
return errors.NewFormattedError(errors.PerformanceError, err)
}
// run for specified period of time
err = cpuProfile(profile, "cpu.profile", func() error {
runner := func() error {
// setup trigger that expires when duration has elapsed
timesUp := make(chan bool)
@ -85,23 +85,34 @@ func Check(output io.Writer, profile bool, cartridgeFile string, display bool, t
}
})
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
return errors.NewFormattedError(errors.PerformanceError, err)
}
return nil
})
}
if profile {
err = ProfileCPU("cpu.profile", runner)
} else {
err = runner()
}
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
return errors.NewFormattedError(errors.PerformanceError, err)
}
// get ending frame number
endFrame, err := vcs.TV.GetState(television.ReqFramenum)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
return errors.NewFormattedError(errors.PerformanceError, err)
}
numFrames := endFrame - startFrame
fps, accuracy := CalcFPS(ftv, numFrames, duration.Seconds())
output.Write([]byte(fmt.Sprintf("%.2f fps (%d frames in %.2f seconds) %.1f%%\n", fps, numFrames, duration.Seconds(), accuracy)))
return memProfile(profile, "mem.profile")
if profile {
err = ProfileMem("mem.profile")
}
return err
}

View file

@ -7,36 +7,34 @@ import (
"runtime/pprof"
)
func cpuProfile(profile bool, outFile string, run func() error) error {
if profile {
// write cpu profile
f, err := os.Create(outFile)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
}
err = pprof.StartCPUProfile(f)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
}
defer pprof.StopCPUProfile()
// ProfileCPU runs supplied function "through" the pprof CPU profiler
func ProfileCPU(outFile string, run func() error) error {
// write cpu profile
f, err := os.Create(outFile)
if err != nil {
return errors.NewFormattedError(errors.PerformanceError, err)
}
err = pprof.StartCPUProfile(f)
if err != nil {
return errors.NewFormattedError(errors.PerformanceError, err)
}
defer pprof.StopCPUProfile()
return run()
}
func memProfile(profile bool, outFile string) error {
if profile {
f, err := os.Create(outFile)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
}
runtime.GC()
err = pprof.WriteHeapProfile(f)
if err != nil {
return errors.NewFormattedError(errors.FPSError, err)
}
f.Close()
// ProfileMem takes a snapshot of memory and writes to outFile
func ProfileMem(outFile string) error {
f, err := os.Create(outFile)
if err != nil {
return errors.NewFormattedError(errors.PerformanceError, err)
}
runtime.GC()
err = pprof.WriteHeapProfile(f)
if err != nil {
return errors.NewFormattedError(errors.PerformanceError, err)
}
f.Close()
return nil
}