[gjs] Add gjs_context_get_all and gjs_dumpstack
- From: Colin Walters <walters src gnome org>
- To: svn-commits-list gnome org
- Subject: [gjs] Add gjs_context_get_all and gjs_dumpstack
- Date: Wed, 4 Mar 2009 12:27:54 -0500 (EST)
commit 6974e0fdfaeaccb821a5f10d60a695f5833d01bc
Author: Colin Walters <walters verbum org>
Date: Wed Mar 4 10:28:30 2009 -0500
Add gjs_context_get_all and gjs_dumpstack
The new gjs_dumpstack global function is useful to invoke from a debugger
such as gdb. gjs_context_get_all is infrastructure to support the implementation
of that function, but made public for other global debugging-like uses.
---
COPYING | 7 +-
Makefile.am | 2 +
gjs/context.c | 35 ++++++++
gjs/context.h | 9 ++
gjs/stack.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 298 insertions(+), 2 deletions(-)
diff --git a/COPYING b/COPYING
index 2759cdf..f5350bd 100644
--- a/COPYING
+++ b/COPYING
@@ -18,5 +18,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-The console module (modules/console.c) contains code from Mozilla which
-is tripple licensed under MPL1.1/LGPLv2+/GPLv2+.
+
+The following files contain code from Mozilla which
+is triple licensed under MPL1.1/LGPLv2+/GPLv2+:
+The console module (modules/console.c)
+Stack printer (gjs/stack.c)
diff --git a/Makefile.am b/Makefile.am
index 25619ea..a1bbf38 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -72,6 +72,7 @@ libgjs_la_SOURCES = \
gjs/mem.c \
gjs/native.c \
gjs/profiler.c \
+ gjs/stack.c \
util/error.c \
util/glib.c \
util/crash.c \
@@ -82,6 +83,7 @@ gjstest_files_with_tests += \
gjs/jsapi-util-array.c \
gjs/jsapi-util-error.c \
gjs/jsapi-util-string.c \
+ gjs/stack.c \
util/glib.c
include Makefile-gi.am
diff --git a/gjs/context.c b/gjs/context.c
index 8448343..b121a1b 100644
--- a/gjs/context.c
+++ b/gjs/context.c
@@ -86,6 +86,11 @@ enum {
PROP_IS_LOAD_CONTEXT
};
+
+static GStaticMutex contexts_lock = G_STATIC_MUTEX_INIT;
+static GList *all_contexts = NULL;
+
+
static JSBool
gjs_log(JSContext *context,
JSObject *obj,
@@ -291,6 +296,10 @@ gjs_context_finalize(GObject *object)
g_strfreev(js_context->search_path);
js_context->search_path = NULL;
}
+
+ g_static_mutex_lock(&contexts_lock);
+ all_contexts = g_list_remove(all_contexts, object);
+ g_static_mutex_unlock(&contexts_lock);
G_OBJECT_CLASS(gjs_context_parent_class)->finalize(object);
}
@@ -411,6 +420,10 @@ gjs_context_constructor (GType type,
js_context->profiler = gjs_profiler_new(js_context->runtime);
}
+ g_static_mutex_lock (&contexts_lock);
+ all_contexts = g_list_prepend(all_contexts, object);
+ g_static_mutex_unlock (&contexts_lock);
+
return object;
}
@@ -476,6 +489,28 @@ gjs_context_new_with_search_path(char** search_path)
NULL);
}
+/**
+ * gjs_context_get_all:
+ *
+ * Returns a newly-allocated list containing all known instances of #GjsContext.
+ * This is useful for operating on the contexts from a process-global situation
+ * such as a debugger.
+ *
+ * Return value: (element-type GjsContext) (transfer full): Known #GjsContext instances
+ */
+GList*
+gjs_context_get_all(void)
+{
+ GList *result;
+ GList *iter;
+ g_static_mutex_lock (&contexts_lock);
+ result = g_list_copy(all_contexts);
+ for (iter = result; iter; iter = iter->next)
+ g_object_ref((GObject*)iter->data);
+ g_static_mutex_unlock (&contexts_lock);
+ return result;
+}
+
JSContext*
gjs_context_get_context(GjsContext *js_context)
{
diff --git a/gjs/context.h b/gjs/context.h
index cbb9bed..a5b518c 100644
--- a/gjs/context.h
+++ b/gjs/context.h
@@ -62,6 +62,15 @@ gboolean gjs_context_define_string_array (GjsContext *js_context,
const char **array_values,
GError **error);
+GList* gjs_context_get_all (void);
+
+void gjs_context_print_stack_to_buffer (GjsContext *js_context,
+ GString *buf);
+
+void gjs_context_print_stack_stderr (GjsContext *js_context);
+
+void gjs_dumpstack (void);
+
G_END_DECLS
#endif /* __GJS_CONTEXT_H__ */
diff --git a/gjs/stack.c b/gjs/stack.c
new file mode 100644
index 0000000..611c4af
--- /dev/null
+++ b/gjs/stack.c
@@ -0,0 +1,247 @@
+/* This file contains code derived from xpcdebug.cpp in Mozilla. The license
+ * for that file follows:
+ */
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * John Bandhauer <jband netscape com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <config.h>
+#include "context-jsapi.h"
+#include <glib.h>
+#include <string.h>
+#include <jsdbgapi.h>
+
+static const char*
+jsvalue_to_string(JSContext* cx, jsval val, gboolean* is_string)
+{
+ const char* value = NULL;
+ JSString* value_str;
+
+ JS_EnterLocalRootScope(cx);
+
+ value_str = JS_ValueToString(cx, val);
+ if (value_str)
+ value = JS_GetStringBytes(value_str);
+ if (value) {
+ const char* found = strstr(value, "function ");
+ if(found && (value == found || value+1 == found || value+2 == found))
+ value = "[function]";
+ }
+
+ if (is_string)
+ *is_string = JSVAL_IS_STRING(val);
+
+ JS_LeaveLocalRootScope(cx);
+
+ return value;
+}
+
+
+static void
+format_frame(JSContext* cx, JSStackFrame* fp,
+ GString *buf, int num)
+{
+ JSPropertyDescArray call_props = { 0, NULL };
+ JSObject* this_obj = NULL;
+ JSObject* call_obj = NULL;
+ const char* funname = NULL;
+ const char* filename = NULL;
+ guint32 lineno = 0;
+ guint32 named_arg_count = 0;
+ JSFunction* fun = NULL;
+ JSScript* script;
+ guchar* pc;
+ guint32 i;
+ gboolean is_string;
+ jsval val;
+
+ JS_EnterLocalRootScope(cx);
+
+ if (JS_IsNativeFrame(cx, fp)) {
+ g_string_append_printf(buf, "%d [native frame]\n", num);
+ goto out;
+ }
+
+ /* get the info for this stack frame */
+
+ script = JS_GetFrameScript(cx, fp);
+ pc = JS_GetFramePC(cx, fp);
+
+ if (script && pc) {
+ filename = JS_GetScriptFilename(cx, script);
+ lineno = (guint32) JS_PCToLineNumber(cx, script, pc);
+ fun = JS_GetFrameFunction(cx, fp);
+ if (fun)
+ funname = JS_GetFunctionName(fun);
+
+ call_obj = JS_GetFrameCallObject(cx, fp);
+ if (call_obj) {
+ if (!JS_GetPropertyDescArray(cx, call_obj, &call_props))
+ call_props.array = NULL;
+ }
+
+ this_obj = JS_GetFrameThis(cx, fp);
+ }
+
+ /* print the frame number and function name */
+
+ if (funname)
+ g_string_append_printf(buf, "%d %s(", num, funname);
+ else if (fun)
+ g_string_append_printf(buf, "%d anonymous(", num);
+ else
+ g_string_append_printf(buf, "%d <TOP LEVEL>", num);
+
+ for (i = 0; i < call_props.length; i++) {
+ const char *name;
+ const char *value;
+ JSPropertyDesc* desc = &call_props.array[i];
+ if(desc->flags & JSPD_ARGUMENT) {
+ name = jsvalue_to_string(cx, desc->id, &is_string);
+ if(!is_string)
+ name = NULL;
+ value = jsvalue_to_string(cx, desc->value, &is_string);
+
+ g_string_append_printf(buf, "%s%s%s%s%s%s",
+ named_arg_count ? ", " : "",
+ name ? name :"",
+ name ? " = " : "",
+ is_string ? "\"" : "",
+ value ? value : "?unknown?",
+ is_string ? "\"" : "");
+ named_arg_count++;
+ }
+ }
+
+ /* print any unnamed trailing args (found in 'arguments' object) */
+
+ if (call_obj != NULL &&
+ JS_GetProperty(cx, call_obj, "arguments", &val) &&
+ JSVAL_IS_OBJECT(val)) {
+ guint32 k;
+ guint32 arg_count;
+ JSObject* args_obj = JSVAL_TO_OBJECT(val);
+ if (JS_GetProperty(cx, args_obj, "length", &val) &&
+ JS_ValueToECMAUint32(cx, val, &arg_count) &&
+ arg_count > named_arg_count) {
+ for (k = named_arg_count; k < arg_count; k++) {
+ char number[8];
+ g_snprintf(number, 8, "%d", (int) k);
+
+ if (JS_GetProperty(cx, args_obj, number, &val)) {
+ const char *value = jsvalue_to_string(cx, val, &is_string);
+ g_string_append_printf(buf, "%s%s%s%s",
+ k ? ", " : "",
+ is_string ? "\"" : "",
+ value ? value : "?unknown?",
+ is_string ? "\"" : "");
+ }
+ }
+ }
+ }
+
+ /* print filename and line number */
+
+ g_string_append_printf(buf, "%s [\"%s\":%d]\n",
+ fun ? ")" : "",
+ filename ? filename : "<unknown>",
+ lineno);
+ out:
+ JS_LeaveLocalRootScope(cx);
+}
+
+void
+gjs_context_print_stack_to_buffer(GjsContext* context, GString *buf)
+{
+ JSContext *js_context = gjs_context_get_context(context);
+ JSStackFrame* fp;
+ JSStackFrame* iter = NULL;
+ int num = 0;
+
+ g_string_append_printf(buf, "== Stack trace for context %p ==\n", context);
+ while ((fp = JS_FrameIterator(js_context, &iter)) != NULL) {
+ format_frame(js_context, fp, buf, num);
+ num++;
+ }
+
+ if(!num)
+ g_string_append_printf(buf, "(JavaScript stack is empty)\n");
+ g_string_append(buf, "\n");
+}
+
+void
+gjs_context_print_stack_stderr(GjsContext *context)
+{
+ GString *str = g_string_new("");
+ gjs_context_print_stack_to_buffer(context, str);
+ g_printerr("%s\n", str->str);
+ g_string_free(str, TRUE);
+}
+
+void
+gjs_dumpstack(void)
+{
+ GList *contexts = gjs_context_get_all();
+ GList *iter;
+
+ for (iter = contexts; iter; iter = iter->next) {
+ GjsContext *context = (GjsContext*)iter->data;
+ gjs_context_print_stack_stderr(context);
+ g_object_unref(context);
+ }
+ g_list_free(contexts);
+}
+
+#if GJS_BUILD_TESTS
+void
+gjstest_test_func_gjs_stack_dump(void)
+{
+ GjsContext *context;
+
+ g_type_init();
+
+ /* TODO this test could be better - maybe expose dumpstack as a JS API
+ * so that we have a JS stack to dump? At least here we're getting some
+ * coverage.
+ */
+ context = gjs_context_new();
+ gjs_dumpstack();
+ g_object_unref(context);
+ gjs_dumpstack();
+}
+#endif /* GJS_BUILD_TESTS */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]