Condividi tramite


Profiler CLR e app di Windows Store

Questo argomento illustra cosa occorre considerare quando si scrivono strumenti di diagnostica che analizzano il codice gestito in esecuzione all'interno di un'app di Windows Store. Fornisce inoltre linee guida per modificare gli strumenti di sviluppo esistenti in modo che continuino a funzionare quando vengono eseguiti nelle app di Windows Store. Per comprendere queste informazioni, è consigliabile acquisire familiarità con l'API di profilatura di Common Language Runtime, è già stata usata questa API in uno strumento di diagnostica che viene eseguito correttamente sulle applicazioni desktop di Windows e si è interessati a modificare lo strumento per l'esecuzione corretta nelle app di Windows Store.

Introduzione

Se è stato creato oltre il paragrafo introduttivo, si ha familiarità con l'API profilatura CLR. È già stato scritto uno strumento di diagnostica adatto alle applicazioni desktop gestite. Ora sei curioso di cosa fare in modo che lo strumento funzioni con un'app di Windows Store gestita. Forse hai già provato a fare questo lavoro e hai scoperto che non è un compito semplice. In effetti, esistono alcune considerazioni che potrebbero non essere ovvie per tutti gli sviluppatori di strumenti. Ad esempio:

  • Le app di Windows Store vengono eseguite in un contesto con autorizzazioni notevolmente ridotte.

  • I file di metadati di Windows presentano caratteristiche univoci rispetto ai moduli gestiti tradizionali.

  • Le app di Windows Store hanno l'abitudine di sospendere se stesse quando l'interattività diventa inattiva.

  • I meccanismi di comunicazione tra processi potrebbero non funzionare più per vari motivi.

In questo argomento sono elencati gli aspetti da conoscere e come gestirli correttamente.

Se non si ha familiarità con l'API profilatura CLR, passare alle risorse alla fine di questo argomento per trovare informazioni introduttive migliori.

Fornisce informazioni dettagliate su API Windows specifiche e su come devono essere usate anche all'esterno dell'ambito di questo argomento. Si consideri questo argomento un punto di partenza e fare riferimento a MSDN per altre informazioni sulle API di Windows a cui si fa riferimento qui.

Architettura e terminologia

In genere, uno strumento di diagnostica ha un'architettura simile a quella illustrata nella figura seguente. Usa il termine "profiler", ma molti di questi strumenti vanno ben oltre le normali prestazioni o la profilatura della memoria in aree come code coverage, framework di oggetti fittizi, debug di viaggi in tempo, monitoraggio delle applicazioni e così via. Per semplicità, questo argomento continuerà a fare riferimento a tutti questi strumenti come profiler.

In questo argomento viene usata la terminologia seguente:

Applicazione

Si tratta dell'applicazione che il profiler sta analizzando. In genere, lo sviluppatore di questa applicazione usa il profiler per diagnosticare i problemi con l'applicazione. Tradizionalmente, questa applicazione sarebbe un'applicazione desktop di Windows, ma in questo argomento verranno esaminate le app di Windows Store.

Profiler DLL

Si tratta del componente che carica nello spazio di elaborazione dell'applicazione da analizzare. Questo componente, noto anche come "agente del profiler", implementa le interfacce ICorProfilerCallbackICorProfilerCallback Interface(2,3 e così via) e utilizza le interfacce ICorProfilerInfo(2,3 e così via) per raccogliere dati sull'applicazione analizzata e potenzialmente modificare gli aspetti del comportamento dell'applicazione.

Interfaccia utente del profiler

Si tratta di un'applicazione desktop con cui l'utente del profiler interagisce. È responsabile della visualizzazione dello stato dell'applicazione all'utente e di fornire all'utente i mezzi per controllare il comportamento dell'applicazione analizzata. Questo componente viene sempre eseguito nel proprio spazio di processo, separato dallo spazio di processo dell'applicazione sottoposta a profilatura. L'interfaccia utente del profiler può anche fungere da "trigger di collegamento", ovvero il processo che chiama il metodo ICLRProfiling::AttachProfiler , per fare in modo che l'applicazione analizzata carichi la DLL profiler in quei casi in cui la DLL del profiler non è stata caricata all'avvio.

Importante

L'interfaccia utente del profiler deve rimanere un'applicazione desktop di Windows, anche quando viene usata per controllare e segnalare un'app di Windows Store. Non aspettatevi di essere in grado di creare un pacchetto e spedire lo strumento di diagnostica in Windows Store. Lo strumento deve eseguire operazioni che le app di Windows Store non possono eseguire e molte di queste operazioni risiedono all'interno dell'interfaccia utente di Profiler.

In questo documento, il codice di esempio presuppone che:

  • La DLL del profiler viene scritta in C++, perché deve essere una DLL nativa, in base ai requisiti dell'API di profilatura CLR.

  • L'interfaccia utente del profiler è scritta in C#. Questo non è necessario, ma perché non esistono requisiti per la lingua per il processo dell'interfaccia utente di Profiler, perché non scegliere una lingua concisa e semplice?

Dispositivi Windows RT

I dispositivi Windows RT sono abbastanza bloccati. I profiler di terze parti non possono semplicemente essere caricati su tali dispositivi. Questo documento è incentrato sui PC Windows 8.

Uso delle API di Windows Runtime

In diversi scenari descritti nelle sezioni seguenti, l'applicazione desktop dell'interfaccia utente profiler deve usare alcune nuove API di Windows Runtime. Si vuole consultare la documentazione per comprendere quali API di Windows Runtime possono essere usate dalle applicazioni desktop e se il loro comportamento è diverso quando viene chiamato dalle applicazioni desktop e dalle app di Windows Store.

Se l'interfaccia utente di Profiler è scritta nel codice gestito, sarà necessario eseguire alcuni passaggi per semplificare l'utilizzo di tali API di Windows Runtime. Per altre informazioni, vedere l'articolo App desktop gestite e Windows Runtime .

Caricamento della DLL del profiler

Questa sezione descrive come l'interfaccia utente del profiler fa sì che l'app di Windows Store carichi la DLL del profiler. Il codice descritto in questa sezione appartiene all'app desktop dell'interfaccia utente di Profiler e implica quindi l'uso di API di Windows sicure per le app desktop, ma non necessariamente sicure per le app di Windows Store.

L'interfaccia utente del profiler può causare il caricamento della DLL del profiler nello spazio di elaborazione dell'applicazione in due modi:

  • All'avvio dell'applicazione, come controllato dalle variabili di ambiente.

  • Connettendosi all'applicazione al termine dell'avvio chiamando il metodo ICLRProfiling::AttachProfiler .

Uno dei primi ostacoli consiste nel ottenere il caricamento di avvio e il caricamento del collegamento della DLL del profiler per funzionare correttamente con le app di Windows Store. Entrambe le forme di caricamento condividono alcune considerazioni speciali in comune, quindi iniziamo con loro.

Considerazioni comuni per l'avvio e il collegamento dei carichi

Firma della DLL del profiler

Quando Windows tenta di caricare la DLL del profiler, verifica che la DLL del profiler sia firmata correttamente. In caso contrario, il caricamento ha esito negativo per impostazione predefinita. A questo scopo è possibile procedere in due modi:

  • Assicurarsi che la DLL del profiler sia firmata.

  • Informare l'utente che deve installare una licenza per sviluppatori nel computer Windows 8 prima di usare lo strumento. Questa operazione può essere eseguita automaticamente da Visual Studio o manualmente da un prompt dei comandi. Per altre informazioni, vedere Ottenere una licenza per sviluppatori.

Autorizzazioni del file system

L'app di Windows Store deve avere l'autorizzazione per caricare ed eseguire la DLL del profiler dal percorso nel file system in cui si trova per impostazione predefinita, l'app di Windows Store non dispone di tale autorizzazione per la maggior parte delle directory e qualsiasi tentativo non riuscito di caricare la DLL del profiler genererà una voce nel registro eventi dell'applicazione di Windows simile alla seguente:

NET Runtime version 4.0.30319.17929 - Loading profiler failed during CoCreateInstance.  Profiler CLSID: '{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}'.  HRESULT: 0x80070005.  Process ID (decimal): 4688.  Message ID: [0x2504].

In genere, le app di Windows Store possono accedere solo a un set limitato di posizioni sul disco. Ogni app di Windows Store può accedere alle proprie cartelle dati dell'applicazione, nonché ad altre aree nel file system per cui a tutte le app di Windows Store viene concesso l'accesso. È consigliabile installare la DLL del profiler e le relative dipendenze in Programmi o Programmi (x86), perché per impostazione predefinita tutte le app di Windows Store hanno autorizzazioni di lettura ed esecuzione.

Caricamento di avvio

In genere, in un'app desktop, l'interfaccia utente di Profiler richiede un caricamento di avvio della DLL del profiler inizializzando un blocco di ambiente che contiene le variabili di ambiente dell'API profilatura CLR necessarie (ad esempio, COR_PROFILER, COR_ENABLE_PROFILINGe COR_PROFILER_PATH) e quindi creando un nuovo processo con tale blocco di ambiente. Lo stesso vale per le app di Windows Store, ma i meccanismi sono diversi.

Non eseguire con privilegi elevati

Se il processo A tenta di generare il processo B dell'app di Windows Store, il processo A deve essere eseguito a livello di integrità medio, non a livello di integrità elevato ,ovvero non elevato. Ciò significa che l'interfaccia utente del profiler deve essere in esecuzione a livello di integrità media oppure deve generare un altro processo desktop a livello di integrità medio per occuparsi dell'avvio dell'app di Windows Store.

Scelta di un'app di Windows Store da profilare

In primo luogo, dovrai chiedere all'utente del profiler quale app di Windows Store avviare. Per le app desktop, è possibile che venga visualizzata una finestra di dialogo Sfoglia file e che l'utente trovi e selezioni un file .exe. Ma le app di Windows Store sono diverse e l'uso di una finestra di dialogo Sfoglia non ha senso. È invece preferibile mostrare all'utente un elenco di app di Windows Store installate per l'utente da selezionare.

È possibile usare la PackageManager classe per generare questo elenco. PackageManager è una classe di Windows Runtime disponibile per le app desktop e in realtà è disponibile solo per le app desktop.

L'esempio di codice seguente di un'interfaccia utente ipotetica di Profiler scritta come app desktop in C# usa per PackageManager generare un elenco di app di Windows:

string currentUserSID = WindowsIdentity.GetCurrent().User.ToString();
IAppxFactory appxFactory = (IAppxFactory) new AppxFactory();
PackageManager packageManager = new PackageManager();
IEnumerable<Package> packages = packageManager.FindPackagesForUser(currentUserSID);

Specifica del blocco di ambiente personalizzato

Una nuova interfaccia COM, IPackageDebug Impostazioni, consente di personalizzare il comportamento di esecuzione di un'app di Windows Store per semplificare alcune forme di diagnostica. Uno dei suoi metodi, EnableDebugging, consente di passare un blocco di ambiente all'app di Windows Store al momento dell'avvio, insieme ad altri effetti utili come la disabilitazione della sospensione automatica del processo. Il blocco di ambiente è importante perché è necessario specificare le variabili di ambiente (COR_PROFILER, COR_ENABLE_PROFILINGe COR_PROFILER_PATH)) usate da CLR per caricare la DLL del profiler.

Si consideri il frammento di codice seguente:

IPackageDebugSettings pkgDebugSettings = new PackageDebugSettings();
pkgDebugSettings.EnableDebugging(packageFullName, debuggerCommandLine,
                                                                 (IntPtr)fixedEnvironmentPzz);

Ci sono un paio di elementi che dovrai ottenere correttamente:

  • packageFullName può essere determinato durante l'iterazione dei pacchetti e l'afferramento di package.Id.FullName.

  • debuggerCommandLine è un po ' più interessante. Per passare il blocco di ambiente personalizzato all'app di Windows Store, devi scrivere il debugger fittizio personalizzato. Windows genera l'app di Windows Store sospesa e quindi collega il debugger avviando il debugger con una riga di comando come in questo esempio:

    MyDummyDebugger.exe -p 1336 -tid 1424
    

    dove -p 1336 indica che l'app di Windows Store ha l'ID processo 1336 e -tid 1424 indica che l'ID thread 1424 è il thread sospeso. Il debugger fittizio analizza l'ID thread dalla riga di comando, riprende il thread e quindi termina.

    Ecco un esempio di codice C++ per eseguire questa operazione (assicurarsi di aggiungere il controllo degli errori!):

    int wmain(int argc, wchar_t* argv[])
    {
        // …
        // Parse command line here
        // …
    
        HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME,
                                                                  FALSE /* bInheritHandle */, nThreadID);
        ResumeThread(hThread);
        CloseHandle(hThread);
        return 0;
    }
    

    È necessario distribuire questo debugger fittizio come parte dell'installazione dello strumento di diagnostica e quindi specificare il percorso di questo debugger nel debuggerCommandLine parametro .

Avvio dell'app di Windows Store

Il momento in cui avviare l'app di Windows Store è finalmente arrivata. Se hai già provato a eseguire questa operazione manualmente, potresti aver notato che CreateProcess non è il modo in cui crei un processo di app di Windows Store. Dovrai invece usare il metodo IApplicationActivationManager::ActivateApplication . A tale scopo, dovrai ottenere l'ID modello utente dell'app di Windows Store che stai avviando. E questo significa che dovrai fare un po 'di scavare attraverso il manifesto.

Durante l'iterazione dei pacchetti (vedi "Scelta di un'app di Windows Store da profilare" nella sezione Caricamento di avvio in precedenza), vuoi acquisire il set di applicazioni contenute nel manifesto del pacchetto corrente:

string manifestPath = package.InstalledLocation.Path + "\\AppxManifest.xml";

AppxPackaging.IStream manifestStream;
SHCreateStreamOnFileEx(
                    manifestPath,
                    0x00000040,     // STGM_READ | STGM_SHARE_DENY_NONE
                    0,              // file creation attributes
                    false,          // fCreate
                    null,           // reserved
                    out manifestStream);

IAppxManifestReader manifestReader = appxFactory.CreateManifestReader(manifestStream);

IAppxManifestApplicationsEnumerator appsEnum = manifestReader.GetApplications();

Sì, un pacchetto può avere più applicazioni e ogni applicazione ha il proprio ID modello utente applicazione. È quindi necessario chiedere all'utente quale applicazione profilare e recuperare l'ID modello utente applicazione da tale applicazione specifica:

while (appsEnum.GetHasCurrent() != 0)
{
    IAppxManifestApplication app = appsEnum.GetCurrent();
    string appUserModelId = app.GetAppUserModelId();
    //...
}

Infine, ora hai ciò che devi avviare l'app di Windows Store:

IApplicationActivationManager appActivationMgr = new ApplicationActivationManager();
appActivationMgr.ActivateApplication(appUserModelId, appArgs, ACTIVATEOPTIONS.AO_NONE, out pid);

Ricordarsi di chiamare DisableDebugging

Quando hai chiamato IPackageDebug Impostazioni::EnableDebugging, hai promesso di eseguire la pulizia dopo te chiamando il metodo IPackageDebug Impostazioni::D isableDebugging, quindi assicurati di farlo quando la sessione di profilatura è finita.

Collegare il carico

Quando l'interfaccia utente del profiler vuole collegare la DLL profiler a un'applicazione già in esecuzione, usa ICLRProfiling::AttachProfiler. Lo stesso vale per le app di Windows Store. Tuttavia, oltre alle considerazioni comuni elencate in precedenza, assicurarsi che l'app di Windows Store di destinazione non sia sospesa.

EnableDebugging

Come per il caricamento di avvio, chiamare il metodo IPackageDebug Impostazioni::EnableDebugging. Non è necessario per passare un blocco di ambiente, ma è necessaria una delle altre funzionalità: disabilitazione della sospensione automatica del processo. In caso contrario, quando l'interfaccia utente del profiler chiama AttachProfiler, l'app di Windows Store di destinazione potrebbe essere sospesa. In effetti, questo è probabile se l'utente interagisce ora con l'interfaccia utente del profiler e l'app di Windows Store non è attiva in nessuna delle schermate dell'utente. Se l'app di Windows Store è sospesa, non sarà in grado di rispondere ad alcun segnale inviato da CLR per allegare la DLL del profiler.

È quindi consigliabile eseguire operazioni simili alla seguente:

IPackageDebugSettings pkgDebugSettings = new PackageDebugSettings();
pkgDebugSettings.EnableDebugging(packageFullName, null /* debuggerCommandLine */,
                                                                 IntPtr.Zero /* environment */);

Questa è la stessa chiamata che si farebbe per il caso di caricamento di avvio, tranne che non si specifica una riga di comando del debugger o un blocco di ambiente.

DisableDebugging

Come sempre, non dimenticare di chiamare IPackageDebug Impostazioni::D isableDebugging al termine della sessione di profilatura.

Esecuzione all'interno dell'app di Windows Store

Quindi l'app di Windows Store ha infine caricato la DLL del profiler. Ora la DLL del profiler deve essere illustrata in base alle diverse regole richieste dalle app di Windows Store, incluse le API consentite e come eseguire con autorizzazioni ridotte.

Attenersi alle API delle app di Windows Store

Mentre si esplora l'API di Windows, si noterà che ogni API è documentata come applicabile alle app desktop, alle app di Windows Store o a entrambe. Ad esempio, la sezione Requisiti della documentazione per la funzione InitializeCriticalSectionAndSpinCount indica che la funzione si applica solo alle app desktop. Al contrario, la funzione InitializeCriticalSectionEx è disponibile sia per le app desktop che per le app di Windows Store.

Quando si sviluppa la DLL del profiler, considerarla come se fosse un'app di Windows Store e usa solo le API documentate come disponibili per le app di Windows Store. Analizzare le dipendenze( ad esempio, è possibile eseguire link /dump /imports sulla DLL del profiler da controllare) e quindi cercare nella documentazione per vedere quali dipendenze sono ok e quali no. Nella maggior parte dei casi, le violazioni possono essere risolte semplicemente sostituendole con una forma più recente dell'API documentata come sicura, ad esempio sostituendo InitializeCriticalSectionAndSpinCount con InitializeCriticalSectionEx.

Potresti notare che la DLL del profiler chiama alcune API che si applicano solo alle app desktop, ma sembrano funzionare anche quando la DLL del profiler viene caricata all'interno di un'app di Windows Store. Tenere presente che è rischioso usare qualsiasi API non documentata per l'uso con le app di Windows Store nella DLL del profiler quando viene caricato in un processo di app di Windows Store:

  • Queste API non sono garantite per funzionare quando vengono chiamate nel contesto univoco in cui le app di Windows Store vengono eseguite.

  • Queste API potrebbero non funzionare in modo coerente quando vengono chiamate da processi di app di Windows Store diversi.

  • Tali API potrebbero sembrare funzionare correttamente dalle app di Windows Store nella versione corrente di Windows, ma potrebbero interrompersi o essere disabilitate nelle versioni future di Windows.

Il consiglio migliore è quello di correggere tutte le violazioni ed evitare il rischio.

Potresti scoprire che non puoi assolutamente fare senza un'API specifica e non puoi trovare una sostituzione adatta per le app di Windows Store. In questo caso, almeno:

  • Testare, testare le luci viventi dall'uso di tale API.

  • Comprendere che l'API potrebbe improvvisamente interrompersi o scomparire se viene chiamato dall'interno delle app di Windows Store nelle versioni future di Windows. Questo non sarà considerato un problema di compatibilità da Parte di Microsoft e il supporto dell'utilizzo di esso non sarà una priorità.

Autorizzazioni ridotte

Non rientra nell'ambito di questo argomento per elencare tutti i modi in cui le autorizzazioni delle app di Windows Store differiscono dalle app desktop. Ma certamente il comportamento sarà diverso ogni volta che la DLL del profiler (quando caricata in un'app di Windows Store rispetto a un'app desktop) tenta di accedere a qualsiasi risorsa. Il file system è l'esempio più comune. Esistono tuttavia alcune posizioni sul disco a cui una determinata app di Windows Store è autorizzata ad accedere (vedi Accesso ai file e autorizzazioni (app di Windows Runtime) e la DLL del profiler si troverà nelle stesse restrizioni. Testare accuratamente il codice.

Comunicazione tra processi

Come illustrato nel diagramma all'inizio di questo documento, la DLL del profiler (caricata nello spazio di elaborazione dell'app di Windows Store) dovrà probabilmente comunicare con l'interfaccia utente del profiler (in esecuzione in uno spazio di elaborazione dell'app desktop separato) tramite il canale IPC (Custom Inter-Process Communication). L'interfaccia utente del profiler invia segnali alla DLL del profiler per modificarne il comportamento e la DLL profiler invia i dati dall'app di Windows Store analizzata all'interfaccia utente del profiler per la post-elaborazione e la visualizzazione all'utente del profiler.

La maggior parte dei profiler deve funzionare in questo modo, ma le scelte per i meccanismi IPC sono più limitate quando la DLL del profiler viene caricata in un'app di Windows Store. Ad esempio, le named pipe non fanno parte dell'SDK dell'app di Windows Store, quindi non è possibile usarle.

Ma naturalmente, i file sono ancora in, anche se in modo più limitato. Sono disponibili anche eventi.

Comunicazione tramite file

La maggior parte dei dati passerà probabilmente tra la DLL del profiler e l'interfaccia utente del profiler tramite file. La chiave consiste nel selezionare un percorso di file a cui sia la DLL profiler (nel contesto di un'app di Windows Store) sia l'interfaccia utente di Profiler abbiano accesso in lettura e scrittura. Ad esempio, il percorso della cartella temporanea è un percorso a cui possono accedere sia la DLL del profiler che l'interfaccia utente del profiler, ma nessun altro pacchetto dell'app di Windows Store può accedere (in modo da schermatura tutte le informazioni che registri da altri pacchetti di app di Windows Store).

Sia l'interfaccia utente del profiler che la DLL del profiler possono determinare questo percorso in modo indipendente. L'interfaccia utente del profiler, quando scorre tutti i pacchetti installati per l'utente corrente (vedere il codice di esempio in precedenza), ottiene l'accesso alla PackageId classe da cui il percorso della cartella temporanea può essere derivato con codice simile a questo frammento. (Come sempre, il controllo degli errori viene omesso per brevità).

// C# code for the Profiler UI.
ApplicationData appData =
    ApplicationDataManager.CreateForPackageFamily(
        packageId.FamilyName);

tempDir = appData.TemporaryFolder.Path;

Nel frattempo, la DLL del profiler può eseguire fondamentalmente la stessa operazione, anche se può raggiungere più facilmente la ApplicationData classe usando la proprietà ApplicationData.Current .

Comunicazione tramite eventi

Se vuoi una semplice semantica di segnalazione tra l'interfaccia utente di Profiler e la DLL profiler, puoi usare eventi all'interno di app di Windows Store e app desktop.

Dalla DLL del profiler è sufficiente chiamare la funzione CreateEventEx per creare un evento denominato con qualsiasi nome. Ad esempio:

// Profiler DLL in Windows Store app (C++).
CreateEventEx(
    NULL,  // Not inherited
    "MyNamedEvent"
    CREATE_EVENT_MANUAL_RESET, /* explicit ResetEvent() required; leave initial state unsignaled */
    EVENT_ALL_ACCESS);

L'interfaccia utente del profiler deve quindi trovare l'evento denominato nello spazio dei nomi dell'app di Windows Store. Ad esempio, l'interfaccia utente del profiler potrebbe chiamare CreateEventEx, specificando il nome dell'evento come

AppContainerNamedObjects\<acSid>\MyNamedEvent

<acSid> è il SID AppContainer dell'app di Windows Store. Una sezione precedente di questo argomento ha illustrato come scorrere i pacchetti installati per l'utente corrente. Da questo codice di esempio è possibile ottenere il packageId. E dal packageId è possibile ottenere con codice <acSid> simile al seguente:

IntPtr acPSID;
DeriveAppContainerSidFromAppContainerName(packageId.FamilyName, out acPSID);

string acSid;
ConvertSidToStringSid(acPSID, out acSid);

string acDir;
GetAppContainerFolderPath(acSid, out acDir);

Nessuna notifica di arresto

Quando si esegue all'interno di un'app di Windows Store, la DLL del profiler non deve basarsi su ICorProfilerCallback::Shutdown o anche DllMain (con DLL_PROCESS_DETACH) che viene chiamata per notificare alla DLL del profiler che l'app di Windows Store è in uscita. Infatti, dovresti aspettarti che non verranno mai chiamati. Storicamente, molte DLL profiler hanno usato tali notifiche come luoghi pratici per scaricare cache su disco, chiudere i file, inviare notifiche all'interfaccia utente del profiler e così via. Ma ora la DLL del profiler deve essere organizzata in modo leggermente diverso.

La DLL del profiler deve registrare le informazioni man mano che passa. Per motivi di prestazioni, è consigliabile raggruppare le informazioni in memoria e scaricarla su disco man mano che il batch aumenta di dimensioni oltre una soglia. Si supponga tuttavia che qualsiasi informazione non ancora scaricata su disco possa andare persa. Ciò significa che si vuole selezionare la soglia in modo saggio e che l'interfaccia utente del profiler deve essere avanzata per gestire le informazioni incomplete scritte dalla DLL del Profiler.

File di metadati di Windows Runtime

Non rientra nell'ambito di questo documento per esaminare in dettaglio i file di metadati (WinMD) di Windows Runtime. Questa sezione è limitata al modo in cui l'API profilatura CLR reagisce quando i file WinMD vengono caricati dall'app di Windows Store che la DLL del profiler sta analizzando.

WinMD gestiti e non gestiti

Se uno sviluppatore usa Visual Studio per creare un nuovo progetto componente Windows Runtime, una compilazione di tale progetto produce un file WinMD che descrive i metadati (le descrizioni dei tipi di classi, interfacce e così via) creati dallo sviluppatore. Se questo progetto è un progetto di linguaggio gestito scritto in C# o Visual Basic, lo stesso file WinMD contiene anche l'implementazione di tali tipi (vale a dire che contiene tutto il IL compilato dal codice sorgente dello sviluppatore). Tali file sono noti come file WinMD gestiti. Sono interessanti in quanto contengono sia i metadati di Windows Runtime che l'implementazione sottostante.

Al contrario, se uno sviluppatore crea un progetto componente Windows Runtime per C++, una compilazione di tale progetto produce un file WinMD che contiene solo metadati e l'implementazione viene compilata in una DLL nativa separata. Analogamente, i file WinMD forniti in Windows SDK contengono solo metadati, con l'implementazione compilata in DLL native separate incluse in Windows.

Le informazioni seguenti si applicano a winMD gestiti, che contengono metadati e implementazione, e a winMD non gestiti, che contengono solo metadati.

I file WinMD hanno un aspetto simile ai moduli CLR

Per quanto riguarda CLR, tutti i file WinMD sono moduli. L'API profilatura CLR indica quindi alla DLL del profiler quando i file WinMD vengono caricati e quali sono i relativi moduleID, allo stesso modo di altri moduli gestiti.

La DLL del profiler può distinguere i file WinMD da altri moduli chiamando il metodo ICorProfilerInfo3::GetModuleInfo2 e controllando il pdwModuleFlags parametro di output per il flag COR_PRF_MODULE_WINDOWS_RUNTIME . Viene impostato se e solo se ModuleID rappresenta un WinMD.

Lettura dei metadati da WinMD

I file WinMD, come i moduli normali, contengono metadati che possono essere letti tramite le API metadati. Tuttavia, CLR esegue il mapping dei tipi Windows Runtime ai tipi .NET Framework quando legge i file WinMD in modo che gli sviluppatori che programmano nel codice gestito e utilizzano il file WinMD possano avere un'esperienza di programmazione più naturale. Per alcuni esempi di questi mapping, vedi Supporto di .NET Framework per le app di Windows Store e Windows Runtime.

Quale visualizzazione otterrà il profiler quando usa le API dei metadati: la visualizzazione Windows Runtime non elaborata o la visualizzazione di .NET Framework mappata? La risposta: spetta a te.

Quando chiami il metodo ICorProfilerInfo::GetModuleMetaData in un WinMD per ottenere un'interfaccia di metadati, ad esempio IMetaDataImport, puoi scegliere di impostare ofNoTransform nel dwOpenFlags parametro per disattivare questo mapping. In caso contrario, per impostazione predefinita, il mapping verrà abilitato. In genere, un profiler manterrà abilitato il mapping, in modo che le stringhe ottenute dalla DLL profiler dai metadati WinMD (ad esempio, nomi di tipi) siano familiari e naturali per l'utente del profiler.

Modifica dei metadati da WinMD

La modifica dei metadati in WinMD non è supportata. Se chiami il metodo ICorProfilerInfo::GetModuleMetaData per un file WinMD e specifichi ofWrite nel dwOpenFlags parametro o chiedi un'interfaccia di metadati scrivibile, ad esempio IMetaDataEmit, GetModuleMetaData avrà esito negativo. Questa è di particolare importanza per i profiler di riscrittura IL, che devono modificare i metadati per supportare la strumentazione, ad esempio per aggiungere AssemblyRefs o nuovi metodi. È quindi consigliabile cercare prima COR_PRF_MODULE_WINDOWS_RUNTIME (come descritto nella sezione precedente) e non richiedere interfacce di metadati scrivibili in tali moduli.

Risoluzione dei riferimenti agli assembly con WinMD

Molti profiler devono risolvere manualmente i riferimenti ai metadati per facilitare l'ispezione della strumentazione o del tipo. Tali profiler devono essere consapevoli del modo in cui CLR risolve i riferimenti agli assembly che puntano a WinMD, perché tali riferimenti vengono risolti in modo completamente diverso rispetto ai riferimenti agli assembly standard.

Profiler di memoria

Il Garbage Collector e l'heap gestito non sono fondamentalmente diversi in un'app di Windows Store e in un'app desktop. Esistono tuttavia alcune piccole differenze di cui gli autori del profiler devono essere a conoscenza.

ForceGC crea un thread gestito

Quando si esegue la profilatura della memoria, la DLL del profiler crea in genere un thread separato da cui chiamare il metodo ForceGC . Non c'è niente di nuovo. Ma ciò che potrebbe essere sorprendente è che l'azione di eseguire una Garbage Collection all'interno di un'app di Windows Store può trasformare il thread in un thread gestito (ad esempio, un ThreadID dell'API profilatura verrà creato per tale thread).

Per comprendere le conseguenze di questo problema, è importante comprendere le differenze tra chiamate sincrone e asincrone, come definito dall'API profilatura CLR. Si noti che questo è molto diverso dal concetto di chiamate asincrone nelle app di Windows Store. Per altre informazioni, vedere il post di blog Why we have CORPROF_E_UNSUPPORTED_CALL_edizione Standard QUENCE .See the blog post Why we have CORPROF_E_UNSUPPORTED_CALL_edizione Standard QUENCE for more information.

Il punto rilevante è che le chiamate effettuate sui thread creati dal profiler vengono sempre considerate sincrone, anche se tali chiamate vengono effettuate dall'esterno di un'implementazione di uno dei metodi ICorProfilerCallback della DLL del profiler. Almeno, che era il caso. Ora che CLR ha trasformato il thread del profiler in un thread gestito a causa della chiamata al metodo ForceGC, tale thread non è più considerato il thread del profiler. Di conseguenza, CLR applica una definizione più rigorosa di ciò che viene qualificato come sincrono per quel thread, vale a dire che una chiamata deve provenire dall'interno di uno dei metodi ICorProfilerCallback della DLL del profiler per qualificarsi come sincrona.

Cosa significa in pratica? La maggior parte dei metodi ICorProfilerInfo è sicura solo per essere chiamata in modo sincrono e non riuscirà immediatamente in caso contrario. Pertanto, se la DLL del profiler riutilizza il thread del metodo ForceGC per altre chiamate eseguite in genere nei thread creati dal profiler, ad esempio a RequestProfilerDetach, RequestReJIT o RequestRevert, si avranno problemi. Anche una funzione asincrona, ad esempio DoStackSnapshot , dispone di regole speciali quando viene chiamato dai thread gestiti. (Vedere il post di blogStack walking profiler: Nozioni di base e oltre per altre informazioni.

È pertanto consigliabile usare qualsiasi thread creato dalla DLL del profiler per chiamare il metodoForceGC solo allo scopo di attivare I GC e quindi rispondere ai callback GC. Non deve chiamare l'API profilatura per eseguire altre attività, ad esempio il campionamento dello stack o la disconnessione.

ConditionalWeakTableReferences

A partire da .NET Framework 4.5 è disponibile un nuovo callback GC, ConditionalWeakTableElementReferences, che offre al profiler informazioni più complete sugli handle dipendenti. Questi handle aggiungono in modo efficace un riferimento da un oggetto di origine a un oggetto di destinazione allo scopo della gestione della durata di GC. Gli handle dipendenti non sono niente di nuovo e gli sviluppatori che programmano nel codice gestito sono stati in grado di creare handle dipendenti personalizzati usando la System.Runtime.CompilerServices.ConditionalWeakTable<TKey,TValue> classe anche prima di Windows 8 e .NET Framework 4.5.

Tuttavia, le app di Windows Store XAML gestite ora usano pesantemente gli handle dipendenti. In particolare, CLR li usa per facilitare la gestione dei cicli di riferimento tra oggetti gestiti e oggetti Windows Runtime non gestiti. Ciò significa che è più importante che mai per i profiler di memoria essere informati di questi handle dipendenti in modo che possano essere visualizzati insieme al resto dei bordi nel grafico dell'heap. La DLL del profiler deve usare RootReferences2, ObjectReferences e ConditionalWeakTableElementReferences insieme per formare una visualizzazione completa del grafico dell'heap.

Conclusione

È possibile usare l'API profilatura CLR per analizzare il codice gestito in esecuzione nelle app di Windows Store. In effetti, puoi prendere un profiler esistente che stai sviluppando e apportare alcune modifiche specifiche in modo che possa avere come destinazione le app di Windows Store. L'interfaccia utente del profiler deve usare le nuove API per attivare l'app di Windows Store in modalità di debug. Assicurarsi che la DLL del profiler utilizza solo le API applicabili per le app di Windows Store. Il meccanismo di comunicazione tra la DLL del profiler e l'interfaccia utente del profiler deve essere scritto tenendo presente le restrizioni dell'API delle app di Windows Store e tenendo presenti le autorizzazioni limitate applicate per le app di Windows Store. La DLL del profiler deve essere a conoscenza del modo in cui CLR gestisce winMD e del comportamento del Garbage Collector rispetto ai thread gestiti.

Risorse

The Common Language Runtime

Interazione di CLR con Windows Runtime

App di Windows Store