Source file
src/runtime/vdso_test.go
1
2
3
4
5
6
7 package runtime_test
8
9 import (
10 "bytes"
11 "internal/asan"
12 "internal/goarch"
13 "internal/testenv"
14 "os"
15 "os/exec"
16 "path/filepath"
17 "syscall"
18 "testing"
19 "time"
20 )
21
22
23
24 func TestUsingVDSO(t *testing.T) {
25 if asan.Enabled {
26 t.Skip("test fails with ASAN because the ASAN leak checker won't run under strace")
27 }
28
29 const calls = 100
30
31 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
32
33 var total int64
34 for i := 0; i < calls; i++ {
35 total += time.Now().UnixNano()
36 }
37 os.Exit(0)
38 }
39
40 t.Parallel()
41
42
43
44 strace := "/bin/strace"
45 if _, err := os.Stat(strace); err != nil {
46 strace = "/usr/bin/strace"
47 if _, err := os.Stat(strace); err != nil {
48 t.Skipf("skipping test because strace not found: %v", err)
49 }
50 }
51
52 exe, err := os.Executable()
53 if err != nil {
54 t.Skipf("skipping because Executable failed: %v", err)
55 }
56
57 traceFilter := "clock_gettime"
58
59 if goarch.PtrSize == 4 {
60
61
62
63 traceFilter += ",clock_gettime64"
64 }
65
66 t.Logf("GO_WANT_HELPER_PROCESS=1 %s -f -e %s %s -test.run=^TestUsingVDSO$", strace, traceFilter, exe)
67 cmd := testenv.Command(t, strace, "-f", "-e", traceFilter, exe, "-test.run=^TestUsingVDSO$")
68 cmd = testenv.CleanCmdEnv(cmd)
69 cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
70 out, err := cmd.CombinedOutput()
71 if len(out) > 0 {
72 t.Logf("%s", out)
73 }
74 if err != nil {
75 if err, ok := err.(*exec.ExitError); ok && err.Sys().(syscall.WaitStatus).Signaled() {
76 if !bytes.Contains(out, []byte("+++ killed by")) {
77
78
79
80
81 t.Log(err)
82 testenv.SkipFlaky(t, 63734)
83 }
84 }
85 t.Fatal(err)
86 }
87
88 if got := bytes.Count(out, []byte("gettime")); got >= calls {
89 t.Logf("found %d gettime calls, want < %d", got, calls)
90
91
92 tempdir := t.TempDir()
93 cfn := filepath.Join(tempdir, "time.c")
94 cexe := filepath.Join(tempdir, "time")
95 if err := os.WriteFile(cfn, []byte(vdsoCProgram), 0o644); err != nil {
96 t.Fatal(err)
97 }
98 cc := os.Getenv("CC")
99 if cc == "" {
100 cc, err = exec.LookPath("gcc")
101 if err != nil {
102 cc, err = exec.LookPath("clang")
103 if err != nil {
104 t.Skip("can't verify VDSO status, no C compiler")
105 }
106 }
107 }
108
109 t.Logf("%s -o %s %s", cc, cexe, cfn)
110 cmd = testenv.Command(t, cc, "-o", cexe, cfn)
111 cmd = testenv.CleanCmdEnv(cmd)
112 out, err = cmd.CombinedOutput()
113 if len(out) > 0 {
114 t.Logf("%s", out)
115 }
116 if err != nil {
117 t.Skipf("can't verify VDSO status, C compiled failed: %v", err)
118 }
119
120 t.Logf("%s -f -e %s %s", strace, traceFilter, cexe)
121 cmd = testenv.Command(t, strace, "-f", "-e", traceFilter, cexe)
122 cmd = testenv.CleanCmdEnv(cmd)
123 out, err = cmd.CombinedOutput()
124 if len(out) > 0 {
125 t.Logf("%s", out)
126 }
127 if err != nil {
128 t.Skipf("can't verify VDSO status, C program failed: %v", err)
129 }
130
131 if cgot := bytes.Count(out, []byte("gettime")); cgot >= 100 {
132 t.Logf("found %d gettime calls, want < %d", cgot, 100)
133 t.Log("C program does not use VDSO either")
134 return
135 }
136
137
138
139 t.Errorf("did not use VDSO system call")
140 }
141 }
142
143 const vdsoCProgram = `
144 #include <stdio.h>
145 #include <time.h>
146
147 int main() {
148 int i;
149 time_t tot;
150 for (i = 0; i < 100; i++) {
151 struct timespec ts;
152 clock_gettime(CLOCK_MONOTONIC, &ts);
153 tot += ts.tv_nsec;
154 }
155 printf("%d\n", (int)(tot));
156 return 0;
157 }
158 `
159
View as plain text