Significantly improve speed of nasm tests

- Reuse v86 instances (in particular, memory allocation and wasm
loading)
- Reduce memory size from default (64M) to 2M
This commit is contained in:
Fabian 2017-07-27 12:25:36 +02:00
parent 4871e5a288
commit 3014f95359
5 changed files with 166 additions and 139 deletions

View file

@ -615,6 +615,8 @@ function V86Starter(options)
{
this.bus.send("cpu-run");
}
this.emulator_bus.send("emulator-loaded");
}.bind(this), 0);
}.bind(this), 0);
}

View file

@ -7,7 +7,7 @@ function BusConnector()
{
this.listeners = {};
this.pair = undefined;
};
}
/**
* @param {string} name
@ -46,7 +46,7 @@ BusConnector.prototype.unregister = function(name, fn)
this.listeners[name] = listeners.filter(function(l)
{
return l.fn !== fn
return l.fn !== fn;
});
};

View file

@ -603,6 +603,11 @@ CPU.prototype.reset = function()
this.fw_value[0] = 0;
};
CPU.prototype.reset_memory = function()
{
this.mem8.fill(0);
};
/** @export */
CPU.prototype.create_memory = function(size)
{
@ -1267,13 +1272,19 @@ CPU.prototype.run_prefix_instruction = function()
CPU.prototype.hlt_loop = function()
{
dbg_assert(this.flags[0] & flag_interrupt);
//dbg_log("In HLT loop", LOG_CPU);
if(this.flags[0] & flag_interrupt)
{
//dbg_log("In HLT loop", LOG_CPU);
this.run_hardware_timers(v86.microtick());
this.handle_irqs();
this.run_hardware_timers(v86.microtick());
this.handle_irqs();
return 0;
return 0;
}
else
{
return 100;
}
};
CPU.prototype.run_hardware_timers = function(now)
@ -3161,29 +3172,25 @@ CPU.prototype.hlt_op = function()
if((this.flags[0] & flag_interrupt) === 0)
{
this.debug.show("cpu halted");
// execution can never resume (until NMIs are supported)
this.bus.send("cpu-event-halt");
if(DEBUG) this.debug.dump_regs();
throw "HALT";
}
else
{
// get out of here and into hlt_loop
this.in_hlt = true;
//if(false) // possibly unsafe, test in safari
//{
// this.hlt_loop();
// this.diverged();
// if(this.in_hlt)
// {
// throw MAGIC_CPU_EXCEPTION;
// }
//}
//else
{
throw MAGIC_CPU_EXCEPTION;
}
// get out of here and into hlt_loop
this.in_hlt = true;
//if(false) // possibly unsafe, test in safari
//{
// this.hlt_loop();
// this.diverged();
// if(this.in_hlt)
// {
// throw MAGIC_CPU_EXCEPTION;
// }
//}
//else
{
throw MAGIC_CPU_EXCEPTION;
}
};

View file

@ -26,6 +26,8 @@ function v86(bus, wm)
v86.prototype.run = function()
{
this.stopped = false;
if(!this.running)
{
this.bus.send("emulator-started");

View file

@ -21,6 +21,7 @@ const cluster = require('cluster');
const MAX_PARALLEL_TESTS = +process.env.MAX_PARALLEL_TESTS || 99;
const TEST_DIR = __dirname + "/build/";
const DONE_MSG = 'DONE';
const TERMINATE_MSG = 'DONE';
const MASK_ARITH = 1 | 1 << 2 | 1 << 4 | 1 << 6 | 1 << 7 | 1 << 11;
@ -101,8 +102,15 @@ if (cluster.isMaster) {
current_test++;
}
else {
worker.send(TERMINATE_MSG);
worker.disconnect();
setTimeout(() => {
// The emulator currently doesn't cleanly exit, so this is necessary
console.log("Worker killed");
worker.kill();
}, 100);
finished_workers++;
if(finished_workers === nr_of_cpus)
{
@ -155,7 +163,7 @@ if (cluster.isMaster) {
worker.on('online', send_work_to_worker.bind(null, worker));
worker.on('exit', function(code, signal) {
if(code !== 0) {
if(code !== 0 && code !== null) {
console.log('Worker error code:', code);
process.exit(code);
}
@ -191,135 +199,143 @@ if (cluster.isMaster) {
}
}
else {
function run_test(test, done) {
function run_test(test)
{
if(!loaded)
{
first_test = test;
return;
}
current_test = test;
console.info('Testing', test.img_name);
let emulator = new V86({
multiboot: {
url: TEST_DIR + test.img_name
},
autostart: false
var cpu = emulator.v86.cpu;
cpu.reset();
cpu.reset_memory();
cpu.load_multiboot(new Uint8Array(fs.readFileSync(TEST_DIR + current_test.img_name)).buffer);
emulator.run();
}
let loaded = false;
let current_test = undefined;
let first_test = undefined;
let emulator = new V86({
autostart: false,
memory_size: 2 * 1024 * 1024,
});
emulator.add_listener("emulator-loaded", function()
{
loaded = true;
if(first_test)
{
run_test(first_test);
}
});
//emulator.v86.cpu.debug.show = () => {};
emulator.bus.register('cpu-event-halt', function() {
emulator.stop();
var cpu = emulator.v86.cpu;
emulator.bus.register('cpu-event-halt', function() {
var cpu = emulator.v86.cpu;
const filename = TEST_DIR + current_test.img_name;
const evaluated_mmxs = cpu.reg_mmxs;
const evaluated_xmms = cpu.reg_xmm32s;
const esp = cpu.reg32s[4];
const evaluated_memory = new Int32Array(cpu.mem8.slice(0x120000 - 16 * 4, 0x120000).buffer);
let individual_failures = [];
const filename = TEST_DIR + test.img_name;
const evaluated_mmxs = cpu.reg_mmxs;
const evaluated_xmms = cpu.reg_xmm32s;
const esp = cpu.reg32s[4];
const evaluated_memory = new Int32Array(cpu.mem8.slice(0x120000 - 16 * 4, 0x120000).buffer);
let individual_failures = [];
if(current_test.exception)
{
throw "TODO: Handle exceptions";
}
if(test.exception)
{
throw "TODO: Handle exceptions";
}
console.assert(current_test.fixture.array);
if(current_test.fixture.array)
{
let offset = 0;
const expected_reg32s = current_test.fixture.array.slice(offset, offset += 8);
const expected_mmx_registers = current_test.fixture.array.slice(offset, offset += 16);
const expected_xmm_registers = current_test.fixture.array.slice(offset, offset += 32);
const expected_memory = current_test.fixture.array.slice(offset, offset += 16);
const expected_eflags = current_test.fixture.array[offset] & MASK_ARITH;
console.assert(test.fixture.array);
if(test.fixture.array)
{
let offset = 0;
const expected_reg32s = test.fixture.array.slice(offset, offset += 8);
const expected_mmx_registers = test.fixture.array.slice(offset, offset += 16);
const expected_xmm_registers = test.fixture.array.slice(offset, offset += 32);
const expected_memory = test.fixture.array.slice(offset, offset += 16);
const expected_eflags = test.fixture.array[offset] & MASK_ARITH;
for (let i = 0; i < cpu.reg32s.length; i++) {
let reg = cpu.reg32s[i];
if (reg !== expected_reg32s[i]) {
individual_failures.push({
name: "cpu.reg32s[" + i + "]",
expected: expected_reg32s[i],
actual: reg,
});
}
}
for (let i = 0; i < evaluated_mmxs.length; i++) {
if (evaluated_mmxs[i] !== expected_mmx_registers[i]) {
individual_failures.push({
name: "mm" + (i >> 1) + ".int32[" + (i & 1) + "] (cpu.reg_mmx[" + i + "])",
expected: expected_mmx_registers[i],
actual: evaluated_mmxs[i],
});
}
}
for (let i = 0; i < evaluated_xmms.length; i++) {
if (evaluated_xmms[i] !== expected_xmm_registers[i]) {
individual_failures.push({
name: "xmm" + (i >> 2) + ".int32[" + (i & 3) + "] (cpu.reg_xmm[" + i + "])",
expected: expected_xmm_registers[i],
actual: evaluated_xmms[i],
});
}
}
for (let i = 0; i < evaluated_memory.length; i++) {
if (evaluated_memory[i] !== expected_memory[i]) {
individual_failures.push({
name: "mem[" + i + "]",
expected: expected_memory[i],
actual: evaluated_memory[i],
});
}
}
const seen_eflags = cpu.get_eflags() & MASK_ARITH;
if(seen_eflags !== expected_eflags)
{
for (let i = 0; i < cpu.reg32s.length; i++) {
let reg = cpu.reg32s[i];
if (reg !== expected_reg32s[i]) {
individual_failures.push({
name: "eflags",
expected: expected_eflags,
actual: seen_eflags,
name: "cpu.reg32s[" + i + "]",
expected: expected_reg32s[i],
actual: reg,
});
}
}
if (individual_failures.length > 0) {
done({
failures: individual_failures,
img_name: test.img_name
for (let i = 0; i < evaluated_mmxs.length; i++) {
if (evaluated_mmxs[i] !== expected_mmx_registers[i]) {
individual_failures.push({
name: "mm" + (i >> 1) + ".int32[" + (i & 1) + "] (cpu.reg_mmx[" + i + "])",
expected: expected_mmx_registers[i],
actual: evaluated_mmxs[i],
});
}
}
for (let i = 0; i < evaluated_xmms.length; i++) {
if (evaluated_xmms[i] !== expected_xmm_registers[i]) {
individual_failures.push({
name: "xmm" + (i >> 2) + ".int32[" + (i & 3) + "] (cpu.reg_xmm[" + i + "])",
expected: expected_xmm_registers[i],
actual: evaluated_xmms[i],
});
}
}
for (let i = 0; i < evaluated_memory.length; i++) {
if (evaluated_memory[i] !== expected_memory[i]) {
individual_failures.push({
name: "mem[" + i + "]",
expected: expected_memory[i],
actual: evaluated_memory[i],
});
}
}
const seen_eflags = cpu.get_eflags() & MASK_ARITH;
if(seen_eflags !== expected_eflags)
{
individual_failures.push({
name: "eflags",
expected: expected_eflags,
actual: seen_eflags,
});
}
else {
done();
}
}
});
emulator.bus.register('emulator-ready', function() {
try {
emulator.run();
}
catch(e) {
console.log(e);
}
});
}
// To silence logs from emulator in the worker
console.log = () => {};
process.on('uncaughtException', (err) => {
if (err !== 'HALT') {
console.error(err);
throw err;
if (individual_failures.length > 0) {
process.send({
failures: individual_failures,
img_name: current_test.img_name
});
}
else {
process.send(DONE_MSG);
}
});
cluster.worker.on('message', function(test) {
run_test(test, function(test_failure) {
if (test_failure) {
process.send(test_failure);
}
else {
process.send(DONE_MSG);
}
});
cluster.worker.on('message', function(message) {
if(message === TERMINATE_MSG)
{
emulator.stop();
emulator = null;
}
else
{
run_test(message);
}
});
}