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