PDA

View Full Version : [C++/Win] Perdita memoria Non paged (difficile, sorry)


sottovento
08-08-2007, 16:32
Ciao a tutti
ho un quesito piuttosto difficile: mi e' stato chiesto di trovare dove un programma (scritto meta' in C++ e meta' in Fortran) perde memoria.

Da Windows Task Manager ho visto che non e' il solito memory leak, ma la memoria persa si vede sotto la colonna "NP Pool". Insomma, sembra che qualche chiamata a sistema sia sbagliata.
Il software da analizzare e' piuttosto grosso. Ho quindi installato il Windows Support Tool per avere a disposizione il comando PoolMon, in modo di sapere il tag della memoria persa.

Quindi... installazione OK, eseguo il comando gflags per abilitare la raccolta delle informazioni sui pool, riavvio il sistema e.... il problema sparisce!!!
Faccio un'altra prova: disabilito gflags ed ecco che il problema compare ancora. (ovviamente poolmon e' fuori gioco, se disabilito).

Altre prove: installo lo stesso software e gli stessi tool sotto altre macchine con Win XP, Win 2000 e Win 2003. Il risultato non cambia.

C'e' qualche esperto di Windows, o in alternativa un esperto in divinazioni, che sappia dirmi cosa succede?
Oltretutto e' anche difficile googlare/yahoo!are su un problema di questo genere...

71104
08-08-2007, 19:56
scusa ma la metà scritta in C++ è un driver...? :wtf:
perché il pool non paginato in Windows lo si può controllare solamente da kernel mode. d'altra parte un driver WDM scritto in C++ mi pare quantomeno originale... ^_^'

sottovento
09-08-2007, 06:32
scusa ma la metà scritta in C++ è un driver...? :wtf:
perché il pool non paginato in Windows lo si può controllare solamente da kernel mode. d'altra parte un driver WDM scritto in C++ mi pare quantomeno originale... ^_^'
Ovviamente hai ragione. Non si tratta, pero', di un driver.
Non sono esperto di Win32, ma posso ben immaginare che le chiamate di sistema (fatte quindi a livello utente) possano essere eseguite in kernel mode.

Pertanto posso immaginare che ci sia del software di sistema o driver che funzioni correttamente, ma che sia chiamato da un programma che non sia corretto.
L'esempio che mi viene fatto e', per esempio, di una chiamata a CreateFile(): se sbaglio la chiamata potrei avere un problema di questo genere.

La cosa che pero' risulta singolare e' che se abilito l'uso di poolmon, tutto sparisce ed il programma funziona alla perfezione. Se, sempre usando gflags, tolgo il flag relativo alla raccolta delle informazioni sulla memoria "taggata", dopo 2/3 giorni di funzionamento, devo riavviare la macchina.....

EDIT - c'e' anche una parte scritta in C, che implementa una struttura IPC: la mailbox. Per fare questo, utilizza una mutex ed un'area di memoria shared, oltre che qualche semaforo.
Siccome e' una parte ben delimitata e scritta in una DLL che risulta piuttosto generica, l'ho provata a parte, scrivendo un paio di processi che si scambiassero messaggi a raffica. Funziona correttamente, anche se non l'ho "scagionata" del tutto, non essendo riuscito a provare tutte le possibili condizioni....

71104
09-08-2007, 07:17
mi sa che sei a un passo dall'aver trovato un bug di Windows, che contrariamente a quanto si pensi è una cosa piuttosto difficile :sofico:

a meno che non sia colpa ancora una volta delle famigerate terze parti. in generale comunque un'applicazione user-mode non deve mai avere nessun modo di causare un leak nel pool non paginato.

di preciso quali flags hai abilitato? o meglio ancora, quale/i flag/s è sufficiente abilitare per far sparire il leak?

sottovento
09-08-2007, 07:30
mi sa che sei a un passo dall'aver trovato un bug di Windows, che contrariamente a quanto si pensi è una cosa piuttosto difficile :sofico:

a meno che non sia colpa ancora una volta delle famigerate terze parti. in generale comunque un'applicazione user-mode non deve mai avere nessun modo di causare un leak nel pool non paginato.

di preciso quali flags hai abilitato? o meglio ancora, quale/i flag/s è sufficiente abilitare per far sparire il leak?

Beh, non penso di aver trovato un bug. Penso di aver trovato un pessimo lavoro da completare :cry:
Ovviamente non si sa mai, ma non penso che un'applicazione user-mode non possa creare dei leak nel pool non paginato! Nel TaskManager, infatti, puoi selezionare la colonna "NP Pool" e vedere come e' in uso da tutte le applicazioni, piu' o meno. Ecco, e' quel numero che continua a crescere.

Il flag che ho abilitato (usando gflags, mi sento piu' sicuro che scrivere nel registry) e' "Enable pool tagging". Questo e' l'unico.
Il risultato e' ormai noto: se abilito per avere informazioni sul problema, il problema sparisce. Viceversa, se disabilito "Enable pool tagging" e quindi non posso raccogliere informazioni sul problema, il problema appare ancora.

Penso che l'unico modo di stabilire qual e' la chiamata galeotta e' quella di eseguire un crash di Windows ed andare ad analizzarlo con un kernel debugger, per esempio windbg o kd.
Ovviamente preferirei 20 frustate.....

71104
09-08-2007, 07:36
non è così che ragionerei io infatti: bisogna piuttosto cercare di capire che modo ha un componente kernel mode di "capire" (anche involontariamente) se il tagging è abilitato o meno, e di cambiare il suo comportamento di conseguenza.

sottovento
09-08-2007, 07:54
non è così che ragionerei io infatti: bisogna piuttosto cercare di capire che modo ha un componente kernel mode di "capire" (anche involontariamente) se il tagging è abilitato o meno, e di cambiare il suo comportamento di conseguenza.

Capisco il tuo modo di ragionare, ma il mio punto di partenza e' diverso: fermo restando il fatto che ogni pezzo di software puo' essere affetto da bug, sono propenso a pensare che le probabilita' di failure siano piu' alte nel software che ho in esame piuttosto che in Windows, pur non potendolo escludere.

Hai parlato che potrebbe essere un bug di Win: bene, e' sicuramente ragionevole.
Pero' questo mi pone un po' di problemi:
1 - non riesco a trovare documentazione via Internet sullo stesso, poiche' mi e' difficile da descrivere in una query di ricerca. Non penso che un bug di questo genere non sia stato "visto" prima...
2 - Nel caso, invece, che sia davvero un bug di Win e che nessuno l'abbia visto prima, vorrei essere sicuro e non gridare "al lupo" immediatamente.
Quindi, dopo la condanna in primo grado (questa), dovrei cercare di avere quanto meno un processo d'Appello e magari andare in Cassazione.

Proprio mentre ti sto scrivendo, mi e' venuto in mente che potrei provare ad avere il processo d'Appello utilizzando perfmon! In effetti, Performance Monitor mi rileva questo parametro, giusto? Anzi, se non ricordo male mi riporta il numero di allocazioni ed il totale dei byte allocati.
D'accordo, non e' tanto ma comincio ad avere piu' info a disposizione.

A questo punto, mi viene spontanea una domanda, ancor prima di cominciare: se davvero fosse un bug di Windows, quale sara' il risultato dell'analisi con perfmon? variera' in accordo con quanto settato in gflags?
E se per caso questo non succedesse?

Beh, ti devo ringraziare per la discussione, visto che sta portando a considerare aspetti che prima non mi passavano minimamente per la testa...

71104
09-08-2007, 07:55
non è così che ragionerei io infatti: bisogna piuttosto cercare di capire che modo ha un componente kernel mode di "capire" (anche involontariamente) se il tagging è abilitato o meno, e di cambiare il suo comportamento di conseguenza. finora non ho trovato nulla del genere. fondamentalmente la routine che permette ad un componente kernel mode di specificare un tag nell'allocare un blocco di memoria è questa: http://msdn2.microsoft.com/en-us/library/ms796989.aspx
ma non ti è molto d'aiuto. ciò che mi interessa capire è se il tagging avviene sempre e comunque (e quel flag permette solo a poolmon di vedere i tags) o se avviene solo quando si abilita il flag. se il flag è disabilitato e un driver chiama la ExAllocatePoolWithTag il tag specificato viene ignorato? :wtf:

ora purtroppo devo uscire e non ho tempo, proverò a guardarci stasera.

71104
09-08-2007, 08:09
Capisco il tuo modo di ragionare, ma il mio punto di partenza e' diverso: fermo restando il fatto che ogni pezzo di software puo' essere affetto da bug, sono propenso a pensare che le probabilita' di failure siano piu' alte nel software che ho in esame piuttosto che in Windows, pur non potendolo escludere. rimango dell'opinione che, non essendo il pool non paginato qualcosa di controllabile direttamente dalle applicazioni utente, un programma user mode non deve poter causare un leak lì dentro in nessun modo. le cose sono due: o è colpa di Windows o è colpa di un driver di terze parti. se mai risolverai questo problema sarà solo un workaround: qualche componente kernel mode rimarrà colpevole. sono anche più propenso a pensare che si tratti delle terze parti piuttosto che di Windows: di solito è sempre colpa loro (non è una battuta) :)

Proprio mentre ti sto scrivendo, mi e' venuto in mente che potrei provare ad avere il processo d'Appello utilizzando perfmon! In effetti, Performance Monitor mi rileva questo parametro, giusto? Anzi, se non ricordo male mi riporta il numero di allocazioni ed il totale dei byte allocati.
D'accordo, non e' tanto ma comincio ad avere piu' info a disposizione. non dirlo troppo in giro ma... non ho mai usato perfmon :stordita:
se ti riporta anche il numero di bytes allocati nel pool non paginato tanto meglio, ma non so quanto perfmon sia meglio del Task Manager da questo punto di vista.

A questo punto, mi viene spontanea una domanda, ancor prima di cominciare: se davvero fosse un bug di Windows, quale sara' il risultato dell'analisi con perfmon? variera' in accordo con quanto settato in gflags?
E se per caso questo non succedesse? in teoria dovrebbe; anche perché cè un aggiornamento al post #8: credo di aver scoperto che è la seconda (col flag disabilitato il tagging non avviene per nulla). se non avvenisse boh :D

sottovento
09-08-2007, 08:09
finora non ho trovato nulla del genere. fondamentalmente la routine che permette ad un componente kernel mode di specificare un tag nell'allocare un blocco di memoria è questa: http://msdn2.microsoft.com/en-us/library/ms796989.aspx
ma non ti è molto d'aiuto. ciò che mi interessa capire è se il tagging avviene sempre e comunque (e quel flag permette solo a poolmon di vedere i tags) o se avviene solo quando si abilita il flag. se il flag è disabilitato e un driver chiama la ExAllocatePoolWithTag il tag specificato viene ignorato? :wtf:

ora purtroppo devo uscire e non ho tempo, proverò a guardarci stasera.

Si, avevo gia' visto la chiamata.
Se posso dire la mia, penso che il tagging avvenga sempre. Infatti, oltre alla "normale" deallocazione alla quale si passa l'indirizzo della memoria da deallocare, e' possibile utilizzare un'altra API a cui si deve passare sia l'indirizzo sia il tag. Se il tag passato non coincide con il tag della memoria che si va a deallocare, allora viene ritornato un codice di errore.
Ovviamente non ricordo i nomi delle due funzioni di deallocazione, ma penso non sia un problema recuperarli. Comunque questo dovrebbe significare che il tagging avviene sempre ed il check box di gflags permetta di raccogliere informazioni per monitorare in "tempo reale" quanto viene fatto sui pool di memoria.
Posso immaginare che abilitando questo check box si vada ad inserire una serie di dati in una struttura globale, ed eventualmente a notificare l'avvenuta scrittura. Tutto questo avra' un costo in prestazioni, pertanto immagino che l'abilitazione/disabilitazione serva a questo.

Ovviamente e' solo una mia ipotesi....

71104
09-08-2007, 08:14
Si, avevo gia' visto la chiamata.
Se posso dire la mia, penso che il tagging avvenga sempre. lol ^^
vabbè, allora controllerò meglio stasera; la cosa mi interessa.

Infatti, oltre alla "normale" deallocazione alla quale si passa l'indirizzo della memoria da deallocare, e' possibile utilizzare un'altra API a cui si deve passare sia l'indirizzo sia il tag. Se il tag passato non coincide con il tag della memoria che si va a deallocare, allora viene ritornato un codice di errore.
Ovviamente non ricordo i nomi delle due funzioni di deallocazione, ma penso non sia un problema recuperarli. ExFreePool ed ExFreePoolWithTag

Comunque questo dovrebbe significare che il tagging avviene sempre ed il check box di gflags permetta di raccogliere informazioni per monitorare in "tempo reale" quanto viene fatto sui pool di memoria. per me non significa che il tagging debba necessariamente avvenire sempre: semplicemente la ExFreePoolWithTag potrebbe ignorare il tag se il tagging è disabilitato. inoltre se il tagging avvenisse sempre perché sarebbe necessario un reboot per abilitare il flag?

Ovviamente e' solo una mia ipotesi.... infatti stiamo andando entrambi per ipotesi, ma mi sembra una cosa abbastanza importante da capire nel nostro caso. quando avrò tempo la vedrò meglio.

sottovento
09-08-2007, 09:16
Provato con perfmon (ovviamente disabilitando il flag incriminato).
Risultato:
- il numero di allocazioni totali (per tutto il sistema) produce un grafico che apparentemente e' a dente di sega (i.e. il numero di allocazioni cresce e poi ritorna al valore di base). La linea di base sembra costante (quindi sembrerebbe OK) ma ho ancora pochi dati per confermare;
- Il numero di bytes allocati cresce a scalini (costante per qualche minuto, poi incrementa);
- Il numero di byte allocati per il processo che presenta il problema e' una bisettrice 1./3. quadrante, per cui cresce costantemente

Non e' molto incoraggiante. Quanto meno e' coerente, come ti aspettavi, con Windows Task Manager

EDIT - con piu' dati a disposizione posso scartare il dente di sega: il numero di allocazioni cresce drammaticamente.
A proposito di driver di terze parti: questo comportamento l'ho riscontrato su diversi computer, con diverse configurazioni e diversi sistemi operativi (XP, 2000, 2003).
Per lo meno, il comportamento e' riproducibilissimo, visto che l'ho provato con ALMENO una decina di computer

EDIT - Altro particolare importante: non si incrementa il contatore degli handle!! E' come se si rilasciasse la memoria e se ne prendesse di piu', con una nuova allocazione. Questo pero' non torna con il numero di allocazioni che si incrementa (assumendo, ovviamente, che sia il numero di allocazioni attualmente in uso per un processo).

71104
09-08-2007, 18:42
EDIT - con piu' dati a disposizione posso scartare il dente di sega: il numero di allocazioni cresce drammaticamente.
A proposito di driver di terze parti: questo comportamento l'ho riscontrato su diversi computer, con diverse configurazioni e diversi sistemi operativi (XP, 2000, 2003).
Per lo meno, il comportamento e' riproducibilissimo, visto che l'ho provato con ALMENO una decina di computer bene, ciò dunque ti permette ci escludere completamente il software di terze parti; è la prima volta in tutta la mia esperienza che la colpa non è ne' della parte primaria (cioè tu in questo caso) ne' delle terze parti :|
ribadisco, mi sa tanto che hai trovato un bug di Windows :D

EDIT - Altro particolare importante: non si incrementa il contatore degli handle!! E' come se si rilasciasse la memoria e se ne prendesse di piu', con una nuova allocazione. Questo pero' non torna con il numero di allocazioni che si incrementa (assumendo, ovviamente, che sia il numero di allocazioni attualmente in uso per un processo). be', non vedo cosa ci sia di male nel fatto che il conteggio degli HANDLE non aumenti: se il programma non apre HANDLEs il fatto che il loro numero non aumenti mi pare il minimo :D

sottovento
13-08-2007, 06:04
be', non vedo cosa ci sia di male nel fatto che il conteggio degli HANDLE non aumenti: se il programma non apre HANDLEs il fatto che il loro numero non aumenti mi pare il minimo :D

E' perche' mi aspettavo che, se la memoria viene persa, qualcuno cerchi di prenderla: ritenevo quindi probabilissimo che gli handles aumentassero....


Altra missione fallita: ho provato a riprodurre il memory leak (quindi disabilitando i gflags) e a far crashare il computer con CTRL+Scroll Lock.
Al riavvio ho usato windbg per controllare il MEMORY.DMP che e' stato creato.

Risultato: niente! Il numero di NP pool in uso, secondo il dump, era zero!

Sembra piu' duro del previsto....

71104
13-08-2007, 12:05
E' perche' mi aspettavo che, se la memoria viene persa, qualcuno cerchi di prenderla: ritenevo quindi probabilissimo che gli handles aumentassero.... non ho capito il ragionamento, ma fa niente.

Altra missione fallita: ho provato a riprodurre il memory leak (quindi disabilitando i gflags) e a far crashare il computer con CTRL+Scroll Lock.
Al riavvio ho usato windbg per controllare il MEMORY.DMP che e' stato creato.

Risultato: niente! Il numero di NP pool in uso, secondo il dump, era zero! si vede che l'hai fatto crashare troppo presto: tu fallo crashare quando il Task Manager indica un uso del non-paged pool :p

71104
13-08-2007, 12:13
una notizia che ti può interessare, non so se è una cosa che già avevi: nella mia cartella di installazione dei Debugging Tools for Windows mi sono ritrovato in qualche meandro un file txt contenente la lista di tutti i tags usati dai componenti di Windows per taggare memoria. quindi quando riesci ad avere questo memory dump se riesci a tirare fuori anche dei tags puoi facilmente vedere se è colpa di Windows o meno (anche se sembra che sia proprio colpa sua) e di quale driver è la colpa.

dal momento che usi WinDbg dovresti avere anche tu quel file: si chiama pooltag.txt e sta nella sottocartella "triage" della cartella di installazione dei debugging tools.

sottovento
13-08-2007, 12:32
una notizia che ti può interessare, non so se è una cosa che già avevi: nella mia cartella di installazione dei Debugging Tools for Windows mi sono ritrovato in qualche meandro un file txt contenente la lista di tutti i tags usati dai componenti di Windows per taggare memoria. quindi quando riesci ad avere questo memory dump se riesci a tirare fuori anche dei tags puoi facilmente vedere se è colpa di Windows o meno (anche se sembra che sia proprio colpa sua) e di quale driver è la colpa.

dal momento che usi WinDbg dovresti avere anche tu quel file: si chiama pooltag.txt e sta nella sottocartella "triage" della cartella di installazione dei debugging tools.

Si, lo conosco. E' per questo che volevo ad arrivare a fare un'analisi con il pool monitor, per avere il tag e capire da quale parte sbattere la testa.

Intanto continuo a fare ricerche in rete, ma non ho trovato nulla: mi sento comunque di scartare l'ipotesi di un bug di Windows, visto che nessuno ne parla....

sottovento
16-08-2007, 15:14
Mi regalo un UP

71104
16-08-2007, 16:47
Mi regalo un UP embè? sei riuscito a scoprire i tag di quei blocchi?? :D
se riesci a scoprire quelli sei a cavallo: puoi vedere subito con certezza se il leak è provocato da Windows o da un driver di terze parti, e nel caso fosse provocato da Windows conoscendo il componente che l'ha provocato hai anche un'ottima pista su cui indagare.

sottovento
16-08-2007, 22:25
embè? sei riuscito a scoprire i tag di quei blocchi?? :D
se riesci a scoprire quelli sei a cavallo: puoi vedere subito con certezza se il leak è provocato da Windows o da un driver di terze parti, e nel caso fosse provocato da Windows conoscendo il componente che l'ha provocato hai anche un'ottima pista su cui indagare.

Purtroppo no, non ci sono riuscito: ogni volta che si abilita con gflags, il leak scompare.
Se non abilito, non vedo il tag nemmeno causando il crash (blue screen) o aspettando che vada in crash da solo.

Diventero' matto. Nel frattempo mi sono trasferito in USA dove devo restarci x 8 mesi e questo rendera' le cose piu' difficili, visto che ora ho il fiato del cliente sul collo...

Se hai suggerimenti, di qualsiasi tipo, posta pure....

71104
17-08-2007, 00:09
l'unica che mi viene in mente è il monitoraggio della chiave di registro HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\GlobalFlag per vedere chi è che la legge. dovresti trovare un tool che oltre a determinare il contesto del processo da cui proviene la richiesta di lettura riesca anche a determinare se la richiesta è avvenuta in user mode o in kernel mode, e nel secondo caso quale driver l'abbia effettuata. RegMon temo proprio non basti, e io non ne conosco (oltre a non essere sicuro che sia una cosa fattibile con le interfacce documentate).

mi è venuta questa idea perché dopo aver googlato un po', e dando per scontato che di fatto il tagging della ExAllocatePoolWithTag avvenga sempre, non sono riuscito a trovare funzioni ufficiali per la gestione di quello e altri flag, quindi ritengo che sia un valore di registro da gestire direttamente: è altamente probabile, IMHO, che chiunque modifichi il proprio comportamento in base al valore di quel flag lo deve aver letto dal registro.

sottovento
17-08-2007, 13:09
Grazie per il suggerimento. Appena possibile daro' un'occhiata, sperando di trovare qualche info in piu' su questo benedetto problema....