Segmentation fault bei großen Verzeichnis



  • Hallo,

    Ich habe folgendes Programm, was durch einen Dateibaum geht, alle Pfade einsammelt und dann mit einem stat() die Informationen in eine Mysql-DB schreibt.

    Allerdings kriege ich bei großen Bäumen ein Segmentation fault. Da ich zu Anfang mit einer rekursivem Verzeichnis-Walker gearbeitet hab, dachte ich der Stack wäre zu voll. Deswegen habe ich die Rekursion aufgelöst und mache das ganze jetzt in einer queue.

    Nur woher kommt bei großen Verzeichnisbäumen jetzt der Segmentation fault?

    #include <map>
    #include <set>
    #include <queue>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <dirent.h>
    #include <iostream>
    #include <boost/algorithm/string.hpp>
    #include <locale>
    #include <mysql/mysql.h>
    #include <iostream>
    #include <sstream>
    #include <string.h>
    
    #include "DbWriter.h"
    
    using namespace std;
    
    typedef multimap<string, string> StringStringMap;
    typedef map<string, int> StringIntMap;
    
    StringStringMap files;
    StringIntMap fileCount;
    map<string,off64_t> fileCountSize;
    set<string> keys;
    
    /* todo: alles kleinschreiben */
    string getFileExtension(string name)
    {
    	//zieht Endung heraus
    	string temp = name.substr(name.rfind(".")+1, name.size());
    
    	//gibt es kleingeschrieben zurueck 
    	return temp; // string(	strlwr(temp.c_str())   );
    }
    
    void parseDir(string path)
    {
    	DIR *dir = NULL;// = opendir(path.c_str());
    	struct dirent *entry = NULL;// = readdir(dir);
    	string fileExt = "";
    	string current_path;
    	queue<string> folders;
    	folders.push(path);
    
    	while(!folders.empty())
    	{
    		current_path = folders.front();
    		folders.pop();
    		dir = opendir(current_path.c_str());
    		entry = readdir(dir);
    
    		while(entry != NULL)
    		{
    
    			if(string(entry->d_name) != ".." && string(entry->d_name) != ".")
    			{
    				//cout << "\t" << string(entry->d_name) << endl;
    				switch(entry->d_type)
    				{
    					case DT_DIR: 
    						folders.push(current_path+string(entry->d_name)+"/");
    						//parseDir(path+string(entry->d_name)+"/");
    						break;
    					case DT_REG:
    						fileExt = getFileExtension(string(entry->d_name));
    						files.insert(pair <string, string>(fileExt, current_path + string(entry->d_name)));
    						fileCount[fileExt]++;
    						keys.insert(fileExt);
    						break;
    					case DT_UNKNOWN:cout << "UNKNOWN: "  << string(entry->d_name) << endl; break;
    					case DT_FIFO: 	cout << "A Named pope: " << string(entry->d_name) << endl; break;
    					case DT_SOCK:   cout << "A local-domain socket: " << string(entry->d_name) << endl; break;
    					case DT_CHR: 	cout << "A character device: " << string(entry->d_name) << endl; break;
    					case DT_BLK: 	cout << "A block device: " << string(entry->d_name) << endl; break;
    					default: 		cout << "DEFAULT: " << string(entry->d_name) << endl; break;; 
    				}
    			}
    			entry = readdir(dir);
    		}
    
    	}
    
    }
    
    void writeFileStatToDb(string key)
    {
    	struct stat64 s;
    	string name;
    	for(StringStringMap::iterator i = files.find(key); i!= files.upper_bound(key); i++)
    	{
    		name = string(i->second);
    		stat64(name.c_str(), &s);
    		fileCountSize[key]+= s.st_size;
    		importStatStruct(name, &s);
    	}
    }
    
    int main(int argc, char *argv[])
    {
    	if(argc == 1)
    	{
    		cout << "Usage: " << argv[0] << " path1 path2 .. pathN" << endl;
    		return -1;
    	}
    
    	string currentPath="";
    
    	for(int i=1; i < argc; i++)
    	{
    		currentPath = string(argv[i]);
    
    		if(currentPath.substr(currentPath.length()-1, currentPath.length()+1) != "/")
    			currentPath += "/";
    
    		cout << "parsing: " << currentPath << endl;
    		parseDir(currentPath);
    
    	}
    
    	switch(connect())
    	{
    		case INITERROR: 
    			cout << "Cannot initialize mysql" << endl;
    			return INITERROR;
    
    		case CONNECTERROR:
    			cout << "Cannot connect to mysql_db" << endl;
    			return CONNECTERROR;
    
    	}
    
    	deleteFileTable();
    	deleteFileStatTable();
    
    	for(set<string>::iterator it = keys.begin(); it != keys.end(); it++)
    		writeFileStatToDb(*it);
    
    	importFileStatStruct(fileCount, fileCountSize);
    
    	disconnect();
    
    	return 0;	
    
    }
    

    Die DbWriter.h steht hier zur Verfügung:
    http://nopaste.info/e6df8a782e.html (Ist denke ich nicht von belang)

    Danke schonmal für die Hilfe!



  • Nur woher kommt bei großen Verzeichnisbäumen jetzt der Segmentation fault?

    Das verrät dir der Debugger.



  • Könnte es nicht einfach sein, dass eine Map oder MultiMap einfach ab einer gewissen größe Probleme bekommt?



  • ocean_driver schrieb:

    Könnte es nicht einfach sein, dass eine Map oder MultiMap einfach ab einer gewissen größe Probleme bekommt?

    Unwahrscheinlich. Frag den Debugger.



  • Da ich noch relativ neu im C-Geschäft bin: Wie benutze ich den Debugger?

    Programmiere unter Linux mit g++ auf der Konsole



  • Den Debugger startest du mit gdb deinprogramm
    Wichtige Befehle sind start , continue (kurz: c) und nach dem segmentation fault: backtrace (kurz: bt).
    Das sollte für deinen konkreten Fall reichen, mehr findest du unter man gdb und natürlich Google.



  • Danke für den Ansatz!

    Bin jetzt ein bisschen weiter.
    Der Debugger meldet folgendes:

    [Thread debugging using libthread_db enabled]
    parsing: analyseFileDir/
    [New Thread 0xb7ad88d0 (LWP 21048)]

    Program received signal SIGSEGV, Segmentation fault.
    [Switching to Thread 0xb7ad88d0 (LWP 21048)]
    0xb7be2725 in readdir () from /lib/tls/i686/cmov/libc.so.6
    (gdb) bt
    #0 0xb7be2725 in readdir () from /lib/tls/i686/cmov/libc.so.6
    #1 0x080498d5 in parseDir ()
    #2 0x0804aff2 in main ()

    Also wird entry = readdir(dir); den Fehler verursachen.
    Nur ich verstehe nicht warum?

    Ich habe diesen Befehl zweimal im Programm:

    Einmal wenn ich gerade neu in einem Verzeichnis bin. Dort muss aber doch alles stimmen, weil ich durch !folders.empty() garantiere, dass ein pfad in folders steht. und den benutzte ich um den ersten Eintrag des neuen Verzeichnises auszulesen. Dann habe ich ja den anderen Aufruf, der in der while(entry != NULL) Schleife steht. Aber was soll der schon für einen Fehler verursachen? Wenn der durch ist, wrid entry ja NULL womit die Schleife nicht noch einmal iteriert wird.
    Ich habe in die äußere Schleife noch ein closedir(dir) hinzugefügt, dass wurde vorher nicht ordentlich von mir geschlossen. Das hat mich leider nicht weiter gebracht. Wie gehe ich jetzt am besten vor?

    Danke!



  • Ich bins nochmal. Ich habe durch googlen folgenden Eintrag gefunden:

    http://sourceware.org/ml/glibc-bugs/2009-02/msg00014.html

    Anscheinend passiert dass, wenn opendir() NULL zurückliefert (was ja auch Sinn macht). Ich hab, da jetzt ne while-schleife rumgepackt um im NULL-Fall, das nächste Verzeichnis zu nutzen:

    do
    		{
    			current_path = folders.front();
    			folders.pop();
    			dir = opendir(current_path.c_str());
    		}
    		while(dir == NULL);
    

    Ich denke es läuft jetzt. Danke!



  • Noch ein Nachtrag: wenn du am Debuggen bist, solltest du dein Programm mit Debuginformationen kompilieren (Schalter: -g), damit bekommst du im Backtrace auch Zeilenangaben und Funktionsparameter zu sehen.
    Die meisten anderen Features des Debuggers benötigen ebenfalls Debuginformationen.


Anmelden zum Antworten