| /* |
| * Interface for configuring and controlling the state of tracing events. |
| * |
| * Copyright (C) 2014-2017 LluĂs Vilanova <vilanova@ac.upc.edu> |
| * |
| * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| * See the COPYING file in the top-level directory. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "cpu.h" |
| #include "trace-root.h" |
| #include "trace/control.h" |
| |
| |
| void trace_event_set_state_dynamic_init(TraceEvent *ev, bool state) |
| { |
| bool state_pre; |
| assert(trace_event_get_state_static(ev)); |
| /* |
| * We ignore the "vcpu" property here, since no vCPUs have been created |
| * yet. Then dstate can only be 1 or 0. |
| */ |
| state_pre = *ev->dstate; |
| if (state_pre != state) { |
| if (state) { |
| trace_events_enabled_count++; |
| *ev->dstate = 1; |
| } else { |
| trace_events_enabled_count--; |
| *ev->dstate = 0; |
| } |
| } |
| } |
| |
| void trace_event_set_state_dynamic(TraceEvent *ev, bool state) |
| { |
| CPUState *vcpu; |
| assert(trace_event_get_state_static(ev)); |
| if (trace_event_is_vcpu(ev) && likely(first_cpu != NULL)) { |
| CPU_FOREACH(vcpu) { |
| trace_event_set_vcpu_state_dynamic(vcpu, ev, state); |
| } |
| } else { |
| /* |
| * Without the "vcpu" property, dstate can only be 1 or 0. With it, we |
| * haven't instantiated any vCPU yet, so we will set a global state |
| * instead, and trace_init_vcpu will reconcile it afterwards. |
| */ |
| bool state_pre = *ev->dstate; |
| if (state_pre != state) { |
| if (state) { |
| trace_events_enabled_count++; |
| *ev->dstate = 1; |
| } else { |
| trace_events_enabled_count--; |
| *ev->dstate = 0; |
| } |
| } |
| } |
| } |
| |
| static void trace_event_synchronize_vcpu_state_dynamic( |
| CPUState *vcpu, run_on_cpu_data ignored) |
| { |
| bitmap_copy(vcpu->trace_dstate, vcpu->trace_dstate_delayed, |
| CPU_TRACE_DSTATE_MAX_EVENTS); |
| cpu_tb_jmp_cache_clear(vcpu); |
| } |
| |
| void trace_event_set_vcpu_state_dynamic(CPUState *vcpu, |
| TraceEvent *ev, bool state) |
| { |
| uint32_t vcpu_id; |
| bool state_pre; |
| assert(trace_event_get_state_static(ev)); |
| assert(trace_event_is_vcpu(ev)); |
| vcpu_id = trace_event_get_vcpu_id(ev); |
| state_pre = test_bit(vcpu_id, vcpu->trace_dstate); |
| if (state_pre != state) { |
| if (state) { |
| trace_events_enabled_count++; |
| set_bit(vcpu_id, vcpu->trace_dstate_delayed); |
| (*ev->dstate)++; |
| } else { |
| trace_events_enabled_count--; |
| clear_bit(vcpu_id, vcpu->trace_dstate_delayed); |
| (*ev->dstate)--; |
| } |
| if (vcpu->created) { |
| /* |
| * Delay changes until next TB; we want all TBs to be built from a |
| * single set of dstate values to ensure consistency of generated |
| * tracing code. |
| */ |
| async_run_on_cpu(vcpu, trace_event_synchronize_vcpu_state_dynamic, |
| RUN_ON_CPU_NULL); |
| } else { |
| trace_event_synchronize_vcpu_state_dynamic(vcpu, RUN_ON_CPU_NULL); |
| } |
| } |
| } |
| |
| static bool adding_first_cpu1(void) |
| { |
| CPUState *cpu; |
| size_t count = 0; |
| CPU_FOREACH(cpu) { |
| count++; |
| if (count > 1) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static bool adding_first_cpu(void) |
| { |
| bool res; |
| cpu_list_lock(); |
| res = adding_first_cpu1(); |
| cpu_list_unlock(); |
| return res; |
| } |
| |
| void trace_init_vcpu(CPUState *vcpu) |
| { |
| TraceEventIter iter; |
| TraceEvent *ev; |
| trace_event_iter_init(&iter, NULL); |
| while ((ev = trace_event_iter_next(&iter)) != NULL) { |
| if (trace_event_is_vcpu(ev) && |
| trace_event_get_state_static(ev) && |
| trace_event_get_state_dynamic(ev)) { |
| if (adding_first_cpu()) { |
| /* check preconditions */ |
| assert(*ev->dstate == 1); |
| /* disable early-init state ... */ |
| *ev->dstate = 0; |
| trace_events_enabled_count--; |
| /* ... and properly re-enable */ |
| trace_event_set_vcpu_state_dynamic(vcpu, ev, true); |
| } else { |
| trace_event_set_vcpu_state_dynamic(vcpu, ev, true); |
| } |
| } |
| } |
| trace_guest_cpu_enter(vcpu); |
| } |