/*** Copyright (c) Meta Platforms, Inc. and affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.*/#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);
}