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