Μεταγλώττιση - compiling
Για να μεταγλωτίσουμε ένα πρόγραμμα από πηγαίο κώδικα (source code) σε εκτελέσιμο
ώστε να μπορούμε να το "τρέξουμε", χρησιμοποιούμε τον μεταγλωττιστή (compiler) gcc.
Εάν ο πηγαίος κώδικας του προγράμματος μας βρίσκεται σε ένα αρχείο my_program.c
τότε για να το μεταγλωτίσουμε δίνουμε την εντολή :
$> gcc my_program.c
Εάν το πρόγραμμά μας περιέχει συντακτικά λάθη -για παράδειγμα έχουμε ξεχάσει
κάποια παρένθεση, κάποιο ερωτηματικό, ξεχάσαμε να δηλώσουμε μια μεταβλητή την οποία
χρησιμοποιούμε στο πρόγραμμά μας κλπ- τότε ο compiler σταματάει τη μεταγλώτιση
και μας τυπώνει ένα μήνυμα το οποίο υποδεικνύει τα συντακτικά λάθη του προγράμματος
μας καθώς επίσης τη γραμμή στην οποία αυτά εμφανίζονται, έτσι ώστε ο προγραμματιστής
να τα διορθώσει.
Συχνά, ένα συντακτικό λάθος οδηγεί τον compiler να θεωρήσει και άλλες εντολές
του προγράμματος μας σαν λανθασμένες. Για αυτό το λόγο, όταν ο compiler μάς εμφανίζει
πολλά συντακτικά λάθη στο πρόγραμμά μας, μια χρήσιμη τακτική είναι να κάνουμε τη
διόρθωσή τους με τη σειρά που τα εμφανίζει ο compiler, καθώς κάποια συντακτικά
λάθη που εμφανίζει ο compiler πιθανόν να μην είναι λάθη αλλά να οφείλονται σε προηγούμενα
τα οποία οδήγησαν σε λανθασμένη μεταγλώτιση.
Όταν πλέον το πρόγραμμά μας δεν περιέχει συντακτικά λάθη τότε ο compiler δημιουργεί ένα
εκτελέσιμο αρχείο που ονομάζεται a.out ("a.exe" στα Windows). Για να εκτελέσουμε αυτό
το πρόγραμμα απλά γράφουμε το όνομά του στη γραμμή εντολών :
$> a.out
Για περισσότερες πληροφορίες για τις επιλογές που μπορείτε να χρησιμοποιήσετε με τον
gcc μπορείτε να κάνετε man gcc στα εργαστήρια και να δείτε αναλυτικά το
εγχειρίδιο του gcc.
Εκσφαλμάτωση - Debugging
Πολύ συχνά, παρόλο που το πρόγραμμά μας δεν έχει συντακτικά λάθη και εκτελείται
κανονικά, δεν παράγει τα αποτελέσματα που περιμέναμε καθώς υπάρχει κάποιο λογικό
λάθος στο πρόγραμμά μας. Τα λάθη αυτά είναι φυσικά πιο δύσκολο να βρεθούν από τα
συντακτικά λάθη καθώς ο compiler δεν μπορεί να μας κάνει υποδείξεις τί να διορθώσουμε.
Για να διορθώσουμε τα λογικά λάθη σε ένα πρόγραμμά μας καταρχήν αυτό που χρειάζεται
είναι να εξετάσουμε λεπτομερώς την αλληλουχία των εντολών και τη λογική του προγράμματος
μας και να βεβαιωθούμε ότι ότι οι εντολές που γράφουμε στο πρόγραμμά μας όταν εκτελεστούν
θα δώσουν το επιθυμητό αποτέλεσμα.
Ένα εργαλείο που μας βοηθάει πολύ για να βρούμε τα λογικά λάθη στα προγράμματά μας είναι
ο gdb (gnu debugger).
Για να μπορεί ο debugger να μας βοηθήσει πρέπει κατά τη μεταγλώτιση του προγράμματος μας
να συμπεριλάβουμε την επιλογή -g δηλαδή να κάνουμε compile ως εξής :
$> gcc -g my_program.c
Με αυτό τον τρόπο ο compiler μαζί με τον εκτελέσιμο κώδικα παράγει επιπλέον πληροφορίες
οι οποίες αργότερα θα βοηθήσουν τον debugger. Εάν δε συμπεριλάβουμε την επιλογή -g
κατά το compiling τότε ο debugger δεν θα μπορεί να μας βοηθήσει.
Εκτέλεση - running
Για να τρέξουμε τον debugger γράφουμε :
$> gdb a.out
και ο debugger θα ξεκινήσει, εμφανίζοντάς μας μια γραμμή εντολών :
(gdb)
στην οποία μπορούμε να γράφουμε εντολές και να παρεμβαίνουμε στην εκτέλεση του προγράμματός μας
ώστε να εξετάζουμε την ορθή λειτουργία του.
Παρακάτω εξετάζουμε μερικές από τις πιο βασικές εντολές του gdb. Για πλήρες
εγχειρίδιο χρήσης μπορείτε να κάνετε man gdb ή να κοιτάξετε τη σελίδα
http://www.gnu.org/manual/gdb-4.17/gdb.html.
-
run Ξεκινάει την εκτέλεση του προγράμματός μας.
Δηλαδή το πρόγραμμά μας πλέον τρέχει μέσα από το ελεγχόμενο περιβάλλον του gdb όπου
και μπορούμε να παρεμβαίνουμε κατά βούληση στην εκτέλεσή του. Το πρόγραμμα θα τρέχει έως
ότου φτάσει σε ένα breakpoint (δες παρακάτω στην εντολή break) ή να τερματίσει.
-
break Με την εντολή break μπορούμε να καθορίσουμε συγκεκριμένα σημεία του πηγαίου
κώδικά μας στα οποία θέλουμε να σταματήσει η εκτέλεση του προγράμματος. Η break συντάσσεται
με διάφορους τρόπους ανάλογα τί θέλουμε να κάνουμε :
-
break <number> Δίνουμε στην break σαν παράμετρο τον αριθμό μιας γραμμής του
κώδικά μας.Όταν η εκτέλεση του προγράμματος φτάσει σε αυτή τη γραμμή τότε ο gdb το
σταματάει και μας εμφανίζει ξανά τη γραμμή εντολών.
-
break <function name> Δίνουμε στην break σαν παράμετρο ένα όνομα συνάρτησης.
Μόλις η εκτέλεση του προγράμματος φτάσει σε ένα σημείο όπου θα καλεστεί η συνάρτηση αυτή
τότε η εκτέλεση του προγράμματος θα σταματήσει και θα εμφανιστεί η γραμμή εντολών του gdb.
-
step Με την εντολή step προχωράμε την εκτέλεση του προγράμματός μας κατά μία εντολή. Αυτό
φυσικά προϋποθέτει ότι το πρόγραμμα τρέχει (έχουμε ξεκινήσει την εκτέλεση με την εντολή run και
το πρόγραμμα δεν έχει ακόμα τερματίσει). Εάν κατά την εκτέλεση υπάρχει κάποια κλήση συνάρτησης
τότε η εντολή step θα προχωρήσει στην επόμενη εντολή μέσα στην συνάρτηση αυτή.
-
next Με την εντολή next προχωράμε την εκτέλεση του προγράμματός μας κατά μία εντολή, παρόμοια
με την εντολή step. Η διαφορά με τη step είναι ότι εάν κατά την εκτέλεση φτάσει σε ένα σημείο
όπου υπάρχει κλήση συνάρτησης, δεν θα προχωρήσει στην επόμενη εντολή μέσα στη συνάρτηση,
αλλά στην επόμενη εντολή μετά τη συνάρτηση, δηλαδή θα αντιμετωπίσει την κλήση σαν μία απλή
εντολή.
-
continue Με την εντολή continue ξεκινάει ξανά η εκτέλεση του προγράμματος από το σημείο
που έχει σταματήσει αυτή τη στιγμή και έως ότου συναντήσει ξανά κάποιο breakpoint (σημείο στο
οποίο έχουμε καθορίσει να σταματήσει η εκτέλεση με την εντολή break) ή να τερματίσει.
-
print Με την εντολή print μπορούμε να τυπώνουμε την τιμή οποιασδήποτε μεταβλητής του προγράμματός
μας. Αυτό προυποθέτει ότι η μεταβλητή αυτή βρίσκεται σε τέτοια εμβέλεια όπου είναι "ορατή" από
το πρόγραμμά μας. Για παράδειγμα, εάν στο πρόγραμμά μας υπάρχει μια μεταβλητή counter
και στο σημείο όπου έχουμε σταματήσει την εκτέλεση του προγράμματός μας η μεταβλητή αυτή βρίσκεται
σε ορατή εμβέλεια τότε με την εντολή :
(gdb) print counter
θα τυπωθεί η τρέχουσα τιμή της μεταβλητής counter.
-
help Με την εντολή help ο gdb μας τυπώνει μια λίστα από τις εντολές που διαθέτει, επιλογές
και σύνταξη της καθε μίας κλπ. Με help <όνομα εντολής> μας τυπώνει βοηθητικές
πληροφορίες για τη συγκεκριμένη εντολή.
core dumped
Πολλές φορές κάποιο λάθος στο πρόγραμμά μας προκαλεί ανώμαλο τερματισμό της εκτέλεσης, το οποίο
συχνά συνοδεύεται από αποτύπωση της μνήμης του υπολογιστή όπως ήταν κατά τη διάρκεια της εκτέλεσης
του προγράμματος ώστε να την εξετάσουμε και να δούμε τί πήγε στραβά.
Το πρόγραμμα τυπώνει
segmentation fault (core dumped)
ή
bus error (core dumped)
και παράγει ένα αρχείο core το οποίο περιέχει την αποτύπωση της μνήμης όπως αναφέραμε.
Το αρχείο αυτό μπορούμε να το εξετάσουμε με τη βοήθεια του gdb ώστε να βρούμε σε ποιο ακριβώς
σημείο του κώδικά μας προέκυψε το πρόβλημα που πρόκαλεσε το βίαιο τερματισμό της εκτέλεσης.
Γράφουμε στη γραμμή εντολών :
$> gdb a.out core
και μέσα στο περιβάλλον του gdb δίνουμε την εντολή where. Με αυτή την εντολή ο debugger
θα μας τυπώσει σε ποια γραμμή ακριβώς του προγράμματός μας προέκυψε το πρόβλημα. Απαραίτητη
προυπόθεση για να μπορεί ο debugger να βρει και να μας τυπώσει την ακριβή γραμμή είναι το πρόγραμμά μας
να έχει γίνει compile με την επιλογή -g (δες και παραπάνω στην παράγραφο
Μεταγλώτιση - compiling).
χρήσιμα hints για τη χρήση του gdb
- Δε χρειάζεται να γράφουμε ολόκληρη την εντολή που θέλουμε να εκτελέσει
ο gdb. Εάν από το αρχικό τμήμα της εντολής που έχουμε γράψει μπορεί να
καταλάβει ποια εντολή θέλουμε τότε αρκεί να γράφουμε εκείνο το αρχικό τμήμα.
Για παράδειγμα , εάν γράψουμε
(gdb) p counter
τότε ο gdb θα εκτελέσει
(gdb) print counter
Ομοίως με την εντολή s θα εκτελέσει
step, με την εντολή c
θα εκτελέσει continue κ.ό.κ.
-
Εάν πατήσουμε απλά ENTER στη γραμμή εντολών του gdb τότε εκτελεί
την τελευταία εντολή που εκτελέσαμε (π.χ. θέλουμε να εκτελέσουμε πολλά
συνεχόμενα step, μπορούμε απλά να εκτελέσουμε step και μετά να πατάμε ENTER).
-
Ο gdb υποστηρίζει tab completion , δηλαδή εάν πληκτρολογήσουμε μόνο ένα αρχικό
τμήμα μιας εντολής και πατήσουμε το TAB τότε εάν μπορεί μονοσήμαντα
να καταλάβει ποιά εντολή θέλουμε συμπληρώνει αυτόματα το όνομά της, διαφορετικά
εμφανίζει τις διαφορετικές επιλογές που έχουμε.
Η σελίδα αυτή συντάχθηκε από τον
Γιώργο Ζαχαριουδάκη, κατά το χειμερινό εξάμηνο 2001-2002.
Κλείσιμο Παραθύρου