Ruby-Programmierung: Debugging

Zurück zum Inhaltsverzeichnis.

Debuggen beschreibt eine Handlung Fehler im Programm zu suchen und zu beheben, da der interne Zustand des Programms im normalen Ablauf nicht bekannt sein sollte, ist es schwierig die Quelle eines Fehlers zu finden ohne das Programm zu ändern. Neben der sehr einfachen Möglichkeit das Programm mittels puts aufzublähen, werden hier zwei Bibliotheken vorgestellt die das untersuchen des Programmtextes mit nur sehr kleinen Änderungen an ihm ermöglichen.

Die Bibliothek debug ist ein einfaches Tool, das es ermöglicht den Programmablauf zu unterbrechen und an dieser Stelle das Programm Schritt für Schritt auszuführen und zudem den Inhalt von Variablen auszugeben.

Das folgende Skript definiert eine Methode, die die Fakultät einer Zahl berechnen soll, doch funktioniert sie nicht, denn bei jedem Parameter gibt sie null zurück, anstatt des richtigen Wertes.

require "debug"

def fak(n)
  res = 1
  n.downto(0) { |i| res *= i }
  res
end

puts fak(5)

Die Bibliothek unterbricht die Ausführung des Programms und stattdessen ist es möglich mit s die Ausführung schrittweise durchzuführen. Mit v l werden alle lokalen Variablen angezeigt, die im aktuellen Kontext existieren. Damit ist es möglich das Problem einzugrenzen auf die Zeile 5, wo zwar die Fakultät korrekt berechnet wird, schließlich jedoch mit null multipliziert. Eine Änderung der Zeile zu n.downto(1) { |i| res *= i } behebt das Problem.

In größeren Programmen kann das komplette schrittweise Ausführen des Programms sehr schnell mühselig werden, daher existieren dafür zwei weitere Befehle: Das Anlegen von Breakpoints und das Fortsetzen des Programmablaufs bis zum nächsten Breakpoint. Dies erfolgt durch b (break) mit Angabe der Zeilennummer oder einem Methodennamen und c (continue).

pry-debugger

Bearbeiten

Die Bibliothek debug stellt eine einfache REPL (Read-Eval-Print-Loop) zur Verfügung, die es erlaubt Rubycode auszuführen und durch das vorhandene Skript zu navigieren. Pry ist ebenfalls eine REPL, die jedoch einige weitere Funktionen bereitstellt. Man kann die Debugvariante ähnlich einfach verwenden, wie in folgendem Skript gezeigt:

require "pry-debugger"

def fak(n)
  res = 1
  n.downto(0) { |i| res *= i; binding.pry }
  res
end

puts fak(5)

Im Gegensatz zu debug wird Pry erst beim Aufruf von binding.pry aktiv und erlaubt es somit auf die Verwendung von Breakpoints zu verzichten und im Sourcecode festzulegen welche Stellen einen interessieren, insbesondere kann man damit auch auf Verzweigungen zurückgreifen, um die Unterbrechungen im Programmablauf möglichst gut einzuschränken.

Das schrittweise Fortsetzen des Programms erfolgt dann mit next bzw. step und ebenfalls kann man an allen Stellen die lokalen Variablen usw anzeigen.