testProfileStrings.cpp (7697B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * 4 * Tests the stack-based instrumentation profiler on a JSRuntime 5 */ 6 /* This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 9 10 #include "js/CallAndConstruct.h" 11 #include "js/ContextOptions.h" 12 #include "js/GlobalObject.h" 13 #include "js/PropertySpec.h" 14 #include "jsapi-tests/tests.h" 15 #include "vm/JSContext.h" 16 17 MOZ_RUNINIT static ProfilingStack profilingStack; 18 static uint32_t peakStackPointer = 0; 19 20 static void reset(JSContext* cx) { 21 profilingStack.stackPointer = 0; 22 cx->runtime()->geckoProfiler().stringsReset(); 23 cx->runtime()->geckoProfiler().enableSlowAssertions(true); 24 js::EnableContextProfilingStack(cx, true); 25 } 26 27 static const JSClass ptestClass = { 28 "Prof", 29 0, 30 }; 31 32 static bool test_fn(JSContext* cx, unsigned argc, JS::Value* vp) { 33 peakStackPointer = profilingStack.stackPointer; 34 return true; 35 } 36 37 static bool test_fn2(JSContext* cx, unsigned argc, JS::Value* vp) { 38 JS::RootedValue r(cx); 39 JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); 40 return JS_CallFunctionName(cx, global, "d", JS::HandleValueArray::empty(), 41 &r); 42 } 43 44 static bool enable(JSContext* cx, unsigned argc, JS::Value* vp) { 45 js::EnableContextProfilingStack(cx, true); 46 return true; 47 } 48 49 static bool disable(JSContext* cx, unsigned argc, JS::Value* vp) { 50 js::EnableContextProfilingStack(cx, false); 51 return true; 52 } 53 54 static bool Prof(JSContext* cx, unsigned argc, JS::Value* vp) { 55 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 56 JSObject* obj = JS_NewObjectForConstructor(cx, &ptestClass, args); 57 if (!obj) { 58 return false; 59 } 60 args.rval().setObject(*obj); 61 return true; 62 } 63 64 static const JSFunctionSpec ptestFunctions[] = { 65 JS_FN("test_fn", test_fn, 0, 0), 66 JS_FN("test_fn2", test_fn2, 0, 0), 67 JS_FN("enable", enable, 0, 0), 68 JS_FN("disable", disable, 0, 0), 69 JS_FS_END, 70 }; 71 72 static JSObject* initialize(JSContext* cx) { 73 js::SetContextProfilingStack(cx, &profilingStack); 74 JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); 75 return JS_InitClass(cx, global, nullptr, nullptr, "Prof", Prof, 0, nullptr, 76 ptestFunctions, nullptr, nullptr); 77 } 78 79 BEGIN_TEST(testProfileStrings_isCalledWithInterpreter) { 80 CHECK(initialize(cx)); 81 82 EXEC("function g() { var p = new Prof(); p.test_fn(); }"); 83 EXEC("function f() { g(); }"); 84 EXEC("function e() { f(); }"); 85 EXEC("function d() { e(); }"); 86 EXEC("function c() { d(); }"); 87 EXEC("function b() { c(); }"); 88 EXEC("function a() { b(); }"); 89 EXEC("function check() { var p = new Prof(); p.test_fn(); a(); }"); 90 EXEC("function check2() { var p = new Prof(); p.test_fn2(); }"); 91 92 reset(cx); 93 { 94 JS::RootedValue rval(cx); 95 /* Make sure the stack resets and we have an entry for each stack */ 96 CHECK(JS_CallFunctionName(cx, global, "check", 97 JS::HandleValueArray::empty(), &rval)); 98 CHECK(profilingStack.stackPointer == 0); 99 CHECK(peakStackPointer >= 8); 100 CHECK(cx->runtime()->geckoProfiler().stringsCount() == 8); 101 /* Make sure the stack resets and we added no new entries */ 102 peakStackPointer = 0; 103 CHECK(JS_CallFunctionName(cx, global, "check", 104 JS::HandleValueArray::empty(), &rval)); 105 CHECK(profilingStack.stackPointer == 0); 106 CHECK(peakStackPointer >= 8); 107 CHECK(cx->runtime()->geckoProfiler().stringsCount() == 8); 108 } 109 reset(cx); 110 { 111 JS::RootedValue rval(cx); 112 CHECK(JS_CallFunctionName(cx, global, "check2", 113 JS::HandleValueArray::empty(), &rval)); 114 CHECK(cx->runtime()->geckoProfiler().stringsCount() == 5); 115 CHECK(peakStackPointer >= 6); 116 CHECK(profilingStack.stackPointer == 0); 117 } 118 return true; 119 } 120 END_TEST(testProfileStrings_isCalledWithInterpreter) 121 122 BEGIN_TEST(testProfileStrings_isCalledWithJIT) { 123 CHECK(initialize(cx)); 124 125 EXEC("function g() { var p = new Prof(); p.test_fn(); }"); 126 EXEC("function f() { g(); }"); 127 EXEC("function e() { f(); }"); 128 EXEC("function d() { e(); }"); 129 EXEC("function c() { d(); }"); 130 EXEC("function b() { c(); }"); 131 EXEC("function a() { b(); }"); 132 EXEC("function check() { var p = new Prof(); p.test_fn(); a(); }"); 133 EXEC("function check2() { var p = new Prof(); p.test_fn2(); }"); 134 135 reset(cx); 136 { 137 JS::RootedValue rval(cx); 138 /* Make sure the stack resets and we have an entry for each stack */ 139 CHECK(JS_CallFunctionName(cx, global, "check", 140 JS::HandleValueArray::empty(), &rval)); 141 CHECK(profilingStack.stackPointer == 0); 142 CHECK(peakStackPointer >= 8); 143 144 /* Make sure the stack resets and we added no new entries */ 145 uint32_t cnt = cx->runtime()->geckoProfiler().stringsCount(); 146 peakStackPointer = 0; 147 CHECK(JS_CallFunctionName(cx, global, "check", 148 JS::HandleValueArray::empty(), &rval)); 149 CHECK(profilingStack.stackPointer == 0); 150 CHECK(cx->runtime()->geckoProfiler().stringsCount() == cnt); 151 CHECK(peakStackPointer >= 8); 152 } 153 154 return true; 155 } 156 END_TEST(testProfileStrings_isCalledWithJIT) 157 158 BEGIN_TEST(testProfileStrings_isCalledWhenError) { 159 CHECK(initialize(cx)); 160 161 EXEC("function check2() { throw 'a'; }"); 162 163 reset(cx); 164 { 165 JS::RootedValue rval(cx); 166 /* Make sure the stack resets and we have an entry for each stack */ 167 bool ok = JS_CallFunctionName(cx, global, "check2", 168 JS::HandleValueArray::empty(), &rval); 169 CHECK(!ok); 170 CHECK(profilingStack.stackPointer == 0); 171 CHECK(cx->runtime()->geckoProfiler().stringsCount() == 1); 172 173 JS_ClearPendingException(cx); 174 } 175 176 return true; 177 } 178 END_TEST(testProfileStrings_isCalledWhenError) 179 180 BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly) { 181 CHECK(initialize(cx)); 182 183 EXEC("function b(p) { p.test_fn(); }"); 184 EXEC("function a() { var p = new Prof(); p.enable(); b(p); }"); 185 reset(cx); 186 js::EnableContextProfilingStack(cx, false); 187 { 188 /* enable it in the middle of JS and make sure things check out */ 189 JS::RootedValue rval(cx); 190 JS_CallFunctionName(cx, global, "a", JS::HandleValueArray::empty(), &rval); 191 CHECK(profilingStack.stackPointer == 0); 192 CHECK(peakStackPointer >= 1); 193 CHECK(cx->runtime()->geckoProfiler().stringsCount() == 1); 194 } 195 196 EXEC("function d(p) { p.disable(); }"); 197 EXEC("function c() { var p = new Prof(); d(p); }"); 198 reset(cx); 199 { 200 /* now disable in the middle of js */ 201 JS::RootedValue rval(cx); 202 JS_CallFunctionName(cx, global, "c", JS::HandleValueArray::empty(), &rval); 203 CHECK(profilingStack.stackPointer == 0); 204 } 205 206 EXEC("function e() { var p = new Prof(); d(p); p.enable(); b(p); }"); 207 reset(cx); 208 { 209 /* now disable in the middle of js, but re-enable before final exit */ 210 JS::RootedValue rval(cx); 211 JS_CallFunctionName(cx, global, "e", JS::HandleValueArray::empty(), &rval); 212 CHECK(profilingStack.stackPointer == 0); 213 CHECK(peakStackPointer >= 3); 214 } 215 216 EXEC("function h() { }"); 217 EXEC("function g(p) { p.disable(); for (var i = 0; i < 100; i++) i++; }"); 218 EXEC("function f() { g(new Prof()); }"); 219 reset(cx); 220 cx->runtime()->geckoProfiler().enableSlowAssertions(false); 221 { 222 JS::RootedValue rval(cx); 223 /* disable, and make sure that if we try to re-enter the JIT the pop 224 * will still happen */ 225 JS_CallFunctionName(cx, global, "f", JS::HandleValueArray::empty(), &rval); 226 CHECK(profilingStack.stackPointer == 0); 227 } 228 return true; 229 } 230 END_TEST(testProfileStrings_worksWhenEnabledOnTheFly)