summaryrefslogtreecommitdiff
path: root/js/src/perf
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/perf
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloaduxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/perf')
-rw-r--r--js/src/perf/jsperf.cpp293
-rw-r--r--js/src/perf/jsperf.h133
-rw-r--r--js/src/perf/pm_linux.cpp310
-rw-r--r--js/src/perf/pm_stub.cpp63
4 files changed, 799 insertions, 0 deletions
diff --git a/js/src/perf/jsperf.cpp b/js/src/perf/jsperf.cpp
new file mode 100644
index 0000000000..ca2db4458c
--- /dev/null
+++ b/js/src/perf/jsperf.cpp
@@ -0,0 +1,293 @@
+/* -*- 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
diff --git a/js/src/perf/jsperf.h b/js/src/perf/jsperf.h
new file mode 100644
index 0000000000..b8f2909ad2
--- /dev/null
+++ b/js/src/perf/jsperf.h
@@ -0,0 +1,133 @@
+/* -*- 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/. */
+
+#ifndef perf_jsperf_h
+#define perf_jsperf_h
+
+#include "jstypes.h"
+
+#include "js/TypeDecls.h"
+#include "js/Utility.h"
+
+namespace JS {
+
+/*
+ * JS::PerfMeasurement is a generic way to access detailed performance
+ * measurement APIs provided by your operating system. The details of
+ * exactly how this works and what can be measured are highly
+ * system-specific, but this interface is (one hopes) implementable
+ * on top of all of them.
+ *
+ * To use this API, create a PerfMeasurement object, passing its
+ * constructor a bitmask indicating which events you are interested
+ * in. Thereafter, Start() zeroes all counters and starts timing;
+ * Stop() stops timing again; and the counters for the events you
+ * requested are available as data values after calling Stop(). The
+ * object may be reused for many measurements.
+ */
+class JS_FRIEND_API(PerfMeasurement)
+{
+ protected:
+ // Implementation-specific data, if any.
+ void* impl;
+
+ public:
+ /*
+ * Events that may be measured. Taken directly from the list of
+ * "generalized hardware performance event types" in the Linux
+ * perf_event API, plus some of the "software events".
+ */
+ enum EventMask {
+ CPU_CYCLES = 0x00000001,
+ INSTRUCTIONS = 0x00000002,
+ CACHE_REFERENCES = 0x00000004,
+ CACHE_MISSES = 0x00000008,
+ BRANCH_INSTRUCTIONS = 0x00000010,
+ BRANCH_MISSES = 0x00000020,
+ BUS_CYCLES = 0x00000040,
+ PAGE_FAULTS = 0x00000080,
+ MAJOR_PAGE_FAULTS = 0x00000100,
+ CONTEXT_SWITCHES = 0x00000200,
+ CPU_MIGRATIONS = 0x00000400,
+
+ ALL = 0x000007ff,
+ NUM_MEASURABLE_EVENTS = 11
+ };
+
+ /*
+ * Bitmask of events that will be measured when this object is
+ * active (between Start() and Stop()). This may differ from the
+ * bitmask passed to the constructor if the platform does not
+ * support measuring all of the requested events.
+ */
+ const EventMask eventsMeasured;
+
+ /*
+ * Counters for each measurable event.
+ * Immediately after one of these objects is created, all of the
+ * counters for enabled events will be zero, and all of the
+ * counters for disabled events will be uint64_t(-1).
+ */
+ uint64_t cpu_cycles;
+ uint64_t instructions;
+ uint64_t cache_references;
+ uint64_t cache_misses;
+ uint64_t branch_instructions;
+ uint64_t branch_misses;
+ uint64_t bus_cycles;
+ uint64_t page_faults;
+ uint64_t major_page_faults;
+ uint64_t context_switches;
+ uint64_t cpu_migrations;
+
+ /*
+ * Prepare to measure the indicated set of events. If not all of
+ * the requested events can be measured on the current platform,
+ * then the eventsMeasured bitmask will only include the subset of
+ * |toMeasure| corresponding to the events that can be measured.
+ */
+ explicit PerfMeasurement(EventMask toMeasure);
+
+ /* Done with this set of measurements, tear down OS-level state. */
+ ~PerfMeasurement();
+
+ /* Start a measurement cycle. */
+ void start();
+
+ /*
+ * End a measurement cycle, and for each enabled counter, add the
+ * number of measured events of that type to the appropriate
+ * visible variable.
+ */
+ void stop();
+
+ /* Reset all enabled counters to zero. */
+ void reset();
+
+ /*
+ * True if this platform supports measuring _something_, i.e. it's
+ * not using the stub implementation.
+ */
+ static bool canMeasureSomething();
+};
+
+/* Inject a Javascript wrapper around the above C++ class into the
+ * Javascript object passed as an argument (this will normally be a
+ * global object). The JS-visible API is identical to the C++ API.
+ */
+extern JS_FRIEND_API(JSObject*)
+ RegisterPerfMeasurement(JSContext* cx, JS::HandleObject global);
+
+/*
+ * Given a Value which contains an instance of the aforementioned
+ * wrapper class, extract the C++ object. Returns nullptr if the
+ * Value is not an instance of the wrapper.
+ */
+extern JS_FRIEND_API(PerfMeasurement*)
+ ExtractPerfMeasurement(const Value& wrapper);
+
+} // namespace JS
+
+#endif /* perf_jsperf_h */
diff --git a/js/src/perf/pm_linux.cpp b/js/src/perf/pm_linux.cpp
new file mode 100644
index 0000000000..54ba0c3631
--- /dev/null
+++ b/js/src/perf/pm_linux.cpp
@@ -0,0 +1,310 @@
+/* -*- 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/. */
+
+/* This variant of nsIPerfMeasurement uses the perf_event interface
+ * added in Linux 2.6.31. We key compilation of this file off the
+ * existence of <linux/perf_event.h>.
+ */
+
+#include <errno.h>
+#include <linux/perf_event.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "perf/jsperf.h"
+
+using namespace js;
+
+// As of July 2010, this system call has not been added to the
+// C library, so we have to provide our own wrapper function.
+// If this code runs on a kernel that does not implement the
+// system call (2.6.30 or older) nothing unpredictable will
+// happen - it will just always fail and return -1.
+static int
+sys_perf_event_open(struct perf_event_attr* attr, pid_t pid, int cpu,
+ int group_fd, unsigned long flags)
+{
+ return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
+}
+
+namespace {
+
+using JS::PerfMeasurement;
+typedef PerfMeasurement::EventMask EventMask;
+
+// Additional state required by this implementation.
+struct Impl
+{
+ // Each active counter corresponds to an open file descriptor.
+ int f_cpu_cycles;
+ int f_instructions;
+ int f_cache_references;
+ int f_cache_misses;
+ int f_branch_instructions;
+ int f_branch_misses;
+ int f_bus_cycles;
+ int f_page_faults;
+ int f_major_page_faults;
+ int f_context_switches;
+ int f_cpu_migrations;
+
+ // Counter group leader, for Start and Stop.
+ int group_leader;
+
+ // Whether counters are running.
+ bool running;
+
+ Impl();
+ ~Impl();
+
+ EventMask init(EventMask toMeasure);
+ void start();
+ void stop(PerfMeasurement* counters);
+};
+
+// Mapping from our event bitmask to codes passed into the kernel, and
+// to fields in the PerfMeasurement and PerfMeasurement::impl structures.
+static const struct
+{
+ EventMask bit;
+ uint32_t type;
+ uint32_t config;
+ uint64_t PerfMeasurement::* counter;
+ int Impl::* fd;
+} kSlots[PerfMeasurement::NUM_MEASURABLE_EVENTS] = {
+#define HW(mask, constant, fieldname) \
+ { PerfMeasurement::mask, PERF_TYPE_HARDWARE, PERF_COUNT_HW_##constant, \
+ &PerfMeasurement::fieldname, &Impl::f_##fieldname }
+#define SW(mask, constant, fieldname) \
+ { PerfMeasurement::mask, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_##constant, \
+ &PerfMeasurement::fieldname, &Impl::f_##fieldname }
+
+ HW(CPU_CYCLES, CPU_CYCLES, cpu_cycles),
+ HW(INSTRUCTIONS, INSTRUCTIONS, instructions),
+ HW(CACHE_REFERENCES, CACHE_REFERENCES, cache_references),
+ HW(CACHE_MISSES, CACHE_MISSES, cache_misses),
+ HW(BRANCH_INSTRUCTIONS, BRANCH_INSTRUCTIONS, branch_instructions),
+ HW(BRANCH_MISSES, BRANCH_MISSES, branch_misses),
+ HW(BUS_CYCLES, BUS_CYCLES, bus_cycles),
+ SW(PAGE_FAULTS, PAGE_FAULTS, page_faults),
+ SW(MAJOR_PAGE_FAULTS, PAGE_FAULTS_MAJ, major_page_faults),
+ SW(CONTEXT_SWITCHES, CONTEXT_SWITCHES, context_switches),
+ SW(CPU_MIGRATIONS, CPU_MIGRATIONS, cpu_migrations),
+
+#undef HW
+#undef SW
+};
+
+Impl::Impl()
+ : f_cpu_cycles(-1),
+ f_instructions(-1),
+ f_cache_references(-1),
+ f_cache_misses(-1),
+ f_branch_instructions(-1),
+ f_branch_misses(-1),
+ f_bus_cycles(-1),
+ f_page_faults(-1),
+ f_major_page_faults(-1),
+ f_context_switches(-1),
+ f_cpu_migrations(-1),
+ group_leader(-1),
+ running(false)
+{
+}
+
+Impl::~Impl()
+{
+ // Close all active counter descriptors. Take care to do the group
+ // leader last (this may not be necessary, but it's unclear what
+ // happens if you close the group leader out from under a group).
+ for (const auto& slot : kSlots) {
+ int fd = this->*(slot.fd);
+ if (fd != -1 && fd != group_leader)
+ close(fd);
+ }
+
+ if (group_leader != -1)
+ close(group_leader);
+}
+
+EventMask
+Impl::init(EventMask toMeasure)
+{
+ MOZ_ASSERT(group_leader == -1);
+ if (!toMeasure)
+ return EventMask(0);
+
+ EventMask measured = EventMask(0);
+ struct perf_event_attr attr;
+ for (const auto& slot : kSlots) {
+ if (!(toMeasure & slot.bit))
+ continue;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.size = sizeof(attr);
+
+ // Set the type and config fields to indicate the counter we
+ // want to enable. We want read format 0, and we're not using
+ // sampling, so leave those fields unset.
+ attr.type = slot.type;
+ attr.config = slot.config;
+
+ // If this will be the group leader it should start off
+ // disabled. Otherwise it should start off enabled (but blocked
+ // on the group leader).
+ if (group_leader == -1)
+ attr.disabled = 1;
+
+ // The rest of the bit fields are really poorly documented.
+ // For instance, I have *no idea* whether we should be setting
+ // the inherit, inherit_stat, or task flags. I'm pretty sure
+ // we do want to set mmap and comm, and not any of the ones I
+ // haven't mentioned.
+ attr.mmap = 1;
+ attr.comm = 1;
+
+ int fd = sys_perf_event_open(&attr,
+ 0 /* trace self */,
+ -1 /* on any cpu */,
+ group_leader,
+ 0 /* no flags presently defined */);
+ if (fd == -1)
+ continue;
+
+ measured = EventMask(measured | slot.bit);
+ this->*(slot.fd) = fd;
+ if (group_leader == -1)
+ group_leader = fd;
+ }
+ return measured;
+}
+
+void
+Impl::start()
+{
+ if (running || group_leader == -1)
+ return;
+
+ running = true;
+ ioctl(group_leader, PERF_EVENT_IOC_ENABLE, 0);
+}
+
+void
+Impl::stop(PerfMeasurement* counters)
+{
+ // This scratch buffer is to ensure that we have read all the
+ // available data, even if that's more than we expect.
+ unsigned char buf[1024];
+
+ if (!running || group_leader == -1)
+ return;
+
+ ioctl(group_leader, PERF_EVENT_IOC_DISABLE, 0);
+ running = false;
+
+ // read out and reset all the counter values
+ for (const auto& slot : kSlots) {
+ int fd = this->*(slot.fd);
+ if (fd == -1)
+ continue;
+
+ if (read(fd, buf, sizeof(buf)) == sizeof(uint64_t)) {
+ uint64_t cur;
+ memcpy(&cur, buf, sizeof(uint64_t));
+ counters->*(slot.counter) += cur;
+ }
+
+ // Reset the counter regardless of whether the read did what
+ // we expected.
+ ioctl(fd, PERF_EVENT_IOC_RESET, 0);
+ }
+}
+
+} // namespace
+
+
+namespace JS {
+
+#define initCtr(flag) ((eventsMeasured & flag) ? 0 : -1)
+
+PerfMeasurement::PerfMeasurement(PerfMeasurement::EventMask toMeasure)
+ : impl(js_new<Impl>()),
+ eventsMeasured(impl ? static_cast<Impl*>(impl)->init(toMeasure)
+ : EventMask(0)),
+ cpu_cycles(initCtr(CPU_CYCLES)),
+ instructions(initCtr(INSTRUCTIONS)),
+ cache_references(initCtr(CACHE_REFERENCES)),
+ cache_misses(initCtr(CACHE_MISSES)),
+ branch_instructions(initCtr(BRANCH_INSTRUCTIONS)),
+ branch_misses(initCtr(BRANCH_MISSES)),
+ bus_cycles(initCtr(BUS_CYCLES)),
+ page_faults(initCtr(PAGE_FAULTS)),
+ major_page_faults(initCtr(MAJOR_PAGE_FAULTS)),
+ context_switches(initCtr(CONTEXT_SWITCHES)),
+ cpu_migrations(initCtr(CPU_MIGRATIONS))
+{
+}
+
+#undef initCtr
+
+PerfMeasurement::~PerfMeasurement()
+{
+ js_delete(static_cast<Impl*>(impl));
+}
+
+void
+PerfMeasurement::start()
+{
+ if (impl)
+ static_cast<Impl*>(impl)->start();
+}
+
+void
+PerfMeasurement::stop()
+{
+ if (impl)
+ static_cast<Impl*>(impl)->stop(this);
+}
+
+void
+PerfMeasurement::reset()
+{
+ for (const auto& slot : kSlots) {
+ if (eventsMeasured & slot.bit)
+ this->*(slot.counter) = 0;
+ else
+ this->*(slot.counter) = -1;
+ }
+}
+
+bool
+PerfMeasurement::canMeasureSomething()
+{
+ // Find out if the kernel implements the performance measurement
+ // API. If it doesn't, syscall(__NR_perf_event_open, ...) is
+ // guaranteed to return -1 and set errno to ENOSYS.
+ //
+ // We set up input parameters that should provoke an EINVAL error
+ // from a kernel that does implement perf_event_open, but we can't
+ // be sure it will (newer kernels might add more event types), so
+ // we have to take care to close any valid fd it might return.
+
+ struct perf_event_attr attr;
+ memset(&attr, 0, sizeof(attr));
+ attr.size = sizeof(attr);
+ attr.type = PERF_TYPE_MAX;
+
+ int fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
+ if (fd >= 0) {
+ close(fd);
+ return true;
+ } else {
+ return errno != ENOSYS;
+ }
+}
+
+} // namespace JS
diff --git a/js/src/perf/pm_stub.cpp b/js/src/perf/pm_stub.cpp
new file mode 100644
index 0000000000..bb6910c735
--- /dev/null
+++ b/js/src/perf/pm_stub.cpp
@@ -0,0 +1,63 @@
+/* -*- 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"
+
+namespace JS {
+
+PerfMeasurement::PerfMeasurement(PerfMeasurement::EventMask)
+ : impl(0),
+ eventsMeasured(EventMask(0)),
+ cpu_cycles(-1),
+ instructions(-1),
+ cache_references(-1),
+ cache_misses(-1),
+ branch_instructions(-1),
+ branch_misses(-1),
+ bus_cycles(-1),
+ page_faults(-1),
+ major_page_faults(-1),
+ context_switches(-1),
+ cpu_migrations(-1)
+{
+}
+
+PerfMeasurement::~PerfMeasurement()
+{
+}
+
+void
+PerfMeasurement::start()
+{
+}
+
+void
+PerfMeasurement::stop()
+{
+}
+
+void
+PerfMeasurement::reset()
+{
+ cpu_cycles = -1;
+ instructions = -1;
+ cache_references = -1;
+ cache_misses = -1;
+ branch_instructions = -1;
+ branch_misses = -1;
+ bus_cycles = -1;
+ page_faults = -1;
+ major_page_faults = -1;
+ context_switches = -1;
+ cpu_migrations = -1;
+}
+
+bool
+PerfMeasurement::canMeasureSomething()
+{
+ return false;
+}
+
+} // namespace JS