|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
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 "jscntxt.h" |
|
11 |
|
12 #include "jsapi-tests/tests.h" |
|
13 |
|
14 static js::ProfileEntry pstack[10]; |
|
15 static uint32_t psize = 0; |
|
16 static uint32_t max_stack = 0; |
|
17 |
|
18 static void |
|
19 reset(JSContext *cx) |
|
20 { |
|
21 psize = max_stack = 0; |
|
22 memset(pstack, 0, sizeof(pstack)); |
|
23 cx->runtime()->spsProfiler.stringsReset(); |
|
24 cx->runtime()->spsProfiler.enableSlowAssertions(true); |
|
25 js::EnableRuntimeProfilingStack(cx->runtime(), true); |
|
26 } |
|
27 |
|
28 static const JSClass ptestClass = { |
|
29 "Prof", 0, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, |
|
30 JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
|
31 }; |
|
32 |
|
33 static bool |
|
34 test_fn(JSContext *cx, unsigned argc, jsval *vp) |
|
35 { |
|
36 max_stack = psize; |
|
37 return true; |
|
38 } |
|
39 |
|
40 static bool |
|
41 test_fn2(JSContext *cx, unsigned argc, jsval *vp) |
|
42 { |
|
43 JS::RootedValue r(cx); |
|
44 JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); |
|
45 return JS_CallFunctionName(cx, global, "d", JS::HandleValueArray::empty(), &r); |
|
46 } |
|
47 |
|
48 static bool |
|
49 enable(JSContext *cx, unsigned argc, jsval *vp) |
|
50 { |
|
51 js::EnableRuntimeProfilingStack(cx->runtime(), true); |
|
52 return true; |
|
53 } |
|
54 |
|
55 static bool |
|
56 disable(JSContext *cx, unsigned argc, jsval *vp) |
|
57 { |
|
58 js::EnableRuntimeProfilingStack(cx->runtime(), false); |
|
59 return true; |
|
60 } |
|
61 |
|
62 static bool |
|
63 Prof(JSContext* cx, unsigned argc, jsval *vp) |
|
64 { |
|
65 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
|
66 JSObject *obj = JS_NewObjectForConstructor(cx, &ptestClass, args); |
|
67 if (!obj) |
|
68 return false; |
|
69 args.rval().setObject(*obj); |
|
70 return true; |
|
71 } |
|
72 |
|
73 static const JSFunctionSpec ptestFunctions[] = { |
|
74 JS_FS("test_fn", test_fn, 0, 0), |
|
75 JS_FS("test_fn2", test_fn2, 0, 0), |
|
76 JS_FS("enable", enable, 0, 0), |
|
77 JS_FS("disable", disable, 0, 0), |
|
78 JS_FS_END |
|
79 }; |
|
80 |
|
81 static JSObject* |
|
82 initialize(JSContext *cx) |
|
83 { |
|
84 js::SetRuntimeProfilingStack(cx->runtime(), pstack, &psize, 10); |
|
85 JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); |
|
86 return JS_InitClass(cx, global, js::NullPtr(), &ptestClass, Prof, 0, |
|
87 nullptr, ptestFunctions, nullptr, nullptr); |
|
88 } |
|
89 |
|
90 BEGIN_TEST(testProfileStrings_isCalledWithInterpreter) |
|
91 { |
|
92 CHECK(initialize(cx)); |
|
93 |
|
94 EXEC("function g() { var p = new Prof(); p.test_fn(); }"); |
|
95 EXEC("function f() { g(); }"); |
|
96 EXEC("function e() { f(); }"); |
|
97 EXEC("function d() { e(); }"); |
|
98 EXEC("function c() { d(); }"); |
|
99 EXEC("function b() { c(); }"); |
|
100 EXEC("function a() { b(); }"); |
|
101 EXEC("function check() { var p = new Prof(); p.test_fn(); a(); }"); |
|
102 EXEC("function check2() { var p = new Prof(); p.test_fn2(); }"); |
|
103 |
|
104 reset(cx); |
|
105 { |
|
106 JS::RootedValue rval(cx); |
|
107 /* Make sure the stack resets and we have an entry for each stack */ |
|
108 CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), |
|
109 &rval)); |
|
110 CHECK(psize == 0); |
|
111 CHECK(max_stack >= 8); |
|
112 CHECK(cx->runtime()->spsProfiler.stringsCount() == 8); |
|
113 /* Make sure the stack resets and we added no new entries */ |
|
114 max_stack = 0; |
|
115 CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), |
|
116 &rval)); |
|
117 CHECK(psize == 0); |
|
118 CHECK(max_stack >= 8); |
|
119 CHECK(cx->runtime()->spsProfiler.stringsCount() == 8); |
|
120 } |
|
121 reset(cx); |
|
122 { |
|
123 JS::RootedValue rval(cx); |
|
124 CHECK(JS_CallFunctionName(cx, global, "check2", JS::HandleValueArray::empty(), |
|
125 &rval)); |
|
126 CHECK(cx->runtime()->spsProfiler.stringsCount() == 5); |
|
127 CHECK(max_stack >= 6); |
|
128 CHECK(psize == 0); |
|
129 } |
|
130 js::EnableRuntimeProfilingStack(cx->runtime(), false); |
|
131 js::SetRuntimeProfilingStack(cx->runtime(), pstack, &psize, 3); |
|
132 reset(cx); |
|
133 { |
|
134 JS::RootedValue rval(cx); |
|
135 pstack[3].setLabel((char*) 1234); |
|
136 CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), |
|
137 &rval)); |
|
138 CHECK((size_t) pstack[3].label() == 1234); |
|
139 CHECK(max_stack >= 8); |
|
140 CHECK(psize == 0); |
|
141 } |
|
142 return true; |
|
143 } |
|
144 END_TEST(testProfileStrings_isCalledWithInterpreter) |
|
145 |
|
146 BEGIN_TEST(testProfileStrings_isCalledWithJIT) |
|
147 { |
|
148 CHECK(initialize(cx)); |
|
149 JS::RuntimeOptionsRef(cx).setBaseline(true) |
|
150 .setIon(true); |
|
151 |
|
152 EXEC("function g() { var p = new Prof(); p.test_fn(); }"); |
|
153 EXEC("function f() { g(); }"); |
|
154 EXEC("function e() { f(); }"); |
|
155 EXEC("function d() { e(); }"); |
|
156 EXEC("function c() { d(); }"); |
|
157 EXEC("function b() { c(); }"); |
|
158 EXEC("function a() { b(); }"); |
|
159 EXEC("function check() { var p = new Prof(); p.test_fn(); a(); }"); |
|
160 EXEC("function check2() { var p = new Prof(); p.test_fn2(); }"); |
|
161 |
|
162 reset(cx); |
|
163 { |
|
164 JS::RootedValue rval(cx); |
|
165 /* Make sure the stack resets and we have an entry for each stack */ |
|
166 CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), |
|
167 &rval)); |
|
168 CHECK(psize == 0); |
|
169 CHECK(max_stack >= 8); |
|
170 |
|
171 /* Make sure the stack resets and we added no new entries */ |
|
172 uint32_t cnt = cx->runtime()->spsProfiler.stringsCount(); |
|
173 max_stack = 0; |
|
174 CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), |
|
175 &rval)); |
|
176 CHECK(psize == 0); |
|
177 CHECK(cx->runtime()->spsProfiler.stringsCount() == cnt); |
|
178 CHECK(max_stack >= 8); |
|
179 } |
|
180 |
|
181 js::EnableRuntimeProfilingStack(cx->runtime(), false); |
|
182 js::SetRuntimeProfilingStack(cx->runtime(), pstack, &psize, 3); |
|
183 reset(cx); |
|
184 { |
|
185 /* Limit the size of the stack and make sure we don't overflow */ |
|
186 JS::RootedValue rval(cx); |
|
187 pstack[3].setLabel((char*) 1234); |
|
188 CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), |
|
189 &rval)); |
|
190 CHECK(psize == 0); |
|
191 CHECK(max_stack >= 8); |
|
192 CHECK((size_t) pstack[3].label() == 1234); |
|
193 } |
|
194 return true; |
|
195 } |
|
196 END_TEST(testProfileStrings_isCalledWithJIT) |
|
197 |
|
198 BEGIN_TEST(testProfileStrings_isCalledWhenError) |
|
199 { |
|
200 CHECK(initialize(cx)); |
|
201 JS::RuntimeOptionsRef(cx).setBaseline(true) |
|
202 .setIon(true); |
|
203 |
|
204 EXEC("function check2() { throw 'a'; }"); |
|
205 |
|
206 reset(cx); |
|
207 JS::ContextOptionsRef(cx).setDontReportUncaught(true); |
|
208 { |
|
209 JS::RootedValue rval(cx); |
|
210 /* Make sure the stack resets and we have an entry for each stack */ |
|
211 bool ok = JS_CallFunctionName(cx, global, "check2", JS::HandleValueArray::empty(), |
|
212 &rval); |
|
213 CHECK(!ok); |
|
214 CHECK(psize == 0); |
|
215 CHECK(cx->runtime()->spsProfiler.stringsCount() == 1); |
|
216 |
|
217 JS_ClearPendingException(cx); |
|
218 } |
|
219 return true; |
|
220 } |
|
221 END_TEST(testProfileStrings_isCalledWhenError) |
|
222 |
|
223 BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly) |
|
224 { |
|
225 CHECK(initialize(cx)); |
|
226 JS::RuntimeOptionsRef(cx).setBaseline(true) |
|
227 .setIon(true); |
|
228 |
|
229 EXEC("function b(p) { p.test_fn(); }"); |
|
230 EXEC("function a() { var p = new Prof(); p.enable(); b(p); }"); |
|
231 reset(cx); |
|
232 js::EnableRuntimeProfilingStack(cx->runtime(), false); |
|
233 { |
|
234 /* enable it in the middle of JS and make sure things check out */ |
|
235 JS::RootedValue rval(cx); |
|
236 JS_CallFunctionName(cx, global, "a", JS::HandleValueArray::empty(), &rval); |
|
237 CHECK(psize == 0); |
|
238 CHECK(max_stack >= 1); |
|
239 CHECK(cx->runtime()->spsProfiler.stringsCount() == 1); |
|
240 } |
|
241 |
|
242 EXEC("function d(p) { p.disable(); }"); |
|
243 EXEC("function c() { var p = new Prof(); d(p); }"); |
|
244 reset(cx); |
|
245 { |
|
246 /* now disable in the middle of js */ |
|
247 JS::RootedValue rval(cx); |
|
248 JS_CallFunctionName(cx, global, "c", JS::HandleValueArray::empty(), &rval); |
|
249 CHECK(psize == 0); |
|
250 } |
|
251 |
|
252 EXEC("function e() { var p = new Prof(); d(p); p.enable(); b(p); }"); |
|
253 reset(cx); |
|
254 { |
|
255 /* now disable in the middle of js, but re-enable before final exit */ |
|
256 JS::RootedValue rval(cx); |
|
257 JS_CallFunctionName(cx, global, "e", JS::HandleValueArray::empty(), &rval); |
|
258 CHECK(psize == 0); |
|
259 CHECK(max_stack >= 3); |
|
260 } |
|
261 |
|
262 EXEC("function h() { }"); |
|
263 EXEC("function g(p) { p.disable(); for (var i = 0; i < 100; i++) i++; }"); |
|
264 EXEC("function f() { g(new Prof()); }"); |
|
265 reset(cx); |
|
266 cx->runtime()->spsProfiler.enableSlowAssertions(false); |
|
267 { |
|
268 JS::RootedValue rval(cx); |
|
269 /* disable, and make sure that if we try to re-enter the JIT the pop |
|
270 * will still happen */ |
|
271 JS_CallFunctionName(cx, global, "f", JS::HandleValueArray::empty(), &rval); |
|
272 CHECK(psize == 0); |
|
273 } |
|
274 return true; |
|
275 } |
|
276 END_TEST(testProfileStrings_worksWhenEnabledOnTheFly) |