Bug 568863: Add a scriptable interface to operating-system performance measurement APIs (Linux only for now). r=cjones,waldo,mitch a2.0=bz
authorZack Weinberg <zweinberg@mozilla.com>
Fri, 30 Jul 2010 12:17:56 -0700
changeset 48637 0406c26819576bc40fdae527a99c24ef9fdf497c
parent 48636 d1b8dd1632bf2cd5bdc2b29bfb965ee8ae17007b
child 48638 af011e92ad0b6a80ea782c427e9ecef92ceb73fb
push id14748
push userrsayre@mozilla.com
push dateSun, 01 Aug 2010 00:33:23 +0000
treeherderautoland@f0df797bb2a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones, waldo, mitch
bugs568863
milestone2.0b3pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 568863: Add a scriptable interface to operating-system performance measurement APIs (Linux only for now). r=cjones,waldo,mitch a2.0=bz
js/src/Makefile.in
js/src/config/autoconf.mk.in
js/src/configure.in
js/src/perf/jsperf.cpp
js/src/perf/jsperf.h
js/src/perf/pm_linux.cpp
js/src/perf/pm_stub.cpp
js/src/shell/js.cpp
js/src/trace-test/tests/basic/perf-smoketest.js
toolkit/components/Makefile.in
toolkit/components/ctypes/Makefile.in
toolkit/components/ctypes/Module.cpp
toolkit/components/ctypes/Module.h
toolkit/components/ctypes/ctypes.cpp
toolkit/components/ctypes/ctypes.h
toolkit/components/perf/Makefile.in
toolkit/components/perf/PerfMeasurement.cpp
toolkit/components/perf/PerfMeasurement.h
toolkit/components/perf/PerfMeasurement.jsm
toolkit/components/perf/test_pm.xul
toolkit/library/libxul-config.mk
toolkit/library/nsStaticXULComponents.cpp
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -317,16 +317,29 @@ endif # JS_HAS_CTYPES
 
 ifdef HAVE_DTRACE
 INSTALLED_HEADERS += \
 		jsdtracef.h \
 		$(CURDIR)/javascript-trace.h \
 		$(NULL)
 endif
 
+# PerfMeasurement is available regardless of low-level support for it;
+# it just doesn't necessarily do anything useful.  There is one
+# implementation source file per supported operating system, plus a stub
+# for unsupported OSes, plus the Javascript wrapper.
+VPATH += $(srcdir)/perf
+INSTALLED_HEADERS += jsperf.h
+CPPSRCS += jsperf.cpp
+ifdef HAVE_LINUX_PERF_EVENT_H
+CPPSRCS += pm_linux.cpp
+else
+CPPSRCS += pm_stub.cpp
+endif
+
 ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH)))
 INSTALLED_HEADERS += jscpucfg.h
 endif
 
 EXPORTS = $(INSTALLED_HEADERS)
 
 DASH_R		= -r
 
--- a/js/src/config/autoconf.mk.in
+++ b/js/src/config/autoconf.mk.in
@@ -333,8 +333,9 @@ UNIVERSAL_BINARY= @UNIVERSAL_BINARY@
 HAVE_DTRACE= @HAVE_DTRACE@
 
 VISIBILITY_FLAGS = @VISIBILITY_FLAGS@
 WRAP_SYSTEM_INCLUDES = @WRAP_SYSTEM_INCLUDES@
 
 ENABLE_TRACEJIT = @ENABLE_TRACEJIT@
 NANOJIT_ARCH = @NANOJIT_ARCH@
 HAVE_ARM_SIMD= @HAVE_ARM_SIMD@
+HAVE_LINUX_PERF_EVENT_H = @HAVE_LINUX_PERF_EVENT_H@
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -3017,16 +3017,20 @@ AC_SUBST(HAVE_DTRACE)
 case $target in
 *-aix4.3*|*-aix5*)
 	;;
 *)
 	AC_CHECK_HEADERS(sys/cdefs.h)
 	;;
 esac
 
+dnl Performance measurement headers.
+AC_CHECK_HEADER(linux/perf_event.h, HAVE_LINUX_PERF_EVENT_H=1)
+AC_SUBST(HAVE_LINUX_PERF_EVENT_H)
+
 dnl Checks for libraries.
 dnl ========================================================
 case $target in
 *-hpux11.*)
 	;;
 *)
 	AC_CHECK_LIB(c_r, gethostbyname_r)
 	;;
new file mode 100644
--- /dev/null
+++ b/js/src/perf/jsperf.cpp
@@ -0,0 +1,278 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Zack Weinberg <zweinberg@mozilla.com>  (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "jsperf.h"
+#include "jscntxt.h" /* for error messages */
+#include "jsobj.h" /* for unwrapping without a context */
+
+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, JSObject* obj, const char* fname);
+static PerfMeasurement* GetPMFromThis(JSContext* cx, jsval* vp);
+
+// Constructor and destructor
+
+static JSBool
+pm_construct(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+    uint32 mask;
+    if (!JS_ConvertArguments(cx, argc, argv, "u", &mask))
+        return JS_FALSE;
+
+    if (!JS_SealObject(cx, obj, JS_FALSE))
+        return JS_FALSE;
+
+    PerfMeasurement* p = new PerfMeasurement(PerfMeasurement::EventMask(mask));
+    if (!p) {
+        JS_ReportOutOfMemory(cx);
+        return JS_FALSE;
+    }
+
+    JS_SetPrivate(cx, obj, p);
+    return JS_TRUE;
+}
+
+static void
+pm_finalize(JSContext* cx, JSObject* obj)
+{
+    delete (PerfMeasurement*) JS_GetPrivate(cx, obj);
+}
+
+// Property access
+
+#define GETTER(name)                                                    \
+    static JSBool                                                       \
+    pm_get_##name(JSContext* cx, JSObject* obj, jsid /*unused*/, jsval* vp) \
+    {                                                                   \
+        PerfMeasurement* p = GetPM(cx, obj, #name);                     \
+        if (!p)                                                         \
+            return JS_FALSE;                                            \
+        return JS_NewNumberValue(cx, p->name, vp);                      \
+    }
+
+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, uintN /*unused*/, jsval* vp)
+{
+    PerfMeasurement* p = GetPMFromThis(cx, vp);
+    if (!p)
+        return JS_FALSE;
+
+    p->start();
+    return JS_TRUE;
+}
+
+static JSBool
+pm_stop(JSContext* cx, uintN /*unused*/, jsval* vp)
+{
+    PerfMeasurement* p = GetPMFromThis(cx, vp);
+    if (!p)
+        return JS_FALSE;
+
+    p->stop();
+    return JS_TRUE;
+}
+
+static JSBool
+pm_canMeasureSomething(JSContext* cx, uintN /*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 PM_FATTRS = JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED;
+static JSFunctionSpec pm_fns[] = {
+    JS_FN("start",               pm_start,               0, PM_FATTRS),
+    JS_FN("stop",                pm_stop,                0, PM_FATTRS),
+    JS_FN("canMeasureSomething", pm_canMeasureSomething, 0, PM_FATTRS),
+    JS_FS_END
+};
+
+const uint8 PM_PATTRS =
+    JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED;
+
+#define GETTER(name)                            \
+    { #name, 0, PM_PATTRS, pm_get_##name, 0 }
+
+static 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,0,0}
+};
+
+#undef GETTER
+
+// If this were C++ these would be "static const" members.
+
+const uint8 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 JSClass pm_class = {
+    "PerfMeasurement", JSCLASS_HAS_PRIVATE,
+    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, pm_finalize,
+    JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+// Helpers (declared above)
+
+static PerfMeasurement*
+GetPM(JSContext* cx, JSObject* 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_GET_CLASS(cx, 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)
+{
+    JSObject *prototype = JS_InitClass(cx, global, 0 /* parent */,
+                                       &pm_class, pm_construct, 1,
+                                       pm_props, pm_fns, 0, 0);
+    if (!prototype)
+        return 0;
+
+    JSObject *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_PropertyStub, PM_CATTRS))
+            return 0;
+    }
+
+    if (!JS_SealObject(cx, prototype, JS_FALSE) ||
+        !JS_SealObject(cx, ctor, JS_FALSE)) {
+        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
new file mode 100644
--- /dev/null
+++ b/js/src/perf/jsperf.h
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Zack Weinberg <zweinberg@mozilla.com>  (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef jsperf_h___
+#define jsperf_h___
+
+#include "jsapi.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(-1).
+     */
+    uint64 cpu_cycles;
+    uint64 instructions;
+    uint64 cache_references;
+    uint64 cache_misses;
+    uint64 branch_instructions;
+    uint64 branch_misses;
+    uint64 bus_cycles;
+    uint64 page_faults;
+    uint64 major_page_faults;
+    uint64 context_switches;
+    uint64 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.
+     */
+    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, JSObject *global);
+
+/*
+ * Given a jsval which contains an instance of the aforementioned
+ * wrapper class, extract the C++ object.  Returns NULL if the
+ * jsval is not an instance of the wrapper.
+ */
+extern JS_FRIEND_API(PerfMeasurement*)
+    ExtractPerfMeasurement(jsval wrapper);
+
+} // namespace JS
+
+#endif // jsperf_h___
new file mode 100644
--- /dev/null
+++ b/js/src/perf/pm_linux.cpp
@@ -0,0 +1,342 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Zack Weinberg <zweinberg@mozilla.com>  (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "jsperf.h"
+#include "jsutil.h"
+
+/* 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 <linux/perf_event.h>
+#include <sys/syscall.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+// 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 type;
+    uint32 config;
+    uint64 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 (int i = 0; i < PerfMeasurement::NUM_MEASURABLE_EVENTS; i++) {
+        int fd = this->*(kSlots[i].fd);
+        if (fd != -1 && fd != group_leader)
+            close(fd);
+    }
+
+    if (group_leader != -1)
+        close(group_leader);
+}
+
+EventMask
+Impl::init(EventMask toMeasure)
+{
+    JS_ASSERT(group_leader == -1);
+    if (!toMeasure)
+        return EventMask(0);
+
+    EventMask measured = EventMask(0);
+    struct perf_event_attr attr;
+    for (int i = 0; i < PerfMeasurement::NUM_MEASURABLE_EVENTS; i++) {
+        if (!(toMeasure & kSlots[i].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 = kSlots[i].type;
+        attr.config = kSlots[i].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 | kSlots[i].bit);
+        this->*(kSlots[i].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 (int i = 0; i < PerfMeasurement::NUM_MEASURABLE_EVENTS; i++) {
+        int fd = this->*(kSlots[i].fd);
+        if (fd == -1)
+            continue;
+
+        if (read(fd, buf, sizeof(buf)) == sizeof(uint64)) {
+            uint64 cur;
+            memcpy(&cur, buf, sizeof(uint64));
+            counters->*(kSlots[i].counter) += cur;
+        }
+
+        // Reset the counter regardless of whether the read did what
+        // we expected.
+        ioctl(fd, PERF_EVENT_IOC_RESET, 0);
+    }
+}
+
+} // anonymous namespace
+
+
+namespace JS {
+
+#define initCtr(flag) ((eventsMeasured & flag) ? 0 : -1)
+
+PerfMeasurement::PerfMeasurement(PerfMeasurement::EventMask toMeasure)
+  : impl(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()
+{
+    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 (int i = 0; i < NUM_MEASURABLE_EVENTS; i++) {
+        if (eventsMeasured & kSlots[i].bit)
+            this->*(kSlots[i].counter) = 0;
+        else
+            this->*(kSlots[i].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
new file mode 100644
--- /dev/null
+++ b/js/src/perf/pm_stub.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Zack Weinberg <zweinberg@mozilla.com>  (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "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
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -70,16 +70,17 @@
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsparse.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jstracer.h"
 #include "jsxml.h"
+#include "jsperf.h"
 
 #include "prmjtime.h"
 
 #ifdef JSDEBUGGER
 #include "jsdebug.h"
 #ifdef JSDEBUGGER_JAVA_UI
 #include "jsdjava.h"
 #endif /* JSDEBUGGER_JAVA_UI */
@@ -4875,16 +4876,18 @@ NewGlobalObject(JSContext *cx, JSAutoCro
 #else
     if (!JS_InitStandardClasses(cx, glob))
         return NULL;
 #endif
 #ifdef JS_HAS_CTYPES
     if (!JS_InitCTypesClass(cx, glob))
         return NULL;
 #endif
+    if (!JS::RegisterPerfMeasurement(cx, glob))
+        return NULL;
     if (!JS_DefineFunctions(cx, glob, shell_functions))
         return NULL;
 
     JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
     if (!it)
         return NULL;
     if (!JS_DefineProperties(cx, it, its_props))
         return NULL;
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/basic/perf-smoketest.js
@@ -0,0 +1,34 @@
+function spin_loop()
+{
+    for (let i = 0; i < 10000; i++) ;
+}
+
+function check_timing(label, count) {
+    if (count == -1) {
+	print("TEST-UNEXPECTED-FAIL | TestPerf | " + label);
+	throwError();
+    } else {
+	print("TEST-PASS | TestPerf | " + label + " = " + count);
+    }
+}
+
+var pm = new PerfMeasurement(PerfMeasurement.ALL);
+if (pm.eventsMeasured == 0) {
+    print("TEST-KNOWN-FAIL | perf-smoketest | stub, skipping test");
+} else {
+    pm.start();
+    spin_loop();
+    pm.stop();
+
+    check_timing("cpu_cycles", pm.cpu_cycles);
+    check_timing("instructions", pm.instructions);
+    check_timing("cache_references", pm.cache_references);
+    check_timing("cache_misses", pm.cache_misses);
+    check_timing("branch_instructions", pm.branch_instructions);
+    check_timing("branch_misses", pm.branch_misses);
+    check_timing("bus_cycles", pm.bus_cycles);
+    check_timing("page_faults", pm.page_faults);
+    check_timing("major_page_faults", pm.major_page_faults);
+    check_timing("context_switches", pm.context_switches);
+    check_timing("cpu_migrations", pm.cpu_migrations);
+}
--- a/toolkit/components/Makefile.in
+++ b/toolkit/components/Makefile.in
@@ -57,16 +57,17 @@ PARALLEL_DIRS += \
   contentprefs \
   cookie \
   exthelper \
   filepicker \
   find \
   microformats \
   parentalcontrols \
   passwordmgr \
+  perf \
   places \
   prompts \
   startup \
   statusfilter \
   typeaheadfind \
   urlformatter \
   viewconfig \
   $(NULL)
--- a/toolkit/components/ctypes/Makefile.in
+++ b/toolkit/components/ctypes/Makefile.in
@@ -52,17 +52,17 @@ EXTRA_JS_MODULES = \
     $(NULL)
 
 LIBRARY_NAME = jsctypes
 LIBXUL_LIBRARY = 1
 EXPORT_LIBRARY = 1
 IS_COMPONENT = 1
 
 CPPSRCS = \
-    Module.cpp \
+    ctypes.cpp \
     $(NULL)
 
 EXTRA_DSO_LDOPTS += \
     $(MOZ_COMPONENT_LIBS) \
     $(MOZ_JS_LIBS) \
     $(NULL)
 
 ifdef ENABLE_TESTS
rename from toolkit/components/ctypes/Module.cpp
rename to toolkit/components/ctypes/ctypes.cpp
--- a/toolkit/components/ctypes/Module.cpp
+++ b/toolkit/components/ctypes/ctypes.cpp
@@ -32,17 +32,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "Module.h"
+#include "ctypes.h"
 #include "jsapi.h"
 #include "mozilla/ModuleUtils.h"
 #include "nsMemory.h"
 
 #define JSCTYPES_CONTRACTID \
   "@mozilla.org/jsctypes;1"
 
 #define JSCTYPES_CID \
rename from toolkit/components/ctypes/Module.h
rename to toolkit/components/ctypes/ctypes.h
--- a/toolkit/components/ctypes/Module.h
+++ b/toolkit/components/ctypes/ctypes.h
@@ -31,18 +31,18 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#ifndef MODULE_H
-#define MODULE_H
+#ifndef COMPONENTS_CTYPES_H
+#define COMPONENTS_CTYPES_H
 
 #include "nsIXPCScriptable.h"
 
 namespace mozilla {
 namespace ctypes {
 
 class Module : public nsIXPCScriptable
 {
new file mode 100644
--- /dev/null
+++ b/toolkit/components/perf/Makefile.in
@@ -0,0 +1,81 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# The Mozilla Foundation <http://www.mozilla.org/>.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#    Zack Weinberg <zweinberg@mozilla.com> (original author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = perf
+MODULE_NAME = perf
+GRE_MODULE = 1
+
+EXTRA_JS_MODULES = \
+    PerfMeasurement.jsm \
+    $(NULL)
+
+LIBRARY_NAME = jsperf
+LIBXUL_LIBRARY = 1
+EXPORT_LIBRARY = 1
+IS_COMPONENT = 1
+
+CPPSRCS = \
+    PerfMeasurement.cpp \
+    $(NULL)
+
+EXTRA_DSO_LDOPTS += \
+    $(MOZ_COMPONENT_LIBS) \
+    $(MOZ_JS_LIBS) \
+    $(NULL)
+
+ifdef ENABLE_TESTS
+_CHROME_TEST_FILES = \
+    test_pm.xul \
+    $(NULL)
+
+chrometestdir = \
+  $(DEPTH)/_tests/testing/mochitest/chrome/toolkit/components/$(MODULE)
+endif
+
+include $(topsrcdir)/config/rules.mk
+
+ifdef ENABLE_TESTS
+libs:: $(_CHROME_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(chrometestdir)
+endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/perf/PerfMeasurement.cpp
@@ -0,0 +1,149 @@
+/* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation <http://www.mozilla.org/>.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *    Zack Weinberg <zweinberg@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "PerfMeasurement.h"
+#include "jsperf.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsMemory.h"
+
+#define JSPERF_CONTRACTID \
+  "@mozilla.org/jsperf;1"
+
+#define JSPERF_CID            \
+{ 0x421c38e6, 0xaee0, 0x4509, \
+  { 0xa0, 0x25, 0x13, 0x0f, 0x43, 0x78, 0x03, 0x5a } }
+
+namespace mozilla {
+namespace jsperf {
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(Module)
+
+NS_IMPL_ISUPPORTS1(Module, nsIXPCScriptable)
+
+Module::Module()
+{
+}
+
+Module::~Module()
+{
+}
+
+#define XPC_MAP_CLASSNAME Module
+#define XPC_MAP_QUOTED_CLASSNAME "Module"
+#define XPC_MAP_WANT_CALL
+#define XPC_MAP_FLAGS nsIXPCScriptable::WANT_CALL
+#include "xpc_map_end.h"
+
+static JSBool
+SealObjectAndPrototype(JSContext* cx, JSObject* parent, const char* name)
+{
+  jsval prop;
+  if (!JS_GetProperty(cx, parent, name, &prop))
+    return false;
+
+  JSObject* obj = JSVAL_TO_OBJECT(prop);
+  if (!JS_GetProperty(cx, obj, "prototype", &prop))
+    return false;
+
+  JSObject* prototype = JSVAL_TO_OBJECT(prop);
+  return JS_SealObject(cx, obj, JS_FALSE) &&
+         JS_SealObject(cx, prototype, JS_FALSE);
+}
+
+static JSBool
+InitAndSealPerfMeasurementClass(JSContext* cx, JSObject* global)
+{
+  // Init the PerfMeasurement class
+  if (!JS::RegisterPerfMeasurement(cx, global))
+    return false;
+
+  // Seal up Object, Function, and Array and their prototypes.  (This single
+  // object instance is shared amongst everyone who imports the jsperf module.)
+  if (!SealObjectAndPrototype(cx, global, "Object") ||
+      !SealObjectAndPrototype(cx, global, "Function") ||
+      !SealObjectAndPrototype(cx, global, "Array"))
+    return false;
+
+  // Finally, seal the global object, for good measure. (But not recursively;
+  // this breaks things.)
+  return JS_SealObject(cx, global, JS_FALSE);
+}
+
+NS_IMETHODIMP
+Module::Call(nsIXPConnectWrappedNative* wrapper,
+             JSContext* cx,
+             JSObject* obj,
+             PRUint32 argc,
+             jsval* argv,
+             jsval* vp,
+             PRBool* _retval)
+{
+  JSObject* scope = JS_GetScopeChain(cx);
+  if (!scope)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  JSObject* global = JS_GetGlobalForObject(cx, scope);
+  if (!global)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  *_retval = InitAndSealPerfMeasurementClass(cx, global);
+  return NS_OK;
+}
+
+}
+}
+
+NS_DEFINE_NAMED_CID(JSPERF_CID);
+
+static const mozilla::Module::CIDEntry kPerfCIDs[] = {
+  { &kJSPERF_CID, false, NULL, mozilla::jsperf::ModuleConstructor },
+  { NULL }
+};
+
+static const mozilla::Module::ContractIDEntry kPerfContracts[] = {
+  { JSPERF_CONTRACTID, &kJSPERF_CID },
+  { NULL }
+};
+
+static const mozilla::Module kPerfModule = {
+  mozilla::Module::kVersion,
+  kPerfCIDs,
+  kPerfContracts
+};
+
+NSMODULE_DEFN(jsperf) = &kPerfModule;
new file mode 100644
--- /dev/null
+++ b/toolkit/components/perf/PerfMeasurement.h
@@ -0,0 +1,62 @@
+/* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation <http://www.mozilla.org/>.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *    Zack Weinberg <zweinberg@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef COMPONENTS_PERFMEASUREMENT_H
+#define COMPONENTS_PERFMEASUREMENT_H
+
+#include "nsIXPCScriptable.h"
+
+namespace mozilla {
+namespace jsperf {
+
+class Module : public nsIXPCScriptable
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIXPCSCRIPTABLE
+
+  Module();
+
+private:
+  ~Module();
+};
+
+}
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/perf/PerfMeasurement.jsm
@@ -0,0 +1,52 @@
+/* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation <http://www.mozilla.org/>.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *    Zack Weinberg <zweinberg@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+let EXPORTED_SYMBOLS = [ "PerfMeasurement" ];
+
+/*
+ * This is the js module for jsperf. Import it like so:
+ *   Components.utils.import("resource://gre/modules/PerfMeasurement.jsm");
+ *
+ * This will create a 'PerfMeasurement' class.  Instances of this class can
+ * be used to benchmark browser operations.
+ *
+ * For documentation on the API, see js/src/perf/jsperf.h.
+ *
+ */
+
+Components.classes["@mozilla.org/jsperf;1"].createInstance()();
new file mode 100644
--- /dev/null
+++ b/toolkit/components/perf/test_pm.xul
@@ -0,0 +1,88 @@
+<?xml version="1.0"?>
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is mozilla.org code.
+   -
+   - The Initial Developer of the Original Code is
+   - The Mozilla Foundation.
+   - Portions created by the Initial Developer are Copyright (C) 2010
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -   Zack Weinberg <zweinberg@mozilla.com>
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either the GNU General Public License Version 2 or later (the "GPL"), or
+   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   - in which case the provisions of the GPL or the LGPL are applicable instead
+   - of those above. If you wish to allow use of your version of this file only
+   - under the terms of either the GPL or the LGPL, and not to allow others to
+   - use your version of this file under the terms of the MPL, indicate your
+   - decision by deleting the provisions above and replace them with the notice
+   - and other provisions required by the LGPL or the GPL. If you do not delete
+   - the provisions above, a recipient may use your version of this file under
+   - the terms of any one of the MPL, the GPL or the LGPL.
+   -
+   - ***** END LICENSE BLOCK ***** -->
+
+<window title="Performance measurement tests"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="test()">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+  <script type="application/javascript"><![CDATA[
+function test()
+{
+  SimpleTest.waitForExplicitFinish();
+
+  Components.utils.import("resource://gre/modules/PerfMeasurement.jsm");
+  let pm = new PerfMeasurement(PerfMeasurement.ALL);
+  if (pm.eventsMeasured == 0) {
+    todo(false, "stub, skipping test");
+  } else {
+    is(pm.eventsMeasured, PerfMeasurement.ALL, "all events measurable");
+
+    pm.start();
+    for (let i = 0; i < 10000; i++) ;
+      pm.stop();
+
+    isnot(pm.cpu_cycles, -1, "cpu_cycles");
+    isnot(pm.instructions, -1, "instructions");
+    isnot(pm.cache_references, -1, "cache_references");
+    isnot(pm.cache_misses, -1, "cache_misses");
+    isnot(pm.branch_instructions, -1, "branch_instructions");
+    isnot(pm.branch_misses, -1, "branch_misses");
+    isnot(pm.bus_cycles, -1, "bus_cycles");
+    isnot(pm.page_faults, -1, "page_faults");
+    isnot(pm.major_page_faults, -1, "major_page_faults");
+    isnot(pm.context_switches, -1, "context_switches");
+    isnot(pm.cpu_migrations, -1, "cpu_migrations");
+  }
+  SimpleTest.finish();
+}
+]]></script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <p id="display"></p>
+    <div id="content" style="display:none;"></div>
+    <pre id="test"></pre>
+  </body>
+  <label id="test-result"/>
+</window>
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -161,16 +161,18 @@ COMPONENT_LIBS +=  jetpack_s
 endif
 
 ifdef BUILD_CTYPES
 COMPONENT_LIBS += \
 	jsctypes \
 	$(NULL)
 endif
 
+COMPONENT_LIBS += jsperf
+
 ifdef MOZ_PLUGINS
 DEFINES += -DMOZ_PLUGINS
 COMPONENT_LIBS += \
 	gkplugin \
 	$(NULL)
 endif
 
 ifdef MOZ_XUL
--- a/toolkit/library/nsStaticXULComponents.cpp
+++ b/toolkit/library/nsStaticXULComponents.cpp
@@ -273,16 +273,17 @@
     MODULE(NSS)                              \
     SYSTEMPREF_MODULES                       \
     SPELLCHECK_MODULE                        \
     LAYOUT_DEBUG_MODULE                      \
     UNIXPROXY_MODULE                         \
     OSXPROXY_MODULE                          \
     WINDOWSPROXY_MODULE                      \
     JSCTYPES_MODULE                          \
+    MODULE(jsperf)                           \
     /* end of list */
 
 #define MODULE(_name) \
   NSMODULE_DECL(_name);
 
 XUL_MODULES
 
 #undef MODULE