|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "perf/jsperf.h" |
|
7 |
|
8 #include "jscntxt.h" /* for error messages */ |
|
9 #include "jsobj.h" /* for unwrapping without a context */ |
|
10 |
|
11 using namespace JS; |
|
12 |
|
13 // You cannot forward-declare a static object in C++, so instead |
|
14 // we have to forward-declare the helper function that refers to it. |
|
15 static PerfMeasurement* GetPM(JSContext* cx, JS::HandleValue value, const char* fname); |
|
16 |
|
17 // Property access |
|
18 |
|
19 #define GETTER(name) \ |
|
20 static bool \ |
|
21 pm_get_##name(JSContext* cx, unsigned argc, Value *vp) \ |
|
22 { \ |
|
23 CallArgs args = CallArgsFromVp(argc, vp); \ |
|
24 PerfMeasurement* p = GetPM(cx, args.thisv(), #name); \ |
|
25 if (!p) \ |
|
26 return false; \ |
|
27 args.rval().setNumber(double(p->name)); \ |
|
28 return true; \ |
|
29 } |
|
30 |
|
31 GETTER(cpu_cycles) |
|
32 GETTER(instructions) |
|
33 GETTER(cache_references) |
|
34 GETTER(cache_misses) |
|
35 GETTER(branch_instructions) |
|
36 GETTER(branch_misses) |
|
37 GETTER(bus_cycles) |
|
38 GETTER(page_faults) |
|
39 GETTER(major_page_faults) |
|
40 GETTER(context_switches) |
|
41 GETTER(cpu_migrations) |
|
42 GETTER(eventsMeasured) |
|
43 |
|
44 #undef GETTER |
|
45 |
|
46 // Calls |
|
47 |
|
48 static bool |
|
49 pm_start(JSContext* cx, unsigned argc, jsval* vp) |
|
50 { |
|
51 CallArgs args = CallArgsFromVp(argc, vp); |
|
52 PerfMeasurement* p = GetPM(cx, args.thisv(), "start"); |
|
53 if (!p) |
|
54 return false; |
|
55 |
|
56 p->start(); |
|
57 args.rval().setUndefined(); |
|
58 return true; |
|
59 } |
|
60 |
|
61 static bool |
|
62 pm_stop(JSContext* cx, unsigned argc, jsval* vp) |
|
63 { |
|
64 CallArgs args = CallArgsFromVp(argc, vp); |
|
65 PerfMeasurement* p = GetPM(cx, args.thisv(), "stop"); |
|
66 if (!p) |
|
67 return false; |
|
68 |
|
69 p->stop(); |
|
70 args.rval().setUndefined(); |
|
71 return true; |
|
72 } |
|
73 |
|
74 static bool |
|
75 pm_reset(JSContext* cx, unsigned argc, jsval* vp) |
|
76 { |
|
77 CallArgs args = CallArgsFromVp(argc, vp); |
|
78 PerfMeasurement* p = GetPM(cx, args.thisv(), "reset"); |
|
79 if (!p) |
|
80 return false; |
|
81 |
|
82 p->reset(); |
|
83 args.rval().setUndefined(); |
|
84 return true; |
|
85 } |
|
86 |
|
87 static bool |
|
88 pm_canMeasureSomething(JSContext* cx, unsigned argc, jsval* vp) |
|
89 { |
|
90 CallArgs args = CallArgsFromVp(argc, vp); |
|
91 PerfMeasurement* p = GetPM(cx, args.thisv(), "canMeasureSomething"); |
|
92 if (!p) |
|
93 return false; |
|
94 |
|
95 args.rval().setBoolean(p->canMeasureSomething()); |
|
96 return true; |
|
97 } |
|
98 |
|
99 static const uint8_t PM_FATTRS = JSPROP_READONLY | JSPROP_PERMANENT; |
|
100 static const JSFunctionSpec pm_fns[] = { |
|
101 JS_FN("start", pm_start, 0, PM_FATTRS), |
|
102 JS_FN("stop", pm_stop, 0, PM_FATTRS), |
|
103 JS_FN("reset", pm_reset, 0, PM_FATTRS), |
|
104 JS_FN("canMeasureSomething", pm_canMeasureSomething, 0, PM_FATTRS), |
|
105 JS_FS_END |
|
106 }; |
|
107 |
|
108 static const uint8_t PM_PATTRS = |
|
109 JSPROP_ENUMERATE | JSPROP_PERMANENT; |
|
110 |
|
111 #define GETTER(name) \ |
|
112 JS_PSG(#name, pm_get_##name, PM_PATTRS) |
|
113 |
|
114 static const JSPropertySpec pm_props[] = { |
|
115 GETTER(cpu_cycles), |
|
116 GETTER(instructions), |
|
117 GETTER(cache_references), |
|
118 GETTER(cache_misses), |
|
119 GETTER(branch_instructions), |
|
120 GETTER(branch_misses), |
|
121 GETTER(bus_cycles), |
|
122 GETTER(page_faults), |
|
123 GETTER(major_page_faults), |
|
124 GETTER(context_switches), |
|
125 GETTER(cpu_migrations), |
|
126 GETTER(eventsMeasured), |
|
127 JS_PS_END |
|
128 }; |
|
129 |
|
130 #undef GETTER |
|
131 |
|
132 // If this were C++ these would be "static const" members. |
|
133 |
|
134 static const uint8_t PM_CATTRS = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT; |
|
135 |
|
136 #define CONSTANT(name) { #name, PerfMeasurement::name } |
|
137 |
|
138 static const struct pm_const { |
|
139 const char *name; |
|
140 PerfMeasurement::EventMask value; |
|
141 } pm_consts[] = { |
|
142 CONSTANT(CPU_CYCLES), |
|
143 CONSTANT(INSTRUCTIONS), |
|
144 CONSTANT(CACHE_REFERENCES), |
|
145 CONSTANT(CACHE_MISSES), |
|
146 CONSTANT(BRANCH_INSTRUCTIONS), |
|
147 CONSTANT(BRANCH_MISSES), |
|
148 CONSTANT(BUS_CYCLES), |
|
149 CONSTANT(PAGE_FAULTS), |
|
150 CONSTANT(MAJOR_PAGE_FAULTS), |
|
151 CONSTANT(CONTEXT_SWITCHES), |
|
152 CONSTANT(CPU_MIGRATIONS), |
|
153 CONSTANT(ALL), |
|
154 CONSTANT(NUM_MEASURABLE_EVENTS), |
|
155 { 0, PerfMeasurement::EventMask(0) } |
|
156 }; |
|
157 |
|
158 #undef CONSTANT |
|
159 |
|
160 static bool pm_construct(JSContext* cx, unsigned argc, jsval* vp); |
|
161 static void pm_finalize(JSFreeOp* fop, JSObject* obj); |
|
162 |
|
163 static const JSClass pm_class = { |
|
164 "PerfMeasurement", JSCLASS_HAS_PRIVATE, |
|
165 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
166 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, pm_finalize |
|
167 }; |
|
168 |
|
169 // Constructor and destructor |
|
170 |
|
171 static bool |
|
172 pm_construct(JSContext* cx, unsigned argc, jsval* vp) |
|
173 { |
|
174 CallArgs args = CallArgsFromVp(argc, vp); |
|
175 |
|
176 uint32_t mask; |
|
177 if (!args.hasDefined(0)) { |
|
178 js_ReportMissingArg(cx, args.calleev(), 0); |
|
179 return false; |
|
180 } |
|
181 if (!JS::ToUint32(cx, args[0], &mask)) |
|
182 return false; |
|
183 |
|
184 JS::RootedObject obj(cx, JS_NewObjectForConstructor(cx, &pm_class, args)); |
|
185 if (!obj) |
|
186 return false; |
|
187 |
|
188 if (!JS_FreezeObject(cx, obj)) |
|
189 return false; |
|
190 |
|
191 PerfMeasurement* p = cx->new_<PerfMeasurement>(PerfMeasurement::EventMask(mask)); |
|
192 if (!p) { |
|
193 JS_ReportOutOfMemory(cx); |
|
194 return false; |
|
195 } |
|
196 |
|
197 JS_SetPrivate(obj, p); |
|
198 args.rval().setObject(*obj); |
|
199 return true; |
|
200 } |
|
201 |
|
202 static void |
|
203 pm_finalize(JSFreeOp* fop, JSObject* obj) |
|
204 { |
|
205 js::FreeOp::get(fop)->delete_(static_cast<PerfMeasurement*>(JS_GetPrivate(obj))); |
|
206 } |
|
207 |
|
208 // Helpers (declared above) |
|
209 |
|
210 static PerfMeasurement* |
|
211 GetPM(JSContext* cx, JS::HandleValue value, const char* fname) |
|
212 { |
|
213 if (!value.isObject()) { |
|
214 JS_ReportErrorNumber(cx, js_GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT); |
|
215 return nullptr; |
|
216 } |
|
217 RootedObject obj(cx, &value.toObject()); |
|
218 PerfMeasurement* p = (PerfMeasurement*) |
|
219 JS_GetInstancePrivate(cx, obj, &pm_class, nullptr); |
|
220 if (p) |
|
221 return p; |
|
222 |
|
223 // JS_GetInstancePrivate only sets an exception if its last argument |
|
224 // is nonzero, so we have to do it by hand. |
|
225 JS_ReportErrorNumber(cx, js_GetErrorMessage, 0, JSMSG_INCOMPATIBLE_PROTO, |
|
226 pm_class.name, fname, JS_GetClass(obj)->name); |
|
227 return nullptr; |
|
228 } |
|
229 |
|
230 namespace JS { |
|
231 |
|
232 JSObject* |
|
233 RegisterPerfMeasurement(JSContext *cx, HandleObject globalArg) |
|
234 { |
|
235 RootedObject global(cx, globalArg); |
|
236 RootedObject prototype(cx); |
|
237 prototype = JS_InitClass(cx, global, js::NullPtr() /* parent */, |
|
238 &pm_class, pm_construct, 1, |
|
239 pm_props, pm_fns, 0, 0); |
|
240 if (!prototype) |
|
241 return 0; |
|
242 |
|
243 RootedObject ctor(cx); |
|
244 ctor = JS_GetConstructor(cx, prototype); |
|
245 if (!ctor) |
|
246 return 0; |
|
247 |
|
248 for (const pm_const *c = pm_consts; c->name; c++) { |
|
249 if (!JS_DefineProperty(cx, ctor, c->name, c->value, PM_CATTRS, |
|
250 JS_PropertyStub, JS_StrictPropertyStub)) |
|
251 return 0; |
|
252 } |
|
253 |
|
254 if (!JS_FreezeObject(cx, prototype) || |
|
255 !JS_FreezeObject(cx, ctor)) { |
|
256 return 0; |
|
257 } |
|
258 |
|
259 return prototype; |
|
260 } |
|
261 |
|
262 PerfMeasurement* |
|
263 ExtractPerfMeasurement(jsval wrapper) |
|
264 { |
|
265 if (JSVAL_IS_PRIMITIVE(wrapper)) |
|
266 return 0; |
|
267 |
|
268 // This is what JS_GetInstancePrivate does internally. We can't |
|
269 // call JS_anything from here, because we don't have a JSContext. |
|
270 JSObject *obj = JSVAL_TO_OBJECT(wrapper); |
|
271 if (obj->getClass() != js::Valueify(&pm_class)) |
|
272 return 0; |
|
273 |
|
274 return (PerfMeasurement*) obj->getPrivate(); |
|
275 } |
|
276 |
|
277 } // namespace JS |