Saltar para o conteúdo

Introdução aos Sistemas Operacionais/Exemplo de técnica de Debug: Debug com printk

Fonte: Wikiversidade
Marcos André Oliveira - Matr. 060207 ( marcosandre19@gmail.com )
Murilo A. Vasconcelos - Matr. 094816 ( muriloufg@gmail.com )
Paulo Cezar P. Costa - Matr. 080153 ( paulocezar.ufg@gmail.com )
Vinicius Vieira Pessoni - Matr. 080160 ( viniciuspessoni@gmail.com )

Licenciamento

[editar | editar código-fonte]

GPL

Objetivo e descrição

[editar | editar código-fonte]
Debug via Impressão ( printk() )


Depurar o kernel não é algo tão complicado. Na verdade pode ser uma tarefa simples,
que exige o domínio de técnicas simples, e um bocado de paciência e perseverança.

Uma das técnicas mais comuns e simples de depurar o que está acontecendo com a aplicação é atravez
de impressões em pontos estratégicos do código e o mesmo pode ser feito quando queremos debugar o
código do kernel, o que é possível graças à função printk().

A função printk() é, em espaço de núcleo, equivalente a função printf() disponível apenas no
espaço de usuário. Apesar de bastante semelhante à função printf(), printk() tem algumas diferenças,
entre elas a possibilidade classificar as mensagem de acordo com sua gravidade associando diferentes
níveis e prioridades as mensagens. Tal diferenciação é feita com o uso de macros que estão definidas
no arquivo de cabeçalho "linux/kernel.h".

Tais macros são:

KERN_EMERG - Indicando uma mensagem de emergência.
KERN_ALERT - Indicando uma situção que necessita de ação imediata.
KERN_CRIT - Aponta uma condição crítica, geralmente relacionada a falhas graves de hardware ou software.
KERN_ERR - Indica que algum erro ocorreu, geralmente drivers comunicam falhas de hardware atravéz dessa macro.
KERN_WARNING - Usada para reportar situações problemáticas que não prejudicam muito o sistema.
KERN_NOTICE - Geralmente para situações que, apesar de normais, merecem atenção.
KERN_INFO - Mensagens meramente informativas, indicando o estado do hardware ou de variáveis.
KERN_DEBUG - Mensagens de debug para rastrear em que ponto do codigo a aplicação se encontra.


O printk é utilizado da seguinte maneira:

printk( MACRO "MENSAGEM" <, variaveis > );

em tempo de compilação a macro é convertida para uma string de mensagem e incorporada à mensagem que será impressa.

Código-fonte

[editar | editar código-fonte]

flash.c

/* 
 *  flash.c - Blink keyboard leds until the module is unloaded.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/tty.h>				/* For fg_console, MAX_NR_CONSOLES */
#include <linux/kd.h>				/* For KDSETLED */
#include <linux/vt.h>
#include <linux/console_struct.h>	/* For vc_cons */

MODULE_DESCRIPTION("Example module illustrating the use of Keyboard LEDs.");
MODULE_AUTHOR("Daniele Paolo Scarpazza");
MODULE_LICENSE("GPL");

extern int fg_console;
struct timer_list my_timer;
struct tty_driver *my_driver;
char kbledstatus = 0;

#define BLINK_DELAY   HZ/5
#define ALL_LEDS_ON   0x07
#define RESTORE_LEDS  0xFF

static void my_timer_func(unsigned long ptr)
{
	int *pstatus = (int *)ptr;

	if (*pstatus == ALL_LEDS_ON)
	{

#ifdef MODULE_DEBUG
		printk( KERN_NOTICE "Todos os leds estao acesos!\n");
#endif		
		*pstatus = RESTORE_LEDS;
	}
	else
	{
		*pstatus = ALL_LEDS_ON;
	}
	
	((my_driver->ops)->ioctl) (vc_cons[fg_console].d->vc_tty, NULL, KDSETLED, *pstatus);

	my_timer.expires = jiffies + BLINK_DELAY;
	add_timer(&my_timer);
}

static int __init kbleds_init(void)
{
	int i;
	
#ifdef MODULE_DEBUG
	printk(KERN_INFO "Iniciando o modulo em modo de DEBUG\n");
#endif

	printk(KERN_INFO "kbleds: loading\n");
	printk(KERN_INFO "kbleds: fgconsole is %x\n", fg_console);
	
	for (i = 0; i < MAX_NR_CONSOLES; i++)
	{
		if (!vc_cons[i].d) break;
		
		printk(KERN_INFO "poet_atkm: console[%i/%i] #%i, tty %lx\n", i,
								  MAX_NR_CONSOLES, vc_cons[i].d->vc_num,
						          (unsigned long)vc_cons[i].d->vc_tty);
	}
	printk(KERN_INFO "kbleds: finished scanning consoles\n");

	my_driver = vc_cons[fg_console].d->vc_tty->driver;
	printk(KERN_INFO "kbleds: tty driver magic %x\n", my_driver->magic);

#ifdef MODULE_DEBUG
	printk( KERN_NOTICE "Criando timer..\n");
#endif			
	init_timer(&my_timer);
#ifdef MODULE_DEBUG
	printk( KERN_NOTICE "Timer criado.\n");
#endif		
	
	my_timer.function = my_timer_func;
	my_timer.data = (unsigned long)&kbledstatus;
	my_timer.expires = jiffies + BLINK_DELAY;
	add_timer(&my_timer);
	
#ifdef MODULE_DEBUG
	printk( KERN_NOTICE "Modulo Inicializado com sucesso!\n");
#endif		
	
	return 0;
}

static void __exit kbleds_cleanup(void)
{
	
	printk(KERN_INFO "kbleds: unloading...\n");

#ifdef MODULE_DEBUG
	printk(KERN_INFO "Finalizando o modulo...\n");
#endif

	del_timer(&my_timer);
	
#ifdef MODULE_DEBUG
	printk( KERN_NOTICE "Timer deletado.\n");
#endif		
	
	((my_driver->ops)->ioctl) (vc_cons[fg_console].d->vc_tty, NULL, KDSETLED,
			    RESTORE_LEDS);
}

module_init(kbleds_init);
module_exit(kbleds_cleanup);


Procedimentos para testes

[editar | editar código-fonte]

Para testar o basta executar o comando make usando o Makefile abaixo
alterando o valor da variável DEBUG para qualquer coisa diferente de y as mensagens
de debug são desativadas.

Makefile

KER_DIR := /lib/modules/$(shell uname -r)/build
DEBUG = y

obj-m = flash.o

ifeq ($(DEBUG),y)
	DEBFLAGS = -O -g -DMODULE_DEBUG 
else
	DEBFLAHS = -O2
endif

EXTRA_CFLAGS += $(DEBFLAGS)

all:
	$(MAKE) -C $(KER_DIR) M=$(PWD) modules
	@rm -rf *.mod* Module.symvers *.o *~ *.makers *.order

clean:
	@rm -rf *~ *.o *.ko


Considerações finais

[editar | editar código-fonte]

Inicialmente tivemos várias dúvidas sobre o que deveria ser feito no trabalho, o que acreditamos ter sido
o principal empecilho para que fosse produzido um material com mais qualidade. Quando nos demos
conta do que deveria ser feito o tempo já estava curto, e o trabalho pode ter acabado ficando meio simplista.

Escolhemos fazer o debug usando printk(), uma forma bem simples e poderosa de depurar o código,
não tanto por suas características, mas pelo menos para a maioria do grupo, devido ao fato que estamos
acostumados a depurar nossos algoritmos dessa forma.

A principal dificuldade foi controlar quando as mensagens devem ou não aparecer.
Fizemos isso definindo uma variável que deve ser ativada ou desativada no Makefile, porém
a intenção era definir um alvo, "debug", assim quando fosse executado "make debug", o módulo seria
compilado para executar em modo debug, e quando o comando fosse apenas "make" a execução ocorreria
normalmente.

[1] Linux Device Drivers 3rd Edition
[2] The Linux Kernel Module Programming Guide
[3] Linux Kernel Documentation