1
0
mirror of https://github.com/cc65/cc65.git synced 2025-01-12 17:30:50 +00:00

First working version with complete API for line information.

git-svn-id: svn://svn.cc65.org/cc65/trunk@4784 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
uz 2010-08-05 21:00:36 +00:00
parent 12e7cc010d
commit 4b1c5e4157
4 changed files with 409 additions and 127 deletions

View File

@ -38,6 +38,7 @@
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <assert.h>
#include <errno.h>
@ -81,20 +82,12 @@ static const Collection EmptyCollection = COLLECTION_INITIALIZER;
/* ### Parseerror */
enum {
CC65_WARNING,
CC65_ERROR,
};
/* Data structure containing information from the debug info file. A pointer
* to this structure is passed as handle to callers from the outside.
*/
typedef struct DbgInfo DbgInfo;
struct DbgInfo {
Collection FileInfo; /* Collection with file infos */
Collection LineInfo; /* Collection with line infos */
Collection FileInfos; /* Collection with file infos */
};
/* Input tokens */
@ -119,6 +112,9 @@ enum Token {
TOK_FILE, /* FILE keyword */
TOK_LABEL, /* LABEL keyword */
TOK_LINE, /* LINE keyword */
TOK_LONG, /* LONG_keyword */
TOK_MAJOR, /* MAJOR keyword */
TOK_MINOR, /* MINOR keyword */
TOK_MTIME, /* MTIME keyword */
TOK_RANGE, /* RANGE keyword */
TOK_RO, /* RO keyword */
@ -131,15 +127,17 @@ enum Token {
TOK_VALUE, /* VALUE keyword */
TOK_VERSION, /* VERSION keyword */
TOK_ZEROPAGE, /* ZEROPAGE keyword */
TOK_IDENT, /* To catch unknown keywords */
};
/* Data used when parsing the debug info file */
typedef struct InputData InputData;
struct InputData {
const char* FileName; /* Name of input file */
unsigned long Line; /* Current line number */
cc65_line Line; /* Current line number */
unsigned Col; /* Current column number */
unsigned long SLine; /* Line number at start of token */
cc65_line SLine; /* Line number at start of token */
unsigned SCol; /* Column number at start of token */
unsigned Errors; /* Number of errors */
FILE* F; /* Input file */
@ -148,6 +146,9 @@ struct InputData {
unsigned long IVal; /* Integer constant */
StrBuf SVal; /* String constant */
cc65_errorfunc Error; /* Function called in case of errors */
unsigned MajorVersion; /* Major version number */
unsigned MinorVersion; /* Minor version number */
Collection LineInfos; /* Line information */
DbgInfo* Info; /* Pointer to debug info */
};
@ -156,15 +157,18 @@ typedef struct FileInfo FileInfo;
struct FileInfo {
unsigned long Size; /* Size of file */
unsigned long MTime; /* Modification time */
cc65_addr Start; /* Start address of line infos */
cc65_addr End; /* End address of line infos */
Collection LineInfos; /* Line infos for this file */
char FileName[1]; /* Name of file with full path */
};
/* Internally used line info struct */
typedef struct LineInfo LineInfo;
struct LineInfo {
unsigned long Start; /* Start of data range */
unsigned long End; /* End of data range */
unsigned long Line; /* Line number */
cc65_addr Start; /* Start of data range */
cc65_addr End; /* End of data range */
cc65_line Line; /* Line number */
FileInfo* FileInfo; /* Pointer to file info */
char FileName[1]; /* Name of file */
};
@ -200,9 +204,7 @@ static void* xmalloc (size_t Size)
P = malloc (Size);
/* Check for errors */
if (P == 0) {
/* ####### */
}
assert (P != 0);
}
/* Return a pointer to the block */
@ -442,6 +444,30 @@ static void* CollAt (Collection* C, unsigned Index)
static void* CollFirst (Collection* C)
/* Return the first item in a collection */
{
/* We must have at least one entry */
assert (C->Count > 0);
/* Return the element */
return C->Items[0];
}
static void* CollLast (Collection* C)
/* Return the last item in a collection */
{
/* We must have at least one entry */
assert (C->Count > 0);
/* Return the element */
return C->Items[C->Count-1];
}
static void CollDelete (Collection* C, unsigned Index)
/* Remove the item with the given index from the collection. This will not
* free the item itself, just the pointer. All items with higher indices
@ -527,47 +553,6 @@ void CollSort (Collection* C, int (*Compare) (const void*, const void*))
/*****************************************************************************/
/* File info */
/*****************************************************************************/
static FileInfo* NewFileInfo (const StrBuf* FileName)
/* Create a new FileInfo struct and return it */
{
/* Allocate memory */
FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
/* Initialize it */
F->Size = 0;
F->MTime = 0;
memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
/* Return it */
return F;
}
static void FreeFileInfo (FileInfo* F)
/* Free a FileInfo struct */
{
xfree (F);
}
static int CompareFileInfo (const void* L, const void* R)
/* Helper function to sort file infos in a collection */
{
/* Sort by file name */
return strcmp (((const FileInfo*) L)->FileName,
((const FileInfo*) R)->FileName);
}
/*****************************************************************************/
/* Line info */
/*****************************************************************************/
@ -616,6 +601,74 @@ static LineInfo* PreenLineInfo (LineInfo* L, FileInfo* F)
static int CompareLineInfo (const void* L, const void* R)
/* Helper function to sort line infos in a collection */
{
/* Sort by start of range */
if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
return 1;
} else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
return -1;
} else {
return 0;
}
}
/*****************************************************************************/
/* File info */
/*****************************************************************************/
static FileInfo* NewFileInfo (const StrBuf* FileName)
/* Create a new FileInfo struct and return it */
{
/* Allocate memory */
FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
/* Initialize it */
F->Size = 0;
F->MTime = 0;
F->Start = ~(cc65_addr)0;
F->End = 0;
InitCollection (&F->LineInfos);
memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
/* Return it */
return F;
}
static void FreeFileInfo (FileInfo* F)
/* Free a FileInfo struct */
{
unsigned I;
/* Walk through the collection with line infos and delete them */
for (I = 0; I < CollCount (&F->LineInfos); ++I) {
FreeLineInfo (CollAt (&F->LineInfos, I));
}
DoneCollection (&F->LineInfos);
/* Free the file info structure itself */
xfree (F);
}
static int CompareFileInfo (const void* L, const void* R)
/* Helper function to sort file infos in a collection */
{
/* Sort by file name */
return strcmp (((const FileInfo*) L)->FileName,
((const FileInfo*) R)->FileName);
}
/*****************************************************************************/
/* Debug info */
/*****************************************************************************/
@ -629,8 +682,7 @@ static DbgInfo* NewDbgInfo (void)
DbgInfo* Info = xmalloc (sizeof (DbgInfo));
/* Initialize it */
InitCollection (&Info->FileInfo);
InitCollection (&Info->LineInfo);
InitCollection (&Info->FileInfos);
/* Return it */
return Info;
@ -644,16 +696,10 @@ static void FreeDbgInfo (DbgInfo* Info)
unsigned I;
/* Free file info */
for (I = 0; I < CollCount (&Info->FileInfo); ++I) {
FreeFileInfo (CollAt (&Info->FileInfo, I));
for (I = 0; I < CollCount (&Info->FileInfos); ++I) {
FreeFileInfo (CollAt (&Info->FileInfos, I));
}
DoneCollection (&Info->FileInfo);
/* Free line info */
for (I = 0; I < CollCount (&Info->LineInfo); ++I) {
FreeLineInfo (CollAt (&Info->LineInfo, I));
}
DoneCollection (&Info->LineInfo);
DoneCollection (&Info->FileInfos);
/* Free the structure itself */
xfree (Info);
@ -667,7 +713,7 @@ static void FreeDbgInfo (DbgInfo* Info)
static void ParseError (InputData* D, unsigned Type, const char* Msg, ...)
static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
/* Call the user supplied parse error function */
{
va_list ap;
@ -782,6 +828,9 @@ static void NextToken (InputData* D)
{ "file", TOK_FILE },
{ "label", TOK_LABEL },
{ "line", TOK_LINE },
{ "long", TOK_LONG },
{ "major", TOK_MAJOR },
{ "minor", TOK_MINOR },
{ "mtime", TOK_MTIME },
{ "range", TOK_RANGE },
{ "ro", TOK_RO },
@ -826,8 +875,7 @@ static void NextToken (InputData* D)
sizeof (KeywordTable[0]),
(int (*)(const void*, const void*)) strcmp);
if (Entry == 0) {
ParseError (D, CC65_ERROR, "Unknown keyword `%s'", SB_GetConstBuf (&D->SVal));
D->Tok = TOK_INVALID;
D->Tok = TOK_IDENT;
} else {
D->Tok = Entry->Tok;
}
@ -1061,7 +1109,7 @@ static void ParseFile (InputData* D)
}
/* Remember the file info */
CollAppend (&D->Info->FileInfo, F);
CollAppend (&D->Info->FileInfos, F);
/* Done */
return;
@ -1124,7 +1172,7 @@ static void ParseLine (InputData* D)
if (!IntConstFollows (D)) {
goto ErrorExit;
}
L->Start = D->IVal;
L->Start = (cc65_addr) D->IVal;
NextToken (D);
if (!ConsumeMinus (D)) {
goto ErrorExit;
@ -1132,7 +1180,7 @@ static void ParseLine (InputData* D)
if (!IntConstFollows (D)) {
goto ErrorExit;
}
L->Start = D->IVal;
L->End = (cc65_addr) D->IVal;
NextToken (D);
InfoBits |= Range;
break;
@ -1156,7 +1204,7 @@ static void ParseLine (InputData* D)
}
/* Remember the line info */
CollAppend (&D->Info->LineInfo, L);
CollAppend (&D->LineInfos, L);
/* Done */
return;
@ -1195,11 +1243,72 @@ static void ParseSym (InputData* D)
static void ParseVersion (InputData* D)
/* Parse a VERSION line */
{
enum { None = 0x00, Major = 0x01, Minor = 0x02 } InfoBits = None;
/* Skip the VERSION token */
NextToken (D);
/* ### */
SkipLine (D);
/* More stuff follows */
while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
switch (D->Tok) {
case TOK_MAJOR:
NextToken (D);
if (!ConsumeEqual (D)) {
goto ErrorExit;
}
if (!IntConstFollows (D)) {
goto ErrorExit;
}
D->MajorVersion = D->IVal;
NextToken (D);
InfoBits |= Major;
break;
case TOK_MINOR:
NextToken (D);
if (!ConsumeEqual (D)) {
goto ErrorExit;
}
if (!IntConstFollows (D)) {
goto ErrorExit;
}
D->MinorVersion = D->IVal;
NextToken (D);
InfoBits |= Minor;
break;
default:
UnexpectedToken (D);
SkipLine (D);
goto ErrorExit;
}
/* Comma follows before next attribute */
if (D->Tok == TOK_COMMA) {
NextToken (D);
} else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
break;
} else {
UnexpectedToken (D);
goto ErrorExit;
}
}
/* Check for required information */
if ((InfoBits & Major) == None) {
MissingAttribute (D, "major");
goto ErrorExit;
}
if ((InfoBits & Minor) == None) {
MissingAttribute (D, "minor");
goto ErrorExit;
}
ErrorExit:
/* Entry point in case of errors */
return;
}
@ -1214,7 +1323,7 @@ static FileInfo* FindFileInfo (InputData* D, const char* FileName)
/* Find the FileInfo for a given file name */
{
/* Get a pointer to the file info collection */
Collection* FileInfos = &D->Info->FileInfo;
Collection* FileInfos = &D->Info->FileInfos;
/* Do a binary search */
int Lo = 0;
@ -1251,7 +1360,7 @@ static void ProcessFileInfo (InputData* D)
/* Postprocess file infos */
{
/* Get a pointer to the file info collection */
Collection* FileInfos = &D->Info->FileInfo;
Collection* FileInfos = &D->Info->FileInfos;
/* First, sort the file infos, so we can check for duplicates and do
* binary search.
@ -1262,7 +1371,7 @@ static void ProcessFileInfo (InputData* D)
if (CollCount (FileInfos) > 0) {
/* Walk through the file infos and check for duplicates. If we find
* some, remove all but the first, so the file infos are unique after
* some, warn and remove them, so the file infos are unique after
* that step.
*/
FileInfo* F = CollAt (FileInfos, 0);
@ -1294,12 +1403,13 @@ static void ProcessFileInfo (InputData* D)
static void ProcessLineInfo (InputData* D)
/* Postprocess line infos */
{
/* Get a pointer to the line info collection */
Collection* LineInfos = &D->Info->LineInfo;
/* Get pointers to the collections */
Collection* LineInfos = &D->LineInfos;
Collection* FileInfos = &D->Info->FileInfos;
/* Walk over the line infos and replace the name by a pointer to the
* corresponding file info struct. The LineInfo structs will get shrinked
* in this process.
* in this process. Add the line info to each file where it is defined.
*/
unsigned I = 0;
while (I < CollCount (LineInfos)) {
@ -1326,11 +1436,83 @@ static void ProcessLineInfo (InputData* D)
/* Shrink the line info struct effectively removing the file name
* but set the pointer to the file info now.
*/
CollReplace (LineInfos, PreenLineInfo (L, F), I);
L = PreenLineInfo (L, F);
CollReplace (LineInfos, L, I);
/* Add this line info to the file where it is defined */
CollAppend (&F->LineInfos, L);
/* Next one */
++I;
}
/* Walk over all files and sort the line infos for each file by ascending
* start address of the range, so we can do a binary search later.
*/
for (I = 0; I < CollCount (FileInfos); ++I) {
/* Get a pointer to this file info */
FileInfo* F = CollAt (FileInfos, I);
/* Sort the line infos for this file */
CollSort (&F->LineInfos, CompareLineInfo);
/* If there are line info entries, place the first and last address
* of into the FileInfo struct itself, so we can rule out a FileInfo
* quickly when mapping an address to a line info.
*/
if (CollCount (&F->LineInfos) > 0) {
F->Start = ((const LineInfo*) CollFirst (&F->LineInfos))->Start;
F->End = ((const LineInfo*) CollLast (&F->LineInfos))->End;
}
}
}
static LineInfo* FindLineInfo (FileInfo* F, cc65_addr Addr)
/* Find the LineInfo for a given address */
{
Collection* LineInfos;
int Hi;
int Lo;
/* Each file info contains the first and last address for which line
* info is available, so we can rule out non matching ones quickly.
*/
if (Addr < F->Start || Addr > F->End) {
return 0;
}
/* Get a pointer to the line info collection for this file */
LineInfos = &F->LineInfos;
/* Do a binary search */
Lo = 0;
Hi = (int) CollCount (LineInfos) - 1;
while (Lo <= Hi) {
/* Mid of range */
int Cur = (Lo + Hi) / 2;
/* Get item */
LineInfo* CurItem = CollAt (LineInfos, Cur);
/* Found? */
if (Addr < CurItem->Start) {
Hi = Cur - 1;
} else if (Addr > CurItem->End) {
Lo = Cur + 1;
} else {
/* Found! */
return CurItem;
}
}
/* Not found */
return 0;
}
@ -1363,6 +1545,9 @@ cc65_dbginfo cc65_read_dbginfo (const char* filename, cc65_errorfunc errorfunc)
0, /* Integer constant */
STRBUF_INITIALIZER, /* String constant */
errorfunc, /* Function called in case of errors */
0, /* Major version number */
0, /* Minor version number */
COLLECTION_INITIALIZER, /* Line information */
0, /* Pointer to debug info */
};
@ -1432,7 +1617,12 @@ cc65_dbginfo cc65_read_dbginfo (const char* filename, cc65_errorfunc errorfunc)
* return NULL
*/
if (D.Errors > 0) {
/* Free the allocated debug info */
/* Free allocated stuff */
unsigned I;
for (I = 0; I < CollCount (&D.LineInfos); ++I) {
FreeLineInfo (CollAt (&D.LineInfos, I));
}
DoneCollection (&D.LineInfos);
FreeDbgInfo (D.Info);
return 0;
}
@ -1441,39 +1631,90 @@ cc65_dbginfo cc65_read_dbginfo (const char* filename, cc65_errorfunc errorfunc)
ProcessFileInfo (&D);
ProcessLineInfo (&D);
/* Free the collection that contained the line info */
DoneCollection (&D.LineInfos);
/* Return the debug info struct that was created */
return D.Info;
}
cc65_lineinfo* cc65_get_lineinfo (cc65_dbginfo handle, unsigned long addr)
/* Return line information for the given address */
void cc65_free_dbginfo (cc65_dbginfo Handle)
/* Free debug information read from a file */
{
/* The passed handle is actually a pointer to a DbgInfo struct */
DbgInfo* Info = handle;
assert (Info != 0);
if (Handle) {
FreeDbgInfo (Handle);
}
}
void cc65_free_lineinfo (cc65_dbginfo handle, cc65_lineinfo* info)
cc65_lineinfo* cc65_get_lineinfo (cc65_dbginfo Handle, unsigned long Addr)
/* Return line information for the given address. The function returns 0
* if no line information was found.
*/
{
unsigned I;
Collection* FileInfos;
cc65_lineinfo* D = 0;
/* We will place a list of line infos in a collection */
Collection LineInfos = COLLECTION_INITIALIZER;
/* Check the parameter */
assert (Handle != 0);
/* Walk over all files and search for matching line infos */
FileInfos = &((DbgInfo*) Handle)->FileInfos;
for (I = 0; I < CollCount (FileInfos); ++I) {
/* Check if the file contains line info for this address */
LineInfo* L = FindLineInfo (CollAt (FileInfos, I), Addr);
if (L != 0) {
CollAppend (&LineInfos, L);
}
}
/* Do we have line infos? */
if (CollCount (&LineInfos) > 0) {
/* Prepare the struct we will return to the caller */
D = xmalloc (sizeof (*D) +
(CollCount (&LineInfos) - 1) * sizeof (D->data[0]));
D->count = CollCount (&LineInfos);
for (I = 0; I < D->count; ++I) {
/* Pointer to this info */
LineInfo* L = CollAt (&LineInfos, I);
/* Copy data */
D->data[I].name = L->FileInfo->FileName;
D->data[I].size = L->FileInfo->Size;
D->data[I].mtime = L->FileInfo->MTime;
D->data[I].line = L->Line;
D->data[I].start = L->Start;
D->data[I].end = L->End;
}
}
/* Free the line info collection */
DoneCollection (&LineInfos);
/* Return the struct we've created */
return D;
}
void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
/* Free line info returned by cc65_get_lineinfo() */
{
/* The passed handle is actually a pointer to a DbgInfo struct */
DbgInfo* Info = handle;
assert (Info != 0);
/* Just for completeness, check the handle */
assert (Handle != 0);
/* Just free the memory */
xfree (Info);
}

View File

@ -39,22 +39,35 @@
/*****************************************************************************/
/* Data */
/* Data */
/*****************************************************************************/
/* Data types used for addresses and line numbers. Change to "unsigned long"
* if you ever want to run the code on a 16-bit machine.
*/
typedef unsigned cc65_line; /* Used to store line numbers */
typedef unsigned cc65_addr; /* Use to store (65xx) addresses */
/* Pointer to an opaque data structure containing information from the debug
* info file. Actually a handle to the data in the file.
*/
typedef void* cc65_dbginfo;
/* ### Parseerror */
typedef enum cc65_error_severity cc65_error_severity;
enum cc65_error_severity {
CC65_WARNING,
CC65_ERROR,
};
/* Warnings/errors in cc65_read_dbginfo are passed via this struct */
typedef struct cc65_parseerror cc65_parseerror;
struct cc65_parseerror {
unsigned type; /* 0 = warning, 1 = error */
cc65_error_severity type; /* Type of error */
const char* name; /* Name of input file */
unsigned long line; /* Error line */
cc65_line line; /* Error line */
unsigned column; /* Error column */
char errormsg[1]; /* Error message */
};
@ -62,21 +75,17 @@ struct cc65_parseerror {
/* Function that is called in case of parse errors */
typedef void (*cc65_errorfunc) (const struct cc65_parseerror*);
/* File information */
typedef struct cc65_fileinfo cc65_fileinfo;
struct cc65_fileinfo {
char* name; /* Name of file with full path */
unsigned long size; /* Size of file */
unsigned long mtime; /* Modification time */
};
/* Line information */
typedef struct cc65_lineinfo cc65_lineinfo;
struct cc65_lineinfo {
unsigned count; /* Count of data sets that follow */
struct {
cc65_fileinfo* fileinfo; /* File information including name */
unsigned long line; /* Line number */
const char* name; /* Name of the file */
unsigned long size; /* Size of file */
unsigned long mtime; /* Modification time */
cc65_line line; /* Line number */
cc65_addr start; /* Start address for this line */
cc65_addr end; /* End address for this line */
} data[1];
};
@ -96,8 +105,13 @@ cc65_dbginfo cc65_read_dbginfo (const char* filename, cc65_errorfunc errorfunc);
* read successfully, NULL is returned.
*/
void cc65_free_dbginfo (cc65_dbginfo Handle);
/* Free debug information read from a file */
cc65_lineinfo* cc65_get_lineinfo (cc65_dbginfo handle, unsigned long addr);
/* Return line information for the given address */
/* Return line information for the given address. The function returns NULL
* if no line information was found.
*/
void cc65_free_lineinfo (cc65_dbginfo handle, cc65_lineinfo* info);
/* Free line info returned by cc65_get_lineinfo() */

View File

@ -46,7 +46,7 @@ static void ErrorFunc (const struct cc65_parseerror* E)
"%s:%s(%lu): %s\n",
E->type? "Error" : "Warning",
E->name,
E->line,
(unsigned long) E->line,
E->errormsg);
}
@ -63,8 +63,9 @@ static void Usage (void)
int main (int argc, char** argv)
{
const char* Input;
cc65_dbginfo Handle;
const char* Input;
cc65_dbginfo Info;
unsigned long Addr;
/* Input file is argument */
@ -73,11 +74,37 @@ int main (int argc, char** argv)
}
Input = argv[1];
Handle = cc65_read_dbginfo (Input, ErrorFunc);
if (Handle == 0) {
fprintf (stderr, "No handle\n");
/* Read the file */
Info = cc65_read_dbginfo (Input, ErrorFunc);
if (Info == 0) {
fprintf (stderr, "Error reading input file - aborting\n");
return 1;
}
printf ("Input file \"%s\" successfully read\n", Input);
/* Output debug information for all addresses in the complete 6502 address
* space. This is also sort of a benchmark for the search algorithms.
*/
for (Addr = 0; Addr < 0x10000; ++Addr) {
cc65_lineinfo* L = cc65_get_lineinfo (Info, Addr);
if (L) {
unsigned I;
printf ("$%04lX: ", Addr);
for (I = 0; I < L->count; ++I) {
if (I > 0) {
printf (", ");
}
printf ("%s(%lu)", L->data[I].name,
(unsigned long) L->data[I].line);
}
printf ("\n");
cc65_free_lineinfo (Info, L);
}
}
/* Free the debug info */
cc65_free_dbginfo (Info);
return 0;
}

View File

@ -12,9 +12,9 @@ EXE = dbgtest
#
CC = gcc
CFLAGS = -O2 -g -Wall -W
CFLAGS = -g -O2 -Wall -W
EBIND = emxbind
LDFLAGS =
LDFLAGS = -g
# ------------------------------------------------------------------------------
# Object files and libraries to link