Heap-skade etter Win32; hvordan du finner?

stemmer
55

Jeg jobber med en flertrådet C ++ program som er ødelegge haugen. De vanlige verktøy for å lokalisere dette korrupsjon ser ut til å være ubrukelig. Old bygger (18 måneder gammel) av kildekoden utstillings samme atferd som den siste utgivelsen, så dette har eksistert i lang tid og bare ble ikke lagt merke til; på nedsiden, kan kilde deltaer ikke brukes til å identifisere når feilen ble innført - det er mye av kodeendringer i depotet.

Meldingen for bryter behaviuor er å generere gjennomstrømning i dette system - kontakten overføring av data som er munged inn i en intern representasjon. Jeg har et sett med testdata som vil periodevis føre appen unntak (ulike steder, ulike årsaker - inkludert haug Alloc sviktende, og dermed: heap-skade).

Oppførselen synes relatert til CPU-kraft eller minnebåndbredde; jo mer av hvert maskinen har, jo lettere er det å krasje. Deaktivering av en hyper-tråder kjerne eller en dual-core kjerne reduserer hastigheten av (men ikke eliminere) korrupsjon. Dette tyder på et tidspunkt relatert problem.

Nå her er gni:
Når det kjøres under en lett debug miljø (si Visual Studio 98 / AKA MSVC6) heap korrupsjon er rimelig lett å reprodusere - ti eller femten minutter gå før noe svikter horrendously og unntak, som en alloc;når du kjører under en sofistikert debug miljø (Rational rense VS2008/MSVC9eller til og med Microsoft Application bekreft) systemet blir minnehastighet bundet og ikke krasjer (Memory-bundet: CPU er ikke å få over 50%, disk lampen ikke lyser, er programmet kommer så fort den kan, boks forbruker 1.3Gav 2G RAM) . Så, jeg har et valg mellom å være i stand til å gjenskape problemet (men ikke identifisere årsaken) eller å kunne idenify årsaken eller et problem jeg ikke kan gjengi.

Min nåværende beste gjetninger om hvor du skal neste er:

  1. Få en sinnsykt grunty boks (for å erstatte dagens dev boksen: 2Gb RAM i en E6550 Core2 Duo); Dette vil gjøre det mulig å Repro ulykken forårsaker mis-atferd når du kjører under et kraftig debug miljø; eller
  2. Rewrite operatører newog deleteå bruke VirtualAlloc, og VirtualProtectfor å markere minnet som skrivebeskyttet så snart den er ferdig med. Kjøre under MSVC6og ha OS fange den dårlige-fyren som skriver til frigjort minne. Ja, dette er et tegn på desperasjon: Hvem i helvete omskrivninger newog delete?! Jeg lurer på om dette kommer til å gjøre det så sakte som under Rense et al.

Og nei: er Shipping med Purify instrumentering bygget i ikke et alternativ.

En kollega bare gikk forbi og spurte Stack Overflow? Er vi får stabelen flyter nå?!?

Og nå er spørsmålet: Hvordan finner jeg haugen corruptor?


Oppdatering: balansere new[]og delete[]ser ut til å ha fått en lang vei mot å løse problemet. I stedet for 15 minutter, går app nå om lag to timer før krasj. Ikke der ennå. Eventuelle ytterligere forslag? Heap korrupsjon vedvarer.

Oppdatering: en utgivelse bygge i henhold til Visual Studio 2008 virker dramatisk bedre; strøm mistanke hviler på STLgjennomføringen som følger med VS98.


  1. Gjenskape problemet. Dr Watsonvil produsere en dump som kan være nyttig i videre analyse.

Jeg skal ta et notat av det, men jeg er bekymret for at Dr Watson vil kun bli utløst opp i ettertid, ikke når haugen er å bli trampet på.

Et annet forsøk kan være å bruke WinDebugsom et feilsøkingsverktøy som er ganske kraftig som samtidig også lette.

Fikk det går i øyeblikket, igjen: ikke mye hjelp før noe går galt. Jeg ønsker å fange den vandal i loven.

Kanskje disse verktøyene vil tillate deg i det minste å begrense problemet til enkelte komponent.

Jeg holder ikke mye håp, men desperate tider krever ...

Og er du sikker på at alle komponentene i prosjektet har riktige Runtime Library innstillinger ( C/C++ tab, Code Generation kategori i VS 6,0 prosjektinnstillinger)?

Nei jeg er ikke, og jeg kommer til å tilbringe et par timer i morgen gå gjennom arbeidsområdet (58 prosjekter i det) og sjekke de er alle kompilering og linking med passende flagg.


Oppdatering: Dette tok 30 sekunder. Velg alle prosjekter i Settingsdialogen, velge bort til du finner prosjekt (er) som ikke har de riktige innstillingene (de alle hadde de riktige innstillingene).

Publisert på 04/08/2008 klokken 07:30
kilden bruker
På andre språk...                            


15 svar

stemmer
27

Mitt første valg ville være en dedikert haug verktøy som pageheap.exe .

Omskriving ny og slett kan være nyttig, men det betyr ikke fange ALLOCS begått av lavere nivå kode. Hvis dette er hva du vil, bedre omkjøring de low-level alloc APIs ved hjelp av Microsoft Detours.

Også feilsjekkene som: bekrefte kjøre-time biblioteker kamp (release vs. debug, multi-threaded vs single-threaded, dll vs statisk lib), se etter dårlige sletter (for eksempel slette hvor delete [] skulle ha vært brukt), sørg for at du ikke mikse og matche dine ALLOCS.

Også prøve selektivt skru av tråder og se når / hvis problemet forsvinner.

Hva gjør kallstakken etc ser ut som på tidspunktet for den første unntaket?

Svarte 04/08/2008 kl. 07:51
kilden bruker

stemmer
11

Jeg har samme problemene i mitt arbeid (vi bruker også VC6noen ganger). Og det er ingen enkel løsning for det. Jeg har bare noen hint:

  • Prøv med automatisk crash dumper på produksjonsmaskin (se Process Dumper ). Min erfaring sier Dr. Watson er ikke perfekt for dumping.
  • Fjern alle catch (...) fra koden din. De skjuler ofte alvorlige minne unntak.
  • Sjekk Advanced Windows Debugging - det er mange gode tips for problemer som din. Jeg anbefaler dette av hele mitt hjerte.
  • Hvis du bruker STLprøver STLPortog sjekket bygger. Ugyldig iterator er helvete.

Lykke til. Problemer som din ta oss måneder å løse. Vær klar for dette ...

Svarte 06/08/2008 kl. 12:41
kilden bruker

stemmer
8

Kjør den opprinnelige søknaden med ADplus -crash -pn appnename.exe Når minnet problemet dukker opp vil du få en fin stor dump.

Du kan analysere dump å finne hva minnestedet ble ødelagt. Hvis du er heldig skrivings minne er en unik streng du kan finne ut hvor den kom fra. Hvis du ikke er heldig, vil du trenger å grave i win32haugen og finne hva som var orignal minne egenskaper. (haug -x kan hjelpe)

Når du vet hva som ble messed opp, kan du begrense appverifier bruk med spesielle heap innstillinger. dvs. at du kan spesifisere hva DLLdu overvåke, eller hva tildeling størrelse å overvåke.

Forhåpentligvis vil dette speedup overvåkingen nok til å fange den skyldige.

I min erfaring, jeg trengte aldri fullt haug kontrollør modus, men jeg brukte mye tid på å analysere krasjdump (e) og surfing kilder.

PS: Du kan bruke DebugDiag å analysere dumper. Det kan peke ut den DLLsom eier den ødelagte haug, og gi deg annen nyttig informasjon.

Svarte 16/09/2008 kl. 07:33
kilden bruker

stemmer
7

Vi har hatt ganske lykke ved å skrive våre egne malloc og gratis funksjoner. I produksjon, de bare kaller standard malloc og gratis, men i debug, kan de gjøre hva du vil. Vi har også en enkel base klasse som ikke gjør annet enn å overstyre den nye og slette operatører å bruke disse funksjonene, så alle klasser du skriver kan bare arve fra den klassen. Hvis du har massevis av kode, kan det være en stor jobb å erstatte samtaler til malloc og gratis til den nye malloc og gratis (ikke glem realloc!), Men i det lange løp er det svært nyttig.

I Steve Maguire bok Writing Solid kode (anbefales), finnes det eksempler på debug ting som du kan gjøre i disse rutinene, som:

  • Hold orden på bevilgninger for å finne lekkasjer
  • Tildele mer minne enn nødvendig og sette markører på begynnelsen og slutten av minne - i løpet av gratis rutine, kan du sikre disse markørene er der fortsatt
  • memset med en markør på tildeling av minne (for å finne bruk av initialisert minne) og videre fri (for å finne bruk av free'd minne)

En annen god idé er å aldri bruke ting som strcpy, strcateller sprintf- bruk alltid strncpy, strncatog snprintf. Vi har skrevet våre egne versjoner av disse også, for å sørge for at vi ikke avskrive slutten av en buffer, og disse har fanget masse problemer også.

Svarte 22/08/2008 kl. 17:11
kilden bruker

stemmer
4

Du bør angripe dette problemet med både kjøring og statisk analyse.

For statisk analyse vurdere kompilere med PREfast ( cl.exe /analyze). Den oppdager feilaktige deleteog delete[], bufferoverskridelser og en rekke andre problemer. Vær forberedt, men å vasse gjennom mange kilobyte L6 advarsel, spesielt hvis prosjektet har fortsatt L4ikke løst.

PREfast er tilgjengelig med Visual Studio Team System og, tilsynelatende , som en del av Windows SDK.

Svarte 12/10/2008 kl. 21:55
kilden bruker

stemmer
3

Er dette i lite minne forhold? I så fall kan det være at nye er på vei tilbake NULLi stedet for å kaste std :: bad_alloc. Eldre VC++kompilatorer ikke riktig å gjennomføre dette. Det er en artikkel om Legacy minnetildeling feil bryter STLapps bygget med VC6.

Svarte 02/09/2008 kl. 06:03
kilden bruker

stemmer
3

Den tilsynelatende tilfeldigheten minnet korrupsjon høres veldig mye ut som en tråd synkronisering problem - en bug er gjengitt avhengig av maskinhastigheten. Hvis gjenstander (chuncks minne) er delt mellom tråder og synkroniserings (kritiske seksjon, Mutex, semafor, andre) primitivene er ikke på per-klasse (pr-objekt, per-klasse) basis, da det er mulig å komme til en situasjon hvor klasse (mengde minne) er slettet / frigjort under bruk, eller brukt etter slettes / frigjort.

Som en test for det, kan du legge til synkroniserings primitiver til hver klasse og metode. Dette vil gjøre koden tregere fordi mange objekter vil måtte vente på hverandre, men hvis dette eliminerer heap korrupsjon, vil heap-korrupsjon problemet blitt en kode optimalisering ett.

Svarte 25/08/2008 kl. 19:55
kilden bruker

stemmer
1

Hvis du velger å skrive om nye / slette, har jeg gjort dette og har enkel kildekode:

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

Dette fanger minnelekkasjer og også setter vakt data før og etter lagerblokken for å fange opp stakkødeleggelse. Du kan bare integrere med det ved å sette # include "debug.h" på toppen av hver CPP-fil, og definere DEBUG og DEBUG_MEM.

Svarte 17/09/2008 kl. 13:40
kilden bruker

stemmer
1

Så fra den begrensede informasjonen du har, kan dette være en kombinasjon av en eller flere ting:

  • Bad haug bruk, dvs. doble frigjør, leser etter gratis, skrive etter å ha fri, sette HEAP_NO_SERIALIZE flagg med ALLOCS og frigjør fra flere tråder på samme haugen
  • Tomt for minne
  • Dårlig kode (dvs. buffer overflow, buffer underflows etc.)
  • "Timing" problemer

Hvis det er i det hele tatt de to første, men ikke siste, bør du ha fanget det nå med enten pageheap.exe.

Noe som mest sannsynlig betyr at det er på grunn av hvordan koden er tilgang til delt minne. Dessverre, sporing det ned kommer til å være ganske smertefullt. Usynkroniserte tilgang til delt minne manifesterer ofte som rare "timing" problemer. Ting som ikke ved hjelp av innhentings / frigjørings semantikk for synkronisering av adgang til et delt minne med et flagg, ikke ved bruk av låser på riktig måte, etc.

I det minste, ville det hjelpe å kunne spore bevilgninger eller annen måte, som ble foreslått tidligere. Minst da kan du se hva som faktisk har skjedd frem til heap korrupsjon og forsøke å diagnostisere fra det.

Også, hvis du enkelt kan omdirigere bevilgninger til flere hauger, kan det være lurt å prøve det for å se om det enten løser problemet eller gir mer reproduserbar buggy atferd.

Når du ble testet med VS2008, fikk du kjøre med HeapVerifier med spare minne satt til Ja? Det kan redusere ytelsen virkningen av haugen måleren. (I tillegg har du til å kjøre med den Debug-> Start med Application bekreft, men du kanskje allerede vet det.)

Du kan også prøve debugging med Windbg og forskjellig bruk av haugen kommando!.

MSN

Svarte 22/08/2008 kl. 16:51
kilden bruker

stemmer
1

Min første handling ville være som følger:

  1. Bygg binærfilene i "Slipp" versjon, men å skape debug info fil (du finner denne muligheten i prosjektinnstillinger).
  2. Bruk Dr Watson som defualt debugger (drwtsn32 -I) på en maskin som du ønsker å gjenskape problemet.
  3. Repdroduce problemet. Dr Watson vil produsere en dump som kan være nyttig i videre analyse.

Et annet forsøk kan være å bruke WinDebug som et feilsøkingsverktøy som er ganske kraftig som samtidig også lette.

Kanskje disse verktøyene vil tillate deg i det minste å begrense problemet til enkelte komponent.

Og er du sikker på at alle komponentene i prosjektet har riktige Runtime Library innstillinger (tab C / C ++, Code Generation kategori i VS 6,0 prosjektinnstillinger)?

Svarte 04/08/2008 kl. 08:26
kilden bruker

stemmer
1

Du prøvde gammel bygger, men er det en grunn til at du ikke kan holde det gående lenger tilbake i depotet historie og se nøyaktig når feilen ble innført?

Ellers ville jeg foreslå å legge enkel logging av noe slag for å hjelpe spore opp problemet, selv om jeg er på et tap på hva som konkret kan det være lurt å logge.

Hvis du kan finne ut hva som kan nøyaktig forårsaker dette problemet, via google og dokumentasjon av unntakene du får, kanskje det vil gi ytterligere innsikt om hva du skal se etter i koden.

Svarte 04/08/2008 kl. 07:48
kilden bruker

stemmer
0

Et par forslag. Du nevner innholdsrik advarsler på W4 - Jeg foreslår at du tar deg tid til å fikse koden for å kompilere rent på advarselsnivå 4 - dette vil gå en lang vei å hindre subtil vanskelig å finne feil.

Sekund - for / analysere switch - betyr det faktisk genererer innholdsrik advarsler. For å bruke denne bryteren i mitt eget prosjekt, det jeg gjorde var å lage en ny header fil som brukes #pragma advarsel for å slå av alle de ekstra advarsler generert av / analysere. Så lenger ned i filen, slår jeg bare på de advarslene jeg bryr meg om. Deretter bruker / FI kompilatoren bryteren for å tvinge denne header filen som skal inkluderes først i alle dine samle enheter. Dette bør tillate deg å bruke / analysere bryteren mens kontrollerende utgangs

Svarte 03/10/2009 kl. 16:48
kilden bruker

stemmer
0

Tror du dette er en rase tilstand? Er flere tråder å dele en haug? Kan du gi hver tråd en privat haug med HeapCreate, så de kan kjøre fort med HEAP_NO_SERIALIZE. Ellers bør en haug bli tråden trygt, hvis du bruker multi-threaded versjon av systembiblioteker.

Svarte 30/07/2009 kl. 13:48
kilden bruker

stemmer
0

Den lille tiden jeg hadde til å løse et lignende problem. Hvis problemet fortsatt eksisterer foreslår jeg at du gjør dette: Overvåke alle samtaler til nye / slette og malloc / calloc / realloc / gratis. Jeg gjør enkelt DLL eksportere en funksjon for register alle anrop. Denne funksjonen motta parameter for å identifisere kodekilde, pekeren til tilordnede området og type anrop å lagre denne informasjonen i en tabell. Alle allokert / befridd par er eliminert. På slutten eller etter trenger du du ringer til en annen funksjon for å lage rapport for venstre data. Med denne kan du identifisere feil samtaler (nye / gratis eller malloc / slette) eller mangler. Hvis noen tilfelle av buffer overskrevet i koden informasjonen lagret, kan være galt, men hver test kan påvise / oppdage / include en løsning av svikt identifisert. Mange går til å identifisere feilene. Lykke til.

Svarte 19/12/2008 kl. 11:52
kilden bruker

stemmer
0

Graeme forslag om tilpassede malloc / free er en god idé. Se om du kan karakterisere noen mønster om korrupsjon for å gi deg et håndtak for å utnytte.

For eksempel, hvis det er alltid i en blokk av samme størrelse (si 64 byte) og deretter endre malloc / gratis par å alltid sette av 64 byte biter i sin egen side. Når du frigjøre en 64 byte blings deretter sette minne beskyttelse biter på den siden for å hindre leser og wites (ved hjelp VirtualQuery). Så alle som forsøker å få tilgang til dette minnet vil generere et unntak heller enn å ødelegge haugen.

Dette betyr anta at antall utestående 64 byte biter er bare moderat eller du har mye minne til å brenne i boksen!

Svarte 02/09/2008 kl. 04:23
kilden bruker

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