mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-08 02:38:04 +02:00
mesa/main: Auto-generate MESA_VERBOSE=api trace dispatch
Adds api_trace_c.py that emits from gl_and_es_API.xml a _mesa_trace_Foo wrapper per entrypoint plus _mesa_init_dispatch_trace(), called from _mesa_initialize_dispatch_tables() when MESA_VERBOSE & VERBOSE_API. Each wrapper logs one line via _mesa_debug() and forwards through ctx->Dispatch.RealPublished. Scalar arguments are printed type-aware: GLenum decoded via _mesa_enum_to_string, const GLchar * as a null-guarded string, GLbitfield as raw hex, numerics with a size-appropriate spec. Counted array parameters — fixed count="N", or count="counter" with optional count_scale (e.g. UniformMatrix4fv's value=count*16) — render their first MAX_TRACE_ARRAY=16 elements inline as "[v0, v1, ...]" via typed _mesa_trace_format_<T> helpers, with a "..., N of M]" suffix on truncation. Output pointers, opaque GLvoid * blobs, and parameters with no count info fall back to %p. No bitmask decoding or return-value capture — that's apitrace's job. Signed-off-by: Christian Gmeiner <cgmeiner@igalia.com> Generated-by: Claude
This commit is contained in:
parent
9d70d1b2b3
commit
46912a83e8
5 changed files with 298 additions and 0 deletions
281
src/mesa/glapi/glapi/gen/api_trace_c.py
Normal file
281
src/mesa/glapi/glapi/gen/api_trace_c.py
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
# Copyright © 2026 Igalia S.L.
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Generates api_trace.c: per-entrypoint wrappers that log GL calls to stderr
|
||||
# and forward to the real dispatch. Installed at context create when
|
||||
# MESA_VERBOSE=api is set.
|
||||
|
||||
import gl_XML
|
||||
import license
|
||||
import sys
|
||||
|
||||
|
||||
MAX_TRACE_ARRAY = 16
|
||||
TRACE_ARRAY_BUFSZ = 512
|
||||
|
||||
|
||||
header = """
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "glapi/glapi/glapi.h"
|
||||
#include "main/context.h"
|
||||
#include "main/enums.h"
|
||||
#include "main/errors.h"
|
||||
#include "dispatch.h"
|
||||
|
||||
#define MAX_TRACE_ARRAY {max_array}
|
||||
"""
|
||||
|
||||
|
||||
# ``{p}`` is replaced with the parameter name.
|
||||
TYPE_FORMAT = {
|
||||
'GLenum': ('%s', '_mesa_enum_to_string({p})'),
|
||||
'GLboolean': ('%s', '{p} ? "GL_TRUE" : "GL_FALSE"'),
|
||||
'GLbitfield': ('0x%x', '{p}'),
|
||||
'GLbyte': ('%d', '{p}'),
|
||||
'GLubyte': ('%u', '{p}'),
|
||||
'GLshort': ('%d', '{p}'),
|
||||
'GLushort': ('%u', '{p}'),
|
||||
'GLint': ('%d', '{p}'),
|
||||
'GLuint': ('%u', '{p}'),
|
||||
'GLint64': ('%" PRId64 "', '(int64_t){p}'),
|
||||
'GLuint64': ('%" PRIu64 "', '(uint64_t){p}'),
|
||||
'GLsizei': ('%d', '{p}'),
|
||||
'GLintptr': ('%" PRIdPTR "','(intptr_t){p}'),
|
||||
'GLsizeiptr': ('%" PRIdPTR "','(intptr_t){p}'),
|
||||
'GLfloat': ('%f', '{p}'),
|
||||
'GLclampf': ('%f', '{p}'),
|
||||
'GLdouble': ('%f', '{p}'),
|
||||
'GLclampd': ('%f', '{p}'),
|
||||
'GLfixed': ('%d', '{p}'),
|
||||
'GLclampx': ('%d', '{p}'),
|
||||
'GLhalfNV': ('0x%x', '{p}'),
|
||||
'GLvdpauSurfaceNV': ('%" PRIdPTR "', '(intptr_t){p}'),
|
||||
'GLsync': ('%p', '(void *){p}'),
|
||||
'GLhandleARB': ('%u', '{p}'),
|
||||
'GLDEBUGPROC': ('%p', '(void *){p}'),
|
||||
'GLDEBUGPROCARB': ('%p', '(void *){p}'),
|
||||
'GLDEBUGPROCAMD': ('%p', '(void *){p}'),
|
||||
'GLDEBUGPROCKHR': ('%p', '(void *){p}'),
|
||||
'GLVULKANPROCNV': ('%p', '(void *){p}'),
|
||||
}
|
||||
|
||||
TYPE_ALIASES = {
|
||||
'GLint64EXT': 'GLint64',
|
||||
'GLuint64EXT': 'GLuint64',
|
||||
'GLintptrARB': 'GLintptr',
|
||||
'GLsizeiptrARB': 'GLsizeiptr',
|
||||
'float': 'GLfloat',
|
||||
'int': 'GLint',
|
||||
}
|
||||
|
||||
|
||||
# Element types not listed here fall back to ``%p`` at the call site. The
|
||||
# format snippet is a raw C-source fragment so 64-bit specs can break out to
|
||||
# use the inttypes.h macros.
|
||||
ARRAY_ELEM_PRINTF = {
|
||||
'GLfloat': ('"%f"', 'arr[i]'),
|
||||
'GLdouble': ('"%f"', 'arr[i]'),
|
||||
'GLclampf': ('"%f"', 'arr[i]'),
|
||||
'GLclampd': ('"%f"', 'arr[i]'),
|
||||
'GLbyte': ('"%d"', 'arr[i]'),
|
||||
'GLshort': ('"%d"', 'arr[i]'),
|
||||
'GLint': ('"%d"', 'arr[i]'),
|
||||
'GLsizei': ('"%d"', 'arr[i]'),
|
||||
'GLfixed': ('"%d"', 'arr[i]'),
|
||||
'GLubyte': ('"%u"', 'arr[i]'),
|
||||
'GLushort': ('"%u"', 'arr[i]'),
|
||||
'GLuint': ('"%u"', 'arr[i]'),
|
||||
'GLhalfNV': ('"0x%x"', 'arr[i]'),
|
||||
'GLint64': ('"%" PRId64', '(int64_t)arr[i]'),
|
||||
'GLuint64': ('"%" PRIu64', '(uint64_t)arr[i]'),
|
||||
'GLintptr': ('"%" PRIdPTR', '(intptr_t)arr[i]'),
|
||||
'GLsizeiptr': ('"%" PRIdPTR', '(intptr_t)arr[i]'),
|
||||
}
|
||||
|
||||
|
||||
def array_count_expr(p):
|
||||
if p.counter:
|
||||
scale = p.count_scale if p.count_scale else 1
|
||||
if scale == 1:
|
||||
return '(size_t){0}'.format(p.counter)
|
||||
return '(size_t){0} * {1}'.format(p.counter, scale)
|
||||
if p.count and p.count >= 2:
|
||||
return str(p.count)
|
||||
return None
|
||||
|
||||
|
||||
def classify_param(p):
|
||||
"""Return one of:
|
||||
('scalar', spec, expr) — printed inline
|
||||
('array', elem_type, count_expr, name)
|
||||
('opaque', spec, expr) — printed as %p / hex fallback
|
||||
"""
|
||||
opaque = ('opaque', '%p', '(void *){0}'.format(p.name))
|
||||
|
||||
if not p.is_pointer():
|
||||
ts = TYPE_ALIASES.get(p.type_string().strip(), p.type_string().strip())
|
||||
if ts in TYPE_FORMAT:
|
||||
spec, expr = TYPE_FORMAT[ts]
|
||||
return ('scalar', spec, expr.format(p=p.name))
|
||||
return ('scalar', '0x%x', p.name)
|
||||
|
||||
base = p.get_base_type_string()
|
||||
|
||||
if base in ('GLchar', 'GLcharARB'):
|
||||
ts = p.type_string().lstrip()
|
||||
if ts.startswith('const'):
|
||||
return ('scalar', '%s',
|
||||
'{p} ? (const char *){p} : "(null)"'.format(p=p.name))
|
||||
return opaque
|
||||
|
||||
if p.is_output:
|
||||
return opaque
|
||||
|
||||
elem = TYPE_ALIASES.get(base, base)
|
||||
count_expr = array_count_expr(p)
|
||||
if elem in ARRAY_ELEM_PRINTF and count_expr is not None:
|
||||
return ('array', elem, count_expr, p.name)
|
||||
|
||||
return opaque
|
||||
|
||||
|
||||
class PrintCode(gl_XML.gl_print_base):
|
||||
def __init__(self):
|
||||
super(PrintCode, self).__init__()
|
||||
self.name = 'api_trace_c.py'
|
||||
self.license = license.bsd_license_template % (
|
||||
'Copyright (C) 2026 Christian Gmeiner', 'Christian Gmeiner')
|
||||
|
||||
def printRealHeader(self):
|
||||
print(header.format(max_array=MAX_TRACE_ARRAY))
|
||||
|
||||
def printRealFooter(self):
|
||||
pass
|
||||
|
||||
def print_array_helper(self, elem):
|
||||
fmt, val = ARRAY_ELEM_PRINTF[elem]
|
||||
print('static void')
|
||||
print('_mesa_trace_format_{0}(char *buf, size_t buflen,'.format(elem))
|
||||
print(' const {0} *arr, size_t n)'.format(elem))
|
||||
print('{')
|
||||
print(' if (!arr) {')
|
||||
print(' snprintf(buf, buflen, "(null)");')
|
||||
print(' return;')
|
||||
print(' }')
|
||||
print(' size_t shown = (n < MAX_TRACE_ARRAY) ? n : MAX_TRACE_ARRAY;')
|
||||
print(' size_t pos = 0;')
|
||||
print(' int w;')
|
||||
print(' w = snprintf(buf + pos, buflen - pos, "[");')
|
||||
print(' if (w < 0 || (size_t)w >= buflen - pos) return;')
|
||||
print(' pos += w;')
|
||||
print(' for (size_t i = 0; i < shown; i++) {')
|
||||
print(' if (i > 0) {')
|
||||
print(' w = snprintf(buf + pos, buflen - pos, ", ");')
|
||||
print(' if (w < 0 || (size_t)w >= buflen - pos) return;')
|
||||
print(' pos += w;')
|
||||
print(' }')
|
||||
print(' w = snprintf(buf + pos, buflen - pos, {fmt}, {val});'
|
||||
.format(fmt=fmt, val=val))
|
||||
print(' if (w < 0 || (size_t)w >= buflen - pos) return;')
|
||||
print(' pos += w;')
|
||||
print(' }')
|
||||
print(' if (n > MAX_TRACE_ARRAY)')
|
||||
print(' snprintf(buf + pos, buflen - pos,'
|
||||
' ", ... %zu of %zu]", shown, n);')
|
||||
print(' else')
|
||||
print(' snprintf(buf + pos, buflen - pos, "]");')
|
||||
print('}')
|
||||
print('')
|
||||
|
||||
def print_wrapper(self, f):
|
||||
params = [p for p in f.parameters if not p.is_padding]
|
||||
classified = [classify_param(p) for p in params]
|
||||
|
||||
specs = []
|
||||
args = []
|
||||
prelude = []
|
||||
for p, c in zip(params, classified):
|
||||
if c[0] == 'array':
|
||||
_, elem, count_expr, name = c
|
||||
buf_name = '{0}_buf'.format(name)
|
||||
prelude.append('char {buf}[{sz}];'
|
||||
.format(buf=buf_name, sz=TRACE_ARRAY_BUFSZ))
|
||||
prelude.append(
|
||||
'_mesa_trace_format_{elem}({buf}, sizeof({buf}),'
|
||||
' {name}, {count});'
|
||||
.format(elem=elem, buf=buf_name,
|
||||
name=name, count=count_expr))
|
||||
specs.append('%s')
|
||||
args.append(buf_name)
|
||||
else:
|
||||
_, spec, expr = c
|
||||
specs.append(spec)
|
||||
args.append(expr)
|
||||
|
||||
ret = f.return_type
|
||||
param_string = f.get_parameter_string()
|
||||
called = f.get_called_parameter_string()
|
||||
|
||||
print('static {rt} GLAPIENTRY'.format(rt=ret))
|
||||
print('_mesa_trace_{name}({ps})'.format(name=f.name, ps=param_string))
|
||||
print('{')
|
||||
print(' GET_CURRENT_CONTEXT(ctx);')
|
||||
for stmt in prelude:
|
||||
print(' {0}'.format(stmt))
|
||||
if args:
|
||||
fmt_body = 'gl{0}({1})\\n'.format(f.name, ', '.join(specs))
|
||||
print(' _mesa_debug(ctx, "{fmt}", {args});'
|
||||
.format(fmt=fmt_body, args=', '.join(args)))
|
||||
else:
|
||||
print(' _mesa_debug(ctx, "gl{0}()\\n");'.format(f.name))
|
||||
call = 'CALL_{name}(ctx->Dispatch.RealPublished, ({ca}));'.format(
|
||||
name=f.name, ca=called if called else '')
|
||||
if ret != 'void':
|
||||
print(' return {call}'.format(call=call))
|
||||
else:
|
||||
print(' {call}'.format(call=call))
|
||||
print('}')
|
||||
print('')
|
||||
|
||||
def print_install(self, functions):
|
||||
print('bool')
|
||||
print('_mesa_init_dispatch_trace(struct gl_context *ctx)')
|
||||
print('{')
|
||||
print(' struct _glapi_table *table = _mesa_alloc_dispatch_table(false);')
|
||||
print(' if (!table)')
|
||||
print(' return false;')
|
||||
print('')
|
||||
for f in functions:
|
||||
print(' SET_{name}(table, _mesa_trace_{name});'.format(name=f.name))
|
||||
print('')
|
||||
print(' ctx->Dispatch.Trace = table;')
|
||||
print(' return true;')
|
||||
print('}')
|
||||
|
||||
def printBody(self, api):
|
||||
functions = list(api.functionIterateByOffset())
|
||||
|
||||
used_elem_types = set()
|
||||
for f in functions:
|
||||
for p in f.parameters:
|
||||
if p.is_padding:
|
||||
continue
|
||||
c = classify_param(p)
|
||||
if c[0] == 'array':
|
||||
used_elem_types.add(c[1])
|
||||
|
||||
for elem in sorted(used_elem_types):
|
||||
self.print_array_helper(elem)
|
||||
|
||||
for f in functions:
|
||||
self.print_wrapper(f)
|
||||
self.print_install(functions)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
file_name = sys.argv[1]
|
||||
printer = PrintCode()
|
||||
api = gl_XML.parse_GL_API(file_name)
|
||||
printer.Print(api)
|
||||
|
|
@ -111,6 +111,15 @@ main_unmarshal_table_c = custom_target(
|
|||
capture : true,
|
||||
)
|
||||
|
||||
main_api_trace_c = custom_target(
|
||||
'api_trace.c',
|
||||
input : ['api_trace_c.py', 'gl_and_es_API.xml'],
|
||||
output : 'api_trace.c',
|
||||
command : [prog_python, '@INPUT0@', '@INPUT1@'],
|
||||
depend_files : glapi_xml_py_deps,
|
||||
capture : true,
|
||||
)
|
||||
|
||||
main_marshal_generated_c = []
|
||||
foreach x : ['0', '1', '2', '3', '4', '5', '6', '7']
|
||||
main_marshal_generated_c += custom_target(
|
||||
|
|
|
|||
|
|
@ -906,6 +906,10 @@ _mesa_initialize_dispatch_tables(struct gl_context *ctx)
|
|||
_mesa_init_dispatch_save_begin_end(ctx);
|
||||
}
|
||||
|
||||
if ((MESA_VERBOSE & VERBOSE_API) &&
|
||||
!_mesa_init_dispatch_trace(ctx))
|
||||
return false;
|
||||
|
||||
/* This binds the dispatch table to the context, but MakeCurrent will
|
||||
* bind it for the user. If glthread is enabled, it will override it.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -91,6 +91,9 @@ _mesa_alloc_dispatch_tables(gl_api api, struct gl_dispatch *d, bool glthread);
|
|||
extern bool
|
||||
_mesa_initialize_dispatch_tables(struct gl_context *ctx);
|
||||
|
||||
extern bool
|
||||
_mesa_init_dispatch_trace(struct gl_context *ctx);
|
||||
|
||||
extern void
|
||||
_mesa_set_dispatch(struct gl_context *ctx, struct _glapi_table *t);
|
||||
|
||||
|
|
|
|||
|
|
@ -417,6 +417,7 @@ files_libmesa += [
|
|||
mesa_lex,
|
||||
program_parse_tab,
|
||||
main_api_exec_c,
|
||||
main_api_trace_c,
|
||||
main_api_exec_decl_h,
|
||||
main_api_save_h,
|
||||
main_api_save_init_h,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue