From 84cce0a2aff60bced9fc9d1543a6393837f8d63d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Fri, 6 Dec 2024 18:28:31 +0200 Subject: [PATCH] coredump: no need for full dump with bundled files (#213) --- tools/coredump/README.md | 13 +++++++--- tools/coredump/new.go | 54 ++++++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/tools/coredump/README.md b/tools/coredump/README.md index 29ccdef8..efd9410e 100644 --- a/tools/coredump/README.md +++ b/tools/coredump/README.md @@ -108,11 +108,18 @@ with existing test cases. In this variant we essentially make the kernel think that the target application crashed, causing the kernel to save a coredump for us. -#### Setting the coredump filter +#### Setting the coredump filter (optional) Coredumps normally contain only the anonymous and modified pages to save disk -space. For our test cases, we want a full process memory dump that also contains -the pages mapped into the process from the ELF files. +space. This is sufficient if the mapped in ELF files are available to the +`coredump` utility to be bundled. This is the case if you run +`./coredump new -core core` on the same machine where the core was generated, +or if you supply `-sysroot` as a prefix to find the correct files. + +If the above is not possible, the testing infrastructure has limited support +to allow reading the ELF file data directly from the coredump. In this case +a full process memory dump that also contains the pages mapped into the process +from the ELF files is needed. To get a full process memory dump one has to set the [`coredump_filter`][filter] in advance by running: diff --git a/tools/coredump/new.go b/tools/coredump/new.go index af4a519b..56a34250 100644 --- a/tools/coredump/new.go +++ b/tools/coredump/new.go @@ -29,6 +29,7 @@ type newCmd struct { // User-specified command line arguments. coredumpPath string + sysroot string pid uint64 name string importThreadInfo string @@ -110,6 +111,7 @@ func newNewCmd(store *modulestore.Store) *ffcli.Command { set := flag.NewFlagSet("new", flag.ExitOnError) set.StringVar(&args.coredumpPath, "core", "", "Path of the coredump to import") + set.StringVar(&args.sysroot, "sysroot", "", "Path for the coredump associated ELF files") set.Uint64Var(&args.pid, "pid", 0, "PID to create a fresh coredump for") set.StringVar(&args.name, "name", "", "Name for the test case [required]") set.StringVar(&args.importThreadInfo, "import-thread-info", "", "If this flag is specified, "+ @@ -139,17 +141,19 @@ func (cmd *newCmd) exec(context.Context, []string) (err error) { } var corePath string - prefix := "" + prefix := cmd.sysroot if cmd.coredumpPath != "" { corePath = cmd.coredumpPath } else { // No path provided: create a new dump. - corePath, err = dumpCore(cmd.pid) + corePath, err = dumpCore(cmd.pid, cmd.noModuleBundling) if err != nil { return fmt.Errorf("failed to create coredump: %w", err) } defer os.Remove(corePath) - prefix = fmt.Sprintf("/proc/%d/root/", cmd.pid) + if prefix == "" { + prefix = fmt.Sprintf("/proc/%d/root/", cmd.pid) + } } core, err := newTrackedCoredump(corePath, prefix) @@ -195,32 +199,34 @@ func (cmd *newCmd) exec(context.Context, []string) (err error) { return nil } -func dumpCore(pid uint64) (string, error) { - // Backup current coredump filter mask. - // https://man7.org/linux/man-pages/man5/core.5.html - coredumpFilterPath := fmt.Sprintf("/proc/%d/coredump_filter", pid) - prevMask, err := os.ReadFile(coredumpFilterPath) - if err != nil { - return "", fmt.Errorf("failed to read coredump filter: %w", err) - } - // Adjust coredump filter mask. - //nolint:gosec - err = os.WriteFile(coredumpFilterPath, []byte("0x3f"), 0o644) - if err != nil { - return "", fmt.Errorf("failed to write coredump filter: %w", err) - } - // Restore coredump filter mask upon leaving the function. - defer func() { +func dumpCore(pid uint64, noModuleBundling bool) (string, error) { + if noModuleBundling { + // Backup current coredump filter mask. + // https://man7.org/linux/man-pages/man5/core.5.html + coredumpFilterPath := fmt.Sprintf("/proc/%d/coredump_filter", pid) + prevMask, err := os.ReadFile(coredumpFilterPath) + if err != nil { + return "", fmt.Errorf("failed to read coredump filter: %w", err) + } + // Adjust coredump filter mask. //nolint:gosec - err2 := os.WriteFile(coredumpFilterPath, prevMask, 0o644) - if err2 != nil { - log.Warnf("Failed to restore previous coredump filter: %v", err2) + err = os.WriteFile(coredumpFilterPath, []byte("0x3f"), 0o644) + if err != nil { + return "", fmt.Errorf("failed to write coredump filter: %w", err) } - }() + // Restore coredump filter mask upon leaving the function. + defer func() { + //nolint:gosec + err2 := os.WriteFile(coredumpFilterPath, append([]byte("0x"), prevMask...), 0o644) + if err2 != nil { + log.Warnf("Failed to restore previous coredump filter: %v", err2) + } + }() + } // `gcore` only accepts a path-prefix, not an exact path. //nolint:gosec - err = exec.Command("gcore", "-o", gcorePathPrefix, strconv.FormatUint(pid, 10)).Run() + err := exec.Command("gcore", "-o", gcorePathPrefix, strconv.FormatUint(pid, 10)).Run() if err != nil { return "", fmt.Errorf("gcore failed: %w", err) }