00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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
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
00101
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
00167
00168
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
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
00210
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
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
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
00749
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:
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:
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:
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
00995
00996
00997
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 }