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. */
43static bool debug_skip = false;
44
45class skiplist_entry
46{
47public:
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
78private:
79  /* Key that grants access to the constructor.  */
80  struct private_key {};
81public:
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
89private:
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
96private: /* 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
121static std::list<skiplist_entry> skiplist_entries;
122static int highest_skiplist_entry_num = 0;
123
124skiplist_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
154void
155skiplist_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
168static void
169skip_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
198static void
199skip_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
206static void
207skip_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
230static void
231skip_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
357static void
358info_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
434static void
435skip_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
450static void
451skip_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
466static void
467skip_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
490bool
491skiplist_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
526bool
527skiplist_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
566bool
567skiplist_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
581bool
582skiplist_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
618bool
619function_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
650static void
651complete_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
665void _initialize_step_skip ();
666void
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, _("\
673Ignore a function while stepping.\n\
674\n\
675Usage: skip [FUNCTION-NAME]\n\
676       skip [FILE-SPEC] [FUNCTION-SPEC]\n\
677If no arguments are given, ignore the current function.\n\
678\n\
679FILE-SPEC is one of:\n\
680       -fi|-file FILE-NAME\n\
681       -gfi|-gfile GLOB-FILE-PATTERN\n\
682FUNCTION-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, _("\
688Ignore a file while stepping.\n\
689Usage: skip file [FILE-NAME]\n\
690If 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, _("\
695Ignore a function while stepping.\n\
696Usage: skip function [FUNCTION-NAME]\n\
697If 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, _("\
702Enable skip entries.\n\
703Usage: skip enable [NUMBER | RANGE]...\n\
704You can specify numbers (e.g. \"skip enable 1 3\"),\n\
705ranges (e.g. \"skip enable 4-8\"), or both (e.g. \"skip enable 1 3 4-8\").\n\n\
706If 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, _("\
711Disable skip entries.\n\
712Usage: skip disable [NUMBER | RANGE]...\n\
713You can specify numbers (e.g. \"skip disable 1 3\"),\n\
714ranges (e.g. \"skip disable 4-8\"), or both (e.g. \"skip disable 1 3 4-8\").\n\n\
715If 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, _("\
720Delete skip entries.\n\
721Usage: skip delete [NUMBER | RANGES]...\n\
722You can specify numbers (e.g. \"skip delete 1 3\"),\n\
723ranges (e.g. \"skip delete 4-8\"), or both (e.g. \"skip delete 1 3 4-8\").\n\n\
724If 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, _("\
729Display the status of skips.\n\
730Usage: info skip [NUMBER | RANGES]...\n\
731You can specify numbers (e.g. \"info skip 1 3\"), \n\
732ranges (e.g. \"info skip 4-8\"), or both (e.g. \"info skip 1 3 4-8\").\n\n\
733If 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, _("\
738Set whether to print the debug output about skipping files and functions."),
739			   _("\
740Show whether the debug output about skipping files and functions is printed."),
741			   _("\
742When non-zero, debug output about skipping files and functions is displayed."),
743			   NULL, NULL,
744			   &setdebuglist, &showdebuglist);
745}
746