merge in debug template code

This commit is contained in:
Kelvin Sherlock 2015-01-05 20:28:56 -05:00
parent 00c38721a7
commit 2adbe82704
13 changed files with 1185 additions and 133 deletions

View File

@ -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)

View File

@ -1,19 +1,70 @@
#include <cstdint>
#include <ctime>
#include <cstdio>
#include <toolbox/os.h>
#include <macos/errors.h>
#include <cpu/defs.h>
#include <cpu/CpuModule.h>
#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);
}
}

View File

@ -49,6 +49,7 @@
#include "debugger.h"
#include "debugger_internal.h"
#include "template.h"
#include <cpu/defs.h>
#include <cpu/CpuModule.h>
@ -62,14 +63,12 @@
#include <toolbox/loader.h>
#include <toolbox/mm.h>
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<std::string, Debug::Template> TemplateTable;
struct BackTraceInfo {
uint32_t a[8];
uint32_t d[8];
@ -103,48 +104,7 @@ namespace {
std::deque<BackTraceInfo> 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());

View File

@ -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

101
bin/intern.cpp Normal file
View File

@ -0,0 +1,101 @@
#include "intern.h"
#include <unordered_map>
namespace {
std::unordered_multimap<unsigned, std::string *> 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());
}
}

14
bin/intern.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __intern_h__
#define __intern_h__
#include <string>
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

View File

@ -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<std::string> 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);

View File

@ -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)
{

View File

@ -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);

270
bin/template.cpp Normal file
View File

@ -0,0 +1,270 @@
#include "template.h"
#include "debugger.h"
#include "debugger_internal.h"
#include "loader.h" // Flags.
#include <toolbox/toolbox.h>
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);
}
}
}

88
bin/template.h Normal file
View File

@ -0,0 +1,88 @@
#ifndef __debug_template_h__
#define __debug_template_h__
#include <unordered_map>
#include <string>
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<std::string, Template> *templates;
std::unordered_map<std::string, unsigned> *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<std::string, Template> &);
void ApplyTemplate(uint32_t address, FieldEntry *e, unsigned indent = 0);
}
#endif

254
bin/template_loader.rl Normal file
View File

@ -0,0 +1,254 @@
#include <string>
#include <unordered_map>
#include <algorithm>
#include <numeric>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#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<std::string, Template> &Templates)
{
%% write data;
void *parser;
int fd;
struct stat st;
char *buffer;
std::unordered_map<std::string, unsigned> 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;
}
}

127
bin/template_parser.lemon Normal file
View File

@ -0,0 +1,127 @@
%token_prefix tk
%name TemplateParse
%extra_argument { Debug::TemplateParseInfo *info }
%include {
#include <string>
#include <stdlib.h>
#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';
}