Semplificazione del debug di un'immagine in .NET

Nota

Questo articolo è specifico per .NET Framework. Non si applica alle implementazioni più recenti di .NET, incluse .NET 6 e versioni successive.

Quando si compila codice non gestito, è possibile configurare un'immagine eseguibile per il debug impostando le opzioni dell'IDE o della riga di comando. Ad esempio, è possibile usare l'opzione della riga di comando /Zi in Visual C++ per richiedere di generare file di simboli di debug (file con estensione pdb). In modo analogo, l'opzione della riga di comando /Od indica al compilatore di disabilitare l'ottimizzazione. Il codice risultante viene eseguito più lentamente, ma è più semplice eseguirne il debug all'occorrenza.

Durante la compilazione di codice gestito di .NET Framework, i compilatori come Visual C++, Visual Basic e C# compilano il programma di origine in linguaggio Common Intermediate Language (CIL). CIL viene poi compilato tramite JIT, appena prima dell'esecuzione, in codice macchina nativo. Come per il codice non gestito, è possibile configurare un'immagine eseguibile per il debug impostando le opzioni dell'IDE o della riga di comando. È anche possibile configurare la compilazione JIT per il debug in modo analogo.

Questa configurazione JIT presenta due aspetti:

  • È possibile richiedere al compilatore JIT di generare informazioni di traccia. In questo modo il debugger ha la possibilità di abbinare una catena di codice CIL alla controparte in codice macchina e di tenere traccia della posizione di archiviazione delle variabili locali e degli argomenti delle funzioni. In .NET Framework versione 2.0 e successive, il compilatore JIT genererà sempre le informazioni di traccia, quindi non occorre richiederle.

  • È possibile richiedere al compilatore JIT di non ottimizzare il codice macchina risultante.

In genere, il compilatore che genera il codice CIL imposta queste opzioni del compilatore JIT in modo corretto in base ai parametri dell'IDE o alle opzioni della riga di comando specificati, ad esempio /Od.

In alcuni casi, può essere necessario modificare il comportamento del compilatore JIT in modo che sia più semplice eseguire il debug del codice macchina generato. Ad esempio, si potrebbero generare informazioni di rilevamento JIT per una build finale o un'ottimizzazione dei controlli. È possibile farlo con un file di inizializzazione (INI).

Ad esempio, se l'assembly di cui vuole eseguire il debug è denominata App.exe, è possibile creare un file di testo denominato App.ini, nella stessa cartella di App.exe, che contiene queste tre righe:

[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0

È possibile impostare il valore di ogni opzione su 0 o 1 e per qualsiasi opzione assente verrà usato il valore predefinito 0. L'impostazione di GenerateTrackingInfo su 1 e di AllowOptimize su 0 consente di semplificare al massimo il debug.

A partire da .NET Framework versione 2.0, il compilatore JIT genera sempre le informazioni di traccia indipendentemente dal valore per GenerateTrackingInfo. Il valore AllowOptimize, comunque, ha ancora un effetto. Quando si usa Ngen.exe (generatore di immagini native) per precompilare l'immagine nativa senza ottimizzazione, il file INI deve essere presente nella cartella di destinazione con AllowOptimize=0 quando viene eseguito Ngen.exe. Se è stato precompilato un assembly senza ottimizzazione, è necessario rimuovere il codice precompilato usando l'opzione /uninstall di NGen.exe prima di rieseguire Ngen.exe per precompilare il codice con l'ottimizzazione. Se il file INI non è presente nella cartella, Ngen.exe precompila il codice come ottimizzato per impostazione predefinita.

System.Diagnostics.DebuggableAttribute controlla le impostazioni per un assembly. DebuggableAttribute include due campi che controllano se il compilatore JIT deve ottimizzare e/o generare informazioni di traccia. In .NET Framework 2.0 e versioni successive, il compilatore JIT genera sempre le informazioni di traccia.

Per una build finale, i compilatori non impostano alcun DebuggableAttribute. Per impostazione predefinita, il compilatore JIT genera codice macchina con le prestazioni massime e il più difficile da sottoporre a debug. L'abilitazione del rilevamento JIT riduce leggermente le prestazioni e la disabilitazione dell'ottimizzazione le riduce notevolmente.

DebuggableAttribute si applica a un intero assembly per volta, non ai singoli moduli all'interno dell'assembly. Gli strumenti di sviluppo devono quindi associare gli attributi personalizzati al token dei metadati di assembly, se un assembly è già stato creato, o alla classe chiamata System.Runtime.CompilerServices.AssemblyAttributesGoHere. Lo strumento ALink promuove quindi questi attributi DebuggableAttribute da ogni modulo all'assembly di cui diventano parte. In caso di conflitto, l'operazione ALink ha esito negativo.

Nota

Nella versione 1.0 di .NET Framework, il compilatore Microsoft Visual C++ aggiunge DebuggableAttribute quando vengono specificate le opzioni del compilatore /clr e /Zi. Nella versione 1.1 di .NET Framework, è necessario aggiungere manualmente DebuggableAttribute nel codice o usare l'opzione del linker /ASSEMBLYDEBUG.

Vedi anche