debug.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2001-2012 Ben Sapp
00004 Copyright (C) 2007-2009 John Swensen
00005 
00006 This file is part of Octave.
00007 
00008 Octave is free software; you can redistribute it and/or modify it
00009 under the terms of the GNU General Public License as published by the
00010 Free Software Foundation; either version 3 of the License, or (at your
00011 option) any later version.
00012 
00013 Octave is distributed in the hope that it will be useful, but WITHOUT
00014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00015 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00016 for more details.
00017 
00018 You should have received a copy of the GNU General Public License
00019 along with Octave; see the file COPYING.  If not, see
00020 <http://www.gnu.org/licenses/>.
00021 
00022 */
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include <deque>
00028 #include <fstream>
00029 #include <iomanip>
00030 #include <iostream>
00031 #include <set>
00032 #include <string>
00033 
00034 #include "file-stat.h"
00035 #include "singleton-cleanup.h"
00036 
00037 #include "defun.h"
00038 #include "error.h"
00039 #include "help.h"
00040 #include "input.h"
00041 #include "pager.h"
00042 #include "oct-obj.h"
00043 #include "utils.h"
00044 #include "parse.h"
00045 #include "symtab.h"
00046 #include "gripes.h"
00047 #include "ov.h"
00048 #include "ov-usr-fcn.h"
00049 #include "ov-fcn.h"
00050 #include "ov-struct.h"
00051 #include "pt-pr-code.h"
00052 #include "pt-bp.h"
00053 #include "pt-eval.h"
00054 #include "pt-stmt.h"
00055 #include "toplev.h"
00056 #include "unwind-prot.h"
00057 #include "variables.h"
00058 
00059 #include "debug.h"
00060 
00061 // Initialize the singleton object
00062 bp_table *bp_table::instance = 0;
00063 
00064 static std::string
00065 snarf_file (const std::string& fname)
00066 {
00067   std::string retval;
00068 
00069   file_stat fs (fname);
00070 
00071   if (fs)
00072     {
00073       size_t sz = fs.size ();
00074 
00075       std::ifstream file (fname.c_str (), std::ios::in|std::ios::binary);
00076 
00077       if (file)
00078         {
00079           std::string buf (sz+1, 0);
00080 
00081           file.read (&buf[0], sz+1);
00082 
00083           if (file.eof ())
00084             {
00085               // Expected to read the entire file.
00086 
00087               retval = buf;
00088             }
00089           else
00090             error ("error reading file %s", fname.c_str ());
00091         }
00092     }
00093 
00094   return retval;
00095 }
00096 
00097 static std::deque<size_t>
00098 get_line_offsets (const std::string& buf)
00099 {
00100   // This could maybe be smarter.  Is deque the right thing to use
00101   // here?
00102 
00103   std::deque<size_t> offsets;
00104 
00105   offsets.push_back (0);
00106 
00107   size_t len = buf.length ();
00108 
00109   for (size_t i = 0; i < len; i++)
00110     {
00111       char c = buf[i];
00112 
00113       if (c == '\r' && ++i < len)
00114         {
00115           c = buf[i];
00116 
00117           if (c == '\n')
00118             offsets.push_back (i+1);
00119           else
00120             offsets.push_back (i);
00121         }
00122       else if (c == '\n')
00123         offsets.push_back (i+1);
00124     }
00125 
00126   offsets.push_back (len);
00127 
00128   return offsets;
00129 }
00130 
00131 std::string
00132 get_file_line (const std::string& fname, size_t line)
00133 {
00134   std::string retval;
00135 
00136   static std::string last_fname;
00137 
00138   static std::string buf;
00139 
00140   static std::deque<size_t> offsets;
00141 
00142   if (fname != last_fname)
00143     {
00144       buf = snarf_file (fname);
00145 
00146       offsets = get_line_offsets (buf);
00147     }
00148 
00149   if (line > 0)
00150     line--;
00151 
00152   if (line < offsets.size () - 1)
00153     {
00154       size_t bol = offsets[line];
00155       size_t eol = offsets[line+1];
00156 
00157       while (eol > 0 && eol > bol && (buf[eol-1] == '\n' || buf[eol-1] == '\r'))
00158         eol--;
00159 
00160       retval = buf.substr (bol, eol - bol);
00161     }
00162 
00163   return retval;
00164 }
00165 
00166 // Return a pointer to the user-defined function FNAME.  If FNAME is
00167 // empty, search backward for the first user-defined function in the
00168 // current call stack.
00169 
00170 static octave_user_code *
00171 get_user_code (const std::string& fname = std::string ())
00172 {
00173   octave_user_code *dbg_fcn = 0;
00174 
00175   if (fname.empty ())
00176     dbg_fcn = octave_call_stack::caller_user_code ();
00177   else
00178     {
00179       octave_value fcn = symbol_table::find_function (fname);
00180 
00181       if (fcn.is_defined () && fcn.is_user_code ())
00182         dbg_fcn = fcn.user_code_value ();
00183     }
00184 
00185   return dbg_fcn;
00186 }
00187 
00188 static void
00189 parse_dbfunction_params (const char *who, const octave_value_list& args,
00190                          std::string& symbol_name, bp_table::intmap& lines)
00191 {
00192   int nargin = args.length ();
00193   int idx = 0;
00194   int list_idx = 0;
00195   symbol_name = std::string ();
00196   lines = bp_table::intmap ();
00197 
00198   if (args.length () == 0)
00199     return;
00200 
00201   // If we are already in a debugging function.
00202   if (octave_call_stack::caller_user_code ())
00203     {
00204       idx = 0;
00205       symbol_name = get_user_code ()->name ();
00206     }
00207   else if (args(0).is_map ())
00208     {
00209       // Problem because parse_dbfunction_params() can only pass out a
00210       // single function
00211     }
00212   else if (args(0).is_string())
00213     {
00214       symbol_name = args(0).string_value ();
00215       if (error_state)
00216         return;
00217       idx = 1;
00218     }
00219   else
00220     error ("%s: invalid parameter specified", who);
00221 
00222   for (int i = idx; i < nargin; i++ )
00223     {
00224       if (args(i).is_string ())
00225         {
00226           int line = atoi (args(i).string_value().c_str ());
00227           if (error_state)
00228             break;
00229           lines[list_idx++] = line;
00230         }
00231       else if (args(i).is_map ())
00232         octave_stdout << who << ": accepting a struct" << std::endl;
00233       else
00234         {
00235           const NDArray arg = args(i).array_value ();
00236 
00237           if (error_state)
00238             break;
00239 
00240           for (octave_idx_type j = 0; j < arg.nelem (); j++)
00241             {
00242               int line = static_cast<int> (arg.elem (j));
00243               if (error_state)
00244                 break;
00245               lines[list_idx++] = line;
00246             }
00247 
00248           if (error_state)
00249             break;
00250         }
00251     }
00252 }
00253 
00254 bool
00255 bp_table::instance_ok (void)
00256 {
00257   bool retval = true;
00258 
00259   if (! instance)
00260     {
00261       instance = new bp_table ();
00262 
00263       if (instance)
00264         singleton_cleanup_list::add (cleanup_instance);
00265     }
00266 
00267   if (! instance)
00268     {
00269       ::error ("unable to create breakpoint table!");
00270       retval = false;
00271     }
00272 
00273   return retval;
00274 }
00275 
00276 bp_table::intmap
00277 bp_table::do_add_breakpoint (const std::string& fname,
00278                              const bp_table::intmap& line)
00279 {
00280   intmap retval;
00281 
00282   octave_idx_type len = line.size ();
00283 
00284   octave_user_code *dbg_fcn = get_user_code (fname);
00285 
00286   if (dbg_fcn)
00287     {
00288       tree_statement_list *cmds = dbg_fcn->body ();
00289 
00290       if (cmds)
00291         {
00292           for (int i = 0; i < len; i++)
00293             {
00294               const_intmap_iterator p = line.find (i);
00295 
00296               if (p != line.end ())
00297                 {
00298                   int lineno = p->second;
00299 
00300                   retval[i] = cmds->set_breakpoint (lineno);
00301 
00302                   if (retval[i] != 0)
00303                     {
00304                       bp_set.insert (fname);
00305                     }
00306                 }
00307             }
00308         }
00309     }
00310   else
00311     error ("add_breakpoint: unable to find the requested function\n");
00312 
00313   tree_evaluator::debug_mode = bp_table::have_breakpoints () || Vdebugging;
00314 
00315   return retval;
00316 }
00317 
00318 
00319 int
00320 bp_table::do_remove_breakpoint (const std::string& fname,
00321                                 const bp_table::intmap& line)
00322 {
00323   int retval = 0;
00324 
00325   octave_idx_type len = line.size ();
00326 
00327   if (len == 0)
00328     {
00329       intmap results = remove_all_breakpoints_in_file (fname);
00330       retval = results.size ();
00331     }
00332   else
00333     {
00334       octave_user_code *dbg_fcn = get_user_code (fname);
00335 
00336       if (dbg_fcn)
00337         {
00338           tree_statement_list *cmds = dbg_fcn->body ();
00339 
00340           if (cmds)
00341             {
00342               octave_value_list results = cmds->list_breakpoints ();
00343 
00344               if (results.length () > 0)
00345                 {
00346                   for (int i = 0; i < len; i++)
00347                     {
00348                       const_intmap_iterator p = line.find (i);
00349 
00350                       if (p != line.end ())
00351                         cmds->delete_breakpoint (p->second);
00352                     }
00353 
00354                   results = cmds->list_breakpoints ();
00355 
00356                   bp_set_iterator it = bp_set.find (fname);
00357                   if (results.length () == 0 && it != bp_set.end ())
00358                     bp_set.erase (it);
00359 
00360                 }
00361 
00362               retval = results.length ();
00363             }
00364         }
00365       else
00366         error ("remove_breakpoint: unable to find the requested function\n");
00367     }
00368 
00369   tree_evaluator::debug_mode = bp_table::have_breakpoints () || Vdebugging;
00370 
00371   return retval;
00372 }
00373 
00374 
00375 bp_table::intmap
00376 bp_table::do_remove_all_breakpoints_in_file (const std::string& fname,
00377                                              bool silent)
00378 {
00379   intmap retval;
00380 
00381   octave_user_code *dbg_fcn = get_user_code (fname);
00382 
00383   if (dbg_fcn)
00384     {
00385       tree_statement_list *cmds = dbg_fcn->body ();
00386 
00387       if (cmds)
00388         {
00389           octave_value_list bkpts = cmds->list_breakpoints ();
00390 
00391           for (int i = 0; i < bkpts.length (); i++)
00392             {
00393               int lineno = static_cast<int> (bkpts(i).int_value ());
00394               cmds->delete_breakpoint (lineno);
00395               retval[i] = lineno;
00396             }
00397 
00398           bp_set_iterator it = bp_set.find (fname);
00399           if (it != bp_set.end ())
00400             bp_set.erase (it);
00401 
00402         }
00403     }
00404   else if (! silent)
00405     error ("remove_all_breakpoint_in_file: "
00406            "unable to find the requested function\n");
00407 
00408   tree_evaluator::debug_mode = bp_table::have_breakpoints () || Vdebugging;
00409 
00410   return retval;
00411 }
00412 
00413 void
00414 bp_table::do_remove_all_breakpoints (void)
00415 {
00416   for (const_bp_set_iterator it = bp_set.begin (); it != bp_set.end (); it++)
00417     remove_all_breakpoints_in_file (*it);
00418 
00419 
00420   tree_evaluator::debug_mode = bp_table::have_breakpoints () || Vdebugging;
00421 }
00422 
00423 std::string
00424 do_find_bkpt_list (octave_value_list slist,
00425                    std::string match)
00426 {
00427   std::string retval;
00428 
00429   for (int i = 0; i < slist.length (); i++)
00430     {
00431       if (slist (i).string_value () == match)
00432         {
00433           retval = slist(i).string_value ();
00434           break;
00435         }
00436     }
00437 
00438   return retval;
00439 }
00440 
00441 
00442 bp_table::fname_line_map
00443 bp_table::do_get_breakpoint_list (const octave_value_list& fname_list)
00444 {
00445   fname_line_map retval;
00446 
00447   for (bp_set_iterator it = bp_set.begin (); it != bp_set.end (); it++)
00448     {
00449       if (fname_list.length () == 0
00450           || do_find_bkpt_list (fname_list, *it) != "")
00451         {
00452           octave_user_code *f = get_user_code (*it);
00453 
00454           if (f)
00455             {
00456               tree_statement_list *cmds = f->body ();
00457 
00458               if (cmds)
00459                 {
00460                   octave_value_list bkpts = cmds->list_breakpoints ();
00461                   octave_idx_type len = bkpts.length ();
00462 
00463                   if (len > 0)
00464                     {
00465                       bp_table::intmap bkpts_vec;
00466 
00467                       for (int i = 0; i < len; i++)
00468                         bkpts_vec[i] = bkpts (i).double_value ();
00469 
00470                       std::string symbol_name = f->name ();
00471 
00472                       retval[symbol_name] = bkpts_vec;
00473                     }
00474                 }
00475             }
00476         }
00477     }
00478 
00479   return retval;
00480 }
00481 
00482 static octave_value
00483 intmap_to_ov (const bp_table::intmap& line)
00484 {
00485   int idx = 0;
00486 
00487   NDArray retval (dim_vector (1, line.size ()));
00488 
00489   for (size_t i = 0; i < line.size (); i++)
00490     {
00491       bp_table::const_intmap_iterator p = line.find (i);
00492 
00493       if (p != line.end ())
00494         {
00495           int lineno = p->second;
00496           retval(idx++) = lineno;
00497         }
00498     }
00499 
00500   retval.resize (dim_vector (1, idx));
00501 
00502   return retval;
00503 }
00504 
00505 DEFUN (dbstop, args, ,
00506   "-*- texinfo -*-\n\
00507 @deftypefn  {Loadable Function} {@var{rline} =} dbstop (\"@var{func}\")\n\
00508 @deftypefnx {Loadable Function} {@var{rline} =} dbstop (\"@var{func}\", @var{line}, @dots{})\n\
00509 Set a breakpoint in function @var{func}.\n\
00510 \n\
00511 Arguments are\n\
00512 \n\
00513 @table @var\n\
00514 @item func\n\
00515 Function name as a string variable.  When already in debug\n\
00516 mode this should be left out and only the line should be given.\n\
00517 \n\
00518 @item line\n\
00519 Line number where the breakpoint should be set.  Multiple\n\
00520 lines may be given as separate arguments or as a vector.\n\
00521 @end table\n\
00522 \n\
00523 When called with a single argument @var{func}, the breakpoint\n\
00524 is set at the first executable line in the named function.\n\
00525 \n\
00526 The optional output @var{rline} is the real line number where the\n\
00527 breakpoint was set.  This can differ from specified line if\n\
00528 the line is not executable.  For example, if a breakpoint attempted on a\n\
00529 blank line then Octave will set the real breakpoint at the\n\
00530 next executable line.\n\
00531 @seealso{dbclear, dbstatus, dbstep, debug_on_error, debug_on_warning, debug_on_interrupt}\n\
00532 @end deftypefn")
00533 {
00534   bp_table::intmap retval;
00535   std::string symbol_name;
00536   bp_table::intmap lines;
00537 
00538   parse_dbfunction_params ("dbstop", args, symbol_name, lines);
00539 
00540   if (lines.size () == 0)
00541     lines[0] = 1;
00542 
00543   if (! error_state)
00544     retval = bp_table::add_breakpoint (symbol_name, lines);
00545 
00546   return intmap_to_ov (retval);
00547 }
00548 
00549 DEFUN (dbclear, args, ,
00550   "-*- texinfo -*-\n\
00551 @deftypefn  {Loadable Function} {} dbclear (\"@var{func}\")\n\
00552 @deftypefnx {Loadable Function} {} dbclear (\"@var{func}\", @var{line}, @dots{})\n\
00553 Delete a breakpoint in the function @var{func}.\n\
00554 \n\
00555 Arguments are\n\
00556 \n\
00557 @table @var\n\
00558 @item func\n\
00559 Function name as a string variable.  When already in debug\n\
00560 mode this should be left out and only the line should be given.\n\
00561 \n\
00562 @item line\n\
00563 Line number from which to remove a breakpoint.  Multiple\n\
00564 lines may be given as separate arguments or as a vector.\n\
00565 @end table\n\
00566 \n\
00567 When called without a line number specification all breakpoints\n\
00568 in the named function are cleared.\n\
00569 \n\
00570 If the requested line is not a breakpoint no action is performed.\n\
00571 @seealso{dbstop, dbstatus, dbwhere}\n\
00572 @end deftypefn")
00573 {
00574   octave_value retval;
00575   std::string symbol_name = "";
00576   bp_table::intmap lines;
00577 
00578   parse_dbfunction_params ("dbclear", args, symbol_name, lines);
00579 
00580   if (! error_state)
00581     bp_table::remove_breakpoint (symbol_name, lines);
00582 
00583   return retval;
00584 }
00585 
00586 DEFUN (dbstatus, args, nargout,
00587   "-*- texinfo -*-\n\
00588 @deftypefn  {Loadable Function} {} dbstatus ()\n\
00589 @deftypefnx {Loadable Function} {@var{brk_list} =} dbstatus ()\n\
00590 @deftypefnx {Loadable Function} {@var{brk_list} =} dbstatus (\"@var{func}\")\n\
00591 Report the location of active breakpoints.\n\
00592 \n\
00593 When called with no input or output arguments, print the list of\n\
00594 all functions with breakpoints and the line numbers where those\n\
00595 breakpoints are set.\n\
00596 If a function name @var{func} is specified then only report breakpoints\n\
00597 for the named function.\n\
00598 \n\
00599 The optional return argument @var{brk_list} is a struct array with the\n\
00600 following fields.\n\
00601 \n\
00602 @table @asis\n\
00603 @item name\n\
00604 The name of the function with a breakpoint.\n\
00605 \n\
00606 @item file\n\
00607 The name of the m-file where the function code is located.\n\
00608 \n\
00609 @item line\n\
00610 A line number, or vector of line numbers, with a breakpoint.\n\
00611 @end table\n\
00612 \n\
00613 @seealso{dbclear, dbwhere}\n\
00614 @end deftypefn")
00615 {
00616   octave_map retval;
00617   int nargin = args.length ();
00618   octave_value_list fcn_list;
00619   bp_table::fname_line_map bp_list;
00620   std::string symbol_name;
00621 
00622   if (nargin != 0 && nargin != 1)
00623     {
00624       error ("dbstatus: only zero or one arguments accepted\n");
00625       return octave_value ();
00626     }
00627 
00628   if (nargin == 1)
00629     {
00630       if (args(0).is_string ())
00631         {
00632           symbol_name = args(0).string_value ();
00633           fcn_list(0) = symbol_name;
00634           bp_list = bp_table::get_breakpoint_list (fcn_list);
00635         }
00636       else
00637         gripe_wrong_type_arg ("dbstatus", args(0));
00638     }
00639   else
00640     {
00641        octave_user_code *dbg_fcn = get_user_code ();
00642        if (dbg_fcn)
00643          {
00644            symbol_name = dbg_fcn->name ();
00645            fcn_list(0) = symbol_name;
00646          }
00647 
00648        bp_list = bp_table::get_breakpoint_list (fcn_list);
00649     }
00650 
00651   if (nargout == 0)
00652     {
00653       // Print out the breakpoint information.
00654 
00655       for (bp_table::fname_line_map_iterator it = bp_list.begin ();
00656            it != bp_list.end (); it++)
00657         {
00658           octave_stdout << "breakpoint in " << it->first << " at line(s) ";
00659 
00660           bp_table::intmap m = it->second;
00661 
00662           size_t nel = m.size ();
00663 
00664           for (size_t j = 0; j < nel; j++)
00665             octave_stdout << m[j] << ((j < nel - 1) ? ", " : ".");
00666 
00667           if (nel > 0)
00668             octave_stdout << std::endl;
00669         }
00670       return octave_value ();
00671     }
00672   else
00673     {
00674       // Fill in an array for return.
00675 
00676       int i = 0;
00677       Cell names (dim_vector (bp_list.size (), 1));
00678       Cell file (dim_vector (bp_list.size (), 1));
00679       Cell line (dim_vector (bp_list.size (), 1));
00680 
00681       for (bp_table::const_fname_line_map_iterator it = bp_list.begin ();
00682            it != bp_list.end (); it++)
00683         {
00684           names(i) = it->first;
00685           line(i) = intmap_to_ov (it->second);
00686           file(i) = do_which (it->first);
00687           i++;
00688         }
00689 
00690       retval.assign ("name", names);
00691       retval.assign ("file", file);
00692       retval.assign ("line", line);
00693 
00694       return octave_value (retval);
00695     }
00696 }
00697 
00698 DEFUN (dbwhere, , ,
00699   "-*- texinfo -*-\n\
00700 @deftypefn {Loadable Function} {} dbwhere ()\n\
00701 In debugging mode, report the current file and line number where\n\
00702 execution is stopped.\n\
00703 @seealso{dbstatus, dbcont, dbstep, dbup}\n\
00704 @end deftypefn")
00705 {
00706   octave_value retval;
00707 
00708   octave_user_code *dbg_fcn = get_user_code ();
00709 
00710   if (dbg_fcn)
00711     {
00712       bool have_file = true;
00713 
00714       std::string name = dbg_fcn->fcn_file_name ();
00715 
00716       if (name.empty ())
00717         {
00718           have_file = false;
00719 
00720           name = dbg_fcn->name ();
00721         }
00722 
00723       octave_stdout << "stopped in " << name << " at ";
00724 
00725       int l = octave_call_stack::caller_user_code_line ();
00726 
00727       if (l > 0)
00728         {
00729           octave_stdout << " line " << l << std::endl;
00730 
00731           if (have_file)
00732             {
00733               std::string line = get_file_line (name, l);
00734 
00735               if (! line.empty ())
00736                 octave_stdout << l << ": " << line << std::endl;
00737             }
00738         }
00739       else
00740         octave_stdout << " <unknown line>" << std::endl;
00741     }
00742   else
00743     error ("dbwhere: must be inside a user function to use dbwhere\n");
00744 
00745   return retval;
00746 }
00747 
00748 // Copied and modified from the do_type command in help.cc
00749 // Maybe we could share some code?
00750 void
00751 do_dbtype (std::ostream& os, const std::string& name, int start, int end)
00752 {
00753   std::string ff = fcn_file_in_path (name);
00754 
00755   if (! ff.empty ())
00756     {
00757       std::ifstream fs (ff.c_str (), std::ios::in);
00758 
00759       if (fs)
00760         {
00761           char ch;
00762           int line = 1;
00763 
00764           if (line >= start && line <= end)
00765             os << line << "\t";
00766 
00767           while (fs.get (ch))
00768             {
00769               if (line >= start && line <= end)
00770                 {
00771                   os << ch;
00772                 }
00773 
00774               if (ch == '\n')
00775                 {
00776                   line++;
00777                   if (line >= start && line <= end)
00778                     os << line << "\t";
00779                 }
00780             }
00781         }
00782       else
00783         os << "dbtype: unable to open '" << ff << "' for reading!\n";
00784     }
00785   else
00786     os << "dbtype: unknown function " << name << "\n";
00787 
00788   os.flush ();
00789 }
00790 
00791 DEFUN (dbtype, args, ,
00792   "-*- texinfo -*-\n\
00793 @deftypefn  {Loadable Function} {} dbtype ()\n\
00794 @deftypefnx {Loadable Function} {} dbtype (\"startl:endl\")\n\
00795 @deftypefnx {Loadable Function} {} dbtype (\"@var{func}\")\n\
00796 @deftypefnx {Loadable Function} {} dbtype (\"@var{func}\", \"startl:endl\")\n\
00797 When in debugging mode and called with no arguments, list the script file\n\
00798 being debugged with line numbers.  An optional range specification,\n\
00799 specified as a string, can be used to list only a portion of the file.\n\
00800 \n\
00801 When called with the name of a function, list that script file\n\
00802 with line numbers.\n\
00803 @seealso{dbstatus, dbstop}\n\
00804 @end deftypefn")
00805 {
00806   octave_value retval;
00807   octave_user_code *dbg_fcn;
00808 
00809   int nargin = args.length ();
00810   string_vector argv = args.make_argv ("dbtype");
00811 
00812   if (! error_state)
00813     {
00814       switch (nargin)
00815         {
00816         case 0: // dbtype
00817           dbg_fcn = get_user_code ();
00818 
00819           if (dbg_fcn)
00820             do_dbtype (octave_stdout, dbg_fcn->name (), 0, INT_MAX);
00821           else
00822             error ("dbtype: must be inside a user function to give no arguments to dbtype\n");
00823           break;
00824 
00825         case 1: // (dbtype func) || (dbtype start:end)
00826           dbg_fcn = get_user_code (argv[1]);
00827 
00828           if (dbg_fcn)
00829             do_dbtype (octave_stdout, dbg_fcn->name (), 0, INT_MAX);
00830           else
00831             {
00832               dbg_fcn = get_user_code ();
00833 
00834               if (dbg_fcn)
00835                 {
00836                   std::string arg = argv[1];
00837 
00838                   size_t ind = arg.find (':');
00839 
00840                   if (ind != std::string::npos)
00841                     {
00842                       std::string start_str = arg.substr (0, ind);
00843                       std::string end_str = arg.substr (ind + 1);
00844 
00845                       int start = atoi (start_str.c_str ());
00846                       int end = atoi (end_str.c_str ());
00847 
00848                       if (std::min (start, end) <= 0)
00849                         error ("dbtype: start and end lines must be >= 1\n");
00850 
00851                       if (start <= end)
00852                         do_dbtype (octave_stdout, dbg_fcn->name (), start, end);
00853                       else
00854                         error ("dbtype: start line must be less than end line\n");
00855                     }
00856                   else
00857                     error ("dbtype: line specification must be 'start:end'");
00858                 }
00859             }
00860           break;
00861 
00862         case 2: // (dbtype func start:end) , (dbtype func start)
00863           dbg_fcn = get_user_code (argv[1]);
00864 
00865           if (dbg_fcn)
00866             {
00867               std::string arg = argv[2];
00868               int start = 0;
00869               int end = 0;
00870               size_t ind = arg.find (':');
00871 
00872               if (ind != std::string::npos)
00873                 {
00874                   std::string start_str = arg.substr (0, ind);
00875                   std::string end_str = arg.substr (ind + 1);
00876 
00877                   start = atoi (start_str.c_str ());
00878                   end = atoi (end_str.c_str ());
00879 
00880                 }
00881               else
00882                 {
00883                   start = atoi (arg.c_str ());
00884                   end = start;
00885                 }
00886 
00887               if (std::min (start, end) <= 0)
00888                 error ("dbtype: start and end lines must be >= 1\n");
00889 
00890               if (start <= end)
00891                 do_dbtype (octave_stdout, dbg_fcn->name (), start, end);
00892               else
00893                 error ("dbtype: start line must be less than end line\n");
00894             }
00895           break;
00896 
00897         default:
00898           error ("dbtype: expecting zero, one, or two arguments\n");
00899         }
00900     }
00901 
00902   return retval;
00903 }
00904 
00905 static octave_value_list
00906 do_dbstack (const octave_value_list& args, int nargout, std::ostream& os)
00907 {
00908   octave_value_list retval;
00909 
00910   unwind_protect frame;
00911 
00912   octave_idx_type curr_frame = -1;
00913 
00914   size_t nskip = 0;
00915 
00916   if (args.length () == 1)
00917     {
00918       int n = 0;
00919 
00920       octave_value arg = args(0);
00921 
00922       if (arg.is_string ())
00923         {
00924           std::string s_arg = arg.string_value ();
00925 
00926           n = atoi (s_arg.c_str ());
00927         }
00928       else
00929         n = args(0).int_value ();
00930 
00931       if (n > 0)
00932         nskip = n;
00933       else
00934         error ("dbstack: N must be a non-negative integer");
00935     }
00936 
00937   if (! error_state)
00938     {
00939       octave_map stk = octave_call_stack::backtrace (nskip, curr_frame);
00940 
00941       if (nargout == 0)
00942         {
00943           octave_idx_type nframes_to_display = stk.numel ();
00944 
00945           if (nframes_to_display > 0)
00946             {
00947               os << "stopped in:\n\n";
00948 
00949               Cell names = stk.contents ("name");
00950               Cell files = stk.contents ("file");
00951               Cell lines = stk.contents ("line");
00952 
00953               bool show_top_level = true;
00954 
00955               size_t max_name_len = 0;
00956 
00957               for (octave_idx_type i = 0; i < nframes_to_display; i++)
00958                 {
00959                   std::string name = names(i).string_value ();
00960 
00961                   max_name_len = std::max (name.length (), max_name_len);
00962                 }
00963 
00964               for (octave_idx_type i = 0; i < nframes_to_display; i++)
00965                 {
00966                   std::string name = names(i).string_value ();
00967                   std::string file = files(i).string_value ();
00968                   int line = lines(i).int_value ();
00969 
00970                   if (show_top_level && i == curr_frame)
00971                     show_top_level = false;
00972 
00973                   os << (i == curr_frame ? "  --> " : "      ")
00974                      << std::setw (max_name_len) << name
00975                      << " at line " << line
00976                      << " [" << file << "]"
00977                      << std::endl;
00978                 }
00979 
00980               if (show_top_level)
00981                 os << "  --> top level" << std::endl;
00982             }
00983         }
00984       else
00985         {
00986           retval(1) = curr_frame < 0 ? 1 : curr_frame + 1;
00987           retval(0) = stk;
00988         }
00989     }
00990 
00991   return retval;
00992 }
00993 
00994 // A function that can be easily called from a debugger print the Octave
00995 // stack.  This can be useful for finding what line of code the
00996 // interpreter is currently executing when the debugger is stopped in
00997 // some C++ function, for example.
00998 
00999 void
01000 show_octave_dbstack (void)
01001 {
01002   do_dbstack (octave_value_list (), 0, std::cerr);
01003 }
01004 
01005 DEFUN (dbstack, args, nargout,
01006   "-*- texinfo -*-\n\
01007 @deftypefn  {Loadable Function} {} dbstack ()\n\
01008 @deftypefnx {Loadable Function} {} dbstack (@var{n})\n\
01009 @deftypefnx {Loadable Function} {[@var{stack}, @var{idx}] =} dbstack (@dots{})\n\
01010 Display or return current debugging function stack information.\n\
01011 With optional argument @var{n}, omit the @var{n} innermost stack frames.\n\
01012 \n\
01013 The optional return argument @var{stack} is a struct array with the\n\
01014 following fields:\n\
01015 \n\
01016 @table @asis\n\
01017 @item file\n\
01018 The name of the m-file where the function code is located.\n\
01019 \n\
01020 @item name\n\
01021 The name of the function with a breakpoint.\n\
01022 \n\
01023 @item line\n\
01024 The line number of an active breakpoint.\n\
01025 \n\
01026 @item column\n\
01027 The column number of the line where the breakpoint begins.\n\
01028 \n\
01029 @item scope\n\
01030 Undocumented.\n\
01031 \n\
01032 @item context\n\
01033 Undocumented.\n\
01034 @end table\n\
01035 \n\
01036 The return argument @var{idx} specifies which element of the @var{stack}\n\
01037 struct array is currently active.\n\
01038 @seealso{dbup, dbdown, dbwhere, dbstatus}\n\
01039 @end deftypefn")
01040 {
01041   return do_dbstack (args, nargout, octave_stdout);
01042 }
01043 
01044 static void
01045 do_dbupdown (const octave_value_list& args, const std::string& who)
01046 {
01047   int n = 1;
01048 
01049   if (args.length () == 1)
01050     {
01051       octave_value arg = args(0);
01052 
01053       if (arg.is_string ())
01054         {
01055           std::string s_arg = arg.string_value ();
01056 
01057           n = atoi (s_arg.c_str ());
01058         }
01059       else
01060         n = args(0).int_value ();
01061     }
01062 
01063   if (! error_state)
01064     {
01065       if (who == "dbup")
01066         n = -n;
01067 
01068       if (! octave_call_stack::goto_frame_relative (n, true))
01069         error ("%s: invalid stack frame", who.c_str ());
01070     }
01071 }
01072 
01073 DEFUN (dbup, args, ,
01074   "-*- texinfo -*-\n\
01075 @deftypefn  {Loadable Function} {} dbup\n\
01076 @deftypefnx {Loadable Function} {} dbup (@var{n})\n\
01077 In debugging mode, move up the execution stack @var{n} frames.\n\
01078 If @var{n} is omitted, move up one frame.\n\
01079 @seealso{dbstack, dbdown}\n\
01080 @end deftypefn")
01081 {
01082   octave_value retval;
01083 
01084   do_dbupdown (args, "dbup");
01085 
01086   return retval;
01087 }
01088 
01089 DEFUN (dbdown, args, ,
01090   "-*- texinfo -*-\n\
01091 @deftypefn  {Loadable Function} {} dbdown\n\
01092 @deftypefnx {Loadable Function} {} dbdown (@var{n})\n\
01093 In debugging mode, move down the execution stack @var{n} frames.\n\
01094 If @var{n} is omitted, move down one frame.\n\
01095 @seealso{dbstack, dbup}\n\
01096 @end deftypefn")
01097 {
01098   octave_value retval;
01099 
01100   do_dbupdown (args, "dbdown");
01101 
01102   return retval;
01103 }
01104 
01105 DEFUN (dbstep, args, ,
01106   "-*- texinfo -*-\n\
01107 @deftypefn  {Command} {} dbstep\n\
01108 @deftypefnx {Command} {} dbstep @var{n}\n\
01109 @deftypefnx {Command} {} dbstep in\n\
01110 @deftypefnx {Command} {} dbstep out\n\
01111 @deftypefnx {Command} {} dbnext @dots{}\n\
01112 In debugging mode, execute the next @var{n} lines of code.\n\
01113 If @var{n} is omitted, execute the next single line of code.\n\
01114 If the next line of code is itself defined in terms of an m-file remain in\n\
01115 the existing function.\n\
01116 \n\
01117 Using @code{dbstep in} will cause execution of the next line to step into\n\
01118 any m-files defined on the next line.  Using @code{dbstep out} will cause\n\
01119 execution to continue until the current function returns.\n\
01120 \n\
01121 @code{dbnext} is an alias for @code{dbstep}.\n\
01122 @seealso{dbcont, dbquit}\n\
01123 @end deftypefn")
01124 {
01125   if (Vdebugging)
01126     {
01127       int nargin = args.length ();
01128 
01129       if (nargin > 1)
01130         print_usage ();
01131       else if (nargin == 1)
01132         {
01133           if (args(0).is_string ())
01134             {
01135               std::string arg = args(0).string_value ();
01136 
01137               if (! error_state)
01138                 {
01139                   if (arg == "in")
01140                     {
01141                       Vdebugging = false;
01142 
01143                       tree_evaluator::dbstep_flag = -1;
01144                     }
01145                   else if (arg == "out")
01146                     {
01147                       Vdebugging = false;
01148 
01149                       tree_evaluator::dbstep_flag = -2;
01150                     }
01151                   else
01152                     {
01153                       int n = atoi (arg.c_str ());
01154 
01155                       if (n > 0)
01156                         {
01157                           Vdebugging = false;
01158 
01159                           tree_evaluator::dbstep_flag = n;
01160                         }
01161                       else
01162                         error ("dbstep: invalid argument");
01163                     }
01164                 }
01165             }
01166           else
01167             error ("dbstep: input argument must be a character string");
01168         }
01169       else
01170         {
01171           Vdebugging = false;
01172 
01173           tree_evaluator::dbstep_flag = 1;
01174         }
01175     }
01176   else
01177     error ("dbstep: can only be called in debug mode");
01178 
01179   return octave_value_list ();
01180 }
01181 
01182 DEFALIAS (dbnext, dbstep);
01183 
01184 DEFUN (dbcont, args, ,
01185   "-*- texinfo -*-\n\
01186 @deftypefn {Command} {} dbcont\n\
01187 Leave command-line debugging mode and continue code execution normally.\n\
01188 @seealso{dbstep, dbquit}\n\
01189 @end deftypefn")
01190 {
01191   if (Vdebugging)
01192     {
01193       if (args.length () == 0)
01194         {
01195           Vdebugging = false;
01196 
01197           tree_evaluator::reset_debug_state ();
01198         }
01199       else
01200         print_usage ();
01201     }
01202   else
01203     error ("dbcont: can only be called in debug mode");
01204 
01205   return octave_value_list ();
01206 }
01207 
01208 DEFUN (dbquit, args, ,
01209   "-*- texinfo -*-\n\
01210 @deftypefn {Command} {} dbquit\n\
01211 Quit debugging mode immediately without further code execution and\n\
01212 return to the Octave prompt.\n\
01213 @seealso{dbcont, dbstep}\n\
01214 @end deftypefn")
01215 {
01216   if (Vdebugging)
01217     {
01218       if (args.length () == 0)
01219         {
01220           Vdebugging = false;
01221 
01222           tree_evaluator::reset_debug_state ();
01223 
01224           octave_throw_interrupt_exception ();
01225         }
01226       else
01227         print_usage ();
01228     }
01229   else
01230     error ("dbquit: can only be called in debug mode");
01231 
01232   return octave_value_list ();
01233 }
01234 
01235 DEFUN (isdebugmode, args, ,
01236   "-*- texinfo -*-\n\
01237 @deftypefn {Loadable Function} {} isdebugmode ()\n\
01238 Return true if in debugging mode, otherwise false.\n\
01239 @seealso{dbwhere, dbstack, dbstatus}\n\
01240 @end deftypefn")
01241 {
01242   octave_value retval;
01243 
01244   if (args.length () == 0)
01245     retval = Vdebugging;
01246   else
01247     print_usage ();
01248 
01249   return retval;
01250 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines