diff --git a/samples/sample_ctl.c b/samples/sample_ctl.c index 88e570aa..54f996b1 100644 --- a/samples/sample_ctl.c +++ b/samples/sample_ctl.c @@ -5,6 +5,7 @@ #include #include +#include // code to be emulated @@ -160,11 +161,93 @@ void test_uc_ctl_exits() uc_close(uc); } +#define TB_COUNT (8) +#define TCG_MAX_INSNS (512) // from tcg.h +#define CODE_LEN TB_COUNT *TCG_MAX_INSNS + +double time_emulation(uc_engine *uc, uint64_t start, uint64_t end) +{ + time_t t1, t2; + + t1 = clock(); + + uc_emu_start(uc, start, end, 0, 0); + + t2 = clock(); + + return (t2 - t1) * 1000.0 / CLOCKS_PER_SEC; +} + +static void test_uc_ctl_tb_cache() +{ + uc_engine *uc; + uc_err err; + char code[CODE_LEN]; + double standard, cached, evicted; + + // Fill the code buffer with NOP. + memset(code, 0x90, CODE_LEN); + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + if (err) { + printf("Failed on uc_open() with error returned: %u\n", err); + return; + } + + err = uc_mem_map(uc, ADDRESS, 0x10000, UC_PROT_ALL); + if (err) { + printf("Failed on uc_mem_map() with error returned: %u\n", err); + return; + } + + // Write our code to the memory. + err = uc_mem_write(uc, ADDRESS, code, sizeof(code) - 1); + if (err) { + printf("Failed on uc_mem_write() with error returned: %u\n", err); + return; + } + + // Do emulation without any cache. + standard = time_emulation(uc, ADDRESS, ADDRESS + sizeof(code) - 1); + + // Now we request cache for all TBs. + for (int i = 0; i < TB_COUNT; i++) { + err = uc_ctl_request_cache(uc, ADDRESS + i * TCG_MAX_INSNS); + if (err) { + printf("Failed on uc_ctl() with error returned: %u\n", err); + return; + } + } + + // Do emulation with all TB cached. + cached = time_emulation(uc, ADDRESS, ADDRESS + sizeof(code) - 1); + + // Now we clear cache for all TBs. + for (int i = 0; i < TB_COUNT; i++) { + err = uc_ctl_remove_cache(uc, ADDRESS + i * TCG_MAX_INSNS); + if (err) { + printf("Failed on uc_ctl() with error returned: %u\n", err); + return; + } + } + + // Do emulation with all TB cache evicted. + evicted = time_emulation(uc, ADDRESS, ADDRESS + sizeof(code) - 1); + + printf(">>> Run time: First time: %f, Cached: %f, Cache evicted: %f\n", + standard, cached, evicted); + + uc_close(uc); +} + int main(int argc, char **argv, char **envp) { test_uc_ctl_read(); printf("====================\n"); test_uc_ctl_exits(); + printf("====================\n"); + test_uc_ctl_tb_cache(); return 0; } diff --git a/tests/unit/test_ctl.c b/tests/unit/test_ctl.c index 5cf19096..06fea9f4 100644 --- a/tests/unit/test_ctl.c +++ b/tests/unit/test_ctl.c @@ -1,4 +1,6 @@ #include "unicorn_test.h" +#include +#include const uint64_t code_start = 0x1000; const uint64_t code_len = 0x4000; @@ -63,9 +65,58 @@ static void test_uc_ctl_exits() OK(uc_close(uc)); } +double time_emulation(uc_engine *uc, uint64_t start, uint64_t end) +{ + time_t t1, t2; + + t1 = clock(); + + OK(uc_emu_start(uc, start, end, 0, 0)); + + t2 = clock(); + + return (t2 - t1) * 1000.0 / CLOCKS_PER_SEC; +} + +#define TB_COUNT (8) +#define TCG_MAX_INSNS (512) // from tcg.h +#define CODE_LEN TB_COUNT *TCG_MAX_INSNS + +static void test_uc_ctl_tb_cache() +{ + uc_engine *uc; + char code[CODE_LEN]; + double standard, cached, evicted; + + memset(code, 0x90, CODE_LEN); + + uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); + + standard = time_emulation(uc, code_start, code_start + sizeof(code) - 1); + + for (int i = 0; i < TB_COUNT; i++) { + OK(uc_ctl_request_cache(uc, code_start + i * TCG_MAX_INSNS)); + } + + cached = time_emulation(uc, code_start, code_start + sizeof(code) - 1); + + for (int i = 0; i < TB_COUNT; i++) { + OK(uc_ctl_remove_cache(uc, code_start + i * TCG_MAX_INSNS)); + } + evicted = time_emulation(uc, code_start, code_start + sizeof(code) - 1); + + // In fact, evicted is also slightly faster than standard but we don't do + // this guarantee. + TEST_CHECK(cached < standard); + TEST_CHECK(evicted > cached); + + OK(uc_close(uc)); +} + TEST_LIST = {{"test_uc_ctl_mode", test_uc_ctl_mode}, {"test_uc_ctl_page_size", test_uc_ctl_page_size}, {"test_uc_ctl_arch", test_uc_ctl_arch}, {"test_uc_ctl_time_out", test_uc_ctl_time_out}, {"test_uc_ctl_exits", test_uc_ctl_exits}, + {"test_uc_ctl_tb_cache", test_uc_ctl_tb_cache}, {NULL, NULL}}; \ No newline at end of file