2009-05-01 5 views
6

Ho una piccola applicazione C++ single-threaded, compilata e collegata utilizzando Visual Studio 2005, che utilizza boost (crc, program_options e tokenizer), un'infarinatura di STL e altre intestazioni di sistema assortite./MT e/MD si arresta in modo anomalo, ma solo quando il debugger non è collegato: come eseguire il debug?

(Il suo scopo primario è quello di leggere in un .csv e generare un .dat binario personalizzato e un abbinato .h dichiarando strutture che "spiegano" il formato della dat.)

Lo strumento si blocca (violazione di accesso su NULL) quando viene eseguito all'esterno del debugger, solo in versione. Per esempio. premendo F5 non si provoca lo schianto dello strumento, Ctrl-F5 sì. Quando ho ri-connettere il debugger, ottengo questo stack:

[email protected]() + 0x26916 bytes  
csv2bin.exe!malloc(unsigned int size=0x00000014) Line 163 + 0x63 bytes C 
csv2bin.exe!operator new(unsigned int size=0x00000014) Line 59 + 0x8 bytes C++ 
>csv2bin.exe!Record::addField(const char * string=0x0034aac8) Line 62 + 0x7 bytes C++ 
csv2bin.exe!main(int argc=0x00000007, char * * argv=0x00343998) Line 253 C++ 
csv2bin.exe!__tmainCRTStartup() Line 327 + 0x12 bytes C 

La linea è schiantarsi su una ripartizione po 'innocua:

pField = new NumberField(this, static_cast<NumberFieldInfo*>(pFieldInfo)); 

... Io non credo che abbia ha raggiunto ancora il costruttore, assegna solo la memoria prima di saltare al costruttore. Ha anche eseguito questo codice dozzine di volte nel momento in cui si blocca, di solito in una posizione coerente (ma altrimenti non sospetta).

Il problema scompare durante la compilazione con/MTd o/MDd (debug runtime) e viene ripristinato quando si utilizza/MT o/MD.

Il NULL viene caricato dallo stack e posso vederlo in memoria. _RtlAllocateHeap @ 12 + 0x26916 byte sembra uno enorme offset, come se fosse stato effettuato un salto errato.

Ho provato _HAS_ITERATOR_DEBUGGING in una build di debug e che non ha sollevato nulla di sospetto.

Eliminazione di un HeapValidate all'inizio e alla fine di Record :: addField mostra un heap OK fino a quando si arresta in modo anomalo.

Questo funzionava - non sono del tutto sicuro di cosa sia cambiato tra l'ora e l'ultima volta che abbiamo compilato lo strumento (probabilmente anni fa, forse con un VS precedente). Abbiamo provato una versione precedente di boost (1,36 vs 1,38).

Prima di tornare all'indagine manuale del codice o inviarlo a PC-Lint e a sfogliare l'output, qualsiasi suggerimento su come eseguire il debug in modo efficace?

[sarò felice di aggiornare la questione con più informazioni, se richiesta informazioni nei commenti.]

+0

una volta ho avuto la gioia di semi-reverse ingegnerizzare le cose RtlHeap per un bug simile. Non essere confuso dalle enormi compensazioni - è normale. I simboli di debug (che sembrano utilizzare) mancano apparentemente di alcune funzioni private (probabilmente dichiarate "statiche" nel file sorgente) e ottenere enormi offset per RtlAllocateHeap o RtlAllocateHeapSlowly significa semplicemente che era il simbolo più vicino trovato. – arke

+0

@arke: sì, me ne sono reso conto poco dopo aver postato la domanda. (Dovevo tornare indietro per modificarlo.) Molto prima avevo scritto uno strumento di ricerca che analizzava i file xMAP generati da Codewarrior al lavoro, e mi capitava occasionalmente la stessa cosa - semplicemente non mi è venuto in mente che non avrei Devono necessariamente avere tutti i simboli qui, per qualche ragione. – leander

risposta

9

Un piccolo sa differenza tra l'esecuzione con debugger o non è il sistema operativo heap di debug (vedi anche Why does my code run slowly when I have debugger attached?). È possibile disattivare l'heap di debug utilizzando la variabile di ambiente _NO_DEBUG_HEAP. È possibile specificare questo nelle proprietà del computer o nelle Impostazioni progetto in Visual Studio.

Una volta disattivato l'heap di debug, si dovrebbe vedere lo stesso crash anche con il debugger collegato.

Detto questo, essere consapevoli che la corruzione della memoria può essere difficile da eseguire il debug, poiché spesso la vera causa della corruzione (come un sovraccarico del buffer) può essere molto lontana da dove si vedono i sintomi (il crash).

+1

+1: Grazie, non sapevo su _NO_DEBUG_HEAP. Provalo adesso. (Ho avuto la divertente esperienza di rintracciare le corruzioni della memoria che si sono verificate solo sull'hardware embedded al dettaglio senza un debugger collegato, quindi ti sento sulla parte "potrebbe essere molto lontano dai sintomi") – leander

+0

Sì, è stato - ha ottenuto il crash nel debugger. =) Augurami buona fortuna ... – leander

+1

Hmm - l'heap del debug nasconde la corruzione? Questa è sfortuna ... –

2

L'arresto all'interno di new o malloc in genere indica che la struttura (interna) dell'implementazione di malloc è stata danneggiata. Questo è il più delle volte eseguito scrivendo oltre una precedente allocazione (buffer overflow).Quindi alla prossima chiamata a new o malloc l'app si blocca mentre la struttura interna ora contiene dati non validi.

Verificare se è possibile sovrascrivere eventuali spazi allocati in precedenza.

Se la tua applicazione è portatile puoi provare a crearla su Linux ed eseguirla sotto Valgrind.

+0

Sì, questa è la mia ipotesi. E 'tempo di scavare l'electricfence o il dmalloc, specialmente ora che il _NO_DEBUG_HEAP mi permette di bloccarmi all'interno del debugger. – leander

+0

Sì, stavo pensando di portarlo a Linux solo per Valgrind prima! =) Il modulo memcheck è ottimo, l'ho persino usato per eseguire il debug dei server MMORPG in passato. Application Verifier sembra coprire molte delle stesse basi in Windows, fortunatamente, felice di averlo trovato. – leander

2

Application Verifier era super-utile per la risoluzione di una volta ho avuto _NO_DEBUG_HEAP = 1 nell'ambiente, vedere la risposta accettata qui: Finding where memory was last freed?

E 'probabilmente anche la pena menzionare pageheap, che ho trovato mentre guardando Application Verifier. Sembra che copra un terreno simile.

(FYI, è stato un un carattere buffer overflow:

m_pEnumName = (char*)malloc(strlen(data) /* missing +1 here */); 
strcpy(m_pEnumName, data); 

... ancora un altro ridicolmente buon argomento di non usare strcpy direttamente.)