提交 1dce7c3c 编写于 作者: B bellard

new clock logic: cpu ticks and virtual clocks are no longer proportional -...

new clock logic: cpu ticks and virtual clocks are no longer proportional - added timestamps on the stdio console


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2049 c046a42c-6fe2-441c-8c8c-71466251a162
上级 effedbc9
...@@ -58,10 +58,19 @@ static void ioportF0_write(void *opaque, uint32_t addr, uint32_t data) ...@@ -58,10 +58,19 @@ static void ioportF0_write(void *opaque, uint32_t addr, uint32_t data)
} }
/* TSC handling */ /* TSC handling */
uint64_t cpu_get_tsc(CPUX86State *env) uint64_t cpu_get_tsc(CPUX86State *env)
{ {
return qemu_get_clock(vm_clock); /* Note: when using kqemu, it is more logical to return the host TSC
because kqemu does not trap the RDTSC instruction for
performance reasons */
#if USE_KQEMU
if (env->kqemu_enabled) {
return cpu_get_real_ticks();
} else
#endif
{
return cpu_get_ticks();
}
} }
/* IRQ handling */ /* IRQ handling */
......
...@@ -107,29 +107,7 @@ int cpu_get_pic_interrupt(CPUState *env) ...@@ -107,29 +107,7 @@ int cpu_get_pic_interrupt(CPUState *env)
/* timers for rdtsc */ /* timers for rdtsc */
#if defined(__i386__) #if 0
int64_t cpu_get_real_ticks(void)
{
int64_t val;
asm volatile ("rdtsc" : "=A" (val));
return val;
}
#elif defined(__x86_64__)
int64_t cpu_get_real_ticks(void)
{
uint32_t low,high;
int64_t val;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
val = high;
val <<= 32;
val |= low;
return val;
}
#else
static uint64_t emu_time; static uint64_t emu_time;
......
...@@ -482,114 +482,103 @@ int kbd_mouse_is_absolute(void) ...@@ -482,114 +482,103 @@ int kbd_mouse_is_absolute(void)
return qemu_put_mouse_event_absolute; return qemu_put_mouse_event_absolute;
} }
/***********************************************************/ /* compute with 96 bit intermediate result: (a*b)/c */
/* timers */ uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
#if defined(__powerpc__)
static inline uint32_t get_tbl(void)
{ {
uint32_t tbl; union {
asm volatile("mftb %0" : "=r" (tbl)); uint64_t ll;
return tbl; struct {
} #ifdef WORDS_BIGENDIAN
uint32_t high, low;
#else
uint32_t low, high;
#endif
} l;
} u, res;
uint64_t rl, rh;
static inline uint32_t get_tbu(void) u.ll = a;
{ rl = (uint64_t)u.l.low * (uint64_t)b;
uint32_t tbl; rh = (uint64_t)u.l.high * (uint64_t)b;
asm volatile("mftbu %0" : "=r" (tbl)); rh += (rl >> 32);
return tbl; res.l.high = rh / c;
res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
return res.ll;
} }
int64_t cpu_get_real_ticks(void) /***********************************************************/
{ /* real time host monotonic timer */
uint32_t l, h, h1;
/* NOTE: we test if wrapping has occurred */
do {
h = get_tbu();
l = get_tbl();
h1 = get_tbu();
} while (h != h1);
return ((int64_t)h << 32) | l;
}
#elif defined(__i386__) #define QEMU_TIMER_BASE 1000000000LL
int64_t cpu_get_real_ticks(void) #ifdef WIN32
{
#ifdef _WIN32
LARGE_INTEGER ti;
QueryPerformanceCounter(&ti);
return ti.QuadPart;
#else
int64_t val;
asm volatile ("rdtsc" : "=A" (val));
return val;
#endif
}
#elif defined(__x86_64__) static int64_t clock_freq;
int64_t cpu_get_real_ticks(void) static void init_get_clock(void)
{ {
uint32_t low,high; ret = QueryPerformanceFrequency(&freq);
int64_t val; if (ret == 0) {
asm volatile("rdtsc" : "=a" (low), "=d" (high)); fprintf(stderr, "Could not calibrate ticks\n");
val = high; exit(1);
val <<= 32; }
val |= low; clock_freq = freq.QuadPart;
return val;
} }
#elif defined(__ia64) static int64_t get_clock(void)
int64_t cpu_get_real_ticks(void)
{ {
int64_t val; LARGE_INTEGER ti;
asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory"); QueryPerformanceCounter(&ti);
return val; return muldiv64(ti.QuadPart, QEMU_TIMER_BASE, clock_freq);
} }
#elif defined(__s390__) #else
int64_t cpu_get_real_ticks(void) static int use_rt_clock;
static void init_get_clock(void)
{ {
int64_t val; use_rt_clock = 0;
asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc"); #if defined(__linux__)
return val; {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
use_rt_clock = 1;
}
}
#endif
} }
#elif defined(__sparc__) && defined(HOST_SOLARIS) static int64_t get_clock(void)
uint64_t cpu_get_real_ticks (void)
{ {
#if defined(_LP64) #if defined(__linux__)
uint64_t rval; if (use_rt_clock) {
asm volatile("rd %%tick,%0" : "=r"(rval)); struct timespec ts;
return rval; clock_gettime(CLOCK_MONOTONIC, &ts);
#else return ts.tv_sec * 1000000000LL + ts.tv_nsec;
union { } else
uint64_t i64;
struct {
uint32_t high;
uint32_t low;
} i32;
} rval;
asm volatile("rd %%tick,%1; srlx %1,32,%0"
: "=r"(rval.i32.high), "=r"(rval.i32.low));
return rval.i64;
#endif #endif
{
/* XXX: using gettimeofday leads to problems if the date
changes, so it should be avoided. */
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000);
}
} }
#else
#error unsupported CPU
#endif #endif
/***********************************************************/
/* guest cycle counter */
static int64_t cpu_ticks_prev; static int64_t cpu_ticks_prev;
static int64_t cpu_ticks_offset; static int64_t cpu_ticks_offset;
static int64_t cpu_clock_offset;
static int cpu_ticks_enabled; static int cpu_ticks_enabled;
static inline int64_t cpu_get_ticks(void) /* return the host CPU cycle counter and handle stop/restart */
int64_t cpu_get_ticks(void)
{ {
if (!cpu_ticks_enabled) { if (!cpu_ticks_enabled) {
return cpu_ticks_offset; return cpu_ticks_offset;
...@@ -606,11 +595,24 @@ static inline int64_t cpu_get_ticks(void) ...@@ -606,11 +595,24 @@ static inline int64_t cpu_get_ticks(void)
} }
} }
/* return the host CPU monotonic timer and handle stop/restart */
static int64_t cpu_get_clock(void)
{
int64_t ti;
if (!cpu_ticks_enabled) {
return cpu_clock_offset;
} else {
ti = get_clock();
return ti + cpu_clock_offset;
}
}
/* enable cpu_get_ticks() */ /* enable cpu_get_ticks() */
void cpu_enable_ticks(void) void cpu_enable_ticks(void)
{ {
if (!cpu_ticks_enabled) { if (!cpu_ticks_enabled) {
cpu_ticks_offset -= cpu_get_real_ticks(); cpu_ticks_offset -= cpu_get_real_ticks();
cpu_clock_offset -= get_clock();
cpu_ticks_enabled = 1; cpu_ticks_enabled = 1;
} }
} }
...@@ -621,69 +623,14 @@ void cpu_disable_ticks(void) ...@@ -621,69 +623,14 @@ void cpu_disable_ticks(void)
{ {
if (cpu_ticks_enabled) { if (cpu_ticks_enabled) {
cpu_ticks_offset = cpu_get_ticks(); cpu_ticks_offset = cpu_get_ticks();
cpu_clock_offset = cpu_get_clock();
cpu_ticks_enabled = 0; cpu_ticks_enabled = 0;
} }
} }
#ifdef _WIN32 /***********************************************************/
void cpu_calibrate_ticks(void) /* timers */
{
LARGE_INTEGER freq;
int ret;
ret = QueryPerformanceFrequency(&freq);
if (ret == 0) {
fprintf(stderr, "Could not calibrate ticks\n");
exit(1);
}
ticks_per_sec = freq.QuadPart;
}
#else
static int64_t get_clock(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000LL + tv.tv_usec;
}
void cpu_calibrate_ticks(void)
{
int64_t usec, ticks;
usec = get_clock();
ticks = cpu_get_real_ticks();
usleep(50 * 1000);
usec = get_clock() - usec;
ticks = cpu_get_real_ticks() - ticks;
ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec;
}
#endif /* !_WIN32 */
/* compute with 96 bit intermediate result: (a*b)/c */
uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
{
union {
uint64_t ll;
struct {
#ifdef WORDS_BIGENDIAN
uint32_t high, low;
#else
uint32_t low, high;
#endif
} l;
} u, res;
uint64_t rl, rh;
u.ll = a;
rl = (uint64_t)u.l.low * (uint64_t)b;
rh = (uint64_t)u.l.high * (uint64_t)b;
rh += (rl >> 32);
res.l.high = rh / c;
res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
return res.ll;
}
#define QEMU_TIMER_REALTIME 0 #define QEMU_TIMER_REALTIME 0
#define QEMU_TIMER_VIRTUAL 1 #define QEMU_TIMER_VIRTUAL 1
...@@ -822,28 +769,21 @@ int64_t qemu_get_clock(QEMUClock *clock) ...@@ -822,28 +769,21 @@ int64_t qemu_get_clock(QEMUClock *clock)
{ {
switch(clock->type) { switch(clock->type) {
case QEMU_TIMER_REALTIME: case QEMU_TIMER_REALTIME:
#ifdef _WIN32 return get_clock() / 1000000;
return GetTickCount();
#else
{
struct tms tp;
/* Note that using gettimeofday() is not a good solution
for timers because its value change when the date is
modified. */
if (timer_freq == 100) {
return times(&tp) * 10;
} else {
return ((int64_t)times(&tp) * 1000) / timer_freq;
}
}
#endif
default: default:
case QEMU_TIMER_VIRTUAL: case QEMU_TIMER_VIRTUAL:
return cpu_get_ticks(); return cpu_get_clock();
} }
} }
static void init_timers(void)
{
init_get_clock();
ticks_per_sec = QEMU_TIMER_BASE;
rt_clock = qemu_new_clock(QEMU_TIMER_REALTIME);
vm_clock = qemu_new_clock(QEMU_TIMER_VIRTUAL);
}
/* save a timer */ /* save a timer */
void qemu_put_timer(QEMUFile *f, QEMUTimer *ts) void qemu_put_timer(QEMUFile *f, QEMUTimer *ts)
{ {
...@@ -985,11 +925,8 @@ static int start_rtc_timer(void) ...@@ -985,11 +925,8 @@ static int start_rtc_timer(void)
#endif /* !defined(_WIN32) */ #endif /* !defined(_WIN32) */
static void init_timers(void) static void init_timer_alarm(void)
{ {
rt_clock = qemu_new_clock(QEMU_TIMER_REALTIME);
vm_clock = qemu_new_clock(QEMU_TIMER_VIRTUAL);
#ifdef _WIN32 #ifdef _WIN32
{ {
int count=0; int count=0;
...@@ -1354,7 +1291,9 @@ CharDriverState *qemu_chr_open_pipe(const char *filename) ...@@ -1354,7 +1291,9 @@ CharDriverState *qemu_chr_open_pipe(const char *filename)
static int term_got_escape, client_index; static int term_got_escape, client_index;
static uint8_t term_fifo[TERM_FIFO_MAX_SIZE]; static uint8_t term_fifo[TERM_FIFO_MAX_SIZE];
int term_fifo_size; static int term_fifo_size;
static int term_timestamps;
static int64_t term_timestamps_start;
void term_print_help(void) void term_print_help(void)
{ {
...@@ -1363,6 +1302,7 @@ void term_print_help(void) ...@@ -1363,6 +1302,7 @@ void term_print_help(void)
"C-a x exit emulator\n" "C-a x exit emulator\n"
"C-a s save disk data back to file (if -snapshot)\n" "C-a s save disk data back to file (if -snapshot)\n"
"C-a b send break (magic sysrq)\n" "C-a b send break (magic sysrq)\n"
"C-a t toggle console timestamps\n"
"C-a c switch between console and monitor\n" "C-a c switch between console and monitor\n"
"C-a C-a send C-a\n" "C-a C-a send C-a\n"
); );
...@@ -1409,6 +1349,10 @@ static void stdio_received_byte(int ch) ...@@ -1409,6 +1349,10 @@ static void stdio_received_byte(int ch)
goto send_char; goto send_char;
} }
break; break;
case 't':
term_timestamps = !term_timestamps;
term_timestamps_start = -1;
break;
case TERM_ESCAPE: case TERM_ESCAPE:
goto send_char; goto send_char;
} }
...@@ -1466,6 +1410,39 @@ static void stdio_read(void *opaque) ...@@ -1466,6 +1410,39 @@ static void stdio_read(void *opaque)
stdio_received_byte(buf[0]); stdio_received_byte(buf[0]);
} }
static int stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
{
FDCharDriver *s = chr->opaque;
if (!term_timestamps) {
return unix_write(s->fd_out, buf, len);
} else {
int i;
char buf1[64];
for(i = 0; i < len; i++) {
unix_write(s->fd_out, buf + i, 1);
if (buf[i] == '\n') {
int64_t ti;
int secs;
ti = get_clock();
if (term_timestamps_start == -1)
term_timestamps_start = ti;
ti -= term_timestamps_start;
secs = ti / 1000000000;
snprintf(buf1, sizeof(buf1),
"[%02d:%02d:%02d.%03d] ",
secs / 3600,
(secs / 60) % 60,
secs % 60,
(int)((ti / 1000000) % 1000));
unix_write(s->fd_out, buf1, strlen(buf1));
}
}
return len;
}
}
/* init terminal so that we can grab keys */ /* init terminal so that we can grab keys */
static struct termios oldtty; static struct termios oldtty;
static int old_fd0_flags; static int old_fd0_flags;
...@@ -1511,6 +1488,7 @@ CharDriverState *qemu_chr_open_stdio(void) ...@@ -1511,6 +1488,7 @@ CharDriverState *qemu_chr_open_stdio(void)
if (stdio_nb_clients >= STDIO_MAX_CLIENTS) if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
return NULL; return NULL;
chr = qemu_chr_open_fd(0, 1); chr = qemu_chr_open_fd(0, 1);
chr->chr_write = stdio_write;
if (stdio_nb_clients == 0) if (stdio_nb_clients == 0)
qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, NULL); qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, NULL);
client_index = stdio_nb_clients; client_index = stdio_nb_clients;
...@@ -5638,6 +5616,7 @@ int main(int argc, char **argv) ...@@ -5638,6 +5616,7 @@ int main(int argc, char **argv)
SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE); SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE);
#endif #endif
init_timers(); init_timers();
init_timer_alarm();
register_machines(); register_machines();
machine = first_machine; machine = first_machine;
...@@ -6128,7 +6107,6 @@ int main(int argc, char **argv) ...@@ -6128,7 +6107,6 @@ int main(int argc, char **argv)
register_savevm("ram", 0, 1, ram_save, ram_load, NULL); register_savevm("ram", 0, 1, ram_save, ram_load, NULL);
init_ioports(); init_ioports();
cpu_calibrate_ticks();
/* terminal init */ /* terminal init */
if (nographic) { if (nographic) {
......
...@@ -390,6 +390,7 @@ int qemu_timer_pending(QEMUTimer *ts); ...@@ -390,6 +390,7 @@ int qemu_timer_pending(QEMUTimer *ts);
extern int64_t ticks_per_sec; extern int64_t ticks_per_sec;
extern int pit_min_timer_count; extern int pit_min_timer_count;
int64_t cpu_get_ticks(void);
void cpu_enable_ticks(void); void cpu_enable_ticks(void);
void cpu_disable_ticks(void); void cpu_disable_ticks(void);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册