Więcej na rubyonrails.pl: Start | Pobierz | Wdrożenie | Kod (en) | Screencasty | Dokumentacja | Ekosystem | Forum | IRC

Debugowanie w Ruby on Rails

Ten przewodnik przedstawia techniki debugowania aplikacji Railsowych. Korzystając z tego przewodnika będziesz w stanie:

1 Helpery widoku dla debugowania

Zwyczajową czynnością jest przy debugowaniu jest oglądanie zawartości zmiennej. W Railsach może to zrobić za pomocą jednej z trzech metod:

  • debug
  • to_yaml
  • inspect

1.1 debug

Helper debug zwróci tag <pre> który przedstawi obiekt używając formatu YAML. Wygeneruje to czytelne dla człowieka dane z dowolnego obiektu. Na przykład, wykorzystując taki kod:

<%= debug @post %> <p> <b>Title:</b> <%=h @post.title %> </p>

Otrzymasz wynik podobny do poniższego:

--- !ruby/object:Post attributes: updated_at: 2008-09-05 22:55:47 body: It's a very helpful guide for debugging your Rails app. title: Rails debugging guide published: t id: "1" created_at: 2008-09-05 22:55:47 attributes_cache: {} Title: Rails debugging guide

1.2 to_yaml

Wyświetlanie zmiennej instancyjnej (lub jakiegokolwiek innego obiektu bądź metody) w formacie YAML może być przeprowadzone tym sposobem:

<%= simple_format @post.to_yaml %> <p> <b>Title:</b> <%=h @post.title %> </p>

Metoda to_yaml konwertuje wynik metody do formatu YAML, pozostawiając go bardziej czytelnym, następnie helper simple_format jest wykorzystany do wyświetlenia każdej linijki osobno – podobnie jak w konsoli. Odpowiada to działaniu metody debug.

Jako wynik zobaczysz coś takiego:

--- !ruby/object:Post attributes: updated_at: 2008-09-05 22:55:47 body: It's a very helpful guide for debugging your Rails app. title: Rails debugging guide published: t id: "1" created_at: 2008-09-05 22:55:47 attributes_cache: {} Title: Rails debugging guide

1.3 inspect

Kolejna użyteczna metoda wykorzystywana do wyświetlania wartości obiektu to inspect, szczególnie podczas pracy z tablicami lub tablicami asocjacyjnymi. Wyświetli to wartość obiektu jako string. Na przykład:

<%= [1, 2, 3, 4, 5].inspect %> <p> <b>Title:</b> <%=h @post.title %> </p>

Będzie wyświetlone jako:

[1, 2, 3, 4, 5]

Title: Rails debugging guide

1.4 Debugging Javascript

Railsy posiadają wbudowane wspomaganie debugowania RJS. By je aktywować, ustaw ActionView::Base.debug_rjs jako true, to określi czy odpowiedzi RJS powinni być objęte blokiem try/catch i zaalarmować o złapanym wyjątku.

Aby to umożliwić, dodaj poniższy kod w bloku Rails::Initializer do |config| wewnątrz environment.rb:

config.action_view.debug_rjs = true

Lub, w dowolnym momencie, ustaw ActionView::Base.debug_rjs jako true:

ActionView::Base.debug_rjs = true

Po więcej informacji z zakresu debugowania języka javascript odsyłamy do Firebug’a, popularnego debuggera przeglądarki Firefox.

2 Logger

Użyteczne może być również zapisywanie informacji do plików log w czasie pracy. Railsy zachowują oddzielny log dla każdego środowiska uruchomieniowego.

2.1 Czym jest Logger?

Railsy wykorzystują standardowy logger języka Ruby by zapisywać informację w logach. Możesz również zastąpić go innym loggerem, jak na przykład Log4R jeśli chcesz.

Możesz sprecyzować alternatywny logger w twoim environment.rb bądź jakimkolwiek pliku środowiska:

ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")

Lub w sekcji Initializer, dodaj dowolny z poniższych:

config.logger = Logger.new(STDOUT) config.logger = Log4r::Logger.new("Application Log")

Domyślnie, każdy log jest tworzony w katalogu RAILS_ROOT/log/ a jego nazwa to environment_name.log.

2.2 Poziomy logów

Gdy coś jest logowane, jest wpisywane do odpowiadającego loga jeśli poziom logowania tej wiadomości jest równy lub wyższy niż skonfigurowany poziom loggera. Jeśli chcesz znać aktualny poziom loggera, możesz wykorzystać metodę ActiveRecord::Base.logger.level.

Dostępne poziomy logowania to: :debug, :info, :warn, :error, and :fatal, odpowiadające numerom poziomów logowania odpowiednio od 0 do 4. By zmienić domyślny poziom logowania, użyj

config.log_level = Logger::WARN # W dowolnym inicjalizatorze środowiska lub ActiveRecord::Base.logger.level = 0 # w dowolnej chwili

Jest to użyteczne, gdy chcesz logować w środowisku programisty (development) lub przejściowym (staging), ale nie chcesz zaśmiecać loga nieistotnymi informacjami w środowisku produkcyjnym.

Domyślny poziom logów w Rails to info dla trybu produkcyjnego oraz debug dla programowania i testowego.

2.3 Wysyłanie wiadomości

By pisać w aktualnym logu, użyj metody logger.(debug|info|warn|error|fatal) z kontrolera, modelu lub mailera:

logger.debug "Person attributes hash: #{@person.attributes.inspect}" logger.info "Processing the request..." logger.fatal "Terminating application, raised unrecoverable error!!!"

Oto przykład metody wyposażonej w dodatkowe logowanie:

class PostsController < ApplicationController # ... def create @post = Post.new(params[:post]) logger.debug "New post: #{@post.attributes.inspect}" logger.debug "Post should be valid: #{@post.valid?}" if @post.save flash[:notice] = 'Post was successfully created.' logger.debug "The post was saved and now is the user is going to be redirected..." redirect_to(@post) else render :action => "new" end end # ... end

Oraz przykład loga wygenerowanego przez tę metodę:

Processing PostsController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST] Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGl vbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b06c4d724596a4 Parameters: {"commit"=>"Create", "post"=>{"title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>"0"}, "authenticity_token"=>"2059c1286e93402e389127b1153204e0d1e275dd", "action"=>"create", "controller"=>"posts"} New post: {"updated_at"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>false, "created_at"=>nil} Post should be valid: true Post Create (0.000443) INSERT INTO "posts" ("updated_at", "title", "body", "published", "created_at") VALUES('2008-09-08 14:52:54', 'Debugging Rails', 'I''m learning how to print in logs!!!', 'f', '2008-09-08 14:52:54') The post was saved and now is the user is going to be redirected... Redirected to #<Post:0x20af760> Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/posts]

Ustawianie takiego dodatkowego logowania ułatwia wyszukiwanie nieoczekiwanego bądź odstającego od normy zachowania w twoich logach. Jeśli wykorzystasz dodatkowe logowanie, pamiętaj o ostrożnym używaniu poziomów logowania, by uniknąć zapełniania twoich logów produkcyjnych bezużytecznymi drobiazgami.

3 Debugowanie z użyciem ruby-debug

Gdy twój kod zachowuje się nie tak, jak powinien, możesz spróbować raportowania do logów lub konsoli by zdiagnozować problem. Niestety zdarza się, że ten sposób wyszukiwania błędów jest nieefektywny przy znajdywaniu głównej przyczyny problemu. Jeśli faktycznie musisz wybrać się w podróż do wnętrza twojego działającego kodu źródłowego, debugger będzie twoim najlepszym współtowarzyszem.

Debugger może również pomóc ci jeśli chcesz dowiedzieć się o kodzie źródłowym Rails i nie wiesz, gdzie zacząć. Po prostu zdebuguj dowolne żądanie w twojej aplikacji i użyj tego przewodnika by dowiedzieć się jak przejść z napisanego przez siebie kodu głębiej w kod źródłowy Rails.

3.1 Instalacja

Debugger używany przez Railsy, ruby-debug, jest instalowany jako gem. By go zainstalować, po prostu uruchom:

$ sudo gem install ruby-debug

Jeśli chcesz ściągnąć konkretną wersję lub zdobyć kod źródłowy, sprawdź stronę projektu na rubyforge.

Railsy posiadają wbudowane wspomaganie dla ruby-debug począwszy od wersji 2.0. W dowolnej Railsowej aplikacji możesz przywołać debugger używając metody debugger.

Oto przykład:

class PeopleController < ApplicationController def new debugger @person = Person.new end end

Jeśli zobaczysz w konsoli lub w logach wiadomość:

***** Debugger requested, but was not available: Start server with --debugger to enable *****

Upewnij się, że uruchomiłeś swój serwer sieciowy z opcją --debugger:

~/PathTo/rails_project$ script/server --debugger => Booting Mongrel (use 'script/server webrick' to force WEBrick) => Rails 2.2.0 application starting on http://0.0.0.0:3000 => Debugger enabled ...

W trybie programowania, możesz dynamicznie wymóc require "ruby-debug" zamiast restartować serwer, jeśli został uruchomiony bez opcji --debugger.

W celu skorzystania z debuggingu Rails musisz mieć uruchomiony albo serwer WEBrick albo Mongrel. Na dzień dzisiejszy inne alternatywne serwery nie są obsługiwane.

3.2 Powłoka

Gdy tylko twoja aplikacja przywoła metodę debugger, debugger zostanie uruchomiony w powłoce w oknie terminala, w którym uruchomiłeś swój serwer i zostaniesz przeniesiony do znaku zachęty (ang. prompt) ruby-debug (rdb:n). Owe n jest numerem wątku. Znak zachęty pokaże ci również kolejną linijkę kodu oczekującą na uruchomienie.

Jeśli dostałeś się tam poprzez żądanie przeglądarki, okno przeglądarki zawierające żądanie pozostanie zawieszone dopóki debugger nie zostanie zakończony oraz przetwarzanie całego żądania nie zostanie ukończone.

Na przykład:

@posts = Post.find(:all) (rdb:7)

Teraz pora na zbadanie i przekopanie twojej aplikacji. Dobrym początkiem będzie zapytanie debuggera o pomoc… wpisz więc: help (Nikt by się nie domyślił, nieprawdaż?)

(rdb:7) help ruby-debug help v0.10.2 Type 'help <command-name>' for help on a specific command Available commands: backtrace delete enable help next quit show trace break disable eval info p reload source undisplay catch display exit irb pp restart step up condition down finish list ps save thread var continue edit frame method putl set tmate where

By zobaczyć menu pomocy dla dowolnej komendy użyj help w aktywnym trybie debugowania. Na przykład: help var

Kolejna komenda to jedna z najbardziej użytecznych: list. Możesz również skrócić komendy ruby-debug poprzez zapewnienie ilości liter wystarczającej do odróżnienia ich od pozostałych komend – możesz więc użyć tylko l dla komendy list.

Ta komenda informuje cię, gdzie jesteś w kodzie poprzez wyświetlenie 10 linii kodu wokół aktualnej linijki; w poniższym przypadku owa aktualna linijka to linia 6, oznaczona =>.

(rdb:7) list [1, 10] in /PathToProject/posts_controller.rb 1 class PostsController < ApplicationController 2 # GET /posts 3 # GET /posts.xml 4 def index 5 debugger => 6 @posts = Post.find(:all) 7 8 respond_to do |format| 9 format.html # index.html.erb 10 format.xml { render :xml => @posts }

Jeśli powtórzysz komendę list, tym razem używając tylko l, zwrócone zostanie kolejne 10 linijek kodu:

(rdb:7) l [11, 20] in /PathTo/project/app/controllers/posts_controller.rb 11 end 12 end 13 14 # GET /posts/1 15 # GET /posts/1.xml 16 def show 17 @post = Post.find(params[:id]) 18 19 respond_to do |format| 20 format.html # show.html.erb

I tak dalej aż do końca pliku. Kiedy koniec pliku zostanie osiągnięty, komenda list zacznie znów od początku pliku i będzie kontynuować ponownie do jego końca, traktując go jak zapętlony bufor.

3.3 Kontekst

Gdy rozpoczniesz debugowanie swojej aplikacji, będziesz umieszczany w różnych kontekstach w miarę przemieszczania się przez różne części stosu.

ruby-debug tworzy kontekst, gdy osiąga punkt wstrzymania bądź zdarzenie. Kontekst posiada informacje o zawieszonym programie, co pozwala debuggerowi zbadać stos oraz oszacować zmienne z perspektywy debugowanego programu. Posiada również informacje o miejscu, w którym debugger został zatrzymany.

Zawszy możesz użyć komendy backtrace (bądź jej aliasu where) by wyświetlić wsteczny przebieg aplikacji. Może być to bardzo pomocne w przekonaniu się, jak dostałeś się w miejsce, w którym się znajdujesz. Jeśli kiedykolwiek zastanawiałeś się, jak znalazłeś się w danym miejscu kodu, komenda backtrace zapewni odpowiedź.

(rdb:5) where #0 PostsController.index at line /PathTo/project/app/controllers/posts_controller.rb:6 #1 Kernel.send at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175 #2 ActionController::Base.perform_action_without_filters at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175 #3 ActionController::Filters::InstanceMethods.call_filters(chain#ActionController::Fil...,...) at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/filters.rb:617 ...

Możesz poruszać się gdzie tylko chcesz w tym stosie wywołań (tym samym zmieniając kontekst) używając komendy frame _n_, gdzie n to określony numer ramki.

(rdb:5) frame 2 #2 ActionController::Base.perform_action_without_filters at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175

Dostępne zmienne są takie same, jak gdybyś uruchamiał kod linijka za linijką. O to w końcu chodzi w debugowaniu.

Poruszanie się w górę i w dół stosu: Możesz użyć up [n] (w skrócie u) oraz down [n] w celu zmiany kontekstu o n ramek odpowiednio w górę lub w dół stosu. n to domyślnie jeden. “W górę” to w tym przypadku w kierunku ramki o wyższym numerze, zaś “w dół”- w kierunku ramki o niższym.

3.4 Wątki

Debugger może sporządzić listę, zatrzymać, przywrócić oraz przełączać się pomiędzy działającymi wątkami z użyciem komendy thread (lub skrócone th). Ta komenda posiada wiele opcji:

  • thread pokazuje aktualny wątek
  • thread list jest używana do utworzenia listy wszystkich wątków oraz ich statusów. Znak plus + oraz numer oznacza aktualnie wykonywany wątek
  • thread stop _n_ zatrzymuje wątek n
  • thread resume _n_ przywraca wątek n do działania
  • thread switch _n_ zmienia kontekst aktualnego wątku na n

Ta komenda jest bardzo pomocna na przykład (spośród wielu różnych sytuacji) gdy debugujesz równoczesne wątki i musisz upewnić się, że nie ma sytuacji wyścigu (race conditions) w twoim kodzie.

3.5 Sprawdzanie zmiennych

Dowolne wyrażenie może być ocenione w aktualnym kontekście. By oszacować wyrażenie, po prostu wpisz je!

Ten przykład pokazuje, jak można wyświetlić zmienne instancyjne zdefiniowane w aktualnym kontekście:

@posts = Post.find(:all) (rdb:11) instance_variables ["@_response", "@action_name", "@url", "@_session", "@_cookies", "@performed_render", "@_flash", "@template", "@_params", "@before_filter_chain_aborted", "@request_origin", "@_headers", "@performed_redirect", "@_request"]

Jak mogłeś się domyślić, wszystkie zmienne, do których możesz dotrzeć z kontrolera, są wyświetlane. Lista ta jest na bieżąco aktualizowana w miarę jak przeprowadzasz działanie kodu. Na przykład, uruchom kolejną linię używając next (dowiesz się więcej o tej komendzie w dalszej części przewodnika).

(rdb:11) next Processing PostsController#index (for 127.0.0.1 at 2008-09-04 19:51:34) [GET] Session ID: BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA==--b16e91b992453a8cc201694d660147bba8b0fd0e Parameters: {"action"=>"index", "controller"=>"posts"} /PathToProject/posts_controller.rb:8 respond_to do |format|

A następnie spytaj ponownie o zmienne instancyjne:

(rdb:11) instance_variables.include? "@posts" true

Teraz @posts jest zawarty w zmiennych instancyjnych, bo linia go definiująca została wykonana

Możesz również przejść do trybu irb komendą (oczywiście!) irb. W ten sposób sesja irb zostanie uaktywniona w kontekście, w którym ją wywołałeś. Uwaga – jest to póki co opcja eksperymentalna.

Metoda var to najwygodniejszy sposób wyświetlania zmiennych i ich wartości:

var (rdb:1) v[ar] const <object> show constants of object (rdb:1) v[ar] g[lobal] show global variables (rdb:1) v[ar] i[nstance] <object> show instance variables of object (rdb:1) v[ar] l[ocal] show local variables

Świetny sposób sprawdzania wartości zmiennych aktualnego kontekstu. Na przykład:

(rdb:9) var local __dbg_verbose_save => false

Możesz w ten sposób dokonać również inspekcji obiektu:

(rdb:9) var instance Post.new @attributes = {"updated_at"=>nil, "body"=>nil, "title"=>nil, "published"=>nil, "created_at"... @attributes_cache = {} @new_record = true

Komendy p (print) oraz pp (pretty print) mogą być użyte do oszacowania wyrażeń Ruby i wyświetlania wartości zmiennych w konsoli.

Możesz też użyć display by zacząć oglądać zmienne. To dobry sposób śledzenia wartości zmiennych w miarę jak wykonywanie programu postępuje.

(rdb:1) display @recent_comments 1: @recent_comments =

Wyświetlane zmienne podane będą ze swymi wartościami po tym jak przejdziesz po stosie. By wyłączyć wyświetlanie zmiennej, użyj undisplay _n_ gdzie n to numer zmiennej (1 w poprzednim przykładzie).

3.6 Krok po kroku

Teraz powinieneś wiedzieć, w którym miejscu wykonywanego programu jesteś i być w stanie wyświetlić dostępne zmienne. Idźmy więc dalej z wykonywaniem aplikacji.

Użyj step (w skrócie s) aby kontynuować działanie programu do następnego logicznego punktu wstrzymania i przywróć kontrolę ruby-debug.

Możesz też użyć step n oraz step- n+ by poruszyć się odpowiednio naprzód lub wstecz o n kroków.

Możesz też użyć next, które jest analogiczne do step, ale funkcje lub wywołania metod, które pojawiają się w linijce kodu są wykonywane bez zatrzymania. Tak jak przy step, możesz używać znaku plus by przejść o n kroków.

Różnia między next i step jest taka, iż step zatrzymuje się w następnej linijce wykonanego kodu, wykonując po prostu pojedynczy krok, podczas gdy next przechodzi do następnej linii bez zagłębiania się w napotkane metody.

Na przykład, przyjrzyjmy się temu blokowi kodu z wywołaniem debugger:

class Author < ActiveRecord::Base has_one :editorial has_many :comments def find_recent_comments(limit = 10) debugger @recent_comments ||= comments.find( :all, :conditions => ["created_at > ?", 1.week.ago], :limit => limit ) end end

Możesz używać ruby-debug podczas używania script/console. Pamiętaj tylko o require "ruby-debug" przed użyciem metody debugger.

/PathTo/project $ script/console Loading development environment (Rails 2.1.0) >> require "ruby-debug" => [] >> author = Author.first => #<Author id: 1, first_name: "Bob", last_name: "Smith", created_at: "2008-07-31 12:46:10", updated_at: "2008-07-31 12:46:10"> >> author.find_recent_comments /PathTo/project/app/models/author.rb:11 )

Po zatrzymaniu kodu, rozejrzyjmy się w nim:

(rdb:1) list [6, 15] in /PathTo/project/app/models/author.rb 6 debugger 7 @recent_comments ||= comments.find( 8 :all, 9 :conditions => ["created_at > ?", 1.week.ago], 10 :limit => limit => 11 ) 12 end 13 end

Jesteś na końcu linii, ale… czy linia ta została wykonana? Możesz sprawdzić zmienne instancyjne.

(rdb:1) var instance @attributes = {"updated_at"=>"2008-07-31 12:46:10", "id"=>"1", "first_name"=>"Bob", "las... @attributes_cache = {}

@recent_comments nie zostało jeszcze zdefiniowane, więc jest oczywiste, że linia ta nie została jeszcze wykonana. Użyj komendy next by poruszać się w kodzie:

(rdb:1) next /PathTo/project/app/models/author.rb:12 @recent_comments (rdb:1) var instance @attributes = {"updated_at"=>"2008-07-31 12:46:10", "id"=>"1", "first_name"=>"Bob", "las... @attributes_cache = {} @comments = [] @recent_comments = []

Teraz widzisz, że relacja @comments została załadowana i @recent_comments zdefiniowane poprzez wykonanie kodu w tej linijce.

Jeśli chcesz bardziej zagłębić się w stosie możesz poruszać się pojedynczymi krokami, przez twoje wywoływane metody, a także do wnętrza kodu Railsów. To jeden z najlepszych sposobów do wyszukiwania błędów w twoim kodzie albo, być może, w Ruby lub Rails.

3.7 Breakpointy

Breakpoint zatrzymuje twoją aplikację gdy tylko określony punkt programu zostanie osiągnięty. W tej linii uruchamiana jest konsola debuggera.

Możesz dodawać breakpointy na bieżąco komendą break (lub po prostu b). Są 3 metody dodawania breakpointów ręcznie:

  • break line: ustawia breakpoint w line w aktualnym pliku źródłowym
  • break file:line [if expression]: ustawia breakpoint w linii o numerze
  • line wewnątrz pliku file. Jeśli wyrażenie expression jest podane, musi ewaluować się na true by uruchomić debugger
  • break class(.|\#)method [if expression]: ustawia breakpoint w metodzie method (. oraz \# odpowiednio dla metody klasowej i instancyjnej) zdefiniowanej w klasie class. expression działa tak samo, jak przy file:line
(rdb:5) break 10 Breakpoint 1 file /PathTo/project/vendor/rails/actionpack/lib/action_controller/filters.rb, line 10

Użyj info breakpoints _n_ lub info break _n_ by wyświetlić breakpointy. Jeśli podasz liczbę, wyświetli breakpoint o danym numerze. W innym przypadku wyświetlone zostaną wszystkie breakpointy.

(rdb:5) info breakpoints Num Enb What 1 y at filters.rb:10

By usuwać breakpointy: użyj komendy delete n by usunąć breakpoint numer n. Jeśli nie zostanie podany numer, komenda usunie wszystkie aktualnie aktywne breakpointy.

(rdb:5) delete 1 (rdb:5) info breakpoints No breakpoints.

Możesz również umożliwić bądź uniemożliwić działanie breakpointów:

  • enable breakpoints: pozwala liście breakpointów lub wszystkim breakpointom, jeśli nie zostanie sprecyzowana lista, zatrzymywać twój program. Jest to stan domyślny, gdy tworzysz nowy breakpoint.
  • disable breakpoints: breakpointy nie będą wpływać na twój program.

3.8 Łapanie wyjątków

Komenda catch _exception-name_ (lub po prostu cat _exception-name_) może być użyta do przechwycenia wyjątku typu exception-name jeśli nie byłoby niczego innego, co mogłoby zajmować się danym wyjątkiem.

By wyświetlić wszystkie aktywne catchpointy użyj catch.

3.9 Przywracanie wykonywania

Są dwie metody przywrócenia procesu wykonywania aplikacji wstrzymanej w debuggerze:

  • continue [numer linii] (lub c): przywraca wykonywanie programu w miejscu, gdzie twój skrypt ostatnio się zatrzymał; jakiekolwiek breakpointy ustawione w tym miejscu są pomijane. Opcjonalny argument “numer linii” pozwala ci ustawić numer linii, gdzie zostanie ustawiony breakpoint jednorazowego użytku (będzie skasowany, gdy zostanie osiągnięty).
  • finish [numer ramki] (lub fin): wykonuje dopóki wskazana ramka stosu nie zostanie opuszczona. Jeśli nie zostanie podany “numer ramki”, aplikacja będzie działać, dopóki aktualnie wybrana ramka nie zostanie opuszczona. Aktualnie wybrana ramka oznacza najbliższą ostatnią ramkę lub 0 jeśli żadne pozycjonowanie ramek (np. up, down lub frame) nie było wykonywane. Jeśli “numer ramki” jest podany program będzie wykonywany do momentu opuszczenia owej ramki.

3.10 Edytowanie

Dwie komendy pozwalają ci na otwarcie kodu z debuggera w edytorze:

  • edit [file:line]: edytuje file używając edytora sprecyzowanego w zmiennej środowiskowej EDITOR. Można podać konkretną linię line do edycji.
  • tmate n (w skrócie tm): otwiera aktualny plik w TextMate. Używa n-tej ramki, jeśli n jest podane.

3.11 Wychodzenie

By opuścić debugger, użyj komendy quit (w skrócie q), lub jej aliasu exit.

Zwykłe wyjście oznacza zakończenie wszystkich aktualnie działających wątków. Tym samym twój serwer zostanie wstrzymany i będziesz musiał uruchomić go ponownie.

3.12 Ustawienia

Pewne ustawienia mogą zostać skonfigurowane w ruby-debug by ułatwić debugowanie twego kodu. Oto kilka dostępnych opcji:

  • set reload: ładuje ponownie kod źródłowy, gdy został zmieniony.
  • set autolist: wykonuje komendę list przy każdym breakpoincie.
  • set listsize _n_: ustawia ilość linii kodu, które będą domyślnie wyświetlane przy wywołaniu list na n
  • set forcestep: zapewnia, że komendy next oraz step zawsze przenoszą do nowej linii.

Możesz zobaczyć pełną listę używając help set. Użyj help set _komenda_ by dowiedzieć się o konkretnej komendzie typu set.

Możesz zawrzeć dowolną ilość ustawień konfiguracyjnych wewnątrz pliku .rdebugrc w twoim folderze HOME. ruby-debug odczyta ten plik za każdym razem, gdy zostanie załadowany i odpowiednio się skonfiguruje.

Oto dobry początek .rdebugrc:

set autolist set forcestep set listsize 25

4 Debugowanie Wycieków Pamięci

Aplikacja Ruby (on Rails lub nie), może mieć czasem wycieki pamięci – zarówno w kodzie Ruby jak i na poziomie kodu C.

W tej sekcji dowiesz się jak znaleźć i naprawić takie wycieki używając narzędzi debugujących Bleak House oraz Valgrind.

4.1 BleakHouse

BleakHouse to biblioteka służąca do wyszukiwania wycieków pamięci.

Jeśli obiekt Ruby nie znika z zasięgu, Ruby Garbage Collector nie usunie go, gdyż w jakimś miejscu jest do niego odniesienie. Takie wycieki mogą stopniowo narastać i twoja aplikacja będzie pożerać coraz to więcej pamięci, stopniowo wpływając na sprawność całego systemu. To narzędzie pozwoli ci znaleźć wycieki na stercie programu w Rubim.

By ją zainstalować, uruchom:

sudo gem install bleak_house

Następnie ustaw swoją aplikację na profilowanie. Dodaj poniższy wpis na dole config/environment.rb:

require 'bleak_house' if ENV['BLEAK_HOUSE']

Uruchom serwer z integracją BleakHouse:

RAILS_ENV=production BLEAK_HOUSE=1 ruby-bleak-house ./script/server

Uruchom co najmniej kilkaset żądań w celu uzyskania lepszych próbek danych, następnie wciśniej CTRL-C. Serwer zostanie wstrzymany i Bleak House stworzy zrzut pamięci w katalogu /tmp:

** BleakHouse: working... ** BleakHouse: complete ** Bleakhouse: run 'bleak /tmp/bleak.5979.0.dump' to analyze.

By go przeanalizować, po prostu uruchom podane komendy. 20 najbardziej wyciekających linii zostanie podanych:

191691 total objects Final heap size 191691 filled, 220961 free Displaying top 20 most common line/class pairs 89513 __null__:__null__:__node__ 41438 __null__:__null__:String 2348 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:Array 1508 /opt/local//lib/ruby/gems/1.8/specifications/gettext-1.90.0.gemspec:14:String 1021 /opt/local//lib/ruby/gems/1.8/specifications/heel-0.2.0.gemspec:14:String 951 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:111:String 935 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:String 834 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:146:Array ...

W ten sposób możesz znaleźć miejsca, w których twoja aplikacja traci pamięć i naprawić to.

Jeśli BleakHouse nie znajduje żadnego niekontrolowanego wzrostu a wciąż następuje zwiększenie wykorzystania pamięci, możesz posiadać uszkodzone rozszerzenie C, lub prawdziwy wyciek w interpreterze. W takim wypadku spróbuj użyć Valgrind w celu głębszego zbadania sytuacji.

4.2 Valgrind

Valgrind to aplikacja tylko dla systemu Linux używana do wykrywania wycieków pamięci w C oraz sytuacji wyścigu (race conditions).

Istnieją narzędzia Valgrind które mogą automatycznie wykryć liczne błędy zarządzania pamięcią i szczegółowo ustawić twój program. Na przykład, rozszerzenie C w interpreterze wywołuje malloc() ale nie wywołuje prawidłowo free() – ta pamięć nie będzie już dostępna aż do wyłączenia aplikacji.

Po więcej informacji jak zainstalować Valgrind oraz używać go z Ruby, odsyłamy do http://blog.evanweaver.com/articles/2008/02/05/valgrind-and-ruby/[Valgrind and Ruby] autorstwa Evana Weavera.

5 Pluginy do Debugowania

Istnieją pluginy Rails mogące pomóc ci znaleźć błędy oraz zdebugować twoją aplikację. Oto lista użytecznych pluginów do debugowania:

  • Footnotes: Każda strona Rails posiada na dole przypisy które podają informację o żądaniach i odsyłają z powrotem do twojego kodu źródłowego poprzez TextMate.
  • Query Trace: Dodaje śledzenie pochodzenia zapytań do twoich logów.
  • Query Stats: Plugin Rails do śledzenia zapytań bazy danych.
  • Query Reviewer: Ten plugin Rails nie tylko uruchamia “EXPLAIN” przed każdym z twoich zapytań select w trybie programowania, ale dodaje niewielki DIV na każdej renderowanej stronie z podsumowaniem ostrzeżeń dla każdego analizowanego zapytania.
  • http://github.com/rails/exception_notification/tree/master[Exception Notifier]: Zapewnia obiekt mailer oraz domyślny zestaw szablonów do wysyłania mailowych ostrzeżeń, gdy wystąpi błąd aplikacji Rails.
  • Exception Logger: Loguje twoje wyjątki Rails w bazie danych i zapewnia stylowy interfejs sieciowy do zarządzania nimi.

6 Odnośniki

7 Changelog

Lighthouse ticket

  • November 3, 2008: Accepted for publication. Added RJS, memory leaks and plugins chapters by Emilio Tagua
  • October 19, 2008: Copy editing pass by Mike Gunderloy
  • September 16, 2008: initial version by Emilio Tagua