qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RFC 06/14] Added support for block profiling for AArch32 a


From: Christopher Covington
Subject: [Qemu-devel] [RFC 06/14] Added support for block profiling for AArch32 and Aarch64
Date: Wed, 5 Aug 2015 12:51:15 -0400

Blocks are split on all branch instructions. Code assumes a translation
block does not end in a branch. When a branch instruction is identified,
gen_store_is_jmp(1) is called to indicate that a split should happen on
this instruction. The exit notifier is used to finalize the block
profiler, ensuring all block vector files are closed and the block stats
are printed to stdout when QEMU exits.

Written by Gideon Billings and Aaron Lindsay.

Signed-off-by: Christopher Covington <address@hidden>
---
 Makefile.objs              |   1 +
 bbv_profiler.c             |  77 +++++++
 bbv_profiler.h             |  35 ++++
 configure                  |  19 ++
 include/exec/cpu-defs.h    |   6 +
 qemu-options.hx            |  10 +
 rules.mak                  |   3 +-
 target-arm/helper.c        |  57 +++++
 target-arm/helper.h        |   6 +
 target-arm/translate-a64.c |  80 +++++++
 target-arm/translate.c     | 503 ++++++++++++++++++++++++++++++++++++++++++++-
 vl.c                       |  58 ++++++
 12 files changed, 850 insertions(+), 5 deletions(-)
 create mode 100644 bbv_profiler.c
 create mode 100644 bbv_profiler.h

diff --git a/Makefile.objs b/Makefile.objs
index 28999d3..7da955a 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -2,6 +2,7 @@
 # Common libraries for tools and emulators
 stub-obj-y = stubs/
 util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
+util-obj-$(CONFIG_BBVEC) += bbv_profiler.o
 
 #######################################################################
 # block-obj-y is code used by both qemu system emulation and qemu-img
diff --git a/bbv_profiler.c b/bbv_profiler.c
new file mode 100644
index 0000000..51e8060
--- /dev/null
+++ b/bbv_profiler.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 the Linux Foundation.
+ * Written by Gideon Billings and Aaron Lindsay.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include "bbv_profiler.h"
+
+static BasicBlockTraceHandle trace = NULL;
+static uint32_t mode = 0;
+static uint64_t pid = 0;
+static Notifier exit_notifier;
+
+static void bbtrace_exit_notifier(Notifier *notifier, void *data)
+{
+       assert(trace != NULL);
+
+       // In addition to freeing memory, bbvec_free also closes open files and
+       // prints statistics.
+       bbvec_free(trace);
+}
+
+void bbtrace_init(uint64_t interval_size, const char *filename_base, bool 
trace_userspace_only, bool combine_pids)
+{
+       assert(trace == NULL);
+       trace = bbvec_create(interval_size, filename_base, 
trace_userspace_only, combine_pids);
+}
+
+Notifier* get_bbtrace_exit_notifier(void) {
+       exit_notifier.notify = &bbtrace_exit_notifier;
+       return &exit_notifier;
+}
+
+int bbtrace_initialized(void) {
+       return (trace != 0);
+}
+
+void bb_process(uint64_t PC, uint64_t IC)
+{
+       assert(trace != NULL);
+
+       bbvec_insert_bb(trace, PC, IC);
+}
+
+void bb_context_check_mode(uint64_t IC, uint32_t new_mode)
+{
+       assert(trace != NULL);
+
+       if (mode != new_mode) {
+               mode = new_mode;
+               bool usrmode = !new_mode;
+               bbvec_mode_change(trace, usrmode, IC);
+       }
+}
+
+void bb_context_check_pid(uint64_t IC, uint64_t new_pid)
+{
+       assert(trace != NULL);
+
+       if (pid != new_pid) {
+               pid = new_pid;
+               bbvec_pid_change(trace, new_pid, IC);
+       }
+}
diff --git a/bbv_profiler.h b/bbv_profiler.h
new file mode 100644
index 0000000..26dfa1f
--- /dev/null
+++ b/bbv_profiler.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 the Linux Foundation.
+ * Written by Gideon Billings and Aaron Lindsay.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BBV_PROFILER
+#define BBV_PROFILER
+
+#include "bbvec_trace.h"
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "sysemu/sysemu.h"
+
+void bbtrace_init(uint64_t interval_size, const char *filename_base, bool 
trace_userspace_only, bool combine_pids);
+Notifier* get_bbtrace_exit_notifier(void);
+int bbtrace_initialized(void);
+void bb_process(uint64_t PC, uint64_t IC);
+void bb_context_check_mode(uint64_t IC, uint32_t mode);
+void bb_context_check_pid(uint64_t IC, uint64_t tpid);
+
+#endif
diff --git a/configure b/configure
index 255d85b..5ddb2a6 100755
--- a/configure
+++ b/configure
@@ -29,6 +29,8 @@ TMPL="${TMPDIR1}/${TMPB}.lo"
 TMPA="${TMPDIR1}/lib${TMPB}.la"
 TMPE="${TMPDIR1}/${TMPB}.exe"
 
+LBBVEC_FLAG=""
+
 rm -f config.log
 
 # Print a helpful header at the top of config.log
@@ -337,6 +339,9 @@ vhdx=""
 quorum=""
 numa=""
 tcmalloc="no"
+libbbvec_prefix=""
+bbvec_lib=""
+bbvec_include=""
 
 # parse CC options first
 for opt do
@@ -774,6 +779,15 @@ for opt do
   ;;
   --target-list=*) target_list="$optarg"
   ;;
+  --enable-bb-profile)
+    bbvec="yes"
+    
libbbvec_prefix="/prj/qct/qctps/modeling/ral_armv8/workloads/libbbvec/latest"
+    bbvec_lib="$libbbvec_prefix/lib"
+    bbvec_include="$libbbvec_prefix/include"
+    LDFLAGS="-L$bbvec_lib $LDFLAGS"
+    LBBVEC_FLAG="-lbbvec"
+    QEMU_INCLUDES="-I$bbvec_include $QEMU_INCLUDES"
+  ;;
   --enable-trace-backends=*) trace_backends="$optarg"
   ;;
   # XXX: backwards compatibility
@@ -4980,6 +4994,10 @@ if test "$rdma" = "yes" ; then
   echo "CONFIG_RDMA=y" >> $config_host_mak
 fi
 
+if test "$bbvec" = "yes" ; then
+  echo "CONFIG_BBVEC=y" >> $config_host_mak
+fi
+
 # Hold two types of flag:
 #   CONFIG_THREAD_SETNAME_BYTHREAD  - we've got a way of setting the name on
 #                                     a thread we have a handle to
@@ -5040,6 +5058,7 @@ echo "CFLAGS=$CFLAGS" >> $config_host_mak
 echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak
 echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
 echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak
+echo "LBBVEC_FLAG=$LBBVEC_FLAG" >> $config_host_mak
 if test "$sparse" = "yes" ; then
   echo "CC           := REAL_CC=\"\$(CC)\" cgcc"       >> $config_host_mak
   echo "CPP          := REAL_CC=\"\$(CPP)\" cgcc"      >> $config_host_mak
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index 3f56546..1eed930 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -134,5 +134,11 @@ typedef struct CPUIOTLBEntry {
 #define CPU_COMMON                                                      \
     /* soft mmu support */                                              \
     CPU_COMMON_TLB                                                      \
+    /* Instruction Count (for profiling) */                                    
\
+    uint64_t prof_ic;                                                   \
+    /* PC (for profiling) */                                            \
+    uint64_t prof_pc;                                                   \
+    /* next page start (for profiling) */                               \
+    uint64_t prof_is_jmp;                                               \
 
 #endif
diff --git a/qemu-options.hx b/qemu-options.hx
index ec356f6..e3b1a6a 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -913,6 +913,16 @@ with a serial console.  Use @key{C-a h} for help on 
switching between
 the console and monitor.
 ETEXI
 
+DEF("bbvec", HAS_ARG, QEMU_OPTION_bbvec,
+    "-bbvec 
[intervalSize=i][,fileBase=file][,userspaceOnly=i][,combinePIDs=i]\n"
+       "               enable basic block vector profiling\n", QEMU_ARCH_ALL)
+STEXI
address@hidden -bbvec @var{option}[,@var{option}[,@var{option}[,...]]]
address@hidden -bbvec
+This option enables basic block vector profiling. Blocks are split on
+any branch instruction.
+ETEXI
+
 DEF("curses", 0, QEMU_OPTION_curses,
     "-curses         use a curses/ncurses interface instead of SDL\n",
     QEMU_ARCH_ALL)
diff --git a/rules.mak b/rules.mak
index 3a05627..9830f71 100644
--- a/rules.mak
+++ b/rules.mak
@@ -65,7 +65,7 @@ LINKPROG = $(or $(CXX),$(CC))
 ifeq ($(LIBTOOL),)
 LINK = $(call quiet-command, $(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) 
-o $@ \
        $(call process-archive-undefs, $1) \
-       $(version-obj-y) $(call extract-libs,$1) $(LIBS),"  LINK  
$(TARGET_DIR)$@")
+       $(LBBVEC_FLAG) $(version-obj-y) $(call extract-libs,$1) $(LIBS),"  LINK 
 $(TARGET_DIR)$@")
 else
 LIBTOOL += $(if $(V),,--quiet)
 %.lo: %.c
@@ -79,6 +79,7 @@ LINK = $(call quiet-command,\
        $(if $(filter %.lo %.la,$1),$(LIBTOOL) --mode=link --tag=CC \
        )$(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \
        $(call process-archive-undefs, $1)\
+       $(LBBVEC_FLAG) \
        $(if $(filter %.lo %.la,$1),$(version-lobj-y),$(version-obj-y)) \
        $(if $(filter %.lo %.la,$1),$(LIBTOOLFLAGS)) \
        $(call extract-libs,$(1:.lo=.o)) $(LIBS),$(if $(filter %.lo 
%.la,$1),"lt LINK ", "  LINK  ")"$(TARGET_DIR)$@")
diff --git a/target-arm/helper.c b/target-arm/helper.c
index ff3c8f7..d2c02be 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -11,6 +11,10 @@
 #include "arm_ldst.h"
 #include <zlib.h> /* For crc32 */
 
+#ifdef CONFIG_BBVEC
+#include "bbv_profiler.h"
+#endif // CONFIG_BBVEC
+
 #ifndef CONFIG_USER_ONLY
 
 static inline int get_phys_addr(CPUARMState *env, target_ulong address,
@@ -4005,6 +4009,59 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t 
mask)
     env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);
 }
 
+#ifdef CONFIG_BBVEC
+void HELPER(bbv_profile)(CPUARMState *env)
+{
+    /* Helper profiles previous tb, so value of PC will be 0 on first call.
+     * Also make sure basic block split is on a branch. Otherwise, join with
+     * next basic block.
+     */
+    if (env->prof_pc && env->prof_is_jmp) {
+        bb_process(env->prof_pc, env->prof_ic);
+        env->prof_ic = 0;
+    }
+}
+
+void HELPER(context_check_mode)(CPUARMState *env)
+{
+    uint32_t mode;
+
+    /* Get current mode: userspace or privileged */
+    if (env->aarch64) {
+        mode = extract32(env->pstate, 2, 2);
+    }
+    else if ((env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR) {
+        mode = 0;
+    }
+    /* We don't currently implement the Virtualization or TrustZone
+     * extensions, so PL2 and PL3 don't exist for us.
+     */
+    else mode = 1;
+
+    bb_context_check_mode(env->prof_ic, mode);
+}
+
+void HELPER(context_check_pid)(CPUARMState *env)
+{
+    uint64_t pid;
+
+    /* Read pid from CONTEXTIDR register. In aarch32, if EL1 is not in AArch64
+     * mode, we need to shift out the address space identifier in the first 8 
bits.
+     *
+     * FIXME: according to the Arm instruction reference, CONTEXTIDR only 
contains
+     * the address identifier if TTBVR.EAE = 1. We need to check if this is
+     * the same in QEMU.
+     */
+       if (arm_el_is_aa64(env, 1)) {
+        pid = env->cp15.contextidr_el[1];
+    }
+    else
+       pid = env->cp15.contextidr_el[1] >> 8;
+
+    bb_context_check_pid(env->prof_ic, pid);
+}
+#endif //CONFIG_BBVEC
+
 /* Sign/zero extend */
 uint32_t HELPER(sxtb16)(uint32_t x)
 {
diff --git a/target-arm/helper.h b/target-arm/helper.h
index dec3728..41291c9 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -37,6 +37,12 @@ PAS_OP(uq)
 PAS_OP(uh)
 #undef PAS_OP
 
+#ifdef CONFIG_BBVEC
+DEF_HELPER_1(bbv_profile, void, env)
+DEF_HELPER_1(context_check_mode, void, env)
+DEF_HELPER_1(context_check_pid, void, env)
+#endif // CONFIG_BBVEC
+
 DEF_HELPER_3(ssat, i32, env, i32, i32)
 DEF_HELPER_3(usat, i32, env, i32, i32)
 DEF_HELPER_3(ssat16, i32, env, i32, i32)
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index 20d1d3c..80b27ed 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -37,6 +37,10 @@
 
 #include "trace-tcg.h"
 
+#ifdef CONFIG_BBVEC
+#include "bbv_profiler.h"
+#endif // CONFIG_BBVEC
+
 static TCGv_i64 cpu_X[32];
 static TCGv_i64 cpu_pc;
 static TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF;
@@ -45,6 +49,15 @@ static TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF;
 static TCGv_i64 cpu_exclusive_addr;
 static TCGv_i64 cpu_exclusive_val;
 static TCGv_i64 cpu_exclusive_high;
+#ifdef CONFIG_BBVEC
+/* cpu_pc is used for generating jumps and does not always track the
+ * current pc. cpu_prof_pc is used by the basic block profiling code to
+ * keep track of the current pc, so the last pc in the block can be
+ * captured.   */
+static TCGv_i64 cpu_prof_pc;
+static TCGv_i64 cpu_prof_ic;
+static TCGv_i64 cpu_prof_is_jmp;
+#endif // CONFIG_BBVEC
 #ifdef CONFIG_USER_ONLY
 static TCGv_i64 cpu_exclusive_test;
 static TCGv_i32 cpu_exclusive_info;
@@ -109,6 +122,13 @@ void a64_translate_init(void)
     cpu_CF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, CF), 
"CF");
     cpu_VF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, VF), 
"VF");
 
+#ifdef CONFIG_BBVEC
+    // bbvec profiling globals
+    cpu_prof_ic = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, 
prof_ic), "prof_ic");
+    cpu_prof_pc = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, 
prof_pc), "prof_pc");
+    cpu_prof_is_jmp = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, 
prof_is_jmp), "prof_is_jmp");
+#endif // CONFIG_BBVEC
+
     cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0,
         offsetof(CPUARMState, exclusive_addr), "exclusive_addr");
     cpu_exclusive_val = tcg_global_mem_new_i64(TCG_AREG0,
@@ -188,6 +208,24 @@ void gen_a64_set_pc_im(uint64_t val)
     tcg_gen_movi_i64(cpu_pc, val);
 }
 
+#ifdef CONFIG_BBVEC
+/* Basic block profiling functions */
+static void gen_insn_cnt_incr(CPUARMState * env, DisasContext *s)
+{
+    tcg_gen_addi_i64(cpu_prof_ic, cpu_prof_ic, 1);
+}
+
+static void gen_pc_incr(CPUARMState * env, DisasContext *s)
+{
+    tcg_gen_movi_i64(cpu_prof_pc, s->pc);
+}
+
+static void gen_store_is_jmp(uint32_t jmp)
+{
+    tcg_gen_movi_i64(cpu_prof_is_jmp, jmp);
+}
+#endif // CONFIG_BBVEC
+
 static void gen_exception_internal(int excp)
 {
     TCGv_i32 tcg_excp = tcg_const_i32(excp);
@@ -1584,9 +1622,17 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t 
insn)
     switch (opc) {
     case 0: /* BR */
     case 2: /* RET */
+#ifdef CONFIG_BBVEC
+        if (bbtrace_initialized())
+            gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
         tcg_gen_mov_i64(cpu_pc, cpu_reg(s, rn));
         break;
     case 1: /* BLR */
+#ifdef CONFIG_BBVEC
+        if (bbtrace_initialized())
+            gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
         tcg_gen_mov_i64(cpu_pc, cpu_reg(s, rn));
         tcg_gen_movi_i64(cpu_reg(s, 30), s->pc);
         break;
@@ -1619,15 +1665,31 @@ static void disas_b_exc_sys(DisasContext *s, uint32_t 
insn)
     switch (extract32(insn, 25, 7)) {
     case 0x0a: case 0x0b:
     case 0x4a: case 0x4b: /* Unconditional branch (immediate) */
+#ifdef CONFIG_BBVEC
+        if (bbtrace_initialized())
+            gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
         disas_uncond_b_imm(s, insn);
         break;
     case 0x1a: case 0x5a: /* Compare & branch (immediate) */
+#ifdef CONFIG_BBVEC
+        if (bbtrace_initialized())
+            gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
         disas_comp_b_imm(s, insn);
         break;
     case 0x1b: case 0x5b: /* Test & branch (immediate) */
+#ifdef CONFIG_BBVEC
+        if (bbtrace_initialized())
+            gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
         disas_test_b_imm(s, insn);
         break;
     case 0x2a: /* Conditional branch (immediate) */
+#ifdef CONFIG_BBVEC
+        if (bbtrace_initialized())
+            gen_store_is_jmp(1);
+#endif // CONFIG_BBVEC
         disas_cond_b_imm(s, insn);
         break;
     case 0x6a: /* Exception generation / System */
@@ -11006,7 +11068,25 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
 
     tcg_clear_temp_count();
 
+#ifdef CONFIG_BBVEC
+    /* Profile previously run block, check for PID change, and initialize
+     * prof_is_jmp flag.   */
+    if (bbtrace_initialized()) {
+        gen_helper_bbv_profile(cpu_env);
+        gen_store_is_jmp(0);
+        gen_helper_context_check_pid(cpu_env);
+    }
+#endif // CONFIG_BBVEC
+
     do {
+#ifdef CONFIG_BBVEC
+        if (bbtrace_initialized()) {
+            gen_helper_context_check_mode(cpu_env);
+            gen_insn_cnt_incr(env, dc);
+            gen_pc_incr(env, dc);
+        }
+#endif // CONFIG_BBVEC
+
         if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) {
             QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {
                 if (bp->pc == dc->pc) {
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 9116529..7a19a8b 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -37,6 +37,9 @@
 
 #include "trace-tcg.h"
 
+#ifdef CONFIG_BBVEC
+#include "bbv_profiler.h"
+#endif
 
 #define ENABLE_ARCH_4T    arm_dc_feature(s, ARM_FEATURE_V4T)
 #define ENABLE_ARCH_5     arm_dc_feature(s, ARM_FEATURE_V5)
@@ -67,6 +70,11 @@ static TCGv_i32 cpu_R[16];
 static TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF;
 static TCGv_i64 cpu_exclusive_addr;
 static TCGv_i64 cpu_exclusive_val;
+#ifdef CONFIG_BBVEC
+static TCGv_i64 cpu_prof_ic;
+static TCGv_i64 cpu_prof_pc;
+static TCGv_i64 cpu_prof_is_jmp;
+#endif
 #ifdef CONFIG_USER_ONLY
 static TCGv_i64 cpu_exclusive_test;
 static TCGv_i32 cpu_exclusive_info;
@@ -99,6 +107,13 @@ void arm_translate_init(void)
     cpu_VF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, VF), 
"VF");
     cpu_ZF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, ZF), 
"ZF");
 
+#ifdef CONFIG_BBVEC
+    // bbvec profiling globals
+    cpu_prof_ic = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, 
prof_ic), "prof_ic");
+    cpu_prof_pc = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, 
prof_pc), "prof_pc");
+    cpu_prof_is_jmp = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, 
prof_is_jmp), "prof_is_jmp");
+#endif
+
     cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0,
         offsetof(CPUARMState, exclusive_addr), "exclusive_addr");
     cpu_exclusive_val = tcg_global_mem_new_i64(TCG_AREG0,
@@ -177,11 +192,33 @@ static inline TCGv_i32 load_reg(DisasContext *s, int reg)
     return tmp;
 }
 
+#ifdef CONFIG_BBVEC
+/* Basic block profiling functions */
+static void gen_insn_cnt_incr(CPUARMState * env, DisasContext *s)
+{
+    tcg_gen_addi_i64(cpu_prof_ic, cpu_prof_ic, 1);
+}
+
+static void gen_pc_incr(CPUARMState * env, DisasContext *s)
+{
+    tcg_gen_movi_i64(cpu_prof_pc, s->pc);
+}
+
+static void gen_store_is_jmp(uint32_t jmp)
+{
+    tcg_gen_movi_i64(cpu_prof_is_jmp, jmp);
+}
+#endif
+
 /* Set a CPU register.  The source must be a temporary and will be
    marked as dead.  */
 static void store_reg(DisasContext *s, int reg, TCGv_i32 var)
 {
     if (reg == 15) {
+#ifdef CONFIG_BBVEC
+        if (bbtrace_initialized())
+            gen_store_is_jmp(1);
+#endif
         tcg_gen_andi_i32(var, var, ~1);
         s->is_jmp = DISAS_JUMP;
     }
@@ -834,6 +871,10 @@ static inline void gen_bx_im(DisasContext *s, uint32_t 
addr)
 {
     TCGv_i32 tmp;
 
+#ifdef CONFIG_BBVEC
+    if (bbtrace_initialized())
+        gen_store_is_jmp(1);
+#endif
     s->is_jmp = DISAS_UPDATE;
     if (s->thumb != (addr & 1)) {
         tmp = tcg_temp_new_i32();
@@ -847,6 +888,10 @@ static inline void gen_bx_im(DisasContext *s, uint32_t 
addr)
 /* Set PC and Thumb state from var.  var is marked as dead.  */
 static inline void gen_bx(DisasContext *s, TCGv_i32 var)
 {
+#ifdef CONFIG_BBVEC
+    if (bbtrace_initialized())
+        gen_store_is_jmp(1);
+#endif
     s->is_jmp = DISAS_UPDATE;
     tcg_gen_andi_i32(cpu_R[15], var, ~1);
     tcg_gen_andi_i32(var, var, 1);
@@ -3963,6 +4008,10 @@ static inline void gen_jmp (DisasContext *s, uint32_t 
dest)
             dest |= 1;
         gen_bx_im(s, dest);
     } else {
+#ifdef CONFIG_BBVEC
+        if (bbtrace_initialized())
+            gen_store_is_jmp(1);
+#endif
         gen_goto_tb(s, 0, dest);
         s->is_jmp = DISAS_TB_JUMP;
     }
@@ -7795,6 +7844,257 @@ static void disas_arm_insn(DisasContext *s, unsigned 
int insn)
         goto illegal_op;
     }
     if (cond != 0xe) {
+#ifdef CONFIG_BBVEC
+        /* For conditional arm instructions, checks for branching instructions
+         * need to happen here before the conditional is evaluated for basic
+         * block profiling. This is because if the conditional is false, a 
direct
+         * jump to the next instruction is generated and any code after this 
point
+         * will be skipped. We only want to split blocks on conditional 
instructions
+         * that are branches, or instructions that set the pc. The code below 
is
+         * taken from the code for decoding the instruction directly after this
+         * conditional check. Only the cases that lead to the generation of a 
jump or
+         * pc write are checked. Instructions that can write the pc are 
identified by
+         * calls to store_reg(), store_reg_bx(), and store_reg_from_load() 
where the
+         * value of reg can be 15. Instructions that generate a branch are 
identified
+         * by calls to gen_bx_im(), gen_bx(), and gen_jmp().
+         */
+        if (bbtrace_initialized()) {
+            if ((insn & 0x0f900000) == 0x03000000) {
+                if ((insn & (1 << 21)) == 0) {
+                    rd = (insn >> 12) & 0xf;
+                    /* possible write to pc register */
+                    if (rd == 15)
+                        gen_store_is_jmp(1);
+                }
+            }
+            else if ((insn & 0x0f900000) == 0x01000000
+                && (insn & 0x00000090) != 0x00000090) {
+                /* miscellaneous instructions */
+                op1 = (insn >> 21) & 3;
+                sh = (insn >> 4) & 0xf;
+                rm = insn & 0xf;
+                switch (sh) {
+                case 0x0:
+                    if (!(op1 & 1)) {
+                        rd = (insn >> 12) & 0xf;
+                        /* possible write to pc register */
+                        if (rd == 15)
+                            gen_store_is_jmp(1);
+                    }
+                    break;
+                case 0x1:
+                    if (op1 == 1)
+                        gen_store_is_jmp(1);
+                    else if (op1 == 3) {
+                        /* clz */
+                        rd = (insn >> 12) & 0xf;
+                        if (rd == 15)
+                            gen_store_is_jmp(1);
+                    }
+                    break;
+                case 0x2:
+                    if (op1 == 1)
+                        gen_store_is_jmp(1);
+                    break;
+                case 0x3:
+                    if (op1 == 1)
+                        gen_store_is_jmp(1);
+                    break;
+                case 0x4:
+                    rd = extract32(insn, 12, 4);
+                    if (rd == 15)
+                        gen_store_is_jmp(1);
+                    break;
+                case 0x5:
+                    rd = (insn >> 12) & 0xf;
+                    if (rd == 15)
+                        gen_store_is_jmp(1);
+                    break;
+                case 0x8: case 0xa: case 0xc: case 0xe:
+                    rd = (insn >> 16) & 0xf;
+                    if (rd == 15) {
+                        if (op1 != 2)
+                            gen_store_is_jmp(1);
+                    }
+                    break;
+                default:
+                    break;
+                }
+            }
+            else if (((insn & 0x0e000000) == 0 &&
+                    (insn & 0x00000090) != 0x90) ||
+                    ((insn & 0x0e000000) == (1 << 25))) {
+                int set_cc, logic_cc;
+                op1 = (insn >> 21) & 0xf;
+                set_cc = (insn >> 20) & 1;
+                logic_cc = table_logic_cc[op1] & set_cc;
+                rd = (insn >> 12) & 0xf;
+                if (rd == 15) {
+                    if (op1 == 0x00 || op1 == 0x01 || op1 == 0x03 ||
+                        op1 == 0x05 || op1 == 0x06 || op1 == 0x07 ||
+                        op1 == 0x0c || op1 == 0x0e || op1 == 0x0f)
+                        gen_store_is_jmp(1);
+                    else if (op1 == 0x02 && !set_cc)
+                        gen_store_is_jmp(1);
+                    else if (op1 == 0x0d && !logic_cc)
+                        gen_store_is_jmp(1);
+                }
+            }
+            else {
+                /* other instructions */
+                op1 = (insn >> 24) & 0xf;
+                switch(op1) {
+                case 0x0:
+                case 0x1:
+                    /* multiplies, extra load/stores */
+                    sh = (insn >> 5) & 3;
+                    if (sh == 0) {
+                        if (op1 == 0x0) {
+                            rd = (insn >> 16) & 0xf;
+                            rn = (insn >> 12) & 0xf;
+                            op1 = (insn >> 20) & 0xf;
+                            switch (op1) {
+                            case 0: case 1: case 2: case 3: case 6:
+                                if (rd == 15)
+                                    gen_store_is_jmp(1);
+                                break;
+                            case 8: case 9: case 10: case 11:
+                            case 12: case 13: case 14: case 15:
+                                if (rn == 15 || rd == 15)
+                                    gen_store_is_jmp(1);
+                                break;
+                            }
+                        } else {
+                            rd = (insn >> 12) & 0xf;
+                            if (rd == 15) {
+                                if (insn & (1 << 23)) {
+                                    /* load/store exclusive */
+                                    int op2 = (insn >> 8) & 3;
+                                    if (op2 == 0) {
+                                        if (insn & (1 << 20))
+                                            gen_store_is_jmp(1);
+                                    }
+                                } else {
+                                    gen_store_is_jmp(1);
+                                }
+                            }
+                        }
+                    } else {
+                        /* Misc load/store */
+                        int load = 0;
+                        rd = (insn >> 12) & 0xf;
+                        rn = (insn >> 16) & 0xf;
+                        if (insn & (1 << 20))
+                            load = 1;
+                        else if (sh & 2) {
+                            if (sh & 1)
+                                load = 0;
+                            else if (rd == 15) {
+                                gen_store_is_jmp(1);
+                                load = 1;
+                            }
+                        }
+                        if (!(insn & (1 << 24)) && rn == 15)
+                            gen_store_is_jmp(1);
+                        else if ((insn & (1 << 21)) && rn == 15)
+                            gen_store_is_jmp(1);
+                        if (load && rd == 15)
+                            gen_store_is_jmp(1);
+                        break;
+                    }
+                    break;
+                case 0x4:
+                case 0x5:
+                    goto prof_do_ldst;
+                case 0x6:
+                case 0x7:
+                    if (insn & (1 << 4)) {
+                        rd = (insn >> 12) & 0xf;
+                        rn = (insn >> 16) & 0xf;
+                        switch ((insn >> 23) & 3) {
+                        case 0: /* Parallel add/subtract.  */
+                            if (rd == 15)
+                                gen_store_is_jmp(1);
+                            break;
+                        case 1:
+                            if (rd == 15)
+                                gen_store_is_jmp(1);
+                            break;
+                        case 2: /* Multiplies (Type 3).  */
+                            switch ((insn >> 20) & 0x7) {
+                            case 5:
+                                if (rn == 15)
+                                    gen_store_is_jmp(1);
+                                break;
+                            case 0:
+                            case 4:
+                                if (!(insn & (1 << 22)) && rn == 15)
+                                    gen_store_is_jmp(1);
+                                break;
+                            case 1:
+                            case 3:
+                                if (rn == 15)
+                                    gen_store_is_jmp(1);
+                                break;
+                            }
+                            break;
+                        case 3:
+                            op1 = ((insn >> 17) & 0x38) | ((insn >> 5) & 7);
+                            if (op1 == 0 && rn == 15)
+                                gen_store_is_jmp(1);
+                            else if (rd == 15)
+                                gen_store_is_jmp(1);
+                            break;
+                         }
+                         break;
+                     }
+                prof_do_ldst:
+                    rn = (insn >> 16) & 0xf;
+                    rd = (insn >> 12) & 0xf;
+                    if (!(insn & (1 << 24)) && rn == 15)
+                        gen_store_is_jmp(1);
+                    else if ((insn & (1 << 21)) && rn == 15)
+                        gen_store_is_jmp(1);
+                    if ((insn & (1 << 20)) && rd == 15)
+                        gen_store_is_jmp(1);
+                    break;
+                case 0x08:
+                case 0x09:
+                    {
+                        int i, user, loaded_base;
+                        loaded_base = 0;
+                        rn = (insn >> 16) & 0xf;
+                        user = 0;
+                        if (insn & (1 << 22)) {
+                            if ((insn & (1 << 15)) == 0)
+                            user = 1;
+                        }
+                        if (!user) {
+                            for(i=0;i<16;i++) {
+                                if ((insn & (1 << i)) && (insn & (1 << 20))) {
+                                    if (i != rn)
+                                        gen_store_is_jmp(1);
+                                    else
+                                        loaded_base = 1;
+                                }
+                            }
+                        }
+                        if ((insn & (1 << 21)) && rn == 15)
+                            gen_store_is_jmp(1);
+                        if (loaded_base && rn == 15)
+                            gen_store_is_jmp(1);
+                        break;
+                    }
+                case 0xa:
+                case 0xb:
+                    gen_store_is_jmp(1);
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+#endif // CONFIG_BBVEC
         /* if not always execute, we generate a conditional jump to
            next instruction */
         s->condlabel = gen_new_label();
@@ -9944,6 +10244,10 @@ static int disas_thumb2_insn(CPUARMState *env, 
DisasContext *s, uint16_t insn_hw
                     }
                 }
             } else {
+#ifdef CONFIG_BBVEC
+                if (bbtrace_initialized())
+                    gen_store_is_jmp(1);
+#endif
                 /* Conditional branch.  */
                 op = (insn >> 22) & 0xf;
                 /* Generate a conditional jump to next instruction.  */
@@ -10302,16 +10606,177 @@ static void disas_thumb_insn(CPUARMState *env, 
DisasContext *s)
     TCGv_i32 tmp2;
     TCGv_i32 addr;
 
+    insn = arm_lduw_code(env, s->pc, s->bswap_code);
+
     if (s->condexec_mask) {
         cond = s->condexec_cond;
         if (cond != 0x0e) {     /* Skip conditional when condition is AL. */
-          s->condlabel = gen_new_label();
-          arm_gen_test_cc(cond ^ 1, s->condlabel);
-          s->condjmp = 1;
+#ifdef CONFIG_BBVEC
+            /* For conditional arm instructions, checks for branching 
instructions
+             * need to happen here before the conditional is evaluated for 
basic
+             * block profiling. This is because if the conditional is false, a 
direct
+             * jump to the next instruction is generated and any code after 
this point
+             * will be skipped. We only want to split blocks on conditional 
instructions
+             * that are branches, or instructions that set the pc. The code 
below is
+             * taken from the code for decoding the instruction directly after 
this
+             * conditional check. Only the cases that lead to the generation 
of a jump or
+             * pc write are checked. Instructions that can write the pc are 
identified by
+             * calls to store_reg(), store_reg_bx(), and store_reg_from_load() 
where the
+             * value of reg can be 15. Instructions that generate a branch are 
identified
+             * by calls to gen_bx_im(), gen_bx(), and gen_jmp().
+             */
+            if (bbtrace_initialized()) {
+                switch (insn >> 12) {
+                case 0: case 1:
+                    rd = insn & 7;
+                    /* possible write to pc */
+                    if (rd == 15)
+                        gen_store_is_jmp(1);
+                    break;
+                case 2: case 3:
+                    /* arithmetic large immediate */
+                    /* possible write to pc */
+                    op = (insn >> 11) & 3;
+                    rd = (insn >> 8) & 0x7;
+                    if (rd == 15) {
+                        if (op == 0 || op == 2 || op == 3) /* mov, add, sub */
+                            gen_store_is_jmp(1);
+                    }
+                    break;
+                case 4:
+                    if (insn & (1 << 11)) {
+                        rd = (insn >> 8) & 7;
+                        /* possible write to pc */
+                        if (rd == 15)
+                            gen_store_is_jmp(1);
+                        break;
+                    }
+                    if (insn & (1 << 10)) {
+                        rd = (insn & 7) | ((insn >> 4) & 8);
+                        op = (insn >> 8) & 3;
+                        /* possible write to pc */
+                        if (rd == 15) {
+                            if (op == 0 || op == 2) /* cmp, mov/cpy */
+                                gen_store_is_jmp(1);
+                            break;
+                        }
+                        /* branch instruction */
+                        if (op == 3) /* branch [and link] exchange thumb 
register */
+                            gen_store_is_jmp(1);
+                        break;
+                    }
+                    /* data processing register */
+                    rd = insn & 7;
+                    rm = (insn >> 3) & 7;
+                    op = (insn >> 6) & 0xf;
+                    if (op == 2 || op == 3 || op == 4 || op == 7) {
+                        /* the shift/rotate ops want the operands backwards */
+                        val = rm;
+                        rm = rd;
+                        rd = val;
+                        val = 1;
+                    } else {
+                        val = 0;
+                    }
+                    if (op == 0xf) { /* mvn */
+                        val = 1;
+                        rm = rd;
+                    }
+                    if (rd != 16) {
+                        if (val) {
+                            /* possible write to pc */
+                            if (rm == 15)
+                                gen_store_is_jmp(1);
+                        } else {
+                            /* possible write to pc */
+                            if (rd == 15)
+                                gen_store_is_jmp(1);
+                        }
+                    }
+                    break;
+                case 5:
+                    /* load/store register offset. */
+                    rd = insn & 7;
+                    op = (insn >> 9) & 7;
+                    /* load to pc register */
+                    if (op >= 3 && rd == 15)
+                        gen_store_is_jmp(1);
+                    break;
+                case 6: case 7: case 8:
+                    rd = insn & 7;
+                    /* load to pc register */
+                    if ((insn & (1 << 11)) && rd == 15)
+                        gen_store_is_jmp(1);
+                    break;
+                case 9:
+                    rd = (insn >> 8) & 7;
+                    /* load to pc register */
+                    if ((insn & (1 << 11)) && rd == 15)
+                        gen_store_is_jmp(1);
+                    break;
+                case 10:
+                    rd = (insn >> 8) & 7;
+                    /* possible write to pc register */
+                    if (rd == 15)
+                        gen_store_is_jmp(1);
+                    break;
+                case 11:
+                    op = (insn >> 8) & 0xf;
+                    switch (op) {
+                    case 2:
+                        rd = insn & 7;
+                        /* possible write to pc */
+                        if (rd == 15)
+                            gen_store_is_jmp(1);
+                    case 4: case 5: case 0xc: case 0xd:
+                        /* write to pc register */
+                        if ((insn & 0x0900) == 0x0900)
+                            gen_store_is_jmp(1);
+                        break;
+                    case 1: case 3: case 9: case 11: /* czb */
+                        /* branch instruction */
+                        gen_store_is_jmp(1);
+                        break;
+                    case 0xa:
+                        rd = insn & 0x7;
+                        /* possible write to pc register */
+                        if (rd == 15)
+                            gen_store_is_jmp(1);
+                        break;
+                    default:
+                        break;
+                    }
+                case 12:
+                    /* load/store multiple */
+                    rn = (insn >> 8) & 0x7;
+                    /* possible write to pc */
+                    if (rn == 15) {
+                        if (((insn & (1 << rn)) == 0) || (insn & (1 << 11)))
+                            gen_store_is_jmp(1);
+                    }
+                    break;
+                case 13:
+                    cond = (insn >> 8) & 0xf;
+                    /* branch instruction */
+                    if (cond != 0xe && cond != 0xf)
+                        gen_store_is_jmp(1);
+                    break;
+                case 14:
+                    /* branch instruction */
+                    if (!(insn & (1 << 11)))
+                        gen_store_is_jmp(1);
+                    break;
+                default:
+                    break;
+                }
+            }
+#endif // CONFIG_BBVEC
+            s->condlabel = gen_new_label();
+            arm_gen_test_cc(cond ^ 1, s->condlabel);
+            s->condjmp = 1;
         }
     }
 
-    insn = arm_lduw_code(env, s->pc, s->bswap_code);
     s->pc += 2;
 
     switch (insn >> 12) {
@@ -10828,6 +11293,10 @@ static void disas_thumb_insn(CPUARMState *env, 
DisasContext *s)
             break;
 
         case 1: case 3: case 9: case 11: /* czb */
+#ifdef CONFIG_BBVEC
+            if (bbtrace_initialized())
+                gen_store_is_jmp(1);
+#endif
             rm = insn & 7;
             tmp = load_reg(s, rm);
             s->condlabel = gen_new_label();
@@ -10982,6 +11451,10 @@ static void disas_thumb_insn(CPUARMState *env, 
DisasContext *s)
             break;
         }
         /* generate a conditional jump to next instruction */
+#ifdef CONFIG_BBVEC
+        if (bbtrace_initialized())
+            gen_store_is_jmp(1);
+#endif
         s->condlabel = gen_new_label();
         arm_gen_test_cc(cond ^ 1, s->condlabel);
         s->condjmp = 1;
@@ -11153,7 +11626,29 @@ static inline void 
gen_intermediate_code_internal(ARMCPU *cpu,
         tcg_gen_movi_i32(tmp, 0);
         store_cpu_field(tmp, condexec_bits);
       }
+
+#ifdef CONFIG_BBVEC
+    /* Profile previously run block, check for PID change, and initialize
+     * prof_is_jmp flag.   */
+    if (bbtrace_initialized()) {
+        gen_helper_bbv_profile(cpu_env);
+        gen_store_is_jmp(0);
+        gen_helper_context_check_pid(cpu_env);
+    }
+#endif
+
     do {
+#ifdef CONFIG_BBVEC
+        if (bbtrace_initialized()) {
+            gen_helper_context_check_mode(cpu_env);
+            gen_insn_cnt_incr(env, dc);
+            gen_pc_incr(env, dc);
+            /* FIXME: this call should not be necessary if all the cases
+               where the prof_is_jmp flag gets set are correct.   */
+            gen_store_is_jmp(0);
+        }
+#endif
+
 #ifdef CONFIG_USER_ONLY
         /* Intercept jump to the magic kernel page.  */
         if (dc->pc >= 0xffff0000) {
diff --git a/vl.c b/vl.c
index 74c2681..d7f31cf 100644
--- a/vl.c
+++ b/vl.c
@@ -30,6 +30,10 @@
 
 #include "config-host.h"
 
+#ifdef CONFIG_BBVEC
+#include "bbv_profiler.h"
+#endif
+
 #ifdef CONFIG_SECCOMP
 #include "sysemu/seccomp.h"
 #endif
@@ -490,6 +494,35 @@ static QemuOptsList qemu_semihosting_config_opts = {
     },
 };
 
+#ifdef CONFIG_BBVEC
+static QemuOptsList qemu_bbvec_opts = {
+    .name = "bbvec",
+    .implied_opt_name = "bbvec",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_bbvec_opts.head),
+    .merge_lists = true,
+    .desc = {
+        {
+            .name = "intervalSize",
+            .type = QEMU_OPT_NUMBER,
+        },
+        {
+            .name = "fileBase",
+            .type = QEMU_OPT_STRING,
+            .help = "Sets the prefix for the output basic block vector files",
+        },
+        {
+            .name = "userspaceOnly",
+            .type = QEMU_OPT_BOOL,
+        },
+        {
+            .name = "combinePIDs",
+            .type = QEMU_OPT_BOOL,
+        },
+        { /* end of list */ }
+    },
+};
+#endif
+
 /**
  * Get machine options
  *
@@ -2794,6 +2827,9 @@ int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_option_rom_opts);
     qemu_add_opts(&qemu_machine_opts);
     qemu_add_opts(&qemu_mem_opts);
+#ifdef CONFIG_BBVEC
+    qemu_add_opts(&qemu_bbvec_opts);
+#endif
     qemu_add_opts(&qemu_smp_opts);
     qemu_add_opts(&qemu_boot_opts);
     qemu_add_opts(&qemu_sandbox_opts);
@@ -3002,6 +3038,28 @@ int main(int argc, char **argv, char **envp)
             case QEMU_OPTION_nographic:
                 display_type = DT_NOGRAPHIC;
                 break;
+#ifdef CONFIG_BBVEC
+            case QEMU_OPTION_bbvec:
+                opts = qemu_opts_parse(qemu_find_opts("bbvec"), optarg, 0);
+
+                bool userspaceonly, combinepids;
+                uint64_t intervalsize;
+                const char *filebase = NULL;
+
+                intervalsize = qemu_opt_get_number(opts, "intervalSize", 100);
+                intervalsize *= 1000000;
+                userspaceonly = qemu_opt_get_bool(opts, "userspaceOnly", 1);
+                combinepids = qemu_opt_get_bool(opts, "combinePIDs", 0);
+                filebase = qemu_opt_get(opts, "fileBase");
+                if (!filebase)
+                    bbtrace_init(intervalsize, "bbvec", userspaceonly, 
combinepids);
+                else
+                    bbtrace_init(intervalsize, filebase, userspaceonly, 
combinepids);
+
+                qemu_add_exit_notifier(get_bbtrace_exit_notifier());
+
+                break;
+#endif
             case QEMU_OPTION_curses:
 #ifdef CONFIG_CURSES
                 display_type = DT_CURSES;
-- 
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project




reply via email to

[Prev in Thread] Current Thread [Next in Thread]