1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsapi-tests/testProfileStrings.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,276 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * 1.7 + * Tests the stack-based instrumentation profiler on a JSRuntime 1.8 + */ 1.9 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.10 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.11 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.12 + 1.13 +#include "jscntxt.h" 1.14 + 1.15 +#include "jsapi-tests/tests.h" 1.16 + 1.17 +static js::ProfileEntry pstack[10]; 1.18 +static uint32_t psize = 0; 1.19 +static uint32_t max_stack = 0; 1.20 + 1.21 +static void 1.22 +reset(JSContext *cx) 1.23 +{ 1.24 + psize = max_stack = 0; 1.25 + memset(pstack, 0, sizeof(pstack)); 1.26 + cx->runtime()->spsProfiler.stringsReset(); 1.27 + cx->runtime()->spsProfiler.enableSlowAssertions(true); 1.28 + js::EnableRuntimeProfilingStack(cx->runtime(), true); 1.29 +} 1.30 + 1.31 +static const JSClass ptestClass = { 1.32 + "Prof", 0, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, 1.33 + JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub 1.34 +}; 1.35 + 1.36 +static bool 1.37 +test_fn(JSContext *cx, unsigned argc, jsval *vp) 1.38 +{ 1.39 + max_stack = psize; 1.40 + return true; 1.41 +} 1.42 + 1.43 +static bool 1.44 +test_fn2(JSContext *cx, unsigned argc, jsval *vp) 1.45 +{ 1.46 + JS::RootedValue r(cx); 1.47 + JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); 1.48 + return JS_CallFunctionName(cx, global, "d", JS::HandleValueArray::empty(), &r); 1.49 +} 1.50 + 1.51 +static bool 1.52 +enable(JSContext *cx, unsigned argc, jsval *vp) 1.53 +{ 1.54 + js::EnableRuntimeProfilingStack(cx->runtime(), true); 1.55 + return true; 1.56 +} 1.57 + 1.58 +static bool 1.59 +disable(JSContext *cx, unsigned argc, jsval *vp) 1.60 +{ 1.61 + js::EnableRuntimeProfilingStack(cx->runtime(), false); 1.62 + return true; 1.63 +} 1.64 + 1.65 +static bool 1.66 +Prof(JSContext* cx, unsigned argc, jsval *vp) 1.67 +{ 1.68 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.69 + JSObject *obj = JS_NewObjectForConstructor(cx, &ptestClass, args); 1.70 + if (!obj) 1.71 + return false; 1.72 + args.rval().setObject(*obj); 1.73 + return true; 1.74 +} 1.75 + 1.76 +static const JSFunctionSpec ptestFunctions[] = { 1.77 + JS_FS("test_fn", test_fn, 0, 0), 1.78 + JS_FS("test_fn2", test_fn2, 0, 0), 1.79 + JS_FS("enable", enable, 0, 0), 1.80 + JS_FS("disable", disable, 0, 0), 1.81 + JS_FS_END 1.82 +}; 1.83 + 1.84 +static JSObject* 1.85 +initialize(JSContext *cx) 1.86 +{ 1.87 + js::SetRuntimeProfilingStack(cx->runtime(), pstack, &psize, 10); 1.88 + JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); 1.89 + return JS_InitClass(cx, global, js::NullPtr(), &ptestClass, Prof, 0, 1.90 + nullptr, ptestFunctions, nullptr, nullptr); 1.91 +} 1.92 + 1.93 +BEGIN_TEST(testProfileStrings_isCalledWithInterpreter) 1.94 +{ 1.95 + CHECK(initialize(cx)); 1.96 + 1.97 + EXEC("function g() { var p = new Prof(); p.test_fn(); }"); 1.98 + EXEC("function f() { g(); }"); 1.99 + EXEC("function e() { f(); }"); 1.100 + EXEC("function d() { e(); }"); 1.101 + EXEC("function c() { d(); }"); 1.102 + EXEC("function b() { c(); }"); 1.103 + EXEC("function a() { b(); }"); 1.104 + EXEC("function check() { var p = new Prof(); p.test_fn(); a(); }"); 1.105 + EXEC("function check2() { var p = new Prof(); p.test_fn2(); }"); 1.106 + 1.107 + reset(cx); 1.108 + { 1.109 + JS::RootedValue rval(cx); 1.110 + /* Make sure the stack resets and we have an entry for each stack */ 1.111 + CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), 1.112 + &rval)); 1.113 + CHECK(psize == 0); 1.114 + CHECK(max_stack >= 8); 1.115 + CHECK(cx->runtime()->spsProfiler.stringsCount() == 8); 1.116 + /* Make sure the stack resets and we added no new entries */ 1.117 + max_stack = 0; 1.118 + CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), 1.119 + &rval)); 1.120 + CHECK(psize == 0); 1.121 + CHECK(max_stack >= 8); 1.122 + CHECK(cx->runtime()->spsProfiler.stringsCount() == 8); 1.123 + } 1.124 + reset(cx); 1.125 + { 1.126 + JS::RootedValue rval(cx); 1.127 + CHECK(JS_CallFunctionName(cx, global, "check2", JS::HandleValueArray::empty(), 1.128 + &rval)); 1.129 + CHECK(cx->runtime()->spsProfiler.stringsCount() == 5); 1.130 + CHECK(max_stack >= 6); 1.131 + CHECK(psize == 0); 1.132 + } 1.133 + js::EnableRuntimeProfilingStack(cx->runtime(), false); 1.134 + js::SetRuntimeProfilingStack(cx->runtime(), pstack, &psize, 3); 1.135 + reset(cx); 1.136 + { 1.137 + JS::RootedValue rval(cx); 1.138 + pstack[3].setLabel((char*) 1234); 1.139 + CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), 1.140 + &rval)); 1.141 + CHECK((size_t) pstack[3].label() == 1234); 1.142 + CHECK(max_stack >= 8); 1.143 + CHECK(psize == 0); 1.144 + } 1.145 + return true; 1.146 +} 1.147 +END_TEST(testProfileStrings_isCalledWithInterpreter) 1.148 + 1.149 +BEGIN_TEST(testProfileStrings_isCalledWithJIT) 1.150 +{ 1.151 + CHECK(initialize(cx)); 1.152 + JS::RuntimeOptionsRef(cx).setBaseline(true) 1.153 + .setIon(true); 1.154 + 1.155 + EXEC("function g() { var p = new Prof(); p.test_fn(); }"); 1.156 + EXEC("function f() { g(); }"); 1.157 + EXEC("function e() { f(); }"); 1.158 + EXEC("function d() { e(); }"); 1.159 + EXEC("function c() { d(); }"); 1.160 + EXEC("function b() { c(); }"); 1.161 + EXEC("function a() { b(); }"); 1.162 + EXEC("function check() { var p = new Prof(); p.test_fn(); a(); }"); 1.163 + EXEC("function check2() { var p = new Prof(); p.test_fn2(); }"); 1.164 + 1.165 + reset(cx); 1.166 + { 1.167 + JS::RootedValue rval(cx); 1.168 + /* Make sure the stack resets and we have an entry for each stack */ 1.169 + CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), 1.170 + &rval)); 1.171 + CHECK(psize == 0); 1.172 + CHECK(max_stack >= 8); 1.173 + 1.174 + /* Make sure the stack resets and we added no new entries */ 1.175 + uint32_t cnt = cx->runtime()->spsProfiler.stringsCount(); 1.176 + max_stack = 0; 1.177 + CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), 1.178 + &rval)); 1.179 + CHECK(psize == 0); 1.180 + CHECK(cx->runtime()->spsProfiler.stringsCount() == cnt); 1.181 + CHECK(max_stack >= 8); 1.182 + } 1.183 + 1.184 + js::EnableRuntimeProfilingStack(cx->runtime(), false); 1.185 + js::SetRuntimeProfilingStack(cx->runtime(), pstack, &psize, 3); 1.186 + reset(cx); 1.187 + { 1.188 + /* Limit the size of the stack and make sure we don't overflow */ 1.189 + JS::RootedValue rval(cx); 1.190 + pstack[3].setLabel((char*) 1234); 1.191 + CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), 1.192 + &rval)); 1.193 + CHECK(psize == 0); 1.194 + CHECK(max_stack >= 8); 1.195 + CHECK((size_t) pstack[3].label() == 1234); 1.196 + } 1.197 + return true; 1.198 +} 1.199 +END_TEST(testProfileStrings_isCalledWithJIT) 1.200 + 1.201 +BEGIN_TEST(testProfileStrings_isCalledWhenError) 1.202 +{ 1.203 + CHECK(initialize(cx)); 1.204 + JS::RuntimeOptionsRef(cx).setBaseline(true) 1.205 + .setIon(true); 1.206 + 1.207 + EXEC("function check2() { throw 'a'; }"); 1.208 + 1.209 + reset(cx); 1.210 + JS::ContextOptionsRef(cx).setDontReportUncaught(true); 1.211 + { 1.212 + JS::RootedValue rval(cx); 1.213 + /* Make sure the stack resets and we have an entry for each stack */ 1.214 + bool ok = JS_CallFunctionName(cx, global, "check2", JS::HandleValueArray::empty(), 1.215 + &rval); 1.216 + CHECK(!ok); 1.217 + CHECK(psize == 0); 1.218 + CHECK(cx->runtime()->spsProfiler.stringsCount() == 1); 1.219 + 1.220 + JS_ClearPendingException(cx); 1.221 + } 1.222 + return true; 1.223 +} 1.224 +END_TEST(testProfileStrings_isCalledWhenError) 1.225 + 1.226 +BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly) 1.227 +{ 1.228 + CHECK(initialize(cx)); 1.229 + JS::RuntimeOptionsRef(cx).setBaseline(true) 1.230 + .setIon(true); 1.231 + 1.232 + EXEC("function b(p) { p.test_fn(); }"); 1.233 + EXEC("function a() { var p = new Prof(); p.enable(); b(p); }"); 1.234 + reset(cx); 1.235 + js::EnableRuntimeProfilingStack(cx->runtime(), false); 1.236 + { 1.237 + /* enable it in the middle of JS and make sure things check out */ 1.238 + JS::RootedValue rval(cx); 1.239 + JS_CallFunctionName(cx, global, "a", JS::HandleValueArray::empty(), &rval); 1.240 + CHECK(psize == 0); 1.241 + CHECK(max_stack >= 1); 1.242 + CHECK(cx->runtime()->spsProfiler.stringsCount() == 1); 1.243 + } 1.244 + 1.245 + EXEC("function d(p) { p.disable(); }"); 1.246 + EXEC("function c() { var p = new Prof(); d(p); }"); 1.247 + reset(cx); 1.248 + { 1.249 + /* now disable in the middle of js */ 1.250 + JS::RootedValue rval(cx); 1.251 + JS_CallFunctionName(cx, global, "c", JS::HandleValueArray::empty(), &rval); 1.252 + CHECK(psize == 0); 1.253 + } 1.254 + 1.255 + EXEC("function e() { var p = new Prof(); d(p); p.enable(); b(p); }"); 1.256 + reset(cx); 1.257 + { 1.258 + /* now disable in the middle of js, but re-enable before final exit */ 1.259 + JS::RootedValue rval(cx); 1.260 + JS_CallFunctionName(cx, global, "e", JS::HandleValueArray::empty(), &rval); 1.261 + CHECK(psize == 0); 1.262 + CHECK(max_stack >= 3); 1.263 + } 1.264 + 1.265 + EXEC("function h() { }"); 1.266 + EXEC("function g(p) { p.disable(); for (var i = 0; i < 100; i++) i++; }"); 1.267 + EXEC("function f() { g(new Prof()); }"); 1.268 + reset(cx); 1.269 + cx->runtime()->spsProfiler.enableSlowAssertions(false); 1.270 + { 1.271 + JS::RootedValue rval(cx); 1.272 + /* disable, and make sure that if we try to re-enter the JIT the pop 1.273 + * will still happen */ 1.274 + JS_CallFunctionName(cx, global, "f", JS::HandleValueArray::empty(), &rval); 1.275 + CHECK(psize == 0); 1.276 + } 1.277 + return true; 1.278 +} 1.279 +END_TEST(testProfileStrings_worksWhenEnabledOnTheFly)