Depurando o kernel Linux com o KGDB na Beaglebone Black

- por Sergio Prado

Categorias: Beaglebone Black, Linux Tags: , ,

Sim, da mesma forma que você depura uma aplicação, é possível também depurar o kernel Linux. E a resposta para esta funcionalidade é o KGDB.

O KGDB implementa o protocolo de comunicação remota do GDB, possibilitando a comunicação com um cliente GDB. Portanto, o processo de depuração com o KGDB acontece remotamente, com o host (máquina de desenvolvimento) conectado ao target (plataforma de hardware) por uma porta serial RS232 ou por uma interface de rede (via UDP/IP).

Atualmente, diversas arquiteturas suportam o KGDB, dentre elas x86, PPC, MIPS e ARM. Apenas o suporte à comunicação pela rede do KGDB ainda não foi integrado à versão oficial do kernel.

Vamos então colocar o KGDB para funcionar com a Beaglebone Black?

PREPARANDO O KERNEL

O primeiro passo é compilar o kernel com as opções abaixo habilitadas:

Kernel hacking  --->
   [*] KGDB: kernel debugger  --->
      <*>   KGDB: use kgdb over the serial console
   [*] Compile the kernel with debug info

Compile e atualize o kernel da Beaglebone Black.

PREPARANDO A COMUNICAÇÃO SERIAL

Como utilizaremos a porta serial tanto para acessar a console da Beaglebone Black quanto para depurar com o KGDB, é necessário uma aplicação de proxy para gerenciar o acesso à porta serial.

Para isso, já existe uma aplicação chamada agent-proxy, que pode ser compilada e executada conforme abaixo:

$ git clone git://git.kernel.org/pub/scm/utils/kernel/kgdb/agent-proxy.git/
$ cd agent-proxy/
$ make
$ ./agent-proxy 5550^5551 0 /dev/ttyUSB0,115200 &

Esta aplicação irá abrir duas portas TCP para conexão com a serial. No exemplo acima, a porta 5550 deve ser usada para a comunicação via console, e a porta 5551 para comunicação com o KGDB.

Use então o comando abaixo para iniciar a comunicação com a console da Beaglebone Black:

$ telnet localhost 5550

COLOCANDO O KERNEL NO MODO KGDB

Agora o próximo passo é colocar o kernel no modo KGDB.

Você pode fazer isso em tempo de boot, passando os parâmetros abaixo para o kernel:

kgdboc=ttyO0,115200n8 kgdbwait

Ou então em tempo de execução, no terminal do target, executando os comandos abaixo:

# echo ttyO0,115200n8 > /sys/module/kgdboc/parameters/kgdboc
# echo g > /proc/sysrq-trigger

Em ambos os casos, o kernel deverá iniciar o KGDB e aguardar a conexão de um cliente GDB:

[ 0.944295] kgdb: Waiting for connection from remote gdb...

USANDO O CLIENTE GDB

Agora é só iniciar o GDB do toolchain na máquina de desenvolvimento, passando a imagem vmlinux do kernel da Beaglebone Black.

$ arm-linux-gdb vmlinux

E então se conectar com o KGDB pela porta 5551 do proxy:

(gdb) target remote localhost:5551
Remote debugging using localhost:5551
kgdb_breakpoint () at kernel/debug/debug_core.c:1012
1012 arch_kgdb_breakpoint();

O kernel deverá parar no breakpoint do KGDB. A partir daí é só usar o GDB normalmente.

Para continuar a execução do kernel, basta digitar “continue” ou apenas “c“:

(gdb) c

Para parar a execução, pressione CTRL-C no terminal do GDB.

Você pode colocar um breakpoint em qualquer função:

(gdb) b omap_rtc_read_time
(gdb) c

O KGDB irá parar a execução quando o breakpoint for atingido:

Breakpoint 1, omap_rtc_read_time (dev=0xdf10ee10, tm=0xdf4c5ecc) at drivers/rtc/rtc-omap.c:220
220 {

Você poderá executar o kernel passo-a-passo, inspecionar variáveis, imprimir um backtrace do stack, ou executar qualquer outra função disponível no GDB.

DEPURANDO COM O DDD

Se você se sentir mais confortável com uma ferramenta gráfica, pode usar também um frontend gráfico disponível para o GDB, como o Eclipse ou o DDD:

$ ddd --debugger gdb-multiarch vmlinux

ddd-kgdb

NEM TUDO SÃO FLORES

Durante os testes iniciais, não consegui fazer o GDB funcionar. Sempre que tentava me conectar com o KGDB, acontecia o erro abaixo:

Internal error: Oops - undefined instruction: 0 [#1] SMP THUMB

Fiz algumas pesquisas e descobri que o GDB que eu estava usando utilizava algumas instruções que o kernel não suportava, e para resolver este problema precisei aplicar o patch abaixo no kernel:

diff --git a/arch/arm/include/asm/kgdb.h b/arch/arm/include/asm/kgdb.h
index 48066ce9ea34..4421df70141b 100644
--- a/arch/arm/include/asm/kgdb.h
+++ b/arch/arm/include/asm/kgdb.h
@@ -31,10 +31,17 @@
  * Note to ARM HW designers: Add real trap support like SH && PPC to
  * make our lives much much simpler. :)
  */
+#ifdef CONFIG_THUMB2_KERNEL
+#define BREAK_INSTR_SIZE 2
+#define KGDB_BREAKINST 0xdefe
+#define KGDB_COMPILED_BREAK 0xdeff
+#else
 #define BREAK_INSTR_SIZE	4
 #define GDB_BREAKINST		0xef9f0001
 #define KGDB_BREAKINST		0xe7ffdefe
 #define KGDB_COMPILED_BREAK	0xe7ffdeff
+#endif
+
 #define CACHE_FLUSH_IS_SAFE	1
 
 #ifndef	__ASSEMBLY__
diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c
index 778c2f7024ff..cc905fc52f08 100644
--- a/arch/arm/kernel/kgdb.c
+++ b/arch/arm/kernel/kgdb.c
@@ -157,14 +157,16 @@ static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr)
 	return 0;
 }
 
+#define INSTR_MASK ((1ULL<<(BREAK_INSTR_SIZE * 8))-1)
+
 static struct undef_hook kgdb_brkpt_hook = {
-	.instr_mask		= 0xffffffff,
+	.instr_mask		= INSTR_MASK,
 	.instr_val		= KGDB_BREAKINST,
 	.fn			= kgdb_brk_fn
 };
 
 static struct undef_hook kgdb_compiled_brkpt_hook = {
-	.instr_mask		= 0xffffffff,
+	.instr_mask		= INSTR_MASK,
 	.instr_val		= KGDB_COMPILED_BREAK,
 	.fn			= kgdb_compiled_brk_fn
 };

Apesar dos testes terem sido realizados na Beaglebone Black, os procedimentos descritos neste artigo devem funcionar em qualquer outra plataforma de hardware que tenha uma porta serial e o kernel Linux com suporte ao KGDB.

Happy debugging!

Sergio Prado

  • Emerson

    Ola Sérgio, consegui executar tudo conforme tuas instruções, porém parei no “Internal error”, não estou sabendo aplicar esse patch, vc poderia dar mais detalhes de como fazer ?? obrigado e parabens novamente pelo blog !! abs.

    • Emerson

      Blz Sergio, consegui aplicar, usando o git am file.patch , porém só copiando o texto aqui não funcionou, tive que usar o cabeçalho de outro patch pra funcionar. valeu

      • Legal Emerson! Poderia me passar as alterações que você teve que fazer no patch que publiquei no artigo?

        • Emerson

          Ola Sergio, meu patch ficou assim:

          From be35cf60268f3442e20ea195f598113e5d31238f Mon Sep 17 00:00:00 2001

          From: Nicolas Pitre

          Date: Tue, 12 Mar 2013 13:00:42 +0100

          Subject: [PATCH KGDB] ARM: 1: fix the KGDB fix

          Commit 455bd4c430b0 (“ARM: 7668/1: fix memset-related crashes caused by

          recent GCC (4.7.2) optimizations”) attempted to fix a compliance issue

          with the memset return value. However the memset itself became broken

          by that patch for misaligned pointers.

          This fixes the above by branching over the entry code from the

          misaligned fixup code to avoid reloading the original pointer.

          Also, because the function entry alignment is wrong in the Thumb mode

          compilation, that fixup code is moved to the end.

          While at it, the entry instructions are slightly reworked to help dual

          issue pipelines.

          Signed-off-by: Nicolas Pitre

          Tested-by: Alexander Holler

          Signed-off-by: Russell King

          arch/arm/include/asm/kgdb.h | 8 ++++++++

          arch/arm/kernel/kgdb.c | 6 ++++–

          2 files changed, 12 insertions(+), 2 deletions(-)

          diff –git a/arch/arm/include/asm/kgdb.h b/arch/arm/include/asm/kgdb.h

          index 48066ce9ea34..4421df70141b 100644

          — a/arch/arm/include/asm/kgdb.h

          +++ b/arch/arm/include/asm/kgdb.h

          @@ -31,10 +31,17 @@

          * Note to ARM HW designers: Add real trap support like SH && PPC to

          * make our lives much much simpler. :)

          */

          +#ifdef CONFIG_THUMB2_KERNEL

          +#define BREAK_INSTR_SIZE 2

          +#define KGDB_BREAKINST 0xdefe

          +#define KGDB_COMPILED_BREAK 0xdeff

          +#else

          #define BREAK_INSTR_SIZE 4

          #define GDB_BREAKINST 0xef9f0001

          #define KGDB_BREAKINST 0xe7ffdefe

          #define KGDB_COMPILED_BREAK 0xe7ffdeff

          +#endif

          +

          #define CACHE_FLUSH_IS_SAFE 1

          #ifndef __ASSEMBLY__

          diff –git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c

          index 778c2f7024ff..cc905fc52f08 100644

          — a/arch/arm/kernel/kgdb.c

          +++ b/arch/arm/kernel/kgdb.c

          @@ -157,14 +157,16 @@ static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr)

          return 0;

          }

          +#define INSTR_MASK ((1ULL<<(BREAK_INSTR_SIZE * 8))-1)

          +

          static struct undef_hook kgdb_brkpt_hook = {

          – .instr_mask = 0xffffffff,

          + .instr_mask = INSTR_MASK,

          .instr_val = KGDB_BREAKINST,

          .fn = kgdb_brk_fn

          };

          static struct undef_hook kgdb_compiled_brkpt_hook = {

          – .instr_mask = 0xffffffff,

          + .instr_mask = INSTR_MASK,

          .instr_val = KGDB_COMPILED_BREAK,

          .fn = kgdb_compiled_brk_fn

          };

          1.8.1.2

  • vinifr

    Ola Sérgio,

    Quando digito : echo ttyO0,115200n8 > /sys/module/kgdboc/parameters/kgdboc, retorna -bash: echo: write error: No such device

    Mas o arquivo existe. Compilei o kernel com kgdb

    • vinifr

      Ola Sérgio, resolvi o problema. Para mim funcionou usando ttyS0… Mas outra dúvida. Como iniciar o kgdb no uboot?

      • Basta passar os parâmetros abaixo na linha de comandos do kernel (bootargs):

        kgdboc=ttyS0,115200 kgdbwait

        Abs,

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