Bug 568863: Add a scriptable interface to operating-system performance measurement APIs (Linux only for now). r=cjones,waldo,mitch a2.0=bz

--HG--
rename : toolkit/components/ctypes/Module.cpp => toolkit/components/ctypes/ctypes.cpp
rename : toolkit/components/ctypes/Module.h => toolkit/components/ctypes/ctypes.h
This commit is contained in:
Zack Weinberg 2010-07-30 12:17:56 -07:00
parent 460cdeaae5
commit 788b4d617f
20 changed files with 1374 additions and 4 deletions

View File

@ -322,6 +322,19 @@ INSTALLED_HEADERS += \
$(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

View File

@ -338,3 +338,4 @@ 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@

View File

@ -3022,6 +3022,10 @@ case $target in
;;
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

278
js/src/perf/jsperf.cpp Normal file
View File

@ -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

163
js/src/perf/jsperf.h Normal file
View File

@ -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___

342
js/src/perf/pm_linux.cpp Normal file
View File

@ -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

96
js/src/perf/pm_stub.cpp Normal file
View File

@ -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

View File

@ -75,6 +75,7 @@
#include "jsscript.h"
#include "jstracer.h"
#include "jsxml.h"
#include "jsperf.h"
#include "prmjtime.h"
@ -4880,6 +4881,8 @@ NewGlobalObject(JSContext *cx, JSAutoCrossCompartmentCall &call)
if (!JS_InitCTypesClass(cx, glob))
return NULL;
#endif
if (!JS::RegisterPerfMeasurement(cx, glob))
return NULL;
if (!JS_DefineFunctions(cx, glob, shell_functions))
return NULL;

View File

@ -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);
}

View File

@ -62,6 +62,7 @@ PARALLEL_DIRS += \
microformats \
parentalcontrols \
passwordmgr \
perf \
places \
prompts \
startup \

View File

@ -57,7 +57,7 @@ EXPORT_LIBRARY = 1
IS_COMPONENT = 1
CPPSRCS = \
Module.cpp \
ctypes.cpp \
$(NULL)
EXTRA_DSO_LDOPTS += \

View File

@ -37,7 +37,7 @@
*
* ***** END LICENSE BLOCK ***** */
#include "Module.h"
#include "ctypes.h"
#include "jsapi.h"
#include "mozilla/ModuleUtils.h"
#include "nsMemory.h"

View File

@ -36,8 +36,8 @@
*
* ***** END LICENSE BLOCK ***** */
#ifndef MODULE_H
#define MODULE_H
#ifndef COMPONENTS_CTYPES_H
#define COMPONENTS_CTYPES_H
#include "nsIXPCScriptable.h"

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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()();

View File

@ -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>

View File

@ -166,6 +166,8 @@ COMPONENT_LIBS += \
$(NULL)
endif
COMPONENT_LIBS += jsperf
ifdef MOZ_PLUGINS
DEFINES += -DMOZ_PLUGINS
COMPONENT_LIBS += \

View File

@ -278,6 +278,7 @@
OSXPROXY_MODULE \
WINDOWSPROXY_MODULE \
JSCTYPES_MODULE \
MODULE(jsperf) \
/* end of list */
#define MODULE(_name) \