1 /////////////////////////////////////////////////////////////////////////
2 // $Id: logio.cc,v 1.70 2008/10/01 09:44:40 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
5 // Copyright (C) 2001 MandrakeSoft S.A.
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
30 #include "iodev/iodev.h"
34 #define Float32 KLUDGE_Float32
35 #define Float64 KLUDGE_Float64
36 #include <Carbon/Carbon.h>
41 // Just for the iofunctions
45 void iofunctions::flush(void)
47 if(logfd
&& magic
== MAGIC_LOGNUM
) {
52 void iofunctions::init(void)
54 // iofunctions methods must not be called before this magic
58 // sets the default logprefix
59 strcpy(logprefix
,"%t%e%d");
62 log
= new logfunc_t(this);
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
)
78 while ((fn
!= logfn_list
[i
]) && (i
< n_logfn
)) {
82 for (int j
=i
; j
<n_logfn
-1; j
++) {
83 logfn_list
[j
] = logfn_list
[j
+1];
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
100 FILE *newfd
= stderr
;
101 const char *newfn
= "/dev/stderr";
102 if(strcmp(fn
, "-") != 0) {
103 newfd
= fopen(fn
, "w");
106 log
->ldebug("Opened log file '%s'.", fn
);
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
);
117 void iofunctions::init_log(FILE *fs
)
119 assert(magic
==MAGIC_LOGNUM
);
123 logfn
= "/dev/stderr";
124 } else if(fs
== stdout
) {
125 logfn
= "/dev/stdout";
131 void iofunctions::init_log(int fd
)
133 assert(magic
==MAGIC_LOGNUM
);
135 if((tmpfd
= fdopen(fd
,"w")) == NULL
) {
136 log
->panic("Couldn't open fd %d as a stream for writing", fd
);
143 void iofunctions::exit_log()
146 if (logfd
!= stderr
) {
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
)
171 assert(magic
==MAGIC_LOGNUM
);
172 assert(this != NULL
);
173 assert(logfd
!= NULL
);
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;
192 fprintf(logfd
, "%s", prefix
==NULL
?"":prefix
);
195 fprintf(logfd
, FMT_TICK
, bx_pc_system
.time_ticks());
198 #if BX_SUPPORT_SMP == 0
199 fprintf(logfd
, "%08x", BX_CPU(0)->get_eip());
203 fprintf(logfd
, "%c", c
);
209 fprintf(logfd
,"%%%c",*s
);
213 fprintf(logfd
,"%c",*s
);
221 fprintf(logfd
, ">>PANIC<< ");
223 fprintf(logfd
, ">>PASS<< ");
225 vfprintf(logfd
, fmt
, ap
);
226 fprintf(logfd
, "\n");
230 iofunctions::iofunctions(FILE *fs
)
236 iofunctions::iofunctions(const char *fn
)
242 iofunctions::iofunctions(int fd
)
248 iofunctions::iofunctions()
253 iofunctions::~iofunctions(void)
255 // flush before erasing magic number, or flush does nothing.
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
270 ACT_FATAL
, // on panic, quit
275 logfunctions::logfunctions(void)
280 if (io
== NULL
&& Allocio
== 0) {
282 io
= new iofunc_t(stderr
);
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
)
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
313 // give iofunction a pointer to me
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
324 return; // allocation not successful
327 if (this->prefix
!= NULL
)
329 free(this->prefix
); // free previously allocated memory
333 size_t len
=strlen(p
);
334 for(size_t i
=1;i
<len
+1;i
++) {
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;
349 void logfunctions::settype(int t
)
354 void logfunctions::info(const char *fmt
, ...)
358 assert(this != NULL
);
359 assert(this->logio
!= NULL
);
361 if(!onoff
[LOGLEV_INFO
]) return;
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);
372 void logfunctions::error(const char *fmt
, ...)
376 assert(this != NULL
);
377 assert(this->logio
!= NULL
);
379 if(!onoff
[LOGLEV_ERROR
]) return;
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);
390 void logfunctions::panic(const char *fmt
, ...)
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;
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
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);
415 void logfunctions::pass(const char *fmt
, ...)
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;
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
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);
440 void logfunctions::ldebug(const char *fmt
, ...)
444 assert(this != NULL
);
445 assert(this->logio
!= NULL
);
447 if(!onoff
[LOGLEV_DEBUG
]) return;
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);
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;
466 if (in_ask_already
) {
467 fprintf(stderr
, "logfunctions::ask() should not reenter!!\n");
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
);
485 case BX_LOG_ASK_CHOICE_CONTINUE
:
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
);
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!"));
499 case BX_LOG_ASK_CHOICE_DUMP_CORE
:
500 fprintf(stderr
, "User chose to dump core...\n");
504 // do something highly illegal that should kill the process.
507 char *crashptr
= (char *)0; char c
= *crashptr
;
509 fprintf(stderr
, "Sorry, I couldn't find your abort() function. Exiting.");
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
521 case BX_LOG_ASK_CHOICE_ENTER_DEBUG
:
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
);
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
;
542 CFStringRef cfExposition
;
543 DialogItemIndex index
;
544 AlertStdCFStringAlertParamRec alertParam
= {0};
549 cfError
= CFStringCreateWithCString(NULL
, error
, kCFStringEncodingASCII
);
550 if(exposition
!= NULL
)
552 cfExposition
= CFStringCreateWithCString(NULL
, exposition
, kCFStringEncodingASCII
);
557 alertParam
.version
= kStdCFStringAlertVersionOne
;
558 alertParam
.defaultText
= CFSTR("Quit");
559 alertParam
.position
= kWindowDefaultPosition
;
560 alertParam
.defaultButton
= kAlertStdAlertOKButton
;
565 cfExposition
, /* can be NULL */
566 &alertParam
, /* can be NULL */
568 RunStandardAlert(alertDialog
, NULL
, &index
);
571 if(cfExposition
!= NULL
) { CFRelease(cfExposition
); }
575 void logfunctions::fatal(const char *prefix
, const char *fmt
, va_list ap
, int exit_status
)
578 // store prefix and message in 'exit_msg' before unloading device plugins
582 vsprintf(tmpbuf
, fmt
, ap
);
583 sprintf(exit_msg
, "%s %s", prefix
, tmpbuf
);
589 if(!isatty(STDIN_FILENO
) && !SIM
->get_init_done())
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\".");
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
);
607 BX_EXIT(exit_status
);
609 static bx_bool dbg_exit_called
= 0;
610 if (dbg_exit_called
== 0) {
612 bx_dbg_exit(exit_status
);
615 // not safe to use BX_* log functions in here.
616 fprintf(stderr
, "fatal() should never return, but it just did\n");
620 logfunc_t
*genlog
= NULL
;
622 void bx_center_print(FILE *file
, const char *line
, unsigned maxwidth
)
624 size_t len
= strlen(line
);
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
);