diff options
Diffstat (limited to 'tools/ioemu/iodev/logio.cc')
-rw-r--r-- | tools/ioemu/iodev/logio.cc | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/tools/ioemu/iodev/logio.cc b/tools/ioemu/iodev/logio.cc new file mode 100644 index 0000000000..2b79719a2c --- /dev/null +++ b/tools/ioemu/iodev/logio.cc @@ -0,0 +1,631 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: logio.cc,v 1.42 2003/08/24 10:30:07 cbothamy Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2001 MandrakeSoft S.A. +// +// MandrakeSoft S.A. +// 43, rue d'Aboukir +// 75002 Paris - France +// http://www.linux-mandrake.com/ +// http://www.mandrakesoft.com/ +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + +#include "bochs.h" +#include <assert.h> +#include "state_file.h" + +#if BX_WITH_CARBON +#include <Carbon/Carbon.h> +#endif + +// Just for the iofunctions + + +int Allocio=0; + +void +iofunctions::flush(void) { + if(logfd && magic == MAGIC_LOGNUM) { + fflush(logfd); + } +} + +void +iofunctions::init(void) { + // iofunctions methods must not be called before this magic + // number is set. + magic=MAGIC_LOGNUM; + + // sets the default logprefix + strcpy(logprefix,"%t%e%d"); + n_logfn = 0; + init_log(stderr); + log = new logfunc_t(this); + log->put("IO"); + log->settype(IOLOG); + log->ldebug ("Init(log file: '%s').",logfn); +} + +void +iofunctions::add_logfn (logfunc_t *fn) +{ + assert (n_logfn < MAX_LOGFNS); + logfn_list[n_logfn++] = fn; +} + +void +iofunctions::set_log_action (int loglevel, int action) +{ + for (int i=0; i<n_logfn; i++) + logfn_list[i]->setonoff(loglevel, action); +} + +void +iofunctions::init_log(const char *fn) +{ + assert (magic==MAGIC_LOGNUM); + // use newfd/newfn so that we can log the message to the OLD log + // file descriptor. + FILE *newfd = stderr; + char *newfn = "/dev/stderr"; + if( strcmp( fn, "-" ) != 0 ) { + newfd = fopen(fn, "w"); + if(newfd != NULL) { + newfn = strdup(fn); + log->ldebug ("Opened log file '%s'.", fn ); + } else { + // in constructor, genlog might not exist yet, so do it the safe way. + log->error("Couldn't open log file: %s, using stderr instead", fn); + newfd = stderr; + } + } + logfd = newfd; + logfn = newfn; +} + +void +iofunctions::init_log(FILE *fs) +{ + assert (magic==MAGIC_LOGNUM); + logfd = fs; + + if(fs == stderr) { + logfn = "/dev/stderr"; + } else if(fs == stdout) { + logfn = "/dev/stdout"; + } else { + logfn = "(unknown)"; + } + +} + +void +iofunctions::init_log(int fd) +{ + assert (magic==MAGIC_LOGNUM); + FILE *tmpfd; + if( (tmpfd = fdopen(fd,"w")) == NULL ) { + log->panic("Couldn't open fd %d as a stream for writing", fd); + return; + } + + init_log(tmpfd); + return; +}; + +// all other functions may use genlog safely. +#define LOG_THIS genlog-> + +// This converts the option string to a printf style string with the following args: +// 1. timer, 2. event, 3. cpu0 eip, 4. device +void +iofunctions::set_log_prefix(const char* prefix) { + + strcpy(logprefix,prefix); +} + +// iofunctions::out( class, level, prefix, fmt, ap) +// DO NOT nest out() from ::info() and the like. +// fmt and ap retained for direct printinf from iofunctions only! + +void +iofunctions::out(int f, int l, const char *prefix, const char *fmt, va_list ap) +{ + char c=' ', *s; + assert (magic==MAGIC_LOGNUM); + assert (this != NULL); + assert (logfd != NULL); + + //if( showtick ) + // fprintf(logfd, "%011lld", bx_pc_system.time_ticks()); + + switch(l) { + case LOGLEV_INFO: c='i'; break; + case LOGLEV_PANIC: c='p'; break; + case LOGLEV_PASS: c='s'; break; + case LOGLEV_ERROR: c='e'; break; + case LOGLEV_DEBUG: c='d'; break; + default: break; + } + //fprintf(logfd, "-%c",c); + + //if(prefix != NULL) + // fprintf(logfd, "%s ", prefix); + + s=logprefix; + while(*s) { + switch(*s) { + case '%': + if(*(s+1))s++; + else break; + switch(*s) { + case 'd': + fprintf(logfd, "%s", prefix==NULL?"":prefix); + break; + case 't': + fprintf(logfd, "%011lld", bx_pc_system.time_ticks()); + break; +#ifndef BX_USE_VMX + case 'i': + fprintf(logfd, "%08x", BX_CPU(0)==NULL?0:BX_CPU(0)->dword.eip); + break; +#endif + case 'e': + fprintf(logfd, "%c", c); + break; + case '%': + fprintf(logfd,"%%"); + break; + default: + fprintf(logfd,"%%%c",*s); + } + break; + default : + fprintf(logfd,"%c",*s); + } + s++; + } + + fprintf(logfd," "); + + if(l==LOGLEV_PANIC) + fprintf(logfd, ">>PANIC<< "); + if(l==LOGLEV_PASS) + fprintf(logfd, ">>PASS<< "); + + vfprintf(logfd, fmt, ap); + fprintf(logfd, "\n"); + fflush(logfd); + + return; +} + +iofunctions::iofunctions(FILE *fs) +{ + init(); + init_log(fs); +} + +iofunctions::iofunctions(const char *fn) +{ + init(); + init_log(fn); +} + +iofunctions::iofunctions(int fd) +{ + init(); + init_log(fd); +} + +iofunctions::iofunctions(void) +{ + this->init(); +} + +iofunctions::~iofunctions(void) +{ + // flush before erasing magic number, or flush does nothing. + this->flush(); + this->magic=0; +} + +#define LOG_THIS genlog-> + +int logfunctions::default_onoff[N_LOGLEV] = { + ACT_IGNORE, // ignore debug + ACT_REPORT, // report info + ACT_REPORT, // report error +#if BX_WITH_WX + ACT_ASK, // on panic, ask user what to do +#else + ACT_FATAL, // on panic, quit +#endif + ACT_FATAL +}; + +logfunctions::logfunctions(void) +{ + prefix = NULL; + put(" "); + settype(GENLOG); + if (io == NULL && Allocio == 0) { + Allocio = 1; + io = new iofunc_t(stderr); + } + setio(io); + // BUG: unfortunately this can be called before the bochsrc is read, + // which means that the bochsrc has no effect on the actions. + for (int i=0; i<N_LOGLEV; i++) + onoff[i] = get_default_action(i); +} + +logfunctions::logfunctions(iofunc_t *iofunc) +{ + prefix = NULL; + put(" "); + settype(GENLOG); + setio(iofunc); + // BUG: unfortunately this can be called before the bochsrc is read, + // which means that the bochsrc has no effect on the actions. + for (int i=0; i<N_LOGLEV; i++) + onoff[i] = get_default_action(i); +} + +logfunctions::~logfunctions(void) +{ + if ( this->prefix ) + { + free(this->prefix); + this->prefix = NULL; + } +} + +void +logfunctions::setio(iofunc_t *i) +{ + // add pointer to iofunction object to use + this->logio = i; + // give iofunction a pointer to me + i->add_logfn (this); +} + +void +logfunctions::put(char *p) +{ + char *tmpbuf; + tmpbuf=strdup("[ ]");// if we ever have more than 32 chars, + // we need to rethink this + + if ( tmpbuf == NULL) + { + return ; /* allocation not successful */ + } + if ( this->prefix != NULL ) + { + free(this->prefix); /* free previously allocated memory */ + this->prefix = NULL; + } + int len=strlen(p); + for(int i=1;i<len+1;i++) { + tmpbuf[i]=p[i-1]; + } + + switch(len) { + case 1: tmpbuf[2]=' '; + case 2: tmpbuf[3]=' '; + case 3: tmpbuf[4]=' '; + case 4: tmpbuf[5]=' '; + default: tmpbuf[6]=']'; tmpbuf[7]='\0'; break; + } + + this->prefix=tmpbuf; +} + +void +logfunctions::settype(int t) +{ + type=t; +} + +void +logfunctions::info(const char *fmt, ...) +{ + va_list ap; + + assert (this != NULL); + assert (this->logio != NULL); + + if(!onoff[LOGLEV_INFO]) return; + + va_start(ap, fmt); + this->logio->out(this->type,LOGLEV_INFO,this->prefix, fmt, ap); + if (onoff[LOGLEV_INFO] == ACT_ASK) + ask (LOGLEV_INFO, this->prefix, fmt, ap); + if (onoff[LOGLEV_INFO] == ACT_FATAL) + fatal (this->prefix, fmt, ap, 1); + va_end(ap); + +} + +void +logfunctions::error(const char *fmt, ...) +{ + va_list ap; + + assert (this != NULL); + assert (this->logio != NULL); + + if(!onoff[LOGLEV_ERROR]) return; + + va_start(ap, fmt); + this->logio->out(this->type,LOGLEV_ERROR,this->prefix, fmt, ap); + if (onoff[LOGLEV_ERROR] == ACT_ASK) + ask (LOGLEV_ERROR, this->prefix, fmt, ap); + if (onoff[LOGLEV_ERROR] == ACT_FATAL) + fatal (this->prefix, fmt, ap, 1); + va_end(ap); +} + +void +logfunctions::panic(const char *fmt, ...) +{ + va_list ap; + + assert (this != NULL); + assert (this->logio != NULL); + + // Special case for panics since they are so important. Always print + // the panic to the log, no matter what the log action says. + //if(!onoff[LOGLEV_PANIC]) return; + + va_start(ap, fmt); + this->logio->out(this->type,LOGLEV_PANIC,this->prefix, fmt, ap); + + // This fixes a funny bug on linuxppc where va_list is no pointer but a struct + va_end(ap); + va_start(ap, fmt); + + if (onoff[LOGLEV_PANIC] == ACT_ASK) + ask (LOGLEV_PANIC, this->prefix, fmt, ap); + if (onoff[LOGLEV_PANIC] == ACT_FATAL) + fatal (this->prefix, fmt, ap, 1); + va_end(ap); +} + +void +logfunctions::pass(const char *fmt, ...) +{ + va_list ap; + + assert (this != NULL); + assert (this->logio != NULL); + + // Special case for panics since they are so important. Always print + // the panic to the log, no matter what the log action says. + //if(!onoff[LOGLEV_PASS]) return; + + va_start(ap, fmt); + this->logio->out(this->type,LOGLEV_PASS,this->prefix, fmt, ap); + + // This fixes a funny bug on linuxppc where va_list is no pointer but a struct + va_end(ap); + va_start(ap, fmt); + + if (onoff[LOGLEV_PASS] == ACT_ASK) + ask (LOGLEV_PASS, this->prefix, fmt, ap); + if (onoff[LOGLEV_PASS] == ACT_FATAL) + fatal (this->prefix, fmt, ap, 101); + va_end(ap); +} + +void +logfunctions::ldebug(const char *fmt, ...) +{ + va_list ap; + + assert (this != NULL); + assert (this->logio != NULL); + + if(!onoff[LOGLEV_DEBUG]) return; + + va_start(ap, fmt); + this->logio->out(this->type,LOGLEV_DEBUG,this->prefix, fmt, ap); + if (onoff[LOGLEV_DEBUG] == ACT_ASK) + ask (LOGLEV_DEBUG, this->prefix, fmt, ap); + if (onoff[LOGLEV_DEBUG] == ACT_FATAL) + fatal (this->prefix, fmt, ap, 1); + va_end(ap); +} + +void +logfunctions::ask (int level, const char *prefix, const char *fmt, va_list ap) +{ + // Guard against reentry on ask() function. The danger is that some + // function that's called within ask() could trigger another + // BX_PANIC that could call ask() again, leading to infinite + // recursion and infinite asks. + static char in_ask_already = 0; + char buf1[1024]; + if (in_ask_already) { + fprintf (stderr, "logfunctions::ask() should not reenter!!\n"); + return; + } + in_ask_already = 1; + vsprintf (buf1, fmt, ap); + // FIXME: facility set to 0 because it's unknown. + + // update vga screen. This is useful because sometimes useful messages + // are printed on the screen just before a panic. It's also potentially + // dangerous if this function calls ask again... That's why I added + // the reentry check above. + if (SIM->get_init_done()) DEV_vga_refresh(); + +#if !BX_EXTERNAL_DEBUGGER + // ensure the text screen is showing + SIM->set_display_mode (DISP_MODE_CONFIG); + int val = SIM->log_msg (prefix, level, buf1); + switch (val) + { + case BX_LOG_ASK_CHOICE_CONTINUE: + break; + case BX_LOG_ASK_CHOICE_CONTINUE_ALWAYS: + // user said continue, and don't "ask" for this facility again. + setonoff (level, ACT_REPORT); + break; + case BX_LOG_ASK_CHOICE_DIE: + bx_user_quit = 1; + in_ask_already = 0; // because fatal will longjmp out + fatal (prefix, fmt, ap, 1); + // should never get here + BX_PANIC (("in ask(), fatal() should never return!")); + break; + case BX_LOG_ASK_CHOICE_DUMP_CORE: + fprintf (stderr, "User chose to dump core...\n"); +#if BX_HAVE_ABORT + abort (); +#else + // do something highly illegal that should kill the process. + // Hey, this is fun! + { + char *crashptr = (char *)0; char c = *crashptr; + } + fprintf (stderr, "Sorry, I couldn't find your abort() function. Exiting."); + exit (0); +#endif +#if BX_DEBUGGER + case BX_LOG_ASK_CHOICE_ENTER_DEBUG: + // user chose debugger. To "drop into the debugger" we just set the + // interrupt_requested bit and continue execution. Before the next + // instruction, it should notice the user interrupt and return to + // the debugger. + bx_guard.interrupt_requested = 1; + break; +#endif + default: + // this happens if panics happen before the callback is initialized + // in gui/control.cc. + fprintf (stderr, "WARNING: log_msg returned unexpected value %d\n", val); + } +#else + // external debugger ask code goes here +#endif + // return to simulation mode + SIM->set_display_mode (DISP_MODE_SIM); + in_ask_already = 0; +} + +#if BX_WITH_CARBON +/* Panic button to display fatal errors. + Completely self contained, can't rely on carbon.cc being available */ +static void carbonFatalDialog(const char *error, const char *exposition) +{ + DialogRef alertDialog; + CFStringRef cfError; + CFStringRef cfExposition; + DialogItemIndex index; + AlertStdCFStringAlertParamRec alertParam = {0}; + + // Init libraries + InitCursor(); + // Assemble dialog + cfError = CFStringCreateWithCString(NULL, error, kCFStringEncodingASCII); + if(exposition != NULL) + { + cfExposition = CFStringCreateWithCString(NULL, exposition, kCFStringEncodingASCII); + } + else { cfExposition = NULL; } + alertParam.version = kStdCFStringAlertVersionOne; + alertParam.defaultText = CFSTR("Quit"); + alertParam.position = kWindowDefaultPosition; + alertParam.defaultButton = kAlertStdAlertOKButton; + // Display Dialog + CreateStandardAlert( + kAlertStopAlert, + cfError, + cfExposition, /* can be NULL */ + &alertParam, /* can be NULL */ + &alertDialog); + RunStandardAlert( alertDialog, NULL, &index); + // Cleanup + CFRelease( cfError ); + if( cfExposition != NULL ) { CFRelease( cfExposition ); } +} +#endif + +void +logfunctions::fatal (const char *prefix, const char *fmt, va_list ap, int exit_status) +{ + bx_atexit(); +#if BX_WITH_CARBON + if(!isatty(STDIN_FILENO) && !SIM->get_init_done()) + { + char buf1[1024]; + char buf2[1024]; + vsprintf (buf1, fmt, ap); + sprintf (buf2, "Bochs startup error\n%s", buf1); + carbonFatalDialog(buf2, + "For more information, try running Bochs within Terminal by clicking on \"bochs.scpt\"."); + } +#endif +#if !BX_WITH_WX + static char *divider = "========================================================================"; + fprintf (stderr, "%s\n", divider); + fprintf (stderr, "Bochs is exiting with the following message:\n"); + fprintf (stderr, "%s ", prefix); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n%s\n", divider); +#endif +#if 0 && defined(WIN32) +#error disabled because it is not working yet! + // wait for a keypress before quitting. Depending on how bochs is + // installed, the console window can disappear before the user has + // a chance to read the final message. + fprintf (stderr, "\n\nPress Enter to exit...\n"); + char buf[8]; + fgets (buf, 8, stdin); +#endif +#if !BX_DEBUGGER + BX_EXIT(exit_status); +#else + static bx_bool dbg_exit_called = 0; + if (dbg_exit_called == 0) { + dbg_exit_called = 1; + bx_dbg_exit(exit_status); + } +#endif + // not safe to use BX_* log functions in here. + fprintf (stderr, "fatal() should never return, but it just did\n"); +} + +iofunc_t *io = NULL; +logfunc_t *genlog = NULL; + +void bx_center_print (FILE *file, char *line, int maxwidth) +{ + int imax; + int len = strlen(line); + if (len > maxwidth) + BX_PANIC (("bx_center_print: line is too long: '%s'", line)); + imax = (maxwidth - len) >> 1; + for (int i=0; i<imax; i++) fputc (' ', file); + fputs (line, file); +} + + |