Navigacija
Lista poslednjih: 16, 32, 64, 128 poruka.

Interesantan feljton

[es] :: C/C++ programiranje :: Interesantan feljton

[ Pregleda: 1999 | Odgovora: 9 ] > FB > Twit

Postavi temu Odgovori

Autor

Pretraga teme: Traži
Markiranje Štampanje RSS

Revisor
Sofija - NBGD

Član broj: 18843
Poruke: 59
*.ptt.yu

Sajt: alas.matf.bg.ac.yu/~mr021..


Profil

icon Interesantan feljton26.06.2004. u 09:12 - pre 241 meseci
Heh, assembleristi i teoreticari kompajlera ce moci malo da se uposle ovaj put.

Imao sam do skora jedan interesantan problem. Nekako sam ga resio ali ne verujem da cu sam saznati sta ga je neposredno izazivalo. Moguce je da ce koristiti dodatni info: Win32 x OpenGL aplkacija; MS VC++ 6.0 kompajler; WinME i Win2000 platforme.

Problem je naizgled trivijalan: Applikacija nekako pada prilikom WinMain-ovog return 0;. (std. poruka i poziv na debug)

Sta ga naizgled izaziva: Padovi aplikacije su se desavali kada bih u klasu, uslovno receno - kamere, dodao ma kakav niz ili objekat npr. int axe[256], string axe itd. Taj niz nije trebao da radi nista. Samo da postoji - i aplikacija je padala. Sklonim niz iz klase ili ga stavim kao eksternu, sve radi. (moguce ovo za eksternu jer se aplikacija moze runnovati samo sa jednom kamerom)

Sama klasa eksplicitno ne radi nista sto bi se moglo nazvati dinamickom alokacijom memorije. Samo ima gomilu elemenata i koristi ih. Objekte koji su sadrzani kao i objekte objekata koji su sadrzani itd. sam isprobao i pregledao. Zasebno rade kako treba.

Obeshrabrujuce?

Posto ne mogu iz ove koze, krenuo sam sa citanjem koda. Nakon nekog vremena, slucajno mi je zapao za oko jedan staticki objekat u jednoj od metoda klase o kojoj je rec, podesen da alokalizuje string duzine 1025. Premda vrednost moze da zasmeta verovatno svakom ko ce citati ovak tekst, sve je cisto i objekat deluje beznacajno i ispravno sintaksicki i semanticki. Ipak, prebacio sam vrednost na 1024 i BINGO! Problem je nestao. Od tada radim sta hocu sa tom klasom i van nje bez ikakvih hmm - vidljivih posledica.

Interesuje me sta se zaista desavalo. Ima li neko ideju? Delove source-a jos nisam postovao jer mislim da sam dovoljno dobro opisao situaciju. (Uopsteno - neko ce reci - ali zaista ne vidim nikakvu dodatnu karakteristicnost) Ako zatreba, trazite - mene interesuje uzrok.
Restless one,
 
Odgovor na temu

Mikky

Član broj: 18
Poruke: 1563
*.vdial.verat.net

ICQ: 44582291


+58 Profil

icon Re: Interesantan feljton26.06.2004. u 15:46 - pre 241 meseci
Ovo mi lici na par slicnih problema koje sam ja imao.
Posto su jos uvek aktuelni 32bitni procesori, postoji nesto sto se zove dword alignment. To je preporuka od intela da se svi podaci i kod nalaze na offsetu u memoriji koji je deljiv sa 4 bajta (4bajta = 1 dword). Razlog za to je vec dublja problematika rada 32bitnih procesora i komunikacije sa memorijom, (za uzimanje podataka koji je dword aligned potreban je jedan ciklus, dok za non-aligned dva ili mozda vise) a posledica je optimalan rad i brzina izvrsavanja programa. Dakle kao sto rekoh to je samo preporuka a ne must-do i programi ne bi trebali da se zbog toga ruse vec samo sporije izvrsavaju sto opet niko ne bi primetio osim u nekim teskim petljama. To sto se tvoj program rusi je po mom iskustvu 99.9% kriv windows.

Ja sam konkretno imao problem sa pozivanjem nekih API-ja kada je funkcija uvek vracala error code, iako su svi parametri bili u redu. Znam da je jedan od parametara bio ptr na buffer koji bi fja trebalo da popuni nekim podacima. Posto sam to sve radio u asembleru koristeci lokalne promenljive na stacku verujem da adresa koju sam prosledjivao fji nije bila aligned. Naravno posle par sati lupanja glavom dosao sam na ovu ideju i prosledio pointer na buffer koji je bio dword aligned i viola.... API je proradio. Konkretno radio sam sa API-jima za zezanje sa servisima na NT-u tipa EnumServicesStatus(). Naravno nikakvu poruku ili upozorenje o ovome nisam nasao MSDN. Slican problem sam imao kad sam radio jedan vxd drajver za 9x platformu i takodje neka ring0 funkcija je uvek vracala error, ali kako sam tad bio mlad i neiskusan i nisam znao za ovu foru i na kraju sam sam odustao od celog programa zbog toga.

Ja nemam mnogo iskustva sa C++ klasama i kako kompajleri generisu kod iz njih ali probaj sledece, napravi da ti je velicina svih promenljivih u klasi deljiva sa 4. Npr znas da se program rusi ako ti je onaj niz velicine 1025 bajtova, e pa stavi da je 1028 pa probaj onda. Mada opet mi je cudno sto ti se to desilo, jer koliko znam kompajleri uvek generisu dword aligned podatke, znaci ti kad stavis char a; kompajler ce dodati jos 3 bajta posle toga. Mozda si ukljucio neku code size optimizaciju? Probaj malo da exprimentises pa javi.

E sad mozda sve ovo sto sam rekao i nema nikakve veze ali to je moj the best guess... :)
-I know UNIX, PASCAL, C, FORTRAN,
COBOL, and nineteen other high-tech
words.
 
Odgovor na temu

milanche
San Francisco

Član broj: 2447
Poruke: 1200
*.client.comcast.net



+1001 Profil

icon Re: Interesantan feljton26.06.2004. u 20:12 - pre 241 meseci
Najnezgodniji problemi nastaju zbog tzv. stack corruption, ali se to mnogo cesce
desava na embedded sistemima sa ogranicenom memorijom. Malo mi je cudno da se
desava na Win32.

Mehanizam problema - definises staticki array kao lokalnu promenljivu. Posto je
za stack rezervisan samo fiksni deo memorije, u jednom momentu sekvenca pozvanih
funkcija ucini da stack preraste dodeljenu kvotu i podaci iz stacka pocinju da se upisuju
preko komsijske ograde (najcesce preko heap-a).

Simptomi su najgori moguci - potpuno su liseni svake pravilnosti - jednom se u modulu
XYZ pojavljuju neocekivane vrednosti promenljivih (iz cista mira), a ako dodas malo
koda pa kompajliras, desava se na sasvim drugom mestu. Ako su 'zagadjene' vrednosti
pointeri, sistem obavezno crash-uje.
Najgore od svega - kako dodajes novi kod i kompajliras, u nekom momentu problem
moze da prestane da se ispoljava - jednostavno korupcija stacka pocne da utice na
neki modul koji se u tom trenutku ne koristi uopste, niti ce se za duze koristiti (napr.
neka inicijalizaciona funkcija i sl). Taman mislis da je sve u redu, a onda pocne opet
na nekom drugom mestu).

Resenje - lepo procesljas kod i eliminises sve staticke nizove duze od (recimo)
16 bajtova, i zamenis ih dinamicki alociranim nizovima.

Zanimljiv mi je detalj sa smanjivanjem 1025 na 1024 bajtova - ne mogu da mu dam
jednoznacno znacenje. Moze da govori o vise stvari - unutrasnja procedura alokatora
moze da rezervise memoriju u chunk-ovima od po 1024 pa jedan bajt vise moze da
uzrokuje jedan kb vise nego sto je limit ???
 
Odgovor na temu

ned
Australia

Član broj: 1263
Poruke: 23
*.lowrp1.vic.optusnet.com.au



Profil

icon Re: Interesantan feljton27.06.2004. u 02:20 - pre 241 meseci
Prvo,
Ja sam imao statickih variabla dosta vecih od 1024 bajta bez problema.

Drugo, sto se tice DWORD alignment, to je specificni odgovor u primerima
koji su dati. Tacnije, situacija je da koja bilo struktura je u pitanju, razni elementi te strukture mogu biti na 1 byte, 2 byte, 4 byte, 8 byte ili 16 byte
aligned. To je odluka autora. Ako neka funkcija ocekuje 2 byte alingment
to se mora postovati. Kernel funkcije najcesce ocekuju 4 byte alingment ali to nije zakon. Ako ja stvorim neki drajver za neki hardver, ja odlucim format informacije koja se salje iz User Space u Kernel Space. Ja licno i
odlucim dali ce razni elementi biti na 1 byte aligned ili 4 byte aligned.

Pogredajet dokumentaciju na #pragma pack(n) / #pragma pop()

Sto se tice originalnkog problema, moguce je da je problem sa takozvanim 'stack corruption'.

Drugih problema moze biti ako se ne postuju saveti u vezi konstruktora i destruktora, tj. mora se dobro razumeti sta se desava kad se neki objekat unistava, na primer kad program izlazi.





----------------------------------------
 
Odgovor na temu

Revisor
Sofija - NBGD

Član broj: 18843
Poruke: 59
*.matf.bg.ac.yu

Sajt: alas.matf.bg.ac.yu/~mr021..


Profil

icon Re: Interesantan feljton28.06.2004. u 08:42 - pre 241 meseci
Heh, pod bcc32 sve radi kako treba. Kul.

@ned: ne nije problem u velicini. Sve lepo radi i sa 4096 ali nece sa recimo 1025, 1027, 1028.

Mimo toga, nisam menjao defaulte projekta radi optimizacije. Pa, kada sam poceo da eksperimentisem, po savetu, i sam env se zaboo par puta - aplikacija radi isto. Vratih sve na normalu.

Dakle - kompajler? Ima jos zainteresovanih za nastavak teme?
Restless one,
 
Odgovor na temu

yooyo

Član broj: 4891
Poruke: 1101
*.sbb.co.yu



Profil

icon Re: Interesantan feljton28.06.2004. u 14:15 - pre 241 meseci
Cisto da pitam... da li si instalirao Service Pack 5 za MSVC 6? Ako nisi onda me cudi da ti program uopste radi...

Da bi malo bolje shavtili, da li bi mogao da okacis prost primer koji izaziva padanje... klasa, metoda, staticki parametar u metodi,...

yooyo
 
Odgovor na temu

filmil
Filip Miletić
Oce Technologies B.V., inženjer
hardvera
Arcen, NL

Član broj: 243
Poruke: 2114
*.adsl.zonnet.nl

Jabber: filmil@jabber.org
ICQ: 36601391


+3 Profil

icon Re: Interesantan feljton28.06.2004. u 14:36 - pre 241 meseci
IMHO je promena kompajlera i veličine dotičnog niza samo zabašurila problem koji se sada iz nekog (polu)misterioznog razloga ne ispoljava, ali još uvek čuči i čeka.

Ako metoda gledanja ne da rezultate, korisno je da se stvar ispipa programom tipa BoundsChecker. Greška je skoro sigurno u samom programu.

f
 
Odgovor na temu

Revisor
Sofija - NBGD

Član broj: 18843
Poruke: 59
*.ptt.yu

Sajt: alas.matf.bg.ac.yu/~mr021..


Profil

icon Re: Interesantan feljton29.06.2004. u 23:52 - pre 241 meseci
Citat:
filmil: Greška je skoro sigurno u samom programu.

Da, slazem se. Upravo sam pronasao sta ne stima.

Dakle, i ovde je u pitanju korisnicka greska. Reci cete da sam slagao da sa strane sve stima. Pa, ne: Samo vec danima ne spavam kako treba pa mi izvesne stvari promaknu. Da, spavao sam juce. U pitanju je (zamislite... ;>) objekat cija promena parametara utice na ishod programa. Koga zanima objasnjenje, sledi sledeci pasus.

Ranije, ova greska u kodu je postojala ali sam je u toku testiranja objekta kao zasebnog programa otklonio. Kasnije, zbog jednog Shift+Del + Enter (Yes) koji je nekako zakacio direktorijum sa ovim projektom sam bio primoran da restauiram gotovo kompletan kod (nije stigao sve da obrise dok sam video sta se desava) sa jednog starog backup-a. Nisam mnogo izgubio ali na njemu ova greska nije bila ispravljena a ja sam celo vreme verovao da jeste.

Greska jeste trivijalna i necu joj poklanjati posebnu paznju. Pogledajte attachment i nadjite je u C-Toru objekta.
Restless one,
Prikačeni fajlovi
 
Odgovor na temu

filmil
Filip Miletić
Oce Technologies B.V., inženjer
hardvera
Arcen, NL

Član broj: 243
Poruke: 2114
*.adsl.zonnet.nl

Jabber: filmil@jabber.org
ICQ: 36601391


+3 Profil

icon Re: Interesantan feljton30.06.2004. u 06:59 - pre 241 meseci

Citat:

Greska jeste trivijalna i necu joj poklanjati posebnu paznju.

Pogledajte attachment i nadjite je u C-Toru objekta.



Ipak ne bi bilo loše da joj pokloniš pažnju i objasniš šta nije bilo u

redu, kako bi neko drugi saznao za ovaj propust i u budućnosti ga izbegao.


f


 
Odgovor na temu

Revisor
Sofija - NBGD

Član broj: 18843
Poruke: 59
*.matf.bg.ac.yu

Sajt: alas.matf.bg.ac.yu/~mr021..


Profil

icon Re: Interesantan feljton30.06.2004. u 10:46 - pre 241 meseci
U redu.

Sta je ideja? Treba napraviti objekat koji poseduje niz charova koji ce po potrebi na svom pocetku imati niz znakova konstantnih vrednosti - dakle, predstring koji se najverovatnije nece menjati u toku izvrsenja programa. Kod koji radi nedozvoljenu(e) stvar(i) izgleda ovako:

Code:
TextBoost::TextBoost(
    unsigned int l = 1024,    // velicina buffera
    const char * p = 0        // vrednost predstringa    )
{
    // Alokalizovanje buffera
    tbuff = new char[len = l];    

    // 'Inicijalizovanje' celog buffera na prazan string
    *tbuff = 0;

    // Ako treba da postoji predstring, napisati ga na pocetku
    // tbuff-a, ali postaviti buff tako da pokazuje tik na mesto
    // nakon predstringa. Tu pocinje podbuffer sve potrebne
    // operacije. Uz to, smanjiti promenjivu len tako da nosi
    // efektivnu duzinu stringa.
    if(p)
    {        
        // buff pokazuje na pocetak tbuff
        buff = tbuff;
        
        // p se prepisuje na pocetak tbuff, a pritom se 
        // buff koristi kao kursor, sto ce zadovoljiti uslov
        // da buff pokazuje upravo na poziciju nakon
        // predstringa.
        while(*p)
        {
            *(buff++) = *(p++);
        }


        //!
        // Ovo ne valja. Od len treba oduzeti velicinu predstringa
        // ali to se ovde radi preko kursora *p koji iz prethodne
        // petlje izlazi kao kursor na kraj predstringa tj pokazuje
        // na null.
        len -= strl(p);

        // Pisanje null na kraj predstringa prepisanog u tbuff.
        // tj. pocetak efektivnog dela bafera
        *buff = 0;
    }

    // Ukoliko nema predstringa, buff je isto sto i tbuff.
    else buff = tbuff;

    // Sigurnosno zatvaranje tbuff. Pozeljno jer prilikom rukovanja sa
    // strstream nije tesko zaboraviti dodati null na kraju sesije sto
    // moze da izazove neprijatne padove programa. Null se pise na sam
    // kraj bafera.
    buff[len-1]=0;

    // Kreiranje i inicijalizovanje strstream-a koji treba da vrsi
    // operacije nad baferom. Treba primetiti da se strstream-u ne
    // daje kontrola nad null-om upisanim u prethodnom koraku.
    mae = new strstream(buff, len-1);

    //!
    // len nije umanjen za 1.
}


Savet: Sto je moguce redje menjati ulazne parametre f-je, sem ako niste potpuno sigurni da promene nece nezeljeno afektovati dalji rad programa.

Posto je objekat trenutno pravljen za mene, nisam handlovao sledece slucajeve:

Napomena 1: Realna velicina bafera nije eksplicitno zadata ni parametrima konstruktora. (umanjuje se za jos jedan u toku inicijalizacije) Postoji method size() preko koga se moze procitati len. Njega sam bio obrisao u attachmentu radi preglednosti.

Napomena 2: Sasvim je moguce da korisnik unese podstring veci od samog buffera. (!)

Napomene 1,2: Fleksibilnije bi bilo da pravi buffer bude prosiren za duzinu podstringa plus jedan karakter sto bi znacilo da korisnik zadaje upravo duzinu bafera koji ce i koristiti u toku izvrsenja programa.


Ispravniji kod bi bio:
Code:
TextBoost::TextBoost( unsigned int l = 1024, const char * p = 0 )
{
    tbuff = new char[len = l];    
    *tbuff = 0;

    if(p)
    {        
        buff = tbuff;
        
        const char * q = p;    // +
        while(*q)
        {
            *(buff++) = *(q++);
        }

        len -= strl(p);

        *buff = 0;
    }
    else buff = tbuff;

    buff[len-1]=0;
    mae = new strstream(buff, len-1);

    len--;                    // +
}

Restless one,
 
Odgovor na temu

[es] :: C/C++ programiranje :: Interesantan feljton

[ Pregleda: 1999 | Odgovora: 9 ] > FB > Twit

Postavi temu Odgovori

Navigacija
Lista poslednjih: 16, 32, 64, 128 poruka.