diff --git a/NEWS b/NEWS index a37156acb..b134ed13b 100644 --- a/NEWS +++ b/NEWS @@ -306,6 +306,9 @@ Change to the Mercury debugger: * Interactive queries are now supported on OS X. +* The "break" command can now auto-complete on the filename:linenumber pairs + of events, provided the Mercury system can access the readline library. + Changes to the extras distribution: * We have added support for Unicode and other enhancements to the lex and diff --git a/tests/debugger/completion.exp b/tests/debugger/completion.exp index 6d7ca4984..4f76b0d0f 100644 --- a/tests/debugger/completion.exp +++ b/tests/debugger/completion.exp @@ -82,17 +82,34 @@ func*completion.z func*completion.zz b func*completion.z 1: + stop interface func completion.z/0-0 (det) mdb> -completion. completion.sub2. -completion.sub1. completion.sub2.sub3. -completion.sub1. completion.sub2. completion.sub2.sub3. -completion.sub1.z1 completion.sub1.zp +completion. completion.sub1.m:15 +completion.m:17 completion.sub1.m:17 +completion.m:18 completion.sub2. +completion.m:21 completion.sub2.m:15 +completion.m:24 completion.sub2.sub3. +completion.sub1. completion.sub2.sub3.m:13 +completion.sub1. completion.sub2.m:15 +completion.sub1.m:15 completion.sub2.sub3. +completion.sub1.m:17 completion.sub2.sub3.m:13 +completion.sub2. +completion.sub1.m:15 completion.sub1.z1 +completion.sub1.m:17 completion.sub1.zp b completion.sub1.z1 2: + stop interface func completion.sub1.z1/0-0 (det) mdb> -completion. completion.sub2. -completion.sub1. completion.sub2.sub3. -completion.sub1. completion.sub2. completion.sub2.sub3. -completion.sub2. completion.sub2.sub3. +completion. completion.sub1.m:15 +completion.m:17 completion.sub1.m:17 +completion.m:18 completion.sub2. +completion.m:21 completion.sub2.m:15 +completion.m:24 completion.sub2.sub3. +completion.sub1. completion.sub2.sub3.m:13 +completion.sub1. completion.sub2.m:15 +completion.sub1.m:15 completion.sub2.sub3. +completion.sub1.m:17 completion.sub2.sub3.m:13 +completion.sub2. +completion.sub2. completion.sub2.sub3. +completion.sub2.m:15 completion.sub2.sub3.m:13 +completion.sub2.sub3.m:13 completion.sub2.sub3.zabc3 b completion.sub2.sub3.zabc3 3: + stop interface func completion.sub2.sub3.zabc3/0-0 (det) mdb> diff --git a/tests/debugger/completion.inp b/tests/debugger/completion.inp index 49e36167f..3960e174b 100644 --- a/tests/debugger/completion.inp +++ b/tests/debugger/completion.inp @@ -8,7 +8,7 @@ form@_par@ --f@ lines 10 una@ex@ b za@ b func*complet@z@ -b complet@s@1@1 -b complet@s@2@s@ +b complet@s@1@z1 +b complet@s@2@s@z@ 2d@e@l@ c diff --git a/trace/mercury_trace_completion.c b/trace/mercury_trace_completion.c index 960277556..4b77b8d07 100644 --- a/trace/mercury_trace_completion.c +++ b/trace/mercury_trace_completion.c @@ -36,21 +36,42 @@ #endif #endif -/* -** Complete on NULL terminated array of strings. -** The strings will not be `free'd. -*/ -static MR_CompleterList *MR_trace_string_array_completer( - const char *const *strings); +static char *MR_prepend_string(char *completion, + MR_CompleterData *data); static char *MR_trace_completer_list_next(const char *word, size_t word_len, MR_CompleterList **list); static void MR_trace_free_completer_list( MR_CompleterList *completer_list); -static char *MR_prepend_string(char *completion, +static char *MR_trace_sorted_array_completer_next( + const char *word, size_t word_length, MR_CompleterData *data); +static MR_CompleterList *MR_trace_string_array_completer( + const char *const *strings); +static char *MR_trace_string_array_completer_next( + const char *word, size_t word_len, + MR_CompleterData *data); + +static int MR_compare_source_file_lines( + const void *ptr1, const void *ptr2); +static void MR_insert_module_into_source_file_line_table( + const MR_ModuleLayout *module); + +static char *MR_trace_filename_completer_next(const char *word, + size_t word_len, MR_CompleterData *); + +static char *MR_trace_filter_completer_next(const char *word, + size_t word_len, MR_CompleterData *); +static void MR_trace_free_filter_completer_data( + MR_CompleterData data); + +static char *MR_trace_map_completer_next(const char *word, + size_t word_len, MR_CompleterData *); +static void MR_trace_free_map_completer_data( + MR_CompleterData data); + /*---------------------------------------------------------------------------*/ /* ** Called by Readline when the user requests completion. @@ -116,7 +137,7 @@ MR_trace_line_completer(const char *passed_word, int state) } if (command_end == insertion_point) { - /* We're completing the command itself. */ + /* We are completing the command itself. */ int num_digits; char *digits; MR_CompleterList *command_completer; @@ -149,7 +170,7 @@ MR_trace_line_completer(const char *passed_word, int state) digits, MR_free_func, completer_list); } } else { - /* We're completing an argument of the command. */ + /* We are completing an argument of the command. */ #define MR_MAX_COMMAND_NAME_LEN 256 char command[MR_MAX_COMMAND_NAME_LEN]; char *expanded_command; @@ -248,7 +269,7 @@ MR_trace_completer_list_next(const char *word, size_t word_len, while (1) { current_completer = *list; - if (!current_completer) { + if (current_completer == NULL) { return NULL; } result = (current_completer->MR_completer_func)(word, word_len, @@ -296,9 +317,6 @@ typedef struct { int MR_sorted_array_size; } MR_SortedArrayCompleterData; -static char *MR_trace_sorted_array_completer_next(const char *word, - size_t word_length, MR_CompleterData *data); - MR_CompleterList * MR_trace_sorted_array_completer(const char *word, size_t word_length, int array_size, MR_GetSlotName get_slot_name) @@ -364,8 +382,10 @@ typedef struct MR_StringArrayCompleterData_struct { int MR_string_array_current_offset; } MR_StringArrayCompleterData; -static char *MR_trace_string_array_completer_next(const char *word, - size_t word_len, MR_CompleterData *data); +/* +** Complete on a NULL terminated array of strings. +** The strings will not be `free'd. +*/ static MR_CompleterList * MR_trace_string_array_completer(const char *const *strings) @@ -403,10 +423,170 @@ MR_trace_string_array_completer_next(const char *word, size_t word_len, } /*---------------------------------------------------------------------------*/ -/* Use Readline's filename completer. */ +/* +** Complete on either a procedure specification, or a filename:linenumber pair. +** +** We construct the sorted array of filename:linenumber pairs by first +** adding all such pairs implied by the module layout structures of the Mercury +** modules that have debugging information to MR_source_file_lines, +** and then sorting that array. We cannot use the macros defined in +** mercury_array_util.h to keep the array sorted *during* the insertion +** process, because the array can gen very big (more than 150,000 entries +** for the Mercury compiler itself), and performing O(n) calls to +** MR_prepare_insert_into_sorted, whose complexity is itself O(n), +** would yield a quadratic algorithm.' We do the sorting using qsort, +** whose expected complexity is O(n log n). (I don't expect its worst case +** O(n^2) performance to ever occur in practice.) +** +** Since the MR_source_file_lines array is not sorted while we insert into it, +** we have no cheap way to search the entirety of the so-far filled in parts +** of the array to see whether the string we are about to add to it already +** occurs in it. However, we can stop most duplicates from ever getting into +** the array by simply checking if the string we are about to add is for +** the same line number in the same file as the previous string, and we do +** a final pass over the array after it is sorted, squeezing out the remaining +** duplicates, which are now guaranteed to be adjacent to each other. +*/ -static char *MR_trace_filename_completer_next(const char *word, - size_t word_len, MR_CompleterData *); +#define INIT_SOURCE_FILE_LINE_TABLE_SIZE 10 + +static const char **MR_source_file_lines; +static unsigned MR_source_file_line_next = 0; +static unsigned MR_source_file_line_max = 0; +static MR_bool MR_source_file_lines_initialized = MR_FALSE; + +#define INIT_SOURCE_FILE_LINE_CHARS_SIZE 100 +#define LINE_NUM_MAX_CHARS 20 + +static char *MR_source_file_line_chars; +static unsigned MR_source_file_line_char_next = 0; +static unsigned MR_source_file_line_char_max = 0; + +static void +MR_insert_module_into_source_file_line_table(const MR_ModuleLayout *module) +{ + int num_files; + int cur_file; + + num_files = module->MR_ml_filename_count; + for (cur_file = 0; cur_file < num_files; cur_file++) { + const MR_ModuleFileLayout *file; + MR_ConstString file_name; + int file_name_len; + int num_lines; + int cur_line; + + file = module->MR_ml_module_file_layout[cur_file]; + file_name = file->MR_mfl_filename; + file_name_len = strlen(file_name); + + MR_ensure_big_enough(file_name_len + LINE_NUM_MAX_CHARS + 2, + MR_source_file_line_char, char, + INIT_SOURCE_FILE_LINE_CHARS_SIZE); + strcpy(MR_source_file_line_chars, file_name); + MR_source_file_line_chars[file_name_len] = ':'; + + num_lines = file->MR_mfl_label_count; + MR_ensure_big_enough(MR_source_file_line_next + num_lines, + MR_source_file_line, const char *, + INIT_SOURCE_FILE_LINE_TABLE_SIZE); + for (cur_line = 0; cur_line < num_lines; cur_line++) { + int line_num = file->MR_mfl_label_lineno[cur_line]; + + /* + ** Almost all duplicates that end up in MR_source_file_lines + ** get there from consecutive entries for the same line number + ** in the same module file layout structure. Look for this + ** relatively common case, and avoid adding the redundant entry + ** to the array if we find it. + */ + if (cur_line > 0 && + line_num == file->MR_mfl_label_lineno[cur_line - 1]) + { + /* + ** The string we would add would be the same as the string + ** for the previous line number. + */ + continue; + } + + snprintf(&MR_source_file_line_chars[file_name_len + 1], + LINE_NUM_MAX_CHARS, "%d", line_num); + MR_source_file_lines[MR_source_file_line_next] = + strdup(MR_source_file_line_chars); + MR_source_file_line_next++; + } + } +} + +static int +MR_compare_source_file_lines(const void *ptr1, const void *ptr2) +{ + char * const *sptr1 = (char * const *) ptr1; + char * const *sptr2 = (char * const *) ptr2; + return strcmp(*sptr1, *sptr2); +} + +MR_CompleterList * +MR_trace_break_completer(const char *word, size_t word_len) +{ + MR_CompleterList *completer_list; + MR_CompleterList *cur_completer_list; + + completer_list = MR_trace_proc_spec_completer(word, word_len); + + if (MR_strneq(word, "pred*", 5) || MR_strneq(word, "func*", 5)) { + /* + ** These prefixes say that the breakpoint is on a named procedure, + ** not on a filename/linenumber pair. + */ + return completer_list; + } + + if (! MR_source_file_lines_initialized) { + int module_num; + int last; + int i; + + for (module_num = 0; module_num < MR_module_info_next; module_num++) { + MR_insert_module_into_source_file_line_table( + MR_module_infos[module_num]); + } + + qsort(MR_source_file_lines, MR_source_file_line_next, + sizeof(const char *), MR_compare_source_file_lines); + + /* + ** Squeeze out any duplicate entries, which are now guaranteed + ** to be adjacent to each other. + */ + last = 0; + for (i = 1; i < MR_source_file_line_next; i++) { + if (MR_streq(MR_source_file_lines[i], MR_source_file_lines[last])) { + free(MR_source_file_lines[i]); + } else { + ++last; + MR_source_file_lines[last] = MR_source_file_lines[i]; + } + } + MR_source_file_line_next = last + 1; + + MR_source_file_lines_initialized = MR_TRUE; + } + + cur_completer_list = completer_list; + while (cur_completer_list->MR_completer_list_next != NULL) { + cur_completer_list = cur_completer_list->MR_completer_list_next; + } + + cur_completer_list->MR_completer_list_next = + MR_trace_string_array_completer(MR_source_file_lines); + + return completer_list; +} + +/*---------------------------------------------------------------------------*/ +/* Use Readline's filename completer. */ MR_CompleterList * MR_trace_filename_completer(const char *word, size_t word_len) @@ -440,10 +620,6 @@ typedef struct { MR_CompleterList *MR_filter_list; } MR_Filter_Completer_Data; -static char *MR_trace_filter_completer_next(const char *word, - size_t word_len, MR_CompleterData *); -static void MR_trace_free_filter_completer_data(MR_CompleterData data); - MR_CompleterList * MR_trace_filter_completer(MR_CompleterFilter filter, MR_CompleterData filter_data, MR_FreeCompleterData free_filter_data, @@ -502,10 +678,6 @@ typedef struct { MR_CompleterList *MR_map_list; } MR_MapCompleterData; -static char *MR_trace_map_completer_next(const char *word, - size_t word_len, MR_CompleterData *); -static void MR_trace_free_map_completer_data(MR_CompleterData data); - MR_CompleterList * MR_trace_map_completer(MR_Completer_Map map, MR_CompleterData map_data, MR_FreeCompleterData free_data, MR_CompleterList *list) diff --git a/trace/mercury_trace_completion.h b/trace/mercury_trace_completion.h index f1c0a4185..9c7cf667e 100644 --- a/trace/mercury_trace_completion.h +++ b/trace/mercury_trace_completion.h @@ -58,6 +58,10 @@ typedef MR_CompleterList *(* MR_MakeCompleter)(const char *word, extern MR_CompleterList *MR_trace_null_completer(const char *word, size_t word_len); +/* Complete on either a procedure specification, or filename:linenumber. */ +extern MR_CompleterList *MR_trace_break_completer(const char *word, + size_t word_len); + /* Use Readline's filename completer. */ extern MR_CompleterList *MR_trace_filename_completer(const char *word, size_t word_len); diff --git a/trace/mercury_trace_internal.c b/trace/mercury_trace_internal.c index cf86cdffb..348a0430b 100644 --- a/trace/mercury_trace_internal.c +++ b/trace/mercury_trace_internal.c @@ -1556,7 +1556,7 @@ static const MR_TraceCmdTableEntry MR_trace_command_table[] = NULL, MR_trace_null_completer }, { "breakpoint", "break", MR_trace_cmd_break, - MR_trace_break_cmd_args, MR_trace_proc_spec_completer }, + MR_trace_break_cmd_args, MR_trace_break_completer }, { "breakpoint", "condition", MR_trace_cmd_condition, NULL, MR_trace_null_completer }, { "breakpoint", "ignore", MR_trace_cmd_ignore, diff --git a/trace/mercury_trace_spy.c b/trace/mercury_trace_spy.c index a75c7ea3a..d164389cf 100644 --- a/trace/mercury_trace_spy.c +++ b/trace/mercury_trace_spy.c @@ -186,14 +186,14 @@ static int MR_compare_addr(const void *address1, const void *address2) { /* - ** Note that we can't just compare the pointers, because - ** because on a segmented architecture, that might - ** only compare the segments, not the offsets (ANSI C - ** doesn't require pointer comparisons to work unless - ** the pointers point into the same array, which is not - ** necessarily going to be the case here). - ** So instead we need to cast the pointers to integers - ** and compare the integers. + ** Note that we can't just compare the pointers, because on a + ** segmented architecture, that might only compare the segments, + ** not the offsets (ANSI C doesn't require pointer comparisons to work + ** unless the pointers point into the same array, which is not necessarily + ** going to be the case here). + ** + ** So instead we need to cast the pointers to integers and then + ** compare the integers. */ MR_Unsigned num1 = (MR_Unsigned) address1; MR_Unsigned num2 = (MR_Unsigned) address2; @@ -222,7 +222,7 @@ MR_search_spy_table_for_proc(const MR_ProcLayout *entry) /* ** Return the index of the entry in MR_spied_labels whose MR_sl_label field -** is label, or a negative number if absent. +** is equal to the label argument, or a negative number if absent. */ static int @@ -242,8 +242,8 @@ MR_search_spy_table_for_label(const MR_LabelLayout *label) /* ** Return the index of the entry in MR_spied_user_events whose -** MR_sue_user_event_name field is user_event_name, or a negative number -** if absent. +** MR_sue_user_event_name field is equal to the user_event_name argument, +** or a negative number if absent. */ static int @@ -264,8 +264,8 @@ MR_search_spy_table_for_user_event_name(const char *user_event_name) /* ** Return the index of the entry in MR_spied_user_event_sets whose -** MR_sues_user_event_set field is user_event_set, or a negative number -** if absent. +** MR_sues_user_event_set field is equal to the user_event_set argument, +** or a negative number if absent. */ static int @@ -770,7 +770,7 @@ MR_add_line_spy_point(MR_SpyAction action, MR_SpyIgnore_When ignore_when, MR_fatal_error("MR_add_line_spy_point: num_line_matches != 0"); } - /* there were no matching labels */ + /* There were no matching labels. */ #ifdef MR_HAVE_A_SNPRINTF if (num_file_matches == 0) { snprintf(MR_error_msg_buf, MR_ERROR_MSG_BUF_SIZE, @@ -781,7 +781,7 @@ MR_add_line_spy_point(MR_SpyAction action, MR_SpyIgnore_When ignore_when, linenumber, filename); } #else - /* not absolutely safe, but the risk of overflow is minimal */ + /* Not absolutely safe, but the risk of overflow is minimal. */ if (num_file_matches == 0) { sprintf(MR_error_msg_buf, "there is no debuggable source file named %s", filename); @@ -944,12 +944,12 @@ MR_add_spy_point_print_list_start(int point_slot, MR_SpyPrintList print_list) return; } - /* find the last node in print_list */ + /* Find the last node in print_list. */ while (list->MR_pl_next != NULL) { list = list->MR_pl_next; } - /* add the existing spy_print_list at the end of print_list */ + /* Add the existing spy_print_list at the end of print_list. */ list->MR_pl_next = MR_spy_points[point_slot]->MR_spy_print_list; MR_spy_points[point_slot]->MR_spy_print_list = print_list; } @@ -965,12 +965,12 @@ MR_add_spy_point_print_list_end(int point_slot, MR_SpyPrintList print_list) return; } - /* find the last node in print_list */ + /* Find the last node in print_list. */ while (list->MR_pl_next != NULL) { list = list->MR_pl_next; } - /* add the print_list at the end of the existing spy_print_list */ + /* Add the print_list at the end of the existing spy_print_list. */ list->MR_pl_next = print_list; } @@ -1046,7 +1046,7 @@ MR_delete_spy_point(int point_table_slot) MR_spy_points[point_table_slot]->MR_spy_exists = MR_FALSE; MR_delete_spy_print_list(point->MR_spy_print_list); - /* in case it gets deleted again */ + /* In case it gets deleted again. */ point->MR_spy_print_list = NULL; if (point->MR_spy_cond != NULL) { @@ -1054,7 +1054,7 @@ MR_delete_spy_point(int point_table_slot) MR_free(point->MR_spy_cond->MR_cond_what_string); MR_free(point->MR_spy_cond); - /* in case it gets deleted again */ + /* In case it gets deleted again. */ point->MR_spy_cond = NULL; } @@ -1080,8 +1080,7 @@ MR_delete_spy_point(int point_table_slot) MR_spied_label_next = label_slot; } else { /* - ** Remove the spy point from the spied proc table list - ** for its proc. + ** Remove the spy point from the spied proc table list for its proc. */ proc_table_slot = MR_search_spy_table_for_proc(point->MR_spy_proc); diff --git a/trace/mercury_trace_tables.c b/trace/mercury_trace_tables.c index d8e75d424..27f7f999d 100644 --- a/trace/mercury_trace_tables.c +++ b/trace/mercury_trace_tables.c @@ -109,10 +109,10 @@ typedef struct { int MR_unambiguous_matching_module; /* - ** Length of the part of the word to skip when testing for module - ** qualified completions in the current module, zero if we - ** shouldn't test for module qualified completions in the - ** current module. + ** Length of the part of the word to skip when testing for + ** module qualified completions in the current module, + ** zero if we shouldn't test for module qualified completions + ** in the current module. */ int MR_complete_word_matches_module; int MR_complete_current_module; @@ -378,7 +378,7 @@ MR_process_line_layouts(const MR_ModuleFileLayout *file_layout, int line, } /* - ** ... and the call *all* such labels. + ** ... and then do a callback for *all* such labels. */ while (k < file_layout->MR_mfl_label_count @@ -404,7 +404,7 @@ MR_dump_module_tables(FILE *fp, MR_bool separate, MR_bool uci, if (module_name != NULL) { module = MR_search_module_info_by_unique_name(fp, module_name); if (module == NULL) { - /* The error message has already been printed */ + /* The error message has already been printed. */ return; } } else { @@ -449,7 +449,7 @@ MR_dump_module_procs(FILE *fp, const char *name) module = MR_search_module_info_by_unique_name(fp, name); if (module == NULL) { - /* The error message has already been printed */ + /* The error message has already been printed. */ return; } @@ -621,7 +621,8 @@ typedef struct { ((functor1).MR_functor_arity - (functor2).MR_functor_arity) #define MR_functor_compare_type_module(functor1, functor2) \ - strcmp((functor1).MR_functor_type_module, (functor2).MR_functor_type_module) + strcmp((functor1).MR_functor_type_module, \ + (functor2).MR_functor_type_module) #define MR_functor_compare_type_name(functor1, functor2) \ strcmp((functor1).MR_functor_type_name, (functor2).MR_functor_type_name) @@ -1471,11 +1472,6 @@ MR_trace_proc_spec_completer(const char *word, size_t word_len) data->MR_complete_pf = MR_FUNCTION; word += 5; } else { - /* - ** We don't complete on the names of special (unify, index compare, - ** or init) predicates. - */ - data->MR_complete_pf = -1; } @@ -1490,7 +1486,7 @@ MR_trace_proc_spec_completer(const char *word, size_t word_len) /* ** Handle the case where the word matches the first part of a module name. - ** If the word unambiguously determines the module name we want to return + ** If the word unambiguously determines the module name, we want to return ** module qualified completions for all the procedures in that module. ** Otherwise, we just complete on the names of all of the matching modules ** and unqualified procedure names. @@ -1500,6 +1496,9 @@ MR_trace_proc_spec_completer(const char *word, size_t word_len) ** as well as all procedures whose unqualified names begin with `f'. ** Given word to complete `foo.' and modules `foo' and `foo.bar' we want to ** return `foo.bar.' and all the procedures in module `foo' as completions. + ** + ** We don't complete on the names of special (unify, index or compare) + ** predicates. */ MR_bsearch(MR_module_info_next, slot, found, @@ -1546,7 +1545,8 @@ try_completion: if (data->MR_complete_current_module == -1 || data->MR_complete_current_proc == -1 || data->MR_complete_current_proc >= - MR_module_infos[data->MR_complete_current_module]->MR_ml_proc_count) + MR_module_infos[data->MR_complete_current_module]-> + MR_ml_proc_count) { /* ** Move on to the next module.