1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/perf/jsperf.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,277 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "perf/jsperf.h" 1.10 + 1.11 +#include "jscntxt.h" /* for error messages */ 1.12 +#include "jsobj.h" /* for unwrapping without a context */ 1.13 + 1.14 +using namespace JS; 1.15 + 1.16 +// You cannot forward-declare a static object in C++, so instead 1.17 +// we have to forward-declare the helper function that refers to it. 1.18 +static PerfMeasurement* GetPM(JSContext* cx, JS::HandleValue value, const char* fname); 1.19 + 1.20 +// Property access 1.21 + 1.22 +#define GETTER(name) \ 1.23 + static bool \ 1.24 + pm_get_##name(JSContext* cx, unsigned argc, Value *vp) \ 1.25 + { \ 1.26 + CallArgs args = CallArgsFromVp(argc, vp); \ 1.27 + PerfMeasurement* p = GetPM(cx, args.thisv(), #name); \ 1.28 + if (!p) \ 1.29 + return false; \ 1.30 + args.rval().setNumber(double(p->name)); \ 1.31 + return true; \ 1.32 + } 1.33 + 1.34 +GETTER(cpu_cycles) 1.35 +GETTER(instructions) 1.36 +GETTER(cache_references) 1.37 +GETTER(cache_misses) 1.38 +GETTER(branch_instructions) 1.39 +GETTER(branch_misses) 1.40 +GETTER(bus_cycles) 1.41 +GETTER(page_faults) 1.42 +GETTER(major_page_faults) 1.43 +GETTER(context_switches) 1.44 +GETTER(cpu_migrations) 1.45 +GETTER(eventsMeasured) 1.46 + 1.47 +#undef GETTER 1.48 + 1.49 +// Calls 1.50 + 1.51 +static bool 1.52 +pm_start(JSContext* cx, unsigned argc, jsval* vp) 1.53 +{ 1.54 + CallArgs args = CallArgsFromVp(argc, vp); 1.55 + PerfMeasurement* p = GetPM(cx, args.thisv(), "start"); 1.56 + if (!p) 1.57 + return false; 1.58 + 1.59 + p->start(); 1.60 + args.rval().setUndefined(); 1.61 + return true; 1.62 +} 1.63 + 1.64 +static bool 1.65 +pm_stop(JSContext* cx, unsigned argc, jsval* vp) 1.66 +{ 1.67 + CallArgs args = CallArgsFromVp(argc, vp); 1.68 + PerfMeasurement* p = GetPM(cx, args.thisv(), "stop"); 1.69 + if (!p) 1.70 + return false; 1.71 + 1.72 + p->stop(); 1.73 + args.rval().setUndefined(); 1.74 + return true; 1.75 +} 1.76 + 1.77 +static bool 1.78 +pm_reset(JSContext* cx, unsigned argc, jsval* vp) 1.79 +{ 1.80 + CallArgs args = CallArgsFromVp(argc, vp); 1.81 + PerfMeasurement* p = GetPM(cx, args.thisv(), "reset"); 1.82 + if (!p) 1.83 + return false; 1.84 + 1.85 + p->reset(); 1.86 + args.rval().setUndefined(); 1.87 + return true; 1.88 +} 1.89 + 1.90 +static bool 1.91 +pm_canMeasureSomething(JSContext* cx, unsigned argc, jsval* vp) 1.92 +{ 1.93 + CallArgs args = CallArgsFromVp(argc, vp); 1.94 + PerfMeasurement* p = GetPM(cx, args.thisv(), "canMeasureSomething"); 1.95 + if (!p) 1.96 + return false; 1.97 + 1.98 + args.rval().setBoolean(p->canMeasureSomething()); 1.99 + return true; 1.100 +} 1.101 + 1.102 +static const uint8_t PM_FATTRS = JSPROP_READONLY | JSPROP_PERMANENT; 1.103 +static const JSFunctionSpec pm_fns[] = { 1.104 + JS_FN("start", pm_start, 0, PM_FATTRS), 1.105 + JS_FN("stop", pm_stop, 0, PM_FATTRS), 1.106 + JS_FN("reset", pm_reset, 0, PM_FATTRS), 1.107 + JS_FN("canMeasureSomething", pm_canMeasureSomething, 0, PM_FATTRS), 1.108 + JS_FS_END 1.109 +}; 1.110 + 1.111 +static const uint8_t PM_PATTRS = 1.112 + JSPROP_ENUMERATE | JSPROP_PERMANENT; 1.113 + 1.114 +#define GETTER(name) \ 1.115 + JS_PSG(#name, pm_get_##name, PM_PATTRS) 1.116 + 1.117 +static const JSPropertySpec pm_props[] = { 1.118 + GETTER(cpu_cycles), 1.119 + GETTER(instructions), 1.120 + GETTER(cache_references), 1.121 + GETTER(cache_misses), 1.122 + GETTER(branch_instructions), 1.123 + GETTER(branch_misses), 1.124 + GETTER(bus_cycles), 1.125 + GETTER(page_faults), 1.126 + GETTER(major_page_faults), 1.127 + GETTER(context_switches), 1.128 + GETTER(cpu_migrations), 1.129 + GETTER(eventsMeasured), 1.130 + JS_PS_END 1.131 +}; 1.132 + 1.133 +#undef GETTER 1.134 + 1.135 +// If this were C++ these would be "static const" members. 1.136 + 1.137 +static const uint8_t PM_CATTRS = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT; 1.138 + 1.139 +#define CONSTANT(name) { #name, PerfMeasurement::name } 1.140 + 1.141 +static const struct pm_const { 1.142 + const char *name; 1.143 + PerfMeasurement::EventMask value; 1.144 +} pm_consts[] = { 1.145 + CONSTANT(CPU_CYCLES), 1.146 + CONSTANT(INSTRUCTIONS), 1.147 + CONSTANT(CACHE_REFERENCES), 1.148 + CONSTANT(CACHE_MISSES), 1.149 + CONSTANT(BRANCH_INSTRUCTIONS), 1.150 + CONSTANT(BRANCH_MISSES), 1.151 + CONSTANT(BUS_CYCLES), 1.152 + CONSTANT(PAGE_FAULTS), 1.153 + CONSTANT(MAJOR_PAGE_FAULTS), 1.154 + CONSTANT(CONTEXT_SWITCHES), 1.155 + CONSTANT(CPU_MIGRATIONS), 1.156 + CONSTANT(ALL), 1.157 + CONSTANT(NUM_MEASURABLE_EVENTS), 1.158 + { 0, PerfMeasurement::EventMask(0) } 1.159 +}; 1.160 + 1.161 +#undef CONSTANT 1.162 + 1.163 +static bool pm_construct(JSContext* cx, unsigned argc, jsval* vp); 1.164 +static void pm_finalize(JSFreeOp* fop, JSObject* obj); 1.165 + 1.166 +static const JSClass pm_class = { 1.167 + "PerfMeasurement", JSCLASS_HAS_PRIVATE, 1.168 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.169 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, pm_finalize 1.170 +}; 1.171 + 1.172 +// Constructor and destructor 1.173 + 1.174 +static bool 1.175 +pm_construct(JSContext* cx, unsigned argc, jsval* vp) 1.176 +{ 1.177 + CallArgs args = CallArgsFromVp(argc, vp); 1.178 + 1.179 + uint32_t mask; 1.180 + if (!args.hasDefined(0)) { 1.181 + js_ReportMissingArg(cx, args.calleev(), 0); 1.182 + return false; 1.183 + } 1.184 + if (!JS::ToUint32(cx, args[0], &mask)) 1.185 + return false; 1.186 + 1.187 + JS::RootedObject obj(cx, JS_NewObjectForConstructor(cx, &pm_class, args)); 1.188 + if (!obj) 1.189 + return false; 1.190 + 1.191 + if (!JS_FreezeObject(cx, obj)) 1.192 + return false; 1.193 + 1.194 + PerfMeasurement* p = cx->new_<PerfMeasurement>(PerfMeasurement::EventMask(mask)); 1.195 + if (!p) { 1.196 + JS_ReportOutOfMemory(cx); 1.197 + return false; 1.198 + } 1.199 + 1.200 + JS_SetPrivate(obj, p); 1.201 + args.rval().setObject(*obj); 1.202 + return true; 1.203 +} 1.204 + 1.205 +static void 1.206 +pm_finalize(JSFreeOp* fop, JSObject* obj) 1.207 +{ 1.208 + js::FreeOp::get(fop)->delete_(static_cast<PerfMeasurement*>(JS_GetPrivate(obj))); 1.209 +} 1.210 + 1.211 +// Helpers (declared above) 1.212 + 1.213 +static PerfMeasurement* 1.214 +GetPM(JSContext* cx, JS::HandleValue value, const char* fname) 1.215 +{ 1.216 + if (!value.isObject()) { 1.217 + JS_ReportErrorNumber(cx, js_GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT); 1.218 + return nullptr; 1.219 + } 1.220 + RootedObject obj(cx, &value.toObject()); 1.221 + PerfMeasurement* p = (PerfMeasurement*) 1.222 + JS_GetInstancePrivate(cx, obj, &pm_class, nullptr); 1.223 + if (p) 1.224 + return p; 1.225 + 1.226 + // JS_GetInstancePrivate only sets an exception if its last argument 1.227 + // is nonzero, so we have to do it by hand. 1.228 + JS_ReportErrorNumber(cx, js_GetErrorMessage, 0, JSMSG_INCOMPATIBLE_PROTO, 1.229 + pm_class.name, fname, JS_GetClass(obj)->name); 1.230 + return nullptr; 1.231 +} 1.232 + 1.233 +namespace JS { 1.234 + 1.235 +JSObject* 1.236 +RegisterPerfMeasurement(JSContext *cx, HandleObject globalArg) 1.237 +{ 1.238 + RootedObject global(cx, globalArg); 1.239 + RootedObject prototype(cx); 1.240 + prototype = JS_InitClass(cx, global, js::NullPtr() /* parent */, 1.241 + &pm_class, pm_construct, 1, 1.242 + pm_props, pm_fns, 0, 0); 1.243 + if (!prototype) 1.244 + return 0; 1.245 + 1.246 + RootedObject ctor(cx); 1.247 + ctor = JS_GetConstructor(cx, prototype); 1.248 + if (!ctor) 1.249 + return 0; 1.250 + 1.251 + for (const pm_const *c = pm_consts; c->name; c++) { 1.252 + if (!JS_DefineProperty(cx, ctor, c->name, c->value, PM_CATTRS, 1.253 + JS_PropertyStub, JS_StrictPropertyStub)) 1.254 + return 0; 1.255 + } 1.256 + 1.257 + if (!JS_FreezeObject(cx, prototype) || 1.258 + !JS_FreezeObject(cx, ctor)) { 1.259 + return 0; 1.260 + } 1.261 + 1.262 + return prototype; 1.263 +} 1.264 + 1.265 +PerfMeasurement* 1.266 +ExtractPerfMeasurement(jsval wrapper) 1.267 +{ 1.268 + if (JSVAL_IS_PRIMITIVE(wrapper)) 1.269 + return 0; 1.270 + 1.271 + // This is what JS_GetInstancePrivate does internally. We can't 1.272 + // call JS_anything from here, because we don't have a JSContext. 1.273 + JSObject *obj = JSVAL_TO_OBJECT(wrapper); 1.274 + if (obj->getClass() != js::Valueify(&pm_class)) 1.275 + return 0; 1.276 + 1.277 + return (PerfMeasurement*) obj->getPrivate(); 1.278 +} 1.279 + 1.280 +} // namespace JS