# Debugging of a nit program using the NaiveInterpreter
module debugger
+import breakpoint
intrude import naive_interpreter
redef class ToolContext
# Triggers a step over an instruction in a nit program
var stop_after_step_over_trigger = true
+ # Triggers a step out of an instruction
+ var stop_after_step_out_trigger= false
+
+ # Triggers a step in a instruction (enters a function
+ # if the instruction is a function call)
+ var step_in_trigger = false
+
+ # HashMap containing the breakpoints bound to a file
+ var breakpoints = new HashMap[String, HashSet[Breakpoint]]
+
+ # Contains the current file
+ var curr_file = ""
+
+ #######################################################################
+ ## Execution of statement function ##
+ #######################################################################
+
# Main loop, every call to a debug command is done here
redef fun stmt(n: nullable AExpr)
do
steps_fun_call(n)
+ breakpoint_check(n)
+
n.stmt(self)
frame.current_node = old
end
+ # Encpasulates the behaviour for step over/out
private fun steps_fun_call(n: AExpr)
do
if self.stop_after_step_over_trigger then
n.debug("Execute stmt {n.to_s}")
while process_debug_command(gets) 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
+ end
+ else if step_in_trigger then
+ n.debug("Execute stmt {n.to_s}")
+ while process_debug_command(gets) do end
+ end
+ end
+
+ # Checks if a breakpoint is encountered, and launches the debugging prompt if true
+ private fun breakpoint_check(n: AExpr)
+ do
+ var currFileNameSplit = self.frame.current_node.location.file.filename.to_s.split_with("/")
+
+ self.curr_file = currFileNameSplit[currFileNameSplit.length-1]
+
+ var breakpoint = find_breakpoint(curr_file, n.location.line_start)
+
+ if breakpoints.keys.has(curr_file) and breakpoint != null then
+ n.debug("Execute stmt {n.to_s}")
+ while process_debug_command(gets) do end
end
end
# Kills the current program
if command == "kill" then
abort
+ # Step-out command
+ else if command == "finish"
+ then
+ return step_out
+ # Step-in command
+ else if command == "s"
+ then
+ return step_in
# Step-over command
else if command == "n" then
return step_over
# Shows the value of a variable in the current frame
if parts_of_command[0] == "p" or parts_of_command[0] == "print" then
print_command(parts_of_command)
+ # Places a breakpoint on line x of file y
+ else if parts_of_command[0] == "break" or parts_of_command[0] == "b"
+ then
+ process_place_break_fun(parts_of_command)
+ # Removes a breakpoint on line x of file y
+ else if parts_of_command[0] == "d" or parts_of_command[0] == "delete" then
+ process_remove_break_fun(parts_of_command)
+ # Lists all the commands available
+ else
+ list_commands
end
end
return true
end
+ #######################################################################
+ ## Processing specific command functions ##
+ #######################################################################
+
# Sets the flags to step-over an instruction in the current file
fun step_over: Bool
do
self.step_stack_count = frames.length
self.stop_after_step_over_trigger = true
+ self.stop_after_step_out_trigger = false
+ self.step_in_trigger = false
+ return false
+ end
+
+ #Sets the flags to step-out of a function
+ fun step_out: Bool
+ do
+ self.stop_after_step_over_trigger = false
+ self.stop_after_step_out_trigger = true
+ self.step_in_trigger = false
+ self.step_stack_count = frames.length
+ return false
+ end
+
+ # Sets the flags to step-in an instruction
+ fun step_in: Bool
+ do
+ self.step_in_trigger = true
+ self.stop_after_step_over_trigger = false
+ self.stop_after_step_out_trigger = false
return false
end
fun continue_exec: Bool
do
self.stop_after_step_over_trigger = false
+ self.stop_after_step_out_trigger = false
+ self.step_in_trigger = false
return false
end
if instance != null
then
print_instance(instance)
+ else
+ print "Cannot find variable {parts_of_command[1]}"
end
end
end
+ # Processes the input string to know where to put a breakpoint
+ fun process_place_break_fun(parts_of_command: Array[String])
+ do
+ var bp = get_breakpoint_from_command(parts_of_command)
+ if bp != null then
+ place_breakpoint(bp)
+ else
+ list_commands
+ end
+ end
+
+ # Returns a breakpoint containing the informations stored in the command
+ fun get_breakpoint_from_command(parts_of_command: Array[String]): nullable Breakpoint
+ do
+ if parts_of_command[1].is_numeric then
+ return new Breakpoint(parts_of_command[1].to_i, curr_file)
+ else if parts_of_command.length >= 3 and parts_of_command[2].is_numeric then
+ return new Breakpoint(parts_of_command[2].to_i, parts_of_command[1])
+ else
+ return null
+ end
+ end
+
+ # Processes the command of removing a breakpoint on specified line and file
+ fun process_remove_break_fun(parts_of_command: Array[String])
+ do
+ if parts_of_command[1].is_numeric then
+ remove_breakpoint(self.curr_file, parts_of_command[1].to_i)
+ else if parts_of_command.length >= 3 and parts_of_command[2].is_numeric then
+ remove_breakpoint(parts_of_command[1], parts_of_command[2].to_i)
+ else
+ list_commands
+ end
+ end
+
#######################################################################
## Print functions ##
#######################################################################
end
end
+ #######################################################################
+ ## Breakpoint placing functions ##
+ #######################################################################
+
+ # Places a breakpoint on line 'line_to_break' for file 'file_to_break'
+ fun place_breakpoint(breakpoint: Breakpoint)
+ do
+ if not self.breakpoints.keys.has(breakpoint.file) then
+ self.breakpoints[breakpoint.file] = new HashSet[Breakpoint]
+ end
+ if find_breakpoint(breakpoint.file, breakpoint.line) == null then
+ self.breakpoints[breakpoint.file].add(breakpoint)
+ print "Breakpoint added on line {breakpoint.line} for file {breakpoint.file}"
+ else
+ print "Breakpoint already present on line {breakpoint.line} for file {breakpoint.file}"
+ end
+ end
+
+ #######################################################################
+ ## Breakpoint removing functions ##
+ #######################################################################
+
+ # Removes a breakpoint on line 'line_to_break' for file 'file_to_break'
+ fun remove_breakpoint(file_to_break: String, line_to_break: Int)
+ do
+ if self.breakpoints.keys.has(file_to_break) then
+ var bp = find_breakpoint(file_to_break, line_to_break)
+
+ if bp != null then
+ self.breakpoints[file_to_break].remove(bp)
+ print "Breakpoint removed on line {line_to_break} for file {file_to_break}"
+ return
+ end
+ end
+
+ print "No breakpoint existing on line {line_to_break} for file {file_to_break}"
+ end
+
+ #######################################################################
+ ## Breakpoint searching functions ##
+ #######################################################################
+
+ # Finds a breakpoint for 'file' and 'line' in the class HashMap
+ fun find_breakpoint(file: String, line: Int): nullable Breakpoint
+ do
+ if self.breakpoints.keys.has(file)
+ then
+ for i in self.breakpoints[file]
+ do
+ if i.line == line
+ then
+ return i
+ end
+ end
+ end
+
+ return null
+ end
+
+ #######################################################################
+ ## Command listing function ##
+ #######################################################################
+
+ # Lists the commands available when using the debugger
+ fun list_commands
+ do
+ print "\nCommand not recognized\n"
+ print "Commands accepted : \n"
+ print "[break/b] line : Adds a breakpoint on line *line_nb* of the current file\n"
+ print "[break/b] file_name line_nb : Adds a breakpoint on line *line_nb* of file *file_name* \n"
+ print "[p/print] variable : [p/print] * shows the status of all the variables\n"
+ print "s : steps in on the current function\n"
+ print "n : steps-over the current instruction\n"
+ print "finish : steps out of the current function\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 "kill : kills the current program (Exits with an error and stack trace)\n"
+ end
+
end