michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "perf/jsperf.h" michael@0: michael@0: #include "jscntxt.h" /* for error messages */ michael@0: #include "jsobj.h" /* for unwrapping without a context */ michael@0: michael@0: using namespace JS; michael@0: michael@0: // You cannot forward-declare a static object in C++, so instead michael@0: // we have to forward-declare the helper function that refers to it. michael@0: static PerfMeasurement* GetPM(JSContext* cx, JS::HandleValue value, const char* fname); michael@0: michael@0: // Property access michael@0: michael@0: #define GETTER(name) \ michael@0: static bool \ michael@0: pm_get_##name(JSContext* cx, unsigned argc, Value *vp) \ michael@0: { \ michael@0: CallArgs args = CallArgsFromVp(argc, vp); \ michael@0: PerfMeasurement* p = GetPM(cx, args.thisv(), #name); \ michael@0: if (!p) \ michael@0: return false; \ michael@0: args.rval().setNumber(double(p->name)); \ michael@0: return true; \ michael@0: } michael@0: michael@0: GETTER(cpu_cycles) michael@0: GETTER(instructions) michael@0: GETTER(cache_references) michael@0: GETTER(cache_misses) michael@0: GETTER(branch_instructions) michael@0: GETTER(branch_misses) michael@0: GETTER(bus_cycles) michael@0: GETTER(page_faults) michael@0: GETTER(major_page_faults) michael@0: GETTER(context_switches) michael@0: GETTER(cpu_migrations) michael@0: GETTER(eventsMeasured) michael@0: michael@0: #undef GETTER michael@0: michael@0: // Calls michael@0: michael@0: static bool michael@0: pm_start(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: PerfMeasurement* p = GetPM(cx, args.thisv(), "start"); michael@0: if (!p) michael@0: return false; michael@0: michael@0: p->start(); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: pm_stop(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: PerfMeasurement* p = GetPM(cx, args.thisv(), "stop"); michael@0: if (!p) michael@0: return false; michael@0: michael@0: p->stop(); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: pm_reset(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: PerfMeasurement* p = GetPM(cx, args.thisv(), "reset"); michael@0: if (!p) michael@0: return false; michael@0: michael@0: p->reset(); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: pm_canMeasureSomething(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: PerfMeasurement* p = GetPM(cx, args.thisv(), "canMeasureSomething"); michael@0: if (!p) michael@0: return false; michael@0: michael@0: args.rval().setBoolean(p->canMeasureSomething()); michael@0: return true; michael@0: } michael@0: michael@0: static const uint8_t PM_FATTRS = JSPROP_READONLY | JSPROP_PERMANENT; michael@0: static const JSFunctionSpec pm_fns[] = { michael@0: JS_FN("start", pm_start, 0, PM_FATTRS), michael@0: JS_FN("stop", pm_stop, 0, PM_FATTRS), michael@0: JS_FN("reset", pm_reset, 0, PM_FATTRS), michael@0: JS_FN("canMeasureSomething", pm_canMeasureSomething, 0, PM_FATTRS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const uint8_t PM_PATTRS = michael@0: JSPROP_ENUMERATE | JSPROP_PERMANENT; michael@0: michael@0: #define GETTER(name) \ michael@0: JS_PSG(#name, pm_get_##name, PM_PATTRS) michael@0: michael@0: static const JSPropertySpec pm_props[] = { michael@0: GETTER(cpu_cycles), michael@0: GETTER(instructions), michael@0: GETTER(cache_references), michael@0: GETTER(cache_misses), michael@0: GETTER(branch_instructions), michael@0: GETTER(branch_misses), michael@0: GETTER(bus_cycles), michael@0: GETTER(page_faults), michael@0: GETTER(major_page_faults), michael@0: GETTER(context_switches), michael@0: GETTER(cpu_migrations), michael@0: GETTER(eventsMeasured), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: #undef GETTER michael@0: michael@0: // If this were C++ these would be "static const" members. michael@0: michael@0: static const uint8_t PM_CATTRS = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT; michael@0: michael@0: #define CONSTANT(name) { #name, PerfMeasurement::name } michael@0: michael@0: static const struct pm_const { michael@0: const char *name; michael@0: PerfMeasurement::EventMask value; michael@0: } pm_consts[] = { michael@0: CONSTANT(CPU_CYCLES), michael@0: CONSTANT(INSTRUCTIONS), michael@0: CONSTANT(CACHE_REFERENCES), michael@0: CONSTANT(CACHE_MISSES), michael@0: CONSTANT(BRANCH_INSTRUCTIONS), michael@0: CONSTANT(BRANCH_MISSES), michael@0: CONSTANT(BUS_CYCLES), michael@0: CONSTANT(PAGE_FAULTS), michael@0: CONSTANT(MAJOR_PAGE_FAULTS), michael@0: CONSTANT(CONTEXT_SWITCHES), michael@0: CONSTANT(CPU_MIGRATIONS), michael@0: CONSTANT(ALL), michael@0: CONSTANT(NUM_MEASURABLE_EVENTS), michael@0: { 0, PerfMeasurement::EventMask(0) } michael@0: }; michael@0: michael@0: #undef CONSTANT michael@0: michael@0: static bool pm_construct(JSContext* cx, unsigned argc, jsval* vp); michael@0: static void pm_finalize(JSFreeOp* fop, JSObject* obj); michael@0: michael@0: static const JSClass pm_class = { michael@0: "PerfMeasurement", JSCLASS_HAS_PRIVATE, michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, pm_finalize michael@0: }; michael@0: michael@0: // Constructor and destructor michael@0: michael@0: static bool michael@0: pm_construct(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: uint32_t mask; michael@0: if (!args.hasDefined(0)) { michael@0: js_ReportMissingArg(cx, args.calleev(), 0); michael@0: return false; michael@0: } michael@0: if (!JS::ToUint32(cx, args[0], &mask)) michael@0: return false; michael@0: michael@0: JS::RootedObject obj(cx, JS_NewObjectForConstructor(cx, &pm_class, args)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: if (!JS_FreezeObject(cx, obj)) michael@0: return false; michael@0: michael@0: PerfMeasurement* p = cx->new_(PerfMeasurement::EventMask(mask)); michael@0: if (!p) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: michael@0: JS_SetPrivate(obj, p); michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: pm_finalize(JSFreeOp* fop, JSObject* obj) michael@0: { michael@0: js::FreeOp::get(fop)->delete_(static_cast(JS_GetPrivate(obj))); michael@0: } michael@0: michael@0: // Helpers (declared above) michael@0: michael@0: static PerfMeasurement* michael@0: GetPM(JSContext* cx, JS::HandleValue value, const char* fname) michael@0: { michael@0: if (!value.isObject()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT); michael@0: return nullptr; michael@0: } michael@0: RootedObject obj(cx, &value.toObject()); michael@0: PerfMeasurement* p = (PerfMeasurement*) michael@0: JS_GetInstancePrivate(cx, obj, &pm_class, nullptr); michael@0: if (p) michael@0: return p; michael@0: michael@0: // JS_GetInstancePrivate only sets an exception if its last argument michael@0: // is nonzero, so we have to do it by hand. michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, 0, JSMSG_INCOMPATIBLE_PROTO, michael@0: pm_class.name, fname, JS_GetClass(obj)->name); michael@0: return nullptr; michael@0: } michael@0: michael@0: namespace JS { michael@0: michael@0: JSObject* michael@0: RegisterPerfMeasurement(JSContext *cx, HandleObject globalArg) michael@0: { michael@0: RootedObject global(cx, globalArg); michael@0: RootedObject prototype(cx); michael@0: prototype = JS_InitClass(cx, global, js::NullPtr() /* parent */, michael@0: &pm_class, pm_construct, 1, michael@0: pm_props, pm_fns, 0, 0); michael@0: if (!prototype) michael@0: return 0; michael@0: michael@0: RootedObject ctor(cx); michael@0: ctor = JS_GetConstructor(cx, prototype); michael@0: if (!ctor) michael@0: return 0; michael@0: michael@0: for (const pm_const *c = pm_consts; c->name; c++) { michael@0: if (!JS_DefineProperty(cx, ctor, c->name, c->value, PM_CATTRS, michael@0: JS_PropertyStub, JS_StrictPropertyStub)) michael@0: return 0; michael@0: } michael@0: michael@0: if (!JS_FreezeObject(cx, prototype) || michael@0: !JS_FreezeObject(cx, ctor)) { michael@0: return 0; michael@0: } michael@0: michael@0: return prototype; michael@0: } michael@0: michael@0: PerfMeasurement* michael@0: ExtractPerfMeasurement(jsval wrapper) michael@0: { michael@0: if (JSVAL_IS_PRIMITIVE(wrapper)) michael@0: return 0; michael@0: michael@0: // This is what JS_GetInstancePrivate does internally. We can't michael@0: // call JS_anything from here, because we don't have a JSContext. michael@0: JSObject *obj = JSVAL_TO_OBJECT(wrapper); michael@0: if (obj->getClass() != js::Valueify(&pm_class)) michael@0: return 0; michael@0: michael@0: return (PerfMeasurement*) obj->getPrivate(); michael@0: } michael@0: michael@0: } // namespace JS