// vim: ts=4 sw=4 expandtab ft=c // Copyright (C) 1998-2009, 2011 The University of Melbourne. // Copyright (C) 2014-2016, 2018 The Mercury team. // This file is distributed under the terms specified in COPYING.LIB. // Main authors: Mark Brown, Ian MacLarty // // This file implements the back end of the declarative debugger. The back end // is an extension to the internal debugger which collects related trace events // and builds them into an annotated trace. Once built, the structure is // passed to the front end where it can be analysed to find bugs. The front end // is implemented in browser/declarative_debugger.m. // // The interface between the front and back ends is via the typeclass // annotated_trace/2, which is documented in browser/declarative_debugger.m. // It would be possible to replace the front end or the back end with an // alternative implementation which also conforms to the typeclass constraints. // For example: // // - An alternative back end could generate the same tree structure in a // different way, such as via program transformation. // - An alternative front end could graphically display the generated trees // as part of a visualization tool rather than analyzing them for bugs. // // The back end decides which events should be included in the annotated trace. // The back end can be called multiple times to materialize different portions // of the annotated trace. It is the responsibility of the front end to // connect the various portions of the annotated trace together into a // complete tree. This is done in declarative_edt.m. // // Overview of the declarative debugger back end // -------------------------------------------- // // Building of a new portion of the annotated trace is started when either the // user issues a `dd' command from the procedural debugger, or the front end // requests that a new explicit subtree or supertree be built. // // Initially the trace is materialized down to a predefined depth, given by the // value of MR_edt_default_depth_limit. We retry to the CALL event // corresponding to the EXIT, FAIL or EXCP event where the `dd' command was // issued and rerun the program, collecting all events above the depth limit. // Once we get to the EXIT, FAIL or EXCP event where the `dd' command was // issued, we call the front end (in browser/declarative_debugger.m) and ask it // to analyse the generated trace. The front end then returns with one of // three possible responses: // // 1. The front end wants control to return to the procedural debugger. // This could be because the bug has been found, or the user has // exited the declarative debugging session. // 2. The front end wants the subtree of a specific node in the annotated // trace. Here the front end will tell the back end what depth it wants // the new subtree built to. // 3. The front end wants nodes generated above the currently materialized // portion of the annotated trace (referred to here as a supertree). // // In case 1 the front end may want control returned to the procedural debugger // at an event other than the event where the `dd' command was issued. If this // is the case then we perform a retry to get to an event before the desired // event and then simulate a `goto' command, so that mdb prompts the user // at the desired event. // // In case 2 the front end will supply the event number and call sequence // number of the EXIT, FAIL or EXCP event at the root of the required subtree. // The back end will then retry to a CALL event before or equal to the CALL // with the given sequence number. The program is then reexecuted and nodes // are added to the annotated trace to the depth limit specified by the // front end. // // If, while the program is being reexecuted, the call depth exceeds the depth // limit, then we record (in the array MR_edt_implicit_subtree_counters) how // many events are at each depth below the depth limit. The data collected is // used to populate a field at each CALL event that is the root of an implicit // (unmaterialized) subtree in the annotated trace. The field (called the // ideal depth) gives the maximum depth to build the subtree to so that no more // than MR_edt_desired_nodes_in_subtree nodes are materialized. The front end // passes the ideal depth to the back end when requesting a new subtree. // // In case 3 the front end will supply the event number and call sequence // number of the EXIT, FAIL or EXCP event at the root of the currently // materialized tree. As with case 2 the back end will retry to a CALL before // the given call sequence number and start reexecuting the program, however // no events are added to the annotated trace yet. When execution reaches the // CALL event matching the sequence number given by the front end, another // retry is performed to get MR_edt_default_depth_limit levels above the // currently materialized tree. Execution is then restarted from this point // and collection of events begins. Events are collected down to the depth of // the root of the previously materialized tree as illustrated in the following // diagram. // // /\ | // / \ |- Newly materialized supertree // / \ | // /\ | // / \ | // / \ |- Previously materialized tree // / \ | // / \ | #include "mercury_imp.h" #include "mercury_trace_declarative.h" #include "mercury_init.h" // for MR_trace_real #include "mercury_trace.h" #include "mercury_trace_browse.h" #include "mercury_trace_help.h" #include "mercury_trace_internal.h" #include "mercury_trace_tables.h" #include "mercury_trace_util.h" #include "mercury_trace_vars.h" #include "mercury_layout_util.h" #include "mercury_deep_copy.h" #include "mercury_stack_trace.h" #include "mercury_string.h" #include "mercury_timing.h" #include "mercury_trace_base.h" #include "mercury_runtime_util.h" #include "mdb.declarative_debugger.mh" #include "mdb.declarative_execution.mh" #include "mdbcomp.slice_and_dice.mh" #include "benchmarking.mh" #include "gc.mh" #include "std_util.mh" #include // These macros are to aid debugging of the code which constructs // the annotated trace. #ifdef MR_DEBUG_DD_BACK_END #define MR_DEBUG_DD_BACK_END_EVENT #define MR_DEBUG_DD_BACK_END_FILTER #define MR_DEBUG_DD_BACK_END_LINKS #define MR_DEBUG_DD_BACK_END_ALLOC #define MR_DEBUG_DD_BACK_END_PASSES #endif #ifdef MR_DEBUG_DD_BACK_END_EVENT #define MR_decl_checkpoint_event(event_info) \ MR_decl_checkpoint_event_imp("EVENT", MR_TRUE, event_info) #else #define MR_decl_checkpoint_event(event_info) ((void) 0) #endif #ifdef MR_DEBUG_DD_BACK_END_FILTER #define MR_decl_checkpoint_filter(event_info) \ MR_decl_checkpoint_event_imp("FILTER", MR_TRUE, event_info) #else #define MR_decl_checkpoint_filter(event_info) ((void) 0) #endif #ifdef MR_DEBUG_DD_BACK_END_LINKS #define MR_decl_checkpoint_find(location) \ MR_decl_checkpoint_loc("FIND", location) #define MR_decl_checkpoint_step(location) \ MR_decl_checkpoint_loc("STEP", location) #define MR_decl_checkpoint_match(location) \ MR_decl_checkpoint_loc("MATCH", location) #else #define MR_decl_checkpoint_find(location) ((void) 0) #define MR_decl_checkpoint_step(location) ((void) 0) #define MR_decl_checkpoint_match(location) ((void) 0) #endif #ifdef MR_DEBUG_DD_BACK_END_ALLOC #define MR_decl_checkpoint_alloc(location) \ MR_decl_checkpoint_loc("ALLOC", location) #else #define MR_decl_checkpoint_alloc(location) ((void) 0) #endif #ifdef MR_DEBUG_DD_BACK_END_PASSES #define MR_decl_checkpoint_pass(msg, print, event_info) \ MR_decl_checkpoint_event_imp(msg, print, event_info) #define MR_decl_checkpoint_subtree(final, top_seqno, depth) \ MR_decl_checkpoint_tree("SUBTREE", final, top_seqno, depth) #define MR_decl_checkpoint_supertree(final, top_seqno, depth) \ MR_decl_checkpoint_tree("SUPERTREE", final, top_seqno, depth) #else #define MR_decl_checkpoint_pass(msg, print, event_info) ((void) 0) #define MR_decl_checkpoint_subtree(final, top_seqno, depth) ((void) 0) #define MR_decl_checkpoint_supertree(final, top_seqno, depth) ((void) 0) #endif // If this macro is defined then some statistics, such as how many nodes were // added to the annotated trace in the current run and the amount of memory // consumed so far, will be printed to stderr whenever a new subtree or // supertree is built. #ifdef MR_DD_PRINT_EDT_STATS static MR_Unsigned MR_edt_stats_total_constructed_nodes = 0; static MR_Unsigned MR_edt_stats_constructed_nodes_this_time = 0; static MR_Unsigned MR_edt_stats_total_reexecutions = 0; #define MR_decl_maybe_print_edt_stats() MR_decl_print_edt_stats() #define MR_decl_maybe_inc_constructed_nodes() MR_decl_inc_constructed_nodes() #else // MR_DD_PRINT_EDT_STATS #define MR_decl_maybe_print_edt_stats() #define MR_decl_maybe_inc_constructed_nodes() #endif // MR_DD_PRINT_EDT_STATS // The declarative debugger back end is controlled by the // settings of the following variables. They are set in // MR_trace_start_decl_debug when the back end is started. // They are used by MR_trace_decl_debug to decide what action to // take for a particular trace event. static MR_DeclMode MR_decl_mode = MR_DECL_NODUMP; // If we are building a subtree then reaching this event will cause the // declarative debugger to continue its analysis. Reaching this event means // generation of the desired subtree is complete. This variable is not used // when building a new explicit supertree. static MR_Unsigned MR_edt_last_event; // If we are materializing a new subtree then MR_edt_start_seqno is the // call sequence number of the call at the top of the subtree we want to // materialize. If we are building a new supertree then MR_edt_start_seqno // is the call sequence number of the call at the top of the existing // materialized portion of the annotated trace. static MR_Unsigned MR_edt_start_seqno; // This tells MR_trace_decl_debug whether it is inside a portion of the // annotated trace that should be materialized (ignoring any depth limit). // It has opposite meanings depending on whether an explicit supertree or // subtree has been requested. When materializing a subtree, it will be true // for all nodes in the subtree. When materializing a supertree, it will be // true for all nodes outside the subtree above which a supertree was requested // (we don't want to include nodes in the subtree because that has already been // materialized). static MR_bool MR_edt_inside; // The initial event at which the `dd' command was given. This is used when // aborting diagnosis to return the user to the event where they initiated // the declarative debugging session. static MR_Unsigned MR_edt_initial_event; // The node returned to the front end once a subtree or supertree has been // generated. If a supertree is generated then the implicit root in the // new supertree that represents the existing tree is returned, otherwise // the root of the new explicit subtree is returned. static MR_TraceNode MR_edt_return_node; // The time (in milliseconds since the start of the program) when collection of // events, for the current portion of the annotated trace being materialized, // started. static MR_Unsigned MR_edt_start_time; // This global keeps track of how many ticks have been printed so far in // the progress message. static MR_Unsigned MR_edt_progress_last_tick = 0; // The first event executed during the current re-execution. static MR_Unsigned MR_edt_first_event; // The declarative debugger ignores modules that were not compiled with // the required information. However, this may result in incorrect // assumptions being made about the code, so the debugger gives a warning // if this happens. The following flag indicates whether a warning // should be printed before calling the front end. static MR_bool MR_edt_compiler_flag_warning; // When building a supertree there will be 2 retries. The first will // retry to an event before the topmost node of the currently materialized // tree and the second will be a retry from the topmost node to the root // of the new supertree. This global records whether the user said it // was safe to do the first retry across untabled IO. If they said this was // okay then there is no point asking them again for the second retry. static MR_bool MR_edt_unsafe_retry_already_asked; // If trace counts are provided for failing and passing test cases, then // we add the suspicion (an integer between 0 and MR_TRACE_DECL_MAX_SUSPICION) // for each event to the accumulator below. We then store the value of the // accumulator at CALL, EXIT, REDO, FAIL and EXCP events, which allows // the frontend to easily calculate the suspicion of any subtree in the EDT. static MR_Integer MR_edt_suspicion_accumulator; static MR_bool MR_edt_update_suspicion_accumulator = MR_FALSE; // This is used as the abstract map from node identifiers to nodes // in the data structure passed to the front end. It should be // incremented each time the data structure is destructively // updated, before being passed to Mercury code again. static MR_Unsigned MR_trace_node_store; // The front end state is stored here in between calls to it. // MR_trace_decl_ensure_init should be called before using the state. static MR_Word MR_trace_front_end_state; static void MR_trace_decl_ensure_init(void); // MR_trace_current_node always contains the last node allocated, // or NULL if the collection has just started. static MR_TraceNode MR_trace_current_node; // When in test mode, MR_trace_store_file points to an open file to // which the store should be written when built. This global is set // in MR_trace_start_decl_debug, and keeps the same value // throughout the declarative debugging session. static FILE *MR_trace_store_file; //////////////////////////////////////////////////////////////////////////// static MR_TraceNode MR_trace_decl_call(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_exit(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_redo(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_fail(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_excp(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_switch(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_disj_first(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_disj_later(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_cond(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_then(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_else(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_neg_enter(MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_neg_success( MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_decl_neg_failure( MR_EventInfo *event_info, MR_TraceNode prev); static MR_TraceNode MR_trace_matching_call(MR_TraceNode node); static MR_bool MR_trace_first_disjunct(MR_EventInfo *event_info); static MR_bool MR_trace_matching_cond(const char *path, MR_TraceNode node); static MR_bool MR_trace_matching_neg(const char *path, MR_TraceNode node); static MR_bool MR_trace_matching_disj(const char *path, MR_TraceNode node); static MR_bool MR_trace_same_construct(const char *p1, const char *p2); static MR_bool MR_trace_single_component(const char *path); static MR_Word MR_decl_make_atom_args( const MR_LabelLayout *layout, MR_Word *saved_regs, MR_Float *saved_f_regs, MR_TracePort port); static MR_Word MR_decl_atom_args(const MR_LabelLayout *layout, MR_Word *saved_regs); static const char *MR_trace_start_collecting(MR_Unsigned event, MR_Unsigned seqno, MR_Unsigned maxdepth, MR_bool create_supertree, MR_TraceCmdInfo *cmd, MR_EventInfo *event_info, MR_Code **jumpaddr); static MR_Code *MR_trace_restart_decl_debug( MR_TraceNode call_preceding, MR_Unsigned event, MR_Unsigned seqno, MR_bool create_supertree, MR_Unsigned depth_limit, MR_TraceCmdInfo *cmd, MR_EventInfo *event_info); static MR_Code *MR_decl_diagnosis(MR_TraceNode root, MR_TraceCmdInfo *cmd, MR_EventInfo *event_info, MR_bool new_tree); static MR_Code *MR_decl_go_to_selected_event(MR_Unsigned event, MR_TraceCmdInfo *cmd, MR_EventInfo *event_info); static MR_Code *MR_trace_decl_retry_supertree( MR_Unsigned max_distance, MR_EventInfo *event_info); static MR_String MR_trace_node_path(MR_TraceNode node); static MR_TracePort MR_trace_node_port(MR_TraceNode node); static MR_Unsigned MR_trace_node_seqno(MR_TraceNode node); static MR_TraceNode MR_trace_node_first_disj(MR_TraceNode node); static MR_TraceNode MR_trace_step_left_in_contour(MR_TraceNode node); static MR_TraceNode MR_trace_find_prev_contour(MR_TraceNode node); static void MR_decl_checkpoint_event_imp(const char *str, MR_bool print_event, MR_EventInfo *event_info); static void MR_decl_checkpoint_loc(const char *str, MR_TraceNode node); static void MR_decl_checkpoint_tree(const char *tree_kind, MR_Unsigned final_event, MR_Unsigned top_seqno, MR_Unsigned depth_limit); static void MR_decl_print_edt_stats(void); static void MR_decl_inc_constructed_nodes(void); static void MR_trace_edt_build_sanity_check( MR_EventInfo *event_info, const MR_ProcLayout *entry); static MR_bool MR_trace_include_event(const MR_ProcLayout *entry, MR_EventInfo *event_info, MR_Code **jumpaddr); static MR_Unsigned MR_trace_calculate_event_depth( MR_EventInfo *event_info); static void MR_trace_construct_node(MR_EventInfo *event_info); static void MR_trace_count_event_in_implicit_subtree( MR_EventInfo *event_info, MR_Unsigned depth); static void MR_trace_maybe_update_implicit_tree_ideal_depth( MR_Unsigned depth, MR_TraceNode call); static void MR_trace_finish_progress(void); static void MR_trace_init_implicit_subtree_counters( MR_Unsigned size); static void MR_trace_reset_implicit_subtree_counters(void); static void MR_trace_free_implicit_subtree_counters(void); static MR_Unsigned MR_trace_calc_implicit_subtree_ideal_depth(void); static void MR_trace_maybe_update_suspicion_accumulator( const MR_LabelLayout *label_layout); MR_bool MR_trace_decl_assume_all_io_is_tabled = MR_FALSE; MR_Unsigned MR_edt_desired_nodes_in_subtree = MR_TRACE_DESIRED_SUBTREE_NODES; MR_Unsigned MR_edt_default_depth_limit = MR_TRACE_DECL_INITIAL_DEPTH; MR_bool MR_trace_decl_debug_debugger_mode = MR_FALSE; MR_Integer MR_edt_depth; MR_Unsigned MR_edt_max_depth; MR_Integer MR_edt_implicit_subtree_depth; MR_Unsigned *MR_edt_implicit_subtree_counters; MR_Unsigned MR_edt_implicit_subtree_num_counters; MR_bool MR_edt_building_supertree; // This function is called for every traced event when building the // annotated trace. It must decide which events are included in the // annotated trace. MR_Code * MR_trace_decl_debug(MR_EventInfo *event_info) { const MR_ProcLayout *entry; MR_Unsigned node_depth; MR_Unsigned call_seqno; MR_TracePort port; MR_Code *jumpaddr; call_seqno = event_info->MR_call_seqno; port = event_info->MR_trace_port; entry = event_info->MR_event_sll->MR_sll_entry; MR_trace_edt_build_sanity_check(event_info, entry); MR_trace_maybe_update_suspicion_accumulator(event_info->MR_event_sll); MR_DECL_MAYBE_UPDATE_PROGRESS(MR_trace_event_number); if (! MR_trace_include_event(entry, event_info, &jumpaddr)) { return jumpaddr; } MR_DD_CALC_NODE_DEPTH(port, node_depth, MR_edt_depth); if (node_depth == MR_edt_max_depth && (port == MR_PORT_CALL || port == MR_PORT_REDO)) { // We are entering the top of an implicit subtree. Switch to the // event handler that processes the notes in the implicit subtree, // and reset the data structures it works with. MR_decl_checkpoint_pass("SWITCHING TO IMPLICIT SUBTREE", MR_FALSE, event_info); MR_trace_reset_implicit_subtree_counters(); MR_edt_implicit_subtree_counters[0]++; MR_edt_implicit_subtree_depth = 0; MR_selected_trace_func_ptr = MR_trace_real_decl_implicit_subtree; } MR_trace_construct_node(event_info); if (call_seqno == MR_edt_start_seqno && MR_port_is_final(port)) { MR_edt_return_node = MR_trace_current_node; } if ((!MR_edt_building_supertree && MR_trace_event_number == MR_edt_last_event) || (MR_edt_building_supertree && node_depth == 0 && MR_port_is_final(port))) { MR_trace_free_implicit_subtree_counters(); MR_decl_maybe_print_edt_stats(); if (MR_edt_progress_last_tick > 0) { MR_trace_finish_progress(); } MR_decl_checkpoint_pass("CALL FRONTEND", MR_FALSE, event_info); return MR_decl_diagnosis(MR_edt_return_node, &MR_trace_ctrl, event_info, MR_TRUE); } return NULL; } static void MR_trace_edt_build_sanity_check(MR_EventInfo *event_info, const MR_ProcLayout *entry) { if (event_info->MR_event_number > MR_edt_last_event && !MR_edt_building_supertree) { // This shouldn't ever be reached. fprintf(MR_mdb_err, "Error: missed final event.\n"); fprintf(MR_mdb_err, "event %lu\nlast event %lu\n", (unsigned long) event_info->MR_event_number, (unsigned long) MR_edt_last_event); fflush(NULL); MR_fatal_error("Aborting."); } if (!MR_PROC_LAYOUT_HAS_EXEC_TRACE(entry)) { // XXX this should be handled better. MR_fatal_error("layout has no execution tracing"); } } static MR_bool MR_trace_include_event(const MR_ProcLayout *entry, MR_EventInfo *event_info, MR_Code **jumpaddr) { // Filter out events for compiler generated procedures. if (MR_PROC_LAYOUT_IS_UCI(entry)) { *jumpaddr = NULL; return MR_FALSE; } if (entry->MR_sle_module_layout->MR_ml_suppressed_events != 0) { // We ignore events from modules that were not compiled with the // necessary information. Procedures in those modules are effectively // assumed correct, so we give the user a warning. MR_edt_compiler_flag_warning = MR_TRUE; *jumpaddr = NULL; return MR_FALSE; } // Decide if we are inside or outside the subtree or supertree that // needs to be materialized, ignoring for now any depth limit. // If we are materializing a supertree then MR_edt_inside will // be true whenever we are not in the subtree rooted at the call // corresponding to MR_edt_start_seqno. If we are materializing a // subtree then MR_edt_inside will be true whenever we are in the // subtree rooted at the call corresponding to MR_edt_start_segno. if (MR_edt_building_supertree) { if (!MR_edt_inside) { if (event_info->MR_call_seqno == MR_edt_start_seqno && MR_port_is_final(event_info->MR_trace_port)) { // We are exiting the subtree rooted at MR_edt_start_seqno. MR_decl_checkpoint_pass("SUPERTREE: SWITCHING TO INSIDE", MR_FALSE, event_info); MR_edt_inside = MR_TRUE; return MR_TRUE; } else if (event_info->MR_call_seqno == MR_edt_start_seqno && MR_port_is_entry(event_info->MR_trace_port)) { // We are entering the top of the currently materialized // portion of the annotated trace. Since we are building // a supertree, we must retry to above the current event // and start building the new portion of the annotated trace // from there. MR_decl_checkpoint_pass("SUPERTREE: RETRYING", MR_TRUE, event_info); MR_edt_inside = MR_TRUE; *jumpaddr = MR_trace_decl_retry_supertree(MR_edt_max_depth, event_info); // Reset the depth since we will now be at the top of the // supertree to be materialized. We set it to -1 since the // next call to MR_trace_decl_debug will set it to 0. MR_edt_depth = -1; return MR_FALSE; } else { // We are in an existing explicit subtree. MR_decl_checkpoint_pass("SUPERTREE: FILTER", MR_TRUE, event_info); MR_decl_checkpoint_filter(event_info); *jumpaddr = NULL; return MR_FALSE; } } else { if (event_info->MR_call_seqno == MR_edt_start_seqno) { // The port must be either CALL or REDO; we are leaving the // supertree and entering the existing explicit subtree. // We must still however add this node to the generated EDT. MR_decl_checkpoint_pass("SUPERTREE: SWITCHING TO OUTSIDE", MR_FALSE, event_info); MR_edt_inside = MR_FALSE; return MR_TRUE; } } } else { if (MR_edt_inside) { if (event_info->MR_call_seqno == MR_edt_start_seqno && MR_port_is_final(event_info->MR_trace_port)) { // We are leaving the topmost call. MR_decl_checkpoint_pass("INSIDE: SWITCHING TO OUTSIDE", MR_FALSE, event_info); MR_edt_inside = MR_FALSE; return MR_TRUE; } } else { if (event_info->MR_call_seqno == MR_edt_start_seqno) { // The port must be either CALL or REDO; // we are (re)entering the topmost call. MR_decl_checkpoint_pass("INSIDE: REENTERING TOPMOST", MR_FALSE, event_info); MR_edt_inside = MR_TRUE; MR_edt_depth = -1; return MR_TRUE; } else { // Ignore this event -- it is outside the topmost call. MR_decl_checkpoint_pass("INSIDE: FILTERING", MR_TRUE, event_info); MR_decl_checkpoint_filter(event_info); *jumpaddr = NULL; return MR_FALSE; } } } return MR_TRUE; } static void MR_trace_construct_node(MR_EventInfo *event_info) { MR_TraceNode trace; trace = MR_trace_current_node; MR_debug_enabled = MR_FALSE; MR_update_trace_func_enabled(); MR_decl_checkpoint_event(event_info); switch (event_info->MR_trace_port) { case MR_PORT_CALL: trace = MR_trace_decl_call(event_info, trace); break; case MR_PORT_EXIT: trace = MR_trace_decl_exit(event_info, trace); break; case MR_PORT_REDO: trace = MR_trace_decl_redo(event_info, trace); break; case MR_PORT_FAIL: trace = MR_trace_decl_fail(event_info, trace); break; case MR_PORT_DISJ_FIRST: trace = MR_trace_decl_disj_first(event_info, trace); break; case MR_PORT_DISJ_LATER: trace = MR_trace_decl_disj_later(event_info, trace); break; case MR_PORT_SWITCH: trace = MR_trace_decl_switch(event_info, trace); break; case MR_PORT_COND: trace = MR_trace_decl_cond(event_info, trace); break; case MR_PORT_THEN: trace = MR_trace_decl_then(event_info, trace); break; case MR_PORT_ELSE: trace = MR_trace_decl_else(event_info, trace); break; case MR_PORT_NEG_ENTER: trace = MR_trace_decl_neg_enter(event_info, trace); break; case MR_PORT_NEG_SUCCESS: trace = MR_trace_decl_neg_success(event_info, trace); break; case MR_PORT_NEG_FAILURE: trace = MR_trace_decl_neg_failure(event_info, trace); break; case MR_PORT_EXCEPTION: trace = MR_trace_decl_excp(event_info, trace); break; case MR_PORT_USER: // do nothing break; case MR_PORT_TAILREC_CALL: default: MR_fatal_error("MR_trace_construct_node: unknown port"); } MR_decl_checkpoint_alloc(trace); MR_decl_maybe_inc_constructed_nodes(); MR_debug_enabled = MR_TRUE; MR_update_trace_func_enabled(); MR_trace_current_node = trace; } // Retry max_distance if there are that many ancestors, otherwise // retry as far as possible. static MR_Code * MR_trace_decl_retry_supertree(MR_Unsigned max_distance, MR_EventInfo *event_info) { MR_Code *jumpaddr; int retry_distance; const char *problem; MR_RetryResult retry_result; MR_bool unsafe_retry; MR_RetryAcrossIo retry_mode; if (max_distance >= event_info->MR_call_depth) { retry_distance = event_info->MR_call_depth - 1; } else { retry_distance = max_distance; } // If the user was already asked if they want to do an unsafe retry // while building this supertree, then don't ask them again. if (MR_edt_unsafe_retry_already_asked) { retry_mode = MR_RETRY_IO_FORCE; } else { retry_mode = MR_RETRY_IO_INTERACTIVE; } retry_result = MR_trace_retry(event_info, retry_distance, retry_mode, MR_trace_decl_assume_all_io_is_tabled, MR_DECL_UNTABLED_IO_RETRY_MESSAGE, &unsafe_retry, &problem, MR_mdb_in, MR_mdb_out, &jumpaddr); if (retry_result != MR_RETRY_OK_DIRECT) { if (retry_result == MR_RETRY_ERROR) { MR_selected_trace_func_ptr = MR_trace_real; fflush(MR_mdb_out); fprintf(MR_mdb_err, "mdb: retry aborted in MR_trace_decl_retry_supertree: %s\n", problem); return NULL; } else { fflush(MR_mdb_out); fprintf(MR_mdb_err, "mdb: internal error in " "MR_trace_decl_retry_supertree: direct retry impossible\n"); return NULL; } } return jumpaddr; } static MR_TraceNode MR_trace_decl_call(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_Word atom_args; MR_bool at_depth_limit; const MR_LabelLayout *event_label_layout; const MR_ProcLayout *event_proc_layout; const MR_LabelLayout *return_label_layout; MR_StackWalkStepResult result; MR_ConstString problem; MR_Word *base_sp; MR_Word *base_curfr; MR_Word maybe_return_label; MR_Unsigned reused_frames; if (MR_edt_depth == MR_edt_max_depth) { at_depth_limit = MR_TRUE; } else { at_depth_limit = MR_FALSE; } event_label_layout = event_info->MR_event_sll; event_proc_layout = event_label_layout->MR_sll_entry; atom_args = MR_decl_make_atom_args(event_label_layout, event_info->MR_saved_regs, event_info->MR_saved_f_regs, MR_PORT_CALL); base_sp = MR_saved_sp(event_info->MR_saved_regs); base_curfr = MR_saved_curfr(event_info->MR_saved_regs); result = MR_stack_walk_step(event_proc_layout, &return_label_layout, &base_sp, &base_curfr, &reused_frames, &problem); assert(reused_frames == 0); // return_label_layout may be NULL even if result is MR_STEP_OK, // if the current event is inside the code of main/2. MR_TRACE_CALL_MERCURY( if (result == MR_STEP_OK && return_label_layout != NULL) { maybe_return_label = MR_DD_make_yes_maybe_label(return_label_layout); } else { maybe_return_label = MR_DD_make_no_maybe_label(); } node = (MR_TraceNode) MR_DD_construct_call_node((MR_Word) prev, atom_args, (MR_Word) event_info->MR_call_seqno, (MR_Word) event_info->MR_event_number, (MR_Word) at_depth_limit, maybe_return_label, event_label_layout, MR_io_tabling_counter, MR_edt_suspicion_accumulator); ); return node; } static MR_TraceNode MR_trace_decl_exit(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TraceNode call; MR_Word last_interface; MR_Word atom_args; atom_args = MR_decl_make_atom_args(event_info->MR_event_sll, event_info->MR_saved_regs, event_info->MR_saved_f_regs, MR_PORT_EXIT); call = MR_trace_matching_call(prev); MR_decl_checkpoint_match(call); MR_TRACE_CALL_MERCURY( // We need to add 1 to MR_edt_depth since this is an EXIT event, // so 1 should already have been subtracted from MR_edt_depth // in MR_trace_calculate_event_depth. MR_trace_maybe_update_implicit_tree_ideal_depth( MR_edt_depth + 1, call); last_interface = MR_DD_call_node_get_last_interface((MR_Word) call); node = (MR_TraceNode) MR_DD_construct_exit_node((MR_Word) prev, (MR_Word) call, last_interface, atom_args, (MR_Word) event_info->MR_event_number, event_info->MR_event_sll, MR_io_tabling_counter, MR_edt_suspicion_accumulator); MR_DD_call_node_set_last_interface((MR_Word) call, (MR_Word) node); ); return node; } static MR_TraceNode MR_trace_decl_redo(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TraceNode call; MR_TraceNode next; MR_Word last_interface; // Search through previous contour for a matching EXIT event. next = MR_trace_find_prev_contour(prev); while (MR_trace_node_port(next) != MR_PORT_EXIT || MR_trace_node_seqno(next) != event_info->MR_call_seqno) { next = MR_trace_step_left_in_contour(next); } MR_decl_checkpoint_match(next); MR_TRACE_CALL_MERCURY( MR_trace_node_store++; if (!MR_DD_trace_node_call(MR_trace_node_store, (MR_Word) next, (MR_Word *) &call)) { MR_fatal_error("MR_trace_decl_redo: no matching EXIT"); } last_interface = MR_DD_call_node_get_last_interface((MR_Word) call); node = (MR_TraceNode) MR_DD_construct_redo_node((MR_Word) prev, last_interface, (MR_Word) event_info->MR_event_number, event_info->MR_event_sll, MR_edt_suspicion_accumulator); MR_DD_call_node_set_last_interface((MR_Word) call, (MR_Word) node); ); return node; } static MR_TraceNode MR_trace_decl_fail(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TraceNode next; MR_TraceNode call; MR_Word redo; if (MR_trace_node_port(prev) == MR_PORT_CALL) { // We are already at the corresponding call, so there // is no need to search for it. call = prev; } else { next = MR_trace_find_prev_contour(prev); call = MR_trace_matching_call(next); } MR_decl_checkpoint_match(call); MR_TRACE_CALL_MERCURY( // We need to add 1 to MR_edt_depth since this is a FAIL event, // so 1 should already have been subtracted from MR_edt_depth // in MR_trace_calculate_event_depth. MR_trace_maybe_update_implicit_tree_ideal_depth( MR_edt_depth + 1, call); redo = MR_DD_call_node_get_last_interface((MR_Word) call); node = (MR_TraceNode) MR_DD_construct_fail_node((MR_Word) prev, (MR_Word) call, (MR_Word) redo, (MR_Word) event_info->MR_event_number, event_info->MR_event_sll, MR_edt_suspicion_accumulator); MR_DD_call_node_set_last_interface((MR_Word) call, (MR_Word) node); ); return node; } static MR_TraceNode MR_trace_decl_excp(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TraceNode call; MR_Word last_interface; call = MR_trace_matching_call(prev); MR_decl_checkpoint_match(call); MR_TRACE_CALL_MERCURY( // We need to add 1 to MR_edt_depth since this is an EXCP event, // so 1 should already have been subtracted from MR_edt_depth // in MR_trace_calculate_event_depth. MR_trace_maybe_update_implicit_tree_ideal_depth(MR_edt_depth + 1, call); last_interface = MR_DD_call_node_get_last_interface((MR_Word) call); MR_DD_construct_excp_node((MR_Word) prev, (MR_Word) call, last_interface, MR_trace_get_exception_value(), (MR_Word) event_info->MR_event_number, event_info->MR_event_sll, MR_edt_suspicion_accumulator, &node); MR_DD_call_node_set_last_interface((MR_Word) call, (MR_Word) node); ); return node; } static MR_TraceNode MR_trace_decl_cond(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TRACE_CALL_MERCURY( node = (MR_TraceNode) MR_DD_construct_cond_node((MR_Word) prev, event_info->MR_event_sll); ); return node; } static MR_TraceNode MR_trace_decl_then(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TraceNode next; MR_TraceNode cond; const char *path = event_info->MR_event_path; // Search through current contour for a matching COND event. next = prev; while (!MR_trace_matching_cond(path, next)) { next = MR_trace_step_left_in_contour(next); } cond = next; MR_decl_checkpoint_match(cond); MR_TRACE_CALL_MERCURY( MR_DD_cond_node_set_status((MR_Word) cond, MR_TRACE_STATUS_SUCCEEDED); node = (MR_TraceNode) MR_DD_construct_then_node((MR_Word) prev, (MR_Word) cond, event_info->MR_event_sll); ); return node; } static MR_TraceNode MR_trace_decl_else(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TraceNode cond; const char *path = event_info->MR_event_path; // Search through previous contour for a matching COND event. if (MR_trace_matching_cond(path, prev)) { cond = prev; } else { MR_TraceNode next; next = prev; while (!MR_trace_matching_cond(path, next)) { next = MR_trace_step_left_in_contour(next); } cond = next; } MR_decl_checkpoint_match(cond); MR_TRACE_CALL_MERCURY( MR_DD_cond_node_set_status((MR_Word) cond, MR_TRACE_STATUS_FAILED); node = (MR_TraceNode) MR_DD_construct_else_node((MR_Word) prev, (MR_Word) cond, event_info->MR_event_sll); ); return node; } static MR_TraceNode MR_trace_decl_neg_enter(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TRACE_CALL_MERCURY( node = (MR_TraceNode) MR_DD_construct_neg_node((MR_Word) prev, event_info->MR_event_sll); ); return node; } static MR_TraceNode MR_trace_decl_neg_success(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TraceNode nege; const char *path = event_info->MR_event_path; // Search through previous contour for a matching NEGE event. if (MR_trace_matching_neg(path, prev)) { nege = MR_trace_current_node; } else { MR_TraceNode next; next = prev; while (!MR_trace_matching_neg(path, next)) { next = MR_trace_step_left_in_contour(next); } nege = next; } MR_decl_checkpoint_match(nege); MR_TRACE_CALL_MERCURY( MR_DD_neg_node_set_status((MR_Word) nege, MR_TRACE_STATUS_SUCCEEDED); node = (MR_TraceNode) MR_DD_construct_neg_succ_node((MR_Word) prev, (MR_Word) nege, event_info->MR_event_sll); ); return node; } static MR_TraceNode MR_trace_decl_neg_failure(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TraceNode next; // Search through current contour for a matching NEGE event. next = prev; while (!MR_trace_matching_neg(event_info->MR_event_path, next)) { next = MR_trace_step_left_in_contour(next); } MR_decl_checkpoint_match(next); MR_TRACE_CALL_MERCURY( MR_DD_neg_node_set_status((MR_Word) next, MR_TRACE_STATUS_FAILED); node = (MR_TraceNode) MR_DD_construct_neg_fail_node((MR_Word) prev, (MR_Word) next, event_info->MR_event_sll); ); return node; } static MR_TraceNode MR_trace_decl_switch(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TRACE_CALL_MERCURY( node = (MR_TraceNode) MR_DD_construct_switch_node((MR_Word) prev, event_info->MR_event_sll); ); return node; } static MR_TraceNode MR_trace_decl_disj_first(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; MR_TRACE_CALL_MERCURY( node = (MR_TraceNode) MR_DD_construct_first_disj_node( (MR_Word) prev, event_info->MR_event_sll); ); return node; } static MR_TraceNode MR_trace_decl_disj_later(MR_EventInfo *event_info, MR_TraceNode prev) { MR_TraceNode node; const char *path; MR_TraceNode next; MR_TraceNode first; path = event_info->MR_event_path; // Search through previous nodes for a matching DISJ event. next = MR_trace_find_prev_contour(prev); while (!MR_trace_matching_disj(path, next)) { next = MR_trace_step_left_in_contour(next); } MR_decl_checkpoint_match(next); // Find the first disj event of this disjunction. first = MR_trace_node_first_disj(next); if (first == (MR_TraceNode) NULL) { first = next; } MR_TRACE_CALL_MERCURY( node = (MR_TraceNode) MR_DD_construct_later_disj_node( MR_trace_node_store, (MR_Word) prev, event_info->MR_event_sll, (MR_Word) first); ); return node; } static MR_TraceNode MR_trace_matching_call(MR_TraceNode node) { MR_TraceNode next; // Search through contour for any CALL event. Since there is only one // CALL event which can be reached, we assume it is the correct one. next = node; while (MR_trace_node_port(next) != MR_PORT_CALL) { next = MR_trace_step_left_in_contour(next); } return next; } static MR_bool MR_trace_first_disjunct(MR_EventInfo *event_info) { const char *path; // Return MR_TRUE iff the last component of the path is "d1;". path = event_info->MR_event_path; while (*path) { if (MR_string_equal(path, "d1;")) { return MR_TRUE; } path++; } return MR_FALSE; } static MR_bool MR_trace_matching_cond(const char *path, MR_TraceNode node) { MR_TracePort port; const char *node_path; MR_TRACE_CALL_MERCURY( port = (MR_TracePort) MR_DD_trace_node_port(node); ); if (port != MR_PORT_COND) { return MR_FALSE; } node_path = MR_trace_node_path(node); return MR_trace_same_construct(path, node_path); } static MR_bool MR_trace_matching_neg(const char *path, MR_TraceNode node) { MR_TracePort port; const char *node_path; MR_TRACE_CALL_MERCURY( port = (MR_TracePort) MR_DD_trace_node_port(node); ); if (port != MR_PORT_NEG_ENTER) { return MR_FALSE; } node_path = MR_trace_node_path(node); return MR_trace_same_construct(path, node_path); } static MR_bool MR_trace_matching_disj(const char *path, MR_TraceNode node) { MR_TracePort port; const char *node_path; MR_TRACE_CALL_MERCURY( port = (MR_TracePort) MR_DD_trace_node_port(node); ); if (port == MR_PORT_DISJ_FIRST || port == MR_PORT_DISJ_LATER) { node_path = MR_trace_node_path(node); return MR_trace_same_construct(path, node_path); } else { return MR_FALSE; } } static MR_bool MR_trace_same_construct(const char *p1, const char *p2) { // Checks if the two arguments represent goals in the same construct. // If both strings are identical up to the last component, return MR_TRUE, // otherwise return MR_FALSE. If the arguments point to identical strings, // return MR_TRUE. // // See the comment on the MR_sll_path field in // runtime/mercury_stack_layout.h for a possible way to do this test // in linear time, and why we currently do not do it that way. while (*p1 == *p2) { if (*p1 == '\0' && *p2 == '\0') { return MR_TRUE; // They are identical. } if (*p1 == '\0' || *p2 == '\0') { return MR_FALSE; // Different number of elements. } p1++; p2++; } // If there is exactly one component left in each string, then // the goal paths match, otherwise they don't. return MR_trace_single_component(p1) && MR_trace_single_component(p2); } static MR_bool MR_trace_single_component(const char *path) { while (*path != ';') { if (*path == '\0') { return MR_FALSE; } path++; } path++; return (*path == '\0'); } static MR_Word MR_decl_make_atom_args(const MR_LabelLayout *layout, MR_Word *saved_regs, MR_Float *saved_f_regs, MR_TracePort port) { MR_PredFunc pred_or_func; int arity; MR_Word atom_args; int hv; // Any head variable. int num_added_args; const MR_ProcLayout *entry; entry = layout->MR_sll_entry; MR_trace_init_point_vars(layout, saved_regs, saved_f_regs, port, MR_TRUE); MR_proc_id_arity_addedargs_predfunc(entry, &arity, &num_added_args, &pred_or_func); MR_TRACE_CALL_MERCURY( atom_args = MR_DD_init_trace_atom_args(); ); for (hv = entry->MR_sle_num_head_vars - 1; hv >= 0; hv--) { int hlds_num; MR_Word arg; MR_TypeInfo arg_type; MR_Word arg_value; MR_bool is_prog_visible_headvar; const char *problem; hlds_num = entry->MR_sle_head_var_nums[hv]; is_prog_visible_headvar = hv >= num_added_args ? MR_TRUE : MR_FALSE; problem = MR_trace_return_hlds_var_info(hlds_num, &arg_type, &arg_value); if (problem != NULL) { // This head variable is not live at this port. MR_TRACE_CALL_MERCURY( MR_DD_add_trace_atom_arg_no_value(hlds_num, is_prog_visible_headvar, atom_args, &atom_args); ); } else { MR_TRACE_USE_HP( MR_new_univ_on_hp(arg, arg_type, arg_value); ); MR_TRACE_CALL_MERCURY( MR_DD_add_trace_atom_arg_value(hlds_num, is_prog_visible_headvar, arg, atom_args, &atom_args); ); } } return atom_args; } static void MR_trace_decl_ensure_init(void) { static MR_bool done = MR_FALSE; static MercuryFile mdb_in; static MercuryFile mdb_out; MR_mercuryfile_init(MR_mdb_in, 1, &mdb_in); MR_mercuryfile_init(MR_mdb_out, 1, &mdb_out); if (! done) { MR_trace_browse_ensure_init(); MR_TRACE_CALL_MERCURY( MR_trace_node_store = 0; MR_DD_decl_diagnosis_state_init( MR_wrap_input_stream(&mdb_in), MR_wrap_output_stream(&mdb_out), MR_trace_browser_persistent_state, MR_trace_help_system, &MR_trace_front_end_state); ); done = MR_TRUE; } } void MR_trace_decl_session_init() { MR_trace_decl_ensure_init(); MR_TRACE_CALL_MERCURY( MR_DD_decl_session_init( MR_trace_front_end_state, &MR_trace_front_end_state); ); } void MR_trace_decl_set_fallback_search_mode(MR_DeclSearchMode search_mode) { MR_trace_decl_ensure_init(); MR_TRACE_CALL_MERCURY( MR_DD_decl_set_fallback_search_mode(MR_trace_node_store, search_mode, MR_trace_front_end_state, &MR_trace_front_end_state); ); } void MR_trace_decl_reset_knowledge_base() { MR_trace_decl_ensure_init(); MR_TRACE_CALL_MERCURY( MR_DD_decl_reset_knowledge_base( MR_trace_front_end_state, &MR_trace_front_end_state); ); } void MR_trace_decl_set_testing_flag(MR_bool testing) { MR_trace_decl_ensure_init(); if (testing) { MR_TRACE_CALL_MERCURY( MR_DD_decl_set_diagnoser_to_testing( MR_trace_front_end_state, &MR_trace_front_end_state); ); } else { MR_TRACE_CALL_MERCURY( MR_DD_decl_set_diagnoser_to_not_testing( MR_trace_front_end_state, &MR_trace_front_end_state); ); } } MR_bool MR_trace_is_valid_search_mode_string(const char *search_mode_string, MR_DeclSearchMode *search_mode, MR_bool *search_mode_requires_trace_counts) { MR_bool is_valid; *search_mode_requires_trace_counts = MR_FALSE; MR_TRACE_CALL_MERCURY( if (MR_streq(search_mode_string, "top_down") || MR_streq(search_mode_string, "top-down") || MR_streq(search_mode_string, "td")) { *search_mode = MR_DD_decl_top_down_search_mode(); is_valid = MR_TRUE; } else if (MR_streq(search_mode_string, "divide_and_query") || MR_streq(search_mode_string, "divide-and-query") || MR_streq(search_mode_string, "dq")) { *search_mode = MR_DD_decl_divide_and_query_search_mode(); is_valid = MR_TRUE; } else if (MR_streq(search_mode_string, "suspicion_divide_and_query") || MR_streq(search_mode_string, "suspicion-divide-and-query") || MR_streq(search_mode_string, "sdq")) { *search_mode = MR_DD_decl_suspicion_divide_and_query_search_mode(); is_valid = MR_TRUE; *search_mode_requires_trace_counts = MR_TRUE; } else { is_valid = MR_FALSE; } ); return is_valid; } MR_DeclSearchMode MR_trace_get_default_search_mode(void) { MR_DeclSearchMode search_mode; MR_TRACE_CALL_MERCURY( search_mode = MR_DD_decl_top_down_search_mode(); ); return search_mode; } void MR_decl_add_trusted_module(const char *module_name) { MR_String aligned_module_name; MR_TRACE_USE_HP( MR_make_aligned_string(aligned_module_name, (MR_String) module_name); ); MR_trace_decl_ensure_init(); MR_TRACE_CALL_MERCURY( MR_DD_decl_add_trusted_module((MR_String)aligned_module_name, MR_trace_front_end_state, &MR_trace_front_end_state); ); } void MR_decl_add_trusted_pred_or_func(const MR_ProcLayout *entry) { MR_trace_decl_ensure_init(); MR_TRACE_CALL_MERCURY( MR_DD_decl_add_trusted_pred_or_func(entry, MR_trace_front_end_state, &MR_trace_front_end_state); ); } void MR_decl_trust_standard_library(void) { MR_trace_decl_ensure_init(); MR_TRACE_CALL_MERCURY( MR_DD_decl_trust_standard_library( MR_trace_front_end_state, &MR_trace_front_end_state); ); } MR_bool MR_decl_remove_trusted(MR_Integer n) { MR_bool success; MR_Word new_diagnoser; MR_trace_decl_ensure_init(); MR_TRACE_CALL_MERCURY( success = MR_DD_decl_remove_trusted(n, MR_trace_front_end_state, &new_diagnoser); ); if (success) { MR_trace_front_end_state = new_diagnoser; } return success; } void MR_decl_print_all_trusted(FILE *fp, MR_bool mdb_command_format) { MR_String trusted_list; MR_trace_decl_ensure_init(); MR_TRACE_CALL_MERCURY( MR_DD_decl_get_trusted_list(MR_trace_front_end_state, mdb_command_format, &trusted_list); ); fputs(trusted_list, fp); } MR_bool MR_trace_start_decl_debug(MR_DeclMode mode, const char *outfile, MR_bool new_session, MR_TraceCmdInfo *cmd, MR_EventInfo *event_info, MR_Code **jumpaddr) { const MR_ProcLayout *entry; FILE *out; const char *message; MR_TraceLevel trace_level; static MR_bool first_time = MR_TRUE; MR_edt_initial_event = event_info->MR_event_number; // If it was requested that the previous session be resumed and // there was a previous dd session, then there is no need to // build a new annotated trace. if (!new_session && !first_time) { MR_decl_mode = mode; MR_selected_trace_func_ptr = MR_trace_real_decl; *jumpaddr = MR_decl_diagnosis((MR_TraceNode) NULL, cmd, event_info, MR_FALSE); return MR_TRUE; } MR_edt_return_node = (MR_TraceNode) NULL; if (!MR_port_is_final(event_info->MR_trace_port)) { fflush(MR_mdb_out); fprintf(MR_mdb_err, "mdb: declarative debugging is only available" " from EXIT, FAIL or EXCP events.\n"); return MR_FALSE; } entry = event_info->MR_event_sll->MR_sll_entry; if (!MR_PROC_LAYOUT_HAS_EXEC_TRACE(entry)) { fflush(MR_mdb_out); fprintf(MR_mdb_err, "mdb: cannot start declarative debugging, " "because this procedure was not\n" "compiled with execution tracing enabled.\n"); return MR_FALSE; } if (MR_PROC_LAYOUT_IS_UCI(entry)) { fflush(MR_mdb_out); fprintf(MR_mdb_err, "mdb: cannot start declarative debugging " "at compiler generated procedures.\n"); return MR_FALSE; } trace_level = entry->MR_sle_module_layout->MR_ml_trace_level; if (trace_level != MR_TRACE_LEVEL_DEEP && trace_level != MR_TRACE_LEVEL_DECL_REP) { fflush(MR_mdb_out); fprintf(MR_mdb_err, "mdb: cannot start declarative debugging, " "because this procedure was not\n" "compiled with trace level `deep' or `rep'.\n"); return MR_FALSE; } if (entry->MR_sle_module_layout->MR_ml_suppressed_events != 0) { fflush(MR_mdb_out); fprintf(MR_mdb_err, "mdb: cannot start declarative debugging, " "because some event types were\n" "suppressed when this procedure was compiled.\n"); return MR_FALSE; } if (mode == MR_DECL_DUMP) { out = fopen(outfile, "w"); if (out == NULL) { char errbuf[MR_STRERROR_BUF_SIZE]; fflush(MR_mdb_out); fprintf(MR_mdb_err, "mdb: cannot open file `%s' for output: %s.\n", outfile, MR_strerror(errno, errbuf, sizeof(errbuf))); return MR_FALSE; } else { MR_trace_store_file = out; } } MR_decl_mode = mode; MR_selected_trace_func_ptr = MR_trace_real_decl; MR_trace_decl_ensure_init(); MR_trace_current_node = (MR_TraceNode) NULL; message = MR_trace_start_collecting(event_info->MR_event_number, event_info->MR_call_seqno, MR_edt_default_depth_limit, MR_FALSE, cmd, event_info, jumpaddr); if (message == NULL) { first_time = MR_FALSE; return MR_TRUE; } else { fflush(MR_mdb_out); fprintf(MR_mdb_err, "mdb: failed to start collecting events:\n%s\n", message); return MR_FALSE; } } static MR_Code * MR_trace_restart_decl_debug(MR_TraceNode call_preceding, MR_Unsigned event, MR_Unsigned seqno, MR_bool create_supertree, MR_Unsigned depth_limit, MR_TraceCmdInfo *cmd, MR_EventInfo *event_info) { const char *message; MR_Code *jumpaddr; MR_edt_return_node = (MR_TraceNode) NULL; // Set this to the preceding node, so the new explicit tree's parent is // resolved correctly. MR_trace_current_node = call_preceding; message = MR_trace_start_collecting(event, seqno, depth_limit, create_supertree, cmd, event_info, &jumpaddr); if (message != NULL) { fflush(MR_mdb_out); fprintf(MR_mdb_err, "mdb: diagnosis aborted:\n%s\n", message); MR_selected_trace_func_ptr = MR_trace_real; MR_debug_enabled = MR_TRUE; MR_update_trace_func_enabled(); return MR_trace_event_internal(cmd, MR_TRUE, NULL, event_info, NULL); } return jumpaddr; } static const char * MR_trace_start_collecting(MR_Unsigned event, MR_Unsigned seqno, MR_Unsigned maxdepth, MR_bool create_supertree, MR_TraceCmdInfo *cmd, MR_EventInfo *event_info, MR_Code **jumpaddr) { const char *problem; MR_RetryResult retry_result; int retry_distance; MR_bool unsafe_retry; int counter_depth; MR_edt_unsafe_retry_already_asked = MR_FALSE; // We need to do a retry if the current event is greater than the // call event number corresponding to seqno. Since we don't have the // call event number for seqno (`event' is the final event number, // not the call event number), we do a retry if: // // a) The call sequence number of the current call is greater than // or equal to seqno, or // b) The current event number is greater than the final event // for seqno. // // Case b) covers the situation where the current event is after the // final event for seqno and case a) covers the case where the current // event is greater than or equal to the call event for seqno and less // than or equal to the final event for seqno. This means we will do // a retry if the call event for seqno is equal to the current event // but that is not a problem since the retry will be a no-op. if (event_info->MR_call_seqno >= seqno || event_info->MR_event_number > event) { retry_distance = MR_find_first_call_less_eq_seq_or_event( MR_FIND_FIRST_CALL_BEFORE_SEQ, seqno, event_info->MR_event_sll, MR_saved_sp(event_info->MR_saved_regs), MR_saved_curfr(event_info->MR_saved_regs), &problem); if (retry_distance < 0) { return problem; } retry_result = MR_trace_retry(event_info, retry_distance, MR_RETRY_IO_INTERACTIVE, MR_trace_decl_assume_all_io_is_tabled, MR_DECL_UNTABLED_IO_RETRY_MESSAGE, &unsafe_retry, &problem, MR_mdb_in, MR_mdb_out, jumpaddr); if (retry_result != MR_RETRY_OK_DIRECT) { if (retry_result == MR_RETRY_ERROR) { return problem; } else { return "internal error: direct retry impossible"; } } if (unsafe_retry) { MR_edt_unsafe_retry_already_asked = MR_TRUE; } } else { *jumpaddr = NULL; } // Clear any warnings. MR_edt_compiler_flag_warning = MR_FALSE; // Start collecting the trace from the desired call, with the // desired depth bound. MR_edt_last_event = event; MR_edt_start_seqno = seqno; MR_edt_max_depth = maxdepth; MR_edt_inside = MR_FALSE; MR_edt_building_supertree = create_supertree; MR_edt_suspicion_accumulator = 0; MR_edt_start_time = MR_get_user_cpu_milliseconds(); MR_edt_first_event = event_info->MR_event_number; // The deepest we will build any implicit subtree to will be // the number of desired nodes divided by two, since the minimum // number of events at each depth will be 2 (the CALL and EXIT). counter_depth = (MR_edt_desired_nodes_in_subtree / 2) + 1; MR_trace_init_implicit_subtree_counters(counter_depth); // Single step through every event. cmd->MR_trace_cmd = MR_CMD_STEP; cmd->MR_trace_strict = MR_TRUE; cmd->MR_trace_print_level_specified = MR_TRUE; cmd->MR_trace_print_level = MR_PRINT_LEVEL_NONE; cmd->MR_trace_must_check = MR_FALSE; MR_debug_enabled = MR_TRUE; MR_update_trace_func_enabled(); return NULL; } static MR_Code * MR_decl_diagnosis(MR_TraceNode root, MR_TraceCmdInfo *cmd, MR_EventInfo *event_info, MR_bool new_tree) { MR_Word response; MR_bool bug_found; MR_bool symptom_found; MR_bool no_bug_found; MR_bool require_subtree; MR_bool require_supertree; MR_Unsigned bug_event; MR_Unsigned symptom_event; MR_Unsigned final_event; MR_Unsigned topmost_seqno; MR_TraceNode call_preceding; MercuryFile stream; MR_Integer requested_subtree_depth; if (MR_edt_compiler_flag_warning) { fflush(MR_mdb_out); fprintf(MR_mdb_err, "Warning: some modules were compiled with" " a trace level lower than `decl'.\n" "This may result in calls being omitted from" " the debugging tree.\n"); } if (MR_decl_mode == MR_DECL_DUMP && new_tree) { MR_mercuryfile_init(MR_trace_store_file, 1, &stream); MR_TRACE_CALL_MERCURY( MR_DD_save_trace(MR_wrap_output_stream(&stream), MR_trace_node_store, root); ); fclose(MR_trace_store_file); MR_selected_trace_func_ptr = MR_trace_real; MR_debug_enabled = MR_TRUE; MR_update_trace_func_enabled(); return MR_trace_event_internal(cmd, MR_TRUE, NULL, event_info, NULL); } if (MR_trace_decl_debug_debugger_mode) { // This is a quick and dirty way to debug the front end. MR_debug_enabled = MR_TRUE; MR_update_trace_func_enabled(); MR_selected_trace_func_ptr = MR_trace_real; } else { MR_debug_enabled = MR_FALSE; MR_update_trace_func_enabled(); MR_selected_trace_func_ptr = MR_trace_real_decl; } MR_TRACE_CALL_MERCURY( if (new_tree) { MR_DD_decl_diagnosis_new_tree(MR_trace_node_store, root, &response, MR_trace_front_end_state, &MR_trace_front_end_state, MR_trace_browser_persistent_state, &MR_trace_browser_persistent_state ); } else { MR_DD_decl_diagnosis_resume_previous(MR_trace_node_store, &response, MR_trace_front_end_state, &MR_trace_front_end_state, MR_trace_browser_persistent_state, &MR_trace_browser_persistent_state ); } bug_found = MR_DD_diagnoser_bug_found(response, (MR_Integer *) &bug_event); symptom_found = MR_DD_diagnoser_symptom_found(response, (MR_Integer *) &symptom_event); no_bug_found = MR_DD_diagnoser_no_bug_found(response); require_subtree = MR_DD_diagnoser_require_subtree(response, (MR_Integer *) &final_event, (MR_Integer *) &topmost_seqno, (MR_TraceNode *) &call_preceding, (MR_Integer *) &requested_subtree_depth); require_supertree = MR_DD_diagnoser_require_supertree(response, (MR_Integer *) &final_event, (MR_Integer *) &topmost_seqno); ); // Turn off interactive debugging after the diagnosis in case // a new explicit subtree or supertree needs to be constructed. MR_debug_enabled = MR_FALSE; MR_update_trace_func_enabled(); MR_selected_trace_func_ptr = MR_trace_real_decl; if (bug_found) { return MR_decl_go_to_selected_event(bug_event, cmd, event_info); } if (symptom_found) { return MR_decl_go_to_selected_event(symptom_event, cmd, event_info); } if (no_bug_found) { // No bug found. Return to the procedural debugger at the event // where the `dd' command was initially given. return MR_decl_go_to_selected_event(MR_edt_initial_event, cmd, event_info); } if (require_subtree) { // The front end requires a subtree to be made explicit. // Restart the declarative debugger with the appropriate depth limit. MR_decl_checkpoint_subtree(final_event, topmost_seqno, requested_subtree_depth); return MR_trace_restart_decl_debug(call_preceding, final_event, topmost_seqno, MR_FALSE, requested_subtree_depth, cmd, event_info); } if (require_supertree) { // Front end requires a supertree to be made explicit. MR_decl_checkpoint_supertree(final_event, topmost_seqno, MR_edt_default_depth_limit); return MR_trace_restart_decl_debug((MR_TraceNode) NULL, final_event, topmost_seqno, MR_TRUE, MR_edt_default_depth_limit, cmd, event_info); } // We shouldn't ever get here. MR_fatal_error("unknown diagnoser response"); } static MR_Code * MR_decl_go_to_selected_event(MR_Unsigned event, MR_TraceCmdInfo *cmd, MR_EventInfo *event_info) { const char *problem; MR_RetryResult retry_result; MR_Code *jumpaddr; int ancestor_level; MR_bool unsafe_retry; // Initialise this to avoid warnings that it might be used uninitialised. retry_result = MR_RETRY_OK_DIRECT; // We only need to do a retry if the event number we want to be at // is less than or equal to the current event number (we need to do a retry // if the event numbers are equal, because MR_trace_real will increment // the current event number, so the next event displayed by mdb // will be the current event + 1. if (event <= event_info->MR_event_number) { ancestor_level = MR_find_first_call_less_eq_seq_or_event( MR_FIND_FIRST_CALL_BEFORE_EVENT, event, event_info->MR_event_sll, MR_saved_sp(event_info->MR_saved_regs), MR_saved_curfr(event_info->MR_saved_regs), &problem); if (ancestor_level >= 0) { // Perform a retry to get to before the given event. Then set // the command to go to the given event and return to interactive // mode. #ifdef MR_DEBUG_RETRY MR_print_stack_regs(stdout, event_info->MR_saved_regs); MR_print_succip_reg(stdout, event_info->MR_saved_regs); #endif retry_result = MR_trace_retry(event_info, ancestor_level, MR_RETRY_IO_INTERACTIVE, MR_trace_decl_assume_all_io_is_tabled, MR_DECL_UNTABLED_IO_RETRY_MESSAGE, &unsafe_retry, &problem, MR_mdb_in, MR_mdb_out, &jumpaddr); #ifdef MR_DEBUG_RETRY MR_print_stack_regs(stdout, event_info->MR_saved_regs); MR_print_succip_reg(stdout, event_info->MR_saved_regs); MR_print_r_regs(stdout, event_info->MR_saved_regs); #endif } if ((ancestor_level < 0) || (retry_result != MR_RETRY_OK_DIRECT)) { fflush(MR_mdb_out); fprintf(MR_mdb_err, "mdb: diagnosis aborted:\n"); if (ancestor_level < 0) { fprintf(MR_mdb_err, "couldn't find call on stack: %s\n", problem); } else { if (retry_result == MR_RETRY_ERROR) { fprintf(MR_mdb_err, "%s\n", problem); } else { fprintf(MR_mdb_err, "direct retry impossible\n"); } } MR_selected_trace_func_ptr = MR_trace_real; MR_debug_enabled = MR_TRUE; MR_update_trace_func_enabled(); return MR_trace_event_internal(cmd, MR_TRUE, NULL, event_info, NULL); } } else { // Since the event we want to be at is after the current event, // don't jump anywhere, just do forward execution until we // get to the right event. jumpaddr = NULL; } cmd->MR_trace_cmd = MR_CMD_GOTO; cmd->MR_trace_stop_event = event; cmd->MR_trace_print_level_specified = MR_TRUE; cmd->MR_trace_print_level = MR_PRINT_LEVEL_NONE; cmd->MR_trace_strict = MR_TRUE; cmd->MR_trace_must_check = MR_FALSE; MR_selected_trace_func_ptr = MR_trace_real; MR_debug_enabled = MR_TRUE; MR_update_trace_func_enabled(); return jumpaddr; } static MR_String MR_trace_node_path(MR_TraceNode node) { MR_String path; MR_trace_node_store++; MR_TRACE_CALL_MERCURY( path = MR_DD_trace_node_path((MR_Word) node); ); return path; } static MR_TracePort MR_trace_node_port(MR_TraceNode node) { MR_TracePort port; MR_TRACE_CALL_MERCURY( port = (MR_TracePort) MR_DD_trace_node_port((MR_Word) node); ); return port; } static MR_Unsigned MR_trace_node_seqno(MR_TraceNode node) { MR_Unsigned seqno; MR_trace_node_store++; MR_TRACE_CALL_MERCURY( if (!MR_DD_trace_node_seqno(MR_trace_node_store, (MR_Word) node, (MR_Integer *) &seqno)) { MR_fatal_error("MR_trace_node_seqno: not an interface event"); } ); return seqno; } static MR_TraceNode MR_trace_node_first_disj(MR_TraceNode node) { MR_TraceNode first; MR_TRACE_CALL_MERCURY( if (!MR_DD_trace_node_first_disj((MR_Word) node, (MR_Word *) &first)) { MR_fatal_error("MR_trace_node_first_disj: not a DISJ event"); } ); return first; } static MR_TraceNode MR_trace_step_left_in_contour(MR_TraceNode node) { MR_TraceNode next; MR_decl_checkpoint_step(node); MR_trace_node_store++; MR_TRACE_CALL_MERCURY( next = (MR_TraceNode) MR_DD_step_left_in_contour(MR_trace_node_store, node); ); return next; } static MR_TraceNode MR_trace_find_prev_contour(MR_TraceNode node) { MR_TraceNode next; MR_decl_checkpoint_find(node); MR_trace_node_store++; MR_TRACE_CALL_MERCURY( next = (MR_TraceNode) MR_DD_find_prev_contour(MR_trace_node_store, node); ); return next; } static void MR_trace_reset_implicit_subtree_counters(void) { int i; i = 0; while ((i < MR_edt_implicit_subtree_num_counters) && (MR_edt_implicit_subtree_counters[i] != 0)) { MR_edt_implicit_subtree_counters[i] = 0; i++; } } static void MR_trace_init_implicit_subtree_counters(MR_Unsigned size) { size_t i; MR_edt_implicit_subtree_counters = (MR_Unsigned *) malloc(size * sizeof(MR_Unsigned)); for (i = 0; i < size; i++) { MR_edt_implicit_subtree_counters[i] = 0; } MR_edt_implicit_subtree_num_counters = size; } static void MR_trace_free_implicit_subtree_counters(void) { free(MR_edt_implicit_subtree_counters); } static MR_Unsigned MR_trace_calc_implicit_subtree_ideal_depth(void) { MR_Integer depth; MR_Unsigned total; MR_Unsigned events_at_depth; depth = 0; total = 0; while (depth < MR_edt_implicit_subtree_num_counters) { events_at_depth = MR_edt_implicit_subtree_counters[depth]; total += events_at_depth; if (total > MR_edt_desired_nodes_in_subtree) { // Since we have gone over the desired number of nodes, // the ideal depth is depth - 1. Note that we want the depth limit // to be at least one, otherwise we won't add any new nodes // to the annotated trace. return (depth - 1) < 1 ? 1 : (depth - 1); } if (events_at_depth == 0) { return MR_edt_implicit_subtree_num_counters; } depth++; } // We didn't record the number of events at greater depths, so // be conservative and return the biggest depth we recorded up to. return (depth - 1) < 1 ? 1 : (depth - 1); } // NOTE: This function must be called within a MR_TRACE_CALL_MERCURY // wrapper. static void MR_trace_maybe_update_implicit_tree_ideal_depth(MR_Unsigned current_depth, MR_TraceNode call) { if (current_depth == MR_edt_max_depth) { MR_Unsigned ideal_depth; MR_Unsigned prev_ideal_depth; ideal_depth = MR_trace_calc_implicit_subtree_ideal_depth(); // Use the lowest depth if the ideal depth was set on a previous EXIT. // XXX In future the implicit subtree information should be stored // at final events, not CALL events, however this goes hand in hand // with a change to build pieces of the annotated trace // only between a REDO and its EXIT, if the events between // the CALL and the previous EXIT have already been materialized // (currently the events between the CALL and EXIT will be materialized // twice in this kind of situation). prev_ideal_depth = MR_DD_get_implicit_tree_ideal_depth(call); if (prev_ideal_depth == 0 || prev_ideal_depth > ideal_depth) { MR_DD_call_node_update_implicit_tree_info(call, ideal_depth); } } } static void MR_trace_maybe_update_suspicion_accumulator( const MR_LabelLayout *label_layout) { if (MR_edt_update_suspicion_accumulator) { MR_Unsigned *label_suspicion; label_suspicion = MR_trace_lookup_trace_count(label_layout); MR_edt_suspicion_accumulator += *label_suspicion; } } MR_bool MR_trace_decl_init_suspicion_table(char *pass_trace_counts_file, char *fail_trace_counts_file, MR_String *problem) { MR_String aligned_pass_trace_counts_file; MR_String aligned_fail_trace_counts_file; MR_Word maybe_dice; MR_Word dice; int num_modules; int module_num; MR_Integer num_files; int file_num; int num_labels; int label_num; int label_index; const MR_ModuleLayout *module; const MR_ModuleFileLayout *file; const MR_LabelLayout *label; MR_Unsigned *table_cell; MR_Float f_suspicion; MR_TRACE_USE_HP( MR_make_aligned_string(aligned_pass_trace_counts_file, (MR_String) pass_trace_counts_file); MR_make_aligned_string(aligned_fail_trace_counts_file, (MR_String) fail_trace_counts_file); ); MR_TRACE_CALL_MERCURY( MR_MDBCOMP_read_dice( aligned_pass_trace_counts_file, aligned_fail_trace_counts_file, &maybe_dice); MR_MDBCOMP_maybe_dice_error_to_problem_string(maybe_dice, problem); ); if (! MR_streq(*problem, "")) { return MR_FALSE; } else { MR_TRACE_CALL_MERCURY( MR_MDBCOMP_det_maybe_dice_error_to_dice(maybe_dice, &dice); ); } // We have read in a valid dice, so we can go ahead and set up the // suspicion table. We use the execution count table to store the // suspicions of each label. This is a good idea because // (a) it is quick to look up a value in this table given a label, and // (b) it is not used for counting events during an interactive mdb // session. num_modules = MR_module_info_next; for (module_num = 0; module_num < num_modules; module_num++) { module = MR_module_infos[module_num]; num_files = module->MR_ml_filename_count; for (file_num = 0; file_num < num_files; file_num++) { file = module->MR_ml_module_file_layout[file_num]; num_labels = file->MR_mfl_label_count; for (label_num = 0; label_num < num_labels; label_num++) { label = file->MR_mfl_label_layout[label_num]; label_index = label->MR_sll_label_num_in_module; table_cell = &(module->MR_ml_label_exec_count[label_index]); MR_TRACE_CALL_MERCURY( f_suspicion = MR_MDBCOMP_get_suspicion_for_label_layout(dice, label); ); // Instead of using a ratio between 0 and 1 we store an integer // between 0 and MR_TRACE_DECL_MAX_SUSPICION, since this is // quicker and requires less storage space. *table_cell = (MR_Unsigned) ((MR_Float) MR_TRACE_DECL_MAX_SUSPICION * f_suspicion); } } } MR_edt_update_suspicion_accumulator = MR_TRUE; return MR_TRUE; } void MR_trace_show_progress_subtree(MR_Unsigned event_number) { MR_Unsigned current_tick; if (event_number != MR_edt_last_event && MR_edt_progress_last_tick == 0 && (MR_edt_start_time + MR_DECL_DISPLAY_PROGRESS_DELAY < MR_get_user_cpu_milliseconds())) { fprintf(MR_mdb_out, MR_DECL_PROGRESS_MESSAGE_SUBTREE); fflush(MR_mdb_out); // We count the initial progress message as the first tick. MR_edt_progress_last_tick = 1; } else if (MR_edt_progress_last_tick > 0) { current_tick = (MR_Unsigned) (( (float) (event_number - MR_edt_first_event) * (float) MR_DECL_PROGRESS_TOTAL) / (float)(MR_edt_last_event - MR_edt_first_event)); if (current_tick != MR_edt_progress_last_tick) { for (; MR_edt_progress_last_tick < current_tick; MR_edt_progress_last_tick++) { fprintf(MR_mdb_out, MR_DECL_PROGRESS_TICK_STRING); fflush(MR_mdb_out); } } } } void MR_trace_show_progress_supertree(MR_Unsigned event_number) { // If we are building a supertree we don't know what the final event // will be, so we just show a tick every MR_DECL_DISPLAY_PROGRESS_DELAY // milliseconds, so at least the user knows something is happening. if (MR_edt_progress_last_tick == 0 && (MR_edt_start_time + MR_DECL_DISPLAY_PROGRESS_DELAY < MR_get_user_cpu_milliseconds())) { fprintf(MR_mdb_out, MR_DECL_PROGRESS_MESSAGE_SUPERTREE); fflush(MR_mdb_out); MR_edt_progress_last_tick = 1; } else if ((MR_edt_start_time + (MR_edt_progress_last_tick + 1) * MR_DECL_DISPLAY_PROGRESS_DELAY) < MR_get_user_cpu_milliseconds()) { MR_edt_progress_last_tick++; fprintf(MR_mdb_out, MR_DECL_PROGRESS_TICK_STRING); fflush(MR_mdb_out); } } static void MR_trace_finish_progress(void) { if (MR_mdb_decl_print_progress) { fprintf(MR_mdb_out, "\n"); fflush(MR_mdb_out); } MR_edt_progress_last_tick = 0; } static void MR_decl_checkpoint_event_imp(const char *str, MR_bool print_event, MR_EventInfo *event_info) { if (print_event) { fprintf(MR_mdb_out, "DD %s %ld: #%ld %ld %s ", str, (long) event_info->MR_event_number, (long) event_info->MR_call_seqno, (long) event_info->MR_call_depth, MR_actual_port_names[event_info->MR_trace_port]); MR_print_proc_id(MR_mdb_out, event_info->MR_event_sll->MR_sll_entry); fprintf(MR_mdb_out, "\n"); } else { fprintf(MR_mdb_out, "DD AT EVENT %ld: %s\n", (long) event_info->MR_event_number, str); } } static void MR_decl_checkpoint_loc(const char *str, MR_TraceNode node) { MercuryFile mdb_out; MR_mercuryfile_init(MR_mdb_out, 1, &mdb_out); fprintf(MR_mdb_out, "DD %s: %ld ", str, (long) node); MR_TRACE_CALL_MERCURY( MR_DD_print_trace_node((MR_Word) &mdb_out, (MR_Word) node); ); fprintf(MR_mdb_out, "\n"); } static void MR_decl_checkpoint_tree(const char *tree_kind, MR_Unsigned final_event, MR_Unsigned top_seqno, MR_Unsigned depth_limit) { fprintf(MR_mdb_out, "DD STARTING %s: ", tree_kind); fprintf(MR_mdb_out, "final event %lu, topmost seqno %lu, depth %lu\n", (unsigned long) final_event, (unsigned long) top_seqno, (unsigned long) depth_limit); } #ifdef MR_DD_PRINT_EDT_STATS static void MR_decl_print_edt_stats(void) { MR_bool debug_enabled_before = MR_debug_enabled; pid_t pid; char cmdstr[200]; MR_edt_stats_total_constructed_nodes += MR_edt_stats_constructed_nodes_this_time; MR_edt_stats_total_reexecutions++; fflush(NULL); // We use stderr instead of MR_mdb_err since ML_report_stats() // writes to stderr. fprintf(stderr, "EDT construction stats: \n"); fprintf(stderr, "Total reexecutions so far = %i\n", MR_edt_stats_total_reexecutions); fprintf(stderr, "Nodes constructed in this run = %i\n", MR_edt_stats_constructed_nodes_this_time); fprintf(stderr, "Max depth for this run = %i\n", MR_edt_max_depth); fprintf(stderr, "Total nodes constructed so far = %i\n", MR_edt_stats_total_constructed_nodes); fprintf(stderr, "Current event = %i\n", MR_trace_event_number); fprintf(stderr, "Total CPU time = %.2f\n", MR_get_user_cpu_milliseconds() / 1000.0); pid = getpid(); sprintf(cmdstr, "ps -p %i -o rss,vsz | tail -1 |" "awk '{print \"RSS = \" $1 \"\\nVSZ = \" $2}' 1>&2", pid); MR_trace_call_system_display_error_on_failure(stderr, cmdstr); MR_debug_enabled = MR_FALSE; MR_update_trace_func_enabled(); fprintf(stderr, "Benchmarking stats:\n"); MR_TRACE_CALL_MERCURY( ML_report_stats(); ); MR_debug_enabled = debug_enabled_before; MR_update_trace_func_enabled(); fprintf(stderr, "\n"); fflush(stderr); MR_edt_stats_constructed_nodes_this_time = 0; } static void MR_decl_inc_constructed_nodes(void) { MR_edt_stats_constructed_nodes_this_time++; } #endif // MR_DD_PRINT_EDT_STATS