Costruire una base di test del percorso di fallimento Windows con Application Verifier
· Aggiornato il: · Go Komura · Sviluppo Windows, Indagine bug, Telecamera industriale, Application Verifier, Test del percorso di errore, Handle leak
Application Verifier è uno strumento potente quando si desidera far emergere, in anticipo, le anomalie che si verificano nel codice nativo Windows e al limite Win32. Soprattutto quando si desidera testare la gestione di anomalie, corruzione heap e percorsi di errore con risorse limitate, è possibile far emergere abbastanza rapidamente problemi che il solo test del percorso normale non mostrerebbe mai.
Nella parte 1, Quando un’app di controllo della telecamera industriale si blocca improvvisamente dopo un mese (Parte 1) - Individuazione delle perdite di gestione e progettazione dei registri per operazioni di lunga durata, abbiamo trattato un caso in cui l’analisi di un’app di controllo che si è bloccata dopo un’operazione di lunga durata ha rivelato come causa una perdita di handle. Ma rafforzare i tronchi è solo metà del lavoro. Ciò che vuoi veramente è essere in grado di testare, in anticipo, se sei in uno stato in cui “puoi dire cosa è successo” se un errore di programmazione inaspettato causa mai una perdita di memoria, una perdita di handle, un fallimento parziale o un mancato rilascio in futuro.
È lì che abbiamo usato Application Verifier. È uno strumento che consente di inserire controlli di runtime e inserimento di errori nel codice in esecuzione nel codice nativo Windows e al limite Win32. Ciò che è particolarmente conveniente nella pratica è che è possibile attivare in anticipo modalità di guasto simili all’esaurimento della memoria e all’esaurimento delle risorse, senza effettivamente divorare la memoria della macchina.
In questa seconda parte, organizziamo cos’è Application Verifier, cosa può fare e come integrarlo in una base di test del percorso di guasto, nel contesto di un’app di controllo della telecamera industriale.
Sommario
- Prima la conclusione (in una riga)
- Cos’è Application Verifier?
- 2.1. In una frase
- 2.2. Dove splende
- 2.3. Cosa guadagni
- 2.4. Come ottenerlo e abilitarlo in locale
- Cosa può fare Application Verifier
- 3.1. Nozioni di base: maniglie / cumuli / serrature / memoria / TLS, ecc.
- 3.2. Simulazione di risorse limitate: caricamento iniziale della memoria ed esaurimento delle risorse
- 3.3. Pagina Heap e il Debugger
- 3.4.
!avrf/!htrace/ Registri
- Perché lo abbiamo introdotto questa volta
- 4.1. L’obiettivo non è solo “trovare bug”
- 4.2. Innescare fenomeni simili all’esaurimento della memoria
- 4.3. Verificare di poter tracciare le anomalie di gestione quando si verificano
- Come innescare fenomeni simili all’esaurimento della memoria e delle risorse
- 5.1. L’idea alla base della simulazione di risorse limitate
- 5.2. Cosa puoi far fallire
- 5.3. Come applicarlo nella pratica
- Come esaminare le anomalie di gestione
- 6.1. Il controllo
Handles - 6.2. Visualizzazione degli stack aperti / chiusi con
!htrace - 6.3. Come combinarlo con i tuoi registri
- 6.1. Il controllo
- Come costruire una base per il test del percorso di fallimento
- 7.1 Sposta l’unità di esecuzione in un’imbracatura
- 7.2 Dividere il menu Test
- 7.3 Cosa collezionare
- 7.4 Criteri di accettazione
- 7.5 Avvertenze
- Una guida decisionale approssimativa
- Riepilogo
- Riferimenti
1. Prima la conclusione (in una riga)
- Application Verifier è uno strumento che rende più facile individuare in fase di esecuzione l’uso improprio del limite non gestito / nativo di Windows
- Il suo valore non è solo quello di “trovare bug”, ma anche di costringere a verificarsi in anticipo percorsi di errore raramente riscontrati
Handlesrileva handle non validi,Heapsespone la corruzione di heap eLow Resource Simulationesegue l’inserimento di errori in situazioni simili all’esaurimento della memoria e all’esaurimento delle risorse- Delegare l’indagine sulle perdite di un residente di lunga durata EXE interamente a Application Verifier è un approccio errato; combinarlo con il tuo
Handle Counte i registri del ciclo di vita delle risorse è il percorso realistico - In una base di test del percorso di errore, è più semplice leggere i risultati se si esegue un’esecuzione del verificatore del percorso normale e un’esecuzione dell’inserimento di errori separatamente
- Anche quando vuoi testare un DLL, ciò che abiliti su Application Verifier è il test EXE che esercita effettivamente quel DLL
In breve, Application Verifier è uno strumento per trascinare allo scoperto i “brutti bug” che vivono attorno al confine nativo di Windows’/Win32. Si adatta particolarmente bene a mondi come le app di controllo delle apparecchiature, dove SDKs nativo, P/Invoke e Win32 API si mescolano abitualmente.
2. Cos’è Application Verifier?
2.1. In una frase
Application Verifier è uno strumento di verifica del runtime per applicazioni Windows in modalità utente. Monitora il modo in cui un’app in esecuzione utilizza OS API e gestisce le risorse, rilevando utilizzi sospetti e consentendoti di inserire deliberatamente errori.
A differenza dell’“analisi statica” o del “test unitario”, è uno strumento per vedere come si interrompono le cose quando il percorso del codice viene effettivamente esercitato. Ciò lo rende particolarmente adatto per individuare percorsi di guasto che i test funzionali di routine non raggiungono mai.
flowchart LR
A[Test harness] --> B[Control app / SDK wrapper]
B --> C[Application Verifier]
C --> D[Win32 API / native DLL / OS resources]
C --> E[verifier stop]
C --> F[debugger output]
C --> G[AppVerifier logs]
B --> H[Own structured log]
2.2. Dove splende
Tende ad essere particolarmente efficace in situazioni come queste.
- Chiami nativo DLLs o una fotocamera SDK
- Superi i limiti di P/Invoke o COM
- Utilizzi pesantemente handle, heap, blocchi e memoria virtuale, direttamente o indirettamente
- L’app raramente si arresta in modo anomalo nel percorso normale, ma la gestione della durata sembra fragile nei percorsi di errore
- “Occasionalmente restituisce strani errori” viene visualizzato prima del “crash”
Al contrario, non è uno strumento per tracciare i grafici degli oggetti nel mondo puramente gestito. Quindi, anche in un’app C#, si ripaga considerevolmente se il confine nativo SDK o Win32 è spesso, ma non è un unico strumento per indagare completamente sulle perdite di heap gestite pure.
2.3. Cosa guadagni
In pratica, i vantaggi si riducono grosso modo a questi tre.
- Interrompere tempestivamente l’uso improprio dei confini nativi
- Handle non validi
- heap corruzione
- bloccare l’uso improprio
- Utilizzo improprio dell’API della memoria virtuale, ecc.
- Modalità di errore del caricamento iniziale che compaiono solo con risorse scarse
- Gli equivalenti
mallococcasionalmente falliscono CreateEventeCreateFileoccasionalmente fallisconoVirtualAllocfallisce
- Gli equivalenti
- Tracciamento più semplice se combinato con un debugger
!avrf!htrace!heap -p -a- registri degli arresti del verificatore
Ciò che fa male nelle app di controllo delle apparecchiature è “non sapere cosa è successo nel percorso del guasto”. Application Verifier è piuttosto efficace nel ridurre il “non sapere”.
2.4. Come ottenerlo e abilitarlo in locale
Prima di tutto, mettiamo in ordine gli strumenti. Se questo passaggio manca, tutto il resto rimane teorico.
Application Verifier è incluso nel Windows SDK. Non è presente da solo su Windows, quindi eseguire l’installazione di Windows SDK e selezionare “Application Verifier” nella schermata di selezione delle funzionalità. L’eseguibile si chiama appverif.exe.
Ci sono 3 prerequisiti per l’uso.
- L’utente che esegue deve essere membro del gruppo Administrators del computer
- ARM64EC non è supportato
- L’oggetto della verifica deve essere codice non gestito (nativo)
La relazione tra GUI e riga di comando è la seguente.
| Cosa fa | |
|---|---|
GUI (appverif.exe) |
Scrive nel registro le impostazioni relative al nome dell’EXE target e ai test da abilitare |
Riga di comando (appverif -enable ...) |
Scrive le stesse impostazioni di registro tramite comando |
| In esecuzione | Quando l’EXE target si avvia, legge quelle impostazioni e carica le DLL del verifier, hookando le chiamate Win32 API |
Quindi in entrambi i casi si fa la stessa cosa. La GUI è comoda per la prima configurazione manuale; la riga di comando è adatta per CI e script.
L’operazione nella GUI è: fare clic con il tasto destro nel pannello Applications, scegliere “Add Application” per aggiungere l’EXE target, spuntare Basics nel pannello Tests e premere “Save”. Per rimuoverlo, fare clic con il tasto destro sull’applicazione e scegliere “Delete Application”, poi “Save”.
Da qui derivano 2 vincoli importanti.
- Non è possibile abilitare il verifier a un processo già in esecuzione. Poiché l’hook viene inserito al momento del caricamento della DLL, bisogna configurare prima e poi avviare.
- Le impostazioni rimangono finché non vengono cancellate esplicitamente. Se pensi di averlo abilitato “solo una volta” e lo lasci, quell’EXE continuerà a partire con il verifier su quella macchina.
I log di rilevamento vengono salvati per impostazione predefinita in %USERPROFILE%\AppVerifierLogs in formato binario; possono essere convertiti in XML dalla GUI o dalla riga di comando per l’analisi.
3. Cosa può fare Application Verifier
3.1. Nozioni di base: maniglie / cumuli / serrature / memoria / TLS, ecc.
Il set base di Application Verifier è Basics.
I controlli che utilizzi maggiormente nella pratica sono raccolti qui.
| Strato | Cosa guarda | Come si applica in questo contesto |
|---|---|---|
Handles |
Utilizzo di handle non validi | Se stai calpestando maniglie chiuse / danneggiate |
Heaps |
Heap corruzione | Eliminazione della corruzione del buffer e use-after-free al confine nativo SDK |
Leak |
Risorse non rilasciate al DLL scarico | Prove di imbracature di breve durata e casi che includono scarichi |
Locks / SRWLock |
Blocca l’uso improprio | Controllo delle corse tra riconnessione e spegnimento |
Memory |
Uso improprio di VirtualAlloc / MapViewOfFile, ecc. |
Controllo delle anomalie relative a buffer di grandi dimensioni e memoria condivisa |
TLS |
Uso improprio del thread locale Storage API | Assicurazione per codice nativo con limiti di thread complessi |
Threadpool |
Coerenza del pool di thread API e dello stato di lavoro | Backup quando le richiamate e l’elaborazione asincrona sono abbondanti |
Il punto è interrompere immediatamente l’utilizzo sospetto, anziché “leggerlo dopo l’incidente”. Per i difetti di lunga durata, questo caricamento anticipato ripaga notevolmente.
3.2. Simulazione di risorse limitate: caricamento iniziale della memoria ed esaurimento delle risorse
Questa è la parte veramente conveniente nella pratica. Questo perché puoi innescare fenomeni prossimi all’esaurimento della memoria e all’esaurimento delle risorse senza effettivamente divorare il RAM.
L’idea è semplice.
- Accetta una determinata chiamata API
- Con una certa probabilità
- Fallo fallire di proposito
Ciò consente di sperimentare percorsi di errore che praticamente non verrebbero mai presi altrimenti.
Concretamente diventa facile innescare di proposito fenomeni come questi.
HeapAlloceVirtualAllocfallisconoCreateFilefallisceCreateEventfallisceMapViewOfFilefallisce- Le allocazioni OLE / COM come
SysAllocStringfalliscono
Questo è molto più gestibile che cercare di esaurire veramente la memoria e torturare l’intera macchina. Inoltre, è possibile destinare l’inserimento di guasti solo a uno specifico DLLs. Per configurazioni come le app di controllo delle apparecchiature in cui i tuoi wrapper si mescolano con il fornitore SDKs, questo è abbastanza pratico.
3.3. Pagina Heap e il debugger
Per la corruzione di heap, la combinazione di Heaps e pagina heap è efficace.
La pagina intera heap in particolare ha il vantaggio di utilizzare le pagine di guardia per fermarsi in prossimità del momento della corruzione.
Tuttavia, è piuttosto pesante. Piuttosto che lunghe esecuzioni a forza bruta, è più utile restringere il campo agli scenari vicini alla riproduzione ed eseguirli nel debugger.
Quindi, come pratica operativa, una divisione come questa è realistica.
- Innanzitutto applicare
Basicsin modo ampio - Una volta che il heap sembra sospetto, utilizza la pagina intera heap
- Se è troppo pesante, torna alla pagina leggera heap
- Per test a lungo termine di tipo produttivo, affidati principalmente ai tuoi log
In definitiva, AppVerifier non è una bacchetta magica ma uno strumento di cui si scambia la lama in base alla situazione.
3.4. !avrf / !htrace / Registri
Application Verifier non si limita ad alzare uno stop e ad allontanarsi. Con le estensioni e i registri del debugger, ciò che è accaduto diventa più facile da rintracciare.
!avrf- Visualizza le impostazioni attuali del verificatore e la fermata attualmente sollevata
!htrace- Visualizza gli stack dei riferimenti aperti / chiusi / non validi di un handle
!heap -p -a- In combinazione con la pagina heap, traccia il blocco heap danneggiato
- AppVerifier registri
- È possibile conservare i registri per quando si verifica un arresto
È particolarmente gradito il fatto che l’abilitazione di Handles abiliti automaticamente la tracciatura degli handle.
Ciò rende molto più semplice risalire, a posteriori, “dove questa maniglia è stata aperta e dove è stata chiusa”.
4. Perché lo abbiamo introdotto questa volta
4.1. L’obiettivo non è solo “trovare bug”
Il nostro obiettivo questa volta non era semplicemente “trovare un bug con AppVerifier”. In termini più pratici, quello che volevamo verificare era quanto segue.
- Quando in futuro si verifica nuovamente una perdita di risorse su qualche altro percorso di errore
- I log manterranno correttamente il contesto?
- Possiamo seguirlo fino alla fine, insieme alle informazioni del debugger?
- Eviteremo di ritrovarci in uno stato di “nessuna idea di cosa sia successo”?
In altre parole, lo abbiamo utilizzato non solo come rivelatore, ma come test della nostra infrastruttura di osservazione.
4.2. Innescare fenomeni simili all’esaurimento della memoria
Causare effettivamente l’esaurimento della memoria su una normale macchina di sviluppo è piuttosto noioso. Peggio ancora, una volta che l’intera macchina diventa instabile, il test stesso si riempie di rumore.
Quindi abbiamo utilizzato la simulazione di risorse limitate per andare nella direzione di intraprendere deliberatamente i percorsi di fallimento che probabilmente l’esaurimento della memoria o delle risorse avrebbe innescato.
Ciò rende molto più semplice rispondere a domande come queste.
- Se
CreateEventfallisce,cameraIdephaserimangono nei log? - Dopo un’inizializzazione completata a metà, la pulizia viene effettivamente eseguita?
- Se
VirtualAllocfallisce, il nuovo tentativo evita di corrompere lo stato? - Se
CreateFilefallisce nel percorso di salvataggio, l’handle ritorna?
Ciò che vogliamo sottolineare è che causare l’anomalia non è l’obiettivo; l’obiettivo è che la modalità di errore sia leggibile quando si verifica l’anomalia.
4.3. Verificare di poter tracciare le anomalie di gestione quando si verificano
Come nel caso della perdita della maniglia nella Parte 1, con le maniglie il punto che alla fine si blocca e la vera causa si allontanano facilmente.
Quindi quello che volevamo confermare era questo.
- Quando viene sollevato un arresto della maniglia non valido, possiamo tracciare l’apertura / chiusura con
!htrace? - Si ricollega a
resourceId/sessionId/phasenei nostri registri? - Il conteggio degli handle diminuisce dopo l’errore?
- Quando il cablaggio è un processo di breve durata, i delta delle perdite sono facili da leggere?
Una volta che sei riuscito a vedere fin qui, puoi passare da un semplice “è apparso un bug” a “quale gestione della vita della responsabilità si è interrotta.”
5. Come innescare fenomeni simili all’esaurimento della memoria e delle risorse
5.1. L’idea alla base della simulazione di risorse limitate
La simulazione di risorse limitate è, in parole povere, iniezione di errori. Piuttosto che ricreare fedelmente un ambiente con risorse limitate, l’idea è di mescolare artificialmente i guasti API rappresentativi che si verificano con risorse limitate.
Quindi i suoi casi d’uso sono abbastanza chiari.
- Verifica della pulizia sui percorsi di errore
- Verifica della robustezza del tentativo / riconnessione
- Verifica dell’inizializzazione in cui si mescolano successi parziali e fallimenti parziali
- Verificare che i log rimangano anche per “guasti che normalmente non si verificano mai”
Il trucco qui è non fallire tutto dall’inizio. Se accendi tutto in una volta, i registri esplodono e perdi traccia di “cosa stai guardando”.
5.2. Cosa puoi far fallire
Con la simulazione di risorse limitate, è possibile fallire probabilisticamente le seguenti classi rappresentative di API.
| Classe | Esempi | Esempi in un’app di controllo delle apparecchiature |
|---|---|---|
Heap_Alloc |
Heap assegnazione | Buffer temporanei, metadati dell’immagine, allocazioni interne SDK-wrapper |
Virtual_Alloc |
Allocazione della memoria virtuale | Frame buffer più grandi, ring buffer |
File |
CreateFile, ecc. |
Apre i percorsi di salvataggio e i file di registro |
Event |
CreateEvent, ecc. |
Notifica frame-ready, sincronizzazione interruzione / riconnessione |
MapView |
CreateMapView, ecc. |
Memoria condivisa e file mappati in memoria |
Ole_Alloc |
SysAllocString, ecc. |
COM / OLE confine |
Wait |
WaitForXXX famiglia |
Intorno alla sincronizzazione attendere errori |
Registry |
Accesso al registro | Impostazioni di lettura / scrittura e configurazione driver adiacente |
In pratica, invece di aprire tutto in una volta, la chiave è iniziare in modo ristretto, con le classi più vicine al percorso di fallimento che si desidera esaminare in questo momento.
5.3. Come applicarlo nella pratica
Come schizzo da riga di comando, ad esempio, appare così.
appverif /verify CameraHarness.exe
appverif /verify CameraHarness.exe /faults
appverif -enable lowres -for CameraHarness.exe -with heap_alloc=20000 virtual_alloc=20000 file=20000 event=20000
appverif -query lowres -for CameraHarness.exe
Anche se copi e incolli, se non sai cosa fa ogni riga non serve a nulla, quindi riassumiamo riga per riga.
| Comando | Cosa fa |
|---|---|
appverif /verify CameraHarness.exe |
Abilita il gruppo di test Basics per CameraHarness.exe |
appverif /verify CameraHarness.exe /faults |
Aggiunge a quanto sopra l’iniezione di fault. Tuttavia i tipi interessati sono solo OLE_ALLOC e HEAP_ALLOC |
appverif -enable lowres -for CameraHarness.exe -with heap_alloc=20000 ... |
Abilita lowres (Low Resource Simulation) e specifica singolarmente il tipo di API da far fallire e la probabilità |
appverif -query lowres -for CameraHarness.exe |
Mostra quali impostazioni sono attive e con quali probabilità |
appverif /n CameraHarness.exe |
Cancella le impostazioni per quel EXE (stesso scopo di -disable * -for o -delete settings -for) |
Impariamo a leggere gli argomenti.
- La probabilità è in parti per milione. Il valore specificabile è un intero da 0 a 1.000.000;
20000significa20000 / 1.000.000, cioè 2%. Non è “1 volta su 20.000”. Anche la documentazione Microsoft riporta come esempio-with registry=20000 file=20000per far fallire le API di registro e file al 2%. - Dopo
/faultssi possono elencare probabilità, tempo di attesa e nomi di DLL. Il formato è/faults [probabilità [attesa_ms [DLL ...]]]. Se si omette la probabilità diventa 5%, se si omette l’attesa diventa 500 ms. L’attesa è il tempo durante il quale, dall’avvio del processo, non vengono iniettati fault; serve a evitare che la fase di avvio stessa fallisca e non si possa provare nulla. /nè la disattivazione. Basta pensarci come “no verifier”. Serve a non lasciare le impostazioni attive.
L’output di appverif -query lowres è più o meno questo. Qui puoi controllare se le probabilità impostate sono presenti e se hai accidentalmente aperto tipi di fault che non volevi.
Settings for CameraHarness.exe:
Test [lowres] enabled.
Include = *
Exclude =
TimeOut = 2000 (0x7D0)
WAIT = 0 (0x0)
HEAP_ALLOC = 20000 (0x4E20)
VIRTUAL_ALLOC = 0 (0x0)
REGISTRY = 0 (0x0)
FILE = 20000 (0x4E20)
EVENT = 20000 (0x4E20)
MAP_VIEW = 0 (0x0)
OLE_ALLOC = 0 (0x0)
STACKS = false
Include e Exclude indicano i moduli bersaglio; TimeOut è il tempo iniziale durante il quale non vengono iniettati fault. Il valore predefinito cambia a seconda della configurazione, quindi non dare per scontato nulla: verifica con questo output.
L’approccio è questo.
- Per prima cosa esegui il percorso normale solo con
Basics - Quindi aggiungere
Low Resource Simulationed eseguire con l’inserimento di errori - Se necessario, assegna le probabilità solo ai guasti che desideri visualizzare, ad esempio
fileoevent - Se si desidera scegliere come target un DLL specifico, definire l’ambito dell’iniezione a quel DLL
La scorciatoia /faults è comoda, ma da sola è centrata su OLE_ALLOC e HEAP_ALLOC.
Se vuoi esaminare i percorsi di errore di CreateFile o CreateEvent, è più affidabile scrivere -enable lowres -with file=... event=....
Nelle app di controllo delle apparecchiature, spesso è più facile leggere i risultati quando si esamina il wrapper della fotocamera o il percorso di salvataggio DLL, piuttosto che sparpagliare gli errori nell’intera app.
Ecco la scrittura concreta per “limitare a una DLL”. A partire dal terzo argomento di /faults si specificano i moduli di destinazione.
appverif /verify CameraHarness.exe /faults 50000 1000 CameraSdkWrapper.dll
In questo modo, quando avvii CameraHarness.exe, dopo 1000 ms dall’avvio, solo le operazioni che iniziano da CameraSdkWrapper.dll vengono fatte fallire al 5% (50000 / 1.000.000). Scrivi il nome del modulo con estensione, senza percorso. Oltre a .dll puoi indicare anche moduli come .ocx che vengono caricati.
Per verificare se l’ambito è effettivamente ristretto, guarda le righe Include e Exclude dell’output di appverif -query lowres -for CameraHarness.exe. Se Include rimane *, il processo intero è ancora il bersaglio.
Se lo stai eseguendo sotto il debugger, puoi cambiare l’ambito anche a metà.
!avrf -trg dll CameraSdkWrapper.dll
!avrf -skp dll VendorSdk.dll
-trg indica “prendi di mira qui”; -skp indica “salta qui”. Puoi anche usare !avrf -flt per controllare le impostazioni correnti di fault injection, oppure !avrf -flt stacks 10 per vedere gli stack delle ultime injection.
Nelle app di controllo delle apparecchiature, spesso è più facile leggere i risultati quando si esamina il wrapper della fotocamera o il percorso di salvataggio DLL, piuttosto che sparpagliare gli errori nell’intera app.
Ad esempio, puoi creare scenari come questi.
- Errore
CreateEventsubito dopo l’avvio di una riconnessione CreateFileerrore all’inizio del salvataggio- Errore temporaneo nell’allocazione del buffer
- Errore
SysAllocStringdurante la conversione COM - Verifica dei percorsi di fallimento dell’attesa API
Questi valori non vengono praticamente mai raggiunti con i soli test di routine del percorso normale. Questo è esattamente il motivo per cui vale la pena calpestarli deliberatamente.
6. Come esaminare le anomalie di gestione
6.1. Il controllo Handles
Per tutto ciò che riguarda l’handle, inizia con Handles.
Ciò semplifica il rilevamento dell’utilizzo di handle non validi.
Gli incidenti che tipicamente rileva sono questi.
- Utilizzare nuovamente la maniglia dopo averla chiusa
- Passaggio di un valore handle danneggiato
- Utilizzo di un handle non inizializzato a causa di un errore parziale
- Una vita interrotta che porta all’accesso da un altro thread
Laddove un’operazione a lungo termine mostrerebbe solo “uno strano errore appare occasionalmente”, sotto il verificatore può interrompersi immediatamente. Questo caricamento frontale aiuta molto.
6.2. Visualizzazione degli stack aperti / chiusi con !htrace
Ciò che rende Handles così gradito è che si abbina bene al tracciamento degli handle.
windbg -xd av -xd ch -xd sov CameraHarness.exe
!avrf
!htrace 0x00000ABC
Quello che vuoi vedere con !htrace è più o meno questo.
- Dove è stata aperta la maniglia
- Dove era chiuso
- Se è stato indicato come handle non valido
- Se le aperture si stanno accumulando più del previsto
Ciò che rende problematica la gestione delle perdite e degli usi impropri è che l’API che alla fine si è bloccata non è la vera causa.
Con !htrace puoi tracciare la storia di quella maniglia in modo abbastanza concreto.
6.3. Come combinarlo con i tuoi registri
Detto questo, Application Verifier da solo non è sufficiente. In particolare, condurre l’indagine sulla perdita di un residente di lunga durata EXE solo con esso è piuttosto doloroso.
Quindi in pratica combiniamo quanto segue.
- Periodico
Handle Count sessionIdresourceIdphase- Registri del ciclo di vita di creazione / apertura e chiusura / eliminazione
- Dump e output del debugger agli arresti del verificatore
Con questo puoi risolvere il problema in questo modo, ad esempio.
- Il battito cardiaco mostra che la pendenza di
Handle Countè sospetta - I log del ciclo di vita restringono la risorsa che ha un
Createma nessunClose - Un’esecuzione di verifica fa emergere in anticipo l’handle non valido o l’uso improprio
!htracemostra gli stack di apertura / chiusura
Questa combinazione rende le cose notevolmente più facili da inseguire.
7. Come costruire una base per il test del percorso di fallimento
7.1. Sposta l’unità di esecuzione in un’imbracatura
Application Verifier non può essere abilitato retroattivamente su un processo già in esecuzione. Prima si configura, poi si avvia.
Inoltre, le impostazioni persistono finché non le rimuovi esplicitamente. Quindi, in pratica, è più semplice da gestire se si sceglie come target un test cablaggio EXE anziché l’app di produzione stessa.
Ad esempio, una configurazione come questa.
flowchart LR
A[Scenario Runner] --> B[CameraHarness.exe]
B --> C[CameraSdkWrapper.dll]
C --> D[Vendor SDK]
B --> E[Structured Log]
B --> F[Dump / Debugger]
Con questo avrai i vantaggi di:
- Esecuzione di uno scenario per processo
- I delta delle perdite sono facili da leggere
- Facile commutazione delle impostazioni AppVerifier ON / OFF
- Essere in grado di testare DLLs attraverso il lato EXE
I comandi assomigliano a questo.
appverif /verify CameraHarness.exe
appverif /n CameraHarness.exe
Abilita prima del lancio; disabilitare esplicitamente. L’esecuzione di questa operazione con un’imbracatura come premessa aiuta anche a prevenire incidenti di configurazione.
7.2. Dividere il menu Test
In una fondazione di test del percorso di fallimento, è meglio non fare tutto in una volta. La suddivisione in queste tre tracce mantiene le cose leggibili.
- Percorso normale + Nozioni di base
- Non iniettare errori
- Confermare che non si verifichino interruzioni del verificatore
- Traccia di inserimento guasti
Low Resource Simulation- Individuazione dei guasti in
event/file/heap_alloc/virtual_alloc, ecc.
- Heap percorso di immersione profonda
Heaps- pagina intera heap
- Riproduci localmente nel debugger
Dividere questi forti “si rompe durante il normale utilizzo” e “si rompe solo con risorse scarse” perché si aggroviglia.
In particolare, la presenza o l’assenza dell’inserimento di guasti modifica notevolmente i percorsi del codice intrapresi. Quindi dovresti eseguire sia la esecuzione senza errori che la esecuzione con errori.
7.3. Cosa collezionare
Come minimo, vuoi catturarli.
| Categoria | Quello che vuoi |
|---|---|
| Registri dell’app | cameraId, sessionId, phase, handleCount, error code |
| Stato del processo | Handle Count, Private Bytes, Thread Count |
| Informazioni sul debugger | !avrf, !htrace e !heap -p -a secondo necessità |
| Discariche | All’arresto del verificatore o alla conclusione anomala |
| AppVerifier registri | Record delle fermate, esportati in XML per l’aggregazione, se necessario |
Se necessario, i log lato AppVerifier possono anche essere esportati in XML e aggregati. Ma la causa raramente si chiude da soli, quindi la premessa pratica è leggerli fianco a fianco con i propri registri.
Un grande volume di tronchi non è di per sé una virtù. Ciò che conta è che la causalità possa essere collegata in seguito.
7.4. Criteri di accettazione
Anche “non si è schiantato” è troppo debole come criterio di accettazione. In questo contesto, avevamo bisogno almeno di quanto segue.
- Nessun verificatore si ferma nel percorso normale + Esecuzione delle nozioni di base
- Anche con l’inserimento dei guasti, i guasti previsti rimangono nei registri
- Le risorse parzialmente inizializzate vengono ripulite correttamente
- Dopo la riconnessione / riprova,
Handle Countritorna vicino alla linea di base - Quando si verifica un arresto del verificatore, è possibile tracciarlo tramite
sessionId/phase/ stack - Nessun fallimento finisce con “nessuna idea di cosa sia successo”
Ciò che conta qui è valutare non rompersi e essere rintracciabili in caso di rottura come cose separate.
7.5. Avvertenze
Application Verifier è abbastanza conveniente, ma non è magico.
- I percorsi di codice non effettivamente esercitati non vengono verificati
- La pagina intera heap è pesante
- I fermi possono avvenire anche all’interno di soggetti terzi SDKs
- I percorsi del codice adottati differiscono notevolmente con e senza Fault Injection
- Non è un unico strumento per indagare sulle perdite heap gestite pure
Quindi la sua posizione è questa.
- Piste di lunga durata: i tuoi registri e contatori
- Uso improprio dei confini nativi: Application Verifier
- Ricostruzione della causalità in caso di errore: log strutturati + dump + debugger
Questa divisione del lavoro è la più pratica.
8. Una guida decisionale approssimativa
- Si sospettano maniglie non valide o doppie chiusure
Handles+!htrace
- Si sospetta corruzione di Heap / use-after-free
Heaps+ pagina intera heap +!heap -p -a
- Vuoi innescare fenomeni simili all’esaurimento della memoria o delle risorse
Low Resource Simulation
- Le cose si rompono gradualmente in caso di funzionamento prolungato
- Inizia con i tuoi registri
Handle Count/Private Bytes/ del ciclo di vita
- Inizia con i tuoi registri
- Vuoi provare un DLL
- Abilitare Application Verifier sul cablaggio EXE che lo chiama DLL
Accendere tutto dall’inizio di solito produce solo una nebbia di tronchi. Applicare la lama più vicina al percorso di guasto che si desidera vedere è molto più chiaro.
9. Riepilogo
La posizione di Application Verifier è quella di un verificatore di runtime per il limite nativo / Win32 di Windows. Utilizzando handle / heap / blocchi / memoria / TLS / simulazione di risorse limitate e il resto, è possibile forzare l’esercizio in anticipo di percorsi di errore raramente riscontrati.
Ciò che ha dato i suoi frutti in questo contesto è stato che le anomalie di gestione sono diventate facili da rintracciare con !htrace quando si sono verificate, che fenomeni simili all’esaurimento della memoria e delle risorse potevano essere attivati senza distruggere l’intera macchina e che potevamo confermare se i nostri log sarebbero stati davvero utili in quel momento.Per quanto riguarda come eseguirlo in pratica: dividere il percorso normale + l’esecuzione di base dalle esecuzioni di iniezione dei guasti, preparare un cablaggio EXE e scorrere gli scenari attraverso processi di breve durata. Oltre a ciò, combinalo con i tuoi log, dump e informazioni del debugger, mentre osservi la pendenza delle perdite a lungo termine con i tuoi contatori: questa è la divisione del lavoro.
Application Verifier è uno strumento per “andare incontro” a rare anomalie, invece di “aspettare” che si verifichino.
Nelle app di controllo delle apparecchiature, non è importante rompere le cose, ma essere in grado di spiegare cosa è successo quando le cose si rompono è altrettanto importante. In questo senso, riteniamo che sia uno strumento assolutamente pratico.
10. Riferimenti
- Parte 1: Quando un’app di controllo della telecamera industriale si blocca improvvisamente dopo un mese (Parte 1) - Individuazione di perdite di gestione e progettazione di registri per operazioni di lunga durata
- Application Verifier - Panoramica
- Application Verifier - Test delle applicazioni
- Application Verifier - Test entro Application Verifier
- Application Verifier - Il debug Application Verifier si interrompe
- Application Verifier - Caratteristiche
- !htrace (WinDbg)
- Funzione GetProcessHandleCount (processthreadsapi.h)
Articoli correlati
Articoli recenti con gli stessi tag per approfondire argomenti vicini.
Indagine sugli arresti anomali a lungo termine di un'app per fotocamere industriali: la perdita dell'handle (parte 1)
Come esaminare un'app Windows che si arresta improvvisamente in modo anomalo dopo un funzionamento prolungato, utilizzando un caso di stu...
Perché le ritrasmissioni TCP bloccano la comunicazione con telecamere industriali, e come isolarle
Come isolare la causa quando la comunicazione con telecamere industriali si blocca per diversi secondi a causa di ritrasmissioni TCP, cop...
Icone nella system tray e notifiche toast nelle app Windows — le insidie di NotifyIcon e come scegliere l'AppNotification giusta
Una guida pratica per mantenere un'applicazione Windows aziendale residente nella system tray (area di notifica) e avvisare l'utente tram...
Fino a quando funzioneranno le applicazioni VB6? — Lo stato del supporto al runtime e un percorso pratico verso la migrazione a .NET
Fino a quando continueranno a funzionare le applicazioni VB6? Questo articolo chiarisce l'asimmetria tra la politica di supporto del runt...
Era giapponese, festività e date di chiusura nelle applicazioni aziendali — progettazione resiliente ai cambi di era, JapaneseCalendar e calcolo dei giorni lavorativi nella pratica
Mostrare «Reiwa 8» su un documento, calcolare i giorni lavorativi escludendo le festività, pagare entro la fine del mese successivo dopo ...
Argomenti correlati
Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.
Argomenti tecnici Windows
Portale su sviluppo Windows, analisi dei problemi e valorizzazione delle risorse esistenti.
Analisi dei bug e guasti di lunga durata
Errori intermittenti, diagnosi delle comunicazioni, crash prolungati e test dei percorsi di errore.
Casi di studio correlati
Questi casi mostrano un approccio simile all’analisi, alla definizione delle priorità o alla riprogettazione.
Infrastruttura per testare i percorsi di errore con Application Verifier
Caso di studio sulla creazione di una base per i test dei percorsi di errore, pensata per rendere più semplici le indagini future.
Servizi collegati all’argomento
L’articolo è direttamente collegato ai servizi seguenti.
Analisi dei bug e delle cause
Application Verifier e i fondamenti del test del percorso di errore sono un tema centrale del nostro servizio di indagine dei bug e di analisi delle cause principali, che promuove la riproduzione degli errori e l'identificazione delle cause.
Consulenza tecnica e revisione del progetto
Se desideri stabilire in che misura i test del percorso di guasto e i punti di osservazione dovrebbero essere intrecciati nel tuo progetto, questo può essere esplorato come un impegno di consulenza tecnica e revisione del progetto.
Domande frequenti
Domande che ricorrono nelle consulenze sull’argomento dell’articolo.
- A cosa serve Application Verifier?
- Application Verifier è uno strumento di verifica del runtime per le applicazioni Windows in modalità utente. Monitora il modo in cui un'app in esecuzione utilizza OS API e gestisce le risorse, rileva utilizzi sospetti come handle non validi, danneggiamento di heap e blocco di usi impropri e consente di inserire deliberatamente errori. Il suo valore non è solo quello di individuare bug, ma anche di forzare in anticipo percorsi di errore raramente riscontrati, il che lo rende particolarmente efficace per le app che chiamano SDKs nativo, attraversano i limiti P/Invoke o COM o utilizzano handle e heap in modo massiccio.
- Come posso simulare l'esaurimento della memoria o delle risorse senza esaurire la macchina?
- Utilizza la Low Resource Simulation di Application Verifier, che è un'iniezione di errore: fa sì che le chiamate API rappresentative falliscano con una probabilità configurata invece di divorare veramente RAM. È possibile fare in modo che le allocazioni HeapAlloc, VirtualAlloc, CreateFile, CreateEvent, MapViewOfFile e OLE falliscano di proposito e puoi anche definire l'ambito dell'iniezione su DLLs specifico. Ciò esercita percorsi di errore che i test di percorso normale di routine praticamente non raggiungono mai, senza rendere instabile l'intera macchina.
- Come posso risalire alla provenienza di un handle trapelato o non valido?
- Abilita il controllo Handles, che abilita automaticamente la traccia degli handle, quindi utilizza l'estensione del debugger !htrace per visualizzare gli stack in cui un handle è stato aperto, chiuso o referenziato come non valido. Questo è importante perché in caso di problemi di gestione, l'API che alla fine si è bloccata non è solitamente la vera causa. Combinalo con i tuoi log strutturati contenenti sessionId, ResourceId e Phase in modo che sia possibile ricollegare un arresto del verificatore al quale è stata interrotta la gestione della durata della responsabilità.
- Application Verifier riesce a trovare perdite di memoria gestite .NET?
- No, non è uno strumento per tracciare i grafici degli oggetti nel mondo puramente gestito e delegare interamente ad esso l'indagine sulle perdite di un EXE residente di lunga durata è un approccio errato. La divisione realistica del lavoro: osserva le pendenze a lungo termine con i tuoi contatori Handle Count e i log del ciclo di vita delle risorse, usa Application Verifier per usi impropri al confine nativo / Win32 e ricostruisci la causalità in caso di errore con log strutturati, dump e output del debugger. Anche in un'app C# ripaga notevolmente quando il confine nativo SDK o Win32 è spesso.
Profilo dell’autore
Pagina di presentazione dell’autore dell’articolo.
Go Komura
Rappresentante di KomuraSoft LLC
Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.