// ---------------------------------------------------------------------------
// - Debugger.hpp -
// - afnix cross debugger - debugger class implementation -
// ---------------------------------------------------------------------------
// - This program is free software; you can redistribute it and/or modify -
// - it provided that this copyright notice is kept intact. -
// - -
// - 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. In no event shall -
// - the copyright holder be liable for any direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software. -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2007 amaury darsch -
// ---------------------------------------------------------------------------
#include "System.hpp"
#include "Module.hpp"
#include "Return.hpp"
#include "Resume.hpp"
#include "Integer.hpp"
#include "Function.hpp"
#include "Axdcalls.hpp"
#include "Debugger.hpp"
namespace afnix {
// debugger default prompts
static const char* DEFAULT_PROMPT1 = "(axd) ";
static const char* DEFAULT_PROMPT2 = "(...) ";
// create a default debugger
Debugger::Debugger (void) {
// initialize the debugger mode
d_emacs = false;
d_start = false;
d_finish = false;
d_exit = false;
d_vflag = true;
d_lmax = 10;
p_form = nilp;
// initialize the terminal
if (p_term != nilp) {
p_term->setpp (DEFAULT_PROMPT1);
p_term->setsp (DEFAULT_PROMPT2);
p_term->setigneof (true);
}
// initialize the reader
Object::iref (p_reader = new Reader (getis ()));
// bind the axd nameset
Object::iref (p_aset = mknset ("axd"));
// bind the i-files nameset
Object::iref (p_iset = p_aset->mknset ("i-files"));
// bind the commands in the axd nameset
p_aset->symcst ("run", new Function (axd_run));
p_aset->symcst ("load", new Function (axd_ldf));
p_aset->symcst ("exit", new Function (axd_xit));
p_aset->symcst ("quit", new Function (axd_xit));
p_aset->symcst ("next", new Function (axd_nxt));
p_aset->symcst ("break", new Function (axd_bpt));
p_aset->symcst ("list", new Function (axd_lst));
p_aset->symcst ("info", new Function (axd_ifo));
p_aset->symcst ("continue", new Function (axd_cnt));
p_aset->symcst ("break-info", new Function (axd_bfo));
}
// destroy this debugger
Debugger::~Debugger (void) {
d_bpoint.reset ();
if (p_iset != nilp) p_iset->reset ();
if (p_aset != nilp) p_aset->reset ();
Object::dref (p_form);
Object::dref (p_iset);
Object::dref (p_aset);
Object::dref (p_reader);
}
// set the emacs info mode
void Debugger::setemacs (const bool mode) {
wrlock ();
d_emacs = mode;
unlock ();
}
// return the start flag
bool Debugger::getstart (void) const {
rdlock ();
bool result = d_start;
unlock ();
return result;
}
// set the exit flag
void Debugger::setexit (const bool flag) {
wrlock ();
d_exit = flag;
unlock ();
}
// set the initial file name
void Debugger::setinitial (const String& fname) {
wrlock ();
try {
if (fname.length () != 0) {
Ifile* ifile = instrument (fname);
if ((ifile != nilp) && (ifile->length () > 0)) {
Form* form = ifile->getform(0);
if (form != nilp) {
Object::iref (form);
Object::dref (p_form);
p_form = form;
String name = form->getname ();
long lnum = form->getlnum ();
info (name, lnum);
}
}
d_fname = fname;
}
unlock ();
} catch (...) {
unlock ();
throw;
}
}
// run the program as specified in the initial file name
void Debugger::runinitial (void) {
if (d_fname.length () == 0)
throw Exception ("axd-error", "no initial file specified");
d_start = true;
load (d_fname);
}
// instrument a file and return an instrumented file object
Ifile* Debugger::instrument (const String& fname) {
wrlock ();
try {
// create a new module reader
Input* ms = p_rslv->alplkp (fname);
String mn = p_rslv->alpname (fname);
Module mp (ms, mn);
// bind the instrumented file
Ifile* ifile = new Ifile (mp);
if (ifile != nilp) p_iset->bind (mn, ifile);
unlock ();
return ifile;
} catch (...) {
unlock ();
throw;
}
}
// break the debugger in a nameset with an object
bool Debugger::bpt (Nameset* nset, Object* object) {
// check for a form
Form* form = dynamic_cast <Form*> (object);
if (form != nilp) {
String name = form->getname ();
long lnum = form->getlnum ();
if (d_bpoint.exists (form) == true) {
long index = d_bpoint.find (form);
pmsg ("breakpoint", index, name, lnum);
}
// add additional information
info (name, lnum);
// set the form
Object::iref (form);
Object::dref (p_form);
p_form = form;
}
// loop in the nameset
return loop (nset);
}
// load a file name - first read the file - create a block form list
// also updates the debugger tables - and finally execute it
void Debugger::load (const String& fname) {
wrlock ();
try {
// try to find the instrumented file
Ifile* ifile = nilp;
if (p_iset->exists (fname) == false) {
ifile = instrument (fname);
} else {
ifile = dynamic_cast <Ifile*> (p_iset->find (fname));
}
// evaluate the ifile
if (ifile != nilp) Object::cref (ifile->eval (this, getgset ()));
unlock ();
} catch (...) {
unlock ();
throw;
}
}
// the debugger main loop
bool Debugger::loop (void) {
// initialize status
bool status = true;
// loop in the standard input
try {
status = loop ((Nameset*) nilp);
} catch (const Exception& e) {
getes()->errorln (e);
} catch (...) {
status = false;
getes()->errorln ("fatal: unknown exception trapped");
}
return status;
}
// loop on the standard input in a nameset context
bool Debugger::loop (Nameset* nset) {
// check if the nameset is defined
Nameset* lset = (nset == nilp) ? getgset () : nset;
// loop until the exit flag is set
while (d_exit == false) {
Form* form = nilp;
try {
form = p_reader->parse ();
if (form == nilp) break;
Object::cref (form->eval (this, lset));
Object::dref (form);
} catch (const Exception& e) {
getes()->errorln (e);
Object::dref (form);
} catch (const Return& r) {
this->post (r.getobj ());
Object::dref (form);
} catch (const Resume&) {
Object::dref (form);
if (nset != nilp) break;
} catch (...) {
Object::dref (form);
throw;
}
}
return true;
}
// install a breakpoint at the current position
void Debugger::setbpt (void) {
wrlock ();
if (p_form == nilp) {
unlock ();
throw Exception ("debugger-error", "nothing to list - no form found");
}
try {
String name = p_form->getname ();
long lnum = p_form->getlnum ();
setbpt (name, lnum);
unlock ();
} catch (...) {
unlock ();
throw;
}
}
// install a breakpoint by line number
void Debugger::setbpt (const long lnum) {
wrlock ();
if (p_form == nilp) {
unlock ();
throw Exception ("debugger-error", "nothing to list - no form found");
}
try {
String name = p_form->getname ();
setbpt (name, lnum);
unlock ();
} catch (...) {
unlock ();
throw;
}
}
// install a breakpoint by file name and line number - if the line number
// is 0 we use the first form
void Debugger::setbpt (const String& fname, const long lnum) {
wrlock ();
try {
// check if the file name exist or instrument it
Ifile* ifile = nilp;
if (p_iset->exists (fname) == false) {
ifile = instrument (fname);
} else {
ifile = dynamic_cast <Ifile*> (p_iset->find (fname));
}
if (ifile == nilp)
throw Exception ("debugger-error", "cannot find file", fname);
// we got the ifile - get the form by line number
Form* form = ifile->lookup (lnum);
if (form == nilp) {
Integer lstr = lnum;
throw Exception ("debugger-error", "cannot find form at line",
lstr.tostring ());
}
// check if the form already has a breakpoint
if (d_bpoint.exists (form) == false) d_bpoint.append (form);
// set the breakpoint
form->setbpt (true);
// get the breakpoint index
long index = d_bpoint.find (form);
pmsg ("setting breakpoint", index, fname, lnum);
unlock ();
} catch (...) {
unlock ();
throw;
}
}
// list a file at the current form
void Debugger::flist (void) const {
rdlock ();
if (p_form == nilp) {
unlock ();
throw Exception ("debugger-error", "nothing to list - no form found");
}
try {
String name = p_form->getname ();
long lnum = p_form->getlnum ();
flist (name, lnum);
unlock ();
} catch (...) {
unlock ();
throw;
}
}
// list the current file at a certain line
void Debugger::flist (const long lnum) const {
rdlock ();
if (p_form == nilp) {
unlock ();
throw Exception ("debugger-error", "nothing to list - no form found");
}
try {
String name = p_form->getname ();
flist (name, lnum);
unlock ();
} catch (...) {
unlock ();
throw;
}
}
// list a file content on the standard output by line number
void Debugger::flist (const String& fname, const long lnum) const {
rdlock ();
try {
// call the resolver to get an input stream
Input* is = p_rslv->alplkp (fname);
// jump to the line
long lc = 1;
while (lc < lnum) {
char c = is->read ();
if (c == eolc) lc++;
if (c == eofc) break;
}
// get the output stream
Output* os = getos ();
// now print upto max lines
for (long i = 0; i < d_lmax; i++) {
Integer lval (lnum + i);
String line = lval.tostring ();
line = line.rfill (' ', 6);
line = line + is->readln ();
os->writeln (line);
}
unlock ();
} catch (...) {
unlock ();
throw;
}
}
// write an info message based on callback or emacs mode
void Debugger::info (const String& name, const long lnum) const {
rdlock ();
// check for emacs mode
if (d_emacs == true) {
Output* os = getos ();
if ((os == nilp) || (name.length () == 0) || (lnum <= 0)) {
unlock ();
return;
}
*os << '\032' << '\032' << name << ':' << lnum << eolc;
unlock ();
return;
}
unlock ();
}
// print a message if the verbose flag is on
void Debugger::pmsg (const String& msg, const long data,
const String& fname, const long lnum) const {
// check if the verbose flag is on
if ((d_vflag == false) || (msg.length () == 0)) return;
// get the output stream
Output* os = getos ();
*os << msg << ' ' << data;
// check for file name
if (fname.length () == 0) return;
*os << " in file " << fname << " at line " << lnum << eolc;
}
// print some info about the debugger
void Debugger::dbginfo (void) const {
rdlock ();
Output* os = getos ();
String fname = (d_fname.length () == 0) ? "not defined" : d_fname;
String vflag = d_vflag ? "true" : "false";
long blen = d_bpoint.length ();
String sform = (p_form == nilp) ? "not defined" : p_form->getname ();
long slnum = (p_form == nilp) ? 0 : p_form->getlnum ();
// print debugger information
*os << "debugger version : " << System::version () << eolc;
*os << "os name : " << System::osname () << eolc;
*os << "os type : " << System::ostype () << eolc;
*os << "initial file : " << fname << eolc;
*os << "form file name : " << sform << eolc;
*os << "form line number : " << slnum << eolc;
*os << "verbose mode : " << vflag << eolc;
*os << "max line display : " << d_lmax << eolc;
*os << "defined breakpoints : " << blen << eolc;
unlock ();
}
// print some information about the breakpoints
void Debugger::brkinfo (void) const {
rdlock ();
try {
Output* os = getos ();
long blen = d_bpoint.length ();
for (long i = 0; i < blen; i++) {
Form* form = dynamic_cast <Form*> (d_bpoint.get (i));
if (form == nilp) continue;
String name = form->getname ();
long lnum = form->getlnum ();
*os << "Breakpoint " << i << " in file " << name;
*os << " at line " << lnum << eolc;
}
unlock ();
} catch (...) {
unlock ();
throw;
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1