From 6ce93ccb8087109a9c1444746402fcae1b12867a Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 31 Oct 2018 14:48:23 -0500 Subject: [PATCH] Simulate other ARM CPUs when running tests. We test all Intel variants via SDE. For ARM, we can do the next best thing and tweak with OPENSSL_armcap_P. If the host CPU does not support the instructions we wish to test, skip it, but print something so we know whether we need a more featureful test device. Also fix the "CRASHED" status to "CRASH", to match https://chromium.googlesource.com/chromium/src/+/master/docs/testing/json_test_results_format.md (It's unclear if anything actually parses that JSON very carefully...) Bug: 19 Change-Id: I811cc00a0d210a454287ac79c06f18fbc54f96dd Reviewed-on: https://boringssl-review.googlesource.com/c/33204 Commit-Queue: David Benjamin CQ-Verified: CQ bot account: commit-bot@chromium.org Reviewed-by: Adam Langley --- crypto/crypto.c | 4 +++ crypto/internal.h | 8 +++++ crypto/test/gtest_main.cc | 37 ++++++++++++++++++++ util/all_tests.go | 72 +++++++++++++++++++++++++++++++-------- 4 files changed, 106 insertions(+), 15 deletions(-) diff --git a/crypto/crypto.c b/crypto/crypto.c index 5f1a69a8..f7ac2558 100644 --- a/crypto/crypto.c +++ b/crypto/crypto.c @@ -102,6 +102,10 @@ HIDDEN uint32_t OPENSSL_armcap_P = #else HIDDEN uint32_t OPENSSL_armcap_P = 0; + +uint32_t *OPENSSL_get_armcap_pointer_for_test(void) { + return &OPENSSL_armcap_P; +} #endif #endif diff --git a/crypto/internal.h b/crypto/internal.h index a251b951..b98b556c 100644 --- a/crypto/internal.h +++ b/crypto/internal.h @@ -150,6 +150,14 @@ extern "C" { void OPENSSL_cpuid_setup(void); #endif +#if (defined(OPENSSL_ARM) || defined(OPENSSL_AARCH64)) && \ + !defined(OPENSSL_STATIC_ARMCAP) +// OPENSSL_get_armcap_pointer_for_test returns a pointer to |OPENSSL_armcap_P| +// for unit tests. Any modifications to the value must be made after +// |CRYPTO_library_init| but before any other function call in BoringSSL. +OPENSSL_EXPORT uint32_t *OPENSSL_get_armcap_pointer_for_test(void); +#endif + #if (!defined(_MSC_VER) || defined(__clang__)) && defined(OPENSSL_64_BIT) #define BORINGSSL_HAS_UINT128 diff --git a/crypto/test/gtest_main.cc b/crypto/test/gtest_main.cc index 5dc8b232..4501bbb7 100644 --- a/crypto/test/gtest_main.cc +++ b/crypto/test/gtest_main.cc @@ -12,13 +12,22 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include +#include #include #include "gtest_main.h" +#include "../internal.h" + +#if (defined(OPENSSL_ARM) || defined(OPENSSL_AARCH64)) && \ + !defined(OPENSSL_STATIC_ARMCAP) +#include +#define TEST_ARM_CPUS +#endif int main(int argc, char **argv) { @@ -33,5 +42,33 @@ int main(int argc, char **argv) { } #endif +#if defined(TEST_ARM_CPUS) + for (int i = 1; i < argc; i++) { + if (strncmp(argv[i], "--cpu=", 6) == 0) { + const char *cpu = argv[i] + 6; + uint32_t armcap; + if (strcmp(cpu, "none") == 0) { + armcap = 0; + } else if (strcmp(cpu, "neon") == 0) { + armcap = ARMV7_NEON; + } else if (strcmp(cpu, "crypto") == 0) { + armcap = ARMV7_NEON | ARMV8_AES | ARMV8_SHA1 | ARMV8_SHA256 | ARMV8_PMULL; + } else { + fprintf(stderr, "Unknown CPU: %s\n", cpu); + exit(1); + } + + uint32_t *armcap_ptr = OPENSSL_get_armcap_pointer_for_test(); + if ((armcap & *armcap_ptr) != armcap) { + fprintf(stderr, + "Host CPU does not support features for testing CPU '%s'.\n", + cpu); + exit(89); + } + *armcap_ptr = armcap; + } + } +#endif // TEST_ARM_CPUS + return RUN_ALL_TESTS(); } diff --git a/util/all_tests.go b/util/all_tests.go index d5794fcf..8332b1fe 100644 --- a/util/all_tests.go +++ b/util/all_tests.go @@ -18,6 +18,7 @@ import ( "bufio" "bytes" "encoding/json" + "errors" "flag" "fmt" "math/rand" @@ -45,13 +46,19 @@ var ( jsonOutput = flag.String("json-output", "", "The file to output JSON results to.") mallocTest = flag.Int64("malloc-test", -1, "If non-negative, run each test with each malloc in turn failing from the given number onwards.") 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.") + simulateARMCPUs = flag.Bool("simulate-arm-cpus", simulateARMCPUsDefault(), "If true, runs tests simulating different ARM CPUs.") ) +func simulateARMCPUsDefault() bool { + return runtime.GOOS == "linux" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") +} + type test struct { args []string shard, numShards int - // cpu, if not empty, contains an Intel CPU code to simulate. Run - // `sde64 -help` to get a list of these codes. + // cpu, if not empty, contains a code to simulate. For SDE, run `sde64 + // -help` to get a list of these codes. For ARM, see gtest_main.cc for + // the supported values. cpu string } @@ -100,6 +107,12 @@ var sdeCPUs = []string{ "knm", // Knights Mill } +var armCPUs = []string{ + "none", // No support for any ARM extensions. + "neon", // Support for NEON. + "crypto", // Support for NEON and crypto extensions. +} + func newTestOutput() *testOutput { return &testOutput{ Version: 3, @@ -114,10 +127,14 @@ func (t *testOutput) addResult(name, result string) { if _, found := t.Tests[name]; found { panic(name) } + expected := "PASS" + if result == "SKIP" { + expected = "SKIP" + } t.Tests[name] = testResult{ Actual: result, - Expected: "PASS", - IsUnexpected: result != "PASS", + Expected: expected, + IsUnexpected: result != expected, } t.NumFailuresByType[result]++ } @@ -178,17 +195,17 @@ func sdeOf(cpu, path string, args ...string) *exec.Cmd { return exec.Command(*sdePath, sdeArgs...) } -type moreMallocsError struct{} - -func (moreMallocsError) Error() string { - return "child process did not exhaust all allocation calls" -} - -var errMoreMallocs = moreMallocsError{} +var ( + errMoreMallocs = errors.New("child process did not exhaust all allocation calls") + errTestSkipped = errors.New("test was skipped") +) func runTestOnce(test test, mallocNumToFail int64) (passed bool, err error) { prog := path.Join(*buildDir, test.args[0]) args := test.args[1:] + if *simulateARMCPUs && test.cpu != "" { + args = append([]string{"--cpu=" + test.cpu}, args...) + } var cmd *exec.Cmd if *useValgrind { cmd = valgrindOf(false, prog, args...) @@ -218,8 +235,12 @@ func runTestOnce(test test, mallocNumToFail int64) (passed bool, err error) { } if err := cmd.Wait(); err != nil { if exitError, ok := err.(*exec.ExitError); ok { - if exitError.Sys().(syscall.WaitStatus).ExitStatus() == 88 { + switch exitError.Sys().(syscall.WaitStatus).ExitStatus() { + case 88: return false, errMoreMallocs + case 89: + fmt.Print(string(outBuf.Bytes())) + return false, errTestSkipped } } fmt.Print(string(outBuf.Bytes())) @@ -433,6 +454,15 @@ func main() { testForCPU.cpu = cpu tests <- testForCPU } + } else if *simulateARMCPUs { + // This mode is run instead of the default path, + // so also include the native flow. + tests <- test + for _, cpu := range armCPUs { + testForCPU := test + testForCPU.cpu = cpu + tests <- testForCPU + } } else { shards, err := test.getGTestShards() if err != nil { @@ -451,16 +481,21 @@ func main() { }() testOutput := newTestOutput() - var failed []test + var failed, skipped []test for testResult := range results { test := testResult.Test args := test.args - if testResult.Error != nil { + if testResult.Error == errTestSkipped { + fmt.Printf("%s\n", test.longName()) + fmt.Printf("%s was skipped\n", args[0]) + skipped = append(skipped, test) + testOutput.addResult(test.longName(), "SKIP") + } else if testResult.Error != nil { fmt.Printf("%s\n", test.longName()) fmt.Printf("%s failed to complete: %s\n", args[0], testResult.Error) failed = append(failed, test) - testOutput.addResult(test.longName(), "CRASHED") + testOutput.addResult(test.longName(), "CRASH") } else if !testResult.Passed { fmt.Printf("%s\n", test.longName()) fmt.Printf("%s failed to print PASS on the last line.\n", args[0]) @@ -478,6 +513,13 @@ func main() { } } + if len(skipped) > 0 { + fmt.Printf("\n%d of %d tests were skipped:\n", len(skipped), len(testCases)) + for _, test := range skipped { + fmt.Printf("\t%s%s\n", strings.Join(test.args, " "), test.cpuMsg()) + } + } + if len(failed) > 0 { fmt.Printf("\n%d of %d tests failed:\n", len(failed), len(testCases)) for _, test := range failed {