Mais em rubyonrails.pro.br: Geral | Download | Deploy | Código | Apresentações | Documentação | Ecossistema | Comunidade | Podcasts | Blogs

Depurando Aplicações Rails

Este guia apresenta técnicas para depurar aplicações no Ruby on Rails. Através deste guia, você será capaz de:

1 View Helpers para Depuração

Uma tarefa comum é inspecionar os conteúdos de uma variável. No Rails, você pode fazer isto de três formas:

  • debug
  • to_yaml
  • inspect

1.1 debug

O helper debug retornará uma tag <pre> que renderiza o objeto utilizando o formato YAML. Isto gera um bloco de dados legível para um humano a partir de qualquer objeto. Por exemplo, se você tiver este código em uma view:

<%= debug @post %> <p> <b>Título:</b> <%=h @post.title %> </p>

Você verá algo parecido com isto:

--- !ruby/object:Post attributes: updated_at: 2008-09-05 22:55:47 body: É um guia muito útil para depurar suas aplicações Rails. title: Guia de depuração com Rails published: t id: "1" created_at: 2008-09-05 22:55:47 attributes_cache: {} Title: Guia de depuração com Rails

1.2 to_yaml

Para imprimir no formato YAML uma variável de instância ou qualquer outro objeto ou método, você pode fazer da seguinte forma:

<%= simple_format @post.to_yaml %> <p> <b>Título:</b> <%=h @post.title %> </p>

O método to_yaml converte o método para o formato YAML, deixando-o mais legível, e então o helper simple_format é utilizado para renderizar cada linha como acontece em um console. Esta é a forma pela qual o método debug realiza a sua mágica.

Como resultado, você terá o seguinte em sua view:


!ruby/object:Post

attributes: updated_at: 2008-09-05 22:55:47 body: É um guia muito útil para depurar suas aplicações Rails. title: Guia de depuração com Rails published: t id: “1” created_at: 2008-09-05 22:55:47 attributes_cache: {}

Title: Guia de depuração com Rails

1.3 inspect

Outro método útil para imprimir valores de objetos é o inspect, especialmente quando se está trabalhando com arrays ou hashes. Ele imprimirá o valor do objeto como uma string. Por exemplo:

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

Será renderizado da seguinte forma:

[1, 2, 3, 4, 5]

Title: Guia de depuração com Rails

1.4 Depurando Javascript

Rails possui suporte embutido para se depurar RJS. Para ativá-lo, ajuste ActionView::Base.debug_rjs para true. Isto especificará que respostas RJS serão encapsuladas em um bloco try/catch que realizará alert()s quando uma exceção for capturada (a exceção então é relançada).

Para habilitá-lo, adicione a seguinte linha ao bloco Rails::Initializer do |config| dentro de environment.rb:

config.action_view[:debug_rjs] = true

Ou, a qualquer momento, ajuste ActionView::Base.debug_rjs para true:

ActionView::Base.debug_rjs = true

Para mais informações ao depurar javascript visite a página do Firebug, uma popular extensão de depuração para o Firefox.

2 O Logger

Também pode ser útil guardar informações em arquivos de log durante a execução de sua aplicação. O Rails mantém um arquivo de log separado para cada ambiente de execução.

2.1 O que é o Logger?

O Rails utiliza o logger padrão do Ruby para escrever informações de log. Você pode substituí-lo por outros loggers, como o Log4R se você preferir.

Você pode especificar um logger alternativo em seu environment.rb ou em qualquer arquivo de ambiente:

ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Base.logger = Log4r::Logger.new("Log da Aplicação")

Ou, na seção Initializer, adicione algum dos seguintes

config.logger = Logger.new(STDOUT) config.logger = Log4r::Logger.new("Log da Aplicação")

Por padrão, cada log é criado em RAILS_ROOT/log/ e o nome do arquivo de log é environment_name.log.

2.2 Níveis de Log

Quando uma mensagem é enviada para o log, ela é impressa no arquivo de log correspondente se o nível de log da mensagem é igual ou maior do que o nível configurado para o log. Para saber o nível configurado atual de um log, você pode chamar o método ActiveRecord::Base.logger.level.

Os níveis de log disponíveis são: :debug, :info, :warn, :error e :fatal, correspondendo aos níveis de log dos número de 0 até 4 respectivamente. Para alterar o nível de log, utilize:

config.log_level = Logger::WARN # Em qualquer inicializador de ambiente, ou ActiveRecord::Base.logger.level = 0 # a qualquer momento

Isto é útil para quando você deseja depurar algo durante o desenvolvimento ou staging, mas não deseja encher seu log de produção com informações desnecessárias.

O nível de log padrão do Rails é info no modo de produção e debug nos modos de desenvolvimento e teste.

2.3 Enviando Mensagens

Para escrever no log atual, utilize o método logger.(debug|info|warn|error|fatal) dentro de um controller, model ou mailer:

logger.debug "Hash de atributos de uma pessoa: #{@person.attributes.inspect}" logger.info "Processando requisição..." logger.fatal "Finalizando aplicação, encontrado erro irrecuperável!!!"

Aqui temos um exemplo de um método utilizando opções de log:

class PostsController < ApplicationController # ... def create @post = Post.new(params[:post]) logger.debug "Novo post: #{@post.attributes.inspect}" logger.debug "Post deve ser válido: #{@post.valid?}" if @post.save flash[:notice] = 'Post criado com sucesso.' logger.debug "O post foi salvo e agora o usuário será redirecionado..." redirect_to(@post) else render :action => "new" end end # ... end

Aqui temos um exemplo do log gerado por este método:

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]

Adicionar informações de logs extras como estas torna mais simples a busca por comportamentos inexperados ou incomuns em seus logs. Se você adicionar informações extras de log, certifique-se de utilizar corretamente os níveis de log, para evitar encher seu log de produção com informações não-úteis.

3 Depurando com ruby-debug

Quando o seu código estiver comportando-se de forma diferente do esperado, você pode tentar imprimir mensagem nos logs ou no console para diagnosticar o problema. Infelizmente, pode acontecer de haver situações em que este tipo de análise de erro não ser efetivo para encontrar a raiz de um problema. Quando você precisar analisar mais profundamente seu código durante sua execução, o depurador será seu melhor amigo.

O depurador também poderá lhe ajudar caso você deseje aprender sobre o código fonte do Rails e não souber por onde começar. Simplesmente depure qualquer requisição em sua aplicação e utilize este guia para aprender como se aprofundar a partir de seu código para o código do Rails.

3.1 Configuração

O depurador utilizado pelo Rails, ruby-debug, vem em forma de uma gem. Para instalá-lo, execute:

$ sudo gem install ruby-debug

No caso de você precisar baixar uma versão diferente ou o código fonte, você pode acessar a página do projeto no rubyforge.

O Rails tem suporte ao ruby-debug desde a versão 2.0. Dentro de qualquer aplicação Rails você pode utilizar o depurador chamando o método debugger.

Aqui temos um exemplo:

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

Se você receber a seguinte mensagem no console ou nos logs:

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

Certifique-se de iniciar o seu servidor web com a opção --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 ...

No modo de desenvolvimento, você pode dinamicamente utilizar o depurador com require \'ruby-debug\' ao invés de ter de reiniciar o servidor, se ele não foi iniciado com a opção --debugger.

Para utilizar o depurador do Rails você deverá utilizar o WEBrick ou o Mongrel. No momento, nenhum servidor alternativo possui suporte a depuração.

3.2 A Interface

Assim que sua aplicação invocar o método debugger, o depurador será inicializado em uma interface de depuração dentro do terminal de onde você iniciou seu servidor de aplicações, e você poderá interagir com o prompt do ruby-debug (rdb:n). O n é o número da thread. O prompt também lhe mostrará a próxima linha de código que está na espera para ser executada.

Se o código for chamado a partir de uma requisição de um navegador, a aba do navegador contendo a requisição será paralisada até que o processo de depuração e toda a requisição sejam finalizados.

Por exemplo:

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

Agora é a hora de explorar a sua aplicação. Uma boa forma de começar é pedir ajuda ao depurador… então digite: help (você não esperava por esta, né?)

(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

Para visualizar o menu de ajuda de qualquer comando utilize help durante o modo de depuração. Por exemplo: help var

O próximo comando que veremos é um dos mais úteis: list. Você também pode abreviar comandos do ruby-debug digitando somente as letras necessárias para distingüir o comando de outros comandos. Desta forma você pode, por exemplo, utilizar o l para o comando list.

Este comando mostra onde você está no código imprimindo 10 linhas ao redor da linha atual; neste exemplo, a linha atual é a 6 e está marcada com um =>.

(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 }

Se você repetir o comando list, desta vez utilizando somente o l, as próximas dez linhas do arquivo serão impressas.

(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

E assim por diante, até o fim do arquivo atual. Quando chegar ao final do arquivo, o comando list irá continuar do começo do arquivo e seguir novamente até seu final, tratando o arquivo como um buffer cíclico.

3.3 O Contexto

Enquanto você estiver depurando a sua aplicação, você será colocado em diferentes contextos quando passar por diferentes partes da pilha de execução.

O ruby-debug cria conteúdo quando um ponto de parada é alcançado ou quando um evento ocorre. O contexto possui informações sobre o programa paralisado, o que permite ao depurador obter informações sobre a pilha de execução, verificar valores de variáveis na perspectiva do programa depurado, e contém informações sobre o local onde o programa sendo depurado foi paralisado.

A qualquer momento você pode chamar o comando backtrace (ou o seu atalho where) para imprimir o backtrace (rastros) de sua aplicação. Isto pode ser muito útil para descobrir como você chegou onde está agora. Se você em algum momento se perguntou como chegou a algum lugar em seu código, o comando backtrace lhe dará a resposta.

(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 ...

Você pode mover-se dentro dos rastros (alterando seu contexto) utilizando o comando frame _n_, onde n é o número do frame desejado.

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

As variáveis disponíveis são as mesmas que você tem ao executar o código linha por linha. Afinal, isto é o que uma depuração significa.

Movendo-se na pilha: Você pode utilizar os comandos up [n] (abreviação u) e down [n] para alterar o contexto para n frames para cima ou para baixo, respectivamente. O padrão para n é um. Para cima, neste caso, é em direção aos frames de número mais altos, e para baixo é em direção aos de número mais baixos.

3.4 Threads

O depurador pode listar, parar, continuar e alterar entre threads sendo executadas utilizando o comando thread (ou sua abreviação th). Este comando possui as seguintes opções:

  • thread mostra a thread atual.
  • thread list é utilizado para listar todas as threads e seus estados. O caracter mais (+) e o número indicam a thread em execução.
  • thread stop _n_ para a thread n.
  • thread resume _n_ continua a thread n.
  • thread switch _n_ altera o contexto atual para o da thread n.

Este comando é muito útil, dentre outras situações, para quando você estiver depurando threads concorrentes e você quiser verificar se existem disputas de condições em seu código.

3.5 Inspecionando Variáveis

Qualquer expressão pode ser avaliada no contexto atual. Para avaliar uma expressão, simplesmente digite-a!

Este exemplo mostra como você pode imprimir as variáveis de instância (instance_variables) definidas no contexto atual:

@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"]

Como você pode ver, todas as variáveis acessíveis de um controller são mostradas. Esta lista é atualizada dinamicamente enquanto você executa código. Por exemplo, execute a próxima linha utilizando o comando next (você aprenderá mais sobre este comando mais adiante).

(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|

E então solicite novamente as variáveis de instância:

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

Agora @posts está incluído nas variáveis de instância, pois a linha que o define foi executada.

Você também pode entrar no modo irb com o comando irb (claro!). Assim, uma sessão irb será iniciada a partir do contexto em que você a invocou. Mas atenção: este modo ainda está em fase experimental.

O método var é a forma mais conveniente para mostrar variáveis e seus valores:

var (rdb:1) v[ar] const <object> mostra constantes de um objeto (rdb:1) v[ar] g[lobal] mostra variáveis globais (rdb:1) v[ar] i[nstance] <object> mostra variáveis de instância de um objeto (rdb:1) v[ar] l[ocal] mostra variáveis locais

Esta é uma ótima forma de inspecionar valores de variáveis no contexto atual. Por exemplo:

(rdb:9) var local __dbg_verbose_save => false

Você também pode inspecionar um método de um objeto desta forma:

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

Os comandos p (print – imprimir) e pp (pretty print – melhor impressão) podem ser utilizados para avaliar expressões Ruby e mostrar os valores de variáveis no console.

Você também pode utilizar o comando display para observar variáveis. Esta é uma boa forma de avaliar os valores de uma variável enquanto o código é executado.

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

As variáveis dentro da lista de observações serão impressas com seus valores assim que você avançar na pilha de execução. Para parar a observação de uma variável, utilize undisplay n, onde n é o número da variável (1 no último exemplo).

3.6 Passo a Passo

Você já deve se situar na pilha de execução e também deve ser capaz de imprimir os valores de variáveis. Agora, vamos continuar com a execução da aplicação.

Utilize o comando step (abreviação s) para continuar a execução de seu programa até o próximo ponto lógico de parada, quando o controle voltará ao ruby-debug.

Você também pode utilizar step n e step- n para andar para frente ou para trás n passos respectivamente.

Você também pode utilizar o comando next, que é similar ao comando step, mas chamadas de funções ou métodos dentro da linha são executados sem paradas. Assim como o comando step, você pode utilizar sinais de mais para mover n passos.

A diferença entre next e step é que step para na próxima linha de código a ser executada, realizando somente um passo simples, enquanto next move para a próxima linha sem entrar em métodos.

Por exemplo, considere este bloco de código com um comando 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

Você pode utilizar o ruby-debug enquanto estiver utilizando o script/console. Lembre-se apenas de fazer um require "ruby-debug" antes de chamar o método 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 )

Com o código paralisado, dê uma olhada:

(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

Você está no fim da linha, mas… será que esta linha já foi executada? Você pode inspecionar as variáveis de instância.

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

@recent_comments ainda não foi definida, então fica claro que esta linha ainda não foi executada. Utilize o comando next para avançar no código:

(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 = []

Agora você pode ver que o relacionamento @comments foi carregado e @recent_comments foi definido, porque a linha foi executada.

Se você quiser ir mais a fundo na pilha de execução, você pode avançar com um step de cada vez, e aprofundar-se nas chamadas de seus métodos e no código do Rails. Esta é uma das melhores formas de descobrir bugs em seu código, ou no código do próprio Ruby on Rails.

3.7 Breakpoints

Um breakpoint (ponto de parada) faz a sua aplicação parar quando um certo ponto do programa é alcançado. A interface do depurador é invocada a partir desta linha.

Você pode adicionar breakpoints dinamicamente com o comando break (ou somente b). Existem 3 formas de adicionar breakpoints manualmente:

  • break line: coloca um breakpoint na linha de número line do código fonte atual.
  • break file:line [if expression]: coloca um breakpoint na linha de número line do arquivo file. Se existir uma expressão ela deve se avaliada como true para ativar o depurador.
  • break class(.|\#)method [if expression]: coloca um breakpoint no método method (. e \# para classes e métodos de instância, respectivamente) definido na classe class. A expressão funciona da mesma forma como em file:line.
(rdb:5) break 10 Breakpoint 1 file /PathTo/project/vendor/rails/actionpack/lib/action_controller/filters.rb, line 10

Utilize info breakpoints _n_ ou info break _n_ para listar breakpoints. Se você passar um número, ele listará o breakpoint correspondente ao número. Caso contrário, ele listará todos os breakpoints.

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

Para remover breakpoints: utilize o comando delete _n_ para remover o breakpoint de número n. Se nenhum número for passado, todos os breakpoints ativos serão removidos…

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

Você também pode ativar ou desativar breakpoints:

  • enable breakpoints: permite que uma lista breakpoints, ou todos eles caso nenhuma lista seja mencionada, pararem o seu programa. Este é o estado inicial de um breakpoint quando você o cria.
  • disable breakpoints: a lista breakpoints não terá nenhum efeito em seu programa.

3.8 Capturando Exceções

O comando catch nome-da-exceção (ou somente cat exception-name) pode ser utilizado para interceptar uma exceção do tipo nome-da-exceção quando não houver tratamento para a exceção.

Para listar os pontos de captura ativos utilize o comando catch.

3.9 Continuando a Execução

Existem duas formas de continuar a execução de uma aplicação que está parada no depurador:

  • continue [especificação-da-linha] (ou c): continua a execução do programa a partir do local onde o seu script parou; quaisquer outros breakpoints posicionados no local são ignorados. O argumento opcional especificação-da-linha permite a você especificar o número de uma linha para criar um breakpoint temporário que será removido assim que o breakpoint for alcançado.
  • finish [número-do-frame] (ou fin): executa o código até que o frame selecionado retorne. Se nenhum frame for passado, a aplicação executará até que o frame atual retorne. O frame atual é o mais recente ou o 0 se nenhum posicionamento de frame foi feito (com os comandos up, down ou frame, por exemplo). Se um número for passado o código será executado até que o frame retorne.

3.10 Editando

Dois comandos permitem que um código no depurador seja aberto em um editor:

  • edit [arquivo:linha]: edita arquivo utilizando o editor especificado na variável de ambiente EDITOR. Uma linha específica pode ser selecionada.
  • tmate _n_ (abreviação tm): abre o arquivo atual no TextMate. Utiliza o n-ésimo frame se n for especificado.

3.11 Saindo

Para sair do depurador, utilize o comando quit (abreviação q), ou o seu alias exit.

Uma saída simples tentará finalizar todas as threads correntes. Neste caso, seu servidor será finalizado e você deverá inicializá-lo novamente.

3.12 Configurações

Existem algumas configurações que podem ajudar na depuração de seu código com o ruby-debug. Aqui estão algumas das opções disponíveis:

  • set reload: Recarrega o código fonte quando houver alterações.
  • set autolist: Executa o comando list a cada breakpoint.
  • set listsize _n_: Configura o número de linhas do código fonte a serem mostradas para n linhas.
  • set forcestep: Verifica se os comandos next e step sempre movem para uma nova linha.

Você pode ver a lista completa com o comando help set. Utilize help set _subcomando_ para aprender sobre as opções específicas de um comando.

Você pode incluir estas configurações dentro de um arquivo .rdebugrc no seu diretório HOME. O ruby-debug irá ler este arquivo sempre que for carregado.

Aqui temos um bom começo para um .rdebugrc:

set autolist
set forcestep
set listsize 25

4 Depurando Estouros de Memória

Uma aplicação Ruby (seja ela Rails ou não), pode estourar a memória – seja no nível de código Ruby ou C.

Nesta seção, você aprenderá como fazer para encontrar e corrigir estouros utilizando as ferramentas de depuração Bleak House e Valgrind.

4.1 BleakHouse

BleakHouse é uma biblioteca para encontrar estouros de memória.

Se um objeto Ruby não ficar fora de escopo, o Ruby Garbage Collector não irá limpá-lo já que ele estará sendo referenciado em algum lugar. Estouros deste tipo podem crescer lentamente e sua aplicação irá consumir mais e mais memória, gradualmente afetando a performance geral do sistema. Esta ferramenta irá ajudá-lo a encontrar estouros na heap do Ruby.

Para instalá-lo, execute:

sudo gem install bleak_house

Agora você deve configurar sua aplicação para utilizá-lo. E então adicione o seguinte ao final do seu config/environment.rb:

require 'bleak_house' if ENV['BLEAK_HOUSE']

Inicie uma instância do servidor integrado com o BleakHouse:

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

Certifique-se de executar algumas centenas de requisições para ter uma amostra melhor de dados, e então digite CTRL-C. O servidor será finalizado e o Bleak House irá gerar uma saída em /tmp:

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

Para analisá-lo, execute o comando listado. As 20 linhas com maior probabilidade de estouro serão listadas:

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 ...

Desta forma você pode descobrir onde a sua aplicação está com maior probabilidade de estourar memória e corrígi-la.

Se o BleakHouse não apresentar nenhum crescimento na heap mas ainda assim você tiver aumento no uso de memória, você pode estar com uma extensão C quebrada, ou um estouro real no interpretador. Neste caso, tente utilizar o Valgrind para uma investigação mais a fundo.

4.2 Valgrind

Valgrind é uma aplicação para Linux para detectar estouros de memória e condições de disputa em programas C.

Existem ferramentas Valgrind que podem detectar automaticamente bugs de gerenciamento de memória e de threads, e detalhar o perfil de seu programa. Por exemplo, uma extensão C no interpretador chama malloc() mas não chama free(), esta memória não ficará disponível até que a aplicação termine.

Para mais informações de como instalar o Valgrind e utilizá-lo com Ruby, visite Valgrind e Ruby por Evan Weaver.

5 Plugins para Depuração

Existem alguns plugins para o Rails que podem auxiliá-lo a encontrar erros e depurar a sua aplicação. Aqui está uma lista de plugins que podem ser úteis para este propósito:

  • Footnotes: Cada página no Rails recebe notas de rodapé com informações sobre a requisição e com atalhos para seu código fonte via TextMate.
  • Query Trace: Adiciona aos logs o local onde consultas são geradas.
  • Query Stats: Um plugin Rails para seguir suas consultas ao banco dados.
  • Query Reviewer: Este plugin Rails, além de executar o comando “EXPLAIN” antes de cada uma de suas consultas no ambiente de desenvolvimento, também provê uma pequena DIV renderizada na saída de cada página com um sumário de avisos para cada consulta analisada.
  • Exception Notifier: Provê um objeto mailer e um conjunto padrão de templates para enviar notificações por email quando errors ocorrerem em uma aplicação Rails.
  • Exception Logger: Loga exceções Rails no banco de dados e provê uma interface para gerenciá-las.

6 Referências

7 Log de Alterações

Lighthouse ticket

  • Março 3, 2008: Revisão da tradução por Daniel Lopes
  • 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