GDB (API)
/home/stan/gdb/src/gdb/break-catch-throw.c
Go to the documentation of this file.
00001 /* Everything about catch/throw catchpoints, for GDB.
00002 
00003    Copyright (C) 1986-2013 Free Software Foundation, Inc.
00004 
00005    This file is part of GDB.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 3 of the License, or
00010    (at your option) any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
00019 
00020 #include "defs.h"
00021 #include "arch-utils.h"
00022 #include <ctype.h>
00023 #include "breakpoint.h"
00024 #include "gdbcmd.h"
00025 #include "inferior.h"
00026 #include "annotate.h"
00027 #include "valprint.h"
00028 #include "cli/cli-utils.h"
00029 #include "completer.h"
00030 #include "gdb_obstack.h"
00031 #include "mi/mi-common.h"
00032 #include "exceptions.h"
00033 #include "linespec.h"
00034 #include "probe.h"
00035 #include "objfiles.h"
00036 #include "cp-abi.h"
00037 #include "gdb_regex.h"
00038 #include "cp-support.h"
00039 
00040 /* Enums for exception-handling support.  */
00041 enum exception_event_kind
00042 {
00043   EX_EVENT_THROW,
00044   EX_EVENT_RETHROW,
00045   EX_EVENT_CATCH
00046 };
00047 
00048 /* Each spot where we may place an exception-related catchpoint has
00049    two names: the SDT probe point and the function name.  This
00050    structure holds both.  */
00051 
00052 struct exception_names
00053 {
00054   /* The name of the probe point to try, in the form accepted by
00055      'parse_probes'.  */
00056 
00057   const char *probe;
00058 
00059   /* The name of the corresponding function.  */
00060 
00061   const char *function;
00062 };
00063 
00064 /* Names of the probe points and functions on which to break.  This is
00065    indexed by exception_event_kind.  */
00066 static const struct exception_names exception_functions[] =
00067 {
00068   { "-probe-stap libstdcxx:throw", "__cxa_throw" },
00069   { "-probe-stap libstdcxx:rethrow", "__cxa_rethrow" },
00070   { "-probe-stap libstdcxx:catch", "__cxa_begin_catch" }
00071 };
00072 
00073 static struct breakpoint_ops gnu_v3_exception_catchpoint_ops;
00074 
00075 /* The type of an exception catchpoint.  */
00076 
00077 struct exception_catchpoint
00078 {
00079   /* The base class.  */
00080 
00081   struct breakpoint base;
00082 
00083   /* The kind of exception catchpoint.  */
00084 
00085   enum exception_event_kind kind;
00086 
00087   /* If non-NULL, an xmalloc'd string holding the source form of the
00088      regular expression to match against.  */
00089 
00090   char *exception_rx;
00091 
00092   /* If non-NULL, an xmalloc'd, compiled regular expression which is
00093      used to determine which exceptions to stop on.  */
00094 
00095   regex_t *pattern;
00096 };
00097 
00098 
00099 
00100 /* A helper function that fetches exception probe arguments.  This
00101    fills in *ARG0 (if non-NULL) and *ARG1 (which must be non-NULL).
00102    It will throw an exception on any kind of failure.  */
00103 
00104 static void
00105 fetch_probe_arguments (struct value **arg0, struct value **arg1)
00106 {
00107   struct frame_info *frame = get_selected_frame (_("No frame selected"));
00108   CORE_ADDR pc = get_frame_pc (frame);
00109   struct probe *pc_probe;
00110   const struct sym_probe_fns *pc_probe_fns;
00111   unsigned n_args;
00112 
00113   pc_probe = find_probe_by_pc (pc);
00114   if (pc_probe == NULL
00115       || strcmp (pc_probe->provider, "libstdcxx") != 0
00116       || (strcmp (pc_probe->name, "catch") != 0
00117           && strcmp (pc_probe->name, "throw") != 0
00118           && strcmp (pc_probe->name, "rethrow") != 0))
00119     error (_("not stopped at a C++ exception catchpoint"));
00120 
00121   gdb_assert (pc_probe->objfile != NULL);
00122   gdb_assert (pc_probe->objfile->sf != NULL);
00123   gdb_assert (pc_probe->objfile->sf->sym_probe_fns != NULL);
00124 
00125   pc_probe_fns = pc_probe->objfile->sf->sym_probe_fns;
00126   n_args = pc_probe_fns->sym_get_probe_argument_count (pc_probe);
00127   if (n_args < 2)
00128     error (_("C++ exception catchpoint has too few arguments"));
00129 
00130   if (arg0 != NULL)
00131     *arg0 = pc_probe_fns->sym_evaluate_probe_argument (pc_probe, 0);
00132   *arg1 = pc_probe_fns->sym_evaluate_probe_argument (pc_probe, 1);
00133 
00134   if ((arg0 != NULL && *arg0 == NULL) || *arg1 == NULL)
00135     error (_("error computing probe argument at c++ exception catchpoint"));
00136 }
00137 
00138 
00139 
00140 /* A helper function that returns a value indicating the kind of the
00141    exception catchpoint B.  */
00142 
00143 static enum exception_event_kind
00144 classify_exception_breakpoint (struct breakpoint *b)
00145 {
00146   struct exception_catchpoint *cp = (struct exception_catchpoint *) b;
00147 
00148   return cp->kind;
00149 }
00150 
00151 /* Implement the 'dtor' method.  */
00152 
00153 static void
00154 dtor_exception_catchpoint (struct breakpoint *self)
00155 {
00156   struct exception_catchpoint *cp = (struct exception_catchpoint *) self;
00157 
00158   xfree (cp->exception_rx);
00159   if (cp->pattern != NULL)
00160     regfree (cp->pattern);
00161   bkpt_breakpoint_ops.dtor (self);
00162 }
00163 
00164 /* Implement the 'check_status' method.  */
00165 
00166 static void
00167 check_status_exception_catchpoint (struct bpstats *bs)
00168 {
00169   struct exception_catchpoint *self
00170     = (struct exception_catchpoint *) bs->breakpoint_at;
00171   char *typename = NULL;
00172   volatile struct gdb_exception e;
00173 
00174   bkpt_breakpoint_ops.check_status (bs);
00175   if (bs->stop == 0)
00176     return;
00177 
00178   if (self->pattern == NULL)
00179     return;
00180 
00181   TRY_CATCH (e, RETURN_MASK_ERROR)
00182     {
00183       struct value *typeinfo_arg;
00184       char *canon;
00185 
00186       fetch_probe_arguments (NULL, &typeinfo_arg);
00187       typename = cplus_typename_from_type_info (typeinfo_arg);
00188 
00189       canon = cp_canonicalize_string (typename);
00190       if (canon != NULL)
00191         {
00192           xfree (typename);
00193           typename = canon;
00194         }
00195     }
00196 
00197   if (e.reason < 0)
00198     exception_print (gdb_stderr, e);
00199   else if (regexec (self->pattern, typename, 0, NULL, 0) != 0)
00200     bs->stop = 0;
00201 
00202   xfree (typename);
00203 }
00204 
00205 /* Implement the 're_set' method.  */
00206 
00207 static void
00208 re_set_exception_catchpoint (struct breakpoint *self)
00209 {
00210   struct symtabs_and_lines sals = {0};
00211   struct symtabs_and_lines sals_end = {0};
00212   volatile struct gdb_exception e;
00213   struct cleanup *cleanup;
00214   enum exception_event_kind kind = classify_exception_breakpoint (self);
00215   int pass;
00216 
00217   for (pass = 0; sals.sals == NULL && pass < 2; ++pass)
00218     {
00219       TRY_CATCH (e, RETURN_MASK_ERROR)
00220         {
00221           char *spec;
00222 
00223           if (pass == 0)
00224             {
00225               spec = ASTRDUP (exception_functions[kind].probe);
00226               sals = parse_probes (&spec, NULL);
00227             }
00228           else
00229             {
00230               spec = ASTRDUP (exception_functions[kind].function);
00231               self->ops->decode_linespec (self, &spec, &sals);
00232             }
00233         }
00234       /* NOT_FOUND_ERROR just means the breakpoint will be pending, so
00235          let it through.  */
00236       if (e.reason < 0 && e.error != NOT_FOUND_ERROR)
00237         throw_exception (e);
00238     }
00239 
00240   cleanup = make_cleanup (xfree, sals.sals);
00241   update_breakpoint_locations (self, sals, sals_end);
00242   do_cleanups (cleanup);
00243 }
00244 
00245 static enum print_stop_action
00246 print_it_exception_catchpoint (bpstat bs)
00247 {
00248   struct ui_out *uiout = current_uiout;
00249   struct breakpoint *b = bs->breakpoint_at;
00250   int bp_temp;
00251   enum exception_event_kind kind = classify_exception_breakpoint (b);
00252 
00253   annotate_catchpoint (b->number);
00254 
00255   bp_temp = b->disposition == disp_del;
00256   ui_out_text (uiout, 
00257                bp_temp ? "Temporary catchpoint "
00258                        : "Catchpoint ");
00259   if (!ui_out_is_mi_like_p (uiout))
00260     ui_out_field_int (uiout, "bkptno", b->number);
00261   ui_out_text (uiout,
00262                (kind == EX_EVENT_THROW ? " (exception thrown), "
00263                 : (kind == EX_EVENT_CATCH ? " (exception caught), "
00264                    : " (exception rethrown), ")));
00265   if (ui_out_is_mi_like_p (uiout))
00266     {
00267       ui_out_field_string (uiout, "reason", 
00268                            async_reason_lookup (EXEC_ASYNC_BREAKPOINT_HIT));
00269       ui_out_field_string (uiout, "disp", bpdisp_text (b->disposition));
00270       ui_out_field_int (uiout, "bkptno", b->number);
00271     }
00272   return PRINT_SRC_AND_LOC;
00273 }
00274 
00275 static void
00276 print_one_exception_catchpoint (struct breakpoint *b, 
00277                                 struct bp_location **last_loc)
00278 {
00279   struct value_print_options opts;
00280   struct ui_out *uiout = current_uiout;
00281   enum exception_event_kind kind = classify_exception_breakpoint (b);
00282 
00283   get_user_print_options (&opts);
00284   if (opts.addressprint)
00285     {
00286       annotate_field (4);
00287       if (b->loc == NULL || b->loc->shlib_disabled)
00288         ui_out_field_string (uiout, "addr", "<PENDING>");
00289       else
00290         ui_out_field_core_addr (uiout, "addr",
00291                                 b->loc->gdbarch, b->loc->address);
00292     }
00293   annotate_field (5);
00294   if (b->loc)
00295     *last_loc = b->loc;
00296 
00297   switch (kind)
00298     {
00299     case EX_EVENT_THROW:
00300       ui_out_field_string (uiout, "what", "exception throw");
00301       if (ui_out_is_mi_like_p (uiout))
00302         ui_out_field_string (uiout, "catch-type", "throw");
00303       break;
00304 
00305     case EX_EVENT_RETHROW:
00306       ui_out_field_string (uiout, "what", "exception rethrow");
00307       if (ui_out_is_mi_like_p (uiout))
00308         ui_out_field_string (uiout, "catch-type", "rethrow");
00309       break;
00310 
00311     case EX_EVENT_CATCH:
00312       ui_out_field_string (uiout, "what", "exception catch");
00313       if (ui_out_is_mi_like_p (uiout))
00314         ui_out_field_string (uiout, "catch-type", "catch");
00315       break;
00316     }
00317 }
00318 
00319 /* Implement the 'print_one_detail' method.  */
00320 
00321 static void
00322 print_one_detail_exception_catchpoint (const struct breakpoint *b,
00323                                        struct ui_out *uiout)
00324 {
00325   const struct exception_catchpoint *cp
00326     = (const struct exception_catchpoint *) b;
00327 
00328   if (cp->exception_rx != NULL)
00329     {
00330       ui_out_text (uiout, _("\tmatching: "));
00331       ui_out_field_string (uiout, "regexp", cp->exception_rx);
00332       ui_out_text (uiout, "\n");
00333     }
00334 }
00335 
00336 static void
00337 print_mention_exception_catchpoint (struct breakpoint *b)
00338 {
00339   struct ui_out *uiout = current_uiout;
00340   int bp_temp;
00341   enum exception_event_kind kind = classify_exception_breakpoint (b);
00342 
00343   bp_temp = b->disposition == disp_del;
00344   ui_out_text (uiout, bp_temp ? _("Temporary catchpoint ")
00345                               : _("Catchpoint "));
00346   ui_out_field_int (uiout, "bkptno", b->number);
00347   ui_out_text (uiout, (kind == EX_EVENT_THROW ? _(" (throw)")
00348                        : (kind == EX_EVENT_CATCH ? _(" (catch)")
00349                           : _(" (rethrow)"))));
00350 }
00351 
00352 /* Implement the "print_recreate" breakpoint_ops method for throw and
00353    catch catchpoints.  */
00354 
00355 static void
00356 print_recreate_exception_catchpoint (struct breakpoint *b, 
00357                                      struct ui_file *fp)
00358 {
00359   int bp_temp;
00360   enum exception_event_kind kind = classify_exception_breakpoint (b);
00361 
00362   bp_temp = b->disposition == disp_del;
00363   fprintf_unfiltered (fp, bp_temp ? "tcatch " : "catch ");
00364   switch (kind)
00365     {
00366     case EX_EVENT_THROW:
00367       fprintf_unfiltered (fp, "throw");
00368       break;
00369     case EX_EVENT_CATCH:
00370       fprintf_unfiltered (fp, "catch");
00371       break;
00372     case EX_EVENT_RETHROW:
00373       fprintf_unfiltered (fp, "rethrow");
00374       break;
00375     }
00376   print_recreate_thread (b, fp);
00377 }
00378 
00379 static void
00380 handle_gnu_v3_exceptions (int tempflag, char *except_rx, char *cond_string,
00381                           enum exception_event_kind ex_event, int from_tty)
00382 {
00383   struct exception_catchpoint *cp;
00384   struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
00385   regex_t *pattern = NULL;
00386 
00387   if (except_rx != NULL)
00388     {
00389       pattern = XNEW (regex_t);
00390       make_cleanup (xfree, pattern);
00391 
00392       compile_rx_or_error (pattern, except_rx,
00393                            _("invalid type-matching regexp"));
00394     }
00395 
00396   cp = XCNEW (struct exception_catchpoint);
00397   make_cleanup (xfree, cp);
00398 
00399   init_catchpoint (&cp->base, get_current_arch (), tempflag, cond_string,
00400                    &gnu_v3_exception_catchpoint_ops);
00401   /* We need to reset 'type' in order for code in breakpoint.c to do
00402      the right thing.  */
00403   cp->base.type = bp_breakpoint;
00404   cp->kind = ex_event;
00405   cp->exception_rx = except_rx;
00406   cp->pattern = pattern;
00407 
00408   re_set_exception_catchpoint (&cp->base);
00409 
00410   install_breakpoint (0, &cp->base, 1);
00411   discard_cleanups (cleanup);
00412 }
00413 
00414 /* Look for an "if" token in *STRING.  The "if" token must be preceded
00415    by whitespace.
00416    
00417    If there is any non-whitespace text between *STRING and the "if"
00418    token, then it is returned in a newly-xmalloc'd string.  Otherwise,
00419    this returns NULL.
00420    
00421    STRING is updated to point to the "if" token, if it exists, or to
00422    the end of the string.  */
00423 
00424 static char *
00425 extract_exception_regexp (char **string)
00426 {
00427   char *start;
00428   char *last, *last_space;
00429 
00430   start = skip_spaces (*string);
00431 
00432   last = start;
00433   last_space = start;
00434   while (*last != '\0')
00435     {
00436       char *if_token = last;
00437 
00438       /* Check for the "if".  */
00439       if (check_for_argument (&if_token, "if", 2))
00440         break;
00441 
00442       /* No "if" token here.  Skip to the next word start.  */
00443       last_space = skip_to_space (last);
00444       last = skip_spaces (last_space);
00445     }
00446 
00447   *string = last;
00448   if (last_space > start)
00449     return savestring (start, last_space - start);
00450   return NULL;
00451 }
00452 
00453 /* Deal with "catch catch", "catch throw", and "catch rethrow"
00454    commands.  */
00455 
00456 static void
00457 catch_exception_command_1 (enum exception_event_kind ex_event, char *arg,
00458                            int tempflag, int from_tty)
00459 {
00460   char *except_rx;
00461   char *cond_string = NULL;
00462   struct cleanup *cleanup;
00463 
00464   if (!arg)
00465     arg = "";
00466   arg = skip_spaces (arg);
00467 
00468   except_rx = extract_exception_regexp (&arg);
00469   cleanup = make_cleanup (xfree, except_rx);
00470 
00471   cond_string = ep_parse_optional_if_clause (&arg);
00472 
00473   if ((*arg != '\0') && !isspace (*arg))
00474     error (_("Junk at end of arguments."));
00475 
00476   if (ex_event != EX_EVENT_THROW
00477       && ex_event != EX_EVENT_CATCH
00478       && ex_event != EX_EVENT_RETHROW)
00479     error (_("Unsupported or unknown exception event; cannot catch it"));
00480 
00481   handle_gnu_v3_exceptions (tempflag, except_rx, cond_string,
00482                             ex_event, from_tty);
00483 
00484   discard_cleanups (cleanup);
00485 }
00486 
00487 /* Implementation of "catch catch" command.  */
00488 
00489 static void
00490 catch_catch_command (char *arg, int from_tty, struct cmd_list_element *command)
00491 {
00492   int tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
00493 
00494   catch_exception_command_1 (EX_EVENT_CATCH, arg, tempflag, from_tty);
00495 }
00496 
00497 /* Implementation of "catch throw" command.  */
00498 
00499 static void
00500 catch_throw_command (char *arg, int from_tty, struct cmd_list_element *command)
00501 {
00502   int tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
00503 
00504   catch_exception_command_1 (EX_EVENT_THROW, arg, tempflag, from_tty);
00505 }
00506 
00507 /* Implementation of "catch rethrow" command.  */
00508 
00509 static void
00510 catch_rethrow_command (char *arg, int from_tty,
00511                        struct cmd_list_element *command)
00512 {
00513   int tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
00514 
00515   catch_exception_command_1 (EX_EVENT_RETHROW, arg, tempflag, from_tty);
00516 }
00517 
00518 
00519 
00520 /* Implement the 'make_value' method for the $_exception
00521    internalvar.  */
00522 
00523 static struct value *
00524 compute_exception (struct gdbarch *argc, struct internalvar *var, void *ignore)
00525 {
00526   struct value *arg0, *arg1;
00527   struct type *obj_type;
00528 
00529   fetch_probe_arguments (&arg0, &arg1);
00530 
00531   /* ARG0 is a pointer to the exception object.  ARG1 is a pointer to
00532      the std::type_info for the exception.  Now we find the type from
00533      the type_info and cast the result.  */
00534   obj_type = cplus_type_from_type_info (arg1);
00535   return value_ind (value_cast (make_pointer_type (obj_type, NULL), arg0));
00536 }
00537 
00538 /* Implementation of the '$_exception' variable.  */
00539 
00540 static const struct internalvar_funcs exception_funcs =
00541 {
00542   compute_exception,
00543   NULL,
00544   NULL
00545 };
00546 
00547 
00548 
00549 static void
00550 initialize_throw_catchpoint_ops (void)
00551 {
00552   struct breakpoint_ops *ops;
00553 
00554   initialize_breakpoint_ops ();
00555 
00556   /* GNU v3 exception catchpoints.  */
00557   ops = &gnu_v3_exception_catchpoint_ops;
00558   *ops = bkpt_breakpoint_ops;
00559   ops->dtor = dtor_exception_catchpoint;
00560   ops->re_set = re_set_exception_catchpoint;
00561   ops->print_it = print_it_exception_catchpoint;
00562   ops->print_one = print_one_exception_catchpoint;
00563   ops->print_mention = print_mention_exception_catchpoint;
00564   ops->print_recreate = print_recreate_exception_catchpoint;
00565   ops->print_one_detail = print_one_detail_exception_catchpoint;
00566   ops->check_status = check_status_exception_catchpoint;
00567 }
00568 
00569 initialize_file_ftype _initialize_break_catch_throw;
00570 
00571 void
00572 _initialize_break_catch_throw (void)
00573 {
00574   initialize_throw_catchpoint_ops ();
00575 
00576   /* Add catch and tcatch sub-commands.  */
00577   add_catch_command ("catch", _("\
00578 Catch an exception, when caught."),
00579                      catch_catch_command,
00580                      NULL,
00581                      CATCH_PERMANENT,
00582                      CATCH_TEMPORARY);
00583   add_catch_command ("throw", _("\
00584 Catch an exception, when thrown."),
00585                      catch_throw_command,
00586                      NULL,
00587                      CATCH_PERMANENT,
00588                      CATCH_TEMPORARY);
00589   add_catch_command ("rethrow", _("\
00590 Catch an exception, when rethrown."),
00591                      catch_rethrow_command,
00592                      NULL,
00593                      CATCH_PERMANENT,
00594                      CATCH_TEMPORARY);
00595 
00596   create_internalvar_type_lazy ("_exception", &exception_funcs, NULL);
00597 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines