Add rudimentary jsc perf-counters runner

Works at least on a CentOS 7 machine after running `sudo yum install
webkitgtk webkitgtk-devel`.

The only globals you get are `print` and `PerfCounters`. No `console` nor the other globals provided by the `jsc` command-line tool (load, readFile, etc) though they're probably not hard to implement.

You can disable the JIT by setting the environment variable `JSC_useJIT=false`.

Test Plan:
```
~/local/react/scripts/perf-counters$ make
~/local/react/scripts/perf-counters$ build/jsc-perf <(echo 'PerfCounters.init(); var a = PerfCounters.getCounters().instructions; print("moo"); var b = PerfCounters.getCounters().instructions; print(b - a);')
moo
72182
~/local/react/scripts/perf-counters$
```
This commit is contained in:
Ben Alpert
2015-09-30 18:36:19 -07:00
parent d12ec60dd5
commit 3b28c72142
2 changed files with 207 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
build/jsc-perf: src/*
mkdir -p build
g++ -std=c++11 -I/usr/include/webkitgtk-1.0/ -ljavascriptcoregtk-1.0 src/jsc-perf.cpp src/hardware-counter.cpp src/thread-local.cpp -o build/jsc-perf

View File

@@ -0,0 +1,204 @@
/**
* Copyright 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fstream>
#include <iostream>
#include <string>
#include <JavaScriptCore/JavaScript.h>
#include "hardware-counter.h"
using HPHP::HardwareCounter;
void add_native_hook(
JSContextRef ctx,
JSObjectRef obj,
const char *name,
JSObjectCallAsFunctionCallback hook
) {
JSStringRef jsName = JSStringCreateWithUTF8CString(name);
JSObjectSetProperty(
ctx,
obj,
jsName,
JSObjectMakeFunctionWithCallback(ctx, jsName, hook),
kJSPropertyAttributeNone,
NULL
);
JSStringRelease(jsName);
}
static void fprint_value(
FILE *file,
JSContextRef context,
JSValueRef obj
) {
JSStringRef jsStr = JSValueToStringCopy(context, obj, NULL);
size_t size = JSStringGetMaximumUTF8CStringSize(jsStr);
char *str = (char *) calloc(
size,
1
);
JSStringGetUTF8CString(
jsStr,
str,
size
);
JSStringRelease(jsStr);
fprintf(file, "%s", str);
free(str);
}
static JSValueRef js_print(
JSContextRef context,
JSObjectRef object,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception
) {
for (int i = 0; i < argumentCount; i++) {
if (i != 0) {
printf(" ");
}
fprint_value(stdout, context, arguments[i]);
}
printf("\n");
return JSValueMakeUndefined(context);
}
static JSValueRef js_perf_counters_init(
JSContextRef context,
JSObjectRef object,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception
) {
// TODO: Allow customizing recorded events
bool enable = true;
std::string events = "";
bool recordSubprocesses = false;
HardwareCounter::Init(enable, events, recordSubprocesses);
HardwareCounter::s_counter.getCheck();
return JSValueMakeUndefined(context);
}
static JSValueRef js_perf_counters_get_counters(
JSContextRef context,
JSObjectRef object,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception
) {
JSObjectRef result = JSObjectMake(context, NULL, NULL);
std::pair<JSContextRef, JSObjectRef> pair(context, result);
HardwareCounter::GetPerfEvents(
[](const std::string& key, int64_t value, void* data) {
std::pair<JSContextRef, JSObjectRef>& pair =
*reinterpret_cast<std::pair<JSContextRef, JSObjectRef>*>(data);
JSContextRef context = pair.first;
JSObjectRef result = pair.second;
JSObjectSetProperty(
context,
result,
JSStringCreateWithUTF8CString(key.c_str()),
JSValueMakeNumber(context, value),
kJSPropertyAttributeNone,
NULL
);
},
&pair);
return result;
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "usage: jsc-runner file\n");
exit(1);
}
char *filename = argv[1];
std::ifstream ifs(filename);
if (ifs.fail()) {
std::cerr << "Error opening \"" << filename << "\": " << strerror(errno) << "\n";
exit(1);
}
std::string script(
(std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>())
);
JSStringRef jsScript = JSStringCreateWithUTF8CString(script.c_str());
JSStringRef jsURL = JSStringCreateWithUTF8CString(argv[1]);
JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
add_native_hook(
ctx,
JSContextGetGlobalObject(ctx),
"print",
js_print
);
JSObjectRef jsPerfCounters = JSObjectMake(ctx, NULL, NULL);
add_native_hook(
ctx,
jsPerfCounters,
"init",
js_perf_counters_init
);
add_native_hook(
ctx,
jsPerfCounters,
"getCounters",
js_perf_counters_get_counters
);
JSObjectSetProperty(
ctx,
JSContextGetGlobalObject(ctx),
JSStringCreateWithUTF8CString("PerfCounters"),
jsPerfCounters,
kJSPropertyAttributeNone,
NULL
);
JSValueRef jsError = NULL;
JSValueRef result = JSEvaluateScript(
ctx,
jsScript,
NULL,
jsURL,
0,
&jsError
);
if (!result) {
fprintf(stderr, "Exception: ");
fprint_value(stderr, ctx, jsError);
fprintf(stderr, "\n");
JSStringRef jsStackStr = JSStringCreateWithUTF8CString("stack");
if (JSValueIsObject(ctx, jsError)) {
JSValueRef jsStack = JSObjectGetProperty(ctx, (JSObjectRef)jsError, jsStackStr, NULL);
JSStringRelease(jsStackStr);
fprint_value(stderr, ctx, jsStack);
fprintf(stderr, "\n");
}
exit(1);
}
JSGlobalContextRelease(ctx);
}