Unerklärlicher Fehler in meinem Programm - wer kann helfen?



  • Hallo zusammen.

    Ich suche jetzt schon seit 2 Wochen an einem Fehler in meinem Programm und finde einfach nichts. Ich habe schon alle erdenklichen Debugging-Schritte und Abstraktionen versucht, doch nichts hat geholfen. Ich konnte das Programm jetzt so stark vom Code her abstrahieren, dass ich den Code nun posten kann, da er von MySQL-Datenbanken und sonstiger Umgebung unabhängig ist. Dabei habe ich alle Werte/Intervalle etc exakt so gesetzt, dass der Fehler so schnell wie möglich kommt.

    Der Fehler lautet "Speicherzugriffsverletzung" und tritt willkürlich, jedoch generell innerhalb von wenigen Sekunden auf.

    Ich verwende Linux und den GCC Compiler. Die Packages libcurl und pthread werden verwendet.

    Das Programm hat im Grunde folgende Aufgabe:
    - Alle 60 (hier 5) Sekunden einen Thread pwb_neuer_durchlauf(). Dieser garantiert, dass der Abstand genau 60 Sekunden ist.
    - Dieser Thread ruft alle Server aus einer MySQL-Datenbank ab (hier nicht) und startet pro Eintrag einen Thread pwb_checking().
    - pwb_checking() ruft die Webseite (übergebener Parameter) auf und schaut per http_call(), ob diese Erreichbar ist und schreibt seine Erkenntnisse in die Datenbank (hier nicht).
    - In meinem Originalsource habe ich auch bereits versucht, alle pwb_checking() Threads zu joinen, da ich die Vermutung hatte, dass durch das Beenden von pwb_neuer_durchlauf() irgendwelche Ressourcen von pwb_checking() wieder freigegeben werden. Auch keinen Erfolg.

    Es folgt mein sehr stark abstrahierter und fehlererhaltender Code. Könnt ihr den Fehler reproduzieren oder mir Tipps geben? Bitte um Hilfe, ich verzweifel langsam daran...

    Gruß
    blackdrake

    #include <stdlib.h> // free, malloc, atoi, exit, EXIT_SUCCESS, EXIT_FAILURE, setenv
    #include <pthread.h> // pthread_attr_init, PTHREAD_CREATE_DETACHED, pthread_attr_setdetachstate, pthread_create, pthread_attr_destroy
    #include <signal.h>
    #include <curl/curl.h>
    #include <string.h>
    
    #ifdef __cplusplus
    #include <unistd.h> // sleep()
    #endif
    
    #ifndef __cplusplus
    #include <stdbool.h> // Typ "bool"
    #endif
    
    struct meinserver {
    	uint	id;
    	char*	url;
    	int		downtime;
    };
    
    static void async_function_call( void* (*start_routine)(void*), void* arg ) {
    	pthread_t mythread;
    
    	// Zu Testzwecken nicht Detached
    
    	int rc = pthread_create(&mythread, NULL, start_routine, arg);
    
    	if (rc != 0) {
    		printf("pthread_create: '%d'\n", rc);
    	}
    
    	return;
    }
    
    //********************************************************************************************
    //
    // OFFIZIELLER CODE VON CURL, hier kann kein Fehler drinnen sein
    
    #include <curl/types.h>
    #include <curl/easy.h>
    
    struct MemoryStruct {
    	char *memory;
    	size_t size;
    };
    
    static void *myrealloc(void *ptr, size_t size)
    {
    	/* There might be a realloc() out there that doesn't like reallocing
    	   NULL pointers, so we take care of it here */
    	if(ptr)
    		return realloc(ptr, size);
    	else
    		return malloc(size);
    }
    
    static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
    {
    	size_t realsize = size * nmemb;
    	struct MemoryStruct *mem = (struct MemoryStruct *)data;
    
    	#ifndef __cplusplus
    	mem->memory = myrealloc(mem->memory, mem->size + realsize + 1);
    	#else
    	mem->memory = (char*) myrealloc(mem->memory, mem->size + realsize + 1);
    	#endif
    
    	if (mem->memory) {
    		memcpy(&(mem->memory[mem->size]), ptr, realsize);
    		mem->size += realsize;
    		mem->memory[mem->size] = 0;
    	}
    
    	return realsize;
    }
    
    // *** ENDE CURL CODE ***
    
    // Code von einem offiziellen Example abgeleitet, Fehler unwahrscheinlich
    static bool http_call (const char* url) {
    	printf("Calling URL: '%s'\n", url);
    
    	CURL *curl;
    
    	curl = curl_easy_init();
    	if (curl) {
    
    		// URL übergeben
    		curl_easy_setopt(curl, CURLOPT_URL, url);
    
    		// Pufferoptionen
    		char errorBuffer[CURL_ERROR_SIZE];
    		curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);
    
    		struct MemoryStruct chunk;
    		chunk.memory = NULL;
    		chunk.size = 0;
    		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    		curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); // Der Cast ist für'n Arsch
    
    		// Benutzerdefinierter User-Agent-Name
    		curl_easy_setopt(curl, CURLOPT_USERAGENT, "Hello World Browser");
    
    		// Einen Timeout setzen
    		curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1);
    
    		// Einen HTTP-Fehler-Statuscode beachten
    		curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
    
    		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, true);
    		curl_easy_setopt(curl, CURLOPT_DNS_USE_GLOBAL_CACHE, false);
    
    		// Abfrage ausführen und Resultat speichern
    		CURLcode res;
    
    		res = curl_easy_perform(curl); // !! Wäre  diese Zeile weg, käme der Fehler nicht. Aber: http_call() funktioniert alleine korrekt.
    
    		char *buffer = chunk.memory; // Alias
    		printf(buffer);
    		printf("\n");
    		fflush(stdout);
    
    		// Clean up
    		curl_easy_cleanup(curl);
    
    		// Alles OK?
    		if (res == CURLE_OK)
    		{
    			printf("URL successfully entered.\n");
    
    			if (buffer != "\0") {
    				printf("There was an output (see next line).\n");
    				printf(buffer); // Bei snprintf würde der Puffer sonst platzen
    				printf("\n");
    			} else {
    				printf("There was no output.\n");
    			}
    			return true;
    		}
    		else
    		{
    			printf("Error while calling URL '%s': [%d] - '%s'\n", url, res, errorBuffer);
    			return false;
    		}
    	} else {
    		return false;
    	}
    }
    
    static char* strcombine(const char* str1, const char* str2) {
    	#ifndef __cplusplus
    	char *newstr = malloc(strlen(str1)+strlen(str2)+1);
    	#else
    	char *newstr = (char*)malloc(strlen(str1)+strlen(str2)+1);
    	#endif
    
    	if (newstr == NULL) {
    		printf("Malloc-Error at strcombine()!\n");
    		return NULL;
    	}
    
    	memcpy(newstr, str1, strlen(str1)+1);
    	strcat(newstr, str2);
    
    	return newstr;
    }
    
    const char*		CHECKING_APPENDIX = "startchecking.php";
    
    static void* pwb_checking(void* data) {
    	// Lese die übergebene Struktur aus
    	struct meinserver *k = (struct meinserver*)data;
    
    	printf("Start checking of system #%d\n", k->id);
    
    	char* completeurl = strcombine(k->url, CHECKING_APPENDIX);
    
    	if (completeurl == NULL) {
    		return NULL;
    	}
    
    	printf(completeurl);
    	printf("\n");
    	fflush(stdout);
    
    	if (http_call(completeurl)) {
    		printf("HTTP CALL OK\n");
    	} else {
    		printf("HTTP CALL BAD\n");
    	}
    
    	free(completeurl); // malloc() von strcombine() wieder freigeben
    	free(k->url); // malloc() von strclone() wieder freigeben
    
    	printf("Stop checking of system #%d\n", k->id);
    
    	free(k); // malloc() von pwb_neuer_durchlauf() wieder freigeben
    
    	return NULL;
    }
    
    static char* strclone(const char* str) {
    	char *newstr = malloc(strlen(str)+1);
    
    	if (newstr == NULL) {
    		printf("Malloc-Error at strclone()!\n");
    		return NULL;
    	}
    
    	memcpy(newstr, str, strlen(str)+1);
    
    	return newstr;
    }
    
    static void* pwb_neuer_durchlauf(void* data) {
    
    	// Zu Demonstrationszwecken stark abstrahiert, Fehlererhaltend
    	// Die Werte "Fake-Test" und "2000" kommen eigentlich aus einer DB, sind also lokale Variablen in Form von MYSQL_ROW row[...], werden aber korrekt in die Struktur k kopiert, nicht referenziert.
    
    	int i;
    	for (i = 0; i<10; i++) {
    		struct meinserver *k = malloc(sizeof *k);
    
    		if (k == NULL) {
    			printf("Malloc-Error at pwb_neuer_durchlauf()!\n");
    			return NULL;
    		}
    
    		k->id = i;
    		k->url = strclone("http://www.viathinksoft.de/fake_test/"); // String einzigartig machen, damit der Pointer nicht ungültig wird, wenn die Funktion zuende ist
    
    		if (k->url == NULL) {
    			return NULL;
    		}
    
    		k->downtime = atoi("2000");
    
    		async_function_call(pwb_checking, k); // !! Wäre hier ein serieller statt asynchroner Aufruf, dann wäre kein Fehler... wieso?
    	}
    
    	// In diesem Beispiel kein Join der Threads pwb_checking()
    
    	return NULL;
    }
    
    int main(void) {
    	while (true) {
    		printf("-----------------------\n");
    		async_function_call(pwb_neuer_durchlauf, NULL);
    		sleep(5);
    	}
    
    	return EXIT_SUCCESS;
    }
    




  • curlnichtthreadsafe? schrieb:

    was in diese richtung vielleicht?
    http://curl.haxx.se/mail/lib-2007-04/0128.html
    http://curl.haxx.se/libcurl/c/libcurl-tutorial.html#Multi-threading

    Nö, wieso?

    libcurl is completely thread safe, except for two issues ...

    Wie ja oben zu sehen ist, share ich keine Handles oder ähnliches.


Anmelden zum Antworten