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