Per visualizzare l'articolo in inglese, selezionare la casella di controllo Inglese. È possibile anche visualizzare il testo inglese in una finestra popup posizionando il puntatore del mouse sopra il testo.
Traduzione
Inglese

Aggiornamento profiler

Un profiler che viene caricato quando un'applicazione viene avviata è in grado eventualmente di determinare l'intera cronologia di tale applicazione. Il profiler può richiedere gli eventi di callback appropriati per ottenere informazioni sulle classi e sui moduli caricati, sulle funzioni compilate JIT (Just-In-Time), sugli oggetti allocati e così via. Tuttavia, un profiler che viene caricato tramite connessione a un processo già in esecuzione non è a conoscenza di ciò che si è già verificato, pertanto deve aggiornarsi allo stato corrente dell'applicazione e dell'heap di Garbage Collection associato.

Esistono due modalità fondamentali nelle quali il profiler può aggiornarsi allo stato corrente di un'applicazione, come descritto di seguito.

  • Aggiornamento differito. Quando il profiler incontra nuovi ID, esegue una query per ottenere informazioni su tali ID, anziché presupporre la disponibilità di una cache completa compilata nel momento in cui gli ID vengono creati.

  • Enumerazione. Per determinati tipi di ID, il profiler può richiedere (al momento della connessione) un elenco completo degli ID attualmente attivi e può eseguire una query per ottenere informazioni su tali ID.

Ff354387.collapse_all(it-it,VS.110).gifAggiornamento differito

Nell'aggiornamento differito, il profiler ottiene informazioni sugli ID nel momento in cui li incontra. Ad esempio, se il profiler di campionamento incontra un puntatore a istruzione (IP) in un elemento FunctionID non ancora incontrato, cercherà informazioni su tale funzione, anziché presupporre la disponibilità di una cache completa di funzioni dal momento della compilazione JIT. Se tale FunctionID si trova in un modulo che il profiler non ha ancora incontrato, a quel punto cercherà informazioni su tale ModuleID, anziché presupporre l'esistenza di una cache completa di tutti i moduli. Molti profiler hanno già un comportamento simile a questo se supportano il campionamento in base alle funzioni del formato immagine nativo (NGEN) normale, perché non esistono notifiche JIT per le immagini NGEN.

Ff354387.collapse_all(it-it,VS.110).gifEnumerazione

A partire da .NET Framework 4, l'API di profilatura fornisce metodi enumeratori per ID modulo e ID funzione compilati tramite JIT. 

Questi metodi restituiscono un enumeratore standard tramite il quale è possibile scorrere tutti gli ID di quel tipo attualmente caricati. Notare che EnumJITedFunctions enumera solo gli elementi FunctionID per cui vengono ricevuti gli eventi ICorProfilerCallback::JITCompilationStarted e ICorProfilerCallback::JITCompilationFinished e che non verranno inclusi gli elementi FunctionID originati da moduli NGEN.

Questi enumeratori forniscono uno snapshot degli ID mentre il processo è attivo e in esecuzione. Ciò significa che il profiler può incontrare due possibili race condition.

  • La prima race condition è causata da un'enumerazione eseguita con eccessivo anticipo e può dare origine a buchi e consentire la perdita di ID.

  • La seconda race condition è causata dal caricamento o scaricamento di ID mentre il profiler sta eseguendo l'enumerazione di tali ID.

Nelle sezioni riportate di seguito viene descritto il modo in cui un profiler può evitare la perdita di ID. Per una descrizione dettagliata di entrambe le race condition, vedere la voce Connessione profiler, Parte 2 (la pagina potrebbe essere in inglese) nel blog delle API di profilatura CLR.

Ff354387.collapse_all(it-it,VS.110).gifPer evitare buchi

Per evitare la race condition che causa la formazione di buchi, il profiler dovrebbe chiamare i metodi di enumerazione solo dopo che gli eventi sono stati abilitati. Poiché gli eventi vengono abilitati in un determinato punto dopo il completamento dell'esecuzione del metodo ICorProfilerCallback3::InitializeForAttach, la migliore posizione in cui il profiler può chiamare i metodi di enumerazione è all'interno dell'implementazione del metodo ICorProfilerCallback3::ProfilerAttachComplete, fornita in .NET Framework 4 e versioni successive. Gli eventi vengono abilitati subito prima che CLR richiami ProfilerAttachComplete, pertanto il profiler ha la certezza che gli eventi siano stati abilitati quando chiama l'enumerazione dall'interno di ProfilerAttachComplete. Ciò elimina qualsiasi eventuale buco nelle informazioni di aggiornamento.

Ff354387.collapse_all(it-it,VS.110).gifPer evitare duplicati

Negli esempi riportati di seguito viene illustrato come il profiler possa incontrare duplicati quando chiama un metodo di enumerazione dall'interno del metodo ICorProfilerCallback3::ProfilerAttachComplete. I profiler devono essere consapevoli della possibilità di questa race condition e devono essere preparati a gestirla.

Caricamento con un duplicato:

  1. Il modulo inizia il caricamento. ModuleID è ora enumerabile.

  2. Il profiler si connette.

  3. CLR abilita gli eventi e chiama il metodo ProfilerAttachComplete.

  4. Il profiler chiama il metodo EnumModules.

  5. Il profiler riceve l'evento ModuleLoadFinished.

In questa sequenza, l'enumeratore contiene l'elemento ModuleID, pertanto il profiler vede che il modulo è stato caricato. Tuttavia, il profiler riceve in seguito un evento ModuleLoadFinished che può essere imprevisto poiché l'enumeratore aveva notificato che il modulo era già stato caricato. Il profiler riceve due volte la notifica di un ModuleID: una volta inviata dall'enumerazione e una volta inviata dall'evento.

Scaricamento con un duplicato:

  1. Il modulo viene caricato. Se il profiler fosse stato connesso, sarebbe stato generato l'evento, ma non è questo il caso. ModuleID diventa enumerabile.

  2. Il modulo inizia lo scaricamento. ModuleID non è più enumerabile.

  3. Il profiler si connette.

  4. CLR abilita gli eventi e chiama il metodo ProfilerAttachComplete.

  5. Il profiler chiama il metodo EnumModules.

  6. Il profiler riceve l'evento ModuleUnloadStarted.

Nel passaggio 5, il profiler non vede l'elemento ModuleID in scaricamento, perché non è stato enumerato. Tuttavia, nel passaggio 6, il profiler riceve una notifica dello scaricamento in corso di ModuleID. In questo caso, un profiler riceve una notifica relativa allo scaricamento di un ModuleID di cui non è a conoscenza. Il profiler deve essere consapevole della possibilità di ricevere un evento per un modulo che non conosce.

Un'altra possibile sequenza da considerare si verifica quando la connessione del profiler viene eseguita immediatamente prima dell'eliminazione dell'ID dall'enumerazione.

Scaricamento senza un duplicato:

  1. Il modulo viene caricato. Se il profiler fosse stato connesso, sarebbe stato generato l'evento, ma non è questo il caso. ModuleID diventa enumerabile.

  2. Il modulo inizia lo scaricamento.

  3. Il profiler si connette.

  4. CLR abilita gli eventi e chiama il metodo ProfilerAttachComplete.

  5. Il profiler chiama il metodo EnumModules. ModuleID è ancora presente nell'enumerazione.

  6. ModuleID non è più enumerabile.

  7. Il profiler riceve l'evento ModuleUnloadStarted.

In questa sequenza, l'elemento ModuleID esiste nell'enumerazione. Tuttavia, il profiler avrebbe potuto eseguire l'iterazione dell'enumerazione per un certo tempo e quindi potrebbe non conoscere l'elemento ModuleID quando riceve l'evento ModuleUnloadStarted. Per questo motivo, il profiler deve considerare l'evento quando incontra l'elemento ModuleID nell'enumerazione.

La regola generale suggerisce che i callback di caricamento/scaricamento vengono sempre inviati dopo la modifica dell'enumerabilità di un ID. Pertanto, i callback sono più recenti delle enumerazioni e devono avere sempre la precedenza sulle enumerazioni meno recenti.

La maggior parte dei profiler di memoria rispondono agli eventi di Garbage Collection. Quando il profiler di memoria si connette a un processo in esecuzione, deve gestire correttamente il fatto di non disporre ancora di una cache di oggetti nell'heap. I profiler di memoria devono gestire con attenzione il caso in cui Garbage Collection è già in esecuzione al momento della connessione del profiler e devono considerare l'eventualità di avviare Garbage Collection al momento della connessione per compilare la cache iniziale di oggetti di Garbage Collection.

Ff354387.collapse_all(it-it,VS.110).gifGestione di Garbage Collection già in corso

Il profiler si connette in un punto arbitrario durante l'esecuzione del processo, possibilmente quando Garbage Collection è già in corso. Ciò significa che una volta che il profiler ha abilitato gli eventi di callback, può iniziare a ricevere i callback di Garbage Collection (ad esempio, MovedReference e ObjectReferences) da tale Garbage Collection in corso, senza ricevere prima una notifica GarbageCollectionStarted. Il profiler deve ignorare tutti i callback di Garbage Collection finché il primo Garbage Collection completo o il Garbage Collection indotto dal profiler non è iniziato, ovvero finché non riceve il primo callback GarbageCollectionStarted, per un Garbage Collection standard o dopo la chiamata del metodo ICorProfilerInfo::ForceGC.

Ff354387.collapse_all(it-it,VS.110).gifIndurre Garbage Collection di aggiornamento

Può risultare utile per un profiler attivare il primo Garbage Collection completo dopo aver effettuato la connessione al processo. Questo consentirà al profiler di utilizzare eventi quali RootReferences2 e ObjectReferences durante tale Garbage Collection iniziale per compilare la propria cache di oggetti. Dopo il Garbage Collection iniziale di aggiornamento, il profiler può gestire i successivi Garbage Collection nella modalità standard.

Notare che il callback ICorProfilerCallback::ObjectAllocated non è disponibile per i profiler che si connettono a processi in esecuzione. Il profiler dovrà pertanto risolvere qualsiasi supposizione sui callback ObjectAllocated. Gli oggetti allocati dal momento dell'ultimo Garbage Collection possono risultare sconosciuti al profiler finché non incontra riferimenti a tali oggetti tramite i callback del Garbage Collection durante la successiva raccolta (a meno che il profiler non incontri tali oggetti in altre modalità, ad esempio, come parametri passati a metodi connessi alle probe enter/leave/tailcall).

Mostra: