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