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 }