[Powered by Google Translate] [Valgrind] [Nate Hardison, Πανεπιστήμιο Χάρβαρντ] Αυτό είναι CS50, CS50.TV] Μερικά από τα πιο δύσκολα σφάλματα σε προγράμματα C προέρχονται από την κακή διαχείριση της μνήμης. Υπάρχει ένας τεράστιος αριθμός των τρόπων να βίδα τα πράγματα, συμπεριλαμβανομένης της κατανομής του λάθος ποσότητα μνήμης, ξεχνώντας να προετοιμαστεί μεταβλητές, γραπτώς πριν ή μετά από το τέλος ενός ρυθμιστικού διαλύματος, και την απελευθέρωση κρατήσει τη μνήμη πολλές φορές. Τα συμπτώματα κυμαίνονται από απρόβλεπτες διακοπές μυστηριωδώς να αντικατασταθούν αξίες, συχνά σε τόπους και χρόνους πολύ μακριά από το αρχικό σφάλμα. Ιχνηλατώντας την παρατηρούμενη πρόβλημα πίσω με την υποκείμενη αιτία μπορεί να είναι προκλητική, αλλά ευτυχώς υπάρχει ένα χρήσιμο πρόγραμμα που ονομάζεται Valgrind που μπορεί να κάνει πολλά για να βοηθήσει. Μπορείτε να εκτελέσετε ένα πρόγραμμα στο πλαίσιο του Valgrind να ενεργοποιήσετε εκτενέστερο έλεγχο των κονδυλίων της μνήμης σωρού και προσβάσεις. Όταν Valgrind εντοπίσει κάποιο πρόβλημα, σας δίνει άμεση, άμεσες πληροφορίες που σας επιτρέπει να πιο εύκολα να βρείτε και να διορθώσετε το πρόβλημα. Valgrind επίσης εκθέσεις για λιγότερο θανατηφόρα θέματα μνήμης, όπως διαρροές μνήμης, την κατανομή της μνήμης σωρού, και ξεχνάμε να το ελευθερώσει. Όπως και compiler μας, Clang, το πρόγραμμα εντοπισμού σφαλμάτων μας, GDB, Valgrind είναι ελεύθερο λογισμικό, και έχει εγκατασταθεί στη συσκευή. Valgrind τρέχει σε εκτελέσιμη δυαδική σας, Δεν σας. ή γ. h αρχεία πηγαίου κώδικα, έτσι ώστε να είστε σίγουροι ότι έχουν συντάξει ένα up-to-ενημερωμένο αντίγραφο του προγράμματός σας χρησιμοποιώντας Clang ή Πραγματοποίηση. Στη συνέχεια, το τρέξιμο κάτω από το πρόγραμμά σας μπορεί να είναι Valgrind είναι τόσο απλή όσο ακριβώς πρόθεμα το πρότυπο πρόγραμμα με εντολή του Valgrind λέξη, η οποία ξεκινά Valgrind και τρέχει το πρόγραμμα στο εσωτερικό του. Κατά την εκκίνηση, Valgrind κάνει κάποια σύνθετη κοσκινίσεως να ρυθμίσετε το εκτελέσιμο για τους ελέγχους μνήμης, έτσι ώστε να μπορεί να πάρει λίγο για να ιδρυθεί και να λειτουργήσει. Το πρόγραμμα στη συνέχεια θα εκτελέσει κανονικά, είναι πολύ πιο αργά, και όταν τελειώσει, Valgrind θα εκτυπώσετε μια περίληψη της χρήσης της μνήμης. Αν όλα πάνε καλά, θα δούμε κάτι σαν αυτό: Σε αυτή την περίπτωση,. / Clean_program είναι η διαδρομή για το πρόγραμμα που θέλετε να εκτελέσετε. Και ενώ αυτό δεν λαμβάνει κανένα επιχείρημα, αν το έκανε Είχα μόλις καρφί για τους μέχρι το τέλος της εντολής, ως συνήθως. Καθαρίστε το πρόγραμμα είναι απλά ένα ανόητο μικρό πρόγραμμα που δημιούργησα που διαθέτει χώρο για ένα μπλοκ των ints στο σωρό, θέσει κάποιες αξίες μέσα τους, και ελευθερώνει το ολόκληρο οικοδομικό τετράγωνο. Αυτό είναι ό, τι τραβάτε για, χωρίς λάθη και χωρίς διαρροές. Μια άλλη σημαντική μετρική είναι ο συνολικός αριθμός των bytes που διατίθενται. Ανάλογα με το πρόγραμμα, αν κατανομές σας είναι τα megabytes ή υψηλότερη, κάνετε πιθανώς κάτι λάθος. Είσαι άσκοπα την αποθήκευση αντιγράφων; Χρησιμοποιείτε το σωρό για την αποθήκευση, όταν θα ήταν καλύτερα να χρησιμοποιήσετε τη στοίβα; Έτσι, τα σφάλματα μνήμης μπορεί να είναι πραγματικά κακό. Οι πιο εμφανείς αυτές προκαλούν θεαματικά ατυχήματα, αλλά ακόμα και τότε μπορεί ακόμα να είναι δύσκολο να εντοπιστούν τι ακριβώς οδήγησε στην συντριβή. Περισσότερα ύπουλα, ένα πρόγραμμα με ένα σφάλμα μνήμης μπορούν να εξακολουθούν να συγκεντρώνουν καθαρά και μπορεί να εξακολουθεί να φαίνεται να λειτουργεί σωστά γιατί κατάφερε να πάρει τυχερός το μεγαλύτερο μέρος του χρόνου. Μετά από αρκετές «επιτυχή έκβαση», μπορείτε να σκεφτείτε ότι ένα κραχ είναι μια απροσδόκητη επιτυχία του υπολογιστή, αλλά ο υπολογιστής δεν είναι ποτέ λάθος. Τρέχοντας Valgrind μπορεί να σας βοηθήσει να εντοπίσουμε την αιτία των ορατών σφάλματα μνήμης καθώς και βρείτε κρύβονται τα λάθη δεν χρειάζεται καν γνωρίζουν ακόμη. Κάθε φορά που Valgrind εντοπίσει κάποιο πρόβλημα, εκτυπώνει τις πληροφορίες σχετικά με το τι παρατηρείται. Κάθε στοιχείο είναι αρκετά λακωνική - η γραμμή πηγή της παράνομης διδασκαλίας, ποιο είναι το θέμα, και μια μικρή πληροφορίες σχετικά με τη μνήμη που εμπλέκονται - αλλά συχνά είναι αρκετές πληροφορίες για να κατευθύνουν την προσοχή σας στο σωστό μέρος. Εδώ είναι ένα παράδειγμα του Valgrind τρέχει σε ένα αμαξάκι πρόγραμμα που κάνει μια έγκυρη ανάγνωση της μνήμης σωρού. Θα δείτε δεν υπάρχουν σφάλματα ή προειδοποιήσεις σε σύνταξη. Ωχ, η περίληψη σφάλμα λέει ότι υπάρχουν δύο λάθη - δύο άκυρα διαβάσει μεγέθους 4 - bytes, αυτό είναι. Τόσο κακό διαβάζει συνέβη στην κύρια λειτουργία του invalid_read.c, η πρώτη επί της γραμμής 16 και το δεύτερο επί της γραμμής 19. Ας δούμε τον κώδικα. Μοιάζει με την πρώτη πρόσκληση για την printf προσπαθεί να διαβάσει ένα int πέρα ​​από το τέλος του μπλοκ μνήμης μας. Αν κοιτάξουμε πίσω στην έξοδο του Valgrind, βλέπουμε ότι Valgrind μας είπε ακριβώς αυτό. Η διεύθυνση προσπαθούμε να διαβάσετε ξεκινά 0 bytes μετά το τέλος του μπλοκ του μεγέθους 16 bytes - τέσσερις 32-bit ints που διατίθενται. Δηλαδή, η διεύθυνση προσπαθούσαμε να διαβάσετε ξεκινά αμέσως μετά το τέλος του μπλοκ μας, όπως βλέπουμε σε κακή printf κλήση μας. Τώρα, το έχουν διαβάσει άκυρο μπορεί να μην φαίνεται σαν αυτό το μεγάλο ζήτημα, αλλά αν χρησιμοποιείτε αυτά τα δεδομένα για να ελέγχουν τη ροή του προγράμματός σας - για παράδειγμα, ως μέρος του μια δήλωση ή αν βρόχο - τότε τα πράγματα μπορούν να πάνε άσχημα σιωπηλά. Παρακολουθήστε πώς μπορώ να τρέξω το πρόγραμμα invalid_read και τίποτα από το συνηθισμένο συμβαίνει. Scary, ε; Τώρα, ας ρίξουμε μια ματιά σε μερικά περισσότερα είδη των λαθών που ενδέχεται να συναντήσετε τον κωδικό σας, και θα δούμε πώς Valgrind τους εντοπίζει. Είδαμε μόνο ένα παράδειγμα μιας invalid_read, έτσι και τώρα ας ελέγξει έξω μια invalid_write. Και πάλι, δεν υπάρχουν σφάλματα ή προειδοποιήσεις στη σύνταξη. Εντάξει, Valgrind λέει ότι υπάρχουν δύο λάθη σε αυτό το πρόγραμμα - και invalid_write και invalid_read. Ας ελέγξουμε έξω αυτόν τον κώδικα. Φαίνεται πως έχουμε ένα παράδειγμα του κλασικού strlen συν ένα bug. Ο κώδικας δεν malloc ένα επιπλέον byte του χώρου για το χαρακτήρα / 0, έτσι όταν str αντίγραφο πήγε να το γράψετε σε ssubstrlen "CS50 βράχια!" έγραψε 1 byte μετά το τέλος του μπλοκ μας. Η invalid_read έρχεται όταν κάνουμε κλήση μας να printf. Printf καταλήγει ανάγνωση άκυρο μνήμης όταν διαβάζει το / 0 χαρακτήρα όπως φαίνεται στο τέλος αυτής της σειράς E είναι η εκτύπωση. Αλλά τίποτα από αυτά δεν διέφυγε Valgrind. Βλέπουμε ότι έπιασε το invalid_write ως μέρος του αντιγράφου str στη γραμμή 11 από τα κύρια, και η invalid_read είναι μέρος της printf. Ροκ για, Valgrind. Και πάλι, αυτό μπορεί να μην φαίνεται σαν μια μεγάλη υπόθεση. Μπορούμε να τρέξει αυτό το πρόγραμμα ξανά και ξανά έξω από Valgrind και δεν βλέπω κανένα σύμπτωμα σφάλματος. Ωστόσο, ας ρίξουμε μια ματιά σε μια ελαφρά παραλλαγή του αυτό για να δείτε πώς τα πράγματα μπορούν να πάρουν πραγματικά άσχημη. Έτσι, παρέχεται, είμαστε κατάχρηση πράγματα περισσότερο από ό, τι ακριβώς λίγο σε αυτόν τον κώδικα. Είμαστε μόνο τη διάθεση χώρου για την σωρό για δύο χορδές το μήκος του CS50 βράχους, αυτή τη φορά, θυμάται ο / 0 χαρακτήρα. Αλλά τότε ρίχνουμε σε ένα σούπερ-μακρά σειρά μέσα στο μπλοκ μνήμης S που δείχνει να. Τι συνέπειες θα έχει για το μπλοκ μνήμης που δείχνει T; Λοιπόν, αν Τ σημεία στη μνήμη που είναι ακριβώς δίπλα στο S, έρχεται αμέσως μετά από αυτό, τότε θα μπορούσαμε να είχαμε γράψει πάνω μέρος του Τ. Ας εκτελέσετε αυτόν τον κώδικα. Κοιτάξτε τι συνέβη. Οι χορδές που αποθηκεύονται σε μπλοκ σωρού μας δύο φαίνεται να έχουν εκτυπωθεί σωστά. Τίποτα δεν φαίνεται καθόλου λάθος. Ωστόσο, ας πάμε πίσω στον κώδικα μας και σχόλιο από τη γραμμή όπου θα αντιγράψετε CS50 βράχους στο δεύτερο μπλοκ μνήμης, επεσήμανε από την t. Τώρα, όταν θα εκτελέσετε αυτόν τον κώδικα θα πρέπει να μόνο να δείτε τα περιεχόμενα του πρώτου μπλοκ μνήμης εκτυπώσετε. Πω πω, ακόμα κι αν δεν str αντίγραφο τυχόν χαρακτήρες στο δεύτερο μπλοκ σωρού, ο ένας δείχνεται από Τ, θα πάρετε μια εκτύπωση. Πράγματι, το string που γεμιστές σε πρώτο μπλοκ μας υπερέβη την πρώτη ομάδα και εντός του δεύτερου μπλοκ, κάνει ό, τι φαίνεται φυσιολογικό. Valgrind, όμως, μας λέει την αληθινή ιστορία. Εκεί πάμε. Όλα αυτά άκυρη διαβάζει και γράφει. Ας δούμε ένα παράδειγμα από ένα άλλο είδος του λάθους. Εδώ κάνουμε κάτι μάλλον ατυχής. Θα αρπάξει χώρος για έναν int στο σωρό, και να προετοιμάσει ένα δείκτη int - p - να επισημάνω σε αυτό το διάστημα. Ωστόσο, ενώ ο δείκτης μας έχει προετοιμαστεί, τα στοιχεία ότι είναι δείχνει να έχει μόνο σκουπίδια, τιδήποτε είναι σε αυτό το μέρος του σωρού. Έτσι, όταν έχουμε φορτώσει τα δεδομένα σε int i, έχουμε προετοιμαστεί i τεχνικά, αλλά το κάνουμε με τα δεδομένα σκουπίδια. Η κλήση για να διεκδικήσει, η οποία είναι μια εύχρηστη μακρο αποσφαλμάτωσης ορίζεται στο εύστοχα ονομάστηκε διεκδικήσει βιβλιοθήκη, θα διακόψει το πρόγραμμα εάν η κατάστασή του τεστ αποτύχει. Δηλαδή, αν δεν είναι 0. Ανάλογα με το τι έγινε στο χώρο σωρό, επεσήμανε από p, το πρόγραμμα αυτό θα μπορούσε να λειτουργήσει και μερικές φορές αποτυγχάνουν σε άλλες χρονικές στιγμές. Αν δουλέψει, είμαστε απλά τυχεροί. Ο compiler δεν θα πιάσει αυτό το σφάλμα, αλλά Valgrind ότι βούληση. Εκεί βλέπουμε το σφάλμα που απορρέουν από τη χρήση των εν λόγω δεδομένων σκουπίδια. Όταν εκχώρηση μνήμης σωρού, αλλά δεν το deallocate ή ελευθερώνετε, που ονομάζεται διαρροή. Για μια μικρή, βραχύβια πρόγραμμα που τρέχει και αμέσως εξόδους, διαρροές είναι αρκετά αβλαβή, αλλά για ένα έργο του μεγαλύτερου μεγέθους ή / και τη μακροζωία, ακόμη και μια μικρή διαρροή μπορεί να επιδεινώνει σε κάτι σημαντικό. Για CS50, εμείς αναμένουμε από εσάς να να φροντίζει για την απελευθέρωση όλων των μνήμη του σωρού που διαθέτουν, επειδή θέλουμε να οικοδομήσουμε τις δεξιότητες για να χειριστεί σωστά τη χειροκίνητη διαδικασία που απαιτούνται από τον C. Για να γίνει αυτό, το πρόγραμμά σας θα πρέπει να έχουμε μια ακριβή ένα-προς-ένα αντιστοιχία μεταξύ malloc και δωρεάν κλήσεις. Ευτυχώς, Valgrind μπορεί να σας βοηθήσει με τις διαρροές μνήμης πάρα πολύ. Εδώ είναι ένα πρόγραμμα που ονομάζεται διαρροή leak.c που διαθέτει χώρο στο σωρό, γράφει σε αυτό, αλλά δεν το απελευθερώσει. Εμείς το υπολογίσουν με κάνει και να τρέξει κάτω από το Valgrind, και βλέπουμε ότι, ενώ δεν έχουμε σφάλματα μνήμης, έχουμε μία διαρροή. Υπάρχουν 16 bytes σίγουρα χαθεί, πράγμα που σημαίνει ότι ο δείκτης σε αυτή την μνήμη δεν ήταν σε έκταση, όταν βγήκε το πρόγραμμα. Τώρα, Valgrind δεν μας δίνει έναν τόνο των πληροφοριών σχετικά με τη διαρροή, αλλά αν ακολουθήσουμε αυτό το μικρό σημείωμα που δίνει κάτω προς το κάτω μέρος της έκθεσής της να επαναπροσδιορίζονται με - διαρροή-check = πλήρη για να δείτε τα πλήρη στοιχεία της μνήμης που διέρρευσαν, θα πάρουμε περισσότερες πληροφορίες. Τώρα, στην περίληψη σωρό, Valgrind μας λέει όπου η μνήμη που χάθηκε αρχικά κατανεμηθεί. Όπως γνωρίζουμε από την αναζήτηση στον πηγαίο κώδικα, Valgrind μας πληροφορεί ότι διέρρευσε την μνήμη κατανέμονται με μια πρόσκληση για malloc στη γραμμή 8 του leak.c στην κύρια λειτουργία. Αρκετά ικανό. Valgrind κατηγοριοποιεί τις διαρροές χρησιμοποιώντας αυτούς τους όρους: Σίγουρα έχασε - αυτό είναι σωρός διατεθεί μνήμη για τις οποίες το πρόγραμμα δεν έχει πλέον ένα δείκτη. Valgrind ξέρει ότι είχατε μια φορά το δείκτη, αλλά από τότε έχουν χάσει τα ίχνη του. Αυτή η μνήμη είναι σίγουρα διαρρεύσει. Έμμεσα χαθεί - αυτό είναι σωρός διατεθεί μνήμη στο οποίο οι μόνες δείκτες σε επίσης χάνονται. Για παράδειγμα, αν έχετε χάσει το δείκτη του ποντικιού στο πρώτο κόμβο της λίστας που συνδέονται, τότε ο πρώτος κόμβος καθαυτός θα χαθεί οριστικά, ενώ τυχόν μεταγενέστερες κόμβοι θα εμμέσως χαθεί. Ενδεχομένως χαθεί - αυτό είναι σωρός διατεθεί μνήμη στην οποία Valgrind δεν μπορεί να είναι βέβαιος κατά πόσον υπάρχει ένας δείκτης ή όχι. Ακόμα είναι προσβάσιμο σωρός διατεθεί μνήμη με το οποίο το πρόγραμμα εξακολουθεί να έχει ένα δείκτη στην έξοδο, που συνήθως σημαίνει ότι μια καθολική μεταβλητή σημεία σε αυτό. Για να ελέγξετε για διαρροές αυτές, θα πρέπει επίσης να περιλαμβάνει την επιλογή - Ακόμα-προσβάσιμο = ναι στην επίκληση της Valgrind σας. Αυτές οι διαφορετικές περιπτώσεις ενδέχεται να απαιτούν διαφορετικές στρατηγικές για τον καθαρισμό τους επάνω, αλλά διαρροές πρέπει να εξαλειφθούν. Δυστυχώς, για τον καθορισμό διαρροές μπορεί να είναι δύσκολο να το κάνετε, από λανθασμένες κλήσεις στην ελεύθερη να ανατινάξουν το πρόγραμμά σας. Για παράδειγμα, αν κοιτάξουμε invalid_free.c, βλέπουμε ένα παράδειγμα της κακής μνήμης ανακατανομή. Τι θα πρέπει να είναι μια ενιαία πρόσκληση για να απελευθερώσει ολόκληρο το οικοδομικό τετράγωνο της μνήμης που υποδεικνύεται από int_block, έχει γίνει αντ 'αυτού μια προσπάθεια να απελευθερώσει κάθε int μεγέθους τμήμα της μνήμης ξεχωριστά. Αυτό θα αποτύχει καταστροφικά. Boom! Τι λάθος. Αυτό είναι σίγουρα δεν είναι καλό. Αν είστε κολλημένοι με αυτό το είδος του λάθους, όμως, και δεν ξέρετε πού να κοιτάξετε, καταφύγουμε σε νέα καλύτερος φίλος σας. Σωστά μαντέψατε - Valgrind. Valgrind, όπως πάντα, ξέρει ακριβώς τι συμβαίνει. Οι alloc και δωρεάν Η δεν ταιριάζουν. Έχουμε 1 και 4 alloc ελευθερώνει. Και Valgrind μας λέει, επίσης, όπου η πρώτη κακή δωρεάν κλήση - αυτός που προκάλεσε την blowup - έρχεται από - γραμμή 16. Όπως μπορείτε να δείτε, η κακή κλήσεις για την απελευθέρωση είναι πραγματικά κακή, γι 'αυτό προτείνουμε να αφήσει διαρροή πρόγραμμα σας ενώ εργάζεστε για να πάρει τη σωστή λειτουργικότητα. Αρχίσετε να ψάχνετε για διαρροές μόνο μετά το πρόγραμμά σας λειτουργεί σωστά, χωρίς άλλα λάθη. Και αυτό είναι ό, τι έχουμε για αυτό το βίντεο. Τώρα τι περιμένεις; Μετάβαση τρέξει Valgrind στα προγράμματά σας αυτή τη στιγμή. Το όνομά μου είναι Nate Hardison. Αυτό είναι CS50. [CS50.TV]