- added instructions how to update the online documentation
[bochs-mirror.git] / logio.cc
blobf6bdf6f4fca601d5618be2cb0b6f2cfea2ddcf73
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: logio.cc,v 1.70 2008/10/01 09:44:40 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2001 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "bochs.h"
29 #include "cpu/cpu.h"
30 #include "iodev/iodev.h"
31 #include <assert.h>
33 #if BX_WITH_CARBON
34 #define Float32 KLUDGE_Float32
35 #define Float64 KLUDGE_Float64
36 #include <Carbon/Carbon.h>
37 #undef Float32
38 #undef Float64
39 #endif
41 // Just for the iofunctions
43 static int Allocio=0;
45 void iofunctions::flush(void)
47 if(logfd && magic == MAGIC_LOGNUM) {
48 fflush(logfd);
52 void iofunctions::init(void)
54 // iofunctions methods must not be called before this magic
55 // number is set.
56 magic=MAGIC_LOGNUM;
58 // sets the default logprefix
59 strcpy(logprefix,"%t%e%d");
60 n_logfn = 0;
61 init_log(stderr);
62 log = new logfunc_t(this);
63 log->put("IO");
64 log->settype(IOLOG);
65 log->ldebug("Init(log file: '%s').",logfn);
68 void iofunctions::add_logfn(logfunc_t *fn)
70 assert(n_logfn < MAX_LOGFNS);
71 logfn_list[n_logfn++] = fn;
74 void iofunctions::remove_logfn(logfunc_t *fn)
76 assert(n_logfn > 0);
77 int i = 0;
78 while ((fn != logfn_list[i]) && (i < n_logfn)) {
79 i++;
81 if (i < n_logfn) {
82 for (int j=i; j<n_logfn-1; j++) {
83 logfn_list[j] = logfn_list[j+1];
85 n_logfn--;
89 void iofunctions::set_log_action(int loglevel, int action)
91 for(int i=0; i<n_logfn; i++)
92 logfn_list[i]->setonoff(loglevel, action);
95 void iofunctions::init_log(const char *fn)
97 assert(magic==MAGIC_LOGNUM);
98 // use newfd/newfn so that we can log the message to the OLD log
99 // file descriptor.
100 FILE *newfd = stderr;
101 const char *newfn = "/dev/stderr";
102 if(strcmp(fn, "-") != 0) {
103 newfd = fopen(fn, "w");
104 if(newfd != NULL) {
105 newfn = strdup(fn);
106 log->ldebug("Opened log file '%s'.", fn);
107 } else {
108 // in constructor, genlog might not exist yet, so do it the safe way.
109 log->error("Couldn't open log file: %s, using stderr instead", fn);
110 newfd = stderr;
113 logfd = newfd;
114 logfn = newfn;
117 void iofunctions::init_log(FILE *fs)
119 assert(magic==MAGIC_LOGNUM);
120 logfd = fs;
122 if(fs == stderr) {
123 logfn = "/dev/stderr";
124 } else if(fs == stdout) {
125 logfn = "/dev/stdout";
126 } else {
127 logfn = "(unknown)";
131 void iofunctions::init_log(int fd)
133 assert(magic==MAGIC_LOGNUM);
134 FILE *tmpfd;
135 if((tmpfd = fdopen(fd,"w")) == NULL) {
136 log->panic("Couldn't open fd %d as a stream for writing", fd);
137 return;
140 init_log(tmpfd);
143 void iofunctions::exit_log()
145 flush();
146 if (logfd != stderr) {
147 fclose(logfd);
148 logfd = stderr;
149 free((char *)logfn);
150 logfn = "/dev/stderr";
154 // all other functions may use genlog safely.
155 #define LOG_THIS genlog->
157 // This converts the option string to a printf style string with the following args:
158 // 1. timer, 2. event, 3. cpu0 eip, 4. device
159 void iofunctions::set_log_prefix(const char* prefix)
161 strcpy(logprefix, prefix);
164 // iofunctions::out(class, level, prefix, fmt, ap)
165 // DO NOT nest out() from ::info() and the like.
166 // fmt and ap retained for direct printinf from iofunctions only!
168 void iofunctions::out(int f, int l, const char *prefix, const char *fmt, va_list ap)
170 char c=' ', *s;
171 assert(magic==MAGIC_LOGNUM);
172 assert(this != NULL);
173 assert(logfd != NULL);
175 switch(l) {
176 case LOGLEV_INFO: c='i'; break;
177 case LOGLEV_PANIC: c='p'; break;
178 case LOGLEV_PASS: c='s'; break;
179 case LOGLEV_ERROR: c='e'; break;
180 case LOGLEV_DEBUG: c='d'; break;
181 default: break;
184 s=logprefix;
185 while(*s) {
186 switch(*s) {
187 case '%':
188 if(*(s+1)) s++;
189 else break;
190 switch(*s) {
191 case 'd':
192 fprintf(logfd, "%s", prefix==NULL?"":prefix);
193 break;
194 case 't':
195 fprintf(logfd, FMT_TICK, bx_pc_system.time_ticks());
196 break;
197 case 'i':
198 #if BX_SUPPORT_SMP == 0
199 fprintf(logfd, "%08x", BX_CPU(0)->get_eip());
200 #endif
201 break;
202 case 'e':
203 fprintf(logfd, "%c", c);
204 break;
205 case '%':
206 fprintf(logfd,"%%");
207 break;
208 default:
209 fprintf(logfd,"%%%c",*s);
211 break;
212 default :
213 fprintf(logfd,"%c",*s);
215 s++;
218 fprintf(logfd," ");
220 if(l==LOGLEV_PANIC)
221 fprintf(logfd, ">>PANIC<< ");
222 if(l==LOGLEV_PASS)
223 fprintf(logfd, ">>PASS<< ");
225 vfprintf(logfd, fmt, ap);
226 fprintf(logfd, "\n");
227 fflush(logfd);
230 iofunctions::iofunctions(FILE *fs)
232 init();
233 init_log(fs);
236 iofunctions::iofunctions(const char *fn)
238 init();
239 init_log(fn);
242 iofunctions::iofunctions(int fd)
244 init();
245 init_log(fd);
248 iofunctions::iofunctions()
250 init();
253 iofunctions::~iofunctions(void)
255 // flush before erasing magic number, or flush does nothing.
256 flush();
257 magic=0;
260 #define LOG_THIS genlog->
262 int logfunctions::default_onoff[N_LOGLEV] =
264 ACT_IGNORE, // ignore debug
265 ACT_REPORT, // report info
266 ACT_REPORT, // report error
267 #if BX_WITH_WX || BX_WITH_WIN32 || BX_WITH_X11
268 ACT_ASK, // on panic, ask user what to do
269 #else
270 ACT_FATAL, // on panic, quit
271 #endif
272 ACT_FATAL
275 logfunctions::logfunctions(void)
277 prefix = NULL;
278 put(" ");
279 settype(GENLOG);
280 if (io == NULL && Allocio == 0) {
281 Allocio = 1;
282 io = new iofunc_t(stderr);
284 setio(io);
285 // BUG: unfortunately this can be called before the bochsrc is read,
286 // which means that the bochsrc has no effect on the actions.
287 for (int i=0; i<N_LOGLEV; i++)
288 onoff[i] = get_default_action(i);
291 logfunctions::logfunctions(iofunc_t *iofunc)
293 prefix = NULL;
294 put(" ");
295 settype(GENLOG);
296 setio(iofunc);
297 // BUG: unfortunately this can be called before the bochsrc is read,
298 // which means that the bochsrc has no effect on the actions.
299 for (int i=0; i<N_LOGLEV; i++)
300 onoff[i] = get_default_action(i);
303 logfunctions::~logfunctions()
305 this->logio->remove_logfn(this);
306 if (prefix) free(prefix);
309 void logfunctions::setio(iofunc_t *i)
311 // add pointer to iofunction object to use
312 this->logio = i;
313 // give iofunction a pointer to me
314 i->add_logfn(this);
317 void logfunctions::put(const char *p)
319 char * tmpbuf=strdup("[ ]"); // if we ever have more than 32 chars,
320 // we need to rethink this
322 if (tmpbuf == NULL)
324 return; // allocation not successful
327 if (this->prefix != NULL)
329 free(this->prefix); // free previously allocated memory
330 prefix = NULL;
333 size_t len=strlen(p);
334 for(size_t i=1;i<len+1;i++) {
335 tmpbuf[i]=p[i-1];
338 switch(len) {
339 case 1: tmpbuf[2]=' ';
340 case 2: tmpbuf[3]=' ';
341 case 3: tmpbuf[4]=' ';
342 case 4: tmpbuf[5]=' ';
343 default: tmpbuf[6]=']'; tmpbuf[7]='\0'; break;
346 prefix=tmpbuf;
349 void logfunctions::settype(int t)
351 type=t;
354 void logfunctions::info(const char *fmt, ...)
356 va_list ap;
358 assert(this != NULL);
359 assert(this->logio != NULL);
361 if(!onoff[LOGLEV_INFO]) return;
363 va_start(ap, fmt);
364 this->logio->out(this->type,LOGLEV_INFO,this->prefix, fmt, ap);
365 if (onoff[LOGLEV_INFO] == ACT_ASK)
366 ask(LOGLEV_INFO, this->prefix, fmt, ap);
367 if (onoff[LOGLEV_INFO] == ACT_FATAL)
368 fatal(this->prefix, fmt, ap, 1);
369 va_end(ap);
372 void logfunctions::error(const char *fmt, ...)
374 va_list ap;
376 assert(this != NULL);
377 assert(this->logio != NULL);
379 if(!onoff[LOGLEV_ERROR]) return;
381 va_start(ap, fmt);
382 this->logio->out(this->type,LOGLEV_ERROR,this->prefix, fmt, ap);
383 if (onoff[LOGLEV_ERROR] == ACT_ASK)
384 ask(LOGLEV_ERROR, this->prefix, fmt, ap);
385 if (onoff[LOGLEV_ERROR] == ACT_FATAL)
386 fatal(this->prefix, fmt, ap, 1);
387 va_end(ap);
390 void logfunctions::panic(const char *fmt, ...)
392 va_list ap;
394 assert(this != NULL);
395 assert(this->logio != NULL);
397 // Special case for panics since they are so important. Always print
398 // the panic to the log, no matter what the log action says.
399 //if(!onoff[LOGLEV_PANIC]) return;
401 va_start(ap, fmt);
402 this->logio->out(this->type,LOGLEV_PANIC,this->prefix, fmt, ap);
404 // This fixes a funny bug on linuxppc where va_list is no pointer but a struct
405 va_end(ap);
406 va_start(ap, fmt);
408 if (onoff[LOGLEV_PANIC] == ACT_ASK)
409 ask(LOGLEV_PANIC, this->prefix, fmt, ap);
410 if (onoff[LOGLEV_PANIC] == ACT_FATAL)
411 fatal(this->prefix, fmt, ap, 1);
412 va_end(ap);
415 void logfunctions::pass(const char *fmt, ...)
417 va_list ap;
419 assert(this != NULL);
420 assert(this->logio != NULL);
422 // Special case for panics since they are so important. Always print
423 // the panic to the log, no matter what the log action says.
424 //if(!onoff[LOGLEV_PASS]) return;
426 va_start(ap, fmt);
427 this->logio->out(this->type,LOGLEV_PASS,this->prefix, fmt, ap);
429 // This fixes a funny bug on linuxppc where va_list is no pointer but a struct
430 va_end(ap);
431 va_start(ap, fmt);
433 if (onoff[LOGLEV_PASS] == ACT_ASK)
434 ask(LOGLEV_PASS, this->prefix, fmt, ap);
435 if (onoff[LOGLEV_PASS] == ACT_FATAL)
436 fatal(this->prefix, fmt, ap, 101);
437 va_end(ap);
440 void logfunctions::ldebug(const char *fmt, ...)
442 va_list ap;
444 assert(this != NULL);
445 assert(this->logio != NULL);
447 if(!onoff[LOGLEV_DEBUG]) return;
449 va_start(ap, fmt);
450 this->logio->out(this->type,LOGLEV_DEBUG,this->prefix, fmt, ap);
451 if (onoff[LOGLEV_DEBUG] == ACT_ASK)
452 ask(LOGLEV_DEBUG, this->prefix, fmt, ap);
453 if (onoff[LOGLEV_DEBUG] == ACT_FATAL)
454 fatal(this->prefix, fmt, ap, 1);
455 va_end(ap);
458 void logfunctions::ask(int level, const char *prefix, const char *fmt, va_list ap)
460 // Guard against reentry on ask() function. The danger is that some
461 // function that's called within ask() could trigger another
462 // BX_PANIC that could call ask() again, leading to infinite
463 // recursion and infinite asks.
464 static char in_ask_already = 0;
465 char buf1[1024];
466 if (in_ask_already) {
467 fprintf(stderr, "logfunctions::ask() should not reenter!!\n");
468 return;
470 in_ask_already = 1;
471 vsnprintf(buf1, sizeof(buf1), fmt, ap);
472 // FIXME: facility set to 0 because it's unknown.
474 // update vga screen. This is useful because sometimes useful messages
475 // are printed on the screen just before a panic. It's also potentially
476 // dangerous if this function calls ask again... That's why I added
477 // the reentry check above.
478 if (SIM->get_init_done()) DEV_vga_refresh();
480 // ensure the text screen is showing
481 SIM->set_display_mode(DISP_MODE_CONFIG);
482 int val = SIM->log_msg(prefix, level, buf1);
483 switch(val)
485 case BX_LOG_ASK_CHOICE_CONTINUE:
486 break;
487 case BX_LOG_ASK_CHOICE_CONTINUE_ALWAYS:
488 // user said continue, and don't "ask" for this facility again.
489 setonoff(level, ACT_REPORT);
490 break;
491 case BX_LOG_ASK_CHOICE_DIE:
492 case BX_LOG_NOTIFY_FAILED:
493 bx_user_quit = (val==BX_LOG_ASK_CHOICE_DIE)?1:0;
494 in_ask_already = 0; // because fatal will longjmp out
495 fatal(prefix, buf1, ap, 1);
496 // should never get here
497 BX_PANIC(("in ask(), fatal() should never return!"));
498 break;
499 case BX_LOG_ASK_CHOICE_DUMP_CORE:
500 fprintf(stderr, "User chose to dump core...\n");
501 #if BX_HAVE_ABORT
502 abort();
503 #else
504 // do something highly illegal that should kill the process.
505 // Hey, this is fun!
507 char *crashptr = (char *)0; char c = *crashptr;
509 fprintf(stderr, "Sorry, I couldn't find your abort() function. Exiting.");
510 exit(0);
511 #endif
512 #if BX_DEBUGGER
513 case BX_LOG_ASK_CHOICE_ENTER_DEBUG:
514 // user chose debugger. To "drop into the debugger" we just set the
515 // interrupt_requested bit and continue execution. Before the next
516 // instruction, it should notice the user interrupt and return to
517 // the debugger.
518 bx_debug_break();
519 break;
520 #elif BX_GDBSTUB
521 case BX_LOG_ASK_CHOICE_ENTER_DEBUG:
522 bx_gdbstub_break();
523 break;
524 #endif
525 default:
526 // this happens if panics happen before the callback is initialized
527 // in gui/control.cc.
528 fprintf(stderr, "WARNING: log_msg returned unexpected value %d\n", val);
530 // return to simulation mode
531 SIM->set_display_mode(DISP_MODE_SIM);
532 in_ask_already = 0;
535 #if BX_WITH_CARBON
536 /* Panic button to display fatal errors.
537 Completely self contained, can't rely on carbon.cc being available */
538 static void carbonFatalDialog(const char *error, const char *exposition)
540 DialogRef alertDialog;
541 CFStringRef cfError;
542 CFStringRef cfExposition;
543 DialogItemIndex index;
544 AlertStdCFStringAlertParamRec alertParam = {0};
546 // Init libraries
547 InitCursor();
548 // Assemble dialog
549 cfError = CFStringCreateWithCString(NULL, error, kCFStringEncodingASCII);
550 if(exposition != NULL)
552 cfExposition = CFStringCreateWithCString(NULL, exposition, kCFStringEncodingASCII);
554 else {
555 cfExposition = NULL;
557 alertParam.version = kStdCFStringAlertVersionOne;
558 alertParam.defaultText = CFSTR("Quit");
559 alertParam.position = kWindowDefaultPosition;
560 alertParam.defaultButton = kAlertStdAlertOKButton;
561 // Display Dialog
562 CreateStandardAlert(
563 kAlertStopAlert,
564 cfError,
565 cfExposition, /* can be NULL */
566 &alertParam, /* can be NULL */
567 &alertDialog);
568 RunStandardAlert(alertDialog, NULL, &index);
569 // Cleanup
570 CFRelease(cfError);
571 if(cfExposition != NULL) { CFRelease(cfExposition); }
573 #endif
575 void logfunctions::fatal(const char *prefix, const char *fmt, va_list ap, int exit_status)
577 #if !BX_WITH_WX
578 // store prefix and message in 'exit_msg' before unloading device plugins
579 char tmpbuf[1024];
580 char exit_msg[1024];
582 vsprintf(tmpbuf, fmt, ap);
583 sprintf(exit_msg, "%s %s", prefix, tmpbuf);
584 #endif
585 #if !BX_DEBUGGER
586 bx_atexit();
587 #endif
588 #if BX_WITH_CARBON
589 if(!isatty(STDIN_FILENO) && !SIM->get_init_done())
591 char buf1[1024];
592 char buf2[1024];
593 vsnprintf(buf1, sizeof(buf1), fmt, ap);
594 snprintf(buf2, sizeof(buf2), "Bochs startup error\n%s", buf1);
595 carbonFatalDialog(buf2,
596 "For more information, try running Bochs within Terminal by clicking on \"bochs.scpt\".");
598 #endif
599 #if !BX_WITH_WX
600 static const char *divider = "========================================================================";
601 fprintf(stderr, "%s\n", divider);
602 fprintf(stderr, "Bochs is exiting with the following message:\n");
603 fprintf(stderr, "%s", exit_msg);
604 fprintf(stderr, "\n%s\n", divider);
605 #endif
606 #if !BX_DEBUGGER
607 BX_EXIT(exit_status);
608 #else
609 static bx_bool dbg_exit_called = 0;
610 if (dbg_exit_called == 0) {
611 dbg_exit_called = 1;
612 bx_dbg_exit(exit_status);
614 #endif
615 // not safe to use BX_* log functions in here.
616 fprintf(stderr, "fatal() should never return, but it just did\n");
619 iofunc_t *io = NULL;
620 logfunc_t *genlog = NULL;
622 void bx_center_print(FILE *file, const char *line, unsigned maxwidth)
624 size_t len = strlen(line);
625 if (len > maxwidth)
626 BX_PANIC(("bx_center_print: line is too long: '%s'", line));
627 size_t imax = (maxwidth - len) >> 1;
628 for (size_t i=0; i<imax; i++) fputc(' ', file);
629 fputs(line, file);