diff memory_profiler.cpp @ 50:853f80bd3b4b

Added tentative profileMemory() server, which suspends the current JS runtime, creates a new one and runs a web server in it.
author Atul Varma <varmaa@toolness.com>
date Wed, 24 Jun 2009 16:40:51 -0700
parents
children 8750e9ecf3af
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/memory_profiler.cpp	Wed Jun 24 16:40:51 2009 -0700
@@ -0,0 +1,178 @@
+#include "jsdhash.h"
+
+#include "memory_profiler.h"
+#include "server_socket.h"
+
+#define SERVER_FILENAME "memory_profiler_server.js"
+
+// TODO: Lots of code here is copied from spidermonkey-playground.cpp; we should
+// consolidate it.
+
+/* The error reporter callback. */
+static void reportError(JSContext *cx, const char *message, JSErrorReport *report)
+{
+    fprintf(stderr, "%s:%u:%s\n",
+            report->filename ? report->filename : "<no filename>",
+            (unsigned int) report->lineno,
+            message);
+}
+
+static JSClass global_class = {
+  "serverGlobal", JSCLASS_GLOBAL_FLAGS,
+  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+// This native JS function prints the given string to the console.
+
+static JSBool print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+                    jsval *rval)
+{
+  char *str;
+
+  if (!JS_ConvertArguments(cx, argc, argv, "s", &str))
+    return JS_FALSE;
+
+  printf("%s\n", str);
+
+  return JS_TRUE;
+}
+
+// Private structure to track the state of tracing the JS heap.
+
+typedef struct TracingState {
+  // Keeps track of what objects we've visited so far.
+  JSDHashTable visited;
+
+  // Whether the tracing operation is successful or failed.
+  JSBool result;
+
+  // Class to look for.
+  JSClass *classp;
+};
+
+// Static singleton for tracking the state of tracing the JS heap.
+static TracingState tracingState;
+
+// JSTraceCallback for heap dumping and other operations.
+
+static void traceCallback(JSTracer *trc, void *thing, uint32 kind)
+{
+  switch (kind) {
+  case JSTRACE_OBJECT:
+    JSDHashEntryStub *entry = (JSDHashEntryStub *)
+      JS_DHashTableOperate(&tracingState.visited,
+                           thing,
+                           JS_DHASH_LOOKUP);
+    if (JS_DHASH_ENTRY_IS_FREE((JSDHashEntryHdr *)entry)) {
+      entry = (JSDHashEntryStub *) JS_DHashTableOperate(&tracingState.visited,
+                                                        thing,
+                                                        JS_DHASH_ADD);
+      if (entry == NULL) {
+        JS_ReportOutOfMemory(trc->context);
+        tracingState.result = JS_FALSE;
+        return;
+      }
+      entry->key = thing;
+      JSObject *obj = (JSObject *) thing;
+      // TODO: Do something?
+      JS_TraceChildren(trc, thing, kind);
+    }
+    break;
+  case JSTRACE_DOUBLE:
+    break;
+  case JSTRACE_STRING:
+    break;
+  }
+}
+
+static JSFunctionSpec server_global_functions[] = {
+  JS_FS("print",          print,              1, 0, 0),
+  JS_FS("ServerSocket",   createServerSocket, 0, 0, 0),
+  JS_FS_END
+};
+
+JSBool profileMemory(JSContext *cx, JSObject *obj, uintN argc,
+                     jsval *argv, jsval *rval)
+{
+  JSTracer tracer;
+
+  if (!JS_DHashTableInit(&tracingState.visited, JS_DHashGetStubOps(),
+                         NULL, sizeof(JSDHashEntryStub),
+                         JS_DHASH_DEFAULT_CAPACITY(100))) {
+    JS_ReportOutOfMemory(cx);
+    return JS_FALSE;
+  }
+
+  tracingState.result = JS_TRUE;
+  tracer.context = cx;
+  tracer.callback = traceCallback;
+  JS_TraceRuntime(&tracer);
+
+  JS_DHashTableFinish(&tracingState.visited);
+
+  if (!tracingState.result)
+    return JS_FALSE;
+
+  JSRuntime *serverRuntime;
+  serverRuntime = JS_NewRuntime(8L * 1024L * 1024L);
+  if (serverRuntime == NULL) {
+    JS_ReportError(cx, "Couldn't create server JS runtime.");
+    return JS_FALSE;
+  }
+
+  JSContext *serverCx = JS_NewContext(serverRuntime, 8192);
+  if (serverCx == NULL) {
+    JS_ReportError(cx, "Couldn't create server JS context.");
+    return JS_FALSE;
+  }
+  JS_SetOptions(serverCx, JSOPTION_VAROBJFIX);
+  JS_SetVersion(serverCx, JSVERSION_LATEST);
+  JS_SetErrorReporter(serverCx, reportError);
+
+  JSObject *serverGlobal = JS_NewObject(serverCx, &global_class, NULL, NULL);
+  if (serverGlobal == NULL) {
+    JS_ReportError(cx, "Couldn't create server JS global.");
+    return JS_FALSE;
+  }
+
+  if (!JS_InitStandardClasses(serverCx, serverGlobal)) {
+    JS_ReportError(cx, "Couldn't init standard classes on server JS global.");
+    return JS_FALSE;
+  }
+
+  if (!JS_DefineFunctions(serverCx, serverGlobal, server_global_functions)) {
+    JS_ReportError(cx, "Couldn't define functions on server JS global.");
+    return JS_FALSE;
+  }
+
+  FILE *f = fopen(SERVER_FILENAME, "r");
+  if (!f) {
+    JS_ReportError(cx, "Couldn't open " SERVER_FILENAME);
+    return JS_FALSE;
+  }
+
+  fseek(f, 0, SEEK_END);
+  long fileSize = ftell(f);
+  fseek(f, 0, SEEK_SET);
+  
+  char source[fileSize];
+  fread(source, fileSize, 1, f);
+  fclose(f);
+
+  jsval serverRval;
+  if (!JS_EvaluateScript(serverCx, serverGlobal, source,
+                         fileSize, SERVER_FILENAME, 1,
+                         &serverRval)) {
+    JS_ReportError(cx, "Server failed.");
+    return JS_FALSE;
+  }
+
+  /* Cleanup. */
+  JS_DestroyContext(serverCx);
+  JS_DestroyRuntime(serverRuntime);
+
+  *rval = JSVAL_VOID;
+  return JS_TRUE;
+}