github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/integration/testcmd/common/common.go (about)

     1  // Copyright 2021 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package common
     6  
     7  import (
     8  	"archive/tar"
     9  	"fmt"
    10  	"log"
    11  	"os"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"github.com/mvdan/u-root-coreutils/pkg/mount"
    16  	"github.com/mvdan/u-root-coreutils/pkg/tarutil"
    17  	"golang.org/x/sys/unix"
    18  )
    19  
    20  const (
    21  	envUse9P            = "UROOT_USE_9P"
    22  	envNoKernelCoverage = "UROOT_NO_KERNEL_COVERAGE"
    23  
    24  	sharedDir          = "/testdata"
    25  	kernelCoverageFile = "/testdata/kernel_coverage.tar"
    26  
    27  	// https://wiki.qemu.org/Documentation/9psetup#msize recommends an
    28  	// msize of at least 10MiB. Larger number might give better
    29  	// performance. QEMU will print a warning if it is too small. Linux's
    30  	// default is 8KiB which is way too small.
    31  	msize9P = 10 * 1024 * 1024
    32  )
    33  
    34  // gcovFilter filters on all files ending with a gcda or gcno extension.
    35  func gcovFilter(hdr *tar.Header) bool {
    36  	if hdr.Typeflag == tar.TypeDir {
    37  		hdr.Mode = 0o770
    38  		return true
    39  	}
    40  	if (filepath.Ext(hdr.Name) == ".gcda" && hdr.Typeflag == tar.TypeReg) ||
    41  		(filepath.Ext(hdr.Name) == ".gcno" && hdr.Typeflag == tar.TypeSymlink) {
    42  		hdr.Mode = 0o660
    43  		return true
    44  	}
    45  	return false
    46  }
    47  
    48  // CollectKernelCoverage saves the kernel coverage report to a tar file.
    49  func CollectKernelCoverage() {
    50  	if err := collectKernelCoverage(kernelCoverageFile); err != nil {
    51  		log.Printf("Failed to collect kernel coverage: %v", err)
    52  	}
    53  }
    54  
    55  func collectKernelCoverage(filename string) error {
    56  	// Check if we are collecting kernel coverage.
    57  	if os.Getenv(envNoKernelCoverage) == "1" {
    58  		log.Print("Not collecting kernel coverage")
    59  		return nil
    60  	}
    61  	gcovDir := "/sys/kernel/debug/gcov"
    62  	if _, err := os.Stat(gcovDir); os.IsNotExist(err) {
    63  		log.Printf("Not collecting kernel coverage because %q does not exist", gcovDir)
    64  		return nil
    65  	}
    66  	if os.Getenv(envUse9P) != "1" {
    67  		// 9p is required to rescue the file from the VM.
    68  		return fmt.Errorf("not collecting kernel coverage because filesystem is not 9p")
    69  	}
    70  
    71  	// Mount debugfs.
    72  	dfs := "/sys/kernel/debug"
    73  	if err := os.MkdirAll(dfs, 0o666); err != nil {
    74  		return fmt.Errorf("os.MkdirAll(%v): %v != nil", dfs, err)
    75  	}
    76  	if err := unix.Mount("debugfs", dfs, "debugfs", 0, ""); err != nil {
    77  		return fmt.Errorf("failed to mount debugfs: %v", err)
    78  	}
    79  
    80  	// Copy out the kernel code coverage.
    81  	log.Print("Collecting kernel coverage...")
    82  	f, err := os.Create(filename)
    83  	if err != nil {
    84  		return err
    85  	}
    86  	if err := tarutil.CreateTar(f, []string{strings.TrimLeft(gcovDir, "/")}, &tarutil.Opts{
    87  		Filters: []tarutil.Filter{gcovFilter},
    88  		// Make sure the files are not stored absolute; otherwise, they
    89  		// become difficult to extract safely.
    90  		ChangeDirectory: "/",
    91  	}); err != nil {
    92  		f.Close()
    93  		return err
    94  	}
    95  	// Sync to "disk" because we are about to shut down the kernel.
    96  	if err := f.Sync(); err != nil {
    97  		f.Close()
    98  		return fmt.Errorf("error syncing: %v", err)
    99  	}
   100  	if err := f.Close(); err != nil {
   101  		return fmt.Errorf("error closing: %v", err)
   102  	}
   103  	return nil
   104  }
   105  
   106  // MountSharedDir mounts the directory shared with the VM test. A cleanup
   107  // function is returned to unmount.
   108  func MountSharedDir() (func(), error) {
   109  	// Mount a disk and run the tests within.
   110  	var (
   111  		mp  *mount.MountPoint
   112  		err error
   113  	)
   114  
   115  	if err := os.MkdirAll(sharedDir, 0o644); err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	if os.Getenv(envUse9P) == "1" {
   120  		mp, err = mount.Mount("tmpdir", sharedDir, "9p", fmt.Sprintf("9P2000.L,msize=%d", msize9P), 0)
   121  	} else {
   122  		mp, err = mount.Mount("/dev/sda1", sharedDir, "vfat", "", unix.MS_RDONLY)
   123  	}
   124  	if err != nil {
   125  		return nil, fmt.Errorf("failed to mount test directory: %v", err)
   126  	}
   127  	return func() { mp.Unmount(0) }, nil
   128  }