Condividi tramite


Debug di installazioni di dispositivi con il debugger del kernel (KD)

A partire da Windows Vista, quando il gestore Plug and Play (PnP) rileva un nuovo dispositivo nel sistema, il sistema operativo avvia il processo host di installazione del dispositivo (DrvInst.exe) per cercare e installare un driver per il dispositivo.

Poiché l'installazione del dispositivo si verifica all'interno di questo processo in modalità utente, in genere è più semplice usare un debugger in modalità utente, come descritto in Debug di installazioni di dispositivi con un debugger in modalità utente. In alcuni casi, tuttavia, potrebbe essere utile usare il debugger del kernel (KD) per monitorare il processo di installazione del dispositivo in modalità utente.

Ad esempio, usando KD durante il debug dell'installazione del dispositivo in modalità utente, è possibile eseguire le operazioni seguenti:

  • Eseguire simultaneamente il debug di un problema in modalità kernel usando !devnode, !devobj, !drvobj, !irp e altre estensioni KD.

  • Monitorare altri processi in modalità utente senza gestire più debugger usando le estensioni KD !process o .process /p.

Per altre informazioni su KD e altri strumenti di debug, vedere Debug di Windows.

Il valore del Registro di sistema DebugInstall specifica il tipo di supporto per il debug dell'installazione del dispositivo abilitato nel sistema. Per altre informazioni su questo valore del Registro di sistema, vedere Abilitazione del supporto per il debug delle installazioni dei dispositivi.

Quando il valore del Registro di sistema DebugInstall è impostato su 1, DrvInst.exe verificherà prima che il debugger del kernel sia abilitato e attualmente collegato prima che si interrompa nel debugger. Dopo aver apportato questa interruzione, i punti di interruzione possono essere impostati nei moduli in modalità utente del processo corrente. Ad esempio:

kd> .reload /user
kd> bp /p @$proc setupapi!SetupDiCallClassInstaller

In questo modo viene impostato un punto di interruzione nella routine SETUPAPI. SetupDiCallClassInstaller solo per il processo corrente.

Per lo sviluppatore di un pacchetto driver, in genere è preferibile eseguire il debug delle azioni di un programma di installazione di classe o di una DLL con programma di installazione condivisa durante l'installazione di un dispositivo. Tuttavia, quando DrvInst.exe si interrompe nel debugger, qualsiasi programma di installazione di classe o DLL di co-programma di installazione dal pacchetto driver non sarà stato caricato. Anche se i debugger in modalità utente supportano la possibilità di impostare un'eccezione del debugger quando un modulo in modalità utente viene caricato nel processo con il comando "sx e ld", il debugger del kernel supporta solo i moduli in modalità kernel con tale comando.

Nell'esempio di codice seguente viene illustrato come un "Programma di comando debugger" monitora il caricamento di un programma di installazione di classe specifico o di un programma di installazione condivisa nel processo corrente. In questo esempio, il programma di comando del debugger imposterà un punto di interruzione nel punto di ingresso principale (CoInstallerProc) del programma di installazione Mycoinst.dll :

file: Z:\bpcoinst.txt

r $t1 = 0
!for_each_module .if ($spat("@#ModuleName", "mycoinst*") = 0) {r $t1 = 1}
.if (not @$t1 = 0) {.echo mycoinst is loaded, set bp on mycoinst!CoInstallerProc } .else {.echo mycoinst not loaded}
.if (not @$t1 = 0) {.reload mycoinst.dll}
.if (not @$t1 = 0) {bp[0x20] /p @$proc mycoinst!CoInstallerProc } .else {bc[0x20]}

Quando viene eseguito, il programma di comando del debugger controlla l'elenco di moduli caricati nel processo corrente per Mycoinst.dll. Dopo il caricamento di questa DLL con coinstallazione, il debugger imposterà un punto di interruzione (con un ID punto di interruzione noto) nella funzione del punto di ingresso CoInstallerProc.

A partire dall'interruzione di debug avviata dal processo host DrvInst.exe , è prima necessario impostare un punto di interruzione sull'indirizzo restituito della chiamata in cui DrvInst.exe si è interrotto nel debugger del kernel. Questo punto di interruzione cancella tutti i punti di interruzione impostati durante l'installazione del dispositivo e continuerà l'esecuzione:

DRVINST.EXE: Entering debugger during PnP device installation.
Device instance = "X\Y\Z" ...

Break instruction exception - code 80000003 (first chance)
010117b7 cc               int     3

kd> bp[0x13] /p @$proc @$ra "bc *;g"

Successivamente, è necessario impostare alcuni punti di interruzione all'interno del processo per consentire l'esecuzione dei comandi nel programma di comando del debugger al momento appropriato durante l'installazione del dispositivo.

Per assicurarsi che il punto di interruzione per il programma di installazione della classe o il punto di ingresso dll del programma di installazione condivisa sia impostato prima che venga richiamata la funzione per l'installazione del dispositivo, il programma di comando del debugger deve essere eseguito ogni volta che viene caricata una nuova DLL nel processo corrente, ovvero dopo una chiamata a LoadLibraryExW restituisce:

kd> .reload
kd> bp[0x10] /p @$proc kernel32!LoadLibraryExW "gu;$$><Z:\\bpcoinst.txt;g"

Invece di eseguire il programma in ogni chiamata LoadLibraryEx all'interno del processo (bp[0x10]), lo sviluppatore può limitarlo all'esecuzione solo quando le DLL del programma di installazione di classe e del co-programma di installazione vengono caricate nel processo. Poiché SetupDiCallClassInstaller è la routine che richiama i programmi di installazione della classe e i coinstallazioni registrati per un dispositivo, queste DLL verranno caricate nel processo durante tale chiamata.

Poiché non è necessario fare ipotesi su quando queste DLL verranno scaricate dal processo host DrvInst.exe , è necessario assicurarsi che i punti di interruzione possano gestire l'individuazione dei punti di ingresso della DLL durante tutte le chiamate effettuate a SetupDiCallClassInstaller dal processo host DrvInst.exe .

kd> bd[0x10]
kd> bp[0x11] /p @$proc setupapi!SetupDiCallClassInstaller "be[0x10];bp[0x12] /p @$proc @$ra \"bd[0x10];bc[0x12];g\";g"
kd> g

Il punto di interruzione per eseguire il programma di comando del debugger (bp[0x10]) è inizialmente disabilitato. Viene abilitato ogni volta che viene richiamato SetupDiCallClassInstaller (bp[0x11]) e l'esecuzione continua. Il programma di comando del debugger (bp[0x10]) viene nuovamente disabilitato quando SetupDiCallClassInstaller restituisce impostando un punto di interruzione sull'indirizzo restituito della routine stessa (bp[0x12]).

Tenere presente che il punto di interruzione che disabilita il programma di comando del debugger cancella anche se stesso e continua l'esecuzione fino a quando SetupDiCallClassInstaller viene chiamato nuovamente o fino al completamento del programma di installazione e tutti i punti di interruzione vengono cancellati (bp[0x13]).

Quando l'esecuzione inizia dopo l'impostazione dei punti di interruzione precedenti, il processo verrà interrotto in ogni chiamata a mycoinst. CoInstallerProc. In questo modo è possibile eseguire il debug dell'esecuzione del programma di installazione della classe o della DLL del programma di installazione condivisa durante l'installazione del dispositivo principale.

Il periodo di tempo predefinito per il completamento di un processo di installazione è di 5 minuti. Se il processo non viene completato entro il periodo di tempo specificato, il sistema presuppone che il processo abbia interrotto la risposta e venga terminato.

La restrizione di timeout predefinita applicata alle installazioni dei dispositivi è ancora attiva mentre il processo viene sottoposto a debug tramite il debugger del kernel. Poiché l'esecuzione di tutti i programmi nel sistema viene arrestata durante l'interruzione nel debugger, la quantità di tempo impiegato dal processo di installazione viene rilevata come in un sistema che non viene sottoposto a debug.