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 GETTER(cpu_cycles)
GETTER(instructions)33 GETTER(instructions)
34 GETTER(cache_references)
35 GETTER(cache_misses)
36 GETTER(branch_instructions)
37 GETTER(branch_misses)
38 GETTER(bus_cycles)
39 GETTER(page_faults)
40 GETTER(major_page_faults)
41 GETTER(context_switches)
42 GETTER(cpu_migrations)
43 GETTER(eventsMeasured)
44
45 #undef GETTER
46
47 // Calls
48
49 static bool
50 pm_start(JSContext* cx, unsigned argc, Value* vp)
51 {
52 CallArgs args = CallArgsFromVp(argc, vp);
53 PerfMeasurement* p = GetPM(cx, args.thisv(), "start");
54 if (!p)
55 return false;
56
57 p->start();
58 args.rval().setUndefined();
59 return true;
60 }
61
62 static bool
pm_stop(JSContext * cx,unsigned argc,Value * vp)63 pm_stop(JSContext* cx, unsigned argc, Value* vp)
64 {
65 CallArgs args = CallArgsFromVp(argc, vp);
66 PerfMeasurement* p = GetPM(cx, args.thisv(), "stop");
67 if (!p)
68 return false;
69
70 p->stop();
71 args.rval().setUndefined();
72 return true;
73 }
74
75 static bool
pm_reset(JSContext * cx,unsigned argc,Value * vp)76 pm_reset(JSContext* cx, unsigned argc, Value* vp)
77 {
78 CallArgs args = CallArgsFromVp(argc, vp);
79 PerfMeasurement* p = GetPM(cx, args.thisv(), "reset");
80 if (!p)
81 return false;
82
83 p->reset();
84 args.rval().setUndefined();
85 return true;
86 }
87
88 static bool
pm_canMeasureSomething(JSContext * cx,unsigned argc,Value * vp)89 pm_canMeasureSomething(JSContext* cx, unsigned argc, Value* vp)
90 {
91 CallArgs args = CallArgsFromVp(argc, vp);
92 PerfMeasurement* p = GetPM(cx, args.thisv(), "canMeasureSomething");
93 if (!p)
94 return false;
95
96 args.rval().setBoolean(p->canMeasureSomething());
97 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 pm_finalize
171 };
172
173 static const JSClass pm_class = {
174 "PerfMeasurement",
175 JSCLASS_HAS_PRIVATE |
176 JSCLASS_FOREGROUND_FINALIZE,
177 &pm_classOps
178 };
179
180 // Constructor and destructor
181
182 static bool
pm_construct(JSContext * cx,unsigned argc,Value * vp)183 pm_construct(JSContext* cx, unsigned argc, Value* vp)
184 {
185 CallArgs args = CallArgsFromVp(argc, vp);
186
187 uint32_t mask;
188 if (!args.hasDefined(0)) {
189 ReportMissingArg(cx, args.calleev(), 0);
190 return false;
191 }
192 if (!JS::ToUint32(cx, args[0], &mask))
193 return false;
194
195 JS::RootedObject obj(cx, JS_NewObjectForConstructor(cx, &pm_class, args));
196 if (!obj)
197 return false;
198
199 if (!JS_FreezeObject(cx, obj))
200 return false;
201
202 PerfMeasurement* p = cx->new_<PerfMeasurement>(PerfMeasurement::EventMask(mask));
203 if (!p) {
204 JS_ReportOutOfMemory(cx);
205 return false;
206 }
207
208 JS_SetPrivate(obj, p);
209 args.rval().setObject(*obj);
210 return true;
211 }
212
213 static void
pm_finalize(JSFreeOp * fop,JSObject * obj)214 pm_finalize(JSFreeOp* fop, JSObject* obj)
215 {
216 js::FreeOp::get(fop)->delete_(static_cast<PerfMeasurement*>(JS_GetPrivate(obj)));
217 }
218
219 // Helpers (declared above)
220
221 static PerfMeasurement*
GetPM(JSContext * cx,JS::HandleValue value,const char * fname)222 GetPM(JSContext* cx, JS::HandleValue value, const char* fname)
223 {
224 if (!value.isObject()) {
225 UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr);
226 if (!bytes)
227 return nullptr;
228 JS_ReportErrorNumberLatin1(cx, GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
229 return nullptr;
230 }
231 RootedObject obj(cx, &value.toObject());
232 PerfMeasurement* p = (PerfMeasurement*)
233 JS_GetInstancePrivate(cx, obj, &pm_class, nullptr);
234 if (p)
235 return p;
236
237 // JS_GetInstancePrivate only sets an exception if its last argument
238 // is nonzero, so we have to do it by hand.
239 JS_ReportErrorNumberASCII(cx, GetErrorMessage, 0, JSMSG_INCOMPATIBLE_PROTO,
240 pm_class.name, fname, JS_GetClass(obj)->name);
241 return nullptr;
242 }
243
244 namespace JS {
245
246 JSObject*
RegisterPerfMeasurement(JSContext * cx,HandleObject globalArg)247 RegisterPerfMeasurement(JSContext* cx, HandleObject globalArg)
248 {
249 static const uint8_t PM_CATTRS = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT;
250
251 RootedObject global(cx, globalArg);
252 RootedObject prototype(cx);
253 prototype = JS_InitClass(cx, global, nullptr /* parent */,
254 &pm_class, pm_construct, 1,
255 pm_props, pm_fns, 0, 0);
256 if (!prototype)
257 return 0;
258
259 RootedObject ctor(cx);
260 ctor = JS_GetConstructor(cx, prototype);
261 if (!ctor)
262 return 0;
263
264 for (const pm_const* c = pm_consts; c->name; c++) {
265 if (!JS_DefineProperty(cx, ctor, c->name, c->value, PM_CATTRS,
266 JS_STUBGETTER, JS_STUBSETTER))
267 return 0;
268 }
269
270 if (!JS_FreezeObject(cx, prototype) ||
271 !JS_FreezeObject(cx, ctor)) {
272 return 0;
273 }
274
275 return prototype;
276 }
277
278 PerfMeasurement*
ExtractPerfMeasurement(const Value & wrapper)279 ExtractPerfMeasurement(const Value& wrapper)
280 {
281 if (wrapper.isPrimitive())
282 return 0;
283
284 // This is what JS_GetInstancePrivate does internally. We can't
285 // call JS_anything from here, because we don't have a JSContext.
286 JSObject* obj = wrapper.toObjectOrNull();
287 if (obj->getClass() != js::Valueify(&pm_class))
288 return 0;
289
290 return (PerfMeasurement*) obj->as<js::NativeObject>().getPrivate();
291 }
292
293 } // namespace JS
294