From 2adbe82704061363a5377044f2d46bff34291b93 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 5 Jan 2015 20:28:56 -0500 Subject: [PATCH] merge in debug template code --- bin/CMakeLists.txt | 38 +++++- bin/commands.cpp | 181 +++++++++++++++++++++---- bin/debugger.cpp | 114 ++++++++-------- bin/debugger.h | 7 + bin/intern.cpp | 101 ++++++++++++++ bin/intern.h | 14 ++ bin/lexer.rl | 83 +++++++++--- bin/loader.cpp | 22 ---- bin/parser.lemon | 19 ++- bin/template.cpp | 270 ++++++++++++++++++++++++++++++++++++++ bin/template.h | 88 +++++++++++++ bin/template_loader.rl | 254 +++++++++++++++++++++++++++++++++++ bin/template_parser.lemon | 127 ++++++++++++++++++ 13 files changed, 1185 insertions(+), 133 deletions(-) create mode 100644 bin/intern.cpp create mode 100644 bin/intern.h create mode 100644 bin/template.cpp create mode 100644 bin/template.h create mode 100644 bin/template_loader.rl create mode 100644 bin/template_parser.lemon diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt index 41ea2ff..5e1ffff 100644 --- a/bin/CMakeLists.txt +++ b/bin/CMakeLists.txt @@ -20,7 +20,7 @@ add_custom_command( MAIN_DEPENDENCY lexer.rl ) -add_custom_command( +add_custom_command( OUTPUT parser.cpp parser.h COMMAND cp -f "${CMAKE_CURRENT_SOURCE_DIR}/parser.lemon" "parser.lemon" COMMAND lemon parser.lemon @@ -31,6 +31,20 @@ add_custom_command( DEPENDS debugger.h ) + +add_custom_command( + OUTPUT template_parser.cpp template_parser.h + COMMAND cp -f "${CMAKE_CURRENT_SOURCE_DIR}/template_parser.lemon" "template_parser.lemon" + COMMAND lemon template_parser.lemon + COMMAND cp -f template_parser.h "${CMAKE_CURRENT_SOURCE_DIR}/" + COMMAND cp -f template_parser.out "${CMAKE_CURRENT_SOURCE_DIR}/" + COMMAND mv -f template_parser.c template_parser.cpp + MAIN_DEPENDENCY template_parser.lemon + DEPENDS debugger.h +) + + + add_custom_command( OUTPUT loadtrap.cpp COMMAND ragel -p -G2 -o loadtrap.cpp "${CMAKE_CURRENT_SOURCE_DIR}/loadtrap.rl" @@ -39,16 +53,26 @@ add_custom_command( ) -set_source_files_properties( - loadtrap.cpp lexer.cpp - PROPERTIES - COMPILE_FLAGS - "${CMAKE_CXX_FLAGS} -Wno-unused-variable" +add_custom_command( + OUTPUT template_loader.cpp + COMMAND ragel -p -G2 -o template_loader.cpp "${CMAKE_CURRENT_SOURCE_DIR}/template_loader.rl" + MAIN_DEPENDENCY template_loader.rl + DEPENDS debugger.h template_parser.h ) +set_source_files_properties( + loadtrap.cpp lexer.cpp template_loader.cpp + PROPERTIES + COMPILE_FLAGS + "${CMAKE_CXX_FLAGS} -Wno-unused-variable" +) + add_executable(mpw loader.cpp debugger.cpp debugger_internal.cpp - address_map.cpp lexer.cpp parser.cpp loadtrap.cpp commands.cpp) + address_map.cpp lexer.cpp parser.cpp loadtrap.cpp + commands.cpp + template_loader.cpp template_parser.cpp intern.cpp template.cpp) + target_link_libraries(mpw CPU_LIB) target_link_libraries(mpw TOOLBOX_LIB) diff --git a/bin/commands.cpp b/bin/commands.cpp index 2dd3685..83093a8 100644 --- a/bin/commands.cpp +++ b/bin/commands.cpp @@ -1,19 +1,70 @@ #include #include +#include #include #include +#include +#include + #include "debugger.h" #include "debugger_internal.h" +#include "loader.h" // Flags namespace Debug { using namespace Internal; + + void hexdump(const uint8_t *data, ssize_t size, uint32_t address = 0) + { + const char *HexMap = "0123456789abcdef"; + + char buffer1[16 * 3 + 1 + 1]; + char buffer2[16 + 1]; + ssize_t offset = 0; + unsigned i, j; + + + while(size > 0) + { + std::memset(buffer1, ' ', sizeof(buffer1)); + std::memset(buffer2, ' ', sizeof(buffer2)); + + unsigned linelen = (unsigned)std::min(size, (ssize_t)16); + + + for (i = 0, j = 0; i < linelen; i++) + { + unsigned x = data[i]; + buffer1[j++] = HexMap[x >> 4]; + buffer1[j++] = HexMap[x & 0x0f]; + j++; + if (i == 7) j++; + + // isascii not part of std:: and may be a macro. + buffer2[i] = isascii(x) && std::isprint(x) ? x : '.'; + + } + + buffer1[sizeof(buffer1)-1] = 0; + buffer2[sizeof(buffer2)-1] = 0; + + + std::printf("%06x: %s %s\n", address + (unsigned)offset, buffer1, buffer2); + offset += 16; + data += 16; + size -= 16; + } + std::printf("\n"); + } + + + void PrintError(uint32_t value) { /* expr ; error -- interpret expr as an OSErr */ @@ -66,6 +117,7 @@ namespace Debug { struct tm *tm; time_t t = OS::MacToUnix(value); + // localtime vs gmtime? tm = ::localtime(&t); strftime(buffer, sizeof(buffer), "%Y-%m-%d %I:%M:%S %p", tm); puts(buffer); @@ -76,30 +128,115 @@ namespace Debug { { puts(""); - puts(" ## "); - puts(" ## ## #### "); - puts(" ## #### ## "); - puts(" ## ## "); - puts(" ## ## ## ## "); - puts(" ## ## #### "); - puts("## ## ## ## "); - puts(" ######## #### ## ## "); - puts(" ## #################### ## "); - puts(" ## ############## ## "); - puts(" #### ############ ## "); - puts(" ###### ###### ## "); - puts(" ###### ## "); - puts(" #### ## "); - puts(" ## ## "); - puts(" ## ################ ## "); - puts(" ## ## ## ## "); - puts(" ## ## ## ## "); - puts(" ## ## ## ## "); - puts(" ## ## ## ## "); - puts(" ## ## ## ## "); - puts(" ###### ###### "); + puts(" ## "); + puts(" ## ## #### "); + puts(" ## #### ## "); + puts(" ## ## "); + puts(" ## ## ## ## "); + puts(" ## ## #### "); + puts(" ## ## ## ## "); + puts(" ######## #### ## ## "); + puts(" ## #################### ## "); + puts(" ## ############## ## "); + puts(" #### ############ ## "); + puts(" ###### ###### ## "); + puts(" ###### ## "); + puts(" #### ## "); + puts(" ## ## "); + puts(" ## ################ ## "); + puts(" ## ## ## ## "); + puts(" ## ## ## ## "); + puts(" ## ## ## ## "); + puts(" ## ## ## ## "); + puts(" ## ## ## ## "); + puts(" ###### ###### "); puts(""); } + + void StackCrawl(void) + { + // stack crawl based on a6 frame pointers. (link/unlink) + // start with current a6. + + uint32_t a6 = cpuGetAReg(6); + + /* + * a6: previous a6 + * a6+4 return pc + * .... parameters, locals, stack data + * previous a6 + * return pc + * .... + */ + // todo -- need a function to verify address is w/in stack range. + + // todo print in reverse order so newest frame doesn't scroll away. + + if (!a6) return; + printf("a6: %08x\n", a6); + + while(a6) + { + + uint32_t prevA6 = ReadLong(a6); + if (prevA6 <= a6) break; + + uint32_t pc = ReadLong(a6+4); // + + + + // hexdump contents between pc and previous.... + ssize_t size = prevA6 - a6 - 8; + hexdump(Flags.memory + a6 + 8, size); + + + + puts("------------"); + printf("a6: %08x\n", prevA6); + printf("pc: %08x", pc); + + // find the routine name... + for (const auto &kv : SymbolTable) + { + const auto &name = kv.first; + auto range = kv.second; + + // range end may be 0 + if ((pc == range.first) || (pc >= range.first && pc < range.second)) + { + uint32_t offset = pc - range.first; + if (offset) + printf(" %s+$%x", name.c_str(), offset); + else + printf(" %s", name.c_str()); + break; + } + } + puts(""); + + a6 = prevA6; + } + } + + void Dump(uint32_t start, int size) + { + // TODO -- if no address, use previous address. + // TODO -- support range? + + + if (size <= 0) return; + + uint32_t end = start + size; + + if (start >= Flags.memorySize) return; + + end = std::min(end, Flags.memorySize); + size = end - start; + + hexdump(Flags.memory + start, size, start); + } + + } \ No newline at end of file diff --git a/bin/debugger.cpp b/bin/debugger.cpp index 9f1891d..3549aa5 100644 --- a/bin/debugger.cpp +++ b/bin/debugger.cpp @@ -49,6 +49,7 @@ #include "debugger.h" #include "debugger_internal.h" +#include "template.h" #include #include @@ -62,14 +63,12 @@ #include #include - - namespace { using namespace Debug::Internal; const uint32_t kGlobalSize = 0x10000; - const uint32_t kBackTraceSize = 20; + const uint32_t kBackTraceSize = 50; bool sigInt = false; bool memBreak = false; @@ -84,6 +83,8 @@ namespace { ToolMap tbrkMap; // tool breaks. + std::unordered_map TemplateTable; + struct BackTraceInfo { uint32_t a[8]; uint32_t d[8]; @@ -103,48 +104,7 @@ namespace { std::deque BackTrace; - void hexdump(const uint8_t *data, ssize_t size, uint32_t address = 0) - { - const char *HexMap = "0123456789abcdef"; - char buffer1[16 * 3 + 1 + 1]; - char buffer2[16 + 1]; - ssize_t offset = 0; - unsigned i, j; - - - while(size > 0) - { - std::memset(buffer1, ' ', sizeof(buffer1)); - std::memset(buffer2, ' ', sizeof(buffer2)); - - unsigned linelen = (unsigned)std::min(size, (ssize_t)16); - - - for (i = 0, j = 0; i < linelen; i++) - { - unsigned x = data[i]; - buffer1[j++] = HexMap[x >> 4]; - buffer1[j++] = HexMap[x & 0x0f]; - j++; - if (i == 7) j++; - - // isascii not part of std:: and may be a macro. - buffer2[i] = isascii(x) && std::isprint(x) ? x : '.'; - - } - - buffer1[sizeof(buffer1)-1] = 0; - buffer2[sizeof(buffer2)-1] = 0; - - - std::printf("%06x: %s %s\n", address + (unsigned)offset, buffer1, buffer2); - offset += 16; - data += 16; - size -= 16; - } - std::printf("\n"); - } void printMacsbug(uint32_t pc, uint32_t opcode, uint32_t *newPC = nullptr) @@ -479,6 +439,34 @@ namespace { namespace Debug { +std::string ReadPString(uint32_t address) +{ + std::string tmp; + unsigned size = ReadByte(address++); + + tmp.reserve(size); + for (unsigned i = 0; i < size; ++i) + tmp.push_back(ReadByte(address++)); + + + return tmp; +} + +std::string ReadCString(uint32_t address) +{ + std::string tmp; + + for (;;) + { + char c = ReadByte(address++); + if (!c) break; + tmp.push_back(c); + } + + return tmp; +} + + uint32_t ReadLong(uint32_t address) { uint32_t tmp = 0; @@ -633,23 +621,6 @@ void Print(uint32_t data) } -void Dump(uint32_t start, int size) -{ - // TODO -- if no address, use previous address. - // TODO -- support range? - - - if (size <= 0) return; - - uint32_t end = start + size; - - if (start >= Flags.memorySize) return; - - end = std::min(end, Flags.memorySize); - size = end - start; - - hexdump(Flags.memory + start, size, start); -} @@ -730,7 +701,7 @@ void PrintRegisters(const BackTraceInfo &i) i.a[4], i.a[5], i.a[6], i.a[7] ); - printf("PC: %08X CSR: %04x %s\n", i.pc, i.csr, srbits); + printf("PC: %08x CSR: %04x %s\n", i.pc, i.csr, srbits); } @@ -1135,8 +1106,24 @@ void Info(uint32_t address) cp = nullptr; } } + } + void ApplyTemplate(int32_t address, const std::string &name) + { + // find the template.. + + auto iter = TemplateTable.find(name); + if (iter == TemplateTable.end()) { + fprintf(stderr, "Unknown template: %s\n", name.c_str()); + return; + } + + FieldEntry *e = iter->second; + ApplyTemplate(address, e); + } + + namespace { /* @@ -1255,6 +1242,9 @@ void Shell() LoadTrapFile(MPW::RootDirPathForFile("Globals.text"), GlobalTable); LoadTrapFile(MPW::RootDirPathForFile("Traps.text"), TrapTable); + LoadTemplateFile(MPW::RootDirPathForFile("Templates.text"), TemplateTable); + + // load the error code to error mnemonic ErrorTableInvert.reserve(ErrorTable.size()); diff --git a/bin/debugger.h b/bin/debugger.h index 4666eab..46b7bbf 100644 --- a/bin/debugger.h +++ b/bin/debugger.h @@ -110,6 +110,9 @@ uint32_t ReadLong(uint32_t); uint16_t ReadWord(uint32_t); uint8_t ReadByte(uint32_t); +std::string ReadPString(uint32_t); +std::string ReadCString(uint32_t); + void Print(uint32_t value); void PrintRegisters(); @@ -144,6 +147,10 @@ void ReadWriteBreak(int32_t address); void PrintError(uint32_t value); void PrintDate(uint32_t value); +void StackCrawl(void); + +void ApplyTemplate(int32_t address, const std::string &name); + } #endif diff --git a/bin/intern.cpp b/bin/intern.cpp new file mode 100644 index 0000000..2a65947 --- /dev/null +++ b/bin/intern.cpp @@ -0,0 +1,101 @@ +#include "intern.h" +#include + +namespace { + + std::unordered_multimap InternTable; + + std::string EmptyString; + + unsigned int DJBHash(const char* begin, size_t length) + { + unsigned int hash = 5381; + + for(size_t i = 0; i < length; ++i) + { + hash = ((hash << 5) + hash) + (begin[i]); + } + + return hash; + } +} + +namespace Intern { + + const std::string *String(std::string &&str) + { + size_t size = str.size(); + if (!size) return &EmptyString; + + unsigned hash = DJBHash(str.data(), size); + + auto range = InternTable.equal_range(hash); + auto iter = range.first; + auto endit = range.second; + + for( ; iter != endit; ++iter) + { + // hash matches, make sure the string does. + const std::string *s = iter->second; + + if (s->size() == size && std::memcmp(s->data(), str.data(), size) == 0) + return s; + } + + // insert it. I suppose this could throw, in which case a string would leak. + std::string *s = new std::string(std::move(str)); + InternTable.emplace(std::make_pair(hash, s)); + return s; + } + + const std::string *String(const char *begin, size_t size) + { + if (!size) return &EmptyString; + + unsigned hash = DJBHash(begin, size); + + auto range = InternTable.equal_range(hash); + auto iter = range.first; + auto endit = range.second; + + for( ; iter != endit; ++iter) + { + // hash matches, make sure the string does. + const std::string *s = iter->second; + + if (s->size() == size && std::memcmp(s->data(), begin, size) == 0) + return s; + } + + // insert it. I suppose this could throw, in which case a string would leak. + std::string *s = new std::string(begin, size); + InternTable.emplace(std::make_pair(hash, s)); + return s; + } + + const std::string *String(const char *begin, const char *end) + { + return String(begin, end - begin); + } + + + const std::string *String(const char *cp) + { + if (!cp || !*cp) return &EmptyString; + + return String(cp, strlen(cp)); + } + + const std::string *String(const std::string &s) + { + return String(s.data(), s.size()); + } + + + + +} + + + + diff --git a/bin/intern.h b/bin/intern.h new file mode 100644 index 0000000..8d319cd --- /dev/null +++ b/bin/intern.h @@ -0,0 +1,14 @@ +#ifndef __intern_h__ +#define __intern_h__ + +#include + +namespace Intern { + const std::string *String(const std::string &s); + const std::string *String(std::string &&s); + const std::string *String(const char *); + const std::string *String(const char *, size_t size); + const std::string *String(const char *, const char *); +}; + +#endif \ No newline at end of file diff --git a/bin/lexer.rl b/bin/lexer.rl index e4c4a35..45e8510 100644 --- a/bin/lexer.rl +++ b/bin/lexer.rl @@ -105,6 +105,21 @@ namespace { # this exits with cs == lexer_en_error. error := any* ${ fbreak; }; + # identifiers. + ident := |* + + [ \t\r\n]+; + + [_A-Za-z][_A-Za-z0-9]* { + + std::unique_ptr sp(new std::string(ts, te)); + + Parse(parser, tkIDENTIFIER, Token::Make(sp.get(), 0), command); + Strings.push_back(std::move(sp)); + }; + + *|; + # semi-colon commands. semi := |* @@ -130,6 +145,10 @@ namespace { Parse(parser, tkSEMIERROR, 0, command); }; + 't'i { + Parse(parser, tkSEMIT, 0, command); + fgoto ident; + }; *|; @@ -196,6 +215,7 @@ namespace { Parse(parser, tkINTEGER, value, command); }; + # todo -- (\['\]|[^'\]){1,4} ? ['] [^']{1,4} ['] { // 4 cc code @@ -250,6 +270,12 @@ namespace { Parse(parser, tkDUMP, 0, command); }; + + 'sc'i | 'stackcrawl'i { + Parse(parser, tkSTACKCRAWL, 0, command); + }; + + 'h'i | 'help'i { Parse(parser, tkHELP, 0, command); }; @@ -350,28 +376,49 @@ bool ParseLine(const char *iter, Command *command) const char *te; int cs, act; - for(;;) + %% write init; + + + %% write exec; + + if (cs == lexer_error || cs < lexer_first_final) { + if (p == eof) + { + fprintf(stderr, "Unexpected end of line\n"); + } + else + { + for (size_t i = 0, l = 2 + (p - iter); i < l; ++i) + fputc(' ', stderr); - %% write init; - %% write exec; + fprintf(stderr, "^\nunexpected character: `%c'\n", *p); - if (cs == lexer_error) - { - fprintf(stderr, "illegal character: `%c'\n", *p); - ParseFree(parser, free); - return false; - } - if (cs == lexer_en_error) - { - ParseFree(parser, free); - return false; - } - if (p == pe) - { - Parse(parser, tkEOL, 0, command); - break; } + + ParseFree(parser, free); + return false; + } + + /* + if (cs == lexer_en_error) + { + ParseFree(parser, free); + return false; + } + + if (cs < lexer_first_final) + { + fprintf(stderr, "Incomplete command\n"); + ParseFree(parser, free); + return false; + } + */ + + if (p == pe) + { + // always true? + Parse(parser, tkEOL, 0, command); } Parse(parser, 0, 0, command); diff --git a/bin/loader.cpp b/bin/loader.cpp index 49f5161..063710e 100644 --- a/bin/loader.cpp +++ b/bin/loader.cpp @@ -69,28 +69,6 @@ const uint32_t kGlobalSize = 0x10000; uint8_t *Memory = nullptr; uint32_t MemorySize = 0; -#if 0 -uint32_t EmulatedNewPtr(uint32_t size) -{ - if (size & 0x01) size++; - - if (HighWater + size > MemorySize) - { - fprintf(stderr, "Insufficient Memory!\n"); - exit(EX_CONFIG); - } - - - uint32_t address = HighWater; - - - HighWater += size; - - std::memset(Memory + HighWater, 0, size); - - return address; -} -#endif uint8_t ReadByte(const void *data, uint32_t offset) { diff --git a/bin/parser.lemon b/bin/parser.lemon index c27502c..6c6b19f 100644 --- a/bin/parser.lemon +++ b/bin/parser.lemon @@ -162,6 +162,10 @@ stmt ::= LIST expr(a) EOL. Debug::List(a.intValue); } +stmt ::= STACKCRAWL EOL. +{ + Debug::StackCrawl(); +} stmt ::= expr(a) SEMI SEMIH EOL. { @@ -200,19 +204,30 @@ stmt ::= expr(a) COLON expr(b) SEMI SEMIL EOL. Debug::List(a.intValue, b.intValue); } - stmt ::= expr(a) SEMI SEMIDATE EOL. { Debug::PrintDate(a.intValue); } - stmt ::= expr(a) SEMI SEMIERROR EOL. { Debug::PrintError(a.intValue); } + +stmt ::= expr(a) SEMI SEMIT IDENTIFIER(b) EOL. +{ + Debug::ApplyTemplate(a.intValue, *b.stringValue); +} + + +stmt ::= expr SEMI SEMIT error EOL. +{ + fprintf(stderr, "usage: expression ; t TemplateName\n"); +} + + stmt ::= DREGISTER(a) EQ expr(b) EOL. { Debug::SetDRegister(a.intValue, b.intValue); diff --git a/bin/template.cpp b/bin/template.cpp new file mode 100644 index 0000000..ff927a5 --- /dev/null +++ b/bin/template.cpp @@ -0,0 +1,270 @@ +#include "template.h" +#include "debugger.h" +#include "debugger_internal.h" +#include "loader.h" // Flags. + +#include + + +namespace Debug { + + using namespace Debug::Internal; + + namespace { + + + unsigned CalcOneSize(FieldEntry *e) + { + unsigned s = (e->type & 0x0f00) >> 8; + + if (!s) { + // struct or pointer... + if (e->type & 0x8000) s = 4; + else if (e->tmpl) s = e->tmpl->struct_size; + } + + if (e->count) s *= e->count; + + return s; + } + + unsigned CalcSize(FieldEntry *e) + { + unsigned size = 0; + + while (e) + { + size += CalcOneSize(e); + e = e->next; + } + return size; + } + + FieldEntry *Reverse(FieldEntry *e) + { + if (!e) return e; + + // reverse the order... + FieldEntry *prev; + FieldEntry *next; + + prev = nullptr; + for(;;) + { + next = e->next; + e->next = prev; + + prev = e; + e = next; + if (!e) return prev; + } + } + + + inline bool ValidPointer(uint32_t value) + { + return value && value < Flags.memorySize; + } + + + } + + void CreateTypedef(const std::string *name, int type, TemplateParseInfo *info) + { + // check if it's an existing typedef... + + auto &Templates = *info->templates; + auto &Types = *info->types; + + + auto iter = Types.find(*name); + if (iter != Types.end()) + { + if (iter->second == type) return; // ok, just a duplicate. + fprintf(stderr, "Template Error: line %d - redefining %s\n", + info->LineNumber, name->c_str()); + + return; + } + + if (Templates.find(*name) != Templates.end()) + { + fprintf(stderr, "Template Error: line %d - redefining %s\n", + info->LineNumber, name->c_str()); + + return; + } + + Types.emplace(std::make_pair(*name, type)); + } + + + void CreateTemplate(const std::string *name, FieldEntry *firstField, TemplateParseInfo *info) + { + auto &Templates = *info->templates; + auto &Types = *info->types; + + // check if it exists... + + if (Templates.find(*name) != Templates.end()) + { + fprintf(stderr, "Template Error: line %d - redefining %s\n", + info->LineNumber, name->c_str()); + return; + } + + if (Types.find(*name) != Types.end()) + { + fprintf(stderr, "Template Error: line %d - redefining %s\n", + info->LineNumber, name->c_str()); + return; + } + + firstField = Reverse(firstField); + firstField->struct_size = CalcSize(firstField); + + Templates.emplace(std::make_pair(*name, firstField)); + } + + void CleanupString(std::string &s) + { + // replace non-printables. + std::transform(s.begin(), s.end(), s.begin(), [](char c){ + return isprint(c) ? c : '.'; + }); + + if (s.size() > 40) { + s.resize(37); + s.append("..."); + } + } + void PrettyPrint(uint32_t value, unsigned type) + { + + switch (type) + { + case kOSType: + // print 4-cc code + fputc(' ', stdout); + fputs(ToolBox::TypeToString(value).c_str(), stdout); + return; + + case kOSErr: + // print value + short name + { + printf(" %-6d", (int16_t)value); + auto iter = ErrorTableInvert.find(value); + if (iter != ErrorTableInvert.end()) printf(" %s", iter->second.c_str()); + } + return; + + case kPStringPtr: + // read the string... + if (ValidPointer(value)) + { + std::string tmp = ReadPString(value); + CleanupString(tmp); + printf(" '%s'", tmp.c_str()); + } + return; + + case kCStringPtr: + // read the string... + if (ValidPointer(value)) + { + std::string tmp = ReadCString(value); + CleanupString(tmp); + printf(" '%s'", tmp.c_str()); + } + return; + return; + + case kBoolean: + fputc(' ', stdout); + fputs(value ? "true" : "false", stdout); + return; + + case kHandle: + // print address, size, locked stats. + return; + + } + + // int/signed int - print base 10. + + } + + + void ApplyTemplate(uint32_t address, FieldEntry *e, unsigned indent) + { + unsigned offset = 0; + + if (!e) return; + + for( ; e ; e = e->next) + { + + bool nonl = false; + unsigned count = e->count; + unsigned type = e->type; + unsigned s = (type & 0x0f00) >> 8; + + for (unsigned i = 0; i < indent; ++i) fputc('>',stdout); + fputs(e->name->c_str(),stdout); + for(unsigned i = indent + e->name->length(); i < 40; ++i) fputc(' ',stdout); + + // todo -- support arrays + // todo -- pretty print values (boolean, oserr, ostype, etc.) + switch(s) + { + case 1: + { + uint8_t value = ReadByte(address + offset); + printf(" %02x", value); + PrettyPrint(value, type); + break; + } + + case 2: + { + uint16_t value = ReadWord(address + offset); + printf(" %04x", value); + PrettyPrint(value, type); + break; + } + + case 4: + { + uint32_t value = ReadLong(address + offset); + printf("%08x", value); + PrettyPrint(value, type); + break; + } + + case 0: + // either a pointer or a struct + if (type & 0x8000) { + // pointer. + uint32_t value = ReadLong(address + offset); + printf("%08x", value); + PrettyPrint(value, type); + break; + } + if (type == 0) { + // struct ... recurse. + fputc('\n', stdout); + nonl = true; + ApplyTemplate(address + offset, e->tmpl, indent + 1); + break; + } + } + + if (!nonl) fputc('\n', stdout); + offset += CalcOneSize(e); + + } + + + } + +} diff --git a/bin/template.h b/bin/template.h new file mode 100644 index 0000000..a1dc023 --- /dev/null +++ b/bin/template.h @@ -0,0 +1,88 @@ +#ifndef __debug_template_h__ +#define __debug_template_h__ + +#include +#include + +namespace Debug { + + + enum { + + // 0x8000 = pointer + // 0x1000 = unsigned + // 0x0f00 = size + // 0-255 - type + + kSInt64 = 0x0800, + kUInt64 = 0x1800, + + kSInt32 = 0x0400, + kUInt32 = 0x1400, + + kSInt16 = 0x0200, + kUInt16 = 0x1200, + + kSInt8 = 0x0100, + kUInt8 = 0x1100, + + kStruct = 0x0000, + kStructPtr = 0x8000, + + // ptrs are a special case where size = 0 + //kVoid = 0x0001, + kVoidPtr = 0x8001, + + // these exist for display purposes. + kCStringPtr = 0x8002, + kPStringPtr = 0x8003, + + kOSType = 0x00404, + kBoolean = 0x0105, + kOSErr = 0x0206, + kHandle = 0x04007, + + }; + + inline unsigned MakePtr(unsigned type) { return type | 0x8000; } + inline unsigned MakeType(unsigned size, bool sign, bool ptr) { + unsigned rv = size << 8; + if (!sign) rv |= 0x0100; + if (ptr) rv |= 0x8000; + return rv; + } + + + + struct FieldEntry; + typedef FieldEntry *Template; + + struct FieldEntry { + std::string *name; + unsigned type; + unsigned count; + Template tmpl; + FieldEntry *next; + unsigned struct_size; // only populated for head entry. + }; + + struct TemplateParseInfo { + std::unordered_map *templates; + std::unordered_map *types; + int LineNumber; + }; + + + void CreateTypedef(const std::string *name, int type, TemplateParseInfo *); + void CreateTemplate(const std::string *name, FieldEntry *firstField, TemplateParseInfo *); + + + bool LoadTemplateFile(const std::string &filename, std::unordered_map &); + + + void ApplyTemplate(uint32_t address, FieldEntry *e, unsigned indent = 0); + + + +} +#endif diff --git a/bin/template_loader.rl b/bin/template_loader.rl new file mode 100644 index 0000000..7edd15e --- /dev/null +++ b/bin/template_loader.rl @@ -0,0 +1,254 @@ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "template_parser.h" +#include "template.h" +#include "intern.h" + +namespace { + + int tox(char c) + { + c |= 0x20; // lowercase it. + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + return 0; + } + uint32_t scan10(const char *begin, const char *end) + { + return std::accumulate(begin, end, 0, + [](uint32_t value, char c){ + return value * 10 + c - '0'; + }); + } + + uint32_t scan16(const char *begin, const char *end) + { + return std::accumulate(begin, end, 0, + [](uint32_t value, char c){ + return (value << 4) + tox(c); + }); + } + + +} + +void *TemplateParseAlloc(void *(*mallocProc)(size_t)); +void TemplateParseFree(void *p, void (*freeProc)(void*)); + +void TemplateParse(void *yyp, int yymajor, void *yyminor, Debug::TemplateParseInfo *); + + +void TemplateParse(void *yyp, int yymajor, int yyminor, Debug::TemplateParseInfo *info) +{ + TemplateParse(yyp, yymajor, (void *)(ptrdiff_t)yyminor, info); +} + +void TemplateParse(void *yyp, int yymajor, const std::string *yyminor, Debug::TemplateParseInfo *info) +{ + TemplateParse(yyp, yymajor, (void *)yyminor, info); +} + +#define TemplateParse(a,b,c) TemplateParse(a,b,c, &info) + +%%{ + machine lexer; + + # this exits with cs == lexer_en_error. + error := any* ${ fbreak; }; + + block_comment := |* + [\n\r] { info.LineNumber++; }; + '*/' { fgoto main; }; + any ; + *|; + + main := |* + + [\n\r] { info.LineNumber++; }; + [ \t]+; + + '//' [^\r\n]* ; + '/*' { fgoto block_comment; }; + + ';' { TemplateParse(parser, tkSEMI, 0); }; + '{' { TemplateParse(parser, tkLBRACE, 0); }; + '}' { TemplateParse(parser, tkRBRACE, 0); }; + '[' { TemplateParse(parser, tkLBRACKET, 0); }; + ']' { TemplateParse(parser, tkRBRACKET, 0); }; + '*' { TemplateParse(parser, tkSTAR, 0); }; + + 'struct' { TemplateParse(parser, tkSTRUCT, 0); }; + 'typedef' { TemplateParse(parser, tkTYPEDEF, 0); }; + + + 'int' { TemplateParse(parser, tkINT, 0); }; + 'long' { TemplateParse(parser, tkLONG, 0); }; + 'short' { TemplateParse(parser, tkSHORT, 0); }; + 'volatile' { TemplateParse(parser, tkVOLATILE, 0); }; + #'const' { TemplateParse(parser, tkCONST, 0); }; + 'char' { TemplateParse(parser, tkCHAR, 0); }; + #'bool' { TemplateParse(parser, tkBOOL, 0); }; + 'void' { TemplateParse(parser, tkVOID, 0); }; + + 'signed' { TemplateParse(parser, tkSIGNED, 0); }; + 'unsigned' { TemplateParse(parser, tkUNSIGNED, 0); }; + + 'int64_t' { TemplateParse(parser, tkTYPECODE, kSInt64); }; + 'uint64_t' { TemplateParse(parser, tkTYPECODE, kUInt64); }; + + 'int32_t' { TemplateParse(parser, tkTYPECODE, kSInt32); }; + 'uint32_t' { TemplateParse(parser, tkTYPECODE, kUInt32); }; + + 'int16_t' { TemplateParse(parser, tkTYPECODE, kSInt16); }; + 'uint16_t' { TemplateParse(parser, tkTYPECODE, kUInt16); }; + + 'int8_t' { TemplateParse(parser, tkTYPECODE, kSInt8); }; + 'uint8_t' { TemplateParse(parser, tkTYPECODE, kUInt8); }; + + + 'StringPtr' { TemplateParse(parser, tkTYPECODE, kPStringPtr); }; + 'CStringPtr' { TemplateParse(parser, tkTYPECODE, kCStringPtr); }; + 'Ptr' { TemplateParse(parser, tkTYPECODE, kVoidPtr); }; + 'OSType' { TemplateParse(parser, tkTYPECODE, kOSType); }; + 'OSErr' { TemplateParse(parser, tkTYPECODE, kOSErr); }; + 'Boolean' { TemplateParse(parser, tkTYPECODE, kBoolean); }; + 'Handle' { TemplateParse(parser, tkTYPECODE, kHandle); }; + + + # numbers. negative numbers are not allowed. + + '0x'i xdigit+ { + // hexadecimal + uint32_t value = scan16(ts + 2, te); + TemplateParse(parser, tkINTEGER, value); + }; + + digit+ { + uint32_t value = scan10(ts, te); + TemplateParse(parser, tkINTEGER, value); + }; + + # identifier ... but also need to check if it's a type. + [A-Za-z_][A-Za-z0-9_]* { + + // intern the string. + + const std::string *name = Intern::String(ts, te); + bool ok = false; + + if (!ok) { + auto iter = Types.find(*name); + if (iter != Types.end()) + { + TemplateParse(parser, tkTYPECODE, iter->second); + ok = true; + } + } + if (!ok) { + auto iter = Templates.find(*name); + if (iter != Templates.end()) + { + TemplateParse(parser, tkTEMPLATE, iter->second); + ok = true; + } + } + if (!ok) + { + TemplateParse(parser, tkIDENTIFIER, name); + } + }; + + *|; + +}%% + +namespace Debug { + + + + + +bool LoadTemplateFile(const std::string &filename, std::unordered_map &Templates) +{ + %% write data; + + void *parser; + + int fd; + struct stat st; + char *buffer; + + std::unordered_map Types; + TemplateParseInfo info; + + info.LineNumber = 1; + info.templates = &Templates; + info.types = &Types; + + // simple types are handled via the lexer so no need to populate them here. + + + + if (stat(filename.c_str(), &st) < 0) return false; + if (st.st_size == 0) return false; + + fd = open(filename.c_str(), O_RDONLY); + if (fd < 0) { + perror("Error opening template file: "); + return false; + } + + buffer = (char *)mmap(nullptr, st.st_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); + if (buffer == MAP_FAILED) { + perror("Error mapping template file: "); + close(fd); + return false; + } + close(fd); + + + + + parser = TemplateParseAlloc(malloc); + + const char *p = buffer; + const char *pe = buffer + st.st_size; + const char *eof = pe; + const char *ts; + const char *te; + int cs, act; + + %% write init; + %% write exec; + + if (cs == lexer_error || cs < lexer_first_final) + { + if (p == eof) + fprintf(stderr, "Template error: line %d - unexpected EOF\n", info.LineNumber); + else + fprintf(stderr, "Template error: line %d - illegal character: `%c'\n", info.LineNumber, *p); + TemplateParseFree(parser, free); + munmap(buffer, st.st_size); + return false; + } + + + TemplateParse(parser, 0, 0); + TemplateParseFree(parser, free); + + munmap(buffer, st.st_size); + + return true; +} + +} \ No newline at end of file diff --git a/bin/template_parser.lemon b/bin/template_parser.lemon new file mode 100644 index 0000000..ff56e60 --- /dev/null +++ b/bin/template_parser.lemon @@ -0,0 +1,127 @@ +%token_prefix tk +%name TemplateParse +%extra_argument { Debug::TemplateParseInfo *info } + + +%include { + + #include + #include + + #include "template.h" + + using namespace Debug; +} + +%type struct_fields { FieldEntry * } +%type struct_field { FieldEntry * } + +start ::= templates. + +templates ::= templates struct. +templates ::= templates typedef. +templates ::= . + +// typedeffing arrays or pointers is not allowed. + +typedef ::= TYPEDEF type(a) IDENTIFIER(b). { + CreateTypedef((std::string *)b, a, info); +} + +struct ::= STRUCT IDENTIFIER(a) LBRACE struct_fields(b) RBRACE SEMI. +{ + CreateTemplate((std::string *)a, b, info); +} + +struct_fields(rhs) ::= struct_fields(a) struct_field(b). { + // reverse order? + b->next = a; + rhs = b; +} + +struct_fields(rhs) ::= struct_field(a). { + rhs = a; +} + +struct_field(rhs) ::= type(a) IDENTIFIER(b) array_count(c) SEMI. +{ + FieldEntry *e = (FieldEntry *)calloc(sizeof(FieldEntry), 1); + + e->name = (std::string *)b; + e->type = a; + e->count = c; + rhs = e; +} + +struct_field(rhs) ::= opt_volatile TEMPLATE(a) opt_star(star) IDENTIFIER(b) array_count(c) SEMI. { + FieldEntry *e = (FieldEntry *)calloc(sizeof(FieldEntry), 1); + + e->name = (std::string *)b; + e->type = star ? kStructPtr : kStruct; + e->tmpl = (Template)a; + e->count = c; + + rhs = e; +} + +%type array_count { int } +array_count(rhs) ::= . { rhs = 0; } +array_count(rhs) ::= LBRACKET INTEGER(a) RBRACKET. { + int i = (int)(ptrdiff_t)a; + if (i == 0) { + fprintf(stderr, "Template error: line %u: 0-sized arrays are not allowed.\n", + info->LineNumber); + i = 1; + } + rhs = i; +} + +%type type { int } +type(rhs) ::= opt_volatile typecode(a). { rhs = a; } + +// this is an expected error... +type(rhs) ::= opt_volatile IDENTIFIER(xxx). { + + // ugh, Lemon will blindly replace text within a string. + fprintf(stderr, "Template error: line %u: %s is not a known type.\n", + info->LineNumber, ((std::string *)xxx)->c_str()); + + rhs = 'i'; +} + + +opt_volatile ::= . +opt_volatile ::= VOLATILE. + + +%type typecode { int } +typecode(rhs) ::= SIGNED. { rhs = kSInt32; } +typecode(rhs) ::= UNSIGNED. {rhs = kUInt32; } + +typecode(rhs) ::= opt_signed CHAR. { rhs = kSInt8; } +typecode(rhs) ::= UNSIGNED CHAR. { rhs = kUInt8; } + +typecode(rhs) ::= opt_signed SHORT. { rhs = kSInt16; } +typecode(rhs) ::= UNSIGNED SHORT. { rhs = kUInt16; } + +typecode(rhs) ::= opt_signed LONG opt_int. { rhs = kSInt32; } +typecode(rhs) ::= UNSIGNED LONG opt_int. { rhs = kUInt32; } + +typecode(rhs) ::= opt_signed LONG LONG. { rhs = kSInt64; } +typecode(rhs) ::= UNSIGNED LONG LONG. { rhs = kUInt64; } + +typecode(rhs) ::= TYPECODE(a). { rhs = (int)(ptrdiff_t)a; } + +/* pointers are not fully supported yet */ +typecode(rhs) ::= VOID STAR. { rhs = kVoidPtr; } + +opt_signed ::= . +opt_signed ::= SIGNED. + +opt_int ::= . +opt_int ::= INT. + +%type opt_star { int } +opt_star(rhs) ::= . { rhs = 0; } +opt_star(rhs) ::= STAR. { rhs = 1; } +