From 3b28c72142ec937ff222463fc33243a9c43d6633 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Wed, 30 Sep 2015 18:36:19 -0700 Subject: [PATCH] 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$ ``` --- scripts/perf-counters/Makefile | 3 + scripts/perf-counters/src/jsc-perf.cpp | 204 +++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 scripts/perf-counters/Makefile create mode 100644 scripts/perf-counters/src/jsc-perf.cpp diff --git a/scripts/perf-counters/Makefile b/scripts/perf-counters/Makefile new file mode 100644 index 0000000000..a52850f66d --- /dev/null +++ b/scripts/perf-counters/Makefile @@ -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 diff --git a/scripts/perf-counters/src/jsc-perf.cpp b/scripts/perf-counters/src/jsc-perf.cpp new file mode 100644 index 0000000000..024fc438dd --- /dev/null +++ b/scripts/perf-counters/src/jsc-perf.cpp @@ -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 +#include +#include + +#include +#include +#include + +#include + +#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 pair(context, result); + + HardwareCounter::GetPerfEvents( + [](const std::string& key, int64_t value, void* data) { + std::pair& pair = + *reinterpret_cast*>(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(ifs)), + (std::istreambuf_iterator()) + ); + 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); +}