Articoli Manifesto Tools Links Canali Libri Contatti ?
SIForge / GaraGuru / .1

GaraGuru .1: Sfida 2, Bug Hunter

Abstract
Descrivete il peggior bug che avete dovuto affrontare. Mostrate il segmento di codice incriminato, e raccontate come avete diagnosticato il problema e come lo si è risolto.
Data di stesura: 15/06/2004
Data di pubblicazione: 22/11/2004
Ultima modifica: 04/04/2006
di Daniele Ferro Discuti sul forum   Stampa

Ripensandoci adesso, quell'odioso errore incontrato qualche anno fa, ora mi sembra nulla di più che un semplice bug, ma con le poche conoscenze che avevo allora (e che ho ancora oggi, se penso all'intero sapere informatico) persi parecchio tempo per trovare una soluzione decente.

Avevo cominciato a programmare da poco più di un anno in Pascal, e i miei programmi si limitavano a qualche giochino (tipo MasterMind) o qualche grafico colorato. Usavo Turbo Pascal 7.0, e mi ero imbattuto nel noto errore: «Error 200: division by zero»; inizialmente non capii cosa ci fosse nel mio programma di sbagliato, così cominciai a «commentare» pezzi di codice per vedere se riuscivo a isolare l'errore: niente da fare. Alla fine rimaneva scritto solo questo:

  1. Uses Crt; 
  2. Begin 
  3. End. 

Provai a togliere la prima riga e l'errore scomparve. Al che, divenne chiaro che il problema non era nel mio programma, ma nella unit Crt. Per me significava un punto morto, non sapendo come fare a risolverlo, soprattutto perché l'errore non si manifestava sempre, solo certe volte, anzi, solo su certi computer: scoprii che sui 386 e 486 funzionava, mentre sul mio AMD-K6 400MHz no. Doveva essere legato alla velocità dei computer, ma, non sapendo cosa fare, programmai per qualche tempo senza ricorrere alla unit Crt.

Non era certo semplice, dato che avevo bisogno di funzioni come Readkey o Keypressed; per non parlare dei colori: non riuscivo a scrivere testo colorato, dato che non avevo la funzione Textcolor.

Dopo qualche mese, mi accorsi che non riuscivo ad andare avanti, dovevo trovare una soluzione!

Le soluzioni erano 2: aggirare l'ostacolo (non usare la unit Crt), o non rispettare le regole, correggendo i miei programmi (patchare il codice). Certo, non avevo idea che in internet ci fossero così tante soluzioni al mio stesso problema, come scoprii in seguito.

Fortunatamente la mia conoscenza dell'assembly mi venne in aiuto; sapevo qualcosa, avendo anche scritto semplici programmini (usando il Debug del DOS, sigh), così mi procurai una buona lista degli interrupts e cercai... Ciò che scoprii mi illuminò: molte delle funzioni della Crt, non erano altro che servizi dell'int 21h, come Readkey (servizio 08h) o Keypressed (servizio 0Bh). Scrissi così la Crt2, una nuova unit, con le stesse (o quasi) funzioni della Crt, scritte da me; per esempio:

  1. Function ReadKey: Char; Assembler; 
  2.   Asm 
  3.     MOV  AH, 008h 
  4.     INT  021h 
  5.   End; 
  6.  
  7. Procedure HighVideo; Assembler; 
  8.   Asm 
  9.     MOV  AX, 01003h 
  10.     MOV  BX, 00000h 
  11.     INT  010h 
  12.   End; 

Riuscii così a programmare per parecchi mesi, ma non ero ancora contento dato che il problema non era stato risolto.

Così un pomeriggio, armato di molta pazienza, tentai di correggere i miei programmi.

Utilizzando un buon disassembler, esaminai l'entry point degli eseguibili compilati con la unit Crt; erano più o meno così:

00000060: 9A00006400          call    00064:00000
00000065: 9A0D000200          call    00002:0000D
0000006A: 55                  push    bp
0000006B: 89E5                mov     bp,sp
0000006D: 31C0                xor     ax,ax

Mentre quelli compilati senza Crt erano:

00000050: 9A00000200          call    00002:00000
00000055: 55                  push    bp
00000056: 89E5                mov     bp,sp
00000058: 31C0                xor     ax,ax

Da ciò ho capito che la call che creava problemi era quella che puntava all'offset 000Dh, e andando ad esaminare il codice ho scoperto che l'unica operazione che potesse causare un errore di divisione per 0 era la seguente DIV:

0000010A: F7D0                not     ax
0000010C: F7D2                not     dx
0000010E: B93700              mov     cx,00037
00000111: F7F1                div     cx
00000113: A35C00              mov     [0005C],ax

In questo caso, se il dividendo (DX:AX) è troppo grande (come accade con pc troppo veloci), la divisione genera un errore.

Così, sostituii 037h con 0137h, in tal modo, pur sapendo che non sarebbe stata una soluzione del tutto corretta, il mio programma funzionava (senza visibili cambiamenti nell'esecuzione dopo la modifica)! Fu semplice scrivere un programmino che cercasse quella sequenza di istruzioni nei programmi, e sostituisse il byte funesto.

Poi estesi la modifica anche alla Crt originale, cercando quelle istruzioni nella libreria di Turbo Pascal e correggendole. Ora potevo anche fare a meno di correggere i programmi che scrivevo, visto che la Crt non produceva più quel fastidioso errore.

Potevo ritenermi soddisfatto, avevo risolto un fastidioso problema (sebbene non eccessivamente complesso) tutto da solo e, per un ragazzo di 16 anni qual ero io, tutto ciò dà veramente una grande soddisfazione.

Informazioni sull'autore

Daniele Ferro, studente del liceo scientifico tecnologico Luciano dal Cero di San Bonifacio (VR). Appassionato di programmazione, ha partecipato alle Olimpiadi Italiane di Informatica 2004.

È possibile consultare l'elenco degli articoli scritti da Daniele Ferro.

Altri articoli sul tema SIForge / GaraGuru / .1.

Risorse

  1. Bando della prima edizione di GaraGuru.
    http://www.siforge.org/articles/2004/05/10-garaguru.html
Discuti sul forum   Stampa

Cosa ne pensi di questo articolo?

Discussioni

Questo articolo o l'argomento ti ha interessato? Parliamone.