aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2026-02-04 08:29:34 +0100
committerDmitry Vyukov <dvyukov@google.com>2026-02-05 15:34:55 +0000
commitf03c419189ef8ed823e306a342ee4d330fb2c394 (patch)
treeec73a69b1e686ce0a4a3f23e7095e2b4097b706b /pkg
parent7bbe7cb82a09dcf35ac463c8661a3fff688dbb2d (diff)
pkg/aflow/action/crash: handle boot errors better
Provide better errors messages on boot errors.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/aflow/action/crash/reproduce.go37
-rw-r--r--pkg/aflow/action/crash/test.go4
2 files changed, 34 insertions, 7 deletions
diff --git a/pkg/aflow/action/crash/reproduce.go b/pkg/aflow/action/crash/reproduce.go
index cb625af08..7e194a3e3 100644
--- a/pkg/aflow/action/crash/reproduce.go
+++ b/pkg/aflow/action/crash/reproduce.go
@@ -44,6 +44,13 @@ type reproduceResult struct {
CrashReport string
}
+// ReproduceCrash tests reproducer and returns:
+// - Report: if the reproducer caused the kernel crash
+// - boot failure: if the kernel failed to boot or function properly
+// (if kernel crashed during build/boot, the Report is not returned)
+// - error: for unexpected failures
+//
+// All 3 values are empty, if everything went well, and kernel has not crashed.
func ReproduceCrash(args ReproduceArgs, workdir string) (*report.Report, string, error) {
if args.Type != "qemu" {
return nil, "", errors.New("only qemu VM type is supported")
@@ -78,20 +85,40 @@ func ReproduceCrash(args ReproduceArgs, workdir string) (*report.Report, string,
if err != nil {
return nil, "", err
}
+ // TODO: run multiple instances, handle TestError.Infra, and aggregate results.
results, err := env.Test(1, nil, nil, []byte(args.ReproC))
if err != nil {
return nil, "", err
}
- if results[0].Error != nil {
- if crashErr := new(instance.CrashError); errors.As(results[0].Error, &crashErr) {
+ if err := results[0].Error; err != nil {
+ if crashErr := new(instance.CrashError); errors.As(err, &crashErr) {
return crashErr.Report, "", nil
+ } else if testErr := new(instance.TestError); errors.As(err, &testErr) {
+ return parseTestError(testErr)
} else {
- return nil, results[0].Error.Error(), nil
+ return nil, err.Error(), nil
}
}
return nil, "", nil
}
+func parseTestError(err *instance.TestError) (*report.Report, string, error) {
+ if err.Infra {
+ // No point in showing this to LLM and asking to fix.
+ return nil, "", fmt.Errorf("%v\n%v\n%s", err.Error(), err.Title, err.Output)
+ }
+ what := "Basic kernel testing failed"
+ if err.Boot {
+ what = "Kernel failed to boot"
+ }
+ extraInfo := err.Output
+ // Don't use TestError.Report for crashes like "lost connection" that don't have a report.
+ if err.Report != nil && err.Report.Report != nil {
+ extraInfo = err.Report.Report
+ }
+ return nil, fmt.Sprintf("%v: %v\n%s", what, err.Title, extraInfo), nil
+}
+
func reproduce(ctx *aflow.Context, args ReproduceArgs) (reproduceResult, error) {
imageData, err := os.ReadFile(args.Image)
if err != nil {
@@ -112,12 +139,12 @@ func reproduce(ctx *aflow.Context, args ReproduceArgs) (reproduceResult, error)
if err != nil {
return res, err
}
- rep, buildError, err := ReproduceCrash(args, workdir)
+ rep, bootError, err := ReproduceCrash(args, workdir)
if rep != nil {
res.BugTitle = rep.Title
res.Report = string(rep.Report)
}
- res.Error = buildError
+ res.Error = bootError
return res, err
})
if err != nil {
diff --git a/pkg/aflow/action/crash/test.go b/pkg/aflow/action/crash/test.go
index b479cfde0..2eda9b84c 100644
--- a/pkg/aflow/action/crash/test.go
+++ b/pkg/aflow/action/crash/test.go
@@ -94,11 +94,11 @@ func testPatch(ctx *aflow.Context, args testArgs) (testResult, error) {
KernelCommit: args.KernelCommit,
KernelConfig: args.KernelConfig,
}
- rep, reportLog, err := ReproduceCrash(reproduceArgs, workdir)
+ rep, bootError, err := ReproduceCrash(reproduceArgs, workdir)
if rep != nil {
res.TestError = string(rep.Report)
} else {
- res.TestError = reportLog
+ res.TestError = bootError
}
return res, err
})