Add debug info tables to smxbuilder.

This commit is contained in:
David Anderson 2014-08-23 13:21:55 -07:00
parent c4056aea5d
commit 98ec07a419
4 changed files with 230 additions and 248 deletions

View File

@ -22,7 +22,16 @@
#include <am-utility.h>
#include "smx-builder.h"
class MemoryBuffer : public ke::ISmxBuffer
// Interface for SmxBuilder to blit bytes.
class ISmxBuffer
{
public:
virtual bool write(const void *bytes, size_t len) = 0;
virtual size_t pos() const = 0;
};
// An in-memory buffer for SmxBuilder.
class MemoryBuffer : public ISmxBuffer
{
static const size_t kDefaultSize = 4096;

View File

@ -45,7 +45,7 @@
using namespace sp;
using namespace ke;
static void append_dbginfo(void *fout);
static void append_debug_Tables(SmxBuilder *builder);
typedef cell (*OPCODE_PROC)(Vector<cell> *buffer, char *params, cell opcode);
@ -615,10 +615,173 @@ static int sort_by_addr(const void *a1, const void *a2)
return s1->addr - s2->addr;
}
// Helper for parsing a debug string. Debug strings look like this:
// L:40 10
class DebugString
{
public:
DebugString() : kind_('\0'), str_(nullptr)
{ }
DebugString(char *str)
: kind_(str[0]),
str_(str)
{
assert(str_[1] == ':');
str_ += 2;
}
char kind() const {
return kind_;
}
ucell parse() {
return hex2long(str_, &str_);
}
char *skipspaces() {
str_ = ::skipwhitespace(str_);
return str_;
}
void expect(char c) {
assert(*str_ == c);
str_++;
}
char *skipto(char c) {
str_ = strchr(str_, c);
return str_;
}
char getc() {
return *str_++;
}
private:
char kind_;
char *str_;
};
typedef SmxBlobSection<sp_fdbg_info_t> SmxDebugInfoSection;
typedef SmxListSection<sp_fdbg_line_t> SmxDebugLineSection;
typedef SmxListSection<sp_fdbg_file_t> SmxDebugFileSection;
typedef SmxListSection<sp_file_tag_t> SmxTagSection;
typedef SmxBlobSection<void> SmxDebugSymbolsSection;
static void append_debug_tables(SmxBuilder *builder, StringPool &pool)
{
// We use a separate name table for historical reasons that are no longer
// necessary. In the future we should just alias this to ".names".
Ref<SmxNameTable> names = new SmxNameTable(".dbg.strings");
Ref<SmxDebugInfoSection> info = new SmxDebugInfoSection(".dbg.info");
Ref<SmxDebugLineSection> lines = new SmxDebugLineSection(".dbg.lines");
Ref<SmxDebugFileSection> files = new SmxDebugFileSection(".dbg.files");
Ref<SmxDebugSymbolsSection> symbols = new SmxDebugSymbolsSection(".dbg.symbols");
Ref<SmxTagSection> tags = new SmxTagSection(".tags");
stringlist *dbgstrs = get_dbgstrings();
// State for tracking which file we're on. We replicate the original AMXDBG
// behavior here which excludes duplicate addresses.
ucell prev_file_addr = 0;
const char *prev_file_name = nullptr;
// Add debug data.
for (stringlist *iter = dbgstrs; iter; iter = iter->next) {
if (iter->line[0] == '\0')
continue;
DebugString str(iter->line);
switch (str.kind()) {
case 'F':
{
ucell codeidx = str.parse();
if (codeidx != prev_file_addr) {
if (prev_file_name) {
sp_fdbg_file_t &entry = files->add();
entry.addr = prev_file_addr;
entry.name = names->add(pool, prev_file_name);
}
prev_file_addr = codeidx;
}
prev_file_name = str.skipspaces();
break;
}
case 'L':
{
sp_fdbg_line_t &entry = lines->add();
entry.addr = str.parse();
entry.line = str.parse();
break;
}
case 'S':
{
sp_fdbg_symbol_t sym;
sp_fdbg_arraydim_t dims[sDIMEN_MAX];
sym.addr = str.parse();
sym.tagid = str.parse();
str.skipspaces();
str.expect(':');
char *name = str.skipspaces();
char *nameend = str.skipto(' ');
Atom *atom = pool.add(name, nameend - name);
sym.codestart = str.parse();
sym.codeend = str.parse();
sym.ident = (char)str.parse();
sym.vclass = (char)str.parse();
sym.dimcount = 0;
sym.name = names->add(atom);
info->header().num_syms++;
str.skipspaces();
if (str.getc() == '[') {
info->header().num_arrays++;
for (char *ptr = str.skipspaces(); *ptr != ']'; ptr = str.skipspaces()) {
dims[sym.dimcount].tagid = str.parse();
str.skipspaces();
str.expect(':');
dims[sym.dimcount].size = str.parse();
sym.dimcount++;
}
}
symbols->add(&sym, sizeof(sym));
symbols->add(dims, sizeof(dims[0]) * sym.dimcount);
break;
}
}
}
// Add the last file.
if (prev_file_name) {
sp_fdbg_file_t &entry = files->add();
entry.addr = prev_file_addr;
entry.name = names->add(pool, prev_file_name);
}
// Build the tags table.
for (constvalue *constptr = tagname_tab.next; constptr; constptr = constptr->next) {
assert(strlen(constptr->name)>0);
sp_file_tag_t &tag = tags->add();
tag.tag_id = constptr->value;
tag.name = names->add(pool, constptr->name);
}
info->header().num_files = files->count();
info->header().num_lines = lines->count();
builder->add(files);
builder->add(symbols);
builder->add(lines);
builder->add(names);
builder->add(info);
builder->add(tags);
}
typedef SmxListSection<sp_file_natives_t> SmxNativeSection;
typedef SmxListSection<sp_file_publics_t> SmxPublicSection;
typedef SmxListSection<sp_file_pubvars_t> SmxPubvarSection;
typedef SmxListSection<sp_file_tag_t> SmxTagSection;
typedef SmxBlobSection<sp_file_data_t> SmxDataSection;
typedef SmxBlobSection<sp_file_code_t> SmxCodeSection;
@ -629,7 +792,6 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin)
Ref<SmxNativeSection> natives = new SmxNativeSection(".natives");
Ref<SmxPublicSection> publics = new SmxPublicSection(".publics");
Ref<SmxPubvarSection> pubvars = new SmxPubvarSection(".pubvars");
Ref<SmxTagSection> tags = new SmxTagSection(".tags");
Ref<SmxDataSection> data = new SmxDataSection(".data");
Ref<SmxCodeSection> code = new SmxCodeSection(".code");
Ref<SmxNameTable> names = new SmxNameTable(".names");
@ -656,15 +818,6 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin)
}
}
// Build the tags table.
for (constvalue *constptr = tagname_tab.next; constptr; constptr = constptr->next) {
assert(strlen(constptr->name)>0);
sp_file_tag_t &tag = tags->add();
tag.tag_id = constptr->value;
tag.name = names->add(pool, constptr->name);
}
// Shuffle natives to be in address order.
qsort(nativeList.buffer(), nativeList.length(), sizeof(symbol *), sort_by_addr);
for (size_t i = 0; i < nativeList.length(); i++) {
@ -719,7 +872,8 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin)
builder.add(pubvars);
builder.add(natives);
builder.add(names);
builder.add(tags);
append_debug_tables(&builder, pool);
builder.write(buffer);
}
@ -775,225 +929,3 @@ void assemble(const char *binfname, void *fin)
splat_to_binary(binfname, buffer.bytes(), buffer.size());
}
int pc_writebin(void *handle,void *buffer,int size) {
assert(false);
return 1;
}
static void append_dbginfo(void *fout)
{
AMX_DBG_HDR dbghdr;
AMX_DBG_LINE dbgline;
AMX_DBG_SYMBOL dbgsym;
AMX_DBG_SYMDIM dbgidxtag[sDIMEN_MAX];
int dim,dbgsymdim;
char *str,*prevstr,*name,*prevname;
ucell codeidx,previdx;
constvalue *constptr;
char symname[2*sNAMEMAX+16];
int16_t id1,id2;
ucell address;
stringlist *dbgstrs = get_dbgstrings();
stringlist *iter;
/* header with general information */
memset(&dbghdr, 0, sizeof dbghdr);
dbghdr.size=sizeof dbghdr;
dbghdr.magic=AMX_DBG_MAGIC;
dbghdr.file_version=CUR_FILE_VERSION;
dbghdr.amx_version=MIN_AMX_VERSION;
dbgstrs=dbgstrs->next;
/* first pass: collect the number of items in various tables */
/* file table */
previdx=0;
prevstr=NULL;
prevname=NULL;
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str!=NULL);
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='F') {
codeidx=hex2long(str+2,&name);
if (codeidx!=previdx) {
if (prevstr!=NULL) {
assert(prevname!=NULL);
dbghdr.files++;
dbghdr.size+=sizeof(cell)+strlen(prevname)+1;
} /* if */
previdx=codeidx;
} /* if */
prevstr=str;
prevname=skipwhitespace(name);
} /* if */
} /* for */
if (prevstr!=NULL) {
assert(prevname!=NULL);
dbghdr.files++;
dbghdr.size+=sizeof(cell)+strlen(prevname)+1;
} /* if */
/* line number table */
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str!=NULL);
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='L') {
dbghdr.lines++;
dbghdr.size+=sizeof(AMX_DBG_LINE);
} /* if */
} /* for */
/* symbol table */
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str!=NULL);
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='S') {
dbghdr.symbols++;
name=strchr(str+2,':');
assert(name!=NULL);
dbghdr.size+=sizeof(AMX_DBG_SYMBOL)+strlen(skipwhitespace(name+1));
if ((prevstr=strchr(name,'['))!=NULL)
while ((prevstr=strchr(prevstr+1,':'))!=NULL)
dbghdr.size+=sizeof(AMX_DBG_SYMDIM);
} /* if */
} /* for */
/* tag table */
for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) {
assert(strlen(constptr->name)>0);
dbghdr.tags++;
dbghdr.size+=sizeof(AMX_DBG_TAG)+strlen(constptr->name);
} /* for */
/* automaton table */
for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) {
assert((constptr->index==0 && strlen(constptr->name)==0) || strlen(constptr->name)>0);
dbghdr.automatons++;
dbghdr.size+=sizeof(AMX_DBG_MACHINE)+strlen(constptr->name);
} /* for */
/* state table */
for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) {
assert(strlen(constptr->name)>0);
dbghdr.states++;
dbghdr.size+=sizeof(AMX_DBG_STATE)+strlen(constptr->name);
} /* for */
/* pass 2: generate the tables */
writeerror |= !pc_writebin(fout,&dbghdr,sizeof dbghdr);
/* file table */
previdx=0;
prevstr=NULL;
prevname=NULL;
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='F') {
codeidx=hex2long(str+2,&name);
if (codeidx!=previdx) {
if (prevstr!=NULL) {
assert(prevname!=NULL);
writeerror |= !pc_writebin(fout,&previdx,sizeof previdx);
writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1);
} /* if */
previdx=codeidx;
} /* if */
prevstr=str;
prevname=skipwhitespace(name);
} /* if */
} /* for */
if (prevstr!=NULL) {
assert(prevname!=NULL);
writeerror |= !pc_writebin(fout,&previdx,sizeof previdx);
writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1);
} /* if */
/* line number table */
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str!=NULL);
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='L') {
dbgline.address=hex2long(str+2,&str);
dbgline.line=(int32_t)hex2long(str,NULL);
writeerror |= !pc_writebin(fout,&dbgline,sizeof dbgline);
} /* if */
} /* for */
/* symbol table */
for (iter=dbgstrs; iter!=NULL; iter=iter->next) {
str = iter->line;
assert(str!=NULL);
assert(str[0]!='\0' && str[1]==':');
if (str[0]=='S') {
dbgsym.address=hex2long(str+2,&str);
dbgsym.tag=(int16_t)hex2long(str,&str);
str=skipwhitespace(str);
assert(*str==':');
name=skipwhitespace(str+1);
str=strchr(name,' ');
assert(str!=NULL);
assert((int)(str-name)<sizeof symname);
strlcpy(symname,name,(int)(str-name)+1);
dbgsym.codestart=hex2long(str,&str);
dbgsym.codeend=hex2long(str,&str);
dbgsym.ident=(char)hex2long(str,&str);
dbgsym.vclass=(char)hex2long(str,&str);
dbgsym.dim=0;
str=skipwhitespace(str);
if (*str=='[') {
while (*(str=skipwhitespace(str+1))!=']') {
dbgidxtag[dbgsym.dim].tag=(int16_t)hex2long(str,&str);
str=skipwhitespace(str);
assert(*str==':');
dbgidxtag[dbgsym.dim].size=hex2long(str+1,&str);
dbgsym.dim++;
} /* while */
} /* if */
dbgsymdim = dbgsym.dim;
writeerror |= !pc_writebin(fout,&dbgsym,offsetof(AMX_DBG_SYMBOL, name));
writeerror |= !pc_writebin(fout,symname,strlen(symname)+1);
for (dim=0; dim<dbgsymdim; dim++) {
writeerror |= !pc_writebin(fout,&dbgidxtag[dim],sizeof dbgidxtag[dim]);
} /* for */
} /* if */
} /* for */
/* tag table */
for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) {
assert(strlen(constptr->name)>0);
id1=(int16_t)(constptr->value & TAGMASK);
writeerror |= !pc_writebin(fout,&id1,sizeof id1);
writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1);
} /* for */
/* automaton table */
for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) {
assert((constptr->index==0 && strlen(constptr->name)==0) || strlen(constptr->name)>0);
id1=(int16_t)constptr->index;
address=(ucell)constptr->value;
writeerror |= !pc_writebin(fout,&id1,sizeof id1);
writeerror |= !pc_writebin(fout,&address,sizeof address);
writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1);
} /* for */
/* state table */
for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) {
assert(strlen(constptr->name)>0);
id1=(int16_t)constptr->value;
id2=(int16_t)constptr->index;
address=(ucell)constptr->value;
writeerror |= !pc_writebin(fout,&id1,sizeof id1);
writeerror |= !pc_writebin(fout,&id2,sizeof id2);
writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1);
} /* for */
delete_dbgstringtable();
}

View File

@ -24,16 +24,11 @@
#include <am-refcounting.h>
#include <smx/smx-headers.h>
#include "string-pool.h"
#include "memory-buffer.h"
namespace ke {
class ISmxBuffer
{
public:
virtual bool write(const void *bytes, size_t len) = 0;
virtual size_t pos() const = 0;
};
// An SmxSection is a named blob of data.
class SmxSection : public Refcounted<SmxSection>
{
public:
@ -53,13 +48,19 @@ class SmxSection : public Refcounted<SmxSection>
AString name_;
};
// An SmxBlobSection is a section that has some kind of header structure
// (specified as a template parameter), and then an arbitrary run of bytes
// immediately after.
template <typename T>
class SmxBlobSection : public SmxSection
{
public:
SmxBlobSection(const char *name)
: SmxSection(name)
: SmxSection(name),
extra_(nullptr),
extra_len_(0)
{
memset(&t_, 0, sizeof(t_));
}
T &header() {
@ -72,6 +73,8 @@ class SmxBlobSection : public SmxSection
bool write(ISmxBuffer *buf) KE_OVERRIDE {
if (!buf->write(&t_, sizeof(t_)))
return false;
if (!extra_len_)
return true;
return buf->write(extra_, extra_len_);
}
size_t length() const KE_OVERRIDE {
@ -84,6 +87,32 @@ class SmxBlobSection : public SmxSection
size_t extra_len_;
};
// An SmxBlobSection without headers.
template <>
class SmxBlobSection<void> : public SmxSection
{
public:
SmxBlobSection(const char *name)
: SmxSection(name)
{
}
void add(void *bytes, size_t len) {
buffer_.write(bytes, len);
}
bool write(ISmxBuffer *buf) KE_OVERRIDE {
return buf->write(buffer_.bytes(), buffer_.size());
}
size_t length() const KE_OVERRIDE {
return buffer_.size();
}
private:
MemoryBuffer buffer_;
};
// An SmxListSection is a section that is a simple table of uniformly-sized
// structures. It has no header of its own.
template <typename T>
class SmxListSection : public SmxSection
{
@ -104,18 +133,23 @@ class SmxListSection : public SmxSection
return buf->write(list_.buffer(), list_.length() * sizeof(T));
}
size_t length() const KE_OVERRIDE {
return list_.length() * sizeof(T);
return count() * sizeof(T);
}
size_t count() const {
return list_.length();
}
private:
Vector<T> list_;
};
// A name table is a blob of zero-terminated strings. Strings are entered
// into the table as atoms, so duplicate stings are not emitted.
class SmxNameTable : public SmxSection
{
public:
SmxNameTable(const char *name)
: SmxSection(".names"),
: SmxSection(name),
buffer_size_(0)
{
name_table_.init(64);

View File

@ -19,14 +19,19 @@
#ifndef _include_jitcraft_string_pool_h_
#define _include_jitcraft_string_pool_h_
// string-pool is a collection of helpers to atomize/internalize strings. The
// StringPool class provides Atom objects which can be used for efficiently
// handling string sets or dictionaries.
#include <am-hashtable.h>
#include <am-string.h>
#include <string.h>
namespace ke {
// Wrapper around AString, since we might want to remove charfs() in a GC-safe
// implementation.
// An Atom is a string that has a unique instance. That is, any Atom("a") is
// guaranteed to be pointer-equivalent to another Atom("a"), as long as they
// originated from the same StringPool.
class Atom
{
friend class StringPool;
@ -49,6 +54,7 @@ class Atom
AString str_;
};
// Helper class to use as a key for hash table lookups.
class CharsAndLength
{
public:
@ -76,6 +82,7 @@ class CharsAndLength
size_t length_;
};
// Atom dictionary.
class StringPool
{
public: