X-Git-Url: http://nitlanguage.org diff --git a/src/debugger.nit b/src/debugger.nit index fc73825..b16a797 100644 --- a/src/debugger.nit +++ b/src/debugger.nit @@ -19,21 +19,48 @@ module debugger import breakpoint intrude import naive_interpreter +import nitx +intrude import toolcontext redef class ToolContext + private var dbg: nullable Debugger = null + + private var had_error: Bool = false + + redef fun check_errors + do + if dbg == null then + super + else + if messages.length > 0 then + message_sorter.sort(messages) + + for m in messages do + if "Warning".search_in(m.text, 0) == null then had_error = true + stderr.write("{m.to_color_string}\n") + end + end + + messages.clear + end + end + # -d var opt_debugger_mode: OptionBool = new OptionBool("Launches the target program with the debugger attached to it", "-d") + # -c + var opt_debugger_autorun: OptionBool = new OptionBool("Launches the target program with the interpreter, such as when the program fails, the debugging prompt is summoned", "-c") redef init do super self.option_context.add_option(self.opt_debugger_mode) + self.option_context.add_option(self.opt_debugger_autorun) end end redef class ModelBuilder - # Execute the program from the entry point (Sys::main) of the `mainmodule' - # `arguments' are the command-line arguments in order + # Execute the program from the entry point (Sys::main) of the `mainmodule` + # `arguments` are the command-line arguments in order # REQUIRE that: # 1. the AST is fully loaded. # 2. the model is fully built. @@ -50,9 +77,23 @@ redef class ModelBuilder var time1 = get_time self.toolcontext.info("*** END INTERPRETING: {time1-time0} ***", 2) end + + fun run_debugger_autorun(mainmodule: MModule, arguments: Array[String]) + do + var time0 = get_time + self.toolcontext.info("*** START INTERPRETING ***", 1) + + var interpreter = new Debugger(self, mainmodule, arguments) + interpreter.autocontinue = true + + init_naive_interpreter(interpreter, mainmodule) + + var time1 = get_time + self.toolcontext.info("*** END INTERPRETING: {time1-time0} ***", 2) + end end -# The class extending NaiveInterpreter by adding debugging methods +# The class extending `NaiveInterpreter` by adding debugging methods class Debugger super NaiveInterpreter @@ -93,6 +134,9 @@ class Debugger # If it is not, then, the remapping won't be happening var frame_count_aftermath = 1 + # Auto continues the execution until the end or until an error is encountered + var autocontinue = false + ####################################################################### ## Execution of statement function ## ####################################################################### @@ -106,16 +150,18 @@ class Debugger var old = frame.current_node frame.current_node = n - if not n isa ABlockExpr then - steps_fun_call(n) + if not self.autocontinue then + if not n isa ABlockExpr then + steps_fun_call(n) - breakpoint_check(n) + breakpoint_check(n) - check_funcall_and_traced_args(n) + check_funcall_and_traced_args(n) - remap(n) + remap(n) - check_if_vars_are_traced(n) + check_if_vars_are_traced(n) + end end n.stmt(self) @@ -128,16 +174,16 @@ class Debugger if self.stop_after_step_over_trigger then if self.frames.length <= self.step_stack_count then n.debug("Execute stmt {n.to_s}") - while process_debug_command(gets) do end + while read_cmd do end end else if self.stop_after_step_out_trigger then if frames.length < self.step_stack_count then n.debug("Execute stmt {n.to_s}") - while process_debug_command(gets) do end + while read_cmd do end end else if step_in_trigger then n.debug("Execute stmt {n.to_s}") - while process_debug_command(gets) do end + while read_cmd do end end end @@ -160,7 +206,7 @@ class Debugger end n.debug("Execute stmt {n.to_s}") - while process_debug_command(gets) do end + while read_cmd do end end end @@ -175,7 +221,7 @@ class Debugger for j in self.traces do if j.is_variable_traced_in_frame(i, frame) then n.debug("Traced variable {i} used") - if j.break_on_encounter then while process_debug_command(gets) do end + if j.break_on_encounter then while read_cmd do end break end end @@ -227,6 +273,12 @@ class Debugger ## Processing commands functions ## ####################################################################### + fun read_cmd: Bool + do + printn "> " + return process_debug_command(gets) + end + # Takes a user command as a parameter # # Returns a boolean value, representing whether or not to @@ -250,6 +302,10 @@ class Debugger # Step-over command else if command == "n" then return step_over + # Opens a new NitIndex prompt on current model + else if command == "nitx" then + new NitIndex.with_infos(modelbuilder, self.mainmodule).prompt + return true # Continues execution until the end else if command == "c" then return continue_exec @@ -279,6 +335,9 @@ class Debugger # Traces the modifications on a variable else if parts_of_command.length >= 2 and parts_of_command[0] == "trace" then process_trace_command(parts_of_command) + # Untraces the modifications on a variable + else if parts_of_command.length == 2 and parts_of_command[0] == "untrace" then + process_untrace_command(parts_of_command) # Lists all the commands available else list_commands @@ -346,6 +405,8 @@ class Debugger end print "\nEnd of current instruction \n" + else if parts_of_command[1] == "stack" then + print self.stack_trace else if parts_of_command[1].has('[') and parts_of_command[1].has(']') then process_array_command(parts_of_command) else @@ -453,6 +514,19 @@ class Debugger end end + # Processes the untrace variable command + # + # Command pattern : "untrace variable" + fun process_untrace_command(parts_of_command: Array[String]) + do + var variable_name = get_real_variable_name(parts_of_command[1]) + if untrace_variable(variable_name) then + print "Untraced variable {parts_of_command[1]}" + else + print "{parts_of_command[1]} is not traced" + end + end + # Processes the trace variable command # # Command pattern : "trace variable [break/print]" @@ -485,6 +559,28 @@ class Debugger ## Trace Management functions ## ####################################################################### + # Effectively untraces the variable called *variable_name* + # + # Returns true if the variable exists, false otherwise + private fun untrace_variable(variable_name: String): Bool + do + var to_remove: nullable TraceObject = null + for i in self.traces do + if i.is_variable_traced_in_frame(variable_name, frame) then + to_remove = i + end + end + + if to_remove != null then + self.traces.remove(to_remove) + return true + else + return false + end + end + + # Effectively traces the variable *variable_name* either in print or break mode depending on the value of breaker (break if true, print if false) + # private fun trace_variable(variable_name: String, breaker: Bool) do for i in self.traces do @@ -651,6 +747,8 @@ class Debugger # If it is a primitive type, its value is directly printed fun print_instance(instance: Instance) do + print "Printing innards of a variable" + if instance isa MutableInstance then var attributes = instance.attributes print "Object : {instance}" @@ -661,6 +759,8 @@ class Debugger else print "Found variable {instance}" end + + print "Stopping printing innards of a variable" end # Prints the attributes demanded in a SequenceRead @@ -1094,6 +1194,7 @@ class Debugger print "[p/print] variable : [p/print] * shows the status of all the variables\n" print "[p/print] variable[i] : Prints the value of the variable contained at position *i* in SequenceRead collection *variable*\n" print "[p/print] variable[i..j]: Prints the value of all the variables contained between positions *i* and *j* in SequenceRead collection *variable*\n" + print "[p/print] stack: Prints a stack trace at current instruction\n" print "Note : The arrays can be multi-dimensional (Ex : variable[i..j][k] will print all the values at position *k* of all the SequenceRead collections contained between positions *i* and *j* in SequenceRead collection *variable*)\n" print "s : steps in on the current function\n" print "n : steps-over the current instruction\n" @@ -1103,6 +1204,8 @@ class Debugger print "variable = value : Sets the value of *variable* to *value*\n" print "[d/delete] line_nb : Removes a breakpoint on line *line_nb* of the current file \n" print "[d/delete] file_name line_nb : Removes a breakpoint on line *line_nb* of file *file_name* \n" + print "trace variable_name [break/print] : Traces the uses of the variable you chose to trace by printing the statement it appears in or by breaking on each use." + print "untrace variable_name : Removes the trace on the variable you chose to trace earlier in the program" print "kill : kills the current program (Exits with an error and stack trace)\n" end