diff options
Diffstat (limited to 'ghc/runtime/c-as-asm/StgDebug.lc')
-rw-r--r-- | ghc/runtime/c-as-asm/StgDebug.lc | 1677 |
1 files changed, 1677 insertions, 0 deletions
diff --git a/ghc/runtime/c-as-asm/StgDebug.lc b/ghc/runtime/c-as-asm/StgDebug.lc new file mode 100644 index 0000000000..77b24d0083 --- /dev/null +++ b/ghc/runtime/c-as-asm/StgDebug.lc @@ -0,0 +1,1677 @@ +\section[StgDebug]{Useful debugging routines for the STG machine} + +Call these functions directly from a debugger to print Nodes, +registers, stacks, etc. + +(An invocation such as + + make EXTRA_HC_OPTS='-optl-u -optl_DEBUG_LoadSymbols' ghci + + is usually required to get this code included in the object code.) + +Nota Bene: in a registerised build, you have to save all the registers +in their appropriate SAVE locations before calling any code that needs +register contents. (This has to be repeated every time you emerge +from the STG world.) + +On a sparc, this can be done by the following gdb script + +define saveRegs + + set *(&MainRegTable+8) = $l1 + set *(&MainRegTable+9) = $l2 + set *(&MainRegTable+10) = $l3 + set *(&MainRegTable+11) = $l4 + set *(&MainRegTable+12) = $l5 + set *(&MainRegTable+13) = $l6 + set *(&MainRegTable+14) = $l7 + set *(&MainRegTable+4) = $f2 + set *(&MainRegTable+5) = $f3 + set *(&MainRegTable+6) = $f4 + set *(&MainRegTable+7) = $f5 + + set *((double *) &MainRegTable+0) = (double) $f6 + set *((double *) &MainRegTable+2) = (double) $f8 + set *(&MainRegTable+23) = $l0 + set *(&MainRegTable+16) = $i0 + set *(&MainRegTable+17) = $i1 + set *(&MainRegTable+18) = $i2 + set *(&MainRegTable+19) = $i3 + set *(&StorageMgrInfo+0) = $i4 + set *(&StorageMgrInfo+1) = $i5 + +end + + +New code (attempts to interpret heap/stack contents) + DEBUG_LoadSymbols( filename ) Load symbol table from object file + (not essential but useful initialisation) + DEBUG_PrintA( depth, size ) Print "depth" entries from A stack + DEBUG_PrintB( depth, size ) ditto + DEBUG_Where( depth, size ) Ambitious attempt to print stacks + symbolically. Result is a little inaccurate + but often good enough to do the job. + DEBUG_NODE( closure, size ) Print a closure on the heap + DEBUG_INFO_TABLE(closure) Print info-table of a closure + DEBUG_SPT( size ) Print the Stable Pointer Table + +(Use variable DEBUG_details to set level of detail shown.) + +Older code (less fancy ==> more reliable) + DEBUG_ASTACK(lines) Print "lines" lines of the A Stack + DEBUG_BSTACK(lines) Print "lines" lines of the B Stack + DEBUG_UPDATES(frames) Print "frames" update frames + DEBUG_REGS() Print register values + DEBUG_MP() Print the MallocPtr Lists + +\begin{code} +#if defined(RUNTIME_DEBUGGING) + +#include "rtsdefs.h" +\end{code} + +\subsection[StgDebug_Symbol_Tables]{Loading Symbol Tables} + +NB: this assumes a.out files - won't work on Alphas. +ToDo: At least add some #ifdefs + +\begin{code} +#include <a.out.h> +#include <stab.h> +/* #include <nlist.h> */ + +#include <stdio.h> + +#define FROM_START 0 /* for fseek */ + +/* Simple lookup table */ + +/* Current implementation is pretty dumb! */ + +struct entry { + unsigned value; + int index; + char *name; +}; + +static int table_uninitialised = 1; +static int max_table_size; +static int table_size; +static struct entry* table; + +static +void reset_table( int size ) +{ + max_table_size = size; + table_size = 0; + table = (struct entry *) malloc( size * sizeof( struct entry ) ); +} + +static +void prepare_table() +{ + /* Could sort it... */ +} + +static +void insert( unsigned value, int index, char *name ) +{ + if ( table_size >= max_table_size ) { + fprintf( stderr, "Symbol table overflow\n" ); + exit( 1 ); + } + table[table_size].value = value; + table[table_size].index = index; + table[table_size].name = name; + table_size = table_size + 1; +} + +static +int lookup( unsigned value, int *result ) +{ + int i; + for( i = 0; i < table_size && table[i].value != value; ++i ) { + } + if (i < table_size) { + *result = table[i].index; + return 1; + } else { + return 0; + } +} + +static int lookup_name( char *name, unsigned *result ) +{ + int i; + for( i = 0; i < table_size && strcmp(name,table[i].name) != 0; ++i ) { + } + if (i < table_size) { + *result = table[i].value; + return 1; + } else { + return 0; + } +} +\end{code} + +* Z-escapes: + "std"++xs -> "Zstd"++xs + char_to_c 'Z' = "ZZ" + char_to_c '&' = "Za" + char_to_c '|' = "Zb" + char_to_c ':' = "Zc" + char_to_c '/' = "Zd" + char_to_c '=' = "Ze" + char_to_c '>' = "Zg" + char_to_c '#' = "Zh" + char_to_c '<' = "Zl" + char_to_c '-' = "Zm" + char_to_c '!' = "Zn" + char_to_c '.' = "Zo" + char_to_c '+' = "Zp" + char_to_c '\'' = "Zq" + char_to_c '*' = "Zt" + char_to_c '_' = "Zu" + char_to_c c = "Z" ++ show (ord c) + +\begin{code} +static char unZcode( char ch ) +{ + switch (ch) { + case 'Z' : + case '\0' : + return ('Z'); + case 'a' : + return ('&'); + case 'b' : + return ('|'); + case 'c' : + return (':'); + case 'd' : + return ('/'); + case 'e' : + return ('='); + case 'g' : + return ('>'); + case 'h' : + return ('#'); + case 'l' : + return ('<'); + case 'm' : + return ('-'); + case 'n' : + return ('!'); + case 'o' : + return ('.'); + case 'p' : + return ('+'); + case 'q' : + return ('\''); + case 't' : + return ('*'); + case 'u' : + return ('_'); + default : + return (ch); + } +} + +/* Precondition: out big enough to handle output (about twice length of in) */ +static void enZcode( char *in, char *out ) +{ + int i, j; + + j = 0; + out[ j++ ] = '_'; + for( i = 0; in[i] != '\0'; ++i ) { + switch (in[i]) { + case 'Z' : + out[j++] = 'Z'; + out[j++] = 'Z'; + break; + case '&' : + out[j++] = 'Z'; + out[j++] = 'a'; + break; + case '|' : + out[j++] = 'Z'; + out[j++] = 'b'; + break; + case ':' : + out[j++] = 'Z'; + out[j++] = 'c'; + break; + case '/' : + out[j++] = 'Z'; + out[j++] = 'd'; + break; + case '=' : + out[j++] = 'Z'; + out[j++] = 'e'; + break; + case '>' : + out[j++] = 'Z'; + out[j++] = 'g'; + break; + case '#' : + out[j++] = 'Z'; + out[j++] = 'h'; + break; + case '<' : + out[j++] = 'Z'; + out[j++] = 'l'; + break; + case '-' : + out[j++] = 'Z'; + out[j++] = 'm'; + break; + case '!' : + out[j++] = 'Z'; + out[j++] = 'n'; + break; + case '.' : + out[j++] = 'Z'; + out[j++] = 'o'; + break; + case '+' : + out[j++] = 'Z'; + out[j++] = 'p'; + break; + case '\'' : + out[j++] = 'Z'; + out[j++] = 'q'; + break; + case '*' : + out[j++] = 'Z'; + out[j++] = 't'; + break; + case '_' : + out[j++] = 'Z'; + out[j++] = 'u'; + break; + default : + out[j++] = in[i]; + break; + } + } + out[j] = '\0'; +} +\end{code} + +\begin{code} +static int lookupForName( P_ addr, char **result ) +{ + int i; + for( i = 0; i < table_size && table[i].value != (unsigned) addr; ++i ) { + } + if (i < table_size) { + *result = table[i].name; + return 1; + } else { + return 0; + } +} + +static void printZcoded( char *raw ) +{ + int j; + + /* start at 1 to skip the leading "_" */ + for( j = 1; raw[j] != '\0'; /* explicit */) { + if (raw[j] == 'Z') { + putchar(unZcode(raw[j+1])); + j = j + 2; + } else { + putchar(raw[j]); + j = j + 1; + } + } +} + +static void printName( P_ addr ) +{ + char *raw; + + if (lookupForName( addr, &raw )) { + printZcoded(raw); + } else { + printf("0x%x", addr); + } +} + +/* Fairly ad-hoc piece of code that seems to filter out a lot of + rubbish like the obj-splitting symbols */ + +static +int isReal( unsigned char type, char *name ) +{ + int external = type & N_EXT; + int tp = type & N_TYPE; + + if (tp == N_TEXT || tp == N_DATA) { + return( name[0] == '_' && name[1] != '_' ); + } else { + return( 0 ); + } +} + +void DEBUG_LoadSymbols( char *name ) +{ + FILE *binary; + + struct exec header; + + long sym_offset; + long sym_size; + long num_syms; + long num_real_syms; + struct nlist *symbol_table; + + long str_offset; + long str_size; /* assumed 4 bytes.... */ + char *string_table; + + long i; + + binary = fopen( name, "r" ); + if (binary == NULL) { + fprintf( stderr, "Can't open symbol table file \"%s\".\n", name ); + } + + + if (fread( &header, sizeof( struct exec ), 1, binary ) != 1) { + fprintf( stderr, "Can't read symbol table header.\n" ); + exit( 1 ); + } + if ( N_BADMAG( header ) ) { + fprintf( stderr, "Bad magic number in symbol table header.\n" ); + exit( 1 ); + } + + + + sym_offset = N_SYMOFF( header ); + sym_size = header.a_syms; + num_syms = sym_size / sizeof( struct nlist ); + fseek( binary, sym_offset, FROM_START ); + + symbol_table = (struct nlist *) malloc( sym_size ); + if (symbol_table == NULL) { + fprintf( stderr, "Can't allocate symbol table of size %d\n", sym_size ); + exit( 1 ); + } + + printf("Reading %d symbols\n", num_syms); + + if (fread( symbol_table, sym_size, 1, binary ) != 1) { + fprintf( stderr, "Can't read symbol table\n"); + exit( 1 ); + } + + + + str_offset = N_STROFF( header ); + fseek( binary, str_offset, FROM_START ); + + if (fread( &str_size, 4, 1, binary ) != 1) { + fprintf( stderr, "Can't read string table size\n"); + exit( 1 ); + } + + /* apparently the size of the string table includes the 4 bytes that + * store the size... + */ + string_table = (char *) malloc( str_size ); + if (string_table == NULL) { + fprintf( stderr, "Can't allocate string table of size %d\n", str_size ); + exit( 1 ); + } + + if (fread( string_table+4, str_size-4, 1, binary ) != 1) { + fprintf( stderr, "Can't read string table\n"); + exit( 1 ); + } + + num_real_syms = 0; + for( i = 0; i != num_syms; ++i ) { + unsigned char type = symbol_table[i].n_type; + unsigned value = symbol_table[i].n_value; + char *str = &string_table[symbol_table[i].n_un.n_strx]; + + if ( isReal( type, str ) ) { + num_real_syms = num_real_syms + 1; + } + } + + printf("Of which %d are real symbols\n", num_real_syms); + +/* + for( i = 0; i != num_syms; ++i ) { + unsigned char type = symbol_table[i].n_type; + unsigned value = symbol_table[i].n_value; + char *str = &string_table[symbol_table[i].n_un.n_strx]; + + if ( isReal(type, str) ) { + printf("Symbol %d. Extern? %c. Type: %c. Value: 0x%x. Name: %s\n", + i, + (external ? 'y' : 'n'), + type, + value, + str + ); + } + } +*/ + + reset_table( num_real_syms ); + + for( i = 0; i != num_syms; ++i ) { + unsigned char type = symbol_table[i].n_type; + unsigned value = symbol_table[i].n_value; + char *str = &string_table[symbol_table[i].n_un.n_strx]; + + if ( isReal( type, str ) ) { + insert( value, i, str ); + } + + } + + prepare_table(); +} +\end{code} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\subsection[StgDebug_PrettyPrinting]{Pretty printing internal structures} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\begin{code} +#include "../storage/SMinternal.h" + +#ifdef GCap +#define HP_BOT appelInfo.oldbase +#elif GCdu +#define HP_BOT dualmodeInfo.modeinfo[dualmodeInfo.mode].base +#elif GC2s +#define HP_BOT semispaceInfo[semispace].base +#elif GC1s +#define HP_BOT compactingInfo.base +#else + unknown garbage collector - help, help! +#endif +\end{code} + +\begin{code} +/* range: 0..NUM_LEVELS_OF_DETAIL-1. Level of machine-related detail shown */ +#define NUM_LEVELS_OF_DETAIL 3 +static int DEBUG_details = 2; +\end{code} + +\begin{code} +/* Determine the size and number of pointers for this kind of closure */ +static +void +getClosureShape( P_ node, int *vhs, int *size, int *ptrs, char **type ) +{ + /* The result is used for printing out closure contents. If the + info-table is mince, we'd better conservatively guess there's + nothing in the closure to avoid chasing non-ptrs. */ + *vhs = 0; + *size = 0; + *ptrs = 0; + *type = "*unknown info type*"; + + /* ToDo: if in garbage collector, consider subtracting some weird offset which some GCs add to infoptr */ + + /* The order here precisely reflects that in SMInfoTables.lh to make + it easier to check that this list is complete. */ + switch(INFO_TYPE(INFO_PTR(node))) + { + case INFO_SPEC_U_TYPE: + *vhs = 0; /* by decree */ + *size = SPEC_CLOSURE_SIZE(node); + *ptrs = SPEC_CLOSURE_NoPTRS(node); + *type = "SPECU"; + break; + case INFO_SPEC_N_TYPE: + *vhs = 0; /* by decree */ + *size = SPEC_CLOSURE_SIZE(node); + *ptrs = SPEC_CLOSURE_NoPTRS(node); + *type = "SPECN"; + break; + + case INFO_GEN_U_TYPE: + *vhs = GEN_VHS; + *size = GEN_CLOSURE_SIZE(node); + *ptrs = GEN_CLOSURE_NoPTRS(node); + *type = "GENU"; + break; + case INFO_GEN_N_TYPE: + *vhs = GEN_VHS; + *size = GEN_CLOSURE_SIZE(node); + *ptrs = GEN_CLOSURE_NoPTRS(node); + *type = "GENN"; + break; + + case INFO_DYN_TYPE: + *vhs = DYN_VHS; + *size = DYN_CLOSURE_SIZE(node); + *ptrs = DYN_CLOSURE_NoPTRS(node); + *type = "DYN"; + break; + + case INFO_TUPLE_TYPE: + *vhs = TUPLE_VHS; + *size = TUPLE_CLOSURE_SIZE(node); + *ptrs = TUPLE_CLOSURE_NoPTRS(node); + *type = "TUPLE"; + break; + + case INFO_DATA_TYPE: + *vhs = DATA_VHS; + *size = DATA_CLOSURE_SIZE(node); + *ptrs = DATA_CLOSURE_NoPTRS(node); + *type = "DATA"; + break; + + case INFO_MUTUPLE_TYPE: + *vhs = MUTUPLE_VHS; + *size = MUTUPLE_CLOSURE_SIZE(node); + *ptrs = MUTUPLE_CLOSURE_NoPTRS(node); + *type = "MUTUPLE"; + break; + + case INFO_IMMUTUPLE_TYPE: + *vhs = MUTUPLE_VHS; + *size = MUTUPLE_CLOSURE_SIZE(node); + *ptrs = MUTUPLE_CLOSURE_NoPTRS(node); + *type = "IMMUTUPLE"; + break; + + case INFO_STATIC_TYPE: + *vhs = STATIC_VHS; + *size = INFO_SIZE(INFO_PTR(node)); + *ptrs = INFO_NoPTRS(INFO_PTR(node)); + *type = "STATIC"; + break; + + case INFO_CONST_TYPE: + *vhs = 0; + *size = 0; + *ptrs = 0; + *type = "CONST"; + break; + + case INFO_CHARLIKE_TYPE: + *vhs = 0; + *size = 1; + *ptrs = 0; + *type = "CHAR"; + break; + + case INFO_INTLIKE_TYPE: + *vhs = 0; + *size = 1; + *ptrs = 0; + *type = "INT"; + break; + + case INFO_BH_TYPE: + *vhs = 0; + *size = INFO_SIZE(INFO_PTR(node)); + *ptrs = 0; + *type = "BHOLE"; + break; + +/* most of the following are plausible guesses (particularily VHSs) ADR */ + case INFO_BQ_TYPE: +#ifdef CONCURRENT + *vhs = 0; + *size = BQ_CLOSURE_SIZE(node); + *ptrs = BQ_CLOSURE_NoPTRS(node); + *type = "BQ"; +#else + printf("Panic: found BQ Infotable in non-threaded system.\n"); +#endif + break; + + case INFO_IND_TYPE: + *vhs = 0; + *size = IND_CLOSURE_SIZE(node); + *ptrs = IND_CLOSURE_NoPTRS(node); + *type = "IND"; + break; + + case INFO_CAF_TYPE: + *vhs = 0; /* ?? ADR */ + *size = INFO_SIZE(INFO_PTR(node)); + *ptrs = 0; + *type = "CAF"; + break; + + case INFO_FETCHME_TYPE: +#ifdef PAR + *vhs = FETCHME_VHS; + *size = FETCHME_CLOSURE_SIZE(node); + *ptrs = FETCHME_CLOSURE_PTRS(node); + *type = "FETCHME"; +#else + printf("Panic: found FETCHME Infotable in sequential system.\n"); +#endif + break; + + case INFO_FMBQ_TYPE: +#ifdef PAR + *vhs = FMBQ_VHS; + *size = FMBQ_CLOSURE_SIZE(node); + *ptrs = FMBQ_CLOSURE_PTRS(node); + *type = "FMBQ"; +#else + printf("Panic: found FMBQ Infotable in sequential system.\n"); +#endif + break; + + case INFO_BF_TYPE: +#ifdef PAR + *vhs = 0; + *size = 0; + *ptrs = 0; + *type = "BlockedFetch"; +#else + printf("Panic: found BlockedFetch Infotable in sequential system.\n"); +#endif + break; + + case INFO_TSO_TYPE: + /* Conservative underestimate: this will contain a regtable + which comes nowhere near fitting the standard "p ptrs; s-p + non-ptrs" format. ADR */ +#ifdef CONCURRENT + *vhs = TSO_VHS; + *size = 0; + *ptrs = 0; + *type = "TSO"; +#else + printf("Panic: found TSO Infotable in non-threaded system.\n"); +#endif + break; + + case INFO_STKO_TYPE: + /* Conservative underestimate: this will contain stuff + which comes nowhere near fitting the standard "p ptrs; s-p + non-ptrs" format. JSM */ +#ifdef CONCURRENT + *vhs = STKO_VHS; + *size = 0; + *ptrs = 0; + *type = "STKO"; +#else + printf("Panic: found STKO Infotable in non-threaded system.\n"); +#endif + break; + + /* There are no others in SMInfoTables.lh 11/5/94 ADR*/ + default: + printf("Invalid/unknown info type %d\n", INFO_TYPE(INFO_PTR(node))); + break; + } +} + +static +void +printWord( W_ word ) +{ + printf("0x%08lx", word); +} + +static +void +printAddress( P_ address ) +{ +#ifdef PAR + PP_ SpA = STKO_SpA(SAVE_StkO); + PP_ SuA = STKO_SuA(SAVE_StkO); + P_ SpB = STKO_SpB(SAVE_StkO); + P_ SuB = STKO_SuB(SAVE_StkO); +#else + PP_ SpA = SAVE_SpA; + PP_ SuA = SAVE_SuA; + P_ SpB = SAVE_SpB; + P_ SuB = SAVE_SuB; +#endif + P_ Hp = SAVE_Hp; + + PP_ botA = stackInfo.botA; + P_ botB = stackInfo.botB; + P_ HpBot = HP_BOT; + + char *name; + + /* ToDo: check if it's in text or data segment. */ + + /* The @-1@s in stack comparisions are because we sometimes use the + address of just below the stack... */ + + if (lookupForName( address, &name )) { + printZcoded( name ); + } else { + if (DEBUG_details > 1) { + printWord( (W_) address ); + printf(" : "); + } + if (HpBot <= address && address < Hp) { + printf("Hp[%d]", address - HpBot); + } else if (SUBTRACT_A_STK((PP_)address, botA) >= -1 && SUBTRACT_A_STK(SpA, (PP_)address) >= 0) { + printf("SpA[%d]", SUBTRACT_A_STK((PP_)address, botA)); + } else if (SUBTRACT_B_STK(address, botB) >= -1 && SUBTRACT_B_STK(SpB, address) >= 0) { + /* ToDo: check if it's an update frame */ + printf("SpB[%d]", SUBTRACT_B_STK(address, botB)); + } else { + printWord( (W_) address ); + } + } +} + +static +void +printIndentation( int indentation ) +{ + int i; + for (i = 0; i < indentation; ++i) { printf(" "); } +} + +/* The weight parameter is used to (eventually) break cycles */ +static +void +printStandardShapeClosure( + int indentation, + int weight, + P_ closure, int vhs, int size, int noPtrs +) +{ +#ifdef PAR + PP_ SpA = STKO_SpA(SAVE_StkO); + PP_ SuA = STKO_SuA(SAVE_StkO); + P_ SpB = STKO_SpB(SAVE_StkO); + P_ SuB = STKO_SuB(SAVE_StkO); +#else + PP_ SpA = SAVE_SpA; + PP_ SuA = SAVE_SuA; + P_ SpB = SAVE_SpB; + P_ SuB = SAVE_SuB; +#endif + P_ Hp = SAVE_Hp; + + extern void printClosure PROTO( (P_, int, int) ); + int numValues = size - vhs; + P_ HpBot = HP_BOT; + + if (DEBUG_details > 1) { + printAddress( closure ); + printf(": "); + } + printName((P_)INFO_PTR(closure)); + + if ( numValues > 0 ) { + int newWeight = weight-1 ; + /* I've tried dividing the weight by size to share it out amongst + sub-closures - but that didn't work too well. */ + + if (newWeight > 0) { + int i=0; + printf("(\n"); + while (i < numValues) { + P_ data = (P_) closure[_FHS + vhs + i]; + + printIndentation(indentation+1); + if (i < noPtrs) { + printClosure( data, indentation+1, newWeight); + } else { + printAddress( data ); + } + i = i + 1; + if (i < numValues) printf(",\n"); + } + printf(")"); + } else { + int i; + printf("(_"); + for( i = 1; i < size; ++i ) { + printf(",_"); + } + printf(")"); + } + } +} + +/* Should be static but has to be extern to allow mutual recursion */ +void +printClosure( P_ closure, int indentation, int weight ) +{ + int vhs, size, ptrs; + char *type; + + /* I'd love to put a test here that this actually _is_ a closure - + but testing that it is in the heap is overly strong. */ + + getClosureShape(closure, &vhs, &size, &ptrs, &type); + + /* The order here precisely reflects that in SMInfoTables.lh to make + it easier to check that this list is complete. */ + switch(INFO_TYPE(INFO_PTR(closure))) { + case INFO_SPEC_U_TYPE: + case INFO_SPEC_N_TYPE: + case INFO_GEN_U_TYPE: + case INFO_GEN_N_TYPE: + case INFO_DYN_TYPE: + case INFO_TUPLE_TYPE: + case INFO_DATA_TYPE: + case INFO_MUTUPLE_TYPE: + case INFO_IMMUTUPLE_TYPE: + printStandardShapeClosure(indentation, weight, closure, + vhs, size, ptrs); + break; + + case INFO_STATIC_TYPE: + /* If the STATIC contains Floats or Doubles, we can't print it. */ + /* And we can't always rely on the size/ptrs info either */ + printAddress( closure ); + printf(" STATIC"); + break; + + case INFO_CONST_TYPE: + if (DEBUG_details > 1) { + printAddress( closure ); + printf(": "); + } + printName((P_)INFO_PTR(closure)); + break; + + case INFO_CHARLIKE_TYPE: + /* ToDo: check for non-printable characters */ + if (DEBUG_details > 1) printf("CHARLIKE "); + printf("\'%c\'", (unsigned char) CHARLIKE_VALUE(closure)); + break; + + case INFO_INTLIKE_TYPE: + if (DEBUG_details > 1) printf("INTLIKE "); + printf("%d",INTLIKE_VALUE(closure)); + break; + + case INFO_BH_TYPE: + /* Is there anything to say here> */ + if (DEBUG_details > 1) { + printAddress( closure ); + printf(": "); + } + printName((P_)INFO_PTR(closure)); + break; + +/* most of the following are just plausible guesses (particularily VHSs) ADR */ + + case INFO_BQ_TYPE: +#ifdef CONCURRENT + printStandardShapeClosure(indentation, weight, closure, + vhs, size, ptrs); +#else + printf("Panic: found BQ Infotable in non-threaded system.\n"); +#endif + break; + + case INFO_IND_TYPE: + if (DEBUG_details > 0) { + printAddress( closure ); + printf(" IND: "); + } + printClosure( (P_) IND_CLOSURE_PTR(closure), indentation, weight ); + break; + + case INFO_CAF_TYPE: + if (DEBUG_details > 0) { + printAddress( closure ); + printf(" CAF: "); + } + printClosure( (P_) IND_CLOSURE_PTR(closure), indentation, weight ); + break; + + case INFO_FETCHME_TYPE: +#ifdef PAR + printStandardShapeClosure(indentation, weight, closure, + vhs, size, ptrs); +#else + printf("Panic: found FETCHME Infotable in sequential system.\n"); +#endif + break; + + case INFO_FMBQ_TYPE: +#ifdef PAR + printStandardShapeClosure(indentation, weight, closure, + vhs, size, ptrs); +#else + printf("Panic: found FMBQ Infotable in sequential system.\n"); +#endif + break; + + case INFO_BF_TYPE: +#ifdef PAR + printStandardShapeClosure(indentation, weight, closure, + vhs, size, ptrs); +#else + printf("Panic: found BlockedFetch Infotable in sequential system.\n"); +#endif + break; + + case INFO_TSO_TYPE: +#ifdef CONCURRENT + /* A TSO contains a regtable... */ + printAddress( closure ); + printf(" TSO: ..."); +#else + printf("Panic: found TSO Infotable in non-threaded system.\n"); +#endif + break; + + case INFO_STKO_TYPE: +#ifdef CONCURRENT + /* A STKO contains parts of the A and B stacks... */ + printAddress( closure ); + printf(" STKO: ..."); +#else + printf("Panic: found STKO Infotable in non-threaded system.\n"); +#endif + break; + + /* There are no others in SMInfoTables.lh 11/5/94 ADR*/ + default: + printf("Invalid/unknown info type %d\n", INFO_TYPE(INFO_PTR(closure))); + break; + } +} + +void +DEBUG_NODE( P_ closure, int size ) +{ + printClosure( closure, 0, size ); + printf("\n"); +} +\end{code} + +Now some stuff for printing stacks - almost certainly doesn't work +under threads which keep the stack on the heap. + +\begin{code} +#ifndef CONCURRENT + +static int +minimum(int a, int b) +{ + if (a < b) { + return a; + } else { + return b; + } +} + +void DEBUG_PrintA( int depth, int weight ) +{ + PP_ SpA = SAVE_SpA; + PP_ SuA = SAVE_SuA; + P_ SpB = SAVE_SpB; + P_ SuB = SAVE_SuB; + P_ Hp = SAVE_Hp; + + int i; + I_ size = minimum(depth, SUBTRACT_A_STK(SpA, stackInfo.botA)+1); + + printf("Dump of the Address Stack (SpA = 0x%x, SuA = 0x%x)\n", SpA, SuA); + + for( i = 0; i < size; ++i ) { + printIndentation(1); + printf("SpA[%ld] (0x%08lx):", i, SpA + AREL(i)); + printClosure((P_)*(SpA + AREL(i)), 2, weight); + printf("\n"); + } +} + +void DEBUG_PrintB( int depth, int weight ) +{ + PP_ SpA = SAVE_SpA; + PP_ SuA = SAVE_SuA; + P_ SpB = SAVE_SpB; + P_ SuB = SAVE_SuB; + P_ Hp = SAVE_Hp; + + I_ i; + I_ size = minimum(depth, SUBTRACT_B_STK(SpB, stackInfo.botB)+1); + + P_ updateFramePtr; + I_ update_count; + + printf("Dump of the Value Stack (SpB = 0x%x, SuB = 0x%x)\n", SpB, SuB); + + updateFramePtr = SuB; + update_count = 0; + i = 0; + while (i < size) { + if (updateFramePtr == SpB + BREL(i)) { + + printIndentation(1); + printf("SpB[%ld] (0x%08lx): UpdateFrame[%d](", + i, + updateFramePtr, + update_count + ); + printName( (P_) *(SpB + BREL(i)) ); + printf(", UF[%d] (= SpB[%ld]), SpA[%ld], ", + update_count+1, + SUBTRACT_B_STK(SpB, GRAB_SuB(updateFramePtr)), + SUBTRACT_A_STK(SpA, GRAB_SuA(updateFramePtr)) + ); + printAddress( GRAB_UPDATEE(updateFramePtr) ); + printf(")\n"); + + printIndentation(2); + printClosure( GRAB_UPDATEE(updateFramePtr), 3, weight ); + printf("\n"); + + updateFramePtr = GRAB_SuB(updateFramePtr); + update_count = update_count + 1; + + /* ToDo: GhcConstants.lh reveals that there are two other sizes possible */ + i = i + STD_UF_SIZE; + } else { + printIndentation(1); + printf("SpB[%ld] (0x%08lx): ", i, SpB + BREL(i) ); + printName((P_) *(SpB + BREL(i)) ); + printf("\n"); + i = i + 1; + } + } +} +#endif /* not CONCURRENT */ +\end{code} + +ToDo: + + All the following code incorrectly assumes that the only return + addresses are those associated with update frames. + + To do a proper job of printing the environment we need to: + + 1) Recognise vectored and non-vectored returns on the B stack. + + 2) Know where the local variables are in the A and B stacks for + each return situation. + + Until then, we'll just need to look suspiciously at the + "environment" being printed out. + + ADR + +\begin{code} +/* How many real stacks are there on SpA and SpB? */ +static +int numStacks( ) +{ +#ifdef PAR + PP_ SpA = STKO_SpA(SAVE_StkO); + PP_ SuA = STKO_SuA(SAVE_StkO); + P_ SpB = STKO_SpB(SAVE_StkO); + P_ SuB = STKO_SuB(SAVE_StkO); +#else + PP_ SpA = SAVE_SpA; + PP_ SuA = SAVE_SuA; + P_ SpB = SAVE_SpB; + P_ SuB = SAVE_SuB; +#endif + P_ Hp = SAVE_Hp; + + int depth = 1; /* There's always at least one stack */ + + while (SUBTRACT_B_STK(SuB, stackInfo.botB) >= 0) { + SuB = GRAB_SuB( SuB ); + depth = depth + 1; + } + return depth; +} + +static +void printLocalAStack( int depth, int indentation, int weight, PP_ SpA, int size ) +{ + int i; + + ASSERT( size >= 0 ); + + for( i = size-1; i >= 0; --i ) { + printIndentation( indentation ); + printf("A[%ld][%ld]", depth, i); + if (DEBUG_details > 1) printf(" (0x%08lx) ", SpA + AREL(i) ); + printf("="); + printClosure( *(SpA + AREL(i)), indentation+2, weight ); + printf("\n"); + } +} + +static +void printLocalBStack( int depth, int indentation, int weight, P_ SpB, int size ) +{ + int i; + + ASSERT( size >= 0 ); + + for( i = size-1; i >= 0; --i) { + printIndentation( indentation ); + printf("B[%ld][%ld]", depth, i); + if (DEBUG_details > 1) printf(" (0x%08lx) ", SpB + BREL(i) ); + printf("="); + printAddress( (P_) *(SpB + BREL(i)) ); + printf("\n"); + } +} + +static +void printEnvironment( int depth, int indentation, int weight, PP_ SpA, PP_ SuA, P_ SpB, P_ SuB ) +{ + int sizeA = SUBTRACT_A_STK(SpA, SuA); + int sizeB = SUBTRACT_B_STK(SpB, SuB); + + if (sizeA + sizeB > 0) { + printIndentation( indentation ); + printf("let\n"); + + printLocalAStack( depth, indentation+1, weight, SpA, sizeA ); + printLocalBStack( depth, indentation+1, weight, SpB, sizeB ); + + printIndentation( indentation ); + printf("in\n"); + } +} +\end{code} + +Printing the current context is a little tricky. + +Ideally, we would work from the bottom of the stack up to the top +recursively printing the stuff nearer the top. + +In practice, we have to work from the top down because the top +contains info about how much data is below the current return address. + +The result is that we have two recursive passes over the stacks: the +first one prints the "cases" and the second one prints the +continuations (vector tables, etc.) + +Note that because we compress chains of update frames, the depth and +indentation do not always change in step. + +ToDo: + +* detecting non-updating cases too +* printing continuations (from vector tables) properly +* printing sensible names in environment. +* fix bogus nature of lets + + +\begin{code} +static int maxDepth = 5; + +static +int printCases( int depth, int weight, PP_ SpA, PP_ SuA, P_ SpB, P_ SuB ) +{ + int indentation; + + if (depth < maxDepth && SUBTRACT_B_STK(SuB, stackInfo.botB) >= 0) { + PP_ nextSpA, nextSuA; + P_ nextSpB, nextSuB; + + /* ToDo: GhcConstants.lh reveals that there are two other sizes of + update frame possible */ + /* ToDo: botB is probably wrong in THREAD system */ + + nextSpB = SuB + BREL(STD_UF_SIZE); + nextSuB = GRAB_SuB( SuB ); + nextSpA = SuA; + nextSuA = GRAB_SuA( nextSuB ); + + indentation = printCases( depth+1, weight, nextSpA, nextSuA, nextSpB, nextSuB ); + + if (DEBUG_details > 1 || nextSpB != nextSuB) { /* show frame (even if adjacent to another) */ + printIndentation( indentation ); + printf("case\n"); + indentation = indentation + 1; + } + if (SpB != SuB) { + /* next thing on stack is a return vector - no need to show it here. */ + SpB = SpB + BREL(1); + } + printEnvironment( depth, indentation, weight, SpA, SuA, SpB, SuB ); + } else { + printf("...\n"); + indentation = 1; + } + + return indentation; +} + +/* ToDo: pay more attention to format of vector tables in SMupdate.lh */ + +static +int isVTBLEntry( P_ entry ) +{ + char *raw; + + if (lookupForName( entry, &raw )) { + if ( strncmp( "_ret", raw, 4 ) == 0 ) { + return 1; + } else if ( strncmp( "_djn", raw, 4 ) == 0) { + return 1; + } else { + return 0; + } + } else { + return 0; + } +} + +static +void printVectorTable( int indentation, PP_ vtbl ) +{ + if (isVTBLEntry( (P_) vtbl )) { /* Direct return */ + printName( (P_) vtbl ); + } else { + int i = 0; + while( isVTBLEntry( vtbl[RVREL(i)] )) { + printIndentation( indentation ); + printf( "%d -> ", i ); + printName( vtbl[RVREL(i)] ); + printf( "\n" ); + i = i + 1; + } + } +} + +static +void printContinuations( int depth, int indentation, int weight, PP_ SpA, PP_ SuA, P_ SpB, P_ SuB ) +{ + if (depth < maxDepth && SUBTRACT_B_STK(SuB, stackInfo.botB) >= 0) { + PP_ nextSpA, nextSuA; + P_ nextSpB, nextSuB; + int nextIndent = indentation; /* Indentation to print next frame at */ + + /* ToDo: GhcConstants.lh reveals that there are two other sizes of + update frame possible */ + /* ToDo: botB is probably wrong in THREAD system */ + + /* ToDo: ASSERT that SuA == nextSuA */ + + nextSpB = SuB + BREL(STD_UF_SIZE); + nextSuB = GRAB_SuB( SuB ); + nextSpA = SuA; + nextSuA = GRAB_SuA( nextSuB ); + + if (DEBUG_details > 0) { /* print update information */ + + if (SpB != SuB) { /* start of chain of update frames */ + printIndentation( indentation ); + printf("of updatePtr ->\n"); + printIndentation( indentation+1 ); + printf("update\n"); + } + printIndentation( indentation+2 ); + printClosure( (P_)*(SuB + BREL(UF_UPDATEE)), indentation+2, weight ); + printf(" := "); + printName( (P_) *(SuB + BREL(UF_RET)) ); + printf("(updatePtr)\n"); + + if (nextSpB != nextSuB) { /* end of chain of update frames */ + nextIndent = nextIndent-1; + printVectorTable( indentation+1, (PP_) *(nextSpB) ); + } + } else { + if (nextSpB != nextSuB) { /* end of chain of update frames */ + nextIndent = nextIndent-1; + printVectorTable( indentation, (PP_) *(nextSpB) ); + } + } + printContinuations( depth+1, nextIndent, weight, nextSpA, nextSuA, nextSpB, nextSuB ); + + } else { + printf("...\n"); + } +} + + +void DEBUG_Where( int depth, int weight ) +{ +#ifdef PAR + PP_ SpA = STKO_SpA(SAVE_StkO); + PP_ SuA = STKO_SuA(SAVE_StkO); + P_ SpB = STKO_SpB(SAVE_StkO); + P_ SuB = STKO_SuB(SAVE_StkO); +#else + PP_ SpA = SAVE_SpA; + PP_ SuA = SAVE_SuA; + P_ SpB = SAVE_SpB; + P_ SuB = SAVE_SuB; +#endif + P_ Hp = SAVE_Hp; + StgRetAddr RetReg = SAVE_Ret; + P_ Node = SAVE_R1.p; + + int indentation; + + maxDepth = depth; + + printf("WARNING: Non-updating cases may be incorrectly displayed\n"); + + indentation = printCases( 1, weight, SpA, SuA, SpB, SuB ); + + printIndentation( indentation ); + printf("CASE\n"); + + printIndentation( indentation+1 ); + printName( Node ); + printf("\n"); + printVectorTable( indentation+1, (PP_) RetReg ); + + printContinuations( depth, indentation, weight, SpA, SuA, SpB, SuB ); +} +\end{code} + + +\begin{code} +#if defined(RUNTIME_DEBUGGING) + +void +DEBUG_INFO_TABLE(node) +P_ node; +{ + int vhs, size, ptrs; /* not used */ + char *ip_type; + StgPtr info_ptr = (StgPtr) INFO_PTR(node); + + getClosureShape(node, &vhs, &size, &ptrs, &ip_type); + + fprintf(stderr, + "%s Info Ptr 0x%lx; Entry: 0x%lx; Update: 0x%lx\n", + ip_type, info_ptr, + (W_) ENTRY_CODE(info_ptr), (W_) UPDATE_CODE(info_ptr)); + fprintf(stderr, + "Tag: %d; Type: %d; Size: %lu; Ptrs: %lu\n\n", + INFO_TAG(info_ptr), INFO_TYPE(info_ptr), + INFO_SIZE(info_ptr),INFO_NoPTRS(info_ptr)); +#if defined(PAR) + fprintf(stderr,"Enter Flush Entry: 0x%lx;\tExit Flush Entry: 0x%lx\n",INFO_FLUSHENT(info_ptr),INFO_FLUSH(info_ptr)); +#endif /* PAR */ + +#if defined(USE_COST_CENTRES) + fprintf(stderr,"Cost Centre: 0x%lx\n",INFO_CAT(info_ptr)); +#endif /* USE_COST_CENTRES */ + +#if defined(_INFO_COPYING) + fprintf(stderr,"Evacuate Entry: 0x%lx;\tScavenge Entry: 0x%lx\n", + INFO_EVAC_2S(info_ptr),INFO_SCAV_2S(info_ptr)); +#endif /* INFO_COPYING */ + +#if defined(_INFO_COMPACTING) + fprintf(stderr,"Scan Link: 0x%lx;\tScan Move: 0x%lx\n", + (W_) INFO_SCAN_LINK_1S(info_ptr), (W_) INFO_SCAN_MOVE_1S(info_ptr)); + fprintf(stderr,"Mark: 0x%lx;\tMarked: 0x%lx;\n", + (W_) INFO_MARK_1S(info_ptr), (W_) INFO_MARKED_1S(info_ptr)); + if(BASE_INFO_TYPE(info_ptr)==INFO_SPEC_TYPE) + fprintf(stderr,"plus specialised code\n"); + else + fprintf(stderr,"Marking: 0x%lx\n",(W_) INFO_MARKING_1S(info_ptr)); +#endif /* INFO_COMPACTING */ +} + +void +DEBUG_REGS() +{ +#ifdef PAR + PP_ SpA = STKO_SpA(SAVE_StkO); + PP_ SuA = STKO_SuA(SAVE_StkO); + P_ SpB = STKO_SpB(SAVE_StkO); + P_ SuB = STKO_SuB(SAVE_StkO); +#else + PP_ SpA = SAVE_SpA; + PP_ SuA = SAVE_SuA; + P_ SpB = SAVE_SpB; + P_ SuB = SAVE_SuB; +#endif + P_ Hp = SAVE_Hp; + P_ HpLim= SAVE_HpLim; + I_ TagReg= SAVE_Tag; + StgRetAddr RetReg = SAVE_Ret; + P_ Node = SAVE_R1.p; + StgUnion R1 = SAVE_R1; + StgUnion R2 = SAVE_R2; + StgUnion R3 = SAVE_R3; + StgUnion R4 = SAVE_R4; + StgUnion R5 = SAVE_R5; + StgUnion R6 = SAVE_R6; + StgUnion R7 = SAVE_R7; + StgUnion R8 = SAVE_R8; + StgFloat FltReg1 = SAVE_Flt1; + StgFloat FltReg2 = SAVE_Flt2; + StgFloat FltReg3 = SAVE_Flt3; + StgFloat FltReg4 = SAVE_Flt4; + StgDouble DblReg1 = SAVE_Dbl1; + StgDouble DblReg2 = SAVE_Dbl2; + + fprintf(stderr,"STG-Machine Register Values:\n\n"); + fprintf(stderr,"Node: %08lx; Hp: %08lx; HpLim: %08lx; Tag: %8lu\n",Node,(W_)Hp,(W_)HpLim,TagReg); + fprintf(stderr,"SpA: %08lx; SpB: %08lx; SuA: %08lx; SuB: %08lx\n",(W_)SpA,(W_)SpB,(W_)SuA,(W_)SuB); + fprintf(stderr,"RetReg: %08lx\n",RetReg); + +#if 0 +/* These bits need to have the FLUSH_REG_MAP, whereas the surrounding bits + use the MAIN_REG_MAP */ + + fprintf(stderr, "\n"); + fprintf(stderr,"LiveR: %08lx\n", LivenessReg); + fprintf(stderr,"Flush: %08lx; FStk: %08lx; FStkB: %08lx; FTmp: %08lx\n",(W_)FlushP,(W_)FStack,(W_)FStackBase,(W_)Temp); +#endif /* 0 */ + + fprintf(stderr, "\n"); + + fprintf(stderr,"Gen: %8lu, %8lu, %8lu, %8lu\n",R1.i,R2.i,R3.i,R4.i); + fprintf(stderr," %8lu, %8lu, %8lu, %8lu\n",R5.i,R6.i,R7.i,R8.i); + fprintf(stderr,"Float: %8g, %8g, %8g, %8g\n",FltReg1,FltReg2,FltReg3,FltReg4); + fprintf(stderr,"Dble: %8g, %8g\n",DblReg1,DblReg2); +} + +void +DEBUG_MP() +{ + StgPtr mp; + StgInt i; + + fprintf(stderr,"MallocPtrList\n\n"); + + for(mp = StorageMgrInfo.MallocPtrList; + mp != NULL; + mp = MallocPtr_CLOSURE_LINK(mp)) { + + fprintf(stderr, "MallocPtr(0x%lx) = 0x%lx\n", mp, MallocPtr_CLOSURE_DATA(mp)); + +/* + DEBUG_PRINT_NODE(mp); +*/ + } + +#if defined(GCap) || defined(GCgn) + fprintf(stderr,"\nOldMallocPtr List\n\n"); + + for(mp = StorageMgrInfo.OldMallocPtrList; + mp != NULL; + mp = MallocPtr_CLOSURE_LINK(mp)) { + + fprintf(stderr, " MallocPtr(0x%lx) = 0x%lx\n", mp, MallocPtr_CLOSURE_DATA(mp)); +/* + DEBUG_PRINT_NODE(mp); +*/ + } +#endif /* GCap || GCgn */ + + fprintf(stderr, "\n"); +} + +#ifndef PAR +void +DEBUG_SPT(int weight) +{ + StgPtr SPTable = StorageMgrInfo.StablePointerTable; + StgInt size = SPT_SIZE(SPTable); + StgInt ptrs = SPT_NoPTRS(SPTable); + StgInt top = SPT_TOP(SPTable); + + StgInt i; + +/* + DEBUG_PRINT_NODE(SPTable); +*/ + + fprintf(stderr,"SPTable@0x%lx:\n", SPTable); + fprintf(stderr," InfoPtr = 0x%lx\n", INFO_PTR(SPTable)); + fprintf(stderr," size = %d, ptrs = %d, top = %d\n", + size, ptrs, top + ); + for( i=0; i < ptrs; i++ ) { + if (i % 10 == 0) { + fprintf(stderr,"\n "); + } + printClosure(SPT_SPTR(SPTable, i),1,weight); + fprintf(stderr, "\n"); + } + fprintf(stderr, "\n"); + for( i=0; i < top; i++) { + if (i % 10 == 0) { + fprintf(stderr,"\n "); + } + fprintf(stderr, " %3d", SPT_FREE(SPTable, i)); + } + + fprintf(stderr, "\n\n"); + +} +#endif /* !PAR */ + + +/* + These routines crawl over the A and B stacks, printing + a maximum "lines" lines at the top of the stack. +*/ + + +#define STACK_VALUES_PER_LINE 5 + +#if !defined(PAR) +/* (stack stuff is really different on parallel machines) */ + +void +DEBUG_ASTACK(lines) +I_ lines; +{ + PP_ SpA = SAVE_SpA; + PP_ SuA = SAVE_SuA; + P_ SpB = SAVE_SpB; + P_ SuB = SAVE_SuB; + + PP_ stackptr; + I_ count = 0; + + fprintf(stderr,"Dump of the Address Stack, SpA: 0x%08lx, BOS: 0x%08lx\n", + (W_) SpA, (W_) stackInfo.botA); + + for (stackptr = SpA; + SUBTRACT_A_STK(stackptr, stackInfo.botA) >= 0; + stackptr = stackptr + AREL(1)) + { + if( count++ % STACK_VALUES_PER_LINE == 0) + { + if(count >= lines * STACK_VALUES_PER_LINE) + break; + fprintf(stderr,"\nSpA[%ld] (0x%08lx): ",count-1,stackptr); + } + fprintf(stderr,"0x%08lx ",(W_) *stackptr); + } + fprintf(stderr, "\n"); +} + + +void +DEBUG_BSTACK(lines) +I_ lines; +{ + PP_ SpA = SAVE_SpA; + PP_ SuA = SAVE_SuA; + P_ SpB = SAVE_SpB; + P_ SuB = SAVE_SuB; + + P_ stackptr; + I_ count = 0; + + fprintf(stderr,"Dump of the Value Stack, SpB: 0x%08lx, BOS: 0x%08lx\n", + (W_) SpB, (W_) stackInfo.botB); + + for (stackptr = SpB; + SUBTRACT_B_STK(stackptr, stackInfo.botB) > 0; + stackptr = stackptr + BREL(1)) + { + if( count++ % STACK_VALUES_PER_LINE == 0) + { + if(count >= lines * STACK_VALUES_PER_LINE) + break; + fprintf(stderr,"\nSpB[%ld] (0x%08lx): ",count-1,stackptr); + } + fprintf(stderr,"0x%08lx ",(W_) *stackptr); + } + fprintf(stderr, "\n"); +} +#endif /* not parallel */ + +/* + This should disentangle update frames from both stacks. +*/ + +#if ! defined(PAR) +void +DEBUG_UPDATES(limit) +I_ limit; +{ + PP_ SpA = SAVE_SpA; + PP_ SuA = SAVE_SuA; + P_ SpB = SAVE_SpB; + P_ SuB = SAVE_SuB; + + P_ updatee, retreg; + PP_ sua; + P_ sub; + PP_ spa = SuA; + P_ spb = SuB; + I_ count = 0; + + fprintf(stderr,"Update Frame Stack Dump:\n\n"); + + for(spb = SuB; + SUBTRACT_B_STK(spb, stackInfo.botB) > 0 && count++ < limit; + /* re-init given explicitly */) + { + updatee = GRAB_UPDATEE(spb); /* Thing to be updated */ + retreg = (P_) GRAB_RET(spb); /* Return vector below */ + + fprintf(stderr,"SuA: 0x%08lx, SuB: 0x%08lx, Updatee 0x%08lx, RetReg 0x%x\n", + (W_) spa, (W_) spb, + (W_) updatee, (W_) retreg); + + spa = GRAB_SuA(spb); /* Next SuA, SuB */ + spb = GRAB_SuB(spb); + } +} +#endif /* not parallel */ + +#endif /* RUNTIME_DEBUGGING */ + +#endif /* PAR || RUNTIME_DEBUGGING */ +\end{code} |