mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-11-02 12:37:31 +09:00
293 lines
7.9 KiB
C++
293 lines
7.9 KiB
C++
/* -*- 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 "perf/jsperf.h"
|
|
|
|
#include "jscntxt.h" /* for error messages */
|
|
#include "jsobj.h" /* for unwrapping without a context */
|
|
|
|
using namespace js;
|
|
using JS::PerfMeasurement;
|
|
|
|
// You cannot forward-declare a static object in C++, so instead
|
|
// we have to forward-declare the helper function that refers to it.
|
|
static PerfMeasurement* GetPM(JSContext* cx, JS::HandleValue value, const char* fname);
|
|
|
|
// Property access
|
|
|
|
#define GETTER(name) \
|
|
static bool \
|
|
pm_get_##name(JSContext* cx, unsigned argc, Value* vp) \
|
|
{ \
|
|
CallArgs args = CallArgsFromVp(argc, vp); \
|
|
PerfMeasurement* p = GetPM(cx, args.thisv(), #name); \
|
|
if (!p) \
|
|
return false; \
|
|
args.rval().setNumber(double(p->name)); \
|
|
return 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 bool
|
|
pm_start(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
PerfMeasurement* p = GetPM(cx, args.thisv(), "start");
|
|
if (!p)
|
|
return false;
|
|
|
|
p->start();
|
|
args.rval().setUndefined();
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
pm_stop(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
PerfMeasurement* p = GetPM(cx, args.thisv(), "stop");
|
|
if (!p)
|
|
return false;
|
|
|
|
p->stop();
|
|
args.rval().setUndefined();
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
pm_reset(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
PerfMeasurement* p = GetPM(cx, args.thisv(), "reset");
|
|
if (!p)
|
|
return false;
|
|
|
|
p->reset();
|
|
args.rval().setUndefined();
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
pm_canMeasureSomething(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
PerfMeasurement* p = GetPM(cx, args.thisv(), "canMeasureSomething");
|
|
if (!p)
|
|
return false;
|
|
|
|
args.rval().setBoolean(p->canMeasureSomething());
|
|
return true;
|
|
}
|
|
|
|
static 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
|
|
};
|
|
|
|
static const uint8_t PM_PATTRS =
|
|
JSPROP_ENUMERATE | JSPROP_PERMANENT;
|
|
|
|
#define GETTER(name) \
|
|
JS_PSG(#name, pm_get_##name, PM_PATTRS)
|
|
|
|
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),
|
|
JS_PS_END
|
|
};
|
|
|
|
#undef GETTER
|
|
|
|
// If this were C++ these would be "static const" members.
|
|
|
|
#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 bool pm_construct(JSContext* cx, unsigned argc, Value* vp);
|
|
static void pm_finalize(JSFreeOp* fop, JSObject* obj);
|
|
|
|
static const JSClassOps pm_classOps = {
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
pm_finalize
|
|
};
|
|
|
|
static const JSClass pm_class = {
|
|
"PerfMeasurement",
|
|
JSCLASS_HAS_PRIVATE |
|
|
JSCLASS_FOREGROUND_FINALIZE,
|
|
&pm_classOps
|
|
};
|
|
|
|
// Constructor and destructor
|
|
|
|
static bool
|
|
pm_construct(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
|
|
uint32_t mask;
|
|
if (!args.hasDefined(0)) {
|
|
ReportMissingArg(cx, args.calleev(), 0);
|
|
return false;
|
|
}
|
|
if (!JS::ToUint32(cx, args[0], &mask))
|
|
return false;
|
|
|
|
JS::RootedObject obj(cx, JS_NewObjectForConstructor(cx, &pm_class, args));
|
|
if (!obj)
|
|
return false;
|
|
|
|
if (!JS_FreezeObject(cx, obj))
|
|
return false;
|
|
|
|
PerfMeasurement* p = cx->new_<PerfMeasurement>(PerfMeasurement::EventMask(mask));
|
|
if (!p) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return false;
|
|
}
|
|
|
|
JS_SetPrivate(obj, p);
|
|
args.rval().setObject(*obj);
|
|
return 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::HandleValue value, const char* fname)
|
|
{
|
|
if (!value.isObject()) {
|
|
UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr);
|
|
if (!bytes)
|
|
return nullptr;
|
|
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
|
|
return nullptr;
|
|
}
|
|
RootedObject obj(cx, &value.toObject());
|
|
PerfMeasurement* p = (PerfMeasurement*)
|
|
JS_GetInstancePrivate(cx, obj, &pm_class, nullptr);
|
|
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_ReportErrorNumberASCII(cx, GetErrorMessage, 0, JSMSG_INCOMPATIBLE_PROTO,
|
|
pm_class.name, fname, JS_GetClass(obj)->name);
|
|
return nullptr;
|
|
}
|
|
|
|
namespace JS {
|
|
|
|
JSObject*
|
|
RegisterPerfMeasurement(JSContext* cx, HandleObject globalArg)
|
|
{
|
|
static const uint8_t PM_CATTRS = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT;
|
|
|
|
RootedObject global(cx, globalArg);
|
|
RootedObject prototype(cx);
|
|
prototype = JS_InitClass(cx, global, nullptr /* 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, c->value, PM_CATTRS,
|
|
JS_STUBGETTER, JS_STUBSETTER))
|
|
return 0;
|
|
}
|
|
|
|
if (!JS_FreezeObject(cx, prototype) ||
|
|
!JS_FreezeObject(cx, ctor)) {
|
|
return 0;
|
|
}
|
|
|
|
return prototype;
|
|
}
|
|
|
|
PerfMeasurement*
|
|
ExtractPerfMeasurement(const Value& wrapper)
|
|
{
|
|
if (wrapper.isPrimitive())
|
|
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 = wrapper.toObjectOrNull();
|
|
if (obj->getClass() != js::Valueify(&pm_class))
|
|
return 0;
|
|
|
|
return (PerfMeasurement*) obj->as<js::NativeObject>().getPrivate();
|
|
}
|
|
|
|
} // namespace JS
|