github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/runtime/pprof/vminfo_darwin_test.go (about) 1 // Copyright 2023 The Go 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 //go:build !ios 6 7 package pprof 8 9 import ( 10 "bufio" 11 "bytes" 12 "fmt" 13 "internal/abi" 14 "internal/testenv" 15 "os" 16 "os/exec" 17 "strconv" 18 "strings" 19 "testing" 20 ) 21 22 func TestVMInfo(t *testing.T) { 23 var begin, end, offset uint64 24 var filename string 25 first := true 26 machVMInfo(func(lo, hi, off uint64, file, buildID string) { 27 if first { 28 begin = lo 29 end = hi 30 offset = off 31 filename = file 32 } 33 // May see multiple text segments if rosetta is used for running 34 // the go toolchain itself. 35 first = false 36 }) 37 lo, hi, err := useVMMapWithRetry(t) 38 if err != nil { 39 t.Fatal(err) 40 } 41 if got, want := begin, lo; got != want { 42 t.Errorf("got %x, want %x", got, want) 43 } 44 if got, want := end, hi; got != want { 45 t.Errorf("got %x, want %x", got, want) 46 } 47 if got, want := offset, uint64(0); got != want { 48 t.Errorf("got %x, want %x", got, want) 49 } 50 if !strings.HasSuffix(filename, "pprof.test") { 51 t.Errorf("got %s, want pprof.test", filename) 52 } 53 addr := uint64(abi.FuncPCABIInternal(TestVMInfo)) 54 if addr < lo || addr > hi { 55 t.Errorf("%x..%x does not contain function %p (%x)", lo, hi, TestVMInfo, addr) 56 } 57 } 58 59 func useVMMapWithRetry(t *testing.T) (hi, lo uint64, err error) { 60 var retryable bool 61 for { 62 hi, lo, retryable, err = useVMMap(t) 63 if err == nil { 64 return hi, lo, nil 65 } 66 if !retryable { 67 return 0, 0, err 68 } 69 t.Logf("retrying vmmap after error: %v", err) 70 } 71 } 72 73 func useVMMap(t *testing.T) (hi, lo uint64, retryable bool, err error) { 74 pid := strconv.Itoa(os.Getpid()) 75 testenv.MustHaveExecPath(t, "vmmap") 76 cmd := testenv.Command(t, "vmmap", pid) 77 out, cmdErr := cmd.Output() 78 if cmdErr != nil { 79 t.Logf("vmmap output: %s", out) 80 if ee, ok := cmdErr.(*exec.ExitError); ok && len(ee.Stderr) > 0 { 81 t.Logf("%v: %v\n%s", cmd, cmdErr, ee.Stderr) 82 retryable = bytes.Contains(ee.Stderr, []byte("resource shortage")) 83 } 84 t.Logf("%v: %v\n", cmd, cmdErr) 85 if retryable { 86 return 0, 0, true, cmdErr 87 } 88 } 89 // Always parse the output of vmmap since it may return an error 90 // code even if it successfully reports the text segment information 91 // required for this test. 92 hi, lo, err = parseVmmap(out) 93 if err != nil { 94 if cmdErr != nil { 95 return 0, 0, false, fmt.Errorf("failed to parse vmmap output, vmmap reported an error: %v", err) 96 } 97 t.Logf("vmmap output: %s", out) 98 return 0, 0, false, fmt.Errorf("failed to parse vmmap output, vmmap did not report an error: %v", err) 99 } 100 return hi, lo, false, nil 101 } 102 103 // parseVmmap parses the output of vmmap and calls addMapping for the first r-x TEXT segment in the output. 104 func parseVmmap(data []byte) (hi, lo uint64, err error) { 105 // vmmap 53799 106 // Process: gopls [53799] 107 // Path: /Users/USER/*/gopls 108 // Load Address: 0x1029a0000 109 // Identifier: gopls 110 // Version: ??? 111 // Code Type: ARM64 112 // Platform: macOS 113 // Parent Process: Code Helper (Plugin) [53753] 114 // 115 // Date/Time: 2023-05-25 09:45:49.331 -0700 116 // Launch Time: 2023-05-23 09:35:37.514 -0700 117 // OS Version: macOS 13.3.1 (22E261) 118 // Report Version: 7 119 // Analysis Tool: /Applications/Xcode.app/Contents/Developer/usr/bin/vmmap 120 // Analysis Tool Version: Xcode 14.3 (14E222b) 121 // 122 // Physical footprint: 1.2G 123 // Physical footprint (peak): 1.2G 124 // Idle exit: untracked 125 // ---- 126 // 127 // Virtual Memory Map of process 53799 (gopls) 128 // Output report format: 2.4 -64-bit process 129 // VM page size: 16384 bytes 130 // 131 // ==== Non-writable regions for process 53799 132 // REGION TYPE START END [ VSIZE RSDNT DIRTY SWAP] PRT/MAX SHRMOD PURGE REGION DETAIL 133 // __TEXT 1029a0000-1033bc000 [ 10.1M 7360K 0K 0K] r-x/rwx SM=COW /Users/USER/*/gopls 134 // __DATA_CONST 1033bc000-1035bc000 [ 2048K 2000K 0K 0K] r--/rwSM=COW /Users/USER/*/gopls 135 // __DATA_CONST 1035bc000-103a48000 [ 4656K 3824K 0K 0K] r--/rwSM=COW /Users/USER/*/gopls 136 // __LINKEDIT 103b00000-103c98000 [ 1632K 1616K 0K 0K] r--/r-SM=COW /Users/USER/*/gopls 137 // dyld private memory 103cd8000-103cdc000 [ 16K 0K 0K 0K] ---/--SM=NUL 138 // shared memory 103ce4000-103ce8000 [ 16K 16K 16K 0K] r--/r-SM=SHM 139 // MALLOC metadata 103ce8000-103cec000 [ 16K 16K 16K 0K] r--/rwx SM=COW DefaultMallocZone_0x103ce8000 zone structure 140 // MALLOC guard page 103cf0000-103cf4000 [ 16K 0K 0K 0K] ---/rwx SM=COW 141 // MALLOC guard page 103cfc000-103d00000 [ 16K 0K 0K 0K] ---/rwx SM=COW 142 // MALLOC guard page 103d00000-103d04000 [ 16K 0K 0K 0K] ---/rwx SM=NUL 143 144 banner := "==== Non-writable regions for process" 145 grabbing := false 146 sc := bufio.NewScanner(bytes.NewReader(data)) 147 for sc.Scan() { 148 l := sc.Text() 149 if grabbing { 150 p := strings.Fields(l) 151 if len(p) > 7 && p[0] == "__TEXT" && p[7] == "r-x/rwx" { 152 locs := strings.Split(p[1], "-") 153 start, _ := strconv.ParseUint(locs[0], 16, 64) 154 end, _ := strconv.ParseUint(locs[1], 16, 64) 155 return start, end, nil 156 } 157 } 158 if strings.HasPrefix(l, banner) { 159 grabbing = true 160 } 161 } 162 return 0, 0, fmt.Errorf("vmmap no text segment found") 163 }