Introdução à profiling em Linux embarcado

- por Sergio Prado

Categorias: Linux Tags: ,

Profiling de aplicações e sistemas Linux é um tópico interessante e importante quando trabalhamos com Linux embarcado. Quando precisamos tomar decisões sobre performance, consumo de memória, uso da CPU e de I/Os de um dispositivo embarcado, uma ferramenta de profiling pode ser indispensável. Mas o que significa profiling, afinal?

ISSO AQUI ESTA UMA "CARROÇA"!

Então você desenvolveu seu produto, mas a performance esta uma porcaria! Execução lenta, travamentos constantes, display touch com tempo de resposta muito ruins, e por aí vai. Onde esta o gargalo? CPU? Memória? Como otimizar? Por onde começar? É aí que entram as ferramentas de profiling, que possuem mecanismos para medir e analisar a performance de uma aplicação ou até de um sistema inteiro.

As ferramentas de profiling coletam dados durante a execução do software, armazenando a ocorrência de eventos e o estado do sistema durante estes eventos. Estas informações são usadas posteriormente por uma ferramenta de análise de performance.

Para trabalhar com ferramentas de profiling, você precisa seguir basicamente três passos:

  1. Você precisa que sua aplicação ou sistema forneça um mecanismo de coleta dos dados de performance. Isso se chama instrumentação. E existem alguns métodos para realizar esta instrumentação, dependendo da ferramenta. Métodos de simulação logicamente simulam, instrução por instrução, a sua aplicação; é intrusivo e lento, mas em alguns casos pode resolver seu problema (o Valgrind trabalha assim). Você também pode precisar alterar o código-fonte da aplicação para inserir o código de instrumentação, ou então usar uma flag do próprio compilador para esta tarefa (é assim que o gprof funciona). Ou então o próprio kernel pode te oferecer uma interface de instrumentação, como é o caso do OProfile.
  2. Com um mecanismo de instrumentação, as ferramentas de profiling conseguem capturar dados de performance e salvar para posterior análise. Existem ferramentas que coletam 100% dos dados, mas isso tem um custo de processamento e armazenamento, que pode ser proibitivo, principalmente em sistemas embarcados. A maioria das ferramentas trabalham com amostragens, que apesar de não ser um método 100% preciso, consegue ser bem realista.
  3. Com os dados em mãos, podemos fazer a análise e identificar os gargalos na aplicação.

Em um sistema Linux embarcado, os dois primeiros passos são executados no target (dispositivo embarcado) e o último passo do host (sua máquina de desenvolvimento).

FERRAMENTAS DISPONÍVEIS

Existem diversas ferramentas em Linux para trabalhar com profiling. Você deve levar em consideração alguns atributos ao escolher uma ferramenta de profiling:

  • Transparência: quanto mais transparente for trabalhar com a ferramenta, melhor! (Não precisar mexer no ambiente, alterar o toolchain, etc).
  • Precisão: os dados gerados devem ser precisos, você precisa confiar na ferramenta.
  • Abrangência: principalmente em Linux embarcado, quanto mais abrangente melhor. A ferramenta deve ter a capacidade de fazer uma análise completa do sistema, incluindo kernel, módulos, bibliotecas e binários.
  • Não-intrusivo: o fato de usar a ferramenta não deve afetar o resultado (o chamado "ruído" gerado pela ferramenta). Cuidado: às vezes, as ferramentas mentem!

TRACING = PROFILING?

Um tempo atrás, escrevi um artigo sobre ferramentas de trace em Linux. Tracing e profiling tem objetivos bem parecidos, mas com focos um pouco diferentes. Tracing tem o objetivo de analisar o fluxo, a lógica. Profiling tem o objetivo de analisar a performance.

As duas usam os mesmos mecanismos, como por exemplo monitorar a execução de uma função, mas uma quer saber quando e se a função foi executada (fluxo de execução), enquanto que profiling quer saber quantas vezes ela foi chamada, e porque ela esta demorando tanto para ser executada (performance).

Normalmente usa-se tracing quando queremos debugar um problema ou entender uma aplicação, e profiling quando queremos compreender mais profundamente o uso dos recursos pela aplicação para identificar gargalos de performance.

Na prática, elas se complementam.

UMA METODOLOGIA DE PROFILING

Antes de começar a falar das ferramentas, mais um ponto importante sobre o uso de profiling em Linux embarcado. Tenha sempre em mente uma metodologia para o uso de ferramentas de profiling.

  1. Defina onde você esta. Qual a performance atual? Que problema você quer resolver? Lentidão? Travamentos? Tempo de resposta de algum dispositivo de I/O?
  2. Defina onde você quer chegar. Qual a performance desejada? Para que situações? Com quantos usuários logados? Com quantas aplicações rodando simultaneamente?
  3. Faça a análise usando uma ou mais ferramentas de profiling.
  4. Identifique o problema e corrija (mude apenas uma coisa de cada vez).
  5. Documente.
  6. Volte ao passo 3 enquanto a performance atual não alcançar a performance desejada.

E prepare-se, esse processo exige muita paciência!

COMEÇANDO COM FERRAMENTAS SIMPLES

Existem algumas ferramentas mais simples, que podem "tirar uma fotografia" do sistema. Em alguns casos, é tudo o que você precisa. Exemplos:

As ferramentas "top" e "ps" exibem uma lista dos processos em execução. Algumas informações úteis como o uso de memória e de CPU de cada processo são interessantes para analisar o consumo de recursos do equipamento. O comando "top", em específico, é bem útil porque ele ordena e coloca no topo os processos que estão consumindo mais recursos no momento.

$ ps auxf
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         2  0.0  0.0      0     0 ?        S    Jun10   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S    Jun10   0:01  \_ [ksoftirqd/0]
root         4  0.0  0.0      0     0 ?        S    Jun10   0:00  \_ [migration/0]
...
root         1  0.0  0.0   2888  1412 ?        Ss   Jun10   0:00 /sbin/init
root       347  0.0  0.0   2392   608 ?        S    Jun10   0:00 upstart-udev-bridge --daemon
root       353  0.0  0.0   2860   508 ?        Ss   Jun10   0:00 udevd --daemon
root     31221  0.0  0.0   2856   696 ?        S    10:40   0:00  \_ udevd --daemon
root     31222  0.0  0.0   2856   668 ?        S    10:40   0:00  \_ udevd --daemon
daemon     757  0.0  0.0   1960   532 ?        Ss   Jun10   0:00 portmap
...
$ top
top - 12:03:36 up 14:05,  6 users,  load average: 8.66, 8.30, 5.39
Tasks: 232 total,   8 running, 224 sleeping,   0 stopped,   0 zombie
Cpu(s): 88.1%us, 11.9%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   2060440k total,  1480392k used,   580048k free,    39968k buffers
Swap:  3887692k total,    87912k used,  3799780k free,   252996k cached
 
PID   USER      PR  NI  VIRT  RES  SHR S %CPU %MEM  TIME+    COMMAND
 1173 root      20   0  106m  51m 8456 S    9  2.5  18:07.18 Xorg
16435 sprado    20   0     0    0    0 R    7  0.0   0:00.22 cc1plus
16442 sprado    20   0  278m  16m 6008 R    7  0.8   0:00.21 cc1plus
16451 sprado    20   0  275m  13m 5908 R    5  0.7   0:00.14 cc1plus
...

Para saber qual o consumo total de memória, você pode usar o comando "free":

$ free
             total       used       free     shared    buffers     cached
Mem:       2060440    1573096     487344          0      45056     338912
-/+ buffers/cache:    1189128     871312
Swap:      3887692      87912    3799780

E o comando "iostat" pode te dar uma noção do uso de CPU e I/Os:

$ iostat
Linux 2.6.35-24-generic (sprado-desktop) 	06/11/2011 	_i686_	(2 CPU)
 
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           7.28    0.06    2.05    2.09    0.00   88.52
 
Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn
sda              11.85      1515.95      1610.64   77310144   82139048
sdb               8.78       215.64       390.20   10997298   19899536
sdc               5.44       102.98        85.22    5251933    4345860
sdd               0.01         0.05         0.26       2454      13157
sdh               0.00         0.13         0.00       6507          6

Mas como essas ferramentas conseguem extrair todas estas informações? Do diretório "/proc"! Esta tudo lá. É uma fotografia do sistema exportada pelo kernel através do procfs. Então o que estas ferramentas fazem é extrair as informações e exibi-las de forma mais amigável.

A ferramenta "procinfo", por exemplo, mostra um resumo interessante do uso de CPU, memória, I/O e rede:

$ procinfo
Memory:        Total        Used        Free     Buffers
RAM:         2060440     1803240      257200       47152
Swap:        3887692       97936     3789756
 
Bootup: Fri Jun 10 21:58:14 2011   Load average: 9.88 9.52 7.54 12/477 24141
 
user  :      02:17:04.95   7.9%  page in :         46886760
nice  :      00:01:05.71   0.1%  page out:         53492519
system:      00:33:57.85   2.0%  page act:          2062681
IOwait:      00:35:47.53   2.1%  page dea:           968997
hw irq:      00:00:00.11   0.0%  page flt:        109096660
sw irq:      00:02:33.38   0.1%  swap in :             3941
idle  :   1d 01:19:28.89  87.8%  swap out:            27003
uptime:      14:16:40.87         context :        303817744
 
irq   0:        215  timer               irq  17:     139064  uhci_hcd:usb3
irq   1:          2  i8042               irq  18:          0  uhci_hcd:usb4
irq   4:          4                      irq  19:          0  uhci_hcd:usb5
irq   6:          3  floppy              irq  20:     385517  ehci_hcd:usb1, uh
irq   7:          0  parport0            irq  21:   20181408  eth0
irq   8:          1  rtc0                irq  23:    1360640  ata_piix
irq   9:          0  acpi                irq  43:      13620  hda_intel
irq  14:     513661  ata_piix            irq  44:     101316  eth1
irq  15:          0  ata_piix
 
sda           503660r          100420w   sde                0r               0w
sdb           378301r           75021w   sdf                0r               0w
sdc           172751r          110009w   sdg                0r               0w
sdd              369r              99w   sdh              235r               1w
 
eth0        TX 192.50MiB     RX 284.26MiB     tun0        TX 0.00B         RX 0.00B
eth1        TX 362.79MiB     RX 12.67MiB      vboxnet0    TX 0.00B         RX 0.00B
lo          TX 77.02KiB      RX 77.02KiB

Mas Sergio, como vou identificar o problema usando estas ferramentas? A resposta é simples, não vai! O que estas ferramentas fazem é te dar um norte, um caminho a seguir.

Por exemplo: seu sistema esta lento demais. Você executa o comando "free" e descobre que só tem 1MB de RAM disponível. Então você executa o comando "ps" e identifica um processo consumindo mais de 10M de memória, e parece que cada vez que você executa o "ps", este valor aumenta um pouco. Opa, isso me parece memory leak! Então você usa o Valgrind, uma ferramenta mais poderosa, para analisar esta aplicação e identificar a origem do memory leak.

Esta é a filosofia UNIX: muitas ferramentas, mas cada uma fazendo seu papel, com um objetivo bem específico. Sabendo juntar tudo isso, você vai criar uma ferramenta muito mais poderosa, e resolver problemas muito mais facilmente. O que vem pela frente? Gprof, Valgrind, OProfile, perf e muito mais!

Enquanto isso, me diz, você já trabalhou com profiling em Linux? Quais ferramentas?

Um abraço,

Sergio Prado

Navegue
Creative Commons Este trabalho de Sergio Prado é licenciado pelo
Creative Commons BY-NC-SA 3.0.