From 029bc377a1befbc41861b8bb3ff17fd9ccd5ca8d Mon Sep 17 00:00:00 2001 From: Matt Borgerson Date: Wed, 3 Nov 2021 18:35:50 -0700 Subject: [PATCH] shitty spin loop detector --- accel/tcg/cpu-exec.c | 51 ++++++++++++++++++- accel/tcg/translate-all.c | 21 ++++++++ include/exec/exec-all.h | 2 + target/i386/helper.h | 1 + target/i386/tcg/sysemu/misc_helper.c | 16 ++++++ target/i386/tcg/translate.c | 76 ++++++++++++++++++++++++++++ 6 files changed, 166 insertions(+), 1 deletion(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 96ffbfeec1..522c666aa3 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -879,6 +879,22 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, /* main execution loop */ + + +/* Note: only supports millisecond resolution on Windows */ +static void sleep_ns(int64_t ns) +{ +#ifndef _WIN32 + struct timespec sleep_delay, rem_delay; + sleep_delay.tv_sec = ns / 1000000000LL; + sleep_delay.tv_nsec = ns % 1000000000LL; + nanosleep(&sleep_delay, &rem_delay); +#else + Sleep(ns / SCALE_MS); +#endif +} + + int cpu_exec(CPUState *cpu) { int ret; @@ -935,6 +951,8 @@ int cpu_exec(CPUState *cpu) assert_no_pages_locked(); } + int hotblocks = 0; + /* if an exception is pending, we execute it here */ while (!cpu_handle_exception(cpu, &ret)) { TranslationBlock *last_tb = NULL; @@ -989,12 +1007,43 @@ int cpu_exec(CPUState *cpu) } #endif /* See if we can patch the calling TB. */ - if (last_tb) { + if (last_tb && !tb->hot) { tb_add_jump(last_tb, tb_exit, tb); } cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit); + if (tb->hot) { + hotblocks += 1; + } else { + hotblocks = 0; + } + + /* Chill */ + if (hotblocks > 100) { + /* XXX: Eventually all regularly executed blocks will be + * considired 'hot' with current dumb counter. + * + * - Decayed binning + * - Temporal distance from vsync to estimate callback + * probability + * - See if cooling impacts cycles (if yes, then probably + * interrupt bound; if no, then probably false positive) + * - Regular sampling to measure which ones are most hot + * - Global exec counter for delta + */ + + hotblocks = 0; + /* XXX: Replace with schedule. Don't hold locks */ +#if 1 + sleep_ns(1000000); +#else + cpu->halted = 1; + cpu->exception_index = EXCP_HALTED; + qatomic_set(&cpu->exit_request, 1); +#endif + } + /* Try to align the host and virtual clocks if the guest is in advance */ align_clocks(&sc, cpu); diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index ef6b3d019e..2fc56ddc88 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1445,8 +1445,24 @@ TranslationBlock *tb_gen_code(CPUState *cpu, } QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS); + bool tb_hot = false; + tb = inv_tb_htable_lookup(cpu, pc, cs_base, flags, cflags); if (tb) { +#if 1 + if ((tb->xcount & (1ULL << 63)) && !tb->hot) { + fprintf(stderr, "hotblock re-translate %x\n", tb->pc); + + qemu_spin_lock(&tb->jmp_lock); + qatomic_set(&tb->cflags, tb->cflags & ~CF_INVALID); + qemu_spin_unlock(&tb->jmp_lock); + uint32_t h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb_cflags(tb), + tb->trace_vcpu_dstate); + qht_remove(&tb_ctx.inv_htable, tb, h); + tb_hot = true; + } else { +#endif + qemu_spin_lock(&tb->jmp_lock); qatomic_set(&tb->cflags, tb->cflags & ~CF_INVALID); qemu_spin_unlock(&tb->jmp_lock); @@ -1455,6 +1471,10 @@ TranslationBlock *tb_gen_code(CPUState *cpu, bool removed = qht_remove(&tb_ctx.inv_htable, tb, h); g_assert(removed); goto recycle_tb; + +#if 1 + } +#endif } buffer_overflow: @@ -1469,6 +1489,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, } gen_code_buf = tcg_ctx->code_gen_ptr; + tb->hot = tb_hot; tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf); tb->pc = pc; tb->cs_base = cs_base; diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 1c2aff028a..8b60135f06 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -492,6 +492,8 @@ struct TranslationBlock { target_ulong cs_base; /* CS base for this block */ uint32_t flags; /* flags defining in which context the code was generated */ uint32_t cflags; /* compile flags */ + uint64_t xcount; + bool hot; /* Note that TCG_MAX_INSNS is 512; we validate this match elsewhere. */ #define CF_COUNT_MASK 0x000001ff diff --git a/target/i386/helper.h b/target/i386/helper.h index ad7ffc612f..eb99d4082b 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -106,6 +106,7 @@ DEF_HELPER_FLAGS_2(flush_page, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(hlt, TCG_CALL_NO_WG, noreturn, env, int) DEF_HELPER_FLAGS_2(monitor, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_2(mwait, TCG_CALL_NO_WG, noreturn, env, int) +DEF_HELPER_FLAGS_2(hotblock, TCG_CALL_NO_WG, void, env, ptr) DEF_HELPER_1(rdmsr, void, env) DEF_HELPER_1(wrmsr, void, env) DEF_HELPER_FLAGS_2(read_crN, TCG_CALL_NO_RWG, tl, env, int) diff --git a/target/i386/tcg/sysemu/misc_helper.c b/target/i386/tcg/sysemu/misc_helper.c index e7a2ebde81..4517097b04 100644 --- a/target/i386/tcg/sysemu/misc_helper.c +++ b/target/i386/tcg/sysemu/misc_helper.c @@ -481,6 +481,22 @@ void QEMU_NORETURN helper_hlt(CPUX86State *env, int next_eip_addend) do_hlt(env); } +#include "exec/translate-all.h" + +void /*QEMU_NORETURN*/ helper_hotblock(CPUX86State *env, void *opaque) +{ + TranslationBlock *tb = opaque; + fprintf(stderr, "hotblock %x (%ld iterations)\n", tb->pc, tb->xcount & ~(1ULL << 63)); + +#if 1 + /* Re-translate */ + CPUState *cpu = env_cpu(env); + tb_invalidate_phys_page_range(tb->page_addr[0], tb->page_addr[0]); +#else + tb->hot = true; +#endif +} + void helper_monitor(CPUX86State *env, target_ulong ptr) { if ((uint32_t)env->regs[R_ECX] != 0) { diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 20fb88a564..1c91d231cb 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -7696,6 +7696,43 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_jmp_im(s, s->pc - s->cs_base); gen_eob_inhibit_irq(s, true); } + +#if 1 + /* + 0x8001b02e: sti + 0x8001b02f: nop + 0x8001b030: nop + 0x8001b031: cli + 0x8001b032: cmp 0x0(%ebp),%ebp + 0x8001b035: je 0x8001b043 + 0x8001b037: mov $0x2,%cl + 0x8001b039: call 0x8001415c + 0x8001b03e: call 0x8001b08f + 0x8001b043: cmpl $0x0,0x2c(%ebx) + 0x8001b047: je 0x8001b02e + 0x8001b049: sti + 0x8001b04a: mov 0x2c(%ebx),%esi + 0x8001b04d: mov 0x28(%ebx),%edi + 0x8001b050: movl $0x0,0x2c(%ebx) + */ + /* Shitty peek to see if this is the idle loop */ + if (translator_ldub(env, s->pc+0) == 0x90 && + translator_ldub(env, s->pc+1) == 0x90 && + translator_ldub(env, s->pc+2) == 0xfa + ) { + fprintf(stderr, "idle loop at @ %x\n", s->pc); + +#if 0 + /* Insert a HLT to exit CPU loop */ + if (check_cpl0(s)) { + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_hlt(cpu_env, tcg_const_i32(s->pc - pc_start)); + s->base.is_jmp = DISAS_NORETURN; + } +#endif + } +#endif break; case 0x62: /* bound */ if (CODE64(s)) @@ -9124,6 +9161,45 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) static void i386_tr_tb_start(DisasContextBase *db, CPUState *cpu) { + /* XXX: Don't need all this logic here..can periodically check instead */ + + // DisasContext *s = container_of(db, DisasContext, base); + TranslationBlock *tb = db->tb; + + if (tb->hot) { + tb->xcount = 1ULL<<63; + TCGv_i64 val = tcg_temp_local_new_i64(); + TCGv_ptr ptr = tcg_const_local_ptr(&tb->xcount); + tcg_gen_ld_i64(val, ptr, 0); + tcg_gen_addi_i64(val, val, 1); + tcg_gen_st_i64(val, ptr, 0); + tcg_temp_free_ptr(ptr); + tcg_temp_free_i64(val); + return; + } + + tb->xcount = 0; + + TCGLabel *l_doblock = gen_new_label(); + TCGv_i64 val = tcg_temp_local_new_i64(); + TCGv_ptr ptr = tcg_const_local_ptr(&tb->xcount); + + /* Increment exec counter */ + tcg_gen_ld_i64(val, ptr, 0); + tcg_gen_addi_i64(val, val, 1); + tcg_gen_st_i64(val, ptr, 0); + + tcg_gen_brcondi_i64(TCG_COND_LTU, val, 100000000, l_doblock); /* Still cold */ + tcg_gen_brcondi_i64(TCG_COND_GTU, val, 1ULL << 63, l_doblock); /* Already flagged */ + + /* Flag it */ + tcg_gen_ori_i64(val, val, 1ULL << 63); + tcg_gen_st_i64(val, ptr, 0); + gen_helper_hotblock(cpu_env, tcg_const_ptr(tb)); + + gen_set_label(l_doblock); + tcg_temp_free_ptr(ptr); + tcg_temp_free_i64(val); } static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)