github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/internal/crash/crash.go (about)

     1  // Copyright 2017 The ChromiumOS Authors
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  // Package crash can be used by local tests to interact with on-device crash reports.
     6  package crash
     7  
     8  import (
     9  	"context"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"go.chromium.org/tast/core/fsutil"
    15  	"go.chromium.org/tast/core/internal/logging"
    16  )
    17  
    18  const (
    19  	// DefaultCrashDir contains the directory where the kernel writes core and minidump files.
    20  	DefaultCrashDir = "/var/spool/crash"
    21  	// ChromeCrashDir contains the directory where Chrome writes minidump files.
    22  	// Tests configure Chrome to write crashes to this location/ by setting the BREAKPAD_DUMP_LOCATION
    23  	// environment variable. This overrides the default /home/chronos/user/crash location, which is in
    24  	// the user's cryptohome and hence is only accessible while they are logged in.
    25  	ChromeCrashDir = "/home/chronos/crash"
    26  
    27  	// BIOSExt is the extension for bios crash files.
    28  	BIOSExt = ".bios_log"
    29  	// CoreExt is the extension for core files.
    30  	CoreExt = ".core"
    31  	// MinidumpExt is the extension for minidump crash files.
    32  	MinidumpExt = ".dmp"
    33  	// LogExt is the extension for log files containing additional information that are written by crash_reporter.
    34  	LogExt = ".log"
    35  	// InfoExt is the extention for info files.
    36  	InfoExt = ".info"
    37  	// ProclogExt is the extention for proclog files.
    38  	ProclogExt = ".proclog"
    39  	// KCrashExt is the extension for log files created by kernel warnings and crashes.
    40  	KCrashExt = ".kcrash"
    41  	// GPUStateExt is the extension for GPU state files written by crash_reporter.
    42  	GPUStateExt = ".i915_error_state.log.xz"
    43  	// MetadataExt is the extension for metadata files written by crash collectors and read by crash_sender.
    44  	MetadataExt = ".meta"
    45  	// CompressedTxtExt is an extension on the compressed log files written by crash_reporter.
    46  	CompressedTxtExt = ".txt.gz"
    47  	// CompressedLogExt is an extension on the compressed log files written by crash_reporter.
    48  	CompressedLogExt = ".log.gz"
    49  
    50  	lsbReleasePath = "/etc/lsb-release"
    51  )
    52  
    53  // DefaultDirs returns all standard directories to which crashes are written.
    54  func DefaultDirs() []string {
    55  	return []string{DefaultCrashDir, ChromeCrashDir}
    56  }
    57  
    58  // isCrashFile returns true if filename could be the name of a file generated by
    59  // crashes or crash_reporter.
    60  func isCrashFile(filename string) bool {
    61  	knownExts := []string{
    62  		BIOSExt,
    63  		CoreExt,
    64  		MinidumpExt,
    65  		LogExt,
    66  		ProclogExt,
    67  		InfoExt,
    68  		KCrashExt,
    69  		GPUStateExt,
    70  		MetadataExt,
    71  		CompressedTxtExt,
    72  		CompressedLogExt,
    73  	}
    74  	for _, ext := range knownExts {
    75  		if strings.HasSuffix(filename, ext) {
    76  			return true
    77  		}
    78  	}
    79  	return false
    80  }
    81  
    82  // GetCrashes returns the paths of all files in dirs generated in response to crashes.
    83  // Nonexistent directories are skipped.
    84  func GetCrashes(dirs ...string) ([]string, error) {
    85  	var crashFiles []string
    86  	for _, dir := range dirs {
    87  		df, err := os.Open(dir)
    88  		if os.IsNotExist(err) {
    89  			continue
    90  		} else if err != nil {
    91  			return nil, err
    92  		}
    93  		files, err := df.Readdirnames(-1)
    94  		df.Close()
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  
    99  		for _, fn := range files {
   100  			if isCrashFile(fn) {
   101  				crashFiles = append(crashFiles, filepath.Join(dir, fn))
   102  			}
   103  		}
   104  	}
   105  	return crashFiles, nil
   106  }
   107  
   108  // CopyNewFiles copies paths that are present in newPaths but not in oldPaths into dstDir.
   109  // If maxPerExec is positive, it limits the maximum number of files that will be copied
   110  // for each base executable. The returned warnings map contains non-fatal errors keyed by
   111  // crash file paths.
   112  func CopyNewFiles(ctx context.Context, dstDir string, newPaths, oldPaths []string) error {
   113  	oldMap := make(map[string]struct{}, len(oldPaths))
   114  	for _, p := range oldPaths {
   115  		oldMap[p] = struct{}{}
   116  	}
   117  
   118  	for _, sp := range newPaths {
   119  		if _, ok := oldMap[sp]; ok {
   120  			continue
   121  		}
   122  		// Core dumps (.core) are often too large, do not copy them.
   123  		// Minidumps (.dmp) are usually sufficient.
   124  		if strings.HasSuffix(sp, ".core") {
   125  			continue
   126  		}
   127  
   128  		if err := fsutil.CopyFile(sp, filepath.Join(dstDir, filepath.Base(sp))); err != nil {
   129  			logging.Infof(ctx, "%s: %v", sp, err)
   130  		}
   131  	}
   132  	return nil
   133  }
   134  
   135  // CopySystemInfo copies system information relevant to crash dumps (e.g. lsb-release) into dstDir.
   136  func CopySystemInfo(dstDir string) error {
   137  	return fsutil.CopyFile(lsbReleasePath, filepath.Join(dstDir, filepath.Base(lsbReleasePath)))
   138  }