Depurando erros de falta de memória no Firefox OS

Quando um dispositivo Firefox OS fica sem memória, os sistemas low-memory killer e low-memory notifications são executados para encerrar alguns processos e manter o sistema operacional funcionando. Quando o kernel encerra alguns processos que estavam rodando em segundo plano o comportamento assemelha-se a uma finalização inesperada do aplicativo que estava sendo executado. Esse artigo explica como compreender e depurar erros devido a falhas decorrentes de falta de memória.

Nota: Se você não sabe ainda como situações com baixa memória são gerenciadas no Firefox OS, sugerimos você ler o artigo Gerenciamento de falha de memória no Firefox OS antes de continuar a ler esse documento.

Depurando uma falha decorrente de Falta de Memória (OOM - Out Of Memory crash)

Suponha que você tenha uma falha que possa ser reproduzida e suspeita que seja a causadora de falta de memória. Os passos a seguir farão você entender mais sobre o que está dando errado.

Passo 1: Confirme se realmente a falha é devido à falta de memória

Primeiro, devemos garantir que a falha é devido ao fato do telefone estar sem memória disponível. Para fazer isso execute adb shell dmesg.  Se o aplicativo for enecerrado devido a falta de memória, você verá a seguinte resposta:

<4>[06-18 07:40:25.291] [2897: Notes+]send sigkill to 2897 (Notes+), adj 2, size 30625

Essa linha indica que o aplicativo Notes+ (ID do processo 2897) foi encerrado e possuia oom_adj 2. O tamanho reportado aqui é em páginas, com 4kb cada. Dessa forma, nesse caso, o aplicativo Notes+ estava usando 30625 * 4kb = 120mb de memória.

Digressão: se não for falta de memória (OOM)

Se a saída do comando dmesg não for a apresentada, provavelmente a falha não é devido à falta de memória.  your crash is likely not an OOM. O próximo passo na depuração da falha é utilizar o gdb ao processo de falha e conseguir um rastreamento, que pode ser feito dessa forma:

$ cd path/to/B2G/checkout
$ adb shell b2g-ps
# Note pid of the app that you're going to crash
$ ./run-gdb.sh attach <pid>
(gdb) continue
# crash the app
(gdb) bt

Quando for reportar o bug, anexe o resultado obtido, bem como o resultado do comando adb logcat. Se a falha for devido a falta de memória, o rastreamento provavelmente não será interessante, poir a falha de falta de memória é disparada por um sinal enviado pelo kernel e não por uma falha de código que o processo executa.

Passo 2: Coletar relatórios de memória

Depois de você ter se certificado que a falha é devido à falta de memória, o próximo passo é coletar relatório de memória do seu telefone antes da falha. Esse relatório nos ajudará a entender como a memória está sendo usada. Esta etapa é um pouco complicada, porque uma vez que aplicativo falhar, não há nenhuma maneira de coletar um relatório de memória desse processo. Também não há uma forma de disparar um relatório de memória quando o kernel tenta encerrar um processo — quando isso acontece é tarde demais.

Para extrair um relatório de memória do seu telefone, primeiramente atualize sua árvore de compilação a fim de obter a última versão da ferramenta que extrai o relatório. Observe que  repo sync não será suficiente; você deve executar git fetch && git merge ou git pull:

$ cd path/to/B2G/checkout
$ git fetch origin
$ git merge --ff-only origin

Agora execute a ferramenta para extrair o relatório:

$ tools/get_about_memory.py

Uma vez que você conseguiu o relatório, você pode compactar o diretório (chamado about-memory-N) e anexá-lo a um bug. Mas novamente, isso somente será útil se você executar esse comando enquanto o aplicativo que você está monitorando estiver sendo executado e usando muita memória. Você tem algumas opções aqui:

Passo 2, opção 1: Consiga um dispositivo diferente

Geralmente a forma mais fácil é conseguir um dispositivo com mais memória. Você já sabe desde o passo 1 quanta memória o processo estava usando quando falhou, assim você pode simplesmente esperar até o processo utilizar perto desse valor e então pegar um relatório de memória. O aplicativo b2g-info mostrará quanta memória cada um dos diferentes processos B2G estavam usando. Você pode executar esse processo em um loop fazendo o seguinte:

$ adb shell 'while true; do b2g-info; sleep 1; done'

Se b2g-info não está disponível no seu dispositivo, você pode usar b2g-procrank.

Passo 2, opção 2: Dedo mais rápido

Se você não possui um dispositivo com mais memória RAM, você pode tentar executar get_about_memory.py no momento exatamente anterior ao aplicativo falhar. Você pode rodar b2g-info em um loop (como mostrado no item anterior) para descobrir quando executar get_about_memory.py. Executar um relatório de memória congela todos os processos por um pequeno momento, assim não é difícil extrair um relatório de memória antes do processo de falta de memória entrar em ação.

Passo 2, opção 3: Use um pequeno caso de teste

Geralmente falhas de falta de memória ocorrem quando é feito algo como "carregar um arquivo de no mínimo X bytes no aplicativo".

Se o aplicativo falha muito rapidadmente com um caso de teste do tamanho X, você pode tentar executar um teste similar com o tamanho X/2 e capturar um relatório de memória após a execução do teste. O relatório pode nos dar valiosas dicas que definitivamente nos chamarão a atenção.

Passo 2, opção 4: Execute B2G no seu desktop

Se o que é ruim se tornar pior, você pode rodar o B2G no seu desktop, o que provavelmente possui muito mais memória que seu telefone FxOS. Isso é complicado porque B2G rodando num desktopo tem um comportamento diferente que quando roda num dispositivo.

Particularmente, B2G num desktop possui o multiprocessamento desabilitado por padrão. Ele realmente não funciona 100% em qualquer lugar, mas funciona de forma mais precisa no Linux e no Mac. (Verifique Bug 923961, Bug 914584, Bug 891882). Você pode testar em seu desktop com o multiprocessamento desabilitado, mas em minha experiência muitos dos problemas relacionados ao uso de memória são causados por códigos de comunicação interprocessos, de modo que não vai necessariamente provocar o erro que você está vendo.

Também não é conveniente extrari relatórios de memória dos processos B2G no desktop. No Linux, você pode enviar o sinal 34 para o processo principal B2G que ele vai escrever relatórios no diretório /tmp com o seguinte nome: memory-report-*.gz.

Uma vantagem de utilizar o B2G desktop é que você pode usar suas ferramentas de depuração favoritas, como o aplicativo Instruments Activities do Mac OSX. Nós já tivemos muito sucesso no passado. Para coletar relatórios de memória usando esse utilitário do OSX, vá em "New -> Mac OS X -> Allocations". Inicie o b2g-desktop e você verá alguns processos "plugin-container" no utilitário Activity Monitor. Você necessitará de duas instâncias do Instruments: um para registrar as alocações no processo principal b2g e outro para registrar as alocações no aplicativo que você deseja analisar. Com os utiltários monitorando os processos execute o seu caso de teste.

Para analisar quanta memório seu aplicativo está utilizando, analise as árvores de chamadas. Marque "Invert Call Tree", o ordene por bytes usados. Isso irá mostrar qual parte do seu aplicativo usa mais memória. Abaixo uma cópia de tela de uma análise simples de uso de memória de uma aplicativo:

 

Screen shot of instruments.
Para mais informações de como configurar uma compilação do B2G desktop, leia nossa página Modificando o Gaia.

Passo 3: Analise o relatório de memória

Quando você executa get_about_memory.py, será apresentado o relatório de memório no Firefox. Esse arquivo contém informações do uso de memório de todos os processos do sistema. A leitura desse relatório pode ser um tanto pesada num primeiro momento, mas não será tão ruim quando você pegar o jeito da coisa. Note que você pode passar o mouse sobre qualquer nó folha para obter uma descrição do que esse nó descreve. O que você estará procurando é algo "estranhamente grande" no processo que ocasionou a falha. Você pode ter uma ideia do que é "estranhamente grande" capturando um relatório de memória do seu aplicativo quando ele não está usando muita memória.

A leitura de um relatório de memória requer alguma prática, assim fique a vontade para pedir ajuda. Os especialistas no assunto costumam ficar no canal #memshrink do IRC.

Passo 4: Recompile com DMD, se necessário

Um item comum presente nos relatórios de memória extraídos antes de uma falha é heap-unclassifiedheap-unclassified mostra a memória alocada pelo processo que não é apresentada por nenhum outro relatório de memória. Se você tem um valor alto no heap-unclassified, o relatório não pode dizer dizer mais nada sobre a quem a memória pertence. Nossa ferramenta para ir mais fundo no heap-unclassified é chamada DMD. Ela funciona no B2G, mas você deve compilar seu próprio B2G para que funcione pois DMD exige símbolos locais que são mantidos na máquina que roda a compilacão.

Para saber mais informações sobre o DMD e como interpretar sua saída, leia a página do DMD na Wiki Mozilla.
 

Etiquetas do documento e colaboradores

 Colaboradores desta página: jwhitlock, rbrandao
 Última atualização por: jwhitlock,