From 3014f95359cb6b753cc9d297c94072494dc0de04 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 27 Jul 2017 12:25:36 +0200 Subject: [PATCH] Significantly improve speed of nasm tests - Reuse v86 instances (in particular, memory allocation and wasm loading) - Reduce memory size from default (64M) to 2M --- src/browser/starter.js | 2 + src/bus.js | 4 +- src/cpu.js | 57 +++++----- src/main.js | 2 + tests/nasm/run.js | 240 ++++++++++++++++++++++------------------- 5 files changed, 166 insertions(+), 139 deletions(-) diff --git a/src/browser/starter.js b/src/browser/starter.js index 5a5e0943..0e74960f 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -615,6 +615,8 @@ function V86Starter(options) { this.bus.send("cpu-run"); } + + this.emulator_bus.send("emulator-loaded"); }.bind(this), 0); }.bind(this), 0); } diff --git a/src/bus.js b/src/bus.js index 4976aa34..38abc96b 100644 --- a/src/bus.js +++ b/src/bus.js @@ -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; }); }; diff --git a/src/cpu.js b/src/cpu.js index f2ef8594..057f74e0 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -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; } }; diff --git a/src/main.js b/src/main.js index fd163347..b8efd8cb 100644 --- a/src/main.js +++ b/src/main.js @@ -26,6 +26,8 @@ function v86(bus, wm) v86.prototype.run = function() { + this.stopped = false; + if(!this.running) { this.bus.send("emulator-started"); diff --git a/tests/nasm/run.js b/tests/nasm/run.js index 697c6138..40c853e5 100755 --- a/tests/nasm/run.js +++ b/tests/nasm/run.js @@ -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); + } }); }