diff --git a/internal/driver/adb/adb.go b/internal/driver/adb/adb.go index 327fa90..72d543e 100644 --- a/internal/driver/adb/adb.go +++ b/internal/driver/adb/adb.go @@ -3,6 +3,7 @@ package adb import ( + "errors" "fmt" "os/exec" "strings" @@ -11,6 +12,16 @@ import ( "memodroid/internal/driver" ) +// execErr enriches an exec error with the stderr output from the failed command. +func execErr(op string, err error) error { + var exitErr *exec.ExitError + if errors.As(err, &exitErr) && len(exitErr.Stderr) > 0 { + detail := strings.TrimSpace(string(exitErr.Stderr)) + return fmt.Errorf("%s: %s", op, detail) + } + return fmt.Errorf("%s: %w", op, err) +} + // ADB implements driver.Driver using the adb CLI. type ADB struct { mu sync.RWMutex @@ -43,7 +54,7 @@ func (a *ADB) SelectDevice(serial string) error { func (a *ADB) ConnectWifi(addr string) error { out, err := exec.Command("adb", "connect", addr).Output() if err != nil { - return fmt.Errorf("adb connect %s: %w", addr, err) + return execErr(fmt.Sprintf("adb connect %s", addr), err) } result := strings.TrimSpace(string(out)) if strings.HasPrefix(result, "failed") || strings.HasPrefix(result, "error") { @@ -60,7 +71,7 @@ func (a *ADB) ConnectWifi(addr string) error { func (a *ADB) DisconnectWifi(addr string) error { _, err := exec.Command("adb", "disconnect", addr).Output() if err != nil { - return fmt.Errorf("adb disconnect %s: %w", addr, err) + return execErr(fmt.Sprintf("adb disconnect %s", addr), err) } a.mu.Lock() if a.serial == addr { @@ -74,7 +85,7 @@ func (a *ADB) DisconnectWifi(addr string) error { func (a *ADB) ListDevices() ([]string, error) { out, err := exec.Command("adb", "devices").Output() if err != nil { - return nil, fmt.Errorf("adb devices: %w", err) + return nil, execErr("adb devices", err) } var serials []string for _, line := range strings.Split(string(out), "\n") { @@ -96,7 +107,7 @@ func (a *ADB) shell(args ...string) ([]byte, error) { cmdArgs = append(cmdArgs, args...) out, err := exec.Command("adb", cmdArgs...).Output() if err != nil { - return nil, fmt.Errorf("adb shell %s: %w", strings.Join(args, " "), err) + return nil, execErr(fmt.Sprintf("adb shell %s", strings.Join(args, " ")), err) } return out, nil } diff --git a/internal/driver/adb/mem.go b/internal/driver/adb/mem.go index a241a94..7108662 100644 --- a/internal/driver/adb/mem.go +++ b/internal/driver/adb/mem.go @@ -23,7 +23,7 @@ func (a *ADB) Peek(pid int, addr uintptr, size int) ([]byte, error) { return nil, fmt.Errorf("peek decode 0x%x: %w", addr, err) } if len(decoded) != size { - return nil, fmt.Errorf("peek 0x%x: wanted %d bytes, got %d", addr, size, len(decoded)) + return nil, fmt.Errorf("peek 0x%x: short read (wanted %d bytes, got %d — address may be at region boundary or unmapped)", addr, size, len(decoded)) } return decoded, nil } @@ -87,8 +87,7 @@ func (a *ADB) ReadRegion(pid int, addr uintptr, size int) ([]byte, error) { return nil, fmt.Errorf("read region decode 0x%x: %w", cur, err) } if len(decoded) == 0 { - // Region is not readable (e.g. guard page); treat as error. - return nil, fmt.Errorf("read region 0x%x: empty read", cur) + return nil, fmt.Errorf("read region 0x%x+%d: empty output (region may be inaccessible or guard page)", cur, rem) } out = append(out, decoded...) if len(decoded) < rem { diff --git a/internal/driver/adb/process.go b/internal/driver/adb/process.go index 636dd71..7f360b7 100644 --- a/internal/driver/adb/process.go +++ b/internal/driver/adb/process.go @@ -12,7 +12,7 @@ import ( func (a *ADB) ListProcesses() ([]driver.ProcessInfo, error) { out, err := a.shell("ps", "-A") if err != nil { - return nil, err + return nil, fmt.Errorf("list processes: %w", err) } var procs []driver.ProcessInfo for _, line := range strings.Split(string(out), "\n") {