Domanda

Allora, ho bisogno di aiuto.Sto lavorando a un progetto in C++.Tuttavia, penso di essere riuscito in qualche modo a corrompere il mio heap.Ciò si basa sul fatto che ho aggiunto un file std::string a una classe e assegnandole un valore da un'altra std::string:

std::string hello = "Hello, world.\n";
/* exampleString = "Hello, world.\n" would work fine. */
exampleString = hello;

si blocca sul mio sistema con un dump dello stack.Quindi in pratica ne ho bisogno fermare ed esaminare tutto il mio codice e le cose sulla gestione della memoria e scoprire dove ho sbagliato.La base di codice è ancora piccola (circa 1000 righe), quindi è facilmente fattibile.

Tuttavia, sono fuori di testa con questo genere di cose, quindi ho pensato di buttarle lì.Sono su un sistema Linux e ho curiosato valgrind, e pur non sapendo completamente cosa sto facendo, ha riferito che the std::stringIl distruttore di era un invalido gratuito.Devo ammettere di aver ottenuto il termine "corruzione dell'heap" da una ricerca su Google;anche eventuali articoli di carattere generale su questo genere di cose sarebbero apprezzati.

(Dentro prima rm -rf ProjectDir, ripetilo in C# :D)

MODIFICARE:Non l'ho chiarito, ma quello che chiedo sono dei consigli per diagnosticare questo tipo di problemi di memoria.So che le cose std::string sono giuste, quindi è qualcosa che ho fatto (o un bug, ma non c'è problema con Select).Sono sicuro che potrei controllare il codice che ho scritto e voi persone molto intelligenti vedreste il problema in pochissimo tempo, ma voglio aggiungere questo tipo di analisi del codice alla mia "cassetta degli attrezzi", per così dire.

È stato utile?

Soluzione

Questi sono meccanismi relativamente economici per eventualmente risolvere il problema:

  1. Tieni d'occhio il mio questione della corruzione accumulata - Sto aggiornando con le risposte man mano che si scuotono.Il primo era il bilanciamento new[] E delete[], ma lo stai già facendo.
  2. Dare valgrind più di un tentativo;è uno strumento eccellente e vorrei solo che fosse disponibile sotto Windows.Rallenta il tuo programma solo di circa la metà, il che è abbastanza buono rispetto agli equivalenti Windows.
  3. Pensa a usare il Strumenti per le prestazioni di Google in sostituzione di malloc/new.
  4. Hai ripulito tutti i file oggetto e ricominciato da capo?Forse il tuo file make è..."non ottimale"
  5. Tu non sei assert()ing abbastanza nel tuo codice.Come faccio a saperlo senza averlo visto?Tipo usare il filo interdentale, nessuno assert()È abbastanza nel loro codice.Aggiungi una funzione di convalida per i tuoi oggetti e chiamala all'inizio e alla fine del metodo.
  6. Sei compilazione -wall?In caso contrario, fallo.
  7. Trovati uno strumento per lanugine come PC-Lint.Una piccola app come la tua potrebbe adattarsi a Demo di PC-lint pagina, il che significa nessun acquisto per te!
  8. Controlla di annullare i puntatori dopo averli eliminati.A nessuno piace un puntatore penzolante.Stesso concerto con puntatori dichiarati ma non allocati.
  9. Smetti di usare gli array.Usare un vettore Invece.
  10. Non utilizzare puntatori grezzi.Usare un puntatore intelligente.Non utilizzare auto_ptr!Quella cosa è...sorprendente;la sua semantica è molto strana.Scegli invece uno dei Potenzia i puntatori intelligenti, o qualcosa fuori da la biblioteca di Loki.

Altri suggerimenti

Una volta abbiamo avuto un bug che eludeva tutte le tecniche regolari, valgrind, purificazione ecc.Il crash si è verificato solo su macchine con molta memoria e solo su set di dati di input di grandi dimensioni.

Alla fine lo abbiamo rintracciato utilizzando i punti di controllo del debugger.Provo a descrivere la procedura qui:

1) Trovare la causa del guasto.Dal codice di esempio sembra che la memoria per "exampleString" sia danneggiata e quindi non sia possibile scrivervi.Continuiamo con questo presupposto.

2) Impostare un punto di interruzione nell'ultima posizione nota in cui "exampleString" viene utilizzato o modificato senza alcun problema.

3) Aggiungere un punto di controllo al membro dati di "exampleString".Con la mia versione di g++, la stringa è archiviata in _M_dataplus._M_p.Vogliamo sapere quando questo membro dati cambia.La tecnica GDB per questo è:

(gdb) p &exampleString._M_dataplus._M_p
$3 = (char **) 0xbfccc2d8
(gdb)  watch *$3
Hardware watchpoint 1: *$3

Ovviamente sto usando Linux con g++ e gdb qui, ma credo che i punti di controllo della memoria siano disponibili con la maggior parte dei debugger.

4) Continuare fino all'attivazione del punto di guardia:

Continuing.
Hardware watchpoint 2: *$3

Old value = 0xb7ec2604 ""
New value = 0x804a014 ""
0xb7e70a1c in std::string::_M_mutate () from /usr/lib/libstdc++.so.6
(gdb) where

Il GDB where il comando fornirà una traccia indietro che mostra cosa ha provocato la modifica.Questa è una modifica perfettamente legale, nel qual caso continua, o se sei fortunato sarà la modifica dovuta alla corruzione della memoria.In quest'ultimo caso, ora dovresti essere in grado di rivedere il codice Veramente causando il problema e, si spera, risolverlo.

La causa del nostro bug era un accesso all'array con un indice negativo.L'indice era il risultato del cast di un puntatore a un modulo "int" della dimensione dell'array.Il bug non è stato rilevato da valgrind et al.poiché gli indirizzi di memoria allocati durante l'esecuzione con tali strumenti non sono mai stati "> MAX_INT" e quindi non ha mai dato luogo ad un indice negativo.

Oh, se vuoi sapere come eseguire il debug del problema, è semplice.Per prima cosa prendi un pollo morto.Poi, inizia a scuoterlo.

Seriamente, non ho trovato un modo coerente per rintracciare questo tipo di bug.Poiché i problemi potenziali sono così tanti, non esiste una semplice lista di controllo da seguire.Tuttavia, consiglierei quanto segue:

  1. Mettiti comodo in un debugger.
  2. Inizia a girare nel debugger per vedere se riesci a trovare qualcosa che sembra sospetto.Controlla soprattutto per vedere cosa sta succedendo durante il exampleString = hello; linea.
  3. Controlla per assicurarti che si stia effettivamente bloccando sul file exampleString = hello; linea, e non quando si esce da un blocco che lo racchiude (che potrebbe causare l'attivazione dei distruttori).
  4. Controlla eventuali magie di puntamento che potresti eseguire.Aritmetica dei puntatori, casting, ecc.
  5. Controlla tutte le allocazioni e deallocazioni per assicurarti che corrispondano (nessuna doppia deallocazione).
  6. Assicurati di non restituire alcun riferimento o puntatore a oggetti nello stack.

Ci sono anche molte altre cose da provare.Sono sicuro che anche altre persone interverranno con le loro idee.

Alcuni posti da cui iniziare:

Se utilizzi Windows e usi Visual C++ 6 (spero che Dio nessuno lo usi ancora in questi giorni) l'implementazione di std::string non è thread-safe e può portare a questo genere di cose.

Ecco un articolo che ho trovato che spiega molte delle cause più comuni di perdite di memoria e danneggiamento.

Nel mio precedente posto di lavoro utilizzavamo Compuware Boundschecker per aiutarci in questo.È commerciale e molto costoso, quindi potrebbe non essere un'opzione.

Ecco un paio di librerie gratuite che potrebbero essere utili

http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/

http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx

Spero che aiuti.La corruzione della memoria è un posto schifoso in cui trovarsi!

Potrebbe trattarsi di corruzione dell'heap, ma è altrettanto probabile che si tratti di corruzione dello stack.Jim ha ragione.Abbiamo davvero bisogno di un po’ più di contesto.Queste due linee di fonte non ci dicono molto isolatamente.Potrebbero esserci molte cose che causano questo (che è la vera gioia di C/C++).

Se ti senti a tuo agio nel pubblicare il tuo codice, potresti anche gettarlo tutto su un server e pubblicare un collegamento.Sono sicuro che otterresti molti più consigli in questo modo (alcuni dei quali senza dubbio non correlati alla tua domanda).

Il codice era semplicemente un esempio di dove il mio programma stava fallendo (era allocato nello stack, Jim).In realtà non sto cercando "cosa ho fatto di sbagliato", ma piuttosto "come faccio a diagnosticare cosa ho fatto di sbagliato".Insegna a un uomo a pescare e tutto il resto.Anche se esaminando la domanda, non l'ho reso abbastanza chiaro.Grazie al cielo per la funzione di modifica.:')

Inoltre, ho effettivamente risolto il problema std::string.Come?Sostituendolo con un vettore, compilando e quindi sostituendo nuovamente la stringa.Esso era si bloccava costantemente lì, e il problema si risolveva anche se... non poteva.C'è qualcosa di brutto lì, e non sono sicuro di cosa.Volevo controllare l'unica volta in cui alloco manualmente la memoria sull'heap, però:

 this->map = new Area*[largestY + 1];
 for (int i = 0; i < largestY + 1; i++) {
     this->map[i] = new Area[largestX + 1];
 }

e cancellandolo:

for (int i = 0; i < largestY + 1; i++) {
    delete [] this->map[i];
}
delete [] this->map;

Non ho mai allocato un array 2d con C++ prima.Sembra che funzioni.

Inoltre, ho effettivamente risolto il problema std::string.Come?Sostituendolo con un vettore, compilando e quindi sostituendo nuovamente la stringa.Si bloccava costantemente lì, e il problema veniva risolto anche se... non poteva.C'è qualcosa di brutto lì, e non sono sicuro di cosa.

Sembra che tu abbia davvero scosso un pollo.Se non lo sai Perché ora funziona, quindi è ancora rotto e praticamente ti garantirà di morderti di nuovo in seguito (dopo aver aggiunto ancora più complessità).

Esegui Purificazione.

È uno strumento quasi magico che ti segnalerà quando stai distruggendo la memoria che non dovresti toccare, perdendo memoria non liberando cose, liberando due volte, ecc.

Funziona a livello di codice macchina, quindi non è nemmeno necessario avere il codice sorgente.

Una delle teleconferenze con i fornitori più divertenti a cui abbia mai partecipato è stata quando Purify ha riscontrato una perdita di memoria nel loro codice e abbiamo potuto chiedere "è possibile che non stai liberando memoria nella funzione foo()" e ascoltare il stupore nelle loro voci.

Pensavano che stessimo eseguendo il debug degli dei, ma poi abbiamo lasciato loro il segreto in modo che potessero eseguire Purify prima che dovessimo usare il loro codice.:-)

http://www-306.ibm.com/software/awdtools/purify/unix/

(È piuttosto costoso ma hanno un download di valutazione gratuito)

Una delle tecniche di debug che utilizzo frequentemente (tranne nei casi di stranezza più estrema) è dividere e conquistare.Se il tuo programma attualmente fallisce con qualche errore specifico, dividilo a metà in qualche modo e vedi se ha ancora lo stesso errore.Ovviamente il trucco sta nel decidere dove dividere il vostro programma!

Il tuo esempio fornito non mostra un contesto sufficiente per determinare dove potrebbe essere l'errore.Se qualcun altro dovesse provare il tuo esempio, funzionerebbe bene.Quindi, nel tuo programma, prova a rimuovere la maggior parte delle cose extra che non ci hai mostrato e vedi se funziona.Se è così, aggiungi nuovamente l'altro codice un po' alla volta finché non inizia a fallire.Quindi, la cosa che hai appena aggiunto è probabilmente il problema.

Tieni presente che se il tuo programma è multithread, probabilmente avrai problemi più grandi.In caso contrario, dovresti essere in grado di restringere il campo in questo modo.Buona fortuna!

Oltre a strumenti come Boundschecker o Purify, la soluzione migliore per risolvere problemi come questo è diventare davvero bravo a leggere il codice e acquisire familiarità con il codice su cui stai lavorando.

La corruzione della memoria è una delle cose più difficili da risolvere e solitamente questi tipi di problemi vengono risolti trascorrendo ore/giorni in un debugger e notando qualcosa come "ehi, il puntatore X viene utilizzato dopo che è stato eliminato!".

Se ti aiuta, è qualcosa in cui diventi migliore man mano che acquisisci esperienza.

L'allocazione di memoria per l'array sembra corretta, ma assicurati di controllare anche tutti i punti in cui accedi all'array.

Il tuo codice, come posso vedere, non contiene errori.Come è stato detto è necessario più contesto.

Se non hai già provato, installa gdb (il debugger gcc) e compila il programma con -g.Questo compilerà i simboli di debug che gdb potrà utilizzare.Una volta installato gdb, eseguilo con il programma (gdb ). Questo è un trucco utile per usare gdb.

Imposta un punto di interruzione per la funzione che produce il bug e controlla qual è il valore di exampleString.Fai lo stesso anche per qualunque parametro stai passando a exampleString.Questo dovrebbe almeno dirti se le std::strings sono valide.

Ho trovato la risposta da Questo articolo essere una buona guida sui puntatori.

Per quanto ne so, il tuo codice è corretto.Supponendo che exampleString sia un std::string con ambito di classe come descrivi, dovresti essere in grado di inizializzarlo/assegnarlo in questo modo.Forse c'è qualche altro problema?Forse uno snippet di codice reale aiuterebbe a inserirlo nel contesto.

Domanda:exampleString è un puntatore a un oggetto stringa creato con new?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top