Commit 5a564648 authored by bmeurer@chromium.org's avatar bmeurer@chromium.org

Improve reproducibility of test runs.

Add random seed to run-tests.py, using either a user supplied
value or a random number generated by random.SystemRandom().
This same random seed is passed to all test cases, making sure
that we can easily reproduce test failures that depend on
random numbers (i.e. bugs related to our handwritten ASLR).

Also fix all uses of rand() to make use of our RNG class
instead.

R=machenbach@chromium.org

Review URL: https://codereview.chromium.org/231443002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20637 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 707a5831
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "diy-fp.h" #include "diy-fp.h"
#include "double.h" #include "double.h"
#include "strtod.h" #include "strtod.h"
#include "utils/random-number-generator.h"
using namespace v8::internal; using namespace v8::internal;
...@@ -448,13 +449,13 @@ static const int kShortStrtodRandomCount = 2; ...@@ -448,13 +449,13 @@ static const int kShortStrtodRandomCount = 2;
static const int kLargeStrtodRandomCount = 2; static const int kLargeStrtodRandomCount = 2;
TEST(RandomStrtod) { TEST(RandomStrtod) {
srand(static_cast<unsigned int>(time(NULL))); RandomNumberGenerator rng;
char buffer[kBufferSize]; char buffer[kBufferSize];
for (int length = 1; length < 15; length++) { for (int length = 1; length < 15; length++) {
for (int i = 0; i < kShortStrtodRandomCount; ++i) { for (int i = 0; i < kShortStrtodRandomCount; ++i) {
int pos = 0; int pos = 0;
for (int j = 0; j < length; ++j) { for (int j = 0; j < length; ++j) {
buffer[pos++] = rand() % 10 + '0'; buffer[pos++] = rng.NextInt(10) + '0';
} }
int exponent = DeterministicRandom() % (25*2 + 1) - 25 - length; int exponent = DeterministicRandom() % (25*2 + 1) - 25 - length;
buffer[pos] = '\0'; buffer[pos] = '\0';
...@@ -467,7 +468,7 @@ TEST(RandomStrtod) { ...@@ -467,7 +468,7 @@ TEST(RandomStrtod) {
for (int i = 0; i < kLargeStrtodRandomCount; ++i) { for (int i = 0; i < kLargeStrtodRandomCount; ++i) {
int pos = 0; int pos = 0;
for (int j = 0; j < length; ++j) { for (int j = 0; j < length; ++j) {
buffer[pos++] = rand() % 10 + '0'; buffer[pos++] = rng.NextInt(10) + '0';
} }
int exponent = DeterministicRandom() % (308*2 + 1) - 308 - length; int exponent = DeterministicRandom() % (308*2 + 1) - 308 - length;
buffer[pos] = '\0'; buffer[pos] = '\0';
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "cctest.h" #include "cctest.h"
#include "types.h" #include "types.h"
#include "utils/random-number-generator.h"
using namespace v8::internal; using namespace v8::internal;
...@@ -112,6 +113,8 @@ class Types { ...@@ -112,6 +113,8 @@ class Types {
objects.push_back(array); objects.push_back(array);
} }
RandomNumberGenerator rng;
TypeHandle Representation; TypeHandle Representation;
TypeHandle Semantic; TypeHandle Semantic;
TypeHandle None; TypeHandle None;
...@@ -180,14 +183,14 @@ class Types { ...@@ -180,14 +183,14 @@ class Types {
} }
TypeHandle Fuzz(int depth = 5) { TypeHandle Fuzz(int depth = 5) {
switch (rand() % (depth == 0 ? 3 : 20)) { switch (rng.NextInt(depth == 0 ? 3 : 20)) {
case 0: { // bitset case 0: { // bitset
int n = 0 int n = 0
#define COUNT_BITSET_TYPES(type, value) + 1 #define COUNT_BITSET_TYPES(type, value) + 1
BITSET_TYPE_LIST(COUNT_BITSET_TYPES) BITSET_TYPE_LIST(COUNT_BITSET_TYPES)
#undef COUNT_BITSET_TYPES #undef COUNT_BITSET_TYPES
; ;
int i = rand() % n; int i = rng.NextInt(n);
#define PICK_BITSET_TYPE(type, value) \ #define PICK_BITSET_TYPE(type, value) \
if (i-- == 0) return Type::type(region_); if (i-- == 0) return Type::type(region_);
BITSET_TYPE_LIST(PICK_BITSET_TYPE) BITSET_TYPE_LIST(PICK_BITSET_TYPE)
...@@ -195,13 +198,13 @@ class Types { ...@@ -195,13 +198,13 @@ class Types {
UNREACHABLE(); UNREACHABLE();
} }
case 1: // class case 1: // class
switch (rand() % 2) { switch (rng.NextInt(2)) {
case 0: return ObjectClass; case 0: return ObjectClass;
case 1: return ArrayClass; case 1: return ArrayClass;
} }
UNREACHABLE(); UNREACHABLE();
case 2: // constant case 2: // constant
switch (rand() % 6) { switch (rng.NextInt(6)) {
case 0: return SmiConstant; case 0: return SmiConstant;
case 1: return Signed32Constant; case 1: return Signed32Constant;
case 2: return ObjectConstant1; case 2: return ObjectConstant1;
...@@ -211,7 +214,7 @@ class Types { ...@@ -211,7 +214,7 @@ class Types {
} }
UNREACHABLE(); UNREACHABLE();
default: { // union default: { // union
int n = rand() % 10; int n = rng.NextInt(10);
TypeHandle type = None; TypeHandle type = None;
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
type = Type::Union(type, Fuzz(depth - 1), region_); type = Type::Union(type, Fuzz(depth - 1), region_);
......
...@@ -93,7 +93,11 @@ ...@@ -93,7 +93,11 @@
# This test sets the umask on a per-process basis and hence cannot be # This test sets the umask on a per-process basis and hence cannot be
# used in multi-threaded runs. # used in multi-threaded runs.
# On android there is no /tmp directory. # On android there is no /tmp directory.
'd8-os': [PASS, ['isolates or arch == android_arm or arch == android_arm64 or arch == android_ia32', SKIP]], # Currently d8-os generates a temporary directory name using Math.random(), so
# we cannot run several variants of d8-os simultaneously, since all of them
# get the same random seed and would generate the same directory name. Besides
# that, it doesn't make sense to run several variants of d8-os anyways.
'd8-os': [PASS, NO_VARIANTS, ['isolates or arch == android_arm or arch == android_arm64 or arch == android_ia32', SKIP]],
'tools/tickprocessor': [PASS, ['arch == android_arm or arch == android_arm64 or arch == android_ia32', SKIP]], 'tools/tickprocessor': [PASS, ['arch == android_arm or arch == android_arm64 or arch == android_ia32', SKIP]],
############################################################################## ##############################################################################
......
...@@ -34,6 +34,7 @@ import optparse ...@@ -34,6 +34,7 @@ import optparse
import os import os
from os.path import join from os.path import join
import platform import platform
import random
import shlex import shlex
import subprocess import subprocess
import sys import sys
...@@ -200,6 +201,8 @@ def BuildOptions(): ...@@ -200,6 +201,8 @@ def BuildOptions():
result.add_option("--junittestsuite", result.add_option("--junittestsuite",
help="The testsuite name in the JUnit output file", help="The testsuite name in the JUnit output file",
default="v8tests") default="v8tests")
result.add_option("--random-seed", default=0, dest="random_seed",
help="Default seed for initializing random generator")
return result return result
...@@ -250,6 +253,9 @@ def ProcessOptions(options): ...@@ -250,6 +253,9 @@ def ProcessOptions(options):
if options.j == 0: if options.j == 0:
options.j = multiprocessing.cpu_count() options.j = multiprocessing.cpu_count()
while options.random_seed == 0:
options.random_seed = random.SystemRandom().randint(-2147483648, 2147483647)
def excl(*args): def excl(*args):
"""Returns true if zero or one of multiple arguments are true.""" """Returns true if zero or one of multiple arguments are true."""
return reduce(lambda x, y: x + y, args) <= 1 return reduce(lambda x, y: x + y, args) <= 1
...@@ -396,7 +402,8 @@ def Execute(arch, mode, args, options, suites, workspace): ...@@ -396,7 +402,8 @@ def Execute(arch, mode, args, options, suites, workspace):
timeout, options.isolates, timeout, options.isolates,
options.command_prefix, options.command_prefix,
options.extra_flags, options.extra_flags,
options.no_i18n) options.no_i18n,
options.random_seed)
# TODO(all): Combine "simulator" and "simulator_run". # TODO(all): Combine "simulator" and "simulator_run".
simulator_run = not options.dont_skip_simulator_slow_tests and \ simulator_run = not options.dont_skip_simulator_slow_tests and \
......
...@@ -171,6 +171,7 @@ class Runner(object): ...@@ -171,6 +171,7 @@ class Runner(object):
cmd = (self.context.command_prefix + cmd = (self.context.command_prefix +
[os.path.abspath(os.path.join(self.context.shell_dir, shell))] + [os.path.abspath(os.path.join(self.context.shell_dir, shell))] +
d8testflag + d8testflag +
["--random-seed=%s" % self.context.random_seed] +
test.suite.GetFlagsForTestCase(test, self.context) + test.suite.GetFlagsForTestCase(test, self.context) +
self.context.extra_flags) self.context.extra_flags)
return cmd return cmd
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
class Context(): class Context():
def __init__(self, arch, mode, shell_dir, mode_flags, verbose, timeout, def __init__(self, arch, mode, shell_dir, mode_flags, verbose, timeout,
isolates, command_prefix, extra_flags, noi18n): isolates, command_prefix, extra_flags, noi18n, random_seed):
self.arch = arch self.arch = arch
self.mode = mode self.mode = mode
self.shell_dir = shell_dir self.shell_dir = shell_dir
...@@ -39,13 +39,16 @@ class Context(): ...@@ -39,13 +39,16 @@ class Context():
self.command_prefix = command_prefix self.command_prefix = command_prefix
self.extra_flags = extra_flags self.extra_flags = extra_flags
self.noi18n = noi18n self.noi18n = noi18n
self.random_seed = random_seed
def Pack(self): def Pack(self):
return [self.arch, self.mode, self.mode_flags, self.timeout, self.isolates, return [self.arch, self.mode, self.mode_flags, self.timeout, self.isolates,
self.command_prefix, self.extra_flags, self.noi18n] self.command_prefix, self.extra_flags, self.noi18n,
self.random_seed]
@staticmethod @staticmethod
def Unpack(packed): def Unpack(packed):
# For the order of the fields, refer to Pack() above. # For the order of the fields, refer to Pack() above.
return Context(packed[0], packed[1], None, packed[2], False, return Context(packed[0], packed[1], None, packed[2], False,
packed[3], packed[4], packed[5], packed[6], packed[7]) packed[3], packed[4], packed[5], packed[6], packed[7],
packed[8])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment