Segfault, drzewo obiektów, delete zwalnia pamięc.

0

Mam problem - piszę mini system kontroli wersji , w skrócie klasa Repository zawiera liste klas Revision, klasa Revision zawiera map<string, Patch*> który mapuje na nazwe pliku na patch.
Patch dziedziczy z File, File posiada vector<string> lines.

Tutaj wszystkie pliki, sory że w ten sposób , ale nie jestem w stanie odseparowć błedu od reszty kodu i zlokalizować gdzie powstaje:

main.cpp
file.h
repo.h
file.cpp
repo.cpp
revision.h
revision.cpp
LongestCommonSubsequence.hpp

Cała logika wydaję sie ok, zablokowałem copy constructor jako explicit, wszystkie obiekty tworzę przez new, ale mimo to coś kasuje ktorys z obiektow i program segfaultuje w tym momencie(linia 200 file.cpp):

void File::add_line(string line) {
    lines.push_back(line);
}

valgrind pokazuje takie info na temat błedu:

==29517== Invalid read of size 8
==29517==    at 0x405678: std::vector<std::string, std::allocator<std::string> >::push_back(std::string const&) (stl_vector.h:735)
==29517==    by 0x4037AE: File::add_line(std::string) (file.cpp:200)
==29517==    by 0x40C32C: Revision::parse() (revision.cpp:35)
==29517==    by 0x40944D: Repo::load() (repo.cpp:65)
==29517==    by 0x409818: Repo::commit(std::string) (repo.cpp:109)
==29517==    by 0x40970E: Repo::commit() (repo.cpp:97)
==29517==    by 0x408997: main (main.cpp:58)
==29517==  Address 0x10 is not stack'd, malloc'd or (recently) free'd
==29517== 
==29517== 
==29517== Process terminating with default action of signal 11 (SIGSEGV)
==29517==  Access not within mapped region at address 0x10
==29517==    at 0x405678: std::vector<std::string, std::allocator<std::string> >::push_back(std::string const&) (stl_vector.h:735)
==29517==    by 0x4037AE: File::add_line(std::string) (file.cpp:200)
==29517==    by 0x40C32C: Revision::parse() (revision.cpp:35)
==29517==    by 0x40944D: Repo::load() (repo.cpp:65)
==29517==    by 0x409818: Repo::commit(std::string) (repo.cpp:109)
==29517==    by 0x40970E: Repo::commit() (repo.cpp:97)
==29517==    by 0x408997: main (main.cpp:58)

valgrind wskazuje na miejsce gdzie robię push_back, ale skoro metoda add_line dostaje stringa przez wartość, to co może powodować ŧe tego stringa już nie ma? Albo na której linijce z backtrace'u robię jakiś inny bład?

0
void Revision::parse() 
{
	(...)

	else 
	{
		patch_per_file_map[fname]->type();
		patch_per_file_map[fname]->add_line(tmp); //<--- !!!
	}
		
	(...)
}

Jesteś pewny, że zawsze odwołujesz się do uprzednio stworzonego obiektu Patch?

0

Raczej tak - jest możliwe żeby obiekt nie został stworzony ale gdy plik repo.vcs który teraz wyglada tak
nazw pliku]
content1
content2

nie będzie zawierał lini z nazwą pliku potem będę to sprawdzał ale na razie chcę żeby program działał w podstawowej wersji, w tym konkretnym przypadku na którym segfaultuje sprawdziłem pod debuggerem i patch jest tworzony chwilę wcześniej

w liniach:

                patch_per_file_map.insert(pair<string, Patch*>(fname, new Patch(fname)));
                //patch_per_file_map[fname] = new Patch(fname);

Nie wiem co mówi standard o relokacji pointerów na Patch* czy samych obiektów Patch, ale w przypadku list<Patch*> takie coś działało

zeby sprawdizc czy mam dobry obiekt specjalnie stworzyłem Patch::type() która robi couta z informacja ze to jest obiekt patch. Próbowałem jeszce czegos takiego:

            for(int i=0;i<20000;i++) {
                int* fill=new int[i];
                delete[] fill;
            }
    patch_per_file_map[fname]->type();
            for(int i=0;i<20000;i++) {
                int* fill=new int[i];
                delete[] fill;
            }
    patch_per_file_map[fname]->add_line(tmp);

alokuję pamiec blokami o roznych rozmiarach i kasuje je od razu, żeby od razu wypełnić miejsce ostatnio skasowanych obiektów a nie czekać z tym do błedu, wcześniej takie coś pomagało i valgrind od razu pomagał zlokalizować bład..
patch_per_file_map[fname]->type(); działa zawsze
a na
patch_per_file_map[fname]->add_line(tmp); zawsze jest bład

EDIT: plik repo.cpp wcześniej byl zły link:

0

Raczej tak

To 'raczej' średnio mi tu pasuje. Zamiast operatora [] użyj metody find, wtedy na 100% będziesz wiedział czy dodałeś wcześniej ten obiekt.

patch_per_file_map[fname]->type(); działa zawsze

Działa zawsze, bo type nic nie robi z obiektem, w przeciwieństwie do add_line.

0

ok miałes racje :)
nie wchodzi w if(patch_per_file_map.find(fname)!=patch_per_file_map.end())
tylko dlaczego obiekt Patch nie jest tworzony? bo na pewno linia

patch_per_file_map.insert(pair<string, Patch*>(fname, new Patch(fname)));

się wykonuje w wcześniejszym przejściu przez while
próbowałem też tak:

patch_per_file_map[fname] = new Patch(fname);

edit: jednak się nie wykonuje, code::block dziwnie się zachowuje przy kodzie:
if(!fname.compare("x")) {
//patch_per_file_map.insert(pair<string, Patch*>(fname, new Patch(fname)));
patch_per_file_map[fname] = new Patch(fname);
cout << "fname: " << fname << endl;
}
po naciśnięicu F7(next line) przeskakuje od razu do lini z coutem, a zawsze gdy if sie nie powinien wykonać przeskakiwał od razu za niego albo do pierwszej linijki else..

0

(...) tylko dlaczego obiekt Patch nie jest tworzony?

Tego nie wiem. W sumie masz tak skonstruowaną pętle, że wystarczą złe dane wejściowe, żeby wszystko poszło w diabły:

if(!iter->find("[[[")) //<--- (1)
{
	fname = iter->substr(3, iter->size() - 6); //<--- (2)

	if(!fname.compare("x")) //<--- (3)
	{
		patch_per_file_map.insert(pair<string, Patch*>(fname, new Patch(fname)));
	}
} 
else 
{
	patch_per_file_map[fname]->add_line(tmp); //<--- (4)
}

Załóżmy, że warunek (3) nie zostanie spełniony, następny obrót pętli będzie się odwoływał (4) do obiektu, który nie istnieje, ponieważ klucz fname został wcześniej zmieniony (2). Dodatkowo, jeśli przed 'b>. To samo z końcem, jeśli po '<i>]' będzie jakaś spacja, wtedy fname będzie zawierać niepoprawny string (2). Na a wtedy przy zapisie 'x] ' warunek (3) nie zostanie spełniony, a powinien.

0

Tak jak dopisałem we wczesniejszym poście była to wina tego ifa:

 if(!fname.compare("x"))

Teraz na 100% do ifa wchodzi(sprawdziłem) ale dalej coś nie tak z tym map<string,Patch*>.
Czy ta linijka (1) jest na pewno prawidłowa i w 100% odporna na ewentualne relokacje pamięci ?
Bo linijka (2) która powinna wyrzucić na stdout co dopiero wypełnioną zawartosc Patcha nie zwraca nic, niezależnie od tego czy wstawiam tak (3) czy tak: (4) a zmienna tmp na tym etapie posiada wstawianą linie, sprawdziłem.

void Revision::parse() {
    string fname;
    list<string>::iterator iter = unparsed_lines.begin();
    //cout << "before";
    //while(iter!=unparsed_lines.end()) {
    //cout << *iter++ << endl;
    //}
    //cout << "after";

    while(iter!=unparsed_lines.end()) {
        string tmp = *iter;
        cout << tmp << endl;
        if(!iter->find("[[[")) {
            fname = iter->substr(3, iter->size()-6);
            //cout << fname.compare("x") << endl;
            if(fname.compare("x")) {
                patch_per_file_map.insert(make_pair(fname, new Patch(fname))); //(1)
                for(int i=1;i<4;i++)
                //patch_per_file_map[fname] = new Patch(fname);
                cout << "fname: " << fname << endl;
            }
            else {
                patch_per_file_map.find(fname)->second->print(); //(2)
                cout << flush;

            }

        }

        else {

            if(patch_per_file_map.find(fname)!=patch_per_file_map.end()) {
                cout << "add line" << tmp << endl;
                patch_per_file_map.find(fname)->second->lines.push_back(tmp); //(3)
                //patch_per_file_map[fname]->lines.push_back(tmp);
                //patch_per_file_map[fname]->add_line(tmp); //(4)
            }
            else
                cout << "patch file not exists" << endl;
        }
        ++iter;
    }
    patch_per_file_map["d2.txt"]->print();
    cout << endl;

}
0

Czy ta linijka (1) jest na pewno prawidłowa i w 100% odporna na ewentualne relokacje pamięci ?

Tu nie ma żadnej relokacji pamięci, zwykłe przepisanie wskaźnika.

Może pokaż na jakich danych wejściowych to testujesz.

0

Nie chodziło mi o relokacje w tej konkretną linijke, tylko ocały cykl programu, miałem duzy problem z przetwarzaniem danych z objektów File i Patch. gdy testowalem samo patchowanie lub samo liczenie diffa wszystko było ok, ale gdy w tym samym programie tworzyłem dużo obiektów w innych miejscach pamieci coś nadpisywało wewnetrzne dane files/patch. Pomimo tego że na File::lines(vector<Chunk*>) robiłem tylko push_backi i iteratora uzywałem do odczytu, gdy zmieniłem vector na list wszystko zaczęło działac niezaleznie od sytuacji. Map z tego co czytałem są reprezentowane jako binary tree czyli insert nie powinien nic popsuć pod warunkiem że ten binary tree nie będzie optymalizowany przez zmienianie połozenia elementow w celu szybszego dostepu a nie wiem czy tak sie dzieje w STL.

Dane wejściowe:

[[[[[rev1]]]]]
[[[d2.txt]]]
+++:1
+++:2
+++:3
+++:4
+++:5
+++:6
[[[x]]]
[[[d1.txt]]]
+++:a
+++:b
+++:c
+++:d
+++:e
+++:f
[[[x]]]
[[[[[-]]]]]

0

Bo linijka (2) która powinna wyrzucić na stdout co dopiero wypełnioną zawartosc Patcha nie zwraca nic (...)

A co ma zwrócić, jeśli odwołujesz się do obiektu kluczu 'x', który nie istnieje, zamiast odwołać się do 'd2.txt' - fname w linii (2) zawsze będzie zawierać klucz 'x'.

0

super dzięki :) dziala teraz całość jak powinna, nie przypuszczalem że tyle sie może dziać z powodu błedów w logice programu.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.