7 de dezembro de 2021

Do bit Ao Byte

Embarcados, Linux e programação

Comandos GDB para usar com Black Magic Probe

comandos GDB | transformar um BluePill em debugger

No artigo “Como transformar um BluePill em JTAG” vimos alguns comandos GDB que já são suficientes para um debug mínimo. Dá pra fazer muito mais, não só por linha de comando, mas não é uma má ideia aprender fazer algumas coisas por linha de comando, então vamos montar nossa lista de referências dos comandos mais aplicáveis.

Conectar ao GDB

No artigo supracitado vimos como instalar o gdb-multiarch e como executá-lo. Basta chamar o comando gdb-multiarch. O resultado será um shell com um prompt (gdb), onde podemos digitar os demais comandos.

Conectar o GDB ao Black Magic Probe

Estando no prompt do GDB, simplesmente digite target extended-remote /dev/ttyACM0. Se a porta for outra, se o sistema operacional for outro, adapte a porta para corresponder ao seu sistema. Se precisa saber como identificar a porta, mais uma vez, o artigo citado no primeiro parágrafo o auxiliará com isso tudo.

Lista de comandos GDB disponíveis no Black Magic Probe

Digite monitor help no prompt do GDB para ver os comandos disponíveis.

A lista de comandos é context sensitive, isto é, muda conforme o alvo. O comando para o alvo STM32: monitor erase_mass, não estará disponível até que o alvo seja escaneado.

Encontrar alvos conectados

Há dois comandos para varrer por dispositivos que estejam usando a interface JTAG ou SWD; monitor jtag_scan e monitor swdp_scan.

Para desconectá-lo, use monitor tpwr enable. Para conectar-se a ele, use attach N, onde N é o número do dispositivo que aparece.

Acessar o mapeamento de memória

Esse comando instrui o GDB a permitir acesso à memória fora do dispositivo, conhecida como memory map. Use: set mem inaccessible-by-default off.

Como carregar o firmware com GDB

Para carregar o firmware para o dispositivo alvo, usamos load /tmp/arduino_build_123456/firmware.elf para debug. Esse caminho e nome de arquivo são parcialmente fictícios, uma vez que o path se refere ao mesmo caminho que encontramos a compilação da IDE do Arduino no Linux, mudando basicamente os números e o nome do firmware, para o correspondente ao programa compilado na IDE do Arduino.

Para fazer o debug do artigo anterior, usei seguidamente o comando file /tmp/arduino_build_123456/firmware.elf, com os mesmos princípios descritos acima. A diferença é que aqui deveremos confirmar com um y e depois disso poderemos fazer o debug de um modo mais fluido.

Podemos executar o firwamre de teste com run até que seja interrompido por outro evento, enquanto start rodará da inicialização até a entrada de main. Usando a API do Arduino fica um pouco estranho, porque invés da inicialização de variáveis, declarações de função e outras coisas que acontecem fora de main, com a API do Arduino temos setup e loop dentro de main, então não tem muito proveito nesse momento.

Podemos interromper a aplicação com Ctrl+C também, quando em execução.

Condições de parada

Essa é a parte mais gostosa da depuração. Podemos adicionar pontos de parada no código e assim analisar o funcionamento de uma função, o retorno de uma variável, encontrar um ponto de exceção no código e outras coisas mais. Podemos também assistir diretamente a mudança de valor de uma variável, com watch <variável>.

Para adicionar um breakpoint podemos usar a forma break <função> ou break <arquivo>:<linha>.

Lembre-se: Breakpoints interrompem quando o ponto alvo entra em seu momento de execução. Os watchpoints interromperão quando a variável monitorada mudar seu valor.

comandos GDB

Esse exemplo de comandos GDB acima é baseado nesse código:

uint16_t i = 0;
 void setup() {
   // initialize digital pin PC13 as an output.
   pinMode(PB12, OUTPUT);
   pinMode(PC13, OUTPUT);
 }
 // the loop function runs over and over again forever
 void loop() {
   digitalWrite(PB12, HIGH);   // turn the LED on (HIGH is the voltage level)
   digitalWrite(PC13, HIGH);
   delay(500);              // wait for a second
   digitalWrite(PB12, LOW);    // turn the LED off by making the voltage LOW
   digitalWrite(PC13, LOW);
   delay(500);              // wait for a second
   teste();
 }
 void teste(){
   for (uint8_t j=0; j<20;j++){
      digitalWrite(PB12, HIGH);   // turn the LED on (HIGH is the voltage level)
      digitalWrite(PC13, HIGH);
      delay(300);              // wait for a second
      digitalWrite(PB12, LOW); 
      digitalWrite(PC13, LOW);
      // turn the LED off by making the voltage LOW
      delay(100); 
      i++;
   }
 }

Examinar o estado do alvo

Podemos também avaliar variáveis, memória ou registradores, usando os comandos print <expr>, x <addr>, ou info registers.

Um tanto mais avançado, podemos também fazer um dump da memória para um formato hexa Intel usando dump ihex memory <arquivo> <início> <fim> ou outro formato alternativo, que pode ser consultado através do menu de ajuda help dump.

Finalizando

Se quiser desconectar do dispositivo, use detach. Usando kill, o alvo será reiniciado ao desconectar.

O GDB tem vários outros recursos, mas utilizá-lo através de uma IDE é bastante cômodo, e leva o desenvolvimento para microcontroladores a outro nível. Veremos isso em breve, acompanhe!

Se quiser saber mais sobre o GDB, visite o site oficial do projeto.

Uma referência espetacular pode ser encontrada nesse outro link.

Onde comprar JTAG JLink para RP Pico?

Se não pretende fazer seu próprio JTAG, o J-Link que estou usando é da Werneck Eletronics, que você pode adquirir por um excelente preço na Shopee. Se não conhece a Shopee, vale a pena. Como não tem o achaque do MercadoLivre, os preços são melhores. A MASUGUX também adotou e acredito que seja uma tendência.

Eles têm também um outro modelo que presumo ser a mesma coisa, mas não posso confirmar. Confira nesse link.

Vídeo

Esse vídeo demonstra um processo completo o suficiente para servir de parâmetro. Olha só que delícia é usar um debugger!

Revisão: Ricardo Amaral de Andrade