Mercurial > spidermonkey-playground
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; +}