VS C++ access violation

VS C++ access violation
2010-08-30T11:29:38+02:00
2010-10-01T17:23:13+02:00
2022-11-19T08:55:33+01:00
otherfirst
Hello,
Én egy nagy visual studios projekten (VS 2010) dolgozom és egy hosszabb numerikus algoritus során elég sok CDoc osztály keletkezik, viszont amikor eljutunk az osztályok destruktorának meghívásához, a saját (más CDoc objektumokra) mutató pointer adattagokra access violation reading memory-t dob a program. A destruktorban vannak ellenőrzések, hogy (ptr != NULL) és, ha nem akkor szeretnék rájuk fv.-t hívni, de jön a hiba. Ezen részek kikommentezésével pedig az automatikus memória felszabadítás során jön ugyanez a hiba a destruktor legvégén. Néha kapok outof memory errort is. Kérdés: Lehetséges-e, hogy valamivel "véletlenül" belebuherálok a memóriafoglalásba amival korruptálom a poitereket, mik lehetnek ilyen szempontból "veszélyes" függvények, amit a VS esetleg nem ellenőriz és ő maga automatikusan csinálhat-e valami hasonlót automatikusan a memória végének elérésekor?
A választ előre köszönöm.
Mutasd a teljes hozzászólást!

  • > Kérdés: Lehetséges-e, hogy valamivel "véletlenül" belebuherálok a memóriafoglalásba amival korruptálom a poitereket,

    Bizony, hogy lehetséges.

    > mik lehetnek ilyen szempontból "veszélyes" függvények

    Mindegyik.
    Mutasd a teljes hozzászólást!
  • Mármint olyan függvényekre gondolok, amik hivatalosak és úgymondd elszipkázhatják a memóriát a pointerem elől (de amúgy közvetlenül nem állnak vele kapcsolatban). A programban van konkrét alloc fv., de a többi részegységben semmilyen hibát nem okoz, sőt a sok-sok osztály közül csak egyetlen-egy destruktorával van gond és nyilván a saját pointereinek a hibája miatt. Kipróbálta a vs opcióit, de nem nagyon sikerült pontosan nyomon követnem, hogy hol dolgozik az adott memória címmel. Esetleg ötlet erre a konkrét problémára?
    Mutasd a teljes hozzászólást!
  • (Ha linux lenne, akkor azt mondanám, hogy Electric Fence vagy Valgrind... lehet, hogy VS-ben is van hasonló, de ahhoz nem értek, sajnos)
    Mutasd a teljes hozzászólást!
  • A legvalószínűbb, hogy már törölt mutató törlődik újra. Valahol "klónozódott" a mutató, az egyiket törölte a program, a másikból zombi lett.

    Ha érdekel, majd leírom, hogyan lehet automatát írni az ilyen "elnézések" pontos behatárolására.
    Mutasd a teljes hozzászólást!
  • Köszönöm, azt hiszem ezzel valóban segítettél, mert erre még nem gondoltam (nem is tudom miért). Ugyan még nem néztem, hogy ez volt-e a probléma (elég nagy kód és kusza (nem az enyém)). Valamint az automata is érdekelne, hogy pontosan mire gondolsz (egy ilyen mindig hasznos).
    Mutasd a teljes hozzászólást!
  • Az ember a tizen-huszadik ilyen hiba után elgondolkozik, nem lehetne ezt egyszerűbben is megoldani?
    De lehet.

    Gyakorlatilag a programba beépül egy olyan figyelő rendszer, ami azonnal jelzi ha zombit talál. Mivel minden new operátorral létrehozott mutató megkapja azonosítóként a __FILE__ és a __LINE__ makrókat, a mutatóról azonnal kiderül, hogy ki is valójában. Még bele lehetne építeni a __FUNCTION__ makrót, erről elfelejtkeztem.
    Mivel állandóan egy listában keres, ezért jelentősen lassít. De csak akkor kell futtatni így a programot, ha bug van.


    Régen win32 alatt ment, most délután megírtam linuxban, mert itt csak ez van.



    Több próbálkozást is talál az ember a neten, mivel néhány ötlet tőlük is származik, itt vannak referenciának.

    Google Heap Leak Checker
    A Cross-Platform Memory Leak Detector
    Overload Operator new to detect memory leaks
    Mutasd a teljes hozzászólást!
  • ...és a kód. Amennyire egyszerű, annyira hatékony.
    Mint egy feszítővas.
    Két kényelmetlenség van, amit még nem sikerült eltüntetnem.

    A new operátorokat mindenhol le kell cserélni _new makróra. Erre azért van szükség, mert a __LINE__ csak így adja meg a new pontos sorát.
    A megadott linken található olyan technika, hogy a hívó címe tárolódik, amit asm-el lehet megkapni, és utánna debuggerrel megtalálni. Szerintem az kényelmetlen, inkább átnevezek 50 new operatort.

    A másik, hogy az osztályoknál ellenőríztetni kell a THIS érvényességét. Ez már nem kis munka.
    De garantáltan megtalálja, ha gond van a heap-al.


    A kimenet,
    ww.cpp(164)
    ww.cpp(145)
    fuggveny 1
    fuggveny 2
    :ismeretlen mutato hasznalatban
    :zoombie location: ww.cpp : 164

    A 164.sor pedig ez. aa=_new valami;


    #include <cstdio> #include <stdlib.h> #include <malloc.h> #include <string.h> #include <iostream> typedef unsigned int uint; void err(char *err,char *ext=0) { printf(":%s\n",err); // MessageBox(0,err,err,MB_OK); if(ext) { printf(":%s\n",ext); // MessageBox(0,ext,ext,MB_OK); } exit(0); } /*****************************************/ #define plist_maxsize 10000 struct struct_plist { uint pointer; uint size; char *location; int line; }plist[plist_maxsize],plistzoombie[plist_maxsize]; int plist_size=0; int plistzoombie_size=0; void add2plist(void * new_pointer,uint size,char *location,int line) { printf("%s(%d)\n",location,line); for(int p=0;p<plist_size;p++) { if(plist[p].pointer<=(uint)new_pointer) if((uint)new_pointer<(plist[p].pointer + plist[p].size)) { char str[128]; sprintf(str,"location: %s : %d \n",plist[p].location,plist[p].line); err("az uj mutato terulete mar hasznalatban van",str); } } plist[plist_size].pointer=(uint)new_pointer; plist[plist_size].size=size; plist[plist_size].location=location; plist[plist_size].line=line; plist_size++; if(plist_size>plist_maxsize) err("plist_maxsize kicsi, noveld meg"); // plist nem hasznalhat memoriafoglalast } void delplist(void * pointer) { bool deleted=false; for(int p=0;p<plist_size;p++) if(plist[p].pointer==(uint)pointer) { memcpy(&plistzoombie[plistzoombie_size],&plist[p],sizeof(struct_plist));//egy uj zoombie memcpy(&plist[p],&plist[plist_size-1],sizeof(struct_plist)); plist_size--; plistzoombie_size++; deleted=true; } if(deleted==false) err("ismeretlen mutato torles tortent"); } void checkpointer(void *pointer) { char str[128]; str[0]=0; if(pointer==0) err("zero mutato"); for(int p=0;p<plist_size;p++) if(plist[p].pointer==(uint)pointer) return; for(int p=0;p<plistzoombie_size;p++) { if(plistzoombie[p].pointer==(uint)pointer) { sprintf(str,"zoombie location: %s : %d \n",plistzoombie[p].location,plistzoombie[p].line); } } err("ismeretlen mutato hasznalatban",str); } /*****************************************/ #define _new new(__FILE__,__LINE__) void* operator new(std::size_t size,char *_location,int _line) { void *pointer=malloc(size); add2plist(pointer,size,_location,_line); return pointer; } void* operator new[](std::size_t size,char *_location,int _line) { void *pointer=malloc(size); add2plist(pointer,size,_location,_line); return pointer; } void operator delete(void* pointer) { delplist(pointer); free(pointer); } void operator delete[](void* pointer) { delplist(pointer); free(pointer); } /*****************************************/ class valami { private: int *p; public: valami() {p=_new int[10];}; ~valami() {delete p;p=0;}; void fuggveny(int n) { printf("fuggveny %d\n",n); checkpointer(this); //checkpointer(p); p[n]=n; }; }; int main() { valami *aa=0, *bb=0; aa=_new valami; bb=aa; aa->fuggveny(1); delete aa; bb->fuggveny(2); return 0; }
    Mutasd a teljes hozzászólást!
  • A plistzoombie a törölt mutatók listája, ami egy idő után felgyülhet. Ezt még nem teszteltem nagyobb projekten, a többit igen.
    Mutasd a teljes hozzászólást!
  • simán az hogy:
    if (ptr != 0)
    {
    delete ptr;
    ptr = 0;
    }
    Mutasd a teljes hozzászólást!
  • Nem értem, mire gondolsz.

    Manapság szinte mindenki úgy törli a mutatókat, ahogy írod. De nem ez a probléma. Törlés előtt le lett másolva.
    Hiába nullázod az aa-t, attól a bb még "éles" marad.

    De nem csak ezt a tipusú memória bugot találja meg, hanem másféléket is, hiszen már a mutató létrehozásánál is ellenőrzi az adott memória területet.
    Mutasd a teljes hozzászólást!
  • De próbáld ki. Az aa nullázása sem segít.
    delete aa;aa=0;
    bb->fuggveny(2);

    ww.cpp(164)
    ww.cpp(145)
    fuggveny 1
    fuggveny 2
    :ismeretlen mutato hasznalatban
    :zoombie location: ww.cpp : 164



    ...utánna vedd ki a védelmet, és töröld bb-t.

    //checkpointer(this);
    if(bb!= 0) delete bb;


    ww.cpp(164)
    ww.cpp(145)
    fuggveny 1
    fuggveny 2
    Segmentation fault

    Ha nincs ellenőrzés, akkor linuxon megfagy a pointer törlésénél VALAHOL, az ellenőrzés pedig PONTOSAN megmondja mivel van a gond.

    Mutasd a teljes hozzászólást!
  • nem értem minek feltalálni a feszítővasat, ha már létezik erre alkotott külön zárnyitogató szerkezet.

    a VS alapból nézi a heap érvényességét debug környezetben. Ezt maga a heap invalid értékének vizsgálatával teszi meg.


    de létezik heap debugger is, erre google jó barát tud lenni. invalid pointerre meg sosem az allokáció a hibás, hanem a pointer kezelésében van valami fatális tévedés.
    Mutasd a teljes hozzászólást!
  • pointer kezelésében van valami fatális tévedés.


    így van, ez pont erre jó. Mint olvashattad , idegen kódról van szó. Megtalálja 5 perc alatt, amit több hét programolvasás és debuggolással találnál meg.

    Ennyi. De nem kötelező használni.
    Lehet olvasgatni a debuggerben a hexa címeket, ha az nagyobb boldogságot okoz.
    Mutasd a teljes hozzászólást!
  • 5 perc alatt.. hát hogyne pályafutásom alatt azért ez az 5 perc akár 5 napot is jelentett olykor...



    mert nem az allokáció fogja a hibát elkövetni. Hanem az algoritmus, amivel operálsz, annak pedig nem két perc a debugja!

    ha az alap debugger se tud mást tenni mint hexa címeket irogatni, te mire mész egy olyan kóddal ami csak kevesebbet tud mint a debugger-runtime lib páros ?

    üdv.

    Mutasd a teljes hozzászólást!
  • Jó, biztos igazad van.

    Én csak leírtam, amit tapasztaltam. Majd a témanyitó is leírja, lehet hogy neki nem ér semmit az egész.

    Nekem 5 perc alatt 3 bugot talált miután megírtam. Azután pedig többé nem volt gondom a memória kezeléssel.

    De hát tudom, hogy én nem értek hozzá.
    Internet, LOL
    Mutasd a teljes hozzászólást!
  • Azt hiszem, a
    void checkpointer(void *pointer)
    -ben a zombikat visszafele kellene vizsgálni.
    A legutoljára törölteknek nagyobb a megjelenési valószínűségük.
    Mutasd a teljes hozzászólást!
  • nem az allokáció fogja a hibát elkövetni.

    Most pont hogy azzal lehet gond.

    amikor eljutunk az osztályok destruktorának meghívásához, a saját (más CDoc objektumokra) mutató pointer adattagokra access violation reading memory-t dob a program.


    Ez tipikusan egy törölt pointer újra törlése lehet. Természetesen lehet más jellegű hiba is, teszemazt tömb túlindexelés, de az már nagyon dúrva hiba.

    Arra a legegyszerűbb módszer egy általános tömb-osztály használata, ahol a [] operátor ellenőrzi az indexelési határokat.

    Nem kizárólag a THIS pointereket lehet ellenőrízni a checkpointer()-rel, hanem az összes mutatót.
    A gond az, hogy a program futási sebessége jelentősen esik. Tegyük fel, hogy az előbb említett [] operátor hívja a checkpointert. Ekkor minden egyes tömbműveletnél végignézi az addig létrehozott plist-et, ami miatt a program nagyon lelassul.

    De ha bármilyen már törölt pointert talál, egyből megáll ott, és a bug azonnal javítható, hiszen az esetek többségében egyértelmű lesz a hiba oka.
    Mutasd a teljes hozzászólást!
  • Tyhűű! szép anyag! Még egy-két laikus kérdés tőlem és utána igyekszem az adott anyaggal dolgozni legjobb tudásom szerint.
    Ugyanis van itt egy "out of memory" error is, ami csak néha jön elő egy kirajzoló függvény hivásakor (abban nem találtam semmi rendelleneset), viszont a hibák időzítése és a debuggolás eredményeképpen arra a következtetésre jutottam, hogy a forrás az a bizonyos "access violation", ami persze nem mindig ugyanazon a memória címen történik és általában egy CArray-el megvalósított int mátrix egy eleménél (aminek az értéke -5 millió valamennyi) fordul elő. Tehát ptr duplázásnál elméletileg nincs memory leak. Kérdés: ez a hiba okozhat-e sok-sok memóriafoglálst (különböző memóriaméretű gépeknél ugyanúgy) valamilyen módon vagy ez biztosan két külön hiba.
    Ja és kösz az eddigi segítséget, "talán" ... "remélem" közelebb jutottam a megoldáshoz, de még nincs meg a hiba gyökere.
    Mutasd a teljes hozzászólást!
  • "mindig ugyanazon a memória címen történik"
    Tegyél hardware breakpoint-ot arra a memóriacímre és akkor megtudod, hogy kik szemetelik össze.

    Mutasd a teljes hozzászólást!
  • Lényeg, ami lényeg nincs még megoldás és elég reménytelen az egész, de köszönöm a segítséget.
    Mutasd a teljes hozzászólást!
  • Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd