#include #include #include #include #include #include #include #include #include #ifdef __unix__ #include #include #include #include #include #endif /* when a fatal error is discovered (PF_ASSERT) an external process is started with arguments you can use this to attach gdb for instance. */ #define ATTACH_DEBUGGER "pf-gdb" static int attach_on_assert = 0; static volatile int debug_pid = -1; // unique global variable used by attach-debugger script // to enable continuation of execution volatile int wait_for_debugger = 1; #define ME "DEBUG: " /* send a debug trap signal */ void pf_debug_trap(void) { #ifdef __unix__ kill(getpid(), SIGTRAP); #else pf_post("WARNING: pf_debug_trap(): not supported"); #endif } static void check_debugger(void){ #ifdef __unix__ if (-1 == debug_pid) return; // no pid if (!waitpid(debug_pid, 0, WNOHANG)) return; // still running pf_post(ME "Debugger has exited."); waitpid(debug_pid, 0, 0);; // if exited, reap it debug_pid = -1; #endif } /* run shell command: attachgdb this can be used to launch an "emergency debugger" */ void pf_debug_attach_debugger(int stackframe) { #ifdef __unix__ int cont = 0; char pid[10], frame[10]; // find out if we have a debugger running check_debugger(); if (-1 != debug_pid){ pf_post(ME "Debugger already running."); return; } // start debugger sprintf(pid, "%d", getpid()); sprintf(frame, "%d", stackframe); pf_post(ME "Executing debugger : \"%s %s %s\".", ATTACH_DEBUGGER, pid, frame); if (!(debug_pid = fork())){ if (-1 == execlp(ATTACH_DEBUGGER, ATTACH_DEBUGGER, pid, frame, NULL)) exit(1); // exit child } #endif } void pf_debug_assert_hook (const char *condition, const char *file, int line, const char *function) { pf_post("%s:%d:error in %s : assert (%s) failed ", file, line, function, condition); // gcc style error message #ifdef __unix__ if (attach_on_assert){ // if already running, send SIGTRAP check_debugger(); if (-1 != debug_pid){ pf_post(ME "Entering trap."); pf_debug_trap(); pf_post(ME "Back from trap."); return; } // spawn debugger pf_debug_attach_debugger(0); // find out how to pass stackframe address // number = relative stackframe from point where debugger attaches // todo: find a way to pass the stackframe address // raise SIGTRAP anyway when starting debugger failed if (-1 == debug_pid){ pf_debug_trap(); return; } // if success, loop until global variable is set // the attach-debugger script feeds gdb with a // 'set wait_for_debugger = 0' line pf_post_n(ME "Waiting for attach."); while (wait_for_debugger){ struct timespec tv; tv.tv_sec = 0; tv.tv_nsec = 100000000; nanosleep(&tv, NULL); pf_post_n("."); check_debugger(); if (-1 == debug_pid){ pf_post(ME "Failed to start debugger."); pf_debug_trap(); } } pf_post(""); pf_post(ME "Resuming operation."); return; } else { pf_debug_trap(); } #endif exit(1); } /*** DEBUG && PROFILE ***/ //#define PF_PROFILE #ifdef PF_PROFILE // use pentium cycle counter typedef unsigned long long counter_t; static counter_t counter_get(void){ counter_t count; unsigned long *c = (unsigned long *)&count; asm("rdtsc; movl %%edx, %0; movl %%eax, %1" : "=r" (*(c+1)), "=r" (*c) : /* no input */ : "%edx", "%eax"); return count; } #endif // debug print, since all other printing uses higher level features. void pf_debug_post_stack(pf_list_t *s){ // pf_post_n("STACK: "); pf_atom_t *a = s->first; while(a){ switch(a->t){ case a_packet: { pf_header_t *h = pf_packet_header(a->w.w_packet); pf_symbol_t *type = pf_packet_type(h); if (type == pf_symbol("string")) { pf_post_n("\"%s\" ", pf_packet_string_data(a->w.w_packet)); } else { pf_post_n(" ", pf_packet_type(h)->s_name); } break; } case a_list: pf_post_n(" "); break; case a_undef: pf_post_n(" "); break; case a_symbol: pf_post_n("%s ", a->w.w_symbol->s_name); break; case a_error: pf_post_n("%s ", pf_error(a->w.w_error)); break; case a_float: pf_post_n("%f ", a->w.w_float); break; case a_int: pf_post_n("%d ", a->w.w_int); break; case a_atom_pointer: pf_post_n("<%x> ", a->w.w_pointer); break; case a_forth_xt: pf_post_n(" ", a->w.w_pointer); break; default: // PF_ASSERT(0 && "unprintable"); pf_post_n(" ", a->t); break; } a = a -> next; } pf_post(""); } //pf_error_t pf_bug(char *msg) { // pf_post(msg); // pf_debug_trap(); // return e_bug; //} void pf_debug_setup(void) { //char *c = malloc(1000); //int i; //for (i=0; i<2000; i++) c[i] = 0; // see vars above //attach_on_assert = 1; // TODO: make this configurable from forth }