Probleme mit Speicher-Bereinigung nach Programmende

29/05/2009 - 13:55 von Andreas W. Wylach | Report spam
Hallo zusammen,

ich arbeite derzeit an einem Projekt, welches ich mal vor einiger Zeit
angefangen hatte
und vor kurzem wieder aufgenommen habe. Prinzipiell bin ich fast durch
und optimiere nun einige Stellen bzw baue einiges um, damit ich fuer
Erweiterungen besser geruestet bin.

Es handelt sich um eine Metasuche mit Post-Processing Algorithmen
(Fuzzy matching und Clustering) und zukuenftig auch visuell grafisch
dargestellte Suchergebnisse, also nicht nur eine "dumme" Metasuche.
Das komplette Projekt ist in C programmiert, einer der Cluster-
Algorithmen ist in Java programmiert und wird mittels JNI
angetriggert. Wiegesagt, klappt alles gut und vor allem sehr schnell
(dank C). Meine Plattform ist Debian Etch, glibc 2.3.6 und gcc 4.1.1.
Soviel grundsaetzlich.

Nun zum Problem: Zur Optimierung und Verbesserung einiger
Programmteile verwende ich Valgrind und dort tauchen nun einige
Ausgaben auf, wo ich keine Idee habe, wie ich dem Problem beikommen
kann.

1.) Nachdem die angefragten Suchmaschinen geantwortet haben werden die
heruntengeladenen HTML-Seiten mittels regex geparsed und die
Ergebnisse in eine XML Datei gespeichert (mit libxml2). Mittels xpath
lese ich die XML Datei, bin damit auch in der Lage nur einzelne
Suchmaschinen auszulesen.
Die einzelnen Nodes werden in einem Loop herausgelesen, und
anschliessend
in einen Binaerbaum eingelesen. Dann wird der Baum anhand eines
Arrays mit Zeigern zu den Nodes ausbalanciert. Eine Treffer-Score wird
errechnet und noch ein paar weitere Funktionen ausgefuehrt sowie
schlussendlich sortiert (Score, Titel oder URL). Anschliessend ist die
Liste fertig, kann angezeigt oder an die Post-Processing Algorithmen
weitergereicht werden.

Hier ist die Routine, welche die gelesenen XML Nodes in den Binaerbaum
einfuegt:
(Hinweis: Ich bin es gewohnt, meinen Source immer in englisch zu
kommentieren)

// parse xml searchresult file and construct result linktree
Linktree **get_searchresult(int *arraysize, xmlDocPtr doc, MS_common
*MS) {
Linktree *root = NULL, **linkarray;
int linkarraysize = 0;
xmlXPathContextPtr xpathCtx;
xmlXPathObjectPtr xpathObj;
xmlDocPtr ms_results_xml_doc = NULL;

xmlChar *xpathExpr = (xmlChar *)construct_xpath_command(MS);

xmlNodePtr node = NULL;
xmlAttrPtr attr_ptr;
xmlNodeSetPtr nodes = NULL;
int xml_result_size;
int i;

char *xml_title = NULL;
char *xml_url = NULL;
char *xml_real_url = NULL;
char *xml_snippet = NULL;
double xml_score = 0;
int xml_source_id = 0;
int xml_doc_id = 0;

int ref_cnt[ get_num_available_engines() ];

char xml_file[512];
sprintf(xml_file, "%s/%s.xml", APP_XML_PATH, MS->active_session);

// parse search results from xml document
ms_results_xml_doc = xml_parse_searchresult_document(xml_file);

// error if null
assert( ms_results_xml_doc );

// Create xpath evaluation context
xpathCtx = xmlXPathNewContext( ms_results_xml_doc );
if (xpathCtx == NULL) {
fprintf(stderr,"Error: unable to create new XPath context");
xmlFreeDoc( ms_results_xml_doc );
xfree( xpathExpr );

return NULL;
}

// Evaluate xpath expression
xpathObj = xmlXPathEvalExpression( xpathExpr, xpathCtx );
if (xpathObj == NULL) {
fprintf(stderr,"Error: unable to evaluate xpath expression \"%s
\"", xpathExpr);
xmlXPathFreeContext(xpathCtx);
xmlFreeDoc( ms_results_xml_doc );
xfree( xpathExpr );

return NULL;
}
xml_result_size = (xpathObj->nodesetval) ? xpathObj->nodesetval-

nodeNr : 0;



// check if there's a xml result with xpath cmd, return NULL
of none
if (xml_result_size <= 0)
return NULL;

// set state for parsing xpath results .. a simple state
machine
state = _res_start;

// init references count array for each search engine
for (i = 0; i < get_num_available_engines(); i++) {
ref_cnt[ i ] = 0;
}

// get xpath queried nodes ...
for (i = 0; i < xml_result_size; i++){
assert(xpathObj->nodesetval->nodeTab[i]);

node = xpathObj->nodesetval->nodeTab[i]->children;

while ( node ) {

if (! xmlIsBlankNode(node)){

if ( state == _res_start ) {
attr_ptr = xpathObj->nodesetval->nodeTab[i]-

parent->properties;


if (attr_ptr != NULL) {
if (attr_ptr->children != NULL) {
if ((xmlStrcasecmp((xmlChar *)attr_ptr-

name , (xmlChar *)"id") == 0)) {


xml_doc_id = (int)atoi((char *)
attr_ptr->children->content);
}
}
}
}
if ((xmlStrcasecmp((xmlChar *)xpathObj->nodesetval-

nodeTab[i]->name,


(xmlChar *)"title") == 0)) {
xml_title = (char*)xmalloc((strlen(
(char *)node->content)+1) * sizeof(char));
strcpy(xml_title, (char *)node->content);
}
if ((xmlStrcasecmp((xmlChar *)xpathObj->nodesetval-

nodeTab[i]->name,


(xmlChar *)"snippet") == 0)) {
xml_snippet = (char*)xmalloc((strlen(
(char *)node->content)+1) * sizeof(char));
strcpy(xml_snippet, (char *)node->content);
}
if ((xmlStrcasecmp((xmlChar *)xpathObj->nodesetval-

nodeTab[i]->name,


(xmlChar *)"url") == 0)) {
xml_url = (char*)xmalloc((strlen(
(char *)node->content)+1) * sizeof(char));
strcpy(xml_url, (char *)node->content);
}
if ((xmlStrcasecmp((xmlChar *)xpathObj->nodesetval-

nodeTab[i]->name,


(xmlChar *)"real_url") == 0)) {
xml_real_url = (char*)xmalloc((strlen(
(char *)node->content)+1) * sizeof(char));
strcpy(xml_real_url, (char *)node->content);
}
if ((xmlStrcasecmp((xmlChar *)xpathObj->nodesetval-

nodeTab[i]->name,


(xmlChar *)"score") == 0)) {
xml_score = (double)atof((char *)node->content);
}
if ((xmlStrcasecmp((xmlChar *)xpathObj->nodesetval-

nodeTab[i]->name,


(xmlChar *)"source") == 0)) {
xml_source_id = (int)atoi((char *)node->content);
state = _res_end;
}
}

if (state == _res_end) {
// count document references for this search
engine
ref_cnt[ xml_source_id ] += 1;

// add this node to tree
root = linktree_add_node(root,
xml_doc_id,
xml_source_id,
xml_title,
xml_url,
xml_real_url,
xml_snippet,
xml_score);

state = _res_start;
// initialize snippet string as it could be empty
xml_snippet = NULL;
}
node = node->next;
}
}

// clean up
xmlXPathFreeObject(xpathObj);
xmlXPathFreeContext(xpathCtx);
xfree( xpathExpr );
xmlCleanupParser();
xmlFreeDoc( ms_results_xml_doc );

// make array with pointer to linktree nodes
linkarray = linktree2array(root, &linkarraysize);

// assign array size
*arraysize = linkarraysize;

// normalize score, highest score is 1.0
normalize_weight(linkarray, linkarraysize);

return linkarray;
}

Folgende Funktion fuegt einen Eintrag in den Baum ein:

/* add search result to link tree */
Linktree *
linktree_add_node(Linktree *root, int doc_id, Engine_ID id, char
*title,
char *url, char *real_url, char *content, double weight) {
Linktree *new;

if (url == NULL)
return root;

new = (Linktree *)xmalloc(sizeof(Linktree));
new->uas = NULL;
new->left = new->right = NULL;
new->doc_id = doc_id;
new->url = url;
new->real_url = real_url;
new->title = convert_string_to_utf8(title);
new->content = convert_string_to_utf8(content);
new->weight = weight;
new->uas = prepend_word(NULL, get_engine_name_human(id));
root = add_linknode(root, new);
return root;
}

Hier wird nun das Array mit den Pointern zu den Nodes erzeugt:

static Linktree
**linktree2array(Linktree *root, int *size) {
int nodes, addnodes;
Linktree **array;

nodes = count_linktree_nodes(root, 0);
array = (Linktree **)xmalloc(sizeof(Linktree *) * (nodes + 1)); /*
+1:NULL */
addnodes = addlinknode2array(array, 0, root);
assert(addnodes == nodes); /* nodes been counted */
array[addnodes] = NULL; /* mark end of array */
*size = nodes; /* e.g. for use in qsort() , list size */

return array;
}

Hier nun die Ausgabe von Valgrind:
...
...
=#400== 8,412 bytes in 190 blocks are indirectly lost in loss record
3 of 4
=#400== at 0x401D38B: malloc (vg_replace_malloc.c:149)
=#400== by 0x80548EA: xmalloc (util.c:273)
=#400== by 0x804D07F: get_searchresult (multiua.c:589)
=#400== by 0x804B3F5: search (metasearch.c:452)
=#400== by 0x804AC73: main (metasearch.c:196)
=#400==#400==#400== 9,772 (1,360 direct, 8,412 indirect) bytes in 34 blocks are
definitely lost in loss record 4 of 4
=#400== at 0x401D38B: malloc (vg_replace_malloc.c:149)
=#400== by 0x80548EA: xmalloc (util.c:273)
=#400== by 0x804D6FB: linktree_add_node (multiua.c:818)
=#400== by 0x804D176: get_searchresult (multiua.c:623)
=#400== by 0x804B3F5: search (metasearch.c:452)
=#400== by 0x804AC73: main (metasearch.c:196)
=#400==#400== LEAK SUMMARY:
=#400== definitely lost: 1,368 bytes in 35 blocks.
=#400== indirectly lost: 8,412 bytes in 190 blocks.
=#400== possibly lost: 0 bytes in 0 blocks.
=#400== still reachable: 276 bytes in 9 blocks.
=#400== suppressed: 0 bytes in 0 blocks.

Das soll es im Groben sein. Ich weiss nicht genau was speichermaessig
auf der Strecke bleibt. Vermutlich sind es die einzelnen mallocs der
Felder der Funktion get_searchresult()
aber ohne das geht es nun auch nicht.
Am Ende des Programms (nach Ausgabe der Ergebnisse) mache ich meiner
Meinung nach auch ein sauberes deallozieren des Arrays wie folgt:

for (i = 0; i < arraysize; i++) {
free_wordlist( result[i]->uas ); // free linked list
containing search engines of this result
xfree( result[i]->title );
xfree( result[i]->content );
xfree( result[i]->url );
xfree( result[i]->real_url );
xfree( result[i] );
}
xfree( result );
result = NULL;

Ich weiss absolut nicht mehr was ich noch machen soll. Das
Speicherleck ist mir einfach zu gross und kann so nicht bleiben. In
Produktion laeuft mir vermutlich irgendwann der virt. Speicher voll
und dann ist irgendwann Ende und die Kiste rebootet oder was auch
immer.

Gibt es irgendeinen Spezi da draussen der mir einen Tip geben kann,
den Fehler zu finden bzw einen Weg das ich alles wieder sauber
dealloziert bekomme? Ich bin fuer jeden Tip sehr dankbar da ich jetzt
an einen Punkt bin wo ich nicht mehr so recht weiter weiss. Ich hatte
mit C auch eine laengere Pause, sodass ich gerade wieder reinkomme und
denke, dass ich hier irgendetwas uebersehe oder vllt falsch bedenke.
Oder der Weg wie ich die XML Nodes in den baum einfuege ist
bedenklich ...

Sorry fuer diesen langen Post, ich weiss nicht ob alles klar
verstaendlich ist oder ob etwas an Info's fehlt. Von daher bitte
nachfragen. Hauptsache ich komme irgendwie zum Ziel.

Besten Dank und Gruesse aus dem Tal!

Andreas W Wylach
 

Lesen sie die antworten

#1 Markus Raab
29/05/2009 - 16:54 | Warnen spam
Andreas W. Wylach wrote:

Hier nun die Ausgabe von Valgrind:
...
...



Die Punkte lassen darauf schließen dass da noch andere Ausgaben von Valgrind
sind? Immer von oben nach unten abarbeiten, da spàtere Ausgaben
möglicherweise Folgefehler sind.

=#400== 8,412 bytes in 190 blocks are indirectly lost in loss record
3 of 4
=#400==    at 0x401D38B: malloc (vg_replace_malloc.c:149)
=#400==    by 0x80548EA: xmalloc (util.c:273)
=#400==    by 0x804D07F: get_searchresult (multiua.c:589)
=#400==    by 0x804B3F5: search (metasearch.c:452)
=#400==    by 0x804AC73: main (metasearch.c:196)



Hilfreich wàre jetzt natürlich die Zeile 589 von multiua.c (du hast sie
vielleicht angegeben, aber ich konnte auf die schnelle nicht verifizieren
welcher Code in welcher Datei ist und von welcher Zeile an du überhaupt
gepastet hast).

mfg Markus

Ähnliche fragen