added MEMUSAGE PROFILE command

mem profile saved on ctrl+alt+m

memory profiles can be used with "go tool pprof"
This commit is contained in:
JetSetIlly 2024-05-06 09:50:47 +01:00
parent 47c7c95fd7
commit c9ee3fc20f
6 changed files with 67 additions and 13 deletions

View file

@ -2109,17 +2109,30 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
}
case cmdMemUsage:
var m runtime.MemStats
runtime.ReadMemStats(&m)
option, ok := tokens.Get()
if ok {
option = strings.ToUpper(option)
switch option {
case "PROFILE":
fn, err := dbg.memoryProfile()
if err != nil {
return err
}
dbg.printLine(terminal.StyleLog, fmt.Sprintf("memory profile written to %s", fn))
}
} else {
var m runtime.MemStats
runtime.ReadMemStats(&m)
s := strings.Builder{}
s := strings.Builder{}
s.WriteString(fmt.Sprintf("Alloc = %v MB\n", m.Alloc/1048576))
s.WriteString(fmt.Sprintf(" TotalAlloc = %v MB\n", m.TotalAlloc/1048576))
s.WriteString(fmt.Sprintf(" Sys = %v MB\n", m.Sys/1048576))
s.WriteString(fmt.Sprintf(" NumGC = %v", m.NumGC))
s.WriteString(fmt.Sprintf("Alloc = %v MB\n", m.Alloc/1048576))
s.WriteString(fmt.Sprintf(" TotalAlloc = %v MB\n", m.TotalAlloc/1048576))
s.WriteString(fmt.Sprintf(" Sys = %v MB\n", m.Sys/1048576))
s.WriteString(fmt.Sprintf(" NumGC = %v", m.NumGC))
dbg.printLine(terminal.StyleLog, s.String())
dbg.printLine(terminal.StyleLog, s.String())
}
case cmdVersion:
dbg.printLine(terminal.StyleLog, version.Version)

View file

@ -532,6 +532,8 @@ Note that while "ONSTEP LOG LAST" is a valid construct it may not print what you
log entry after every step, even if the last log entry is not new. "ONSTEP LOG LAST; LOG CLEAR" is maybe more intuitive
but with the maybe unwanted side effect of clearing the log.`,
cmdMemUsage: "Print memory usage information",
cmdVersion: "Print version information for the emulator",
cmdMemUsage: `Print memory usage information. The PROFILE option will save a pprof file in
the working directory.`,
cmdVersion: "Print version information for the emulator",
}

View file

@ -143,7 +143,7 @@ var commandTemplate = []string{
// emulation
cmdLog + " (LAST|RECENT|CLEAR)",
cmdMemUsage,
cmdMemUsage + " (PROFILE)",
cmdVersion + " (REVISION)",
}

View file

@ -21,6 +21,8 @@ import (
"io"
"os"
"os/signal"
"runtime"
"runtime/pprof"
"strings"
"sync/atomic"
"syscall"
@ -1489,7 +1491,7 @@ func (dbg *Debugger) InsertCartridge(filename string) {
}
// GetLiveDisasmEntry returns the formatted disasembly entry of the last CPU
// execution and the bank information
// execution and the bank informations.String())
func (dbg *Debugger) GetLiveDisasmEntry() disassembly.Entry {
if dbg.liveDisasmEntry == nil {
return disassembly.Entry{}
@ -1502,3 +1504,23 @@ func (dbg *Debugger) GetLiveDisasmEntry() disassembly.Entry {
func (dbg *Debugger) GetCoProcBus() coprocessor.CartCoProcBus {
return dbg.vcs.Mem.Cart.GetCoProcBus()
}
// memoryProfile forces a garbage collection event and takes a runtime memory
// profile and saves it to the working directory
func (dbg *Debugger) memoryProfile() (string, error) {
fn := unique.Filename("", dbg.cartload.Name)
fn = fmt.Sprintf("%s_mem.profile", fn)
f, err := os.Create(fn)
if err != nil {
return "", err
}
defer f.Close()
runtime.GC()
err = pprof.WriteHeapProfile(f)
if err != nil {
return "", err
}
return fn, nil
}

View file

@ -81,3 +81,16 @@ func (dbg *Debugger) PushPropertyLookup(hashMD5 string, result chan properties.E
result <- e
})
}
// PushMemoryProfile forces a garbage collection event and takes a runtime
// memory profile and saves it to the working directory
func (dbg *Debugger) PushMemoryProfile() {
dbg.PushFunctionImmediate(func() {
fn, err := dbg.memoryProfile()
if err != nil {
logger.Logf(logger.Allow, "memory profiling", err.Error())
return
}
logger.Logf(logger.Allow, "memory profiling", "saved to %s", fn)
})
}

View file

@ -205,7 +205,11 @@ func (img *SdlImgui) serviceKeyboard(ev *sdl.KeyboardEvent) {
case sdl.SCANCODE_M:
if ctrl {
img.toggleAudioMute()
if alt {
img.dbg.PushMemoryProfile()
} else {
img.toggleAudioMute()
}
} else {
handled = false
}