Mercurial > spidermonkey-playground
changeset 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 | 45fd970860f2 |
children | 8750e9ecf3af |
files | memory_profiler.cpp memory_profiler.h memory_profiler_server.js pavement.py spidermonkey-playground.cpp tcb.js |
diffstat | 6 files changed, 236 insertions(+), 155 deletions(-) [+] |
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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/memory_profiler.h Wed Jun 24 16:40:51 2009 -0700 @@ -0,0 +1,4 @@ +#include "jsapi.h" + +extern JSBool profileMemory(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/memory_profiler_server.js Wed Jun 24 16:40:51 2009 -0700 @@ -0,0 +1,48 @@ +var socket = new ServerSocket(); + +var PORT = 8080; + +socket.bind("127.0.0.1", PORT); +socket.listen(); + +var NEWLINE = "\r\n"; +var DOUBLE_NEWLINE = NEWLINE + NEWLINE; + +function getHeaders(conn) { + var headers = ""; + while (1) { + var character = conn.recv(1); + if (character == null) + return null; + headers += character; + if (headers.indexOf(DOUBLE_NEWLINE) != -1) + return headers; + } +} + +print("Waiting for requests on port " + PORT + "."); + +while (1) { + var conn = socket.accept(); + + if (conn) { + var requestHeaders = getHeaders(conn); + if (requestHeaders == null) + continue; + var requestLines = requestHeaders.split("\r\n"); + print(requestLines[0]); + //print("headers: " + uneval(requestHeaders)); + var toSend = "hello there."; + var headerLines = ["HTTP/1.0 200 OK", + "Content-Type: text/plain", + "Connection: close", + "Content-Length: " + toSend.length]; + var headers = headerLines.join("\r\n"); + var response = headers + DOUBLE_NEWLINE + toSend; + //print("response: " + uneval(response)); + conn.send(response); + conn.close(); + //print("response sent."); + } else + throw new Error("Unexpected: conn is " + conn); +}
--- a/pavement.py Wed Jun 24 16:10:12 2009 -0700 +++ b/pavement.py Wed Jun 24 16:40:51 2009 -0700 @@ -17,6 +17,7 @@ "spidermonkey-playground.cpp", "wrapper.cpp", "server_socket.cpp", + "memory_profiler.cpp", "-lmozjs", "-lnspr4"]
--- a/spidermonkey-playground.cpp Wed Jun 24 16:10:12 2009 -0700 +++ b/spidermonkey-playground.cpp Wed Jun 24 16:40:51 2009 -0700 @@ -41,10 +41,10 @@ #include "string.h" #include "jsapi.h" #include "jsdbgapi.h" -#include "jsdhash.h" #include "wrapper.h" #include "server_socket.h" +#include "memory_profiler.h" // The name of our JS script containing our Trusted Code Base (TCB). #define TCB_FILENAME "tcb.js" @@ -57,7 +57,7 @@ // the interpreter doesn't randomly crash or something. /* The class of the global object. */ -static JSClass global_class = { +JSClass global_class = { "global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, @@ -79,116 +79,6 @@ 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; - JSClass *classp = JS_GET_CLASS(trc->context, obj); - if (classp == tracingState.classp) { - JSObject *constructor = JS_GetConstructor(trc->context, obj); - if (constructor == NULL) { - tracingState.result = JS_FALSE; - return; - } - if (JS_ObjectIsFunction(trc->context, constructor)) { - JSFunction *func = JS_ValueToFunction( - trc->context, - OBJECT_TO_JSVAL(constructor) - ); - if (func == NULL) { - tracingState.result = JS_FALSE; - return; - } - JSString *name = JS_GetFunctionId(func); - if (name != NULL) { - const char *str = JS_GetStringBytes(name); - printf("function: %s\n", str); - } - } - } - JS_TraceChildren(trc, thing, kind); - } - break; - case JSTRACE_DOUBLE: - break; - case JSTRACE_STRING: - break; - } -} - -// This native JS function dumps the heap to stdout. It's available even on -// non-debug builds of SpiderMonkey, but it's also not terribly robust. - -static JSBool dumpHeap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *object; - - if (!JS_ConvertArguments(cx, argc, argv, "o", &object)) - return JS_FALSE; - - tracingState.classp = JS_GET_CLASS(cx, object); - - if (tracingState.classp == NULL) { - JS_ReportError(cx, "Object has no class."); - return JS_FALSE; - } - - 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; - *rval = JSVAL_VOID; - return JS_TRUE; -} - // This native JS function forces garbage collection. static JSBool forceGC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, @@ -491,7 +381,7 @@ JS_FS("functionInfo", functionInfo, 1, 0, 0), JS_FS("enumerate", enumerate, 1, 0, 0), JS_FS("forceGC", forceGC, 0, 0, 0), - JS_FS("dumpHeap", dumpHeap, 0, 0, 0), + JS_FS("profileMemory", profileMemory, 0, 0, 0), JS_FS("ServerSocket", createServerSocket, 0, 0, 0), JS_FS_END }; @@ -557,7 +447,7 @@ jsval rval; if (!JS_EvaluateScript(tcb_cx, tcb_global, source, - strlen(source), TCB_FILENAME, 1, + fileSize, TCB_FILENAME, 1, &rval)) { jsval handleError; if (JS_GetProperty(tcb_cx, tcb_global, "handleError", &handleError) &&
--- a/tcb.js Wed Jun 24 16:10:12 2009 -0700 +++ b/tcb.js Wed Jun 24 16:40:51 2009 -0700 @@ -233,50 +233,10 @@ getWrapper(getWrapper(wrapped)) !== null) throw new Error("Getting the wrapper doesn't work!"); -dumpHeap(new Object()); - var socket = new ServerSocket(); try { var boop = {bind: socket.bind}; boop.bind("127.0.0.1", 8080); } catch (e) {} -socket.bind("127.0.0.1", 8080); -socket.listen(); -var NEWLINE = "\r\n"; -var DOUBLE_NEWLINE = NEWLINE + NEWLINE; - -function getHeaders(conn) { - var headers = ""; - while (1) { - var character = conn.recv(1); - if (character == null) - return null; - headers += character; - if (headers.indexOf(DOUBLE_NEWLINE) != -1) - return headers; - } -} - -while (1) { - var conn = socket.accept(); - - if (conn) { - var requestHeaders = getHeaders(conn); - if (requestHeaders == null) - continue; - //print("headers: " + uneval(requestHeaders)); - var toSend = "hello there."; - var headerLines = ["HTTP/1.0 200 OK", - "Content-Type: text/plain", - "Connection: close", - "Content-Length: " + toSend.length]; - var headers = headerLines.join("\r\n"); - var response = headers + DOUBLE_NEWLINE + toSend; - //print("response: " + uneval(response)); - conn.send(response); - conn.close(); - //print("response sent."); - } else - throw new Error("Unexpected: conn is " + conn); -} +profileMemory();