2fbf67cb29
* NEW: Update FSF address in every source file. git-svn-id: svn://localhost/gambas/trunk@3870 867c0c6c-44f3-4631-809d-bfa615b0a4ec
1022 lines
19 KiB
C
1022 lines
19 KiB
C
/***************************************************************************
|
|
|
|
debug.c
|
|
|
|
(c) 2000-2011 Benoît Minisini <gambas@users.sourceforge.net>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA.
|
|
|
|
***************************************************************************/
|
|
|
|
#define __DEBUG_C
|
|
|
|
// Do not include gbx_debug.h
|
|
#define __GBX_DEBUG_H
|
|
|
|
#include "gb_common.h"
|
|
#include "gambas.h"
|
|
|
|
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "gb_error.h"
|
|
#include "gbx_type.h"
|
|
#include "gb_limit.h"
|
|
#include "gbx_stack.h"
|
|
#include "gbx_class.h"
|
|
#include "gbx_exec.h"
|
|
#include "gbx_local.h"
|
|
#include "gbx_object.h"
|
|
|
|
#include "gbx_eval.h"
|
|
|
|
#include "print.h"
|
|
|
|
#include "debug.h"
|
|
|
|
//#define DEBUG_ME
|
|
|
|
DEBUG_INFO DEBUG_info = { 0 };
|
|
GB_DEBUG_INTERFACE *DEBUG_interface;
|
|
char DEBUG_buffer[DEBUG_BUFFER_MAX + 1];
|
|
|
|
static DEBUG_BREAK *Breakpoint;
|
|
static bool Error;
|
|
|
|
static EVAL_INTERFACE EVAL;
|
|
|
|
static int _fdr;
|
|
static int _fdw;
|
|
static FILE *_out;
|
|
static FILE *_in;
|
|
static bool _fifo;
|
|
|
|
#define EXEC_current (*(STACK_CONTEXT *)GB_DEBUG.GetExec())
|
|
|
|
#ifdef DEBUG_ME
|
|
#define WARNING(_msg, ...) fprintf(stderr, "W\t" _msg "\n", ##__VA_ARGS__)
|
|
#define INFO(_msg, ...) fprintf(stderr, "I\t" _msg "\n", ##__VA_ARGS__)
|
|
#else
|
|
#define WARNING(_msg, ...) fprintf(_out, "W\t" _msg "\n", ##__VA_ARGS__)
|
|
#define INFO(_msg, ...) fprintf(_out, "I\t" _msg "\n", ##__VA_ARGS__)
|
|
#endif
|
|
|
|
static void init_eval_interface()
|
|
{
|
|
static bool init = FALSE;
|
|
|
|
if (!init)
|
|
{
|
|
GB.GetInterface("gb.eval", EVAL_INTERFACE_VERSION, &EVAL);
|
|
init = TRUE;
|
|
}
|
|
}
|
|
|
|
void DEBUG_break_on_next_line(void)
|
|
{
|
|
DEBUG_info.stop = TRUE;
|
|
DEBUG_info.leave = FALSE;
|
|
DEBUG_info.fp = NULL;
|
|
DEBUG_info.bp = NULL;
|
|
}
|
|
|
|
|
|
static void signal_user(int sig)
|
|
{
|
|
signal(SIGUSR1, signal_user);
|
|
|
|
#ifdef DEBUG_ME
|
|
fprintf(stderr, "Got SIGUSR1\n");
|
|
#endif
|
|
|
|
/*CAPP_got_signal();*/
|
|
DEBUG_break_on_next_line();
|
|
}
|
|
|
|
|
|
static bool calc_line_from_position(CLASS *class, FUNCTION *func, PCODE *addr, ushort *line)
|
|
{
|
|
int i;
|
|
ushort pos = addr - func->code;
|
|
ushort *post;
|
|
|
|
if (func->debug)
|
|
{
|
|
post = func->debug->pos;
|
|
for (i = 0; i < (func->debug->nline - 1); i++)
|
|
{
|
|
if (pos >= post[i] && pos < post[i + 1])
|
|
{
|
|
*line = i + func->debug->line;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*printf("pos = %d addr=%p func->code=%p\n", pos, addr, func->code);*/
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static bool calc_position_from_line(CLASS *class, ushort line, FUNCTION **function, PCODE **addr)
|
|
{
|
|
int i;
|
|
ushort pos, pos_after;
|
|
FUNCTION *func = NULL;
|
|
FUNC_DEBUG *debug = NULL;
|
|
|
|
for (i = 0; i < class->load->n_func; i++)
|
|
{
|
|
func = &class->load->func[i];
|
|
debug = func->debug;
|
|
if (debug && line >= debug->line && line < (debug->line + debug->nline))
|
|
break;
|
|
}
|
|
|
|
if (i >= class->load->n_func)
|
|
return TRUE;
|
|
|
|
line -= debug->line;
|
|
|
|
for(;;)
|
|
{
|
|
pos = debug->pos[line];
|
|
pos_after = debug->pos[line + 1];
|
|
if (pos != pos_after)
|
|
break;
|
|
|
|
line++;
|
|
if (line >= debug->nline)
|
|
return TRUE;
|
|
}
|
|
|
|
*function = func;
|
|
*addr = &func->code[pos];
|
|
|
|
/*printf("%s.%d -> %04X\n", class->name, line + debug->line, **addr);*/
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DEBUG_INFO *DEBUG_init(GB_DEBUG_INTERFACE *debug, bool fifo, const char *fifo_name)
|
|
{
|
|
char path[DEBUG_FIFO_PATH_MAX];
|
|
char name[16];
|
|
//int i;
|
|
|
|
//if (!EXEC_debug)
|
|
// return;
|
|
|
|
DEBUG_interface = debug;
|
|
_fifo = fifo;
|
|
|
|
if (_fifo)
|
|
{
|
|
if (!fifo_name)
|
|
{
|
|
sprintf(name, "%d", getppid());
|
|
fifo_name = name;
|
|
}
|
|
|
|
snprintf(path, sizeof(path), "/tmp/gambas.%d/%s.out", getuid(), fifo_name);
|
|
|
|
/*for (i = 0; i < 20; i++)
|
|
{
|
|
_fdr = open(path, O_RDONLY | O_NONBLOCK);
|
|
if (_fdr >= 0)
|
|
break;
|
|
usleep(10000);
|
|
}
|
|
if (_fdr < 0)
|
|
return NULL;*/
|
|
|
|
_fdr = open(path, O_RDONLY);
|
|
if (_fdr < 0)
|
|
return NULL;
|
|
fcntl(_fdr, F_SETFD, FD_CLOEXEC);
|
|
|
|
snprintf(path, sizeof(path), "/tmp/gambas.%d/%s.in", getuid(), fifo_name);
|
|
|
|
_fdw = open(path, O_WRONLY);
|
|
if (_fdw < 0)
|
|
return NULL;
|
|
|
|
fcntl(_fdw, F_SETFD, FD_CLOEXEC);
|
|
|
|
_in = fdopen(_fdr, "r");
|
|
_out = fdopen(_fdw, "w");
|
|
|
|
if (!_in || !_out)
|
|
return NULL;
|
|
//ERROR_panic("Cannot open fifos");
|
|
|
|
setlinebuf(_in);
|
|
//setvbuf(_in, NULL, _IONBF, 0);
|
|
setlinebuf(_out);
|
|
//setvbuf(_out, NULL, _IONBF, 0);
|
|
}
|
|
else
|
|
{
|
|
_in = stdin;
|
|
_out = stdout;
|
|
}
|
|
|
|
//ARRAY_create(&Breakpoint);
|
|
GB.NewArray(&Breakpoint, sizeof(DEBUG_BREAK), 16);
|
|
signal(SIGUSR1, signal_user);
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
setlinebuf(_out);
|
|
|
|
return &DEBUG_info;
|
|
}
|
|
|
|
void DEBUG_exit(void)
|
|
{
|
|
GB.FreeArray(&Breakpoint);
|
|
|
|
/* Don't do it, it blocks!
|
|
|
|
if (EXEC_fifo)
|
|
{
|
|
fclose(_in);
|
|
fclose(_out);
|
|
}
|
|
|
|
*/
|
|
}
|
|
|
|
|
|
static int find_free_breakpoint(void)
|
|
{
|
|
int i;
|
|
char used[MAX_BREAKPOINT];
|
|
|
|
memset(used, FALSE, MAX_BREAKPOINT);
|
|
|
|
for (i = 0; i < ARRAY_count(Breakpoint); i++)
|
|
used[Breakpoint[i].id - 1] = TRUE;
|
|
|
|
for (i = 0; i < MAX_BREAKPOINT; i++)
|
|
if (!used[i])
|
|
return (i + 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool init_breakpoint(DEBUG_BREAK *brk)
|
|
{
|
|
PCODE *addr = NULL;
|
|
FUNCTION *func;
|
|
|
|
//fprintf(stderr, "init_breakpoint: id = %d\n", brk->id);
|
|
|
|
if (brk->addr || brk->class->state == CS_NULL)
|
|
{
|
|
WARNING("Breakpoint is pending");
|
|
return TRUE;
|
|
}
|
|
|
|
if (CLASS_is_native(brk->class) || !brk->class->debug)
|
|
{
|
|
WARNING("Cannot set breakpoint: no debugging information");
|
|
return TRUE;
|
|
}
|
|
|
|
if (calc_position_from_line(brk->class, brk->line, &func, &addr))
|
|
{
|
|
WARNING("Cannot set breakpoint: cannot calculate position");
|
|
//fprintf(_out, "Cannot calc position from line number\n");
|
|
return TRUE;
|
|
}
|
|
|
|
if (!PCODE_is_breakpoint(*addr))
|
|
{
|
|
//WARNING("Cannot set breakpoint: Not a line beginning: %04d: %04X", addr - func->code, *addr);
|
|
WARNING("Cannot set breakpoint: Not a line beginning");
|
|
//fprintf(_out, "Not a line beginning ?\n");
|
|
return TRUE;
|
|
}
|
|
|
|
if (*addr & 0xFF)
|
|
{
|
|
WARNING("Breakpoint already set");
|
|
//fprintf(_out, "Breakpoint already set\n");
|
|
return FALSE;
|
|
}
|
|
|
|
brk->addr = addr;
|
|
*addr = PCODE_BREAKPOINT(brk->id);
|
|
|
|
//fprintf(stderr, "init_breakpoint: OK\n");
|
|
|
|
#ifdef DEBUG_ME
|
|
fprintf(stderr, "init_breakpoint: %s.%d\n", brk->class->name, brk->line);
|
|
#endif
|
|
|
|
INFO("Breakpoint set: %s.%d", brk->class->name, brk->line);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static bool set_breakpoint(CLASS *class, ushort line)
|
|
{
|
|
DEBUG_BREAK *brk;
|
|
int id;
|
|
|
|
if (GB.Count(Breakpoint) >= MAX_BREAKPOINT)
|
|
{
|
|
WARNING("Too many breakpoints");
|
|
return TRUE;
|
|
}
|
|
|
|
id = find_free_breakpoint();
|
|
if (id == 0)
|
|
{
|
|
WARNING("Cannot create breakpoint");
|
|
return TRUE;
|
|
}
|
|
|
|
brk = (DEBUG_BREAK *)GB.Add(&Breakpoint);
|
|
|
|
brk->id = id;
|
|
brk->addr = NULL;
|
|
brk->class = class;
|
|
brk->line = line;
|
|
|
|
#ifdef DEBUG_ME
|
|
fprintf(stderr, "set_breakpoint: %s.%d\n", class->name, line);
|
|
#endif
|
|
|
|
init_breakpoint(brk);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static bool unset_breakpoint(CLASS *class, ushort line)
|
|
{
|
|
int i;
|
|
DEBUG_BREAK *brk;
|
|
|
|
for (i = 0; i < GB.Count(Breakpoint); i++)
|
|
{
|
|
brk = &Breakpoint[i];
|
|
if (brk->class == class && brk->line == line)
|
|
{
|
|
if (brk->addr)
|
|
*(brk->addr) = PCODE_BREAKPOINT(0);
|
|
GB.Remove(&Breakpoint, i, 1);
|
|
|
|
#ifdef DEBUG_ME
|
|
fprintf(stderr, "unset_breakpoint: %s.%d\n", class->name, line);
|
|
#endif
|
|
|
|
INFO("Breakpoint removed");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
WARNING("Unknown breakpoint");
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void DEBUG_init_breakpoints(CLASS *class)
|
|
{
|
|
int i;
|
|
DEBUG_BREAK *brk;
|
|
|
|
#ifdef DEBUG_ME
|
|
fprintf(stderr, "DEBUG_init_breakpoints: %p %s\n", class, class->name);
|
|
#endif
|
|
|
|
for (i = 0; i < GB.Count(Breakpoint); i++)
|
|
{
|
|
brk = &Breakpoint[i];
|
|
if (brk->class == class)
|
|
{
|
|
//fprintf(stderr, "DEBUG_init_breakpoints: %s\n", class->name);
|
|
init_breakpoint(brk);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void print_local()
|
|
{
|
|
int i;
|
|
FUNCTION *fp;
|
|
LOCAL_SYMBOL *lp;
|
|
|
|
fp = DEBUG_info.fp;
|
|
|
|
if (!fp || !fp->debug)
|
|
return;
|
|
|
|
for (i = 0; i < fp->debug->n_local; i++)
|
|
{
|
|
lp = &fp->debug->local[i];
|
|
fprintf(_out, "%.*s ", lp->sym.len, lp->sym.name);
|
|
}
|
|
|
|
/*else
|
|
{
|
|
cmd++;
|
|
|
|
for (i = 0; i < FP->debug->n_local; i++)
|
|
{
|
|
lp = &FP->debug->local[i];
|
|
if (lp->sym.len == strlen(cmd) && strncasecmp(lp->sym.name, cmd, lp->sym.len) == 0)
|
|
{
|
|
fprintf(_out, "=");
|
|
//fprintf(_out, "TUT\n");
|
|
PRINT_value(_out, &BP[lp->value], TRUE);
|
|
nl = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
|
|
|
|
static void print_symbol(GLOBAL_SYMBOL *gp, bool is_static, bool is_public)
|
|
{
|
|
if (CTYPE_get_kind(gp->ctype) != TK_VARIABLE && CTYPE_get_kind(gp->ctype) != TK_CONST)
|
|
return;
|
|
|
|
if (CTYPE_is_static(gp->ctype) && !is_static)
|
|
return;
|
|
|
|
if (!CTYPE_is_static(gp->ctype) && is_static)
|
|
return;
|
|
|
|
if (CTYPE_is_public(gp->ctype) && !is_public)
|
|
return;
|
|
|
|
if (!CTYPE_is_public(gp->ctype) && is_public)
|
|
return;
|
|
|
|
fprintf(_out, "%.*s ", gp->sym.len, gp->sym.name);
|
|
}
|
|
|
|
|
|
static void print_object()
|
|
{
|
|
int i;
|
|
GLOBAL_SYMBOL *gp;
|
|
CLASS *cp = DEBUG_info.cp;
|
|
void *op = DEBUG_info.op;
|
|
|
|
if (!cp || !cp->load)
|
|
return;
|
|
|
|
fprintf(_out, "S: ");
|
|
|
|
for (i = 0; i < cp->load->n_global; i++)
|
|
{
|
|
gp = &cp->load->global[i];
|
|
print_symbol(gp, TRUE, TRUE);
|
|
}
|
|
|
|
fprintf(_out, "s: ");
|
|
|
|
for (i = 0; i < cp->load->n_global; i++)
|
|
{
|
|
gp = &cp->load->global[i];
|
|
print_symbol(gp, TRUE, FALSE);
|
|
}
|
|
|
|
if (op)
|
|
{
|
|
fprintf(_out, "D: ");
|
|
|
|
for (i = 0; i < cp->load->n_global; i++)
|
|
{
|
|
gp = &cp->load->global[i];
|
|
print_symbol(gp, FALSE, TRUE);
|
|
}
|
|
|
|
fprintf(_out, "d: ");
|
|
|
|
for (i = 0; i < cp->load->n_global; i++)
|
|
{
|
|
gp = &cp->load->global[i];
|
|
print_symbol(gp, FALSE, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void command_quit(const char *cmd)
|
|
{
|
|
exit(1);
|
|
}
|
|
|
|
static void command_go(const char *cmd)
|
|
{
|
|
GB.Signal(GB_SIGNAL_DEBUG_CONTINUE, 0);
|
|
|
|
DEBUG_info.stop = FALSE;
|
|
DEBUG_info.leave = FALSE;
|
|
DEBUG_info.fp = NULL;
|
|
DEBUG_info.bp = NULL;
|
|
}
|
|
|
|
static void command_step(const char *cmd)
|
|
{
|
|
GB.Signal(GB_SIGNAL_DEBUG_FORWARD, 0);
|
|
DEBUG_break_on_next_line();
|
|
}
|
|
|
|
static void command_next(const char *cmd)
|
|
{
|
|
GB.Signal(GB_SIGNAL_DEBUG_FORWARD, 0);
|
|
DEBUG_info.stop = TRUE;
|
|
DEBUG_info.leave = FALSE;
|
|
DEBUG_info.fp = FP;
|
|
DEBUG_info.bp = BP;
|
|
}
|
|
|
|
static void command_from(const char *cmd)
|
|
{
|
|
STACK_CONTEXT *sc = GB_DEBUG.GetStack(0); //STACK_get_current();
|
|
|
|
if (sc && sc->pc)
|
|
{
|
|
GB.Signal(GB_SIGNAL_DEBUG_FORWARD, 0);
|
|
DEBUG_info.stop = TRUE;
|
|
DEBUG_info.leave = FALSE;
|
|
DEBUG_info.fp = sc->fp;
|
|
DEBUG_info.bp = sc->bp;
|
|
}
|
|
else
|
|
{
|
|
GB.Signal(GB_SIGNAL_DEBUG_FORWARD, 0);
|
|
DEBUG_info.stop = TRUE;
|
|
DEBUG_info.leave = TRUE;
|
|
DEBUG_info.fp = FP;
|
|
DEBUG_info.bp = BP;
|
|
}
|
|
}
|
|
|
|
|
|
static void command_set_breakpoint(const char *cmd)
|
|
{
|
|
char class_name[64];
|
|
ushort line;
|
|
//CLASS *class;
|
|
|
|
if (sscanf(cmd, "+%64[^.].%hu", class_name, &line) != 2)
|
|
WARNING("Cannot set breakpoint: syntax error");
|
|
else
|
|
{
|
|
//class = (CLASS *)GB.FindClassLocal(class_name);
|
|
//CLASS_load_without_init(class);
|
|
//fprintf(stderr, "command_set_breakpoint: %s %s\n", class->name, class->component ? class->component->name : "?");
|
|
set_breakpoint((CLASS *)GB_DEBUG.FindClass(class_name), line);
|
|
}
|
|
}
|
|
|
|
|
|
static void command_unset_breakpoint(const char *cmd)
|
|
{
|
|
char class_name[64];
|
|
ushort line;
|
|
|
|
if (sscanf(cmd, "-%64[^.].%hu", class_name, &line) != 2)
|
|
WARNING("Cannot remove breakpoint: Syntax error");
|
|
else
|
|
{
|
|
//class = CLASS_find(class_name);
|
|
//CLASS_load_without_init(class);
|
|
unset_breakpoint((CLASS *)GB_DEBUG.FindClass(class_name), line);
|
|
}
|
|
}
|
|
|
|
|
|
void DEBUG_backtrace(FILE *out)
|
|
{
|
|
int i, n;
|
|
STACK_CONTEXT *context;
|
|
ushort line;
|
|
|
|
if (CP)
|
|
fprintf(out, "%s", DEBUG_get_current_position());
|
|
else
|
|
fprintf(out, "?");
|
|
|
|
//for (i = 0; i < (STACK_frame_count - 1); i++)
|
|
n = 0;
|
|
for (i = 0;; i++)
|
|
{
|
|
context = GB_DEBUG.GetStack(i); //&STACK_frame[i];
|
|
if (!context)
|
|
break;
|
|
|
|
if (context->pc)
|
|
{
|
|
line = 0;
|
|
if (calc_line_from_position(context->cp, context->fp, context->pc, &line))
|
|
n += fprintf(out, " %s.?.?", context->cp->name);
|
|
else
|
|
n += fprintf(out, " %s.%s.%d", context->cp->name, context->fp->debug->name, line);
|
|
}
|
|
else if (context->cp)
|
|
n += fprintf(out, " ?");
|
|
|
|
if (n >= (DEBUG_OUTPUT_MAX_SIZE / 2))
|
|
{
|
|
fprintf(out, " ...");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void debug_info()
|
|
{
|
|
fprintf(_out, "*\t");
|
|
|
|
if (Error)
|
|
GB_DEBUG.PrintError(_out, TRUE, FALSE);
|
|
|
|
fprintf(_out, "\t");
|
|
|
|
DEBUG_backtrace(_out);
|
|
fprintf(_out, "\t");
|
|
|
|
print_local();
|
|
fprintf(_out, "\t");
|
|
|
|
print_object();
|
|
fprintf(_out, "\n");
|
|
}
|
|
|
|
static void command_frame(const char *cmd)
|
|
{
|
|
int i;
|
|
int frame;
|
|
STACK_CONTEXT *context = NULL;
|
|
|
|
if (cmd)
|
|
{
|
|
frame = atoi(&cmd[1]);
|
|
//fprintf(_out, "switching to frame %d\n", frame);
|
|
|
|
if (frame > 0)
|
|
{
|
|
for (i = 0;; i++)
|
|
{
|
|
context = GB_DEBUG.GetStack(i);
|
|
if (!context)
|
|
break;
|
|
if (!context->pc && !context->cp)
|
|
continue;
|
|
|
|
frame--;
|
|
if (!frame)
|
|
{
|
|
DEBUG_info.bp = context->bp;
|
|
DEBUG_info.fp = context->fp;
|
|
DEBUG_info.op = context->op;
|
|
DEBUG_info.cp = context->cp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!context)
|
|
{
|
|
DEBUG_info.bp = BP;
|
|
DEBUG_info.fp = FP;
|
|
DEBUG_info.op = OP;
|
|
DEBUG_info.cp = CP;
|
|
}
|
|
|
|
debug_info();
|
|
}
|
|
|
|
static void command_eval(const char *cmd)
|
|
{
|
|
EXPRESSION *expr;
|
|
ERROR_INFO save_error = { 0 };
|
|
DEBUG_INFO save_debug;
|
|
VALUE *val;
|
|
int start, len;
|
|
FILE *out;
|
|
const char *name;
|
|
int ret;
|
|
|
|
init_eval_interface();
|
|
|
|
out = *cmd == '!' ? stdout : _out;
|
|
|
|
len = strlen(cmd);
|
|
for (start = 0; start < len; start++)
|
|
{
|
|
if (cmd[start] == '\t')
|
|
break;
|
|
if (*cmd != '!')
|
|
fputc(cmd[start], _out);
|
|
}
|
|
|
|
if (start >= len)
|
|
return;
|
|
|
|
if (*cmd != '!')
|
|
fprintf(_out, "\t");
|
|
|
|
GB_DEBUG.SaveError(&save_error);
|
|
save_debug = DEBUG_info;
|
|
|
|
start++;
|
|
EVAL.New(POINTER(&expr), &cmd[start], len - start);
|
|
|
|
if (EVAL.Compile(expr, *cmd == '='))
|
|
{
|
|
if (*cmd != '!')
|
|
fprintf(_out, "!");
|
|
fputs(expr->error, out);
|
|
goto __END;
|
|
}
|
|
|
|
val = (VALUE *)EVAL.Run(expr, GB_DEBUG.GetValue);
|
|
if (!val)
|
|
goto __ERROR;
|
|
|
|
switch(*cmd)
|
|
{
|
|
case '?':
|
|
PRINT_value(out, val, TRUE);
|
|
break;
|
|
|
|
case '!':
|
|
PRINT_value(out, val, FALSE);
|
|
break;
|
|
|
|
case '#':
|
|
PRINT_object(out, val);
|
|
break;
|
|
|
|
case '=':
|
|
if (!EVAL.GetAssignmentSymbol(expr, &name, &len))
|
|
{
|
|
ret = GB_DEBUG.SetValue(name, len, val);
|
|
if (ret == GB_DEBUG_SET_ERROR)
|
|
goto __ERROR;
|
|
else if (ret == GB_DEBUG_SET_READ_ONLY)
|
|
{
|
|
fprintf(out, "!%.*s is read-only", len, name);
|
|
goto __END;
|
|
}
|
|
}
|
|
fprintf(out, "OK");
|
|
break;
|
|
}
|
|
|
|
goto __END;
|
|
|
|
__ERROR:
|
|
|
|
if (*cmd != '!')
|
|
fprintf(out, "!");
|
|
GB_DEBUG.PrintError(out, TRUE, FALSE);
|
|
|
|
__END:
|
|
|
|
EVAL.Free(POINTER(&expr));
|
|
DEBUG_info = save_debug; //.cp = NULL;
|
|
GB_DEBUG.RestoreError(&save_error);
|
|
|
|
fprintf(out, "\n");
|
|
fflush(out);
|
|
}
|
|
|
|
|
|
static void command_symbol(const char *cmd)
|
|
{
|
|
int start, len;
|
|
DEBUG_INFO save_debug = DEBUG_info;
|
|
|
|
len = strlen(cmd);
|
|
for (start = 0; start < len; start++)
|
|
{
|
|
if (cmd[start] == '\t')
|
|
break;
|
|
fputc(cmd[start], _out);
|
|
}
|
|
|
|
if (start >= len)
|
|
return;
|
|
|
|
fprintf(_out, "\t");
|
|
|
|
/*DEBUG_info.bp = BP;
|
|
DEBUG_info.fp = FP;
|
|
DEBUG_info.op = OP;
|
|
DEBUG_info.cp = CP;*/
|
|
|
|
start++;
|
|
PRINT_symbol(_out, &cmd[start], len - start);
|
|
|
|
fprintf(_out, "\n");
|
|
fflush(_out);
|
|
|
|
DEBUG_info = save_debug;
|
|
}
|
|
|
|
|
|
void DEBUG_main(bool error)
|
|
{
|
|
static DEBUG_TYPE last_command = TC_NONE;
|
|
|
|
static DEBUG_COMMAND Command[] =
|
|
{
|
|
{ "q", TC_NONE, command_quit, FALSE },
|
|
{ "n", TC_NEXT, command_next, FALSE },
|
|
{ "s", TC_STEP, command_step, FALSE },
|
|
{ "f", TC_FROM, command_from, FALSE },
|
|
{ "g", TC_GO, command_go, FALSE },
|
|
{ "+", TC_NONE, command_set_breakpoint, TRUE },
|
|
{ "-", TC_NONE, command_unset_breakpoint, TRUE },
|
|
//{ "w", TC_NONE, command_where, TRUE },
|
|
//{ "l", TC_NONE, command_local, TRUE },
|
|
{ "&", TC_NONE, command_symbol, TRUE },
|
|
{ "?", TC_NONE, command_eval, TRUE },
|
|
{ "!", TC_NONE, command_eval, TRUE },
|
|
{ "#", TC_NONE, command_eval, TRUE },
|
|
{ "=", TC_NONE, command_eval, TRUE },
|
|
//{ "e", TC_NONE, command_error, TRUE },
|
|
{ "@", TC_NONE, command_frame, TRUE },
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
|
|
char *cmd = NULL;
|
|
char cmdbuf[64];
|
|
int len;
|
|
DEBUG_COMMAND *tc = NULL;
|
|
/*static int cpt = 0;*/
|
|
|
|
Error = error;
|
|
|
|
fflush(NULL);
|
|
|
|
#ifdef DEBUG_ME
|
|
fprintf(stderr, "DEBUG_main {\n");
|
|
#endif
|
|
|
|
if (_fifo)
|
|
fprintf(_out, "!\n");
|
|
|
|
command_frame(NULL);
|
|
|
|
do
|
|
{
|
|
/*if (CP == NULL)
|
|
printf("[]:");
|
|
else
|
|
printf("[%s%s]:", DEBUG_get_current_position(), Error ? "*" : "");*/
|
|
|
|
GB.Signal(GB_SIGNAL_DEBUG_BREAK, 0);
|
|
|
|
if (!_fifo)
|
|
{
|
|
fprintf(_out, "> ");
|
|
fflush(NULL);
|
|
}
|
|
|
|
GB.FreeString(&cmd);
|
|
|
|
for(;;)
|
|
{
|
|
*cmdbuf = 0;
|
|
errno = 0;
|
|
if (fgets(cmdbuf, sizeof(cmdbuf), _in) == NULL && errno != EINTR)
|
|
break;
|
|
if (!*cmdbuf)
|
|
continue;
|
|
GB.AddString(&cmd, cmdbuf, 0);
|
|
if (cmd[GB.StringLength(cmd) - 1] == '\n')
|
|
break;
|
|
}
|
|
|
|
len = GB.StringLength(cmd);
|
|
|
|
// A null string command means an I/O error
|
|
if (len == 0)
|
|
{
|
|
fprintf(stderr, "warning: debugger I/O error: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
if (len > 0 && cmd[len - 1] == '\n')
|
|
{
|
|
len--;
|
|
cmd[len] = 0;
|
|
}
|
|
|
|
#ifdef DEBUG_ME
|
|
fprintf(stderr, "--> %s\n", cmd);
|
|
#endif
|
|
|
|
if (len == 0)
|
|
{
|
|
if (last_command == TC_NONE)
|
|
continue;
|
|
|
|
for (tc = Command; tc->pattern; tc++)
|
|
{
|
|
if (tc->type == last_command)
|
|
{
|
|
(*tc->func)(cmd);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (tc = Command; tc->pattern; tc++)
|
|
{
|
|
if (strncasecmp(tc->pattern, cmd, strlen(tc->pattern)) == 0)
|
|
{
|
|
if (tc->type != TC_NONE)
|
|
last_command = tc->type;
|
|
(*tc->func)(cmd);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tc->pattern == NULL)
|
|
WARNING("Unknown command: %s", cmd);
|
|
|
|
fflush(_out);
|
|
}
|
|
while (last_command == TC_NONE || tc->pattern == NULL || tc->loop);
|
|
|
|
GB.FreeString(&cmd);
|
|
|
|
#ifdef DEBUG_ME
|
|
fprintf(stderr, "} DEBUG_main\n");
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
void DEBUG_breakpoint(int id)
|
|
{
|
|
DEBUG_main(FALSE);
|
|
}
|
|
|
|
|
|
const char *DEBUG_get_position(CLASS *cp, FUNCTION *fp, PCODE *pc)
|
|
{
|
|
ushort line = 0;
|
|
|
|
if (fp != NULL && fp->debug)
|
|
calc_line_from_position(cp, fp, pc, &line);
|
|
|
|
snprintf(DEBUG_buffer, sizeof(DEBUG_buffer), "%.64s.%.64s.%d",
|
|
cp ? cp->name : "?",
|
|
(fp && fp->debug) ? fp->debug->name : "?",
|
|
line);
|
|
|
|
return DEBUG_buffer;
|
|
}
|
|
|
|
|
|
const char *DEBUG_get_current_position(void)
|
|
{
|
|
return DEBUG_get_position(CP, FP, PC);
|
|
}
|
|
|
|
|
|
void DEBUG_where(void)
|
|
{
|
|
fprintf(_out ? _out : stderr, "%s: ", DEBUG_get_current_position());
|
|
}
|
|
|
|
|
|
void DEBUG_welcome(void)
|
|
{
|
|
if (!_fifo)
|
|
fprintf(_out, DEBUG_WELCOME);
|
|
}
|