mikrocontroller.net

Forum: PC-Programmierung Segfault wird durch printf() unterbunden?


Autor: trustno1 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,
zur Zeit arbeite ich an einem primitiven HTTP Server, für diesen wollte 
ich Funktionen schreiben, welche Logdateien schreiben.

In der Funktion log_error_write() werden mehrere Zeichenketten mit Hilfe 
von string_append() zusammen gesetzt. Da string_append() nur mit 
übergebenen char * umgehen kann, werden vorher die Zeilennummer und der 
Fehlercode mit snprintf() in eine Zeichenkette umgewandelt.

Nun ist das Problem, dass alles problemlos funktioniert, wenn ich nach 
dem snprintf() einen printf() Aufruf durchführe. Dabei ist es auch egal 
was printf() von sich gibt. Wenn ich printf() weglasse, beendet sich das 
Programm mit einem Segmentation Fault.
int log_error_write(config *cnf, const char *filename, uint line, uint errno, const char *error)
{
  if(-1 == cnf->logfile_fd) return 0;
  char *log_sequence;
  time_t ts;
  time(&ts);
  
  char iline[] = "23", ierrno[] = "555";
  
  snprintf(ierrno, 4, "%d", errno);
  snprintf(iline, 4, "%d", line);
  
  printf("Eigentlich sinnloser printf() Aufruf... \n");
  
  log_sequence = string_append(8, "Error: [", ierrno, "] ", " on line ", iline," in file ", filename,"...\r\n");
  
  if(-1 != write(cnf->logfile_fd, log_sequence, strlen(log_sequence)))
  {
    printf("Log geschrieben\n");
  }
  else
  {
    printf("Log nicht geschrieben\n");
  }
}

void *string_append(usint n_strings, ...)
{
  if(n_strings < 0) return 0;
  va_list argptr;
  va_list argclone;
  
  unsigned int len, remember;
  char *dest = NULL, *src = NULL, *dest_start = NULL;
  
  remember = n_strings;
  
  va_start(argptr, n_strings);
  va_copy(argclone, argptr);
  
  while(n_strings-- > 0)
  {
    len += strlen(va_arg(argclone, char *));
  }
  
  dest = malloc((sizeof(char) * len)+1);
  if(dest == NULL) return (void *)-1;
  dest_start = dest;
  n_strings = remember;
  
  while(n_strings-- > 0)
  {
    src = va_arg(argptr, char *);
    while(*src)
    {
      *dest++ = *src++;  
    }
  }
  *dest = '\0';
  dest = dest_start;

  va_end(argclone);
  va_end(argptr);
  
  return dest;
}

int main(void)
{
  config cnf;

  log_open_file(&cnf, "foobar.log");
  log_error_write(&cnf, "foobar.c", 23, 676, "Slayer!!!");
  return EXIT_SUCCESS;
}

Woran liegt das? Wie behebe ich das? Und wahrscheinlich kann man das 
doch auch irgendwie einfacher lösen oder?

Beste vorweihnachtliche Grüße
James

Autor: trustno1 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Uh die Zeile
char iline[] = "23", ierrno[] = "555";

muss eigentlich
char iline[5], ierrno[5];

lauten. War jetzt nur für Testzwecke verändert.

Autor: Schorsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Woher kommt dein "string_append"?
Das string_append, das ich auf die Schnelle gefunden habe, will als 
ersten Parameter keinen Int, sondern einen char * auf einen Buffer, der 
für den zusamengesetzten String verwendet wird.

Autor: Schorsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Arg... weiterlesen hätte geholfen...
Ignorier mich einfach.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auf die SChnelle:
len wird nirgends initial auf 0 gesetzt

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Allerdings frage ich mich, wozu du dir die ganze Mühe machst, den String 
erst mal mmühselig im Speicher zusammenzusetzen, anstelle von ganz 
einfach mehreren Aufrufen von write.

Dann gäbs nänmlich auch kein Speicherleck. Du gibst den allokierten 
Speicher nie wieder frei.


Was verbirgt sich hinter  cnf->logfile_fd  bzw. wie wird das File 
geöffnet?

Edit: Offenbar ein File Handle. Gibt es irgendeinen Grund warum das kein 
FILE* sein kann? Dann wäre deine Funktion
int log_error_write(config *cnf, const char *filename, uint line, uint errno, const char *error)
{
  if( cnf->logfile_fd == NULL )
    return 0;

  fprintf( cnf->logfile_fd,
           "Error: [%u] on line %u in file %s\r\n",
                 errno,
                 line,
                 filename );
}

Autor: Schorsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> anstelle von ganz
> einfach mehreren Aufrufen von write.

Das kann daneben gehen, wenn der Server mit threads oder ge-fork()-ten 
tasks arbeitet, die sich das Log-Filehandle teilen.

Damit sich die Ausgaben nicht innerhalb einer Zeile "vermischen", macht 
es schon sinn, zuerst die Zeile im Ram vorzubereiten, und dann mit nur 
einem write rauszuschreiben.

Ich würd das allerdings mit nur einem snprintf erschlagen. Kein 
Speicherleck, und der Rückgabe-Wert geht gleich als Längenangabe fürs 
Write, d.H. die ganzen O(n)-strlen-Aufrufe entfallen.
char buffer[1024];
int len=snprintf(buffer,sizeof(buffer),"Error [%d] on line %d in file %s ...\n",ierrno,iline,filename);
if (len>0) write(logfile_handle,buffer,len);

Autor: trustno1 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Allerdings frage ich mich, wozu du dir die ganze Mühe machst, den String
> erst mal mmühselig im Speicher zusammenzusetzen, anstelle von ganz
> einfach mehreren Aufrufen von write.

->

Schorsch schrieb:
> Das kann daneben gehen, wenn der Server mit threads oder ge-fork()-ten
> tasks arbeitet, die sich das Log-Filehandle teilen.

Ja, danke Schorsch, so funktioniert es wunderbar. Ich frage mich gerade 
warum ich es mir so kompliziert gemacht habe. Aber super, nun läuft es 
ja problemlos, danke, hätte ich selbst drauf kommen sollen.

Aber merkwürdig fand ich das Problem schon.

Schönen Abend
James

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.