Heap corruptie onder Win32; hoe te lokaliseren?

stemmen
54

Ik ben bezig met een multithreaded C ++ toepassing die corrumperen de heap. De gebruikelijke hulpmiddelen om deze corruptie te lokaliseren lijken niet van toepassing te zijn. Old bouwt (18 maanden oud) van de broncode vertonen hetzelfde gedrag als de meest recente release, dus dit is al een lange tijd en was gewoon niet opgemerkt; Aan de andere kant, kan de bron delta's niet worden gebruikt om vast te stellen wanneer de bug werd geïntroduceerd - er zijn een heleboel van de code veranderingen in de repository.

De prompt voor beuken behaviuor is doorvoer in dit systeem te genereren - bus overdracht van gegevens die handen genomen tot een interne representatie. Ik heb een set van testgegevens die periodiek de app uitzondering zal veroorzaken (verschillende plaatsen, verschillende oorzaken - met inbegrip van hoop alloc falen, dus: heap corruptie).

Het gedrag lijkt verband te houden CPU-vermogen of het geheugen bandbreedte; hoe meer van elk van de machine heeft, hoe makkelijker het is om te crashen. Het uitschakelen van een hyper-threading kern of een dual-core kern vermindert de snelheid van (maar niet elimineren) corruptie. Dit duidt op een timing gerelateerde kwestie.

Nu hier is het probleem:
Als het draaien onder een lichtgewicht debug-omgeving (bijvoorbeeld Visual Studio 98 / AKA MSVC6) de heap corruptie is redelijk makkelijk te reproduceren - tien of vijftien minuten voorbij voordat er iets verschrikkelijk en uitzonderingen faalt, als een alloc;bij het uitvoeren onder een verfijnde debug omgeving (Rational Purify, VS2008/MSVC9of zelfs Microsoft Application Verifier) wordt het systeem geheugen-speed gebonden en loopt niet vast (-Memory gebonden: CPU is niet boven het krijgen 50%, schijf brandt niet, het programma gaat zo snel het kan, doos consumeren 1.3Gvan 2G van RAM) . Dus, heb ik een keuze tussen de mogelijkheid om het probleem te reproduceren (maar de oorzaak niet identificeren) of in staat zijn om de oorzaak of een probleem dat ik niet kan weergeven idenify gekregen.

Mijn huidige best gissingen waar te daarna is:

  1. Krijg een waanzinnig grunty doos (ter vervanging van de huidige dev doos: 2 GB RAM in een E6550 Core2 Duo); Dit maakt het mogelijk om de crash veroorzaakt mis-gedrag bij het uitvoeren onder een krachtige debug omgeving repro; of
  2. Herschrijf operators newen deletete gebruiken VirtualAllocen VirtualProtectom het geheugen als alleen-lezen, zodra het klaar is met Mark. Ren onder MSVC6en hebben de OS te vangen van de bad-guy wie schriftelijk vrijgegeven geheugen. Ja, dit is een teken van wanhoop: wie de hel herschrijft newen delete?! Ik vraag me af of dit gaat om het zo langzaam als onder Purify et al maken.

En, nee: Het verschepen met Purify instrumentatie ingebouwd is geen optie.

Een collega liep gewoon langs en vroeg stack overflow? Krijgen we stack overflows nu?!?

En nu de vraag: Hoe kan ik zoek de hoop omkoper?


Update: in evenwicht brengen new[]en delete[]lijkt te hebben gekregen een lange weg naar het oplossen van het probleem. In plaats van 15 minuten, de app gaat nu ongeveer twee uur voor de crash. Er nog niet zijn. Eventuele verdere suggesties? De heap corruptie aanhoudt.

Update: een versie build onder Visual Studio 2008 lijkt dramatisch beter; huidige verdenking berust op de STLtoepassing die wordt geleverd bij VS98.


  1. Het probleem reproduceren. Dr Watsonzal een dump die nuttig zijn bij verdere analyse zou kunnen produceren.

Ik zal een nota van dat, maar ik ben bang dat Dr Watson alleen zal worden geactiveerd na het feit, niet wanneer de heap wordt steeds stampte.

Een andere poging zou kunnen worden met behulp van WinDebugals een debugging tool die is vrij krachtig wordt tegelijkertijd ook lichtgewicht.

Heb je dat gaat op dit moment, nog eens: niet veel hulp totdat er iets mis gaat. Ik wil de vandaal op heterdaad te betrappen.

Misschien dat deze hulpmiddelen kunt u op zijn minst om het probleem aan bepaalde component te verkleinen.

Ik niet in het bezit veel hoop, maar wanhopige tijden vragen om ...

En weet je zeker dat alle onderdelen van het project beschikt over de juiste runtime library-instellingen ( C/C++ tab, codegenererings categorie in VS instellingen 6.0-project)?

Nee ik ben niet, en ik zal een paar uur morgen door te brengen die door de werkruimte (58 projecten in het) en controleren ze allemaal compileren en het koppelen met de juiste vlaggen.


Update: Dit duurde 30 seconden. Selecteer alle projecten in het Settingsdialoogvenster, ongedaan totdat u het project (en) die niet de juiste instellingen (ze hadden allemaal de juiste instellingen) hebben vinden.

De vraag is gesteld op 04/08/2008 om 06:30
user
In andere talen...                            


15 antwoorden

stemmen
0

Je probeerde oud bouwt, maar is er een reden dat je niet kunt blijven gaan verder terug in de repository geschiedenis en precies zien wanneer de bug werd ingevoerd?

Anders zou ik stel voor het toevoegen van eenvoudige registratie van een soort te helpen opsporen van het probleem, al ben ik op een verlies van wat er concreet je zou willen om in te loggen.

Als je kunt achterhalen wat er precies kan dit probleem veroorzaken, via google en documentatie van de uitzonderingen die u krijgt, misschien is dat zal verder inzicht over wat te zoeken in de code.

antwoordde op 04/08/2008 om 06:48
bron van user

stemmen
26

Mijn eerste keuze zou een dedicated hoop hulpmiddel zoals pageheap.exe .

Herschrijven van nieuwe en te verwijderen nuttig zou kunnen zijn, maar dat betekent niet de ALLOCS gepleegd door een lager niveau code te vangen. Als dit is wat je wilt, het beter om de omweg low-level alloc APIs met behulp van Microsoft Detours.

Ook sanity controles zoals: controleer uw run-time bibliotheken match (versie vs. debug, multi-threaded vs. single-threaded, dll vs. statische lib), op zoek naar slechte schrapt (bijvoorbeeld doorhalen delete [] had moeten zijn gebruikt), zorg ervoor dat je niet mixen en matchen uw ALLOCS.

Probeer ook selectief uitschakelen van draden en zien wanneer / als het probleem verdwijnt.

Wat betekent de call-stack etc eruit op het moment van de eerste uitzondering?

antwoordde op 04/08/2008 om 06:51
bron van user

stemmen
0

Mijn eerste actie zou zijn als volgt:

  1. Bouw de binaries in "release" versie, maar het creëren van debug info-bestand (u zult deze mogelijkheid in project instellingen te vinden).
  2. Gebruik Dr Watson als een defualt debugger (Drwtsn32 -I) op een machine waarop u het probleem te reproduceren.
  3. Repdroduce het probleem. Dr Watson zal een dump die nuttig zijn bij verdere analyse zou kunnen produceren.

Een andere poging zou kunnen zijn met behulp van WinDebug als een debugging tool die is vrij krachtig wordt tegelijkertijd ook lichtgewicht.

Misschien dat deze hulpmiddelen kunt u op zijn minst om het probleem aan bepaalde component te verkleinen.

En weet je zeker dat alle onderdelen van het project beschikt over de juiste runtime library-instellingen (C / C ++ tabblad Code Generation categorie in VS 6,0 project instellingen)?

antwoordde op 04/08/2008 om 07:26
bron van user

stemmen
11

Ik heb dezelfde problemen in mijn werk (we gebruiken ook VC6soms). En er is geen gemakkelijke oplossing voor. Ik heb slechts een paar tips:

  • Probeer met automatische crashdumps op productiemachine (zie Process Dumper ). Mijn ervaring zegt Dr Watson is niet perfect voor het storten.
  • Verwijder alle vangst (...) van uw code. Ze verstoppen zich vaak ernstige geheugen uitzonderingen.
  • Controleer Geavanceerde Windows Debugging - er zijn veel goede tips om problemen zoals de uwe. Ik beveel dit met heel mijn hart.
  • Als u STLprobeert STLPorten gecontroleerd bouwt. Ongeldige iterator zijn hel.

Succes. Problemen zoals de uwe ons maanden op te lossen. Wees er klaar voor ...

antwoordde op 06/08/2008 om 11:41
bron van user

stemmen
1

Dus van de beperkte informatie die je hebt, kan dit een combinatie van een of meer dingen zijn:

  • Bad heap-gebruik, dat wil zeggen, dubbel bevrijdt, lees na een vrije, schrijven na gratis, het instellen van de HEAP_NO_SERIALIZE vlag met ALLOCS en bevrijdt van meerdere threads op hetzelfde heap
  • Onvoldoende geheugen
  • Slechte code (dat wil zeggen, buffer overflows, buffer onderlopen, etc.)
  • "Timing" kwesties

Als het helemaal de eerste twee, maar niet de laatste, moet je het hebben gevangen door nu met ofwel pageheap.exe.

Die het meest waarschijnlijk betekent dat het is te wijten aan de manier waarop de code wordt de toegang tot gedeeld geheugen. Helaas, het bijhouden van die naar beneden gaat nogal pijnlijk zijn. Gesynchroniseerde toegang tot gedeelde geheugen manifesteert zich vaak als raar "timing" kwesties. Dingen als het niet gebruiken van acquire / release semantiek voor het synchroniseren van de toegang tot gedeeld geheugen met een vlag, niet op de juiste wijze gebruik van sluizen, enz.

Op zijn minst, zou het helpen om te kunnen toewijzingen of andere manier bij te houden, zoals eerder werd gesuggereerd. Tenminste dan kunt u zien wat er werkelijk is gebeurd tot aan de heap corruptie en het proberen te diagnosticeren van dat.

Ook als u gemakkelijk toewijzingen kunnen omleiden naar meerdere hopen, wilt u misschien proberen om te zien of dat of het probleem is opgelost of leidt tot meer reproduceerbare foutieve gedrag.

Toen je testen met VS2008, heb je lopen met HeapVerifier met Conserve Memory ingesteld op Ja? Dat kan invloed op de prestaties van de hoop allocator te verminderen. (Plus, je moet lopen mee Debug-> Begin met Application Verifier, maar u wellicht al weet dat.)

U kunt ook proberen debuggen met Windbg en verschillende toepassingen van de! Heap commando.

MSN

antwoordde op 22/08/2008 om 15:51
bron van user

stemmen
7

We hebben erg veel geluk gehad met het schrijven van onze eigen malloc en gratis functies. In de productie, ze gewoon bellen met de standaard malloc en gratis, maar in debug, kunnen ze doen wat je wilt. We hebben ook een eenvoudige basisklasse die niets doet, maar negeren de nieuwe en exploitanten verwijderen om deze functies te gebruiken, dan is elke klasse je schrijft je gewoon erven van die klasse. Als je een ton van de code, kan het een hele klus om te bellen naar malloc en vrij om de nieuwe malloc en gratis (vergeet niet realloc!) Te vervangen, maar op de lange termijn het is heel nuttig.

In het boek van Steve Maguire Schrijven Solid Code (sterk aanbevolen), zijn er voorbeelden van debug dingen die je kunt doen in deze routines, zoals:

  • Blijf op de hoogte van de toewijzingen om lekken te vinden
  • Wijs meer geheugen dan nodig en zet markeringen aan het begin en het einde van het geheugen - tijdens de vrije routine, kunt u ervoor zorgen dat deze markers zijn er nog steeds
  • memset het geheugen met een marker op de toewijzing en op gratis (om het gebruik van niet-geïnitialiseerd geheugen te vinden) (om het gebruik van free'd geheugen vinden)

Een ander goed idee is om nooit te gebruiken dingen als strcpy, strcatof sprintf- altijd gebruik maken strncpy, strncaten snprintf. We hebben onze eigen versies van deze schriftelijke ook, om ervoor te zorgen dat we niet af te schrijven het einde van een buffer, en deze zijn veel problemen ook gevangen.

antwoordde op 22/08/2008 om 16:11
bron van user

stemmen
3

De schijnbare willekeur van het geheugen corruptie klinkt heel erg als een rode draad synchronisatie kwestie - een bug wordt gereproduceerd afhankelijk van de snelheid van de machine. Als objecten (chuncks geheugen) worden gedeeld threads en synchronisatie (kritieke sectie, mutex, semafoor, andere) primitieven niet op per klasse (per object, per klasse) basis, is het mogelijk om een ​​situatie te komen waarbij klasse (brok geheugen) geschrapt / vrijgemaakt tijdens gebruik of gebruik na verwijderen / bevrijd.

Als een test voor, kon je de synchronisatie primitieven toe te voegen aan elke klasse en de methode. Dit zal uw code trager maken, omdat veel objecten zal moeten wachten voor elkaar, maar als dit elimineert de heap corruptie, zal uw heap-corruptie probleem een ​​optimalisatie van code een te worden.

antwoordde op 25/08/2008 om 18:55
bron van user

stemmen
0

Graeme suggestie aangepaste malloc / free is een goed idee. Kijk of je een bepaald patroon kunnen karakteriseren over de corruptie om u een handvat om hefboomwerking te geven.

Bijvoorbeeld, als het altijd in een blok van dezelfde grootte (zeg 64 bytes), dan verander je malloc / gratis paar om altijd toe te wijzen 64 byte brokken in hun eigen pagina. Wanneer je te bevrijden van een 64 byte brok ingesteld dan kan de bescherming geheugenbits op die pagina om te voorkomen dat leest en wites (met behulp van VirtualQuery). Dan kan iedereen een poging om toegang te krijgen tot dit geheugen zal een uitzondering te genereren in plaats van het bederven van de heap.

Dit veronderstelt wel dat het aantal uitstaande 64 byte brokken is slechts matig of heb je een veel geheugen te branden in de doos!

antwoordde op 02/09/2008 om 03:23
bron van user

stemmen
3

Is dit in weinig geheugen omstandigheden? Als dat zo is kan het zijn dat nieuw is terug te keren NULLin plaats van het gooien std :: bad_alloc. Oudere VC++compilers niet goed dit te implementeren. Er is een artikel over Legacy geheugentoewijzing mislukkingen crashen STLapps gebouwd met VC6.

antwoordde op 02/09/2008 om 05:03
bron van user

stemmen
8

Voer de oorspronkelijke aanvraag met ADplus -crash -pn appnename.exe Wanneer het geheugen probleem opduikt-up vindt u een mooie grote dump te krijgen.

U kunt de dump analyseren om te achterhalen wat het geheugen locatie werd beschadigd. Als je geluk hebt het overschrijven geheugen is een unieke tekenreeks die u kunt achterhalen waar het vandaan kwam. Als je niet gelukkig, moet je graven in win32hoop en erachter wat was de orignal geheugen eigenschappen. (hoop -x kan helpen)

Nadat u weet wat er messed-up, kunt u verkleinen AppVerifier gebruik met een speciale hoop instellingen. dwz kunt u aangeven wat DLLu controleren, of wat de toewijzing grootte te controleren.

Hopelijk zal dit de controle genoeg versnellen om de dader te vangen.

In mijn ervaring, ik nooit vol hoop verificateur modus nodig, maar ik besteed veel tijd aan het analyseren van de crash dump (s) en browsen bronnen.

PS: U kunt gebruik maken van DebugDiag om de stortplaatsen te analyseren. Het kan wijzen op de DLLeigenaar van de beschadigde hoop, en geven u andere nuttige informatie.

antwoordde op 16/09/2008 om 06:33
bron van user

stemmen
1

Als u ervoor kiest om te herschrijven nieuwe / delete, heb ik dit gedaan en hebben een eenvoudige broncode op:

http://gandolf.homelinux.org/~smhanov/blog/?id=10

Dit vangt geheugenlekken en voegt ook bewaken gegevens vóór en na het geheugenblok te heap vangen. Je kunt gewoon integreren met haar door te zetten #include "debug.h" aan de bovenkant van elke CPP-bestand, en het definiëren van DEBUG en DEBUG_MEM.

antwoordde op 17/09/2008 om 12:40
bron van user

stemmen
4

U moet dit probleem aan te pakken met zowel runtime en statische analyse.

Voor statische analyse overwegen het samenstellen met PREfast ( cl.exe /analyze). Het detecteert mismatched deleteen delete[], buffer overschrijdingen en tal van andere problemen. Wees voorbereid, hoewel, te waden door vele kilobytes van L6 waarschuwing, vooral als uw project nog steeds L4niet vast.

PREfast is beschikbaar met Visual Studio Team System en, blijkbaar , als onderdeel van Windows SDK.

antwoordde op 12/10/2008 om 20:55
bron van user

stemmen
0

De kleine keer moest ik een soortgelijk probleem op te lossen. Als het probleem blijft bestaan ​​Ik stel voor dat u dit doet: alle oproepen om nieuwe Monitor / delete en malloc / calloc / realloc / gratis. Ik maak enkele DLL exporteren van een functie voor het register alle gesprekken. Deze functie ontvangt parameter voor het identificeren van uw broncode, aanwijzer naar toegewezen gebied en het type van de oproep opslaan van deze informatie in een tabel. Alle toegewezen / bevrijd pair wordt geëlimineerd. Aan het einde of na moet je u een gesprek voeren met een andere functie voor het creëren van rapport voor links data. Met deze kunt u verkeerd gesprekken (nieuw / gratis of malloc / verwijderen) of ontbrekende identificeren. Als enige geval van buffer overschreven in de code van de opgeslagen informatie kan verkeerd zijn, maar elke test kunnen opsporen / ontdekken / zijn voorzien van een oplossing van mislukking geïdentificeerd. Veel runs te helpen identificeren van de fouten. Succes.

antwoordde op 19/12/2008 om 10:52
bron van user

stemmen
0

Denk je dat dit is een race condition? Zijn er meerdere threads delen een hoop? Kun je elke draad te geven een eigen berg met HeapCreate, dan kunnen ze snel lopen met HEAP_NO_SERIALIZE. Anders moet een hoop worden thread safe, als je met behulp van de multi-threaded versie van het systeem bibliotheken.

antwoordde op 30/07/2009 om 12:48
bron van user

stemmen
0

Een paar suggesties. U noemt de overvloedige waarschuwing W4 - Ik stel de tijd neemt om uw code te bevestigen om netjes te compileren op waarschuwingsniveau 4 - dit zal een lange weg te gaan om te voorkomen dat subtiele moeilijk te vinden bugs te vinden.

Tweede - voor de / analyseren schakelaar - het inderdaad overvloedige waarschuwingen te genereren. Om deze schakelaar gebruiken in mijn eigen project, wat ik deed was om een ​​nieuwe header-bestand dat #pragma waarschuwing die alle extra waarschuwingen gegenereerd door / analyseren uit te schakelen creëren. Dan verder naar beneden in het bestand, ik zet alleen die waarschuwingen waar ik om geef. Gebruik vervolgens de / FI compiler schakelaar om deze header bestand dwingen om voor het eerst in al uw compilatie-eenheden worden opgenomen. Dit moet toelaten om het gebruik / switch te analyseren, terwijl controlerende de output

antwoordde op 03/10/2009 om 15:48
bron van user

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more