// --------------------------------------------------------------------------- // - 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 (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 (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 (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 (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; } } }