github.com/cilium/ebpf@v0.16.0/cmd/bpf2go/main_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strings" 11 "testing" 12 13 "github.com/cilium/ebpf/cmd/bpf2go/gen" 14 "github.com/cilium/ebpf/cmd/bpf2go/internal" 15 "github.com/cilium/ebpf/internal/testutils" 16 "github.com/go-quicktest/qt" 17 ) 18 19 const minimalSocketFilter = `__attribute__((section("socket"), used)) int main() { return 0; }` 20 21 func TestRun(t *testing.T) { 22 clangBin := testutils.ClangBin(t) 23 dir := t.TempDir() 24 mustWriteFile(t, dir, "test.c", minimalSocketFilter) 25 26 modRoot, err := filepath.Abs("../..") 27 qt.Assert(t, qt.IsNil(err)) 28 29 if _, err := os.Stat(filepath.Join(modRoot, "go.mod")); os.IsNotExist(err) { 30 t.Fatal("No go.mod file in", modRoot) 31 } 32 33 modDir := t.TempDir() 34 execInModule := func(name string, args ...string) { 35 t.Helper() 36 37 cmd := exec.Command(name, args...) 38 cmd.Dir = modDir 39 if out, err := cmd.CombinedOutput(); err != nil { 40 if out := string(out); out != "" { 41 t.Log(out) 42 } 43 t.Fatalf("Can't execute %s: %v", name, args) 44 } 45 } 46 47 module := internal.CurrentModule 48 49 execInModule("go", "mod", "init", "bpf2go-test") 50 51 execInModule("go", "mod", "edit", 52 // Require the module. The version doesn't matter due to the replace 53 // below. 54 fmt.Sprintf("-require=%s@v0.0.0", module), 55 // Replace the module with the current version. 56 fmt.Sprintf("-replace=%s=%s", module, modRoot), 57 ) 58 59 goarches := []string{ 60 "amd64", // little-endian 61 "arm64", 62 "s390x", // big-endian 63 } 64 65 err = run(io.Discard, []string{ 66 "-go-package", "main", 67 "-output-dir", modDir, 68 "-cc", clangBin, 69 "-target", strings.Join(goarches, ","), 70 "bar", 71 filepath.Join(dir, "test.c"), 72 }) 73 74 if err != nil { 75 t.Fatal("Can't run:", err) 76 } 77 78 mustWriteFile(t, modDir, "main.go", 79 ` 80 package main 81 82 func main() { 83 var obj barObjects 84 println(obj.Main) 85 }`) 86 87 for _, arch := range goarches { 88 t.Run(arch, func(t *testing.T) { 89 goBuild := exec.Command("go", "build", "-mod=mod", "-o", "/dev/null") 90 goBuild.Dir = modDir 91 goBuild.Env = append(os.Environ(), 92 "GOOS=linux", 93 "GOARCH="+arch, 94 "GOPROXY=off", 95 "GOSUMDB=off", 96 ) 97 out, err := goBuild.CombinedOutput() 98 if err != nil { 99 if out := string(out); out != "" { 100 t.Log(out) 101 } 102 t.Error("Can't compile package:", err) 103 } 104 }) 105 } 106 } 107 108 func TestHelp(t *testing.T) { 109 var stdout bytes.Buffer 110 err := run(&stdout, []string{"-help"}) 111 if err != nil { 112 t.Fatal("Can't execute -help") 113 } 114 115 if stdout.Len() == 0 { 116 t.Error("-help doesn't write to stdout") 117 } 118 } 119 120 func TestErrorMentionsEnvVar(t *testing.T) { 121 err := run(io.Discard, nil) 122 qt.Assert(t, qt.StringContains(err.Error(), gopackageEnv), qt.Commentf("Error should include name of environment variable")) 123 } 124 125 func TestDisableStripping(t *testing.T) { 126 dir := t.TempDir() 127 mustWriteFile(t, dir, "test.c", minimalSocketFilter) 128 129 err := run(io.Discard, []string{ 130 "-go-package", "foo", 131 "-output-dir", dir, 132 "-cc", testutils.ClangBin(t), 133 "-strip", "binary-that-certainly-doesnt-exist", 134 "-no-strip", 135 "bar", 136 filepath.Join(dir, "test.c"), 137 }) 138 139 if err != nil { 140 t.Fatal("Can't run with stripping disabled:", err) 141 } 142 } 143 144 func TestConvertGOARCH(t *testing.T) { 145 tmp := t.TempDir() 146 mustWriteFile(t, tmp, "test.c", 147 ` 148 #ifndef __TARGET_ARCH_x86 149 #error __TARGET_ARCH_x86 is not defined 150 #endif`, 151 ) 152 153 b2g := bpf2go{ 154 pkg: "test", 155 stdout: io.Discard, 156 identStem: "test", 157 cc: testutils.ClangBin(t), 158 disableStripping: true, 159 sourceFile: tmp + "/test.c", 160 outputDir: tmp, 161 } 162 163 if err := b2g.convert(gen.TargetsByGoArch()["amd64"], nil); err != nil { 164 t.Fatal("Can't target GOARCH:", err) 165 } 166 } 167 168 func TestCTypes(t *testing.T) { 169 var ct cTypes 170 valid := []string{ 171 "abcdefghijklmnopqrstuvqxyABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_", 172 "y", 173 } 174 for _, value := range valid { 175 if err := ct.Set(value); err != nil { 176 t.Fatalf("Set returned an error for %q: %s", value, err) 177 } 178 } 179 qt.Assert(t, qt.ContentEquals(ct, valid)) 180 181 for _, value := range []string{ 182 "", 183 " ", 184 " frood", 185 "foo\nbar", 186 ".", 187 ",", 188 "+", 189 "-", 190 } { 191 ct = nil 192 if err := ct.Set(value); err == nil { 193 t.Fatalf("Set did not return an error for %q", value) 194 } 195 } 196 197 ct = nil 198 qt.Assert(t, qt.IsNil(ct.Set("foo"))) 199 qt.Assert(t, qt.IsNotNil(ct.Set("foo"))) 200 } 201 202 func TestParseArgs(t *testing.T) { 203 const ( 204 pkg = "eee" 205 outputDir = "." 206 csource = "testdata/minimal.c" 207 stem = "a" 208 ) 209 t.Run("makebase", func(t *testing.T) { 210 t.Setenv(gopackageEnv, pkg) 211 basePath, _ := filepath.Abs("barfoo") 212 args := []string{"-makebase", basePath, stem, csource} 213 b2g, err := newB2G(&bytes.Buffer{}, args) 214 qt.Assert(t, qt.IsNil(err)) 215 qt.Assert(t, qt.Equals(b2g.makeBase, basePath)) 216 }) 217 218 t.Run("makebase from env", func(t *testing.T) { 219 t.Setenv(gopackageEnv, pkg) 220 basePath, _ := filepath.Abs("barfoo") 221 args := []string{stem, csource} 222 t.Setenv("BPF2GO_MAKEBASE", basePath) 223 b2g, err := newB2G(&bytes.Buffer{}, args) 224 qt.Assert(t, qt.IsNil(err)) 225 qt.Assert(t, qt.Equals(b2g.makeBase, basePath)) 226 }) 227 228 t.Run("makebase flag overrides env", func(t *testing.T) { 229 t.Setenv(gopackageEnv, pkg) 230 basePathFlag, _ := filepath.Abs("barfoo") 231 basePathEnv, _ := filepath.Abs("foobar") 232 args := []string{"-makebase", basePathFlag, stem, csource} 233 t.Setenv("BPF2GO_MAKEBASE", basePathEnv) 234 b2g, err := newB2G(&bytes.Buffer{}, args) 235 qt.Assert(t, qt.IsNil(err)) 236 qt.Assert(t, qt.Equals(b2g.makeBase, basePathFlag)) 237 }) 238 239 t.Run("cc defaults to clang", func(t *testing.T) { 240 t.Setenv(gopackageEnv, pkg) 241 args := []string{stem, csource} 242 b2g, err := newB2G(&bytes.Buffer{}, args) 243 qt.Assert(t, qt.IsNil(err)) 244 qt.Assert(t, qt.Equals(b2g.cc, "clang")) 245 }) 246 247 t.Run("cc", func(t *testing.T) { 248 t.Setenv(gopackageEnv, pkg) 249 args := []string{"-cc", "barfoo", stem, csource} 250 b2g, err := newB2G(&bytes.Buffer{}, args) 251 qt.Assert(t, qt.IsNil(err)) 252 qt.Assert(t, qt.Equals(b2g.cc, "barfoo")) 253 }) 254 255 t.Run("cc from env", func(t *testing.T) { 256 t.Setenv(gopackageEnv, pkg) 257 args := []string{stem, csource} 258 t.Setenv("BPF2GO_CC", "barfoo") 259 b2g, err := newB2G(&bytes.Buffer{}, args) 260 qt.Assert(t, qt.IsNil(err)) 261 qt.Assert(t, qt.Equals(b2g.cc, "barfoo")) 262 }) 263 264 t.Run("cc flag overrides env", func(t *testing.T) { 265 t.Setenv(gopackageEnv, pkg) 266 args := []string{"-cc", "barfoo", stem, csource} 267 t.Setenv("BPF2GO_CC", "foobar") 268 b2g, err := newB2G(&bytes.Buffer{}, args) 269 qt.Assert(t, qt.IsNil(err)) 270 qt.Assert(t, qt.Equals(b2g.cc, "barfoo")) 271 }) 272 273 t.Run("strip defaults to llvm-strip", func(t *testing.T) { 274 t.Setenv(gopackageEnv, pkg) 275 args := []string{stem, csource} 276 b2g, err := newB2G(&bytes.Buffer{}, args) 277 qt.Assert(t, qt.IsNil(err)) 278 qt.Assert(t, qt.Equals(b2g.strip, "llvm-strip")) 279 }) 280 281 t.Run("strip", func(t *testing.T) { 282 t.Setenv(gopackageEnv, pkg) 283 args := []string{"-strip", "barfoo", stem, csource} 284 b2g, err := newB2G(&bytes.Buffer{}, args) 285 qt.Assert(t, qt.IsNil(err)) 286 qt.Assert(t, qt.Equals(b2g.strip, "barfoo")) 287 }) 288 289 t.Run("strip from env", func(t *testing.T) { 290 t.Setenv(gopackageEnv, pkg) 291 args := []string{stem, csource} 292 t.Setenv("BPF2GO_STRIP", "barfoo") 293 b2g, err := newB2G(&bytes.Buffer{}, args) 294 qt.Assert(t, qt.IsNil(err)) 295 qt.Assert(t, qt.Equals(b2g.strip, "barfoo")) 296 }) 297 298 t.Run("strip flag overrides env", func(t *testing.T) { 299 t.Setenv(gopackageEnv, pkg) 300 args := []string{"-strip", "barfoo", stem, csource} 301 t.Setenv("BPF2GO_STRIP", "foobar") 302 b2g, err := newB2G(&bytes.Buffer{}, args) 303 qt.Assert(t, qt.IsNil(err)) 304 qt.Assert(t, qt.Equals(b2g.strip, "barfoo")) 305 }) 306 307 t.Run("no strip defaults to false", func(t *testing.T) { 308 t.Setenv(gopackageEnv, pkg) 309 args := []string{stem, csource} 310 b2g, err := newB2G(&bytes.Buffer{}, args) 311 qt.Assert(t, qt.IsNil(err)) 312 qt.Assert(t, qt.IsFalse(b2g.disableStripping)) 313 }) 314 315 t.Run("no strip", func(t *testing.T) { 316 t.Setenv(gopackageEnv, pkg) 317 args := []string{"-no-strip", stem, csource} 318 b2g, err := newB2G(&bytes.Buffer{}, args) 319 qt.Assert(t, qt.IsNil(err)) 320 qt.Assert(t, qt.IsTrue(b2g.disableStripping)) 321 }) 322 323 t.Run("cflags flag", func(t *testing.T) { 324 t.Setenv(gopackageEnv, pkg) 325 args := []string{"-cflags", "x y z", stem, csource} 326 b2g, err := newB2G(&bytes.Buffer{}, args) 327 qt.Assert(t, qt.IsNil(err)) 328 qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"x", "y", "z"})) 329 }) 330 331 t.Run("cflags multi flag", func(t *testing.T) { 332 t.Setenv(gopackageEnv, pkg) 333 args := []string{"-cflags", "x y z", "-cflags", "u v", stem, csource} 334 b2g, err := newB2G(&bytes.Buffer{}, args) 335 qt.Assert(t, qt.IsNil(err)) 336 qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"u", "v"})) 337 }) 338 339 t.Run("cflags flag and args", func(t *testing.T) { 340 t.Setenv(gopackageEnv, pkg) 341 args := []string{"-cflags", "x y z", "stem", csource, "--", "u", "v"} 342 b2g, err := newB2G(&bytes.Buffer{}, args) 343 qt.Assert(t, qt.IsNil(err)) 344 qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"x", "y", "z", "u", "v"})) 345 }) 346 347 t.Run("cflags from env", func(t *testing.T) { 348 t.Setenv(gopackageEnv, pkg) 349 args := []string{stem, csource} 350 t.Setenv("BPF2GO_CFLAGS", "x y z") 351 b2g, err := newB2G(&bytes.Buffer{}, args) 352 qt.Assert(t, qt.IsNil(err)) 353 qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"x", "y", "z"})) 354 }) 355 356 t.Run("cflags flag overrides env", func(t *testing.T) { 357 t.Setenv(gopackageEnv, pkg) 358 args := []string{"-cflags", "u v", stem, csource} 359 t.Setenv("BPF2GO_CFLAGS", "x y z") 360 b2g, err := newB2G(&bytes.Buffer{}, args) 361 qt.Assert(t, qt.IsNil(err)) 362 qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"u", "v"})) 363 }) 364 365 t.Run("go package overrides env", func(t *testing.T) { 366 t.Setenv(gopackageEnv, pkg) 367 args := []string{"-go-package", "aaa", stem, csource} 368 b2g, err := newB2G(&bytes.Buffer{}, args) 369 qt.Assert(t, qt.IsNil(err)) 370 qt.Assert(t, qt.Equals(b2g.pkg, "aaa")) 371 }) 372 373 t.Run("output dir", func(t *testing.T) { 374 t.Setenv(gopackageEnv, pkg) 375 args := []string{"-output-dir", outputDir, stem, csource} 376 b2g, err := newB2G(&bytes.Buffer{}, args) 377 qt.Assert(t, qt.IsNil(err)) 378 qt.Assert(t, qt.Equals(b2g.outputDir, outputDir)) 379 }) 380 } 381 382 func mustWriteFile(tb testing.TB, dir, name, contents string) { 383 tb.Helper() 384 tmpFile := filepath.Join(dir, name) 385 if err := os.WriteFile(tmpFile, []byte(contents), 0660); err != nil { 386 tb.Fatal(err) 387 } 388 }