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
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
- ruby-debug Homepage
- Article: Debugging a Rails application with ruby-debug
- ink:ruby-debug Basics screencast
- Ryan Bate’s ruby-debug screencast
- Ryan Bate’s stack trace screencast
- Ryan Bate’s logger screencast
- Debugging with ruby-debug
- ruby-debug cheat sheet
- Ruby on Rails Wiki: How to Configure Logging
- Bleak House Documentation
7 Changelog
- 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