···1414 "os"
1515 "os/exec"
1616 "path/filepath"
1717+ "regexp"
1718 "slices"
1819 "strings"
1920 "sync/atomic"
···285286 return watchCtx, exited, cancel
286287}
287288288288-func vmCrashLog(vm VMHandle) string {
289289+func VMCrashLog(vm VMHandle) string {
289290 if vm == nil {
290291 return ""
291292 }
···373374374375 return runner.Start(ctx, cfg, volumePaths, logger)
375376}
377377+378378+// checks serial log for ooms or kernel panic
379379+// this is very linux specific! but these strings are stable in linux itself, see mm/oom_kill.c and kernel/panic.c
380380+func ParseCrashLog(detail string) (error, bool) {
381381+ if strings.Contains(detail, "Out of memory:") {
382382+ // we can show process name where possible
383383+ re := regexp.MustCompile(`Out of memory: Killed process \d+ \(([^)]+)\)`)
384384+ matches := re.FindStringSubmatch(detail)
385385+ if len(matches) > 1 {
386386+ return fmt.Errorf("guest out of memory (process '%s' killed by guest kernel OOM)", matches[1]), true
387387+ }
388388+ return errors.New("guest out of memory (OOM killer invoked)"), true
389389+ }
390390+ if strings.Contains(detail, "Kernel panic") {
391391+ return errors.New("guest kernel panic"), true
392392+ }
393393+ return nil, false
394394+}