aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2012-09-11 19:28:48 -0700
committerAlon Zakai <alonzakai@gmail.com>2012-09-11 19:28:48 -0700
commitafa818a5cbc78bb862f0cae8b305c8033a5b0fc3 (patch)
tree7a96bfc921999e177f2d3e997df58a72cf410b9a
parent24e61522f29d3796c0a1d2736d6008006b2e4ad6 (diff)
parentb37876be6ebf3da38b657e413c80fc6d69560b76 (diff)
Merge branch 'incoming'
-rw-r--r--src/library.js8
-rw-r--r--src/library_browser.js21
-rw-r--r--src/parseTools.js3
-rw-r--r--src/preamble.js19
-rw-r--r--src/settings.js4
-rw-r--r--tests/cases/funcptr.ll2
-rw-r--r--tests/cases/gepoverflow.txt2
-rwxr-xr-xtests/runner.py57
-rwxr-xr-xtools/reproduceriter.py120
-rw-r--r--tools/shared.py1
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):