diff options
author | Alon Zakai <alonzakai@gmail.com> | 2012-09-11 19:28:48 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2012-09-11 19:28:48 -0700 |
commit | afa818a5cbc78bb862f0cae8b305c8033a5b0fc3 (patch) | |
tree | 7a96bfc921999e177f2d3e997df58a72cf410b9a | |
parent | 24e61522f29d3796c0a1d2736d6008006b2e4ad6 (diff) | |
parent | b37876be6ebf3da38b657e413c80fc6d69560b76 (diff) |
Merge branch 'incoming'
-rw-r--r-- | src/library.js | 8 | ||||
-rw-r--r-- | src/library_browser.js | 21 | ||||
-rw-r--r-- | src/parseTools.js | 3 | ||||
-rw-r--r-- | src/preamble.js | 19 | ||||
-rw-r--r-- | src/settings.js | 4 | ||||
-rw-r--r-- | tests/cases/funcptr.ll | 2 | ||||
-rw-r--r-- | tests/cases/gepoverflow.txt | 2 | ||||
-rwxr-xr-x | tests/runner.py | 57 | ||||
-rwxr-xr-x | tools/reproduceriter.py | 120 | ||||
-rw-r--r-- | tools/shared.py | 1 |
10 files changed, 193 insertions, 44 deletions
diff --git a/src/library.js b/src/library.js index d2d61867..798a6f58 100644 --- a/src/library.js +++ b/src/library.js @@ -2802,9 +2802,9 @@ LibraryManager.library = { }); } else if (next == 's'.charCodeAt(0)) { // String. - var arg = getNextArg('i8*') || 0; // 0 holds '(null)' + var arg = getNextArg('i8*') || nullString; var argLength = String_len(arg); - if (precisionSet) argLength = Math.min(String_len(arg), precision); + if (precisionSet) argLength = Math.min(argLength, precision); if (!flagLeftAlign) { while (argLength < width--) { ret.push(' '.charCodeAt(0)); @@ -3578,7 +3578,9 @@ LibraryManager.library = { } if (!whole && !fraction) { - {{{ makeSetValue('endptr', 0, 'origin', '*') }}} + if (endptr) { + {{{ makeSetValue('endptr', 0, 'origin', '*') }}} + } return 0; } diff --git a/src/library_browser.js b/src/library_browser.js index bf235493..43732e5b 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -379,12 +379,6 @@ mergeInto(LibraryManager.library, { emscripten_set_main_loop: function(func, fps) { Module['noExitRuntime'] = true; -#if PROFILE_MAIN_LOOP - Module['totalTime'] = 0; - Module['iterations'] = 0; - Module['maxTime'] = 0; -#endif - var jsFunc = FUNCTION_TABLE[func]; Browser.mainLoop.runner = function() { if (Browser.mainLoop.queue.length > 0) { @@ -414,18 +408,15 @@ mergeInto(LibraryManager.library, { return; } -#if PROFILE_MAIN_LOOP - var start = performance.now(); -#endif + if (Module['preMainLoop']) { + Module['preMainLoop'](); + } jsFunc(); -#if PROFILE_MAIN_LOOP - var time = performance.now() - start; - Module['totalTime'] += time; - Module['iterations']++; - Module['maxTime'] = Math.max(Module['maxTime'], time); -#endif + if (Module['postMainLoop']) { + Module['postMainLoop'](); + } if (Browser.mainLoop.shouldPause) { // catch pauses from the main loop itself diff --git a/src/parseTools.js b/src/parseTools.js index e37f3a99..80ba269b 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1265,6 +1265,9 @@ function makePointer(slab, pos, allocator, type) { de = dedup(evaled); if (de.length === 1 && de[0] === 0) { slab = types.length; + if (USE_TYPED_ARRAYS == 2) { + types = ['i8']; // if data is zeros, we don't need type info + } } // TODO: if not all zeros, at least filter out items with type === 0. requires cleverness to know how to skip at runtime though. also // be careful of structure padding diff --git a/src/preamble.js b/src/preamble.js index a8f19d64..b3cfca85 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -24,6 +24,8 @@ var ACCEPTABLE_SAFE_HEAP_ERRORS = 0; function SAFE_HEAP_ACCESS(dest, type, store, ignore) { //if (dest === A_NUMBER) Module.print ([dest, type, store] + ' ' + new Error().stack); // Something like this may be useful, in debugging + assert(dest >= STACK_ROOT, 'segmentation fault: null pointer, or below normal memory'); + #if USE_TYPED_ARRAYS // When using typed arrays, reads over the top of TOTAL_MEMORY will fail silently, so we must // correct that by growing TOTAL_MEMORY as needed. Without typed arrays, memory is a normal @@ -639,13 +641,6 @@ var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}}; } #endif -var base = intArrayFromString('(null)'); // So printing %s of NULL gives '(null)' - // Also this ensures we leave 0 as an invalid address, 'NULL' -STATICTOP = base.length; -for (var i = 0; i < base.length; i++) { - {{{ makeSetValue(0, 'i', 'base[i]', 'i8') }}} -} - Module['HEAP'] = HEAP; #if USE_TYPED_ARRAYS == 1 Module['IHEAP'] = IHEAP; @@ -664,7 +659,7 @@ Module['HEAPF32'] = HEAPF32; Module['HEAPF64'] = HEAPF64; #endif -STACK_ROOT = STACKTOP = Runtime.alignMemory(STATICTOP); +STACK_ROOT = STACKTOP = Runtime.alignMemory(1); STACK_MAX = STACK_ROOT + TOTAL_STACK; #if USE_TYPED_ARRAYS == 2 @@ -694,6 +689,8 @@ STACK_MAX = tempDoublePtr + 8; STATICTOP = alignMemoryPage(STACK_MAX); +var nullString = allocate(intArrayFromString('(null)'), 'i8', ALLOC_STATIC); + function callRuntimeCallbacks(callbacks) { while(callbacks.length > 0) { var callback = callbacks.shift(); @@ -723,9 +720,9 @@ function exitRuntime() { } function String_len(ptr) { - var i = 0; - while ({{{ makeGetValue('ptr', 'i', 'i8') }}}) i++; // Note: should be |!= 0|, technically. But this helps catch bugs with undefineds - return i; + var i = ptr; + while ({{{ makeGetValue('i++', '0', 'i8') }}}) {}; // Note: should be |!= 0|, technically. But this helps catch bugs with undefineds + return i - ptr - 1; } Module['String_len'] = String_len; diff --git a/src/settings.js b/src/settings.js index 9f63622d..fe532bda 100644 --- a/src/settings.js +++ b/src/settings.js @@ -104,7 +104,9 @@ var CATCH_EXIT_CODE = 0; // If set, causes exit() to throw an exception object w // terminated with an error message. // Generated code debugging options -var SAFE_HEAP = 0; // Check each write to the heap against a list of blocked addresses +var SAFE_HEAP = 0; // Check each write to the heap, for example, this will give a clear + // error on what would be segfaults in a native build (like deferencing + // 0). See preamble.js for the actual checks performed. // If equal to 2, done on a line-by-line basis according to // SAFE_HEAP_LINES, checking only the specified lines. // If equal to 3, checking all *but* the specified lines. Note diff --git a/tests/cases/funcptr.ll b/tests/cases/funcptr.ll index 07e2bf91..0aa03fcf 100644 --- a/tests/cases/funcptr.ll +++ b/tests/cases/funcptr.ll @@ -9,7 +9,7 @@ define i32 @main() { entry: %retval = alloca i32, align 4 ; [#uses=1 type=i32*] store i32 0, i32* %retval - %access_virt_barray = bitcast i32 0 to [64 x i16]* (i32*, i32)** + %access_virt_barray = bitcast i32 100 to [64 x i16]* (i32*, i32)** store [64 x i16]* (i32*, i32)* @access_virt_barray, [64 x i16]* (i32*, i32)** %access_virt_barray, align 4 %wakaptr = bitcast [64 x i16]* (i32*, i32)** %access_virt_barray to i32* %waka = load i32* %wakaptr diff --git a/tests/cases/gepoverflow.txt b/tests/cases/gepoverflow.txt index 164e5c0c..90772c33 100644 --- a/tests/cases/gepoverflow.txt +++ b/tests/cases/gepoverflow.txt @@ -1,2 +1,2 @@ -*5246494,5247064* +*5246502,5247072* *-514,56* diff --git a/tests/runner.py b/tests/runner.py index b81cee9a..e4aba0c9 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -2108,6 +2108,39 @@ c5,de,15,8a ''' self.do_run(src, '*11,74,32,1012*\n*11*\n*22*') + def test_segfault(self): + if self.emcc_args is None: return self.skip('SAFE_HEAP without ta2 means we check types too, which hide segfaults') + + Settings.SAFE_HEAP = 1 + + for addr in ['0', '7', 'new D2()']: + print addr + src = r''' + #include <stdio.h> + + struct Classey { + virtual void doIt() = 0; + }; + + struct D1 : Classey { + virtual void doIt() { printf("fleefl\n"); } + }; + + struct D2 : Classey { + virtual void doIt() { printf("marfoosh\n"); } + }; + + int main(int argc, char **argv) + { + Classey *p = argc == 100 ? new D1() : (Classey*)%s; + + p->doIt(); + + return 0; + } + ''' % addr + self.do_run(src, 'segmentation fault' if addr.isdigit() else 'marfoosh') + def test_dynamic_cast(self): if self.emcc_args is None: return self.skip('need libcxxabi') @@ -2435,6 +2468,30 @@ c5,de,15,8a ''' self.do_run(src, '*70,97,15,3,3029,90*') + def test_bigarray(self): + if self.emcc_args is None: return self.skip('need ta2 to compress type data on zeroinitializers') + + # avoid "array initializer too large" errors + src = r''' + #include <stdio.h> + #include <assert.h> + + #define SIZE (1024*100) + struct Struct { + char x; + int y; + }; + Struct buffy[SIZE]; + + int main() { + for (int i = 0; i < SIZE; i++) { assert(buffy[i].x == 0 && buffy[i].y == 0); } // we were zeroinitialized + for (int i = 0; i < SIZE; i++) { buffy[i].x = i*i; buffy[i].y = i*i*i; } // we can save data + printf("*%d*\n", buffy[SIZE/3].x); + return 0; + } + ''' + self.do_run(src, '*57*') + def test_mod_globalstruct(self): src = ''' #include <stdio.h> diff --git a/tools/reproduceriter.py b/tools/reproduceriter.py index 3402192c..6696915d 100755 --- a/tools/reproduceriter.py +++ b/tools/reproduceriter.py @@ -79,18 +79,19 @@ Examples * BananaBread: Unpack into a directory called bb, then one directory up, run - emscripten/tools/reproduceriter.py bb bench js/game-setup.js + emscripten/tools/reproduceriter.py bb bench js/game-setup.js game.html?low,low,reproduce=repro.data ''' import os, sys, shutil, re -assert len(sys.argv) == 4, 'Usage: reproduceriter.py IN_DIR OUT_DIR FIRST_JS' +assert len(sys.argv) >= 4, 'Usage: reproduceriter.py IN_DIR OUT_DIR FIRST_JS [WINDOW_LOCATION]' # Process input args in_dir = sys.argv[1] out_dir = sys.argv[2] first_js = sys.argv[3] +window_location = sys.argv[4] if len(sys.argv) >= 5 else '' if os.path.exists(out_dir): shutil.rmtree(out_dir) @@ -121,6 +122,69 @@ for parent, dirs, files in os.walk(out_dir): print 'add boilerplate...' open(os.path.join(out_dir, first_js), 'w').write(''' + +// environment for shell +if (typeof nagivator == 'undefined') { + var window = { + location: { + toString: function() { + return '%s'; + }, + search: '%s', + }, + rafs: [], + requestAnimationFrame: function(func) { + window.rafs.push(func); + }, + runEventLoop: function() { + while (1) { // run forever until an exception stops this replay + var currRafs = window.rafs; + window.rafs = []; + for (var i = 0; i < currRafs.length; i++) { + currRafs[i](); + } + } + }, + }; + var document = { + getElementById: function(id) { + switch(id) { + case 'canvas': { + return { + getContext: function(which) { + switch(which) { + case 'experimental-webgl': { + return { + getExtension: function() { return 1 }, + requestPointerLock: function() { + throw 'pointerLock'; + }, + }; + } + default: throw 'canvas.getContext: ' + which; + } + }, + }; + } + default: throw 'getElementById: ' + id; + } + }, + querySelector: function() { + return { + classList: { + add: function(){}, + remove: function(){}, + }, + }; + }, + }; + var performance = { + now: function() { + return Date.now(); + }, + }; +} + var Recorder = (function() { var recorder; var init = 'reproduce='; @@ -155,16 +219,16 @@ var Recorder = (function() { }; // Date.now, performance.now recorder.dnows = []; - var dnow = Date.now; + recorder.dnow = Date.now; Date.now = function() { - var ret = dnow(); + var ret = recorder.dnow(); recorder.dnows.push(ret); return ret; }; recorder.pnows = []; - var pnow = performance.now; + recorder.pnow = performance.now; performance.now = function() { - var ret = pnow(); + var ret = recorder.pnow(); recorder.pnows.push(ret); return ret; }; @@ -270,6 +334,7 @@ var Recorder = (function() { } }; // Date.now, performance.now + recorder.dnow = Date.now; Date.now = function() { if (recorder.dnows.length > 0) { return recorder.dnows.pop(); @@ -278,6 +343,8 @@ var Recorder = (function() { throw 'consuming too many values!'; } }; + var pnow = performance.now || performance.webkitNow || performance.mozNow || performance.oNow || performance.msNow || dnow; + recorder.pnow = function() { return pnow.call(performance) }; performance.now = function() { if (recorder.pnows.length > 0) { return recorder.pnows.pop(); @@ -294,18 +361,47 @@ var Recorder = (function() { recorder.addListener = function(target, which, callback, arg) { recorder['event' + which] = callback; }; + recorder.onFinish = []; + // Benchmarking hooks - emscripten specific + setTimeout(function() { + var totalTime = 0; + var totalSquared = 0; + var iterations = 0; + var maxTime = 0; + var curr = 0; + Module.preMainLoop = function() { + curr = recorder.pnow(); + } + Module.postMainLoop = function() { + var time = recorder.pnow() - curr; + totalTime += time; + totalSquared += time*time; + maxTime = Math.max(maxTime, time); + iterations++; + }; + recorder.onFinish.push(function() { + var mean = totalTime / iterations; + var meanSquared = totalSquared / iterations; + console.log('mean frame : ' + mean + ' ms'); + console.log('frame std dev: ' + Math.sqrt(meanSquared - (mean*mean)) + ' ms'); + console.log('max frame : ' + maxTime + ' ms'); + }); + }); // Finish recorder.finish = function() { - if (recorder.onFinish) { - recorder.onFinish(); - recorder.onFinish = null; - } + recorder.onFinish.forEach(function(finish) { + finish(); + }); }; } + recorder.replaying = replaying; return recorder; })(); -''' + open(os.path.join(in_dir, first_js)).read() -) +''' % (window_location, window_location.split('?')[-1]) + open(os.path.join(in_dir, first_js)).read() + ''' +if (typeof nagivator == 'undefined') { + window.runEventLoop(); +} +''') print 'done!' diff --git a/tools/shared.py b/tools/shared.py index 672a1a18..2149df0a 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -561,6 +561,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e unresolved_symbols = set(['main']) # tracking unresolveds is necessary for .a linking, see below. (and main is always a necessary symbol) resolved_symbols = set() temp_dir = None + files = map(os.path.abspath, files) for f in files: if not Building.is_ar(f): if Building.is_bitcode(f): |