Introdução à profiling em Linux embarcado
- por Sergio Prado
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:
- 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.
- 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.
- 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.
- 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?
- Defina onde você quer chegar. Qual a performance desejada? Para que situações? Com quantos usuários logados? Com quantas aplicações rodando simultaneamente?
- Faça a análise usando uma ou mais ferramentas de profiling.
- Identifique o problema e corrija (mude apenas uma coisa de cada vez).
- Documente.
- 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