mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-27 04:13:53 -06:00

This change improves performance by moving the hot path of the trace_vhost_commit()(or any other trace function) logic to the header file. Previously, even when the trace event was disabled, the function call chain:- trace_vhost_commit()(Or any other trace function) → _nocheck__trace_vhost_commit() → _simple_trace_vhost_commit() incurred a significant function prologue overhead before checking the trace state. Disassembly of _simple_trace_vhost_commit() (from the .c file) showed that 11 out of the first 14 instructions were prologue-related, including: 0x10 stp x29, x30, [sp, #-64]! Prologue: allocates 64-byte frame and saves old FP (x29) & LR (x30) 0x14 adrp x3, trace_events_enabled_count Prologue: computes page-base of the trace-enable counter 0x18 adrp x2, __stack_chk_guard Important (maybe prolog don't know?)(stack-protector): starts up the stack-canary load 0x1c mov x29, sp Prologue: sets new frame pointer 0x20 ldr x3, [x3] Prologue: loads the actual trace-enabled count 0x24 stp x19, x20, [sp, #16] Prologue: spills callee-saved regs used by this function (x19, x20) 0x28 and w20, w0, #0xff Tracepoint setup: extracts the low-8 bits of arg0 as the “event boolean” 0x2c ldr x2, [x2] Prologue (cont’d): completes loading of the stack-canary value 0x30 and w19, w1, #0xff Tracepoint setup: extracts low-8 bits of arg1 0x34 ldr w0, [x3] Important: loads the current trace-enabled flag from memory 0x38 ldr x1, [x2] Prologue (cont’d): reads the canary 0x3c str x1, [sp, #56] Prologue (cont’d): writes the canary into the new frame 0x40 mov x1, #0 Prologue (cont’d): zeroes out x1 for the upcoming branch test 0x44 cbnz w0, 0x88 Important: if tracing is disabled (w0==0) skip the heavy path entirely The trace-enabled check happens after the prologue. This is wasteful when tracing is disabled, which is often the case in production. To optimize this: _nocheck__trace_vhost_commit() is now fully inlined in the .h file with the hot path.It checks trace_event_get_state() before calling into _simple_trace_vhost_commit(), which remains in .c. This avoids calling into the .c function altogether when the tracepoint is disabled, thereby skipping unnecessary prologue instructions. This results in better performance by removing redundant instructions in the tracing fast path. Signed-off-by: Tanish Desai <tanishdesai37@gmail.com> Message-id: 20250528192528.3968-1-tanishdesai37@gmail.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
108 lines
3 KiB
Python
108 lines
3 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Simple built-in backend.
|
|
"""
|
|
|
|
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
|
|
__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
|
|
__license__ = "GPL version 2 or (at your option) any later version"
|
|
|
|
__maintainer__ = "Stefan Hajnoczi"
|
|
__email__ = "stefanha@redhat.com"
|
|
|
|
|
|
from tracetool import out
|
|
|
|
|
|
PUBLIC = True
|
|
|
|
|
|
def is_string(arg):
|
|
strtype = ('const char*', 'char*', 'const char *', 'char *')
|
|
arg_strip = arg.lstrip()
|
|
if arg_strip.startswith(strtype) and arg_strip.count('*') == 1:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def generate_h_begin(events, group):
|
|
for event in events:
|
|
out('void _simple_%(api)s(%(args)s);',
|
|
api=event.api(),
|
|
args=event.args)
|
|
out('')
|
|
|
|
|
|
def generate_h(event, group):
|
|
event_id = 'TRACE_' + event.name.upper()
|
|
if "vcpu" in event.properties:
|
|
# already checked on the generic format code
|
|
cond = "true"
|
|
else:
|
|
cond = "trace_event_get_state(%s)" % event_id
|
|
out(' if (%(cond)s) {',
|
|
' _simple_%(api)s(%(args)s);',
|
|
' }',
|
|
api=event.api(),
|
|
cond=cond,
|
|
args=", ".join(event.args.names()))
|
|
|
|
|
|
def generate_h_backend_dstate(event, group):
|
|
out(' trace_event_get_state_dynamic_by_id(%(event_id)s) || \\',
|
|
event_id="TRACE_" + event.name.upper())
|
|
|
|
|
|
def generate_c_begin(events, group):
|
|
out('#include "qemu/osdep.h"',
|
|
'#include "trace/control.h"',
|
|
'#include "trace/simple.h"',
|
|
'')
|
|
|
|
|
|
def generate_c(event, group):
|
|
out('void _simple_%(api)s(%(args)s)',
|
|
'{',
|
|
' TraceBufferRecord rec;',
|
|
api=event.api(),
|
|
args=event.args)
|
|
sizes = []
|
|
for type_, name in event.args:
|
|
if is_string(type_):
|
|
out(' size_t arg%(name)s_len = %(name)s ? MIN(strlen(%(name)s), MAX_TRACE_STRLEN) : 0;',
|
|
name=name)
|
|
strsizeinfo = "4 + arg%s_len" % name
|
|
sizes.append(strsizeinfo)
|
|
else:
|
|
sizes.append("8")
|
|
sizestr = " + ".join(sizes)
|
|
if len(event.args) == 0:
|
|
sizestr = '0'
|
|
|
|
out('',
|
|
' if (trace_record_start(&rec, %(event_obj)s.id, %(size_str)s)) {',
|
|
' return; /* Trace Buffer Full, Event Dropped ! */',
|
|
' }',
|
|
event_obj=event.api(event.QEMU_EVENT),
|
|
size_str=sizestr)
|
|
|
|
if len(event.args) > 0:
|
|
for type_, name in event.args:
|
|
# string
|
|
if is_string(type_):
|
|
out(' trace_record_write_str(&rec, %(name)s, arg%(name)s_len);',
|
|
name=name)
|
|
# pointer var (not string)
|
|
elif type_.endswith('*'):
|
|
out(' trace_record_write_u64(&rec, (uintptr_t)(uint64_t *)%(name)s);',
|
|
name=name)
|
|
# primitive data type
|
|
else:
|
|
out(' trace_record_write_u64(&rec, (uint64_t)%(name)s);',
|
|
name=name)
|
|
|
|
out(' trace_record_finish(&rec);',
|
|
'}',
|
|
'')
|