all_tests.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /* Copyright (c) 2015, Google Inc.
  2. *
  3. * Permission to use, copy, modify, and/or distribute this software for any
  4. * purpose with or without fee is hereby granted, provided that the above
  5. * copyright notice and this permission notice appear in all copies.
  6. *
  7. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  10. * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  12. * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  13. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
  14. package main
  15. import (
  16. "bufio"
  17. "bytes"
  18. "encoding/json"
  19. "errors"
  20. "flag"
  21. "fmt"
  22. "math/rand"
  23. "os"
  24. "os/exec"
  25. "path"
  26. "runtime"
  27. "strconv"
  28. "strings"
  29. "sync"
  30. "syscall"
  31. "boringssl.googlesource.com/boringssl/util/testresult"
  32. )
  33. // TODO(davidben): Link tests with the malloc shim and port -malloc-test to this runner.
  34. var (
  35. useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
  36. useCallgrind = flag.Bool("callgrind", false, "If true, run code under valgrind to generate callgrind traces.")
  37. useGDB = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb")
  38. useSDE = flag.Bool("sde", false, "If true, run BoringSSL code under Intel's SDE for each supported chip")
  39. sdePath = flag.String("sde-path", "sde", "The path to find the sde binary.")
  40. buildDir = flag.String("build-dir", "build", "The build directory to run the tests from.")
  41. numWorkers = flag.Int("num-workers", runtime.NumCPU(), "Runs the given number of workers when testing.")
  42. jsonOutput = flag.String("json-output", "", "The file to output JSON results to.")
  43. mallocTest = flag.Int64("malloc-test", -1, "If non-negative, run each test with each malloc in turn failing from the given number onwards.")
  44. mallocTestDebug = flag.Bool("malloc-test-debug", false, "If true, ask each test to abort rather than fail a malloc. This can be used with a specific value for --malloc-test to identity the malloc failing that is causing problems.")
  45. simulateARMCPUs = flag.Bool("simulate-arm-cpus", simulateARMCPUsDefault(), "If true, runs tests simulating different ARM CPUs.")
  46. )
  47. func simulateARMCPUsDefault() bool {
  48. return runtime.GOOS == "linux" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64")
  49. }
  50. type test struct {
  51. args []string
  52. shard, numShards int
  53. // cpu, if not empty, contains a code to simulate. For SDE, run `sde64
  54. // -help` to get a list of these codes. For ARM, see gtest_main.cc for
  55. // the supported values.
  56. cpu string
  57. }
  58. type result struct {
  59. Test test
  60. Passed bool
  61. Error error
  62. }
  63. // sdeCPUs contains a list of CPU code that we run all tests under when *useSDE
  64. // is true.
  65. var sdeCPUs = []string{
  66. "p4p", // Pentium4 Prescott
  67. "mrm", // Merom
  68. "pnr", // Penryn
  69. "nhm", // Nehalem
  70. "wsm", // Westmere
  71. "snb", // Sandy Bridge
  72. "ivb", // Ivy Bridge
  73. "hsw", // Haswell
  74. "bdw", // Broadwell
  75. "skx", // Skylake Server
  76. "skl", // Skylake Client
  77. "cnl", // Cannonlake
  78. "knl", // Knights Landing
  79. "slt", // Saltwell
  80. "slm", // Silvermont
  81. "glm", // Goldmont
  82. "knm", // Knights Mill
  83. }
  84. var armCPUs = []string{
  85. "none", // No support for any ARM extensions.
  86. "neon", // Support for NEON.
  87. "crypto", // Support for NEON and crypto extensions.
  88. }
  89. func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
  90. valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full", "--quiet"}
  91. if dbAttach {
  92. valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
  93. }
  94. valgrindArgs = append(valgrindArgs, path)
  95. valgrindArgs = append(valgrindArgs, args...)
  96. return exec.Command("valgrind", valgrindArgs...)
  97. }
  98. func callgrindOf(path string, args ...string) *exec.Cmd {
  99. valgrindArgs := []string{"-q", "--tool=callgrind", "--dump-instr=yes", "--collect-jumps=yes", "--callgrind-out-file=" + *buildDir + "/callgrind/callgrind.out.%p"}
  100. valgrindArgs = append(valgrindArgs, path)
  101. valgrindArgs = append(valgrindArgs, args...)
  102. return exec.Command("valgrind", valgrindArgs...)
  103. }
  104. func gdbOf(path string, args ...string) *exec.Cmd {
  105. xtermArgs := []string{"-e", "gdb", "--args"}
  106. xtermArgs = append(xtermArgs, path)
  107. xtermArgs = append(xtermArgs, args...)
  108. return exec.Command("xterm", xtermArgs...)
  109. }
  110. func sdeOf(cpu, path string, args ...string) *exec.Cmd {
  111. sdeArgs := []string{"-" + cpu}
  112. // The kernel's vdso code for gettimeofday sometimes uses the RDTSCP
  113. // instruction. Although SDE has a -chip_check_vsyscall flag that
  114. // excludes such code by default, it does not seem to work. Instead,
  115. // pass the -chip_check_exe_only flag which retains test coverage when
  116. // statically linked and excludes the vdso.
  117. if cpu == "p4p" || cpu == "pnr" || cpu == "mrm" || cpu == "slt" {
  118. sdeArgs = append(sdeArgs, "-chip_check_exe_only")
  119. }
  120. sdeArgs = append(sdeArgs, "--", path)
  121. sdeArgs = append(sdeArgs, args...)
  122. return exec.Command(*sdePath, sdeArgs...)
  123. }
  124. var (
  125. errMoreMallocs = errors.New("child process did not exhaust all allocation calls")
  126. errTestSkipped = errors.New("test was skipped")
  127. )
  128. func runTestOnce(test test, mallocNumToFail int64) (passed bool, err error) {
  129. prog := path.Join(*buildDir, test.args[0])
  130. args := append([]string{}, test.args[1:]...)
  131. if *simulateARMCPUs && test.cpu != "" {
  132. args = append(args, "--cpu=" + test.cpu)
  133. }
  134. if *useSDE {
  135. // SDE is neither compatible with the unwind tester nor automatically
  136. // detected.
  137. args = append(args, "--no_unwind_tests")
  138. }
  139. var cmd *exec.Cmd
  140. if *useValgrind {
  141. cmd = valgrindOf(false, prog, args...)
  142. } else if *useCallgrind {
  143. cmd = callgrindOf(prog, args...)
  144. } else if *useGDB {
  145. cmd = gdbOf(prog, args...)
  146. } else if *useSDE {
  147. cmd = sdeOf(test.cpu, prog, args...)
  148. } else {
  149. cmd = exec.Command(prog, args...)
  150. }
  151. var outBuf bytes.Buffer
  152. cmd.Stdout = &outBuf
  153. cmd.Stderr = &outBuf
  154. if mallocNumToFail >= 0 {
  155. cmd.Env = os.Environ()
  156. cmd.Env = append(cmd.Env, "MALLOC_NUMBER_TO_FAIL="+strconv.FormatInt(mallocNumToFail, 10))
  157. if *mallocTestDebug {
  158. cmd.Env = append(cmd.Env, "MALLOC_ABORT_ON_FAIL=1")
  159. }
  160. cmd.Env = append(cmd.Env, "_MALLOC_CHECK=1")
  161. }
  162. if err := cmd.Start(); err != nil {
  163. return false, err
  164. }
  165. if err := cmd.Wait(); err != nil {
  166. if exitError, ok := err.(*exec.ExitError); ok {
  167. switch exitError.Sys().(syscall.WaitStatus).ExitStatus() {
  168. case 88:
  169. return false, errMoreMallocs
  170. case 89:
  171. fmt.Print(string(outBuf.Bytes()))
  172. return false, errTestSkipped
  173. }
  174. }
  175. fmt.Print(string(outBuf.Bytes()))
  176. return false, err
  177. }
  178. // Account for Windows line-endings.
  179. stdout := bytes.Replace(outBuf.Bytes(), []byte("\r\n"), []byte("\n"), -1)
  180. if bytes.HasSuffix(stdout, []byte("PASS\n")) &&
  181. (len(stdout) == 5 || stdout[len(stdout)-6] == '\n') {
  182. return true, nil
  183. }
  184. // Also accept a googletest-style pass line. This is left here in
  185. // transition until the tests are all converted and this script made
  186. // unnecessary.
  187. if bytes.Contains(stdout, []byte("\n[ PASSED ]")) {
  188. return true, nil
  189. }
  190. fmt.Print(string(outBuf.Bytes()))
  191. return false, nil
  192. }
  193. func runTest(test test) (bool, error) {
  194. if *mallocTest < 0 {
  195. return runTestOnce(test, -1)
  196. }
  197. for mallocNumToFail := int64(*mallocTest); ; mallocNumToFail++ {
  198. if passed, err := runTestOnce(test, mallocNumToFail); err != errMoreMallocs {
  199. if err != nil {
  200. err = fmt.Errorf("at malloc %d: %s", mallocNumToFail, err)
  201. }
  202. return passed, err
  203. }
  204. }
  205. }
  206. // setWorkingDirectory walks up directories as needed until the current working
  207. // directory is the top of a BoringSSL checkout.
  208. func setWorkingDirectory() {
  209. for i := 0; i < 64; i++ {
  210. if _, err := os.Stat("BUILDING.md"); err == nil {
  211. return
  212. }
  213. os.Chdir("..")
  214. }
  215. panic("Couldn't find BUILDING.md in a parent directory!")
  216. }
  217. func parseTestConfig(filename string) ([]test, error) {
  218. in, err := os.Open(filename)
  219. if err != nil {
  220. return nil, err
  221. }
  222. defer in.Close()
  223. decoder := json.NewDecoder(in)
  224. var testArgs [][]string
  225. if err := decoder.Decode(&testArgs); err != nil {
  226. return nil, err
  227. }
  228. var result []test
  229. for _, args := range testArgs {
  230. result = append(result, test{args: args})
  231. }
  232. return result, nil
  233. }
  234. func worker(tests <-chan test, results chan<- result, done *sync.WaitGroup) {
  235. defer done.Done()
  236. for test := range tests {
  237. passed, err := runTest(test)
  238. results <- result{test, passed, err}
  239. }
  240. }
  241. func (t test) shortName() string {
  242. return t.args[0] + t.shardMsg() + t.cpuMsg()
  243. }
  244. func (t test) longName() string {
  245. return strings.Join(t.args, " ") + t.cpuMsg()
  246. }
  247. func (t test) shardMsg() string {
  248. if t.numShards == 0 {
  249. return ""
  250. }
  251. return fmt.Sprintf(" [shard %d/%d]", t.shard+1, t.numShards)
  252. }
  253. func (t test) cpuMsg() string {
  254. if len(t.cpu) == 0 {
  255. return ""
  256. }
  257. return fmt.Sprintf(" (for CPU %q)", t.cpu)
  258. }
  259. func (t test) getGTestShards() ([]test, error) {
  260. if *numWorkers == 1 || len(t.args) != 1 {
  261. return []test{t}, nil
  262. }
  263. // Only shard the three GTest-based tests.
  264. if t.args[0] != "crypto/crypto_test" && t.args[0] != "ssl/ssl_test" && t.args[0] != "decrepit/decrepit_test" {
  265. return []test{t}, nil
  266. }
  267. prog := path.Join(*buildDir, t.args[0])
  268. cmd := exec.Command(prog, "--gtest_list_tests")
  269. var stdout bytes.Buffer
  270. cmd.Stdout = &stdout
  271. if err := cmd.Start(); err != nil {
  272. return nil, err
  273. }
  274. if err := cmd.Wait(); err != nil {
  275. return nil, err
  276. }
  277. var group string
  278. var tests []string
  279. scanner := bufio.NewScanner(&stdout)
  280. for scanner.Scan() {
  281. line := scanner.Text()
  282. // Remove the parameter comment and trailing space.
  283. if idx := strings.Index(line, "#"); idx >= 0 {
  284. line = line[:idx]
  285. }
  286. line = strings.TrimSpace(line)
  287. if len(line) == 0 {
  288. continue
  289. }
  290. if line[len(line)-1] == '.' {
  291. group = line
  292. continue
  293. }
  294. if len(group) == 0 {
  295. return nil, fmt.Errorf("found test case %q without group", line)
  296. }
  297. tests = append(tests, group+line)
  298. }
  299. const testsPerShard = 20
  300. if len(tests) <= testsPerShard {
  301. return []test{t}, nil
  302. }
  303. // Slow tests which process large test vector files tend to be grouped
  304. // together, so shuffle the order.
  305. shuffled := make([]string, len(tests))
  306. perm := rand.Perm(len(tests))
  307. for i, j := range perm {
  308. shuffled[i] = tests[j]
  309. }
  310. var shards []test
  311. for i := 0; i < len(shuffled); i += testsPerShard {
  312. n := len(shuffled) - i
  313. if n > testsPerShard {
  314. n = testsPerShard
  315. }
  316. shard := t
  317. shard.args = []string{shard.args[0], "--gtest_filter=" + strings.Join(shuffled[i:i+n], ":")}
  318. shard.shard = len(shards)
  319. shards = append(shards, shard)
  320. }
  321. for i := range shards {
  322. shards[i].numShards = len(shards)
  323. }
  324. return shards, nil
  325. }
  326. func main() {
  327. flag.Parse()
  328. setWorkingDirectory()
  329. testCases, err := parseTestConfig("util/all_tests.json")
  330. if err != nil {
  331. fmt.Printf("Failed to parse input: %s\n", err)
  332. os.Exit(1)
  333. }
  334. var wg sync.WaitGroup
  335. tests := make(chan test, *numWorkers)
  336. results := make(chan result, *numWorkers)
  337. for i := 0; i < *numWorkers; i++ {
  338. wg.Add(1)
  339. go worker(tests, results, &wg)
  340. }
  341. go func() {
  342. for _, test := range testCases {
  343. if *useSDE {
  344. // SDE generates plenty of tasks and gets slower
  345. // with additional sharding.
  346. for _, cpu := range sdeCPUs {
  347. testForCPU := test
  348. testForCPU.cpu = cpu
  349. tests <- testForCPU
  350. }
  351. } else if *simulateARMCPUs {
  352. // This mode is run instead of the default path,
  353. // so also include the native flow.
  354. tests <- test
  355. for _, cpu := range armCPUs {
  356. testForCPU := test
  357. testForCPU.cpu = cpu
  358. tests <- testForCPU
  359. }
  360. } else {
  361. shards, err := test.getGTestShards()
  362. if err != nil {
  363. fmt.Printf("Error listing tests: %s\n", err)
  364. os.Exit(1)
  365. }
  366. for _, shard := range shards {
  367. tests <- shard
  368. }
  369. }
  370. }
  371. close(tests)
  372. wg.Wait()
  373. close(results)
  374. }()
  375. testOutput := testresult.NewResults()
  376. var failed, skipped []test
  377. for testResult := range results {
  378. test := testResult.Test
  379. args := test.args
  380. if testResult.Error == errTestSkipped {
  381. fmt.Printf("%s\n", test.longName())
  382. fmt.Printf("%s was skipped\n", args[0])
  383. skipped = append(skipped, test)
  384. testOutput.AddSkip(test.longName())
  385. } else if testResult.Error != nil {
  386. fmt.Printf("%s\n", test.longName())
  387. fmt.Printf("%s failed to complete: %s\n", args[0], testResult.Error)
  388. failed = append(failed, test)
  389. testOutput.AddResult(test.longName(), "CRASH")
  390. } else if !testResult.Passed {
  391. fmt.Printf("%s\n", test.longName())
  392. fmt.Printf("%s failed to print PASS on the last line.\n", args[0])
  393. failed = append(failed, test)
  394. testOutput.AddResult(test.longName(), "FAIL")
  395. } else {
  396. fmt.Printf("%s\n", test.shortName())
  397. testOutput.AddResult(test.longName(), "PASS")
  398. }
  399. }
  400. if *jsonOutput != "" {
  401. if err := testOutput.WriteToFile(*jsonOutput); err != nil {
  402. fmt.Fprintf(os.Stderr, "Error: %s\n", err)
  403. }
  404. }
  405. if len(skipped) > 0 {
  406. fmt.Printf("\n%d of %d tests were skipped:\n", len(skipped), len(testCases))
  407. for _, test := range skipped {
  408. fmt.Printf("\t%s%s\n", strings.Join(test.args, " "), test.cpuMsg())
  409. }
  410. }
  411. if len(failed) > 0 {
  412. fmt.Printf("\n%d of %d tests failed:\n", len(failed), len(testCases))
  413. for _, test := range failed {
  414. fmt.Printf("\t%s%s\n", strings.Join(test.args, " "), test.cpuMsg())
  415. }
  416. os.Exit(1)
  417. }
  418. fmt.Printf("\nAll tests passed!\n")
  419. }