mesa/main: Auto-generate MESA_VERBOSE=api trace dispatch
Some checks are pending
macOS-CI / macOS-CI (dri) (push) Waiting to run
macOS-CI / macOS-CI (xlib) (push) Waiting to run

With the trace plumbing in place, fill in the wrappers from
gl_and_es_API.xml so the trace tracks new entrypoints.

Generated-by: Claude
Signed-off-by: Christian Gmeiner <cgmeiner@igalia.com>
Reviewed-by: Marek Olšák <maraeo@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/41147>
This commit is contained in:
Christian Gmeiner 2026-04-24 09:14:28 +02:00 committed by Marge Bot
parent d128c6f111
commit 9f7f5a27a7
7 changed files with 374 additions and 0 deletions

View file

@ -0,0 +1,226 @@
# 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 apiexec
import gl_XML
import license
TRACE_ARRAY_BUFSZ = 512
header = """
#include <inttypes.h>
#include <stdio.h>
#include "glapi/glapi/glapi.h"
#include "main/api_trace_helpers.h"
#include "main/context.h"
#include "main/enums.h"
#include "main/errors.h"
#include "dispatch.h"
"""
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.
ARRAY_ELEM_KIND = {
'GLfloat': 'MESA_TRACE_ELEM_FLOAT',
'GLclampf': 'MESA_TRACE_ELEM_FLOAT',
'GLdouble': 'MESA_TRACE_ELEM_DOUBLE',
'GLclampd': 'MESA_TRACE_ELEM_DOUBLE',
'GLbyte': 'MESA_TRACE_ELEM_BYTE',
'GLshort': 'MESA_TRACE_ELEM_SHORT',
'GLint': 'MESA_TRACE_ELEM_INT',
'GLsizei': 'MESA_TRACE_ELEM_INT',
'GLfixed': 'MESA_TRACE_ELEM_INT',
'GLclampx': 'MESA_TRACE_ELEM_INT',
'GLubyte': 'MESA_TRACE_ELEM_UBYTE',
'GLushort': 'MESA_TRACE_ELEM_USHORT',
'GLuint': 'MESA_TRACE_ELEM_UINT',
'GLhalfNV': 'MESA_TRACE_ELEM_HALF',
'GLint64': 'MESA_TRACE_ELEM_INT64',
'GLuint64': 'MESA_TRACE_ELEM_UINT64',
'GLintptr': 'MESA_TRACE_ELEM_INTPTR',
'GLsizeiptr': 'MESA_TRACE_ELEM_INTPTR',
}
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', kind, 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_KIND and count_expr is not None:
return ('array', ARRAY_ELEM_KIND[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)
def printRealFooter(self):
pass
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':
_, kind, 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_array({buf}, sizeof({buf}),'
' {name}, {count}, {kind});'
.format(buf=buf_name, name=name,
count=count_expr, kind=kind))
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())
for f in functions:
self.print_wrapper(f)
self.print_install(functions)
if __name__ == '__main__':
apiexec.print_glapi_file(PrintCode())

View file

@ -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@', '-f', '@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(

View file

@ -0,0 +1,101 @@
/*
* Copyright © 2026 Igalia S.L.
* SPDX-License-Identifier: MIT
*/
#include <inttypes.h>
#include <stdio.h>
#include "util/glheader.h"
#include "main/api_trace_helpers.h"
void
_mesa_trace_format_array(char *buf, size_t buflen,
const void *arr, size_t n,
enum mesa_trace_elem_kind kind)
{
static const size_t MAX_TRACE_ARRAY = 16;
if (!arr) {
snprintf(buf, buflen, "(null)");
return;
}
size_t shown = (n < MAX_TRACE_ARRAY) ? n : MAX_TRACE_ARRAY;
size_t pos = 0;
int w;
w = snprintf(buf + pos, buflen - pos, "[");
if (w < 0 || (size_t)w >= buflen - pos)
return;
pos += w;
for (size_t i = 0; i < shown; i++) {
if (i > 0) {
w = snprintf(buf + pos, buflen - pos, ", ");
if (w < 0 || (size_t)w >= buflen - pos)
return;
pos += w;
}
switch (kind) {
case MESA_TRACE_ELEM_FLOAT:
w = snprintf(buf + pos, buflen - pos, "%f",
((const GLfloat *)arr)[i]);
break;
case MESA_TRACE_ELEM_DOUBLE:
w = snprintf(buf + pos, buflen - pos, "%f",
((const GLdouble *)arr)[i]);
break;
case MESA_TRACE_ELEM_BYTE:
w = snprintf(buf + pos, buflen - pos, "%d",
((const GLbyte *)arr)[i]);
break;
case MESA_TRACE_ELEM_SHORT:
w = snprintf(buf + pos, buflen - pos, "%d",
((const GLshort *)arr)[i]);
break;
case MESA_TRACE_ELEM_INT:
w = snprintf(buf + pos, buflen - pos, "%d",
((const GLint *)arr)[i]);
break;
case MESA_TRACE_ELEM_UBYTE:
w = snprintf(buf + pos, buflen - pos, "%u",
((const GLubyte *)arr)[i]);
break;
case MESA_TRACE_ELEM_USHORT:
w = snprintf(buf + pos, buflen - pos, "%u",
((const GLushort *)arr)[i]);
break;
case MESA_TRACE_ELEM_UINT:
w = snprintf(buf + pos, buflen - pos, "%u",
((const GLuint *)arr)[i]);
break;
case MESA_TRACE_ELEM_HALF:
w = snprintf(buf + pos, buflen - pos, "0x%x",
((const GLhalfNV *)arr)[i]);
break;
case MESA_TRACE_ELEM_INT64:
w = snprintf(buf + pos, buflen - pos, "%" PRId64,
(int64_t)((const GLint64 *)arr)[i]);
break;
case MESA_TRACE_ELEM_UINT64:
w = snprintf(buf + pos, buflen - pos, "%" PRIu64,
(uint64_t)((const GLuint64 *)arr)[i]);
break;
case MESA_TRACE_ELEM_INTPTR:
w = snprintf(buf + pos, buflen - pos, "%" PRIdPTR,
(intptr_t)((const GLintptr *)arr)[i]);
break;
}
if (w < 0 || (size_t)w >= buflen - pos)
return;
pos += w;
}
if (n > MAX_TRACE_ARRAY)
snprintf(buf + pos, buflen - pos, ", ... %zu of %zu]", shown, n);
else
snprintf(buf + pos, buflen - pos, "]");
}

View file

@ -0,0 +1,28 @@
/*
* Copyright © 2026 Igalia S.L.
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <stddef.h>
enum mesa_trace_elem_kind {
MESA_TRACE_ELEM_FLOAT,
MESA_TRACE_ELEM_DOUBLE,
MESA_TRACE_ELEM_BYTE,
MESA_TRACE_ELEM_SHORT,
MESA_TRACE_ELEM_INT,
MESA_TRACE_ELEM_UBYTE,
MESA_TRACE_ELEM_USHORT,
MESA_TRACE_ELEM_UINT,
MESA_TRACE_ELEM_HALF,
MESA_TRACE_ELEM_INT64,
MESA_TRACE_ELEM_UINT64,
MESA_TRACE_ELEM_INTPTR,
};
void
_mesa_trace_format_array(char *buf, size_t buflen,
const void *arr, size_t n,
enum mesa_trace_elem_kind kind);

View file

@ -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.
*/

View file

@ -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);

View file

@ -10,6 +10,8 @@ files_libmesa = files(
'main/accum.h',
'main/api_arrayelt.c',
'main/api_arrayelt.h',
'main/api_trace_helpers.c',
'main/api_trace_helpers.h',
'main/arbprogram.c',
'main/arrayobj.c',
'main/arrayobj.h',
@ -417,6 +419,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,