1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  */
    
  7. 
    
  8. #include <errno.h>
    
  9. #include <stdlib.h>
    
  10. #include <string.h>
    
  11. 
    
  12. #include <fstream>
    
  13. #include <iostream>
    
  14. #include <string>
    
  15. 
    
  16. #include <JavaScriptCore/JavaScript.h>
    
  17. 
    
  18. #include "hardware-counter.h"
    
  19. 
    
  20. using HPHP::HardwareCounter;
    
  21. 
    
  22. void add_native_hook(
    
  23.   JSContextRef ctx,
    
  24.   JSObjectRef obj,
    
  25.   const char *name,
    
  26.   JSObjectCallAsFunctionCallback hook
    
  27. ) {
    
  28.   JSStringRef jsName = JSStringCreateWithUTF8CString(name);
    
  29.   JSObjectSetProperty(
    
  30.     ctx,
    
  31.     obj,
    
  32.     jsName,
    
  33.     JSObjectMakeFunctionWithCallback(ctx, jsName, hook),
    
  34.     kJSPropertyAttributeNone,
    
  35.     NULL
    
  36.   );
    
  37.   JSStringRelease(jsName);
    
  38. }
    
  39. 
    
  40. static void fprint_value(
    
  41.   FILE *file,
    
  42.   JSContextRef context,
    
  43.   JSValueRef obj
    
  44. ) {
    
  45.   JSStringRef jsStr = JSValueToStringCopy(context, obj, NULL);
    
  46.   size_t size = JSStringGetMaximumUTF8CStringSize(jsStr);
    
  47.   char *str = (char *) calloc(
    
  48.     size,
    
  49.     1
    
  50.   );
    
  51.   JSStringGetUTF8CString(
    
  52.     jsStr,
    
  53.     str,
    
  54.     size
    
  55.   );
    
  56.   JSStringRelease(jsStr);
    
  57.   fprintf(file, "%s", str);
    
  58.   free(str);
    
  59. }
    
  60. 
    
  61. static JSValueRef js_print(
    
  62.   JSContextRef context,
    
  63.   JSObjectRef object,
    
  64.   JSObjectRef thisObject,
    
  65.   size_t argumentCount,
    
  66.   const JSValueRef arguments[],
    
  67.   JSValueRef *exception
    
  68. ) {
    
  69.   for (int i = 0; i < argumentCount; i++) {
    
  70.     if (i != 0) {
    
  71.       printf(" ");
    
  72.     }
    
  73.     fprint_value(stdout, context, arguments[i]);
    
  74.   }
    
  75.   printf("\n");
    
  76.   return JSValueMakeUndefined(context);
    
  77. }
    
  78. 
    
  79. static JSValueRef js_perf_counters_init(
    
  80.   JSContextRef context,
    
  81.   JSObjectRef object,
    
  82.   JSObjectRef thisObject,
    
  83.   size_t argumentCount,
    
  84.   const JSValueRef arguments[],
    
  85.   JSValueRef *exception
    
  86. ) {
    
  87.   // TODO: Allow customizing recorded events
    
  88.   bool enable = true;
    
  89.   std::string events = "";
    
  90.   bool recordSubprocesses = false;
    
  91.   HardwareCounter::Init(enable, events, recordSubprocesses);
    
  92.   HardwareCounter::s_counter.getCheck();
    
  93. 
    
  94.   return JSValueMakeUndefined(context);
    
  95. }
    
  96. 
    
  97. static JSValueRef js_perf_counters_get_counters(
    
  98.   JSContextRef context,
    
  99.   JSObjectRef object,
    
  100.   JSObjectRef thisObject,
    
  101.   size_t argumentCount,
    
  102.   const JSValueRef arguments[],
    
  103.   JSValueRef *exception
    
  104. ) {
    
  105.   JSObjectRef result = JSObjectMake(context, NULL, NULL);
    
  106.   std::pair<JSContextRef, JSObjectRef> pair(context, result);
    
  107. 
    
  108.   HardwareCounter::GetPerfEvents(
    
  109.     [](const std::string& key, int64_t value, void* data) {
    
  110.       std::pair<JSContextRef, JSObjectRef>& pair =
    
  111.         *reinterpret_cast<std::pair<JSContextRef, JSObjectRef>*>(data);
    
  112.       JSContextRef context = pair.first;
    
  113.       JSObjectRef result = pair.second;
    
  114. 
    
  115.       JSObjectSetProperty(
    
  116.         context,
    
  117.         result,
    
  118.         JSStringCreateWithUTF8CString(key.c_str()),
    
  119.         JSValueMakeNumber(context, value),
    
  120.         kJSPropertyAttributeNone,
    
  121.         NULL
    
  122.       );
    
  123.     },
    
  124.     &pair);
    
  125. 
    
  126.   return result;
    
  127. }
    
  128. 
    
  129. int main(int argc, char **argv) {
    
  130.   if (argc != 2) {
    
  131.     fprintf(stderr, "usage: jsc-runner file\n");
    
  132.     exit(1);
    
  133.   }
    
  134. 
    
  135.   char *filename = argv[1];
    
  136.   std::ifstream ifs(filename);
    
  137.   if (ifs.fail()) {
    
  138.     std::cerr << "Error opening \"" << filename << "\": " << strerror(errno) << "\n";
    
  139.     exit(1);
    
  140.   }
    
  141.   std::string script(
    
  142.     (std::istreambuf_iterator<char>(ifs)),
    
  143.     (std::istreambuf_iterator<char>())
    
  144.   );
    
  145.   JSStringRef jsScript = JSStringCreateWithUTF8CString(script.c_str());
    
  146.   JSStringRef jsURL = JSStringCreateWithUTF8CString(argv[1]);
    
  147. 
    
  148.   JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
    
  149.   add_native_hook(
    
  150.     ctx,
    
  151.     JSContextGetGlobalObject(ctx),
    
  152.     "print",
    
  153.     js_print
    
  154.   );
    
  155. 
    
  156.   JSObjectRef jsPerfCounters = JSObjectMake(ctx, NULL, NULL);
    
  157.   add_native_hook(
    
  158.     ctx,
    
  159.     jsPerfCounters,
    
  160.     "init",
    
  161.     js_perf_counters_init
    
  162.   );
    
  163.   add_native_hook(
    
  164.     ctx,
    
  165.     jsPerfCounters,
    
  166.     "getCounters",
    
  167.     js_perf_counters_get_counters
    
  168.   );
    
  169.   JSObjectSetProperty(
    
  170.     ctx,
    
  171.     JSContextGetGlobalObject(ctx),
    
  172.     JSStringCreateWithUTF8CString("PerfCounters"),
    
  173.     jsPerfCounters,
    
  174.     kJSPropertyAttributeNone,
    
  175.     NULL
    
  176.   );
    
  177. 
    
  178.   JSValueRef jsError = NULL;
    
  179.   JSValueRef result = JSEvaluateScript(
    
  180.     ctx,
    
  181.     jsScript,
    
  182.     NULL,
    
  183.     jsURL,
    
  184.     0,
    
  185.     &jsError
    
  186.   );
    
  187.   if (!result) {
    
  188.     fprintf(stderr, "Exception: ");
    
  189.     fprint_value(stderr, ctx, jsError);
    
  190.     fprintf(stderr, "\n");
    
  191.     JSStringRef jsStackStr = JSStringCreateWithUTF8CString("stack");
    
  192.     if (JSValueIsObject(ctx, jsError)) {
    
  193.       JSValueRef jsStack = JSObjectGetProperty(ctx, (JSObjectRef)jsError, jsStackStr, NULL);
    
  194.       JSStringRelease(jsStackStr);
    
  195.       fprint_value(stderr, ctx, jsStack);
    
  196.       fprintf(stderr, "\n");
    
  197.     }
    
  198.     exit(1);
    
  199.   }
    
  200. 
    
  201.   JSGlobalContextRelease(ctx);
    
  202. }