xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/skip.c (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
1 /* Skipping uninteresting files and functions while stepping.
2 
3    Copyright (C) 2011-2020 Free Software Foundation, Inc.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17 
18 #include "defs.h"
19 #include "skip.h"
20 #include "value.h"
21 #include "valprint.h"
22 #include "ui-out.h"
23 #include "symtab.h"
24 #include "gdbcmd.h"
25 #include "command.h"
26 #include "completer.h"
27 #include "stack.h"
28 #include "cli/cli-utils.h"
29 #include "arch-utils.h"
30 #include "linespec.h"
31 #include "objfiles.h"
32 #include "breakpoint.h" /* for get_sal_arch () */
33 #include "source.h"
34 #include "filenames.h"
35 #include "fnmatch.h"
36 #include "gdb_regex.h"
37 #include "gdbsupport/gdb_optional.h"
38 #include <list>
39 #include "cli/cli-style.h"
40 
41 /* True if we want to print debug printouts related to file/function
42    skipping. */
43 static bool debug_skip = false;
44 
45 class skiplist_entry
46 {
47 public:
48   /* Create a skiplist_entry object and add it to the chain.  */
49   static void add_entry (bool file_is_glob,
50 			 std::string &&file,
51 			 bool function_is_regexp,
52 			 std::string &&function);
53 
54   /* Return true if the skip entry has a file or glob-style file
55      pattern that matches FUNCTION_SAL.  */
56   bool skip_file_p (const symtab_and_line &function_sal) const;
57 
58   /* Return true if the skip entry has a function or function regexp
59      that matches FUNCTION_NAME.  */
60   bool skip_function_p (const char *function_name) const;
61 
62   /* Getters.  */
63   int number () const { return m_number; };
64   bool enabled () const { return m_enabled; };
65   bool file_is_glob () const { return m_file_is_glob; }
66   const std::string &file () const { return m_file; }
67   const std::string &function () const { return m_function; }
68   bool function_is_regexp () const { return m_function_is_regexp; }
69 
70   /* Setters.  */
71   void enable () { m_enabled = true; };
72   void disable () { m_enabled = false; };
73 
74   /* Disable copy.  */
75   skiplist_entry (const skiplist_entry &) = delete;
76   void operator= (const skiplist_entry &) = delete;
77 
78 private:
79   /* Key that grants access to the constructor.  */
80   struct private_key {};
81 public:
82   /* Public so we can construct with container::emplace_back.  Since
83      it requires a private class key, it can't be called from outside.
84      Use the add_entry static factory method to construct instead.  */
85   skiplist_entry (bool file_is_glob, std::string &&file,
86 		  bool function_is_regexp, std::string &&function,
87 		  private_key);
88 
89 private:
90   /* Return true if we're stopped at a file to be skipped.  */
91   bool do_skip_file_p (const symtab_and_line &function_sal) const;
92 
93   /* Return true if we're stopped at a globbed file to be skipped.  */
94   bool do_skip_gfile_p (const symtab_and_line &function_sal) const;
95 
96 private: /* data */
97   int m_number = -1;
98 
99   /* True if FILE is a glob-style pattern.
100      Otherwise it is the plain file name (possibly with directories).  */
101   bool m_file_is_glob;
102 
103   /* The name of the file or empty if no name.  */
104   std::string m_file;
105 
106   /* True if FUNCTION is a regexp.
107      Otherwise it is a plain function name (possibly with arguments,
108      for C++).  */
109   bool m_function_is_regexp;
110 
111   /* The name of the function or empty if no name.  */
112   std::string m_function;
113 
114   /* If this is a function regexp, the compiled form.  */
115   gdb::optional<compiled_regex> m_compiled_function_regexp;
116 
117   /* Enabled/disabled state.  */
118   bool m_enabled = true;
119 };
120 
121 static std::list<skiplist_entry> skiplist_entries;
122 static int highest_skiplist_entry_num = 0;
123 
124 skiplist_entry::skiplist_entry (bool file_is_glob,
125 				std::string &&file,
126 				bool function_is_regexp,
127 				std::string &&function,
128 				private_key)
129   : m_file_is_glob (file_is_glob),
130     m_file (std::move (file)),
131     m_function_is_regexp (function_is_regexp),
132     m_function (std::move (function))
133 {
134   gdb_assert (!m_file.empty () || !m_function.empty ());
135 
136   if (m_file_is_glob)
137     gdb_assert (!m_file.empty ());
138 
139   if (m_function_is_regexp)
140     {
141       gdb_assert (!m_function.empty ());
142 
143       int flags = REG_NOSUB;
144 #ifdef REG_EXTENDED
145       flags |= REG_EXTENDED;
146 #endif
147 
148       gdb_assert (!m_function.empty ());
149       m_compiled_function_regexp.emplace (m_function.c_str (), flags,
150 					  _("regexp"));
151     }
152 }
153 
154 void
155 skiplist_entry::add_entry (bool file_is_glob, std::string &&file,
156 			   bool function_is_regexp, std::string &&function)
157 {
158   skiplist_entries.emplace_back (file_is_glob,
159 				 std::move (file),
160 				 function_is_regexp,
161 				 std::move (function),
162 				 private_key {});
163 
164   /* Incremented after push_back, in case push_back throws.  */
165   skiplist_entries.back ().m_number = ++highest_skiplist_entry_num;
166 }
167 
168 static void
169 skip_file_command (const char *arg, int from_tty)
170 {
171   struct symtab *symtab;
172   const char *filename = NULL;
173 
174   /* If no argument was given, try to default to the last
175      displayed codepoint.  */
176   if (arg == NULL)
177     {
178       symtab = get_last_displayed_symtab ();
179       if (symtab == NULL)
180 	error (_("No default file now."));
181 
182       /* It is not a typo, symtab_to_filename_for_display would be needlessly
183 	 ambiguous.  */
184       filename = symtab_to_fullname (symtab);
185     }
186   else
187     filename = arg;
188 
189   skiplist_entry::add_entry (false, std::string (filename),
190 			     false, std::string ());
191 
192   printf_filtered (_("File %s will be skipped when stepping.\n"), filename);
193 }
194 
195 /* Create a skiplist entry for the given function NAME and add it to the
196    list.  */
197 
198 static void
199 skip_function (const char *name)
200 {
201   skiplist_entry::add_entry (false, std::string (), false, std::string (name));
202 
203   printf_filtered (_("Function %s will be skipped when stepping.\n"), name);
204 }
205 
206 static void
207 skip_function_command (const char *arg, int from_tty)
208 {
209   /* Default to the current function if no argument is given.  */
210   if (arg == NULL)
211     {
212       frame_info *fi = get_selected_frame (_("No default function now."));
213       struct symbol *sym = get_frame_function (fi);
214       const char *name = NULL;
215 
216       if (sym != NULL)
217 	name = sym->print_name ();
218       else
219 	error (_("No function found containing current program point %s."),
220 	       paddress (get_current_arch (), get_frame_pc (fi)));
221       skip_function (name);
222       return;
223     }
224 
225   skip_function (arg);
226 }
227 
228 /* Process "skip ..." that does not match "skip file" or "skip function".  */
229 
230 static void
231 skip_command (const char *arg, int from_tty)
232 {
233   const char *file = NULL;
234   const char *gfile = NULL;
235   const char *function = NULL;
236   const char *rfunction = NULL;
237   int i;
238 
239   if (arg == NULL)
240     {
241       skip_function_command (arg, from_tty);
242       return;
243     }
244 
245   gdb_argv argv (arg);
246 
247   for (i = 0; argv[i] != NULL; ++i)
248     {
249       const char *p = argv[i];
250       const char *value = argv[i + 1];
251 
252       if (strcmp (p, "-fi") == 0
253 	  || strcmp (p, "-file") == 0)
254 	{
255 	  if (value == NULL)
256 	    error (_("Missing value for %s option."), p);
257 	  file = value;
258 	  ++i;
259 	}
260       else if (strcmp (p, "-gfi") == 0
261 	       || strcmp (p, "-gfile") == 0)
262 	{
263 	  if (value == NULL)
264 	    error (_("Missing value for %s option."), p);
265 	  gfile = value;
266 	  ++i;
267 	}
268       else if (strcmp (p, "-fu") == 0
269 	       || strcmp (p, "-function") == 0)
270 	{
271 	  if (value == NULL)
272 	    error (_("Missing value for %s option."), p);
273 	  function = value;
274 	  ++i;
275 	}
276       else if (strcmp (p, "-rfu") == 0
277 	       || strcmp (p, "-rfunction") == 0)
278 	{
279 	  if (value == NULL)
280 	    error (_("Missing value for %s option."), p);
281 	  rfunction = value;
282 	  ++i;
283 	}
284       else if (*p == '-')
285 	error (_("Invalid skip option: %s"), p);
286       else if (i == 0)
287 	{
288 	  /* Assume the user entered "skip FUNCTION-NAME".
289 	     FUNCTION-NAME may be `foo (int)', and therefore we pass the
290 	     complete original arg to skip_function command as if the user
291 	     typed "skip function arg".  */
292 	  skip_function_command (arg, from_tty);
293 	  return;
294 	}
295       else
296 	error (_("Invalid argument: %s"), p);
297     }
298 
299   if (file != NULL && gfile != NULL)
300     error (_("Cannot specify both -file and -gfile."));
301 
302   if (function != NULL && rfunction != NULL)
303     error (_("Cannot specify both -function and -rfunction."));
304 
305   /* This shouldn't happen as "skip" by itself gets punted to
306      skip_function_command.  */
307   gdb_assert (file != NULL || gfile != NULL
308 	      || function != NULL || rfunction != NULL);
309 
310   std::string entry_file;
311   if (file != NULL)
312     entry_file = file;
313   else if (gfile != NULL)
314     entry_file = gfile;
315 
316   std::string entry_function;
317   if (function != NULL)
318     entry_function = function;
319   else if (rfunction != NULL)
320     entry_function = rfunction;
321 
322   skiplist_entry::add_entry (gfile != NULL, std::move (entry_file),
323 			     rfunction != NULL, std::move (entry_function));
324 
325   /* I18N concerns drive some of the choices here (we can't piece together
326      the output too much).  OTOH we want to keep this simple.  Therefore the
327      only polish we add to the output is to append "(s)" to "File" or
328      "Function" if they're a glob/regexp.  */
329   {
330     const char *file_to_print = file != NULL ? file : gfile;
331     const char *function_to_print = function != NULL ? function : rfunction;
332     const char *file_text = gfile != NULL ? _("File(s)") : _("File");
333     const char *lower_file_text = gfile != NULL ? _("file(s)") : _("file");
334     const char *function_text
335       = rfunction != NULL ? _("Function(s)") : _("Function");
336 
337     if (function_to_print == NULL)
338       {
339 	printf_filtered (_("%s %s will be skipped when stepping.\n"),
340 			 file_text, file_to_print);
341       }
342     else if (file_to_print == NULL)
343       {
344 	printf_filtered (_("%s %s will be skipped when stepping.\n"),
345 			 function_text, function_to_print);
346       }
347     else
348       {
349 	printf_filtered (_("%s %s in %s %s will be skipped"
350 			   " when stepping.\n"),
351 			 function_text, function_to_print,
352 			 lower_file_text, file_to_print);
353       }
354   }
355 }
356 
357 static void
358 info_skip_command (const char *arg, int from_tty)
359 {
360   int num_printable_entries = 0;
361   struct value_print_options opts;
362 
363   get_user_print_options (&opts);
364 
365   /* Count the number of rows in the table and see if we need space for a
366      64-bit address anywhere.  */
367   for (const skiplist_entry &e : skiplist_entries)
368     if (arg == NULL || number_is_in_list (arg, e.number ()))
369       num_printable_entries++;
370 
371   if (num_printable_entries == 0)
372     {
373       if (arg == NULL)
374 	current_uiout->message (_("Not skipping any files or functions.\n"));
375       else
376 	current_uiout->message (
377 	  _("No skiplist entries found with number %s.\n"), arg);
378 
379       return;
380     }
381 
382   ui_out_emit_table table_emitter (current_uiout, 6, num_printable_entries,
383 				   "SkiplistTable");
384 
385   current_uiout->table_header (5, ui_left, "number", "Num");   /* 1 */
386   current_uiout->table_header (3, ui_left, "enabled", "Enb");  /* 2 */
387   current_uiout->table_header (4, ui_right, "regexp", "Glob"); /* 3 */
388   current_uiout->table_header (20, ui_left, "file", "File");   /* 4 */
389   current_uiout->table_header (2, ui_right, "regexp", "RE");   /* 5 */
390   current_uiout->table_header (40, ui_noalign, "function", "Function"); /* 6 */
391   current_uiout->table_body ();
392 
393   for (const skiplist_entry &e : skiplist_entries)
394     {
395       QUIT;
396       if (arg != NULL && !number_is_in_list (arg, e.number ()))
397 	continue;
398 
399       ui_out_emit_tuple tuple_emitter (current_uiout, "blklst-entry");
400       current_uiout->field_signed ("number", e.number ()); /* 1 */
401 
402       if (e.enabled ())
403 	current_uiout->field_string ("enabled", "y"); /* 2 */
404       else
405 	current_uiout->field_string ("enabled", "n"); /* 2 */
406 
407       if (e.file_is_glob ())
408 	current_uiout->field_string ("regexp", "y"); /* 3 */
409       else
410 	current_uiout->field_string ("regexp", "n"); /* 3 */
411 
412       current_uiout->field_string ("file",
413 				   e.file ().empty () ? "<none>"
414 				   : e.file ().c_str (),
415 				   e.file ().empty ()
416 				   ? metadata_style.style ()
417 				   : file_name_style.style ()); /* 4 */
418       if (e.function_is_regexp ())
419 	current_uiout->field_string ("regexp", "y"); /* 5 */
420       else
421 	current_uiout->field_string ("regexp", "n"); /* 5 */
422 
423       current_uiout->field_string ("function",
424 				   e.function ().empty () ? "<none>"
425 				   : e.function ().c_str (),
426 				   e.function ().empty ()
427 				   ? metadata_style.style ()
428 				   : function_name_style.style ()); /* 6 */
429 
430       current_uiout->text ("\n");
431     }
432 }
433 
434 static void
435 skip_enable_command (const char *arg, int from_tty)
436 {
437   bool found = false;
438 
439   for (skiplist_entry &e : skiplist_entries)
440     if (arg == NULL || number_is_in_list (arg, e.number ()))
441       {
442 	e.enable ();
443 	found = true;
444       }
445 
446   if (!found)
447     error (_("No skiplist entries found with number %s."), arg);
448 }
449 
450 static void
451 skip_disable_command (const char *arg, int from_tty)
452 {
453   bool found = false;
454 
455   for (skiplist_entry &e : skiplist_entries)
456     if (arg == NULL || number_is_in_list (arg, e.number ()))
457       {
458 	e.disable ();
459 	found = true;
460       }
461 
462   if (!found)
463     error (_("No skiplist entries found with number %s."), arg);
464 }
465 
466 static void
467 skip_delete_command (const char *arg, int from_tty)
468 {
469   bool found = false;
470 
471   for (auto it = skiplist_entries.begin (),
472 	 end = skiplist_entries.end ();
473        it != end;)
474     {
475       const skiplist_entry &e = *it;
476 
477       if (arg == NULL || number_is_in_list (arg, e.number ()))
478 	{
479 	  it = skiplist_entries.erase (it);
480 	  found = true;
481 	}
482       else
483 	++it;
484     }
485 
486   if (!found)
487     error (_("No skiplist entries found with number %s."), arg);
488 }
489 
490 bool
491 skiplist_entry::do_skip_file_p (const symtab_and_line &function_sal) const
492 {
493   if (debug_skip)
494     fprintf_unfiltered (gdb_stdlog,
495 			"skip: checking if file %s matches non-glob %s...",
496 			function_sal.symtab->filename, m_file.c_str ());
497 
498   bool result;
499 
500   /* Check first sole SYMTAB->FILENAME.  It may not be a substring of
501      symtab_to_fullname as it may contain "./" etc.  */
502   if (compare_filenames_for_search (function_sal.symtab->filename,
503 				    m_file.c_str ()))
504     result = true;
505 
506   /* Before we invoke realpath, which can get expensive when many
507      files are involved, do a quick comparison of the basenames.  */
508   else if (!basenames_may_differ
509 	   && filename_cmp (lbasename (function_sal.symtab->filename),
510 			    lbasename (m_file.c_str ())) != 0)
511     result = false;
512   else
513     {
514       /* Note: symtab_to_fullname caches its result, thus we don't have to.  */
515       const char *fullname = symtab_to_fullname (function_sal.symtab);
516 
517       result = compare_filenames_for_search (fullname, m_file.c_str ());
518     }
519 
520   if (debug_skip)
521     fprintf_unfiltered (gdb_stdlog, result ? "yes.\n" : "no.\n");
522 
523   return result;
524 }
525 
526 bool
527 skiplist_entry::do_skip_gfile_p (const symtab_and_line &function_sal) const
528 {
529   if (debug_skip)
530     fprintf_unfiltered (gdb_stdlog,
531 			"skip: checking if file %s matches glob %s...",
532 			function_sal.symtab->filename, m_file.c_str ());
533 
534   bool result;
535 
536   /* Check first sole SYMTAB->FILENAME.  It may not be a substring of
537      symtab_to_fullname as it may contain "./" etc.  */
538   if (gdb_filename_fnmatch (m_file.c_str (), function_sal.symtab->filename,
539 			    FNM_FILE_NAME | FNM_NOESCAPE) == 0)
540     result = true;
541 
542   /* Before we invoke symtab_to_fullname, which is expensive, do a quick
543      comparison of the basenames.
544      Note that we assume that lbasename works with glob-style patterns.
545      If the basename of the glob pattern is something like "*.c" then this
546      isn't much of a win.  Oh well.  */
547   else if (!basenames_may_differ
548       && gdb_filename_fnmatch (lbasename (m_file.c_str ()),
549 			       lbasename (function_sal.symtab->filename),
550 			       FNM_FILE_NAME | FNM_NOESCAPE) != 0)
551     result = false;
552   else
553     {
554       /* Note: symtab_to_fullname caches its result, thus we don't have to.  */
555       const char *fullname = symtab_to_fullname (function_sal.symtab);
556 
557       result = compare_glob_filenames_for_search (fullname, m_file.c_str ());
558     }
559 
560   if (debug_skip)
561     fprintf_unfiltered (gdb_stdlog, result ? "yes.\n" : "no.\n");
562 
563   return result;
564 }
565 
566 bool
567 skiplist_entry::skip_file_p (const symtab_and_line &function_sal) const
568 {
569   if (m_file.empty ())
570     return false;
571 
572   if (function_sal.symtab == NULL)
573     return false;
574 
575   if (m_file_is_glob)
576     return do_skip_gfile_p (function_sal);
577   else
578     return do_skip_file_p (function_sal);
579 }
580 
581 bool
582 skiplist_entry::skip_function_p (const char *function_name) const
583 {
584   if (m_function.empty ())
585     return false;
586 
587   bool result;
588 
589   if (m_function_is_regexp)
590     {
591       if (debug_skip)
592         fprintf_unfiltered (gdb_stdlog,
593 			    "skip: checking if function %s matches regex %s...",
594 			    function_name, m_function.c_str ());
595 
596       gdb_assert (m_compiled_function_regexp);
597       result
598 	= (m_compiled_function_regexp->exec (function_name, 0, NULL, 0) == 0);
599     }
600   else
601     {
602       if (debug_skip)
603         fprintf_unfiltered (gdb_stdlog,
604 			    ("skip: checking if function %s matches non-regex "
605 			     "%s..."),
606 			    function_name, m_function.c_str ());
607       result = (strcmp_iw (function_name, m_function.c_str ()) == 0);
608     }
609 
610   if (debug_skip)
611     fprintf_unfiltered (gdb_stdlog, result ? "yes.\n" : "no.\n");
612 
613   return result;
614 }
615 
616 /* See skip.h.  */
617 
618 bool
619 function_name_is_marked_for_skip (const char *function_name,
620 				  const symtab_and_line &function_sal)
621 {
622   if (function_name == NULL)
623     return false;
624 
625   for (const skiplist_entry &e : skiplist_entries)
626     {
627       if (!e.enabled ())
628 	continue;
629 
630       bool skip_by_file = e.skip_file_p (function_sal);
631       bool skip_by_function = e.skip_function_p (function_name);
632 
633       /* If both file and function must match, make sure we don't errantly
634 	 exit if only one of them match.  */
635       if (!e.file ().empty () && !e.function ().empty ())
636 	{
637 	  if (skip_by_file && skip_by_function)
638 	    return true;
639 	}
640       /* Only one of file/function is specified.  */
641       else if (skip_by_file || skip_by_function)
642 	return true;
643     }
644 
645   return false;
646 }
647 
648 /* Completer for skip numbers.  */
649 
650 static void
651 complete_skip_number (cmd_list_element *cmd,
652 		      completion_tracker &completer,
653 		      const char *text, const char *word)
654 {
655   size_t word_len = strlen (word);
656 
657   for (const skiplist_entry &entry : skiplist_entries)
658     {
659       gdb::unique_xmalloc_ptr<char> name (xstrprintf ("%d", entry.number ()));
660       if (strncmp (word, name.get (), word_len) == 0)
661 	completer.add_completion (std::move (name));
662     }
663 }
664 
665 void _initialize_step_skip ();
666 void
667 _initialize_step_skip ()
668 {
669   static struct cmd_list_element *skiplist = NULL;
670   struct cmd_list_element *c;
671 
672   add_prefix_cmd ("skip", class_breakpoint, skip_command, _("\
673 Ignore a function while stepping.\n\
674 \n\
675 Usage: skip [FUNCTION-NAME]\n\
676        skip [FILE-SPEC] [FUNCTION-SPEC]\n\
677 If no arguments are given, ignore the current function.\n\
678 \n\
679 FILE-SPEC is one of:\n\
680        -fi|-file FILE-NAME\n\
681        -gfi|-gfile GLOB-FILE-PATTERN\n\
682 FUNCTION-SPEC is one of:\n\
683        -fu|-function FUNCTION-NAME\n\
684        -rfu|-rfunction FUNCTION-NAME-REGULAR-EXPRESSION"),
685                   &skiplist, "skip ", 1, &cmdlist);
686 
687   c = add_cmd ("file", class_breakpoint, skip_file_command, _("\
688 Ignore a file while stepping.\n\
689 Usage: skip file [FILE-NAME]\n\
690 If no filename is given, ignore the current file."),
691 	       &skiplist);
692   set_cmd_completer (c, filename_completer);
693 
694   c = add_cmd ("function", class_breakpoint, skip_function_command, _("\
695 Ignore a function while stepping.\n\
696 Usage: skip function [FUNCTION-NAME]\n\
697 If no function name is given, skip the current function."),
698 	       &skiplist);
699   set_cmd_completer (c, location_completer);
700 
701   c = add_cmd ("enable", class_breakpoint, skip_enable_command, _("\
702 Enable skip entries.\n\
703 Usage: skip enable [NUMBER | RANGE]...\n\
704 You can specify numbers (e.g. \"skip enable 1 3\"),\n\
705 ranges (e.g. \"skip enable 4-8\"), or both (e.g. \"skip enable 1 3 4-8\").\n\n\
706 If you don't specify any numbers or ranges, we'll enable all skip entries."),
707 	       &skiplist);
708   set_cmd_completer (c, complete_skip_number);
709 
710   c = add_cmd ("disable", class_breakpoint, skip_disable_command, _("\
711 Disable skip entries.\n\
712 Usage: skip disable [NUMBER | RANGE]...\n\
713 You can specify numbers (e.g. \"skip disable 1 3\"),\n\
714 ranges (e.g. \"skip disable 4-8\"), or both (e.g. \"skip disable 1 3 4-8\").\n\n\
715 If you don't specify any numbers or ranges, we'll disable all skip entries."),
716 	       &skiplist);
717   set_cmd_completer (c, complete_skip_number);
718 
719   c = add_cmd ("delete", class_breakpoint, skip_delete_command, _("\
720 Delete skip entries.\n\
721 Usage: skip delete [NUMBER | RANGES]...\n\
722 You can specify numbers (e.g. \"skip delete 1 3\"),\n\
723 ranges (e.g. \"skip delete 4-8\"), or both (e.g. \"skip delete 1 3 4-8\").\n\n\
724 If you don't specify any numbers or ranges, we'll delete all skip entries."),
725 	       &skiplist);
726   set_cmd_completer (c, complete_skip_number);
727 
728   add_info ("skip", info_skip_command, _("\
729 Display the status of skips.\n\
730 Usage: info skip [NUMBER | RANGES]...\n\
731 You can specify numbers (e.g. \"info skip 1 3\"), \n\
732 ranges (e.g. \"info skip 4-8\"), or both (e.g. \"info skip 1 3 4-8\").\n\n\
733 If you don't specify any numbers or ranges, we'll show all skips."));
734   set_cmd_completer (c, complete_skip_number);
735 
736   add_setshow_boolean_cmd ("skip", class_maintenance,
737 			   &debug_skip, _("\
738 Set whether to print the debug output about skipping files and functions."),
739 			   _("\
740 Show whether the debug output about skipping files and functions is printed."),
741 			   _("\
742 When non-zero, debug output about skipping files and functions is displayed."),
743 			   NULL, NULL,
744 			   &setdebuglist, &showdebuglist);
745 }
746