/*USE This sample to start writing standalone programs. Change AnalyzeTrace to the program name of your choosing. */ #include "SUMA_suma.h" SUMA_SurfaceViewer *SUMAg_cSV = NULL; /*!< Global pointer to current Surface Viewer structure*/ SUMA_SurfaceViewer *SUMAg_SVv = NULL; /*!< Global pointer to the vector containing the various Surface Viewer Structures SUMAg_SVv contains SUMA_MAX_SURF_VIEWERS structures */ int SUMAg_N_SVv = 0; /*!< Number of SVs realized by X */ SUMA_DO *SUMAg_DOv = NULL; /*!< Global pointer to Displayable Object structure vector*/ int SUMAg_N_DOv = 0; /*!< Number of DOs stored in DOv */ SUMA_CommonFields *SUMAg_CF = NULL; /*!< Global pointer to structure containing info common to all viewers */ void usage_AnalyzeTrace (SUMA_GENERIC_ARGV_PARSE *ps) { static char FuncName[]={"usage_AnalyzeTrace"}; char * s = NULL, *sio=NULL, *st = NULL, *sts = NULL; int i; SUMA_ENTRY; s = SUMA_help_basics(); sio = SUMA_help_IO_Args(ps); printf ( "\n" "Usage: A program to analyze SUMA (and AFNI's perhaps) stack output\n" " The program can detect functions that return with RETURN without\n" " bothering to go on the stack.\n" " AnaylzeTrace [options] FILE \n" " where FILE is obtained by redirecting program's trace output.\n" "Optional Param:\n" " -max_func_lines N: Set the maximum number of code lines before a function\n" " returns. Default is no limit.\n" " -suma_c: FILE is a SUMA_*.c file. It is analyzed for functions that use SUMA_ RETURN \n" " (typo on purpose to avoid being caught here) without ENTRY\n" " Note: The file for this program has special strings (in comments at times)\n" " to avoid false alarms when processing it.\n" " \n" " -max_err MAX_ERR: Stop after encountering MAX_ERR errors\n" " reported in log. Default is 5.\n" " Error key terms are:\n" " 'Error', 'error', 'corruption'\n" "\n" "%s" "%s" "\n", sio, s); SUMA_free(s); s = NULL; SUMA_free(st); st = NULL; SUMA_free(sio); sio = NULL; s = SUMA_New_Additions(0, 1); printf("%s\n", s);SUMA_free(s); s = NULL; printf(" Ziad S. Saad SSCC/NIMH/NIH saadz@mail.nih.gov \n"); exit(0); } SUMA_GENERIC_PROG_OPTIONS_STRUCT *SUMA_AnalyzeTrace_ParseInput(char *argv[], int argc, SUMA_GENERIC_ARGV_PARSE *ps) { static char FuncName[]={"SUMA_AnalyzeTrace_ParseInput"}; SUMA_GENERIC_PROG_OPTIONS_STRUCT *Opt=NULL; int kar; SUMA_Boolean brk; SUMA_Boolean LocalHead = NOPE; SUMA_ENTRY; Opt = SUMA_Alloc_Generic_Prog_Options_Struct(); Opt->N_it = 10000000; Opt->obj_type = 0; Opt->N_XYZ = 5; kar = 1; brk = NOPE; while (kar < argc) { /* loop accross command ine options */ /*fprintf(stdout, "%s verbose: Parsing command line...\n", FuncName);*/ if (strcmp(argv[kar], "-h") == 0 || strcmp(argv[kar], "-help") == 0) { usage_AnalyzeTrace(ps); exit (0); } SUMA_SKIP_COMMON_OPTIONS(brk, kar); if (!brk && (strcmp(argv[kar], "-debug") == 0)) { if (kar+1 >= argc) { fprintf (SUMA_STDERR, "need a number after -debug \n"); exit (1); } Opt->debug = atoi(argv[++kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-max_err") == 0)) { if (kar+1 >= argc) { fprintf (SUMA_STDERR, "need a number after -max_err \n"); exit (1); } Opt->N_XYZ = atoi(argv[++kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-max_func_lines") == 0)) { if (kar+1 >= argc) { fprintf (SUMA_STDERR, "need a number after -max_func_lines \n"); exit (1); } Opt->N_it = atoi(argv[++kar]); brk = YUP; } if (!brk && (strcmp(argv[kar], "-suma_c") == 0)) { Opt->obj_type = 1; brk = YUP; } if (!brk && !ps->arg_checked[kar]) { /* Assume the rest is input data */ while (kar < argc) { if (Opt->n_in_namev < SUMA_GENERIC_PROG_MAX_IN_NAME) { Opt->in_namev[Opt->n_in_namev] = argv[kar]; ++Opt->n_in_namev; ++kar; } else { SUMA_S_Err("Too many input dsets on command line"); } } } else { brk = NOPE; kar ++; } } SUMA_RETURN(Opt); } typedef struct { char func[100]; char file[100]; int line; int level; int io; } SUMA_TRACE_STRUCT; void SUMA_ShowTraceStack(SUMA_TRACE_STRUCT *TS, int its, char *head) { int i, j; if (head) { fprintf(SUMA_STDERR, "%s", head); } else { fprintf(SUMA_STDERR, "Current Stack:\n" "--------------\n"); } for (i=0; i ss_init && *pti != '+' && *pti != '-') --pti; /* go back to first sign */ if (pti < ss_init) { SUMA_S_Err("Parsing error, number of consecutive + or - does not match level"); SUMA_RETURN(ss_init); } pti_retry = pti-1; /* store location to continue from if sign is bad news */ /*fprintf(SUMA_STDERR,"Level %d, *pti = %c, *(pti-lll+1)=%c\n", lll, *pti, *(pti-lll+1)); */ if (*(pti-lll+1) == *pti) { ; /* if the sign level characters back the same, then accept it */ ss = pti = pti-lll; /* put ss at beginning of sign and get out */ good = 1; break; } else { /* sign we went back to is bad, go beyond it */ pti = pti_retry; good = 0; } } while (!good); if (good) break; /* from outer while*/ } } /* fprintf(SUMA_STDERR,"%c ", *ss); */ ++ss; } #endif /* scan for errors */ ctmp = *ss; *ss = '\0'; if (strstr(ss_init,"Error")) *error = 1; else if (strstr(ss_init,"error")) *error = 1; else if (strstr(ss_init,"corruption")) *error = 1; else if (strstr(ss_init,"Deallocation") && strstr(ss_init,"pointer") && strstr(ss_init,"not") && strstr(ss_init,"malloced")) *error = 1; else *error = 0; *ss = ctmp; SUMA_RETURN(ss); } int SUMA_AnalyzeTraceFunc(char *fname, SUMA_GENERIC_PROG_OPTIONS_STRUCT *Opt) { static char FuncName[]={"SUMA_AnalyzeTraceFunc"}; char *fl = NULL, *flc = NULL, *fls = NULL, *flo = NULL, *fln = NULL, *fle = NULL, func[100], file[100], *comp_fl=NULL, stmp[300]; int level, cur_level, io, nread, its, line, error, cnt, N_comp_fl, Nrep, N_error = 0; SUMA_TRACE_STRUCT TS[100]; SUMA_Boolean Res = NOPE; FILE *fff=NULL; SUMA_Boolean LocalHead = NOPE; SUMA_ENTRY; if (Opt->debug > 1) LocalHead = YUP; its = 0; /* suck the trace */ nread = SUMA_suck_file( fname , &fl ) ; if (!fl) { SUMA_SL_Err("Failed to read file."); SUMA_RETURN(NOPE); } if (LocalHead) fprintf(SUMA_STDERR,"%s: Read in %d chars\n", FuncName, nread); if (0) { /* not ready yet */ comp_fl = (char *)SUMA_calloc(nread, sizeof(char)); N_comp_fl = 0; } fle = fl+nread; /* end of string */ fls = flc+50; /* set current stop location */ /* go to first entry */ cur_level = 0; func[0] = '\0'; Nrep = 0; fln = fl; do { flc = fln; /* set current location */ fln = SUMA_NextEntry(flc, &level, &io, func, file, &line, &error) ; if (fln == flc) { SUMA_S_Note("\nDone Checking.\nTrace looks OK (Note that exit() calls are not popped off the stack).\n"); SUMA_ShowTraceStack(TS, its, "Stack at Exit:\n"); Res = YUP; goto GETOUT; } if (cur_level == 0) { /* first time */ if (io != 1) { SUMA_S_Err("First entry is neg!"); } else { cur_level = level-1; /* initialization */ } } if (level > 0) { if (io == 1) { /* entry, make sure it is more than current level */ if (LocalHead) { fprintf(SUMA_STDERR, "DBG: Entering %s, level %d, io %d\n", func, level, io); } if (level != cur_level + 1) { fprintf(SUMA_STDERR, "Entering level %d from current level of %d!\n", level, cur_level); /* Show me the trace */ SUMA_ShowTraceStack(TS, its, "Stack at entry level error:\n"); sprintf(stmp,"Chunk in question at %s:%d\n", fname, SUMA_LineNumbersFromTo(fl, flc)+2); SUMA_ShowFromTo(flc, fln, stmp); Res = NOPE; goto GETOUT; } else { cur_level = level; snprintf(TS[its].func, 99*sizeof(char), "%s", func); snprintf(TS[its].file, 99*sizeof(char), "%s", file); /*fprintf(SUMA_STDERR,">>>>>>>>>>>%s<<<<<<<\n", TS[its].func);*/ TS[its].level = level; TS[its].io = io; TS[its].line = line; ++its; if (error) { sprintf(stmp,"\n" "Encountered %d%s error here in this chunk: %s:%d\n" "----------------------------------------\n", N_error+1, SUMA_COUNTER_SUFFIX((N_error+1)), fname, SUMA_LineNumbersFromTo(fl, flc)+2); SUMA_ShowFromTo(flc, fln, stmp); SUMA_ShowTraceStack(TS, its,"Stack at error reported in log:\n"); fprintf(SUMA_STDERR, "\n\n"); ++N_error; if (N_error >= Opt->N_XYZ) { fprintf(SUMA_STDERR, "Analysis terminated.\nMaximum error limit of %d reached.\n", Opt->N_XYZ); Res = NOPE; goto GETOUT; } } } } else if (io == -1) { /* exit, make sure level is current and function is same */ if (LocalHead) { fprintf(SUMA_STDERR, "DBG: Leaving %s, level %d, io %d\n", func, level, io); } if (its < 1) { fprintf(SUMA_STDERR, "Leaving function %s but with its = %d!\n", func, its); /* Show me the trace */ SUMA_ShowTraceStack(TS, its, "Stack at exit index error:\n"); sprintf(stmp,"Chunk in question at %s:%d\n", fname, SUMA_LineNumbersFromTo(fl, flc)+2); SUMA_ShowFromTo(flc, fln, stmp); Res = NOPE; goto GETOUT; } if (level != cur_level) { fprintf(SUMA_STDERR, "Leaving level %d from current level of %d!\n", level, cur_level); /* Show me the trace */ SUMA_ShowTraceStack(TS, its, "Stack at exit level error:\n"); sprintf(stmp,"Chunk in question at %s:%d\n", fname, SUMA_LineNumbersFromTo(fl, flc)+2); SUMA_ShowFromTo(flc, fln, stmp); Res = NOPE; goto GETOUT; } else { /* make sure func at leaving is same as one entering */ if (strcmp(func, TS[its-1].func) != 0) { fprintf(SUMA_STDERR, "Leaving func %s from level current func %s, its = %d!\n", func, TS[its-1].func, its-1); /* Show me the trace */ SUMA_ShowTraceStack(TS, its, "Stack at function discrepancy error:\n"); sprintf(stmp,"Chunk in question at %s:%d\n", fname, SUMA_LineNumbersFromTo(fl, flc)+2); SUMA_ShowFromTo(flc, fln, stmp); Res = NOPE; goto GETOUT; } /* make sure file at leaving is same as one entering */ if (strcmp(file, TS[its-1].file) != 0) { fprintf(SUMA_STDERR, "Leaving purported function (%s) from file %s which is different from entry file %s, its = %d!\n", func, file, TS[its-1].func, its-1); /* Show me the trace */ SUMA_ShowTraceStack(TS, its, "Stack at file discrepancy error:\n"); sprintf(stmp,"Chunk in question at %s:%d\n", fname, SUMA_LineNumbersFromTo(fl, flc)+2); SUMA_ShowFromTo(flc, fln, stmp); Res = NOPE; goto GETOUT; } /* make sure leaving after entrance*/ if (line < TS[its-1].line) { fprintf(SUMA_STDERR, "Leaving purported function (%s) at line %d which is before entry line %d, its = %d!\n" "Check function that is returning in %s:%d , perhaps it has no SUMA_ENTRY (or ENTRY).\n", func, line, TS[its-1].line, its-1, file, line); /* Show me the trace */ SUMA_ShowTraceStack(TS, its, "Stack at line absurdity error:\n"); sprintf(stmp,"Chunk in question at %s:%d\n", fname, SUMA_LineNumbersFromTo(fl, flc)+2); SUMA_ShowFromTo(flc, fln, stmp); Res = NOPE; goto GETOUT; } if (line - TS[its-1].line > Opt->N_it) { fprintf(SUMA_STDERR, "Note: Leaving purported function (%s) at line %d more than %d lines from entry line %d, its = %d!\n", func, line, Opt->N_it, TS[its-1].line, its-1); /* Show me the trace */ SUMA_ShowTraceStack(TS, its, "Stack at function largesse warning:\n"); sprintf(stmp,"Chunk in question at %s:%d\n", fname, SUMA_LineNumbersFromTo(fl, flc)+2); SUMA_ShowFromTo(flc, fln, stmp); } { /* OK, cleanup last its*/ --its; TS[its].func[0] = '\0'; TS[its].level = -1; TS[its].io = 0; cur_level = level - 1; if (error) { sprintf(stmp, "\n" "Encountered %d%s error here in this chunk: %s:%d\n" "-----------------------------------------\n", N_error+1, SUMA_COUNTER_SUFFIX((N_error+1)), fname, SUMA_LineNumbersFromTo(fl, flc)+2); SUMA_ShowFromTo(flc, fln, stmp); SUMA_ShowTraceStack(TS, its, "Stack at error reported in log:\n"); fprintf(SUMA_STDERR, "\n\n"); ++N_error; if (N_error >= Opt->N_XYZ) { fprintf(SUMA_STDERR, "Analysis terminated.\nMaximum error limit of %d reached.\n", Opt->N_XYZ); Res = NOPE; goto GETOUT; } } } } } } else { SUMA_S_Err("Error in level!"); } } while (fln > flc); GETOUT: /* seal comp_fl and write to disk */ if (0){ comp_fl[N_comp_fl] = '\0'; fopen("CompactTrace","w"); fprintf(fff,"%s",comp_fl); SUMA_free(comp_fl); comp_fl = NULL; fclose(fff); fff = NULL; } SUMA_free(fl); fl = NULL; SUMA_RETURN(Res); } char *SUMA_NextFunc(char *ss, char *sslim, int *io, char *func, char *file, int *line, int *error) { #if 0 /* static is not mandatory */ char *key[] = { "static", "char", "FuncName", "=", "{" , "}", ";", NULL}; int max_gap[] = { -1, 5, 5, 5, 5, -1, 20, -1 }; /* this comment with SUMA_ENTRY is placed here to avoid having the program trip on the next line Leave key as the first line . Same for this one SUMA_RETURN */ #else /* static is not mandatory */ char *key[] = { "char", "FuncName", "=", "{" , "}", ";", NULL}; int max_gap[] = { -1, 5, 5, 5, -1, 20, -1 }; /* this comment with SUMA_ENTRY is placed here to avoid having the program trip on the next line Leave key as the first line . Same for this one SUMA_RETURN */ #endif static char FuncName[]={"SUMA_NextFunc"}; char *ss_tmp = NULL; int cnt = 0, found = 0, ok=0; double num = 0.0; char *ss_func = NULL, *ss_entry = NULL, *ss_level= NULL, *ss_init=NULL, ctmp; SUMA_Boolean LocalHead = NOPE; SUMA_ENTRY; *io = 0; *line = -1; *error = -1; ss_init = ss; #if 0 /* doing search via strstr */ /* search from ss for key sequence */ found = 0; ss_entry = ss; ok = 1; while (key[found] && ok) { if (LocalHead) fprintf(SUMA_STDERR,"key[%d]=%s ", found, key[found]); ss_func = strstr(ss, key[found]); if (ss_func && ss_func >= ss) { if (LocalHead) fprintf(SUMA_STDERR," Found "); if (!found || max_gap[found] < 0 || ss_func - ss < max_gap[found]) { /* inside gap limit, OK */ if (LocalHead) fprintf(SUMA_STDERR," in gap "); ss = ss_func+strlen(key[found]); if (!found) ss_entry = ss_func; ++found; } else { if (LocalHead) fprintf(SUMA_STDERR," out of gap (%d, Augment by %d) ", ss_func - ss, (int)strlen(key[0])); /* move SS past first key found */ found = 0; ss = ss_entry + strlen(key[0]); ss_entry = ss; } } else { { ok = 0; } } } if (!ok) { SUMA_LH("Done."); SUMA_RETURN(ss_init); } else { if (ss_func) { ss = ss_func+strlen(key[found-1]); } else { SUMA_S_Err("WTF?"); exit(1); } } #else /* doing search via more flexible macro to test the latter. (both should give same results)*/ SUMA_ADVANCE_PAST_SEQUENCE(ss, sslim, ss_entry, key, max_gap, found, 0); if (!found) { SUMA_LH("Done."); SUMA_RETURN(ss_init); } else { } #endif /* copy the function name */ func[0] = '\0'; cnt = 0; while (ss_entry < ss && cnt < 99) { func[cnt] = *ss_entry; ++ss_entry; ++cnt; } func[cnt] = '\0'; if (LocalHead) { fprintf(SUMA_STDERR, "\n" "Next Function:\n" "ss_entry -ss = %d\n" "func = %s\n", ss_entry - ss, func); } SUMA_RETURN(ss); } int SUMA_AnalyzeSumaFunc(char *fname, SUMA_GENERIC_PROG_OPTIONS_STRUCT *Opt) { static char FuncName[]={"SUMA_AnalyzeSumaFunc"}; char *fl = NULL, rkey[100], *flc = NULL, *fls = NULL, *flo = NULL, *fln = NULL, *fle = NULL, func[100], file[100], ctmp, *sret, *sent; int level, cur_level, io, nread, its, line, error; SUMA_TRACE_STRUCT TS[100]; SUMA_Boolean LocalHead = NOPE; SUMA_ENTRY; if (Opt->debug > 1) LocalHead = YUP; its = 0; /* suck the file */ nread = SUMA_suck_file( fname , &fl ) ; if (!fl) { SUMA_SL_Err("Failed to read file."); SUMA_RETURN(NOPE); } if (LocalHead) fprintf(SUMA_STDERR,"%s: Read in %d chars\n", FuncName, nread); fle = fl+nread; /* end of string */ fls = flc+50; /* set current stop location */ /* go to first entry */ cur_level = 0; flc = fl; fln = SUMA_NextFunc(flc, fle, &io, func, file, &line, &error); do { flc = fln; /* set current location */ fprintf(SUMA_STDERR,"Analyzing function %s in %s:%d, ", func, fname, SUMA_LineNumbersFromTo(fl, flc)); fln = SUMA_NextFunc(flc, fle, &io, func, file, &line, &error) ; fprintf(SUMA_STDERR,"(next function %s): ", func); if (fln == flc) { /* No more functions*/ fln = fle; } /* look for next RETURN */ sprintf(rkey, "SUMA_RETURN"); sret = strstr(flc, rkey); if ( !(sret > flc) || sret > fln) { /* look for exit */ sprintf(rkey, "exit"); sret = strstr(flc, rkey); } if (sret > flc && sret < fln) { /* OK, look for ENTRY in between */ ctmp = *sret; *sret = '\0'; sent = strstr(flc, "ENTRY"); *sret = ctmp; if (sent > flc) { /* found entry, make sure it is before return */ if (sent < sret) { /* function is OK */ fprintf(SUMA_STDERR," OK\n"); } else { fprintf(SUMA_STDERR," BAD\n"); SUMA_S_Err("Function has RETURN (or exit) before ENTRY\n"); SUMA_ShowFromTo(flc, sret, NULL); SUMA_RETURN(NOPE); } } else { if (strcmp(rkey, "SUMA_RETURN") == 0) { fprintf(SUMA_STDERR," Very BAD, RETURN with No ENTRY\n"); SUMA_ShowFromTo(flc, sret, NULL); SUMA_RETURN(NOPE); } else if (strcmp(rkey, "exit") == 0) { fprintf(SUMA_STDERR," Naughty, exit with No ENTRY (use -debug 1 to see code chunk)\n"); if (Opt->debug) SUMA_ShowFromTo(flc, sret, NULL); } else { fprintf(SUMA_STDERR," STRANGE rkey=%s\n", rkey); } } } else { /* is there a return before the next function ? */ sret = strstr(flc, "return"); if (sret > flc && sret < fln) { /* Yes, make sure there is no ENTRY */ ctmp = *sret; *sret = '\0'; sent = strstr(flc, "ENTRY"); *sret = ctmp; if (sent > flc) { /* found entry, make sure it NOT before return */ if (sent < sret) { /* using lower case return with ENTRY */ fprintf(SUMA_STDERR," using return with ENTRY!\n"); SUMA_ShowFromTo(flc, fln, NULL); SUMA_RETURN(NOPE); } else { fprintf(SUMA_STDERR," Note: not using ENTRY or RETURN\n"); } } else { fprintf(SUMA_STDERR," Note: not using ENTRY or RETURN\n"); } } else { fprintf(SUMA_STDERR," No RETURN or exit here\n"); SUMA_ShowFromTo(flc, fln, NULL); SUMA_RETURN(NOPE); } } } while (fln > flc && fln < fle); SUMA_RETURN(YUP); } int main (int argc,char *argv[]) {/* Main */ static char FuncName[]={"AnalyzeTrace"}; SUMA_GENERIC_PROG_OPTIONS_STRUCT *Opt; SUMA_GENERIC_ARGV_PARSE *ps=NULL; int i; SUMA_Boolean LocalHead = NOPE; SUMA_STANDALONE_INIT; SUMA_mainENTRY; /* Allocate space for DO structure */ SUMAg_DOv = SUMA_Alloc_DisplayObject_Struct (SUMA_MAX_DISPLAYABLE_OBJECTS); ps = SUMA_Parse_IO_Args(argc, argv, "-o;-talk;"); if (argc < 2) { usage_AnalyzeTrace(ps); exit (1); } Opt = SUMA_AnalyzeTrace_ParseInput (argv, argc, ps); if (Opt->debug > 2) LocalHead = YUP; for (i=0; in_in_namev; ++i) { if (Opt->obj_type == 0) { fprintf( SUMA_STDOUT, "\n" "Processing file %s\n" , Opt->in_namev[i]); if (!SUMA_AnalyzeTraceFunc(Opt->in_namev[i], Opt)) break; } else { fprintf( SUMA_STDOUT, "\n" "Processing file %s\n" , Opt->in_namev[i]); if (!SUMA_AnalyzeSumaFunc(Opt->in_namev[i], Opt)) break; } } if (ps) SUMA_FreeGenericArgParse(ps); ps = NULL; if (Opt) Opt = SUMA_Free_Generic_Prog_Options_Struct(Opt); if (!SUMA_Free_CommonFields(SUMAg_CF)) SUMA_error_message(FuncName,"SUMAg_CF Cleanup Failed!",1); exit(0); }