/* aux-debug.cc: Debug information */ #include #include #include #include "AST.h" #include "CodeContext.h" #include "MethodDecl.h" #include "StringTable.h" #include "aux-code.h" #include "code-assign.h" #include "code-grid.h" #include "code.h" #include "compiler.h" #include "decls.h" #include "delimit.h" #include "errors.h" #include "interface.h" #include "osstream.h" #include "utils.h" #include "lower.h" // Uncomment the following line to emit the textual name instead // of an index into the string table. Also enables the pretty printer. // #define DEBUG_EMIT_NAME static string _temps; // DOB: need a temp with guaranteed lifetime to safely call c_str #ifdef DEBUG_EMIT_NAME static int prettyPrintIndent = 0; # define EMIT_NAME(st, n) (_temps=(n), string("") + "\"" + _temps.c_str() + "\"") # define PRETTY_PRINT(s) \ endl << form("%*s", \ ( assert(strlen(s) > 0), \ 2 * ( ((s)[strlen(s)-1] == '{') ? \ prettyPrintIndent++ : \ ( (s)[strlen(s)-1] == '}' ? \ --prettyPrintIndent : \ prettyPrintIndent))), \ "") \ << s \ << " " #else // DEBUG_EMIT_NAME # define EMIT_NAME(st, n) (_temps=(n), (st).add(_temps.c_str())) # define PRETTY_PRINT(s) s #endif // DEBUG_EMIT_NAME void TreeNode::emitDebugInfo(ofstream &os, StringTable &st) { foriter (p, allChildren(), ChildIter) (*p)->emitDebugInfo(os, st); } // Takes a NameNode, places the fully qualified name on "os", and returns // the same. static ostringstream &qualifiedName(ostringstream &os, TreeNode &n, int recurse = 0) { assert(strcmp(n.oper_name(), "NameNode") == 0); if (!n.qualifier()->absent()) { qualifiedName(os, *n.qualifier(), 1); os << '.'; } os << *n.ident(); if (!recurse) { os << '\0'; } return os; } void CompileUnitNode::emitDebugInfo(ofstream &os, StringTable &st) { if (selectedForCodeGen(true)) { os << PRETTY_PRINT("F{") << PRETTY_PRINT("N") << EMIT_NAME(st, (*ident())); if (!package()->absent()) { ostringstream qn; os << PRETTY_PRINT("P") << EMIT_NAME(st, qualifiedName(qn, *package()).str()); } foriter (import, imports()->allChildren(), ChildIter) { ostringstream qn; os << PRETTY_PRINT("M") << EMIT_NAME(st, qualifiedName(qn, *(*import)->name()).str()); } TreeNode::emitDebugInfo(os, st); os << PRETTY_PRINT("}"); os << "\"\n\""; } } void TemplateDeclNode::emitDebugInfo( ofstream &, StringTable & ) { } void ClassDeclNode::emitDebugInfo(ofstream &os, StringTable &st) { if (selectedForCodeGen(true)) { ClassDecl *d = (ClassDecl *) decl(); os << PRETTY_PRINT("C{") << PRETTY_PRINT("A") << d->asType()->kind() << PRETTY_PRINT("D") << EMIT_NAME(st, d->cDescriptorName()) << PRETTY_PRINT("N") << EMIT_NAME(st, *d->name()); if (!superClass()->absent()) { os << PRETTY_PRINT("S") << EMIT_NAME(st, superClass()->decl()->fullName()); } foriter (interface, interfaces()->allChildren(), ChildIter) { os << PRETTY_PRINT("I") << EMIT_NAME(st, (*interface)->decl()->fullName()); } TreeNode::emitDebugInfo(os, st); os << PRETTY_PRINT("}"); } } void FieldDeclNode::emitDebugInfo(ofstream &os, StringTable &st) { Decl *d = decl(); os << PRETTY_PRINT("F{") << PRETTY_PRINT("A") << d->modifiers() << PRETTY_PRINT("N") << EMIT_NAME(st, *d->name()) << PRETTY_PRINT("M") << EMIT_NAME(st, cFieldName(d)) << PRETTY_PRINT("}"); } void MethodDeclNode::emitDebugInfo(ofstream &os, StringTable &st) { Decl *d = decl(); os << PRETTY_PRINT("M{") << PRETTY_PRINT("A") << d->modifiers() << PRETTY_PRINT("N") << EMIT_NAME(st, *d->name()) << PRETTY_PRINT("M") << EMIT_NAME(st, d->cMethodNameStatic()) << PRETTY_PRINT("}"); } void ConstructorDeclNode::emitDebugInfo(ofstream &os, StringTable &st) { Decl *d = decl(); os << PRETTY_PRINT("O{") << PRETTY_PRINT("A") << d->modifiers() << PRETTY_PRINT("N") << EMIT_NAME(st, *d->name()) << PRETTY_PRINT("M") << EMIT_NAME(st, d->cMethodNameStatic()) << PRETTY_PRINT("}"); } void InterfaceDeclNode::emitDebugInfo(ofstream &os, StringTable &st) { Decl *d = decl(); os << PRETTY_PRINT("I{") << PRETTY_PRINT("A") << d->asType()->kind() << PRETTY_PRINT("N") << EMIT_NAME(st, *d->name()); foriter (interface, interfaces()->allChildren(), ChildIter) { os << PRETTY_PRINT("I") << EMIT_NAME(st, (*interface)->decl()->fullName()); } TreeNode::emitDebugInfo(os, st); os << PRETTY_PRINT("}"); } /* Emit debug information per TreeNode Because the compiler does whole program compilation, emit the information for all files seen. If the compiler ever does partial compilations, simply emit the the debug information of the files compiled and let the debugger or some link-phase tool concatenate them. */ static void emitDebugInfo(ofstream &os, StringTable &st) { foreach (f, llist, *allFiles) (*f)->emitDebugInfo(os, st); } #if 0 // Old static void emitDeclTree1(StringTable &st, ofstream &os, Decl* curr_decl) { size_t name_index; size_t mangled_name_index; assert(curr_decl); // Visit node switch (curr_decl->category()) { case Decl::Field: name_index = st.add((*(curr_decl->name())).c_str()); mangled_name_index = st.add(cFieldName(curr_decl).c_str()); os << "F" << curr_decl->modifiers() << "," << name_index << "," << mangled_name_index; break; case Decl::Method: case Decl::Constructor: name_index = st.add((*(curr_decl->name())).c_str()); mangled_name_index = st.add((curr_decl->cMethodNameStatic()).c_str()); os << "M" << curr_decl->modifiers() << "," << name_index << "," << mangled_name_index; break; case Decl::LocalVar: case Decl::Formal: fatal_error(""); break; case Decl::Interface: case Decl::Class: name_index = st.add((*(curr_decl->name())).c_str()); cout << "Class: " << *(curr_decl->name()) << endl; os << ((curr_decl->category() == Decl::Class) ? "C" : "I") << "{" << curr_decl->asType()->kind() << "," << name_index; if (curr_decl->visits > 0) { assert(curr_decl->hasEnviron()); assert(curr_decl->environ()); for (EnvironIter curr_children = curr_decl->environ()->allProperDecls(); !curr_children.isDone(); curr_children.next()) { Decl &child_decl = *curr_children; emitDeclTree1(st, os, &child_decl); } } os << "}"; break; case Decl::Package: name_index = st.add((*(curr_decl->name())).c_str()); assert(curr_decl->hasEnviron()); assert(curr_decl->environ()); os << "P{" << name_index; for (EnvironIter curr_children = curr_decl->environ()->allProperDecls(); !curr_children.isDone(); curr_children.next()) { Decl &child_decl = *curr_children; emitDeclTree1(st, os, &child_decl); } os << "}"; break; case Decl::StmtLabel: case Decl::Primitive: fatal_error(""); break; default: fatal_error(""); /* Unknown */ } #if 0 // Older assert(curr_decl); if ((curr_decl->visits > 0 || curr_decl->category() == Decl::Package) && (curr_decl->hasEnviron())) { EnvironIter curr_children; assert(curr_decl->environ()); for (curr_children = curr_decl->environ()->allProperDecls(); !curr_children.isDone(); curr_children.next()) { Decl &child_decl = *curr_children; // Visit node switch (child_decl.category()) { case Decl::Class: os << "C" << *(child_decl.name()) << "{"; break; case Decl::Interface: os << "I" << *(child_decl.name()) << "{"; break; case Decl::Field: os << "F{" << *(child_decl.name()) << "}"; break; case Decl::Method: os << "M{" << *(child_decl.name()) << "}"; break; case Decl::Constructor: os << "O{" << *(child_decl.name()) << "}"; break; case Decl::LocalVar: case Decl::Formal: fatal_error(""); break; case Decl::Package: os << "P" << *(child_decl.name()) << "{"; break; case Decl::StmtLabel: case Decl::Primitive: fatal_error(""); break; default: fatal_error(""); /* Unknown */ } // Recurse emitDeclTree1(os, &child_decl); // Revisit node switch (child_decl.category()) { case Decl::Class: case Decl::Interface: os << "}"; break; case Decl::Field: case Decl::Method: case Decl::Constructor: break; case Decl::LocalVar: case Decl::Formal: fatal_error(""); break; case Decl::Package: os << "}"; break; case Decl::StmtLabel: case Decl::Primitive: fatal_error(""); break; default: fatal_error(""); /* Unknown */ } } } #endif // Older } #endif // Old static void dump_chararray(ofstream &os, char *s, size_t size) { while (size > 0) { if (*s == '\0') { os << "\\0\"\n\""; } else { os << *s; } size--; s++; } } // not currently used #if 0 static void dump_intarray(ofstream &os, size_t *s, size_t size) { assert((size % sizeof(size_t)) == 0); os << hex; while (size > sizeof(size_t)) { os << "0x" << *s << ","; size -= 4; s++; } os << "0x" << *s; size -= 4; s++; assert(size == 0); } #endif extern Decl *unnamedPackage; static void emitDeclTree(ofstream &os) { PackageDecl* root = PackageDecl::System; StringTable st(128*1024, 8*1024, 997); assert(root != NULL); os << "const char titaniumDbgDecls[] = \""; #if 0 emitDeclTree1(st, os, PackageDecl::System); emitDeclTree1(st, os, unnamedPackage); #endif emitDebugInfo(os, st); os << "\";" << endl; os << "const unsigned int titaniumDbgStrtabSize = " << st.getStrTableSize() << ";" << endl; os << "const char titaniumDbgStrtab[] = \""; dump_chararray(os, st.getStrTable(), st.getStrTableSize()); os << "\";" << endl; #if 0 os << "const unsigned int titaniumDbgHashSize = " << st.getHashTableSize() << ";" << endl; os << "const unsigned int titaniumDbgHash[] = {"; dump_intarray(os, (size_t *) st.getHashTable(), st.getHashTableSize()); os << "};" << endl; #endif } /* Stack Debug Information Place information on the program stack. This allows debug information to be easily saved and read on a per thread basis. */ void emitStackDebugInformation(ofstream &os) { /* Emit the name of the "main" method. */ if (debugger_level > 0) { os << endl << " /* Debug information start */" << endl << endl; os << " int titaniumDbgMyProc = MYPROC;" << endl; os << endl << " /* Debug information end */" << endl; } } /* Static Debug Information The information is placed in the data segment of file that contains the "main" method. This works because the compiler does whole program compilation. Also, placing it in the data segment precludes doing any special handling of the debug information at link time. */ void emitStaticDebugInformation(ofstream &os, const MethodDecl &mainMethod) { if (debugger_level > 0) { os << endl << "/* Debug information start */" << endl << endl; #ifndef USE_GC_NONE /* Emit GC information */ os << "const char titaniumDbgDisableSignals[] = \"" << "GC_write_fault_handler\\0SEGV\\0" << "GC_write_fault_handler\\0BUS\\0" << "\";" << endl; os << "const int titaniumDbgDisableSignalsSize = " << "sizeof(titaniumDbgDisableSignals);" << endl; #endif /* USE_GC_NONE */ /* Emit the name of the "main" method. */ os << "const char titaniumDbgMainMethod[] = \"" << mainMethod.cMethodNameStatic() << "\";" << endl; /* Emit the Decl tree */ emitDeclTree(os); os << endl << "/* Debug information end */" << endl; } }