/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * treedump.c: Dump the SPL VM state as human-readable tree */ #include #include #include "spl.h" #include "compat.h" static void print_node(FILE *file, struct spl_node *node, const char *linkid, int flags) { if ( linkid ) fprintf(file, "%s", linkid); else fprintf(file, "%s", node->path); if ( flags & SPL_TREEDUMP_FLAG_ADDR ) fprintf(file, " [%p]", node); fprintf(file, "\n"); if (node->value && (flags & SPL_TREEDUMP_FLAG_VALUE) ) { char *value = spl_hash_encode(spl_get_string(node)); fprintf(file, "\tV: %s\n", *value == '?' ? value+1 : value); free(value); } if ( node->hnode_name && (flags & SPL_TREEDUMP_FLAG_HNODE) ) fprintf(file, "\tH: %s\n", node->hnode_name); if ( node->flags && (flags & SPL_TREEDUMP_FLAG_FLAGS) ) { fprintf(file, "\tF:"); #define X(n) if ( node->flags & SPL_NODE_FLAG_ ## n ) fprintf(file, " " #n); X(FUNCTION) X(METHOD) X(RETINFO) X(STATIC) X(CLASS) X(CLNULL) X(CLEXCEPT) X(EXCEPTION) X(TRY) X(CATCH) X(RERES) X(ARGC) X(IS_INT) X(IS_FLOAT) X(GC) #undef X fprintf(file, "\n"); } if (node->ctx && (flags & SPL_TREEDUMP_FLAG_CTX) ) fprintf(file, "\tC: %s\n", node->ctx->path); if (node->cls && (flags & SPL_TREEDUMP_FLAG_CLS) ) fprintf(file, "\tO: %s\n", node->cls->path); if (node->ctx_type && (flags & SPL_TREEDUMP_FLAG_CTXTYPE) ) fprintf(file, "\tT: %s\n", node->ctx_type == SPL_CTX_FUNCTION ? "FUNCTION" : node->ctx_type == SPL_CTX_OBJECT ? "OBJECT" : node->ctx_type == SPL_CTX_LOCAL ? "LOCAL" : node->ctx_type == SPL_CTX_ROOT ? "ROOT" : "???"); if ( linkid && (flags & SPL_TREEDUMP_FLAG_LINK) ) fprintf(file, "\tL: %s\n", node->path); } static void dump_node(int pass, FILE *file, struct spl_node *node, struct spl_node *parent, char *path, int recurs_level, int flags, const char *prefix) { if (recurs_level > 1024) { spl_report(SPL_REPORT_RUNTIME, 0, "Trying to treedump cyclic object tree!\n"); return; } if (pass == 0) { if (node->dump_tag == 1) return; node->dump_tag = 1; if (node->path) free(node->path); node->path = 0; node->path_metric = 0; node->path_parent = 0; if (node->ctx) dump_node(pass, file, node->ctx, node, path, recurs_level+1, flags, prefix); if (node->cls) dump_node(pass, file, node->cls, node, path, recurs_level+1, flags, prefix); } else node->dump_tag = 0; if (pass == 1) { int this_metric = 2; for (char *p = path; (p = strstr(p, "*CLS*")); p++) this_metric+=10; for (char *p = path; (p = strstr(p, "..")); p++) this_metric++; if (prefix && *prefix) do { int path_len = strlen(path); int prefix_len = strlen(prefix); if (path_len < prefix_len) { char tmp[path_len+2]; snprintf(tmp, path_len+2, "%s.", path); if (strncmp(prefix, tmp, path_len+1)) break; } else if (path_len > prefix_len) { char tmp[prefix_len+2]; snprintf(tmp, prefix_len+2, "%s.", prefix); if (strncmp(path, tmp, prefix_len+1)) break; } else if (strcmp(path, prefix)) break; for (const char *p = prefix; (p = strstr(p, "..")); p++) this_metric--; if (this_metric < 1) this_metric = 1; } while (0); if (!node->path_metric || node->path_metric > this_metric) { if (node->path) free(node->path); node->path = strdup(path); node->path_metric = this_metric; node->path_parent = parent; } else return; if (node->ctx) { int id_len = strlen(path)+2; char id[id_len]; snprintf(id, id_len, "%s.", path); dump_node(pass, file, node->ctx, node, id, recurs_level+1, flags, prefix); } if (node->cls) { int id_len = strlen(path)+7; char id[id_len]; snprintf(id, id_len, "%s.*CLS*", path); dump_node(pass, file, node->cls, node, id, recurs_level+1, flags, prefix); } } if (pass == 2) { print_node(file, node, 0, flags); if (node->ctx && node->ctx->path_parent == node) dump_node(pass, file, node->ctx, node, path, recurs_level+1, flags, prefix); if (node->cls && node->cls->path_parent == node) dump_node(pass, file, node->cls, node, path, recurs_level+1, flags, prefix); } char *last_mod = 0; struct spl_node_sub *s = node->subs_begin; while (s) { if (s->module && !(flags & SPL_TREEDUMP_FLAG_MODS)) goto skip_this_sub; if (pass == 1) { int id_len = strlen(path)+strlen(s->key)+2; if (s->module) id_len += strlen(s->module)+9; char id[id_len]; if (s->module) { snprintf(id, id_len, "%s.(MOD).(%s).%s", path, s->module, s->key); } else snprintf(id, id_len, "%s.%s", path, s->key); dump_node(pass, file, s->node, node, id, recurs_level+1, flags, prefix); } else if (pass != 2 || s->node->path_parent == node) { if (pass == 2 && s->module && (!last_mod || strcmp(last_mod, s->module))) { if (!last_mod) fprintf(file, "%s.(MOD)\n", node->path); fprintf(file, "%s.(MOD).(%s)\n", node->path, s->module); last_mod = s->module; } dump_node(pass, file, s->node, node, path, recurs_level+1, flags, prefix); } else if (pass == 2) { int id_len = strlen(node->path)+strlen(s->key)+2; if (s->module) id_len += strlen(s->module)+9; char id[id_len]; if (s->module) { if (!last_mod || strcmp(last_mod, s->module)) { fprintf(file, "%s.(MOD).(%s)\n", path, s->module); last_mod = s->module; } snprintf(id, id_len, "%s.(MOD).(%s).%s", node->path, s->module, s->key); } else snprintf(id, id_len, "%s.%s", node->path, s->key); print_node(file, s->node, id, flags); } skip_this_sub: s = s->next; } } void spl_treedump(struct spl_vm *vm, FILE *file, int flags, const char *prefix) { struct spl_task *t; dump_node(0, 0, vm->root, 0, 0, 2, flags, prefix); t = flags & SPL_TREEDUMP_FLAG_TASKS ? vm->task_list : 0; while (t) { struct spl_node_stack *s = t->stack; while (s) { dump_node(0, 0, s->node, 0, 0, 2, flags, prefix); s = s->next; } t = t->next; } dump_node(1, 0, vm->root, 0, "ROOT", 2, flags, prefix); t = flags & SPL_TREEDUMP_FLAG_TASKS ? vm->task_list : 0; while (t) { int id_len = (t->id ? strlen(t->id) : 10) + 32; char id[id_len]; if (t->ctx) { snprintf(id, id_len, "TASK(%s).CTX", t->id); dump_node(1, 0, t->ctx, 0, id, 2, flags, prefix); } struct spl_node_stack *s = t->stack; for (int i=0; s; i++) { snprintf(id, id_len, "TASK(%s).STACK(%d)", t->id, i); dump_node(1, 0, s->node, 0, id, 2, flags, prefix); s = s->next; } t = t->next; } dump_node(2, file, vm->root, 0, 0, 2, flags, prefix); t = flags & SPL_TREEDUMP_FLAG_TASKS ? vm->task_list : 0; while (t) { int id_len = (t->id ? strlen(t->id) : 10) + 32; char id[id_len]; if ( flags & SPL_TREEDUMP_FLAG_ADDR ) fprintf(file, "TASK(%s) <%p>\n", t->id, t); else fprintf(file, "TASK(%s)\n", t->id); if (t->ctx) { snprintf(id, id_len, "TASK(%s).CTX", t->id); if ( !strcmp(id, t->ctx->path) ) dump_node(2, file, t->ctx, 0, 0, 2, flags, prefix); else print_node(file, t->ctx, id, flags); } struct spl_node_stack *s = t->stack; for (int i=0; s; i++) { snprintf(id, id_len, "TASK(%s).STACK(%d)", t->id, i); if ( !strcmp(id, s->node->path) ) dump_node(2, file, s->node, 0, 0, 2, flags, prefix); else print_node(file, s->node, id, flags); s = s->next; } t = t->next; } } static char *bt_getfuncname(struct spl_node *ctx, struct spl_task *task) { while (ctx) { if (ctx->ctx_type == SPL_CTX_FUNCTION) { struct spl_node *f = spl_lookup(task, ctx, "?#func", SPL_LOOKUP_NOCTX); if (f) return spl_get_string(f); break; } if (ctx->ctx_type == SPL_CTX_ROOT) return "ROOT"; if (ctx->ctx_type != SPL_CTX_LOCAL) break; ctx = ctx->ctx; } return "????"; } static void bt_layout_info(char *debug, struct spl_code *code, int code_ip, char **info_p) { if (*info_p) free(*info_p); if (!debug || !*debug) { my_asprintf(info_p, " (byte %d in code block '%s')", code_ip, code->id ? code->id : ""); return; } int lineno=0, charno=0; char *srcfile = debug; lineno = atoi(srcfile); while ( *srcfile && *srcfile != ':' ) srcfile++; if ( *srcfile == ':' ) srcfile++; charno = atoi(srcfile); while ( *srcfile && *srcfile != ':' ) srcfile++; if ( *srcfile == ':' ) srcfile++; my_asprintf(info_p, " (near line %d, char %d in '%s')", lineno, charno, srcfile); } void spl_backtrace(struct spl_task *task, FILE *file) { char *info = 0; if (!task || !task->code) return; if ( (task->id && *task->id && strcmp(task->id, "main")) || (task->module && *task->module) ) fprintf(file, "While executing task '%s'%s%s%s:\n", task->id ? task->id : "", task->module ? " (" : "", task->module ? task->module : "", task->module ? ")" : ""); bt_layout_info(task->debug_str, task->code, task->code_ip, &info); fprintf(file, "in: %s%s\n", bt_getfuncname(task->ctx, task), info); struct spl_node_stack *retinfo = task->stack; while (retinfo) { if (retinfo->node->flags & SPL_NODE_FLAG_RETINFO) { bt_layout_info(spl_get_string(retinfo->node), retinfo->node->code, retinfo->node->code_ip, &info); fprintf(file, "called by: %s%s\n", bt_getfuncname(retinfo->node->ctx, task), info); } retinfo = retinfo->next; } free(info); } char *spl_treedump_string(struct spl_vm *vm, int flags, const char *prefix) { #if !defined USEWIN32API && !defined USECYGWINAPI && !defined USEMACOSXAPI && !defined USEBSDAPI && !defined USEIRIXAPI char *bp; size_t size; FILE *stream; stream = open_memstream(&bp, &size); spl_treedump(vm, stream, flags, prefix); fclose(stream); return bp; #else FILE *tempfile = tmpfile(); spl_treedump(vm, tempfile, flags, prefix); size_t size = ftell(tempfile); char *bp = malloc(size + 1); rewind(tempfile); fread(bp, size, 1, tempfile); bp[size] = 0; fclose(tempfile); return bp; #endif } char *spl_backtrace_string(struct spl_task *task) { #if !defined USEWIN32API && !defined USECYGWINAPI && !defined USEMACOSXAPI && !defined USEBSDAPI && !defined USEIRIXAPI char *bp; size_t size; FILE *stream; stream = open_memstream(&bp, &size); spl_backtrace(task, stream); fclose(stream); return bp; #else FILE *tempfile = tmpfile(); spl_backtrace(task, tempfile); size_t size = ftell(tempfile); char *bp = malloc(size + 1); rewind(tempfile); fread(bp, size, 1, tempfile); bp[size] = 0; fclose(tempfile); return bp; #endif }