util/perf: Add gpuvis integration.

Initial integration, still needs the init functions to be changed
across the codebase.

For the context usage https://github.com/mikesart/gpuvis/pull/82 is
needed to display it correctly in gpuvis.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/22505>
This commit is contained in:
Bas Nieuwenhuizen
2023-04-14 14:07:40 +02:00
parent ff3db3e6cf
commit 11198951e6
7 changed files with 944 additions and 6 deletions

View File

@@ -2080,6 +2080,11 @@ if with_perfetto
pre_args += '-DHAVE_PERFETTO' pre_args += '-DHAVE_PERFETTO'
endif endif
with_gpuvis = get_option('gpuvis')
if with_gpuvis
pre_args += '-DHAVE_GPUVIS'
endif
add_project_arguments(pre_args, language : ['c', 'cpp']) add_project_arguments(pre_args, language : ['c', 'cpp'])
add_project_arguments(c_cpp_args, language : ['c', 'cpp']) add_project_arguments(c_cpp_args, language : ['c', 'cpp'])

View File

@@ -582,6 +582,13 @@ option(
'Default: [`auto`]' 'Default: [`auto`]'
) )
option(
'gpuvis',
type : 'boolean',
value : false,
description : 'Enable tracing markers for gpuvis. Default: false'
)
option( option(
'custom-shader-replacement', 'custom-shader-replacement',
type : 'string', type : 'string',

View File

@@ -252,6 +252,14 @@ if with_perfetto
deps_for_libmesa_util += dep_perfetto deps_for_libmesa_util += dep_perfetto
endif endif
if with_gpuvis
files_mesa_util += files(
'perf/u_gpuvis.c',
'perf/u_gpuvis.h',
'perf/gpuvis_trace_utils.h',
)
endif
u_trace_py = files('perf/u_trace.py') u_trace_py = files('perf/u_trace.py')
libmesa_util_sse41 = static_library( libmesa_util_sse41 = static_library(

View File

@@ -7,6 +7,7 @@
#define CPU_TRACE_H #define CPU_TRACE_H
#include "u_perfetto.h" #include "u_perfetto.h"
#include "u_gpuvis.h"
#include "util/macros.h" #include "util/macros.h"
@@ -47,6 +48,31 @@
#endif /* HAVE_PERFETTO */ #endif /* HAVE_PERFETTO */
#if defined(HAVE_GPUVIS)
#define _MESA_GPUVIS_TRACE_BEGIN(name) util_gpuvis_begin(name)
#define _MESA_GPUVIS_TRACE_END() util_gpuvis_end()
#else
#define _MESA_GPUVIS_TRACE_BEGIN(name)
#define _MESA_GPUVIS_TRACE_END()
#endif /* HAVE_GPUVIS */
#define _MESA_COMBINED_TRACE_BEGIN(category, name) \
do { \
_MESA_TRACE_BEGIN(category, name); \
_MESA_GPUVIS_TRACE_BEGIN(name); \
} while (0)
#define _MESA_COMBINED_TRACE_END(category) \
do { \
_MESA_GPUVIS_TRACE_END(); \
_MESA_TRACE_END(category); \
} while (0)
#if __has_attribute(cleanup) && __has_attribute(unused) #if __has_attribute(cleanup) && __has_attribute(unused)
#define _MESA_TRACE_SCOPE_VAR_CONCAT(name, suffix) name##suffix #define _MESA_TRACE_SCOPE_VAR_CONCAT(name, suffix) name##suffix
@@ -69,7 +95,7 @@ static inline int
_mesa_trace_scope_begin(enum util_perfetto_category category, _mesa_trace_scope_begin(enum util_perfetto_category category,
const char *name) const char *name)
{ {
_MESA_TRACE_BEGIN(category, name); _MESA_COMBINED_TRACE_BEGIN(category, name);
return category; return category;
} }
@@ -77,7 +103,7 @@ static inline void
_mesa_trace_scope_end(int *scope) _mesa_trace_scope_end(int *scope)
{ {
/* we save the category in the scope variable */ /* we save the category in the scope variable */
_MESA_TRACE_END((enum util_perfetto_category) * scope); _MESA_COMBINED_TRACE_END((enum util_perfetto_category) * scope);
} }
#else #else
@@ -90,8 +116,9 @@ _mesa_trace_scope_end(int *scope)
* define their own categories/macros. * define their own categories/macros.
*/ */
#define MESA_TRACE_BEGIN(name) \ #define MESA_TRACE_BEGIN(name) \
_MESA_TRACE_BEGIN(UTIL_PERFETTO_CATEGORY_DEFAULT, name) _MESA_COMBINED_TRACE_BEGIN(UTIL_PERFETTO_CATEGORY_DEFAULT, name)
#define MESA_TRACE_END() _MESA_TRACE_END(UTIL_PERFETTO_CATEGORY_DEFAULT) #define MESA_TRACE_END() \
_MESA_COMBINED_TRACE_END(UTIL_PERFETTO_CATEGORY_DEFAULT)
#define MESA_TRACE_SCOPE(name) \ #define MESA_TRACE_SCOPE(name) \
_MESA_TRACE_SCOPE(UTIL_PERFETTO_CATEGORY_DEFAULT, name) _MESA_TRACE_SCOPE(UTIL_PERFETTO_CATEGORY_DEFAULT, name)
#define MESA_TRACE_FUNC() \ #define MESA_TRACE_FUNC() \
@@ -99,11 +126,19 @@ _mesa_trace_scope_end(int *scope)
/* these use the slow category */ /* these use the slow category */
#define MESA_TRACE_BEGIN_SLOW(name) \ #define MESA_TRACE_BEGIN_SLOW(name) \
_MESA_TRACE_BEGIN(UTIL_PERFETTO_CATEGORY_SLOW, name) _MESA_COMBINED_TRACE_BEGIN(UTIL_PERFETTO_CATEGORY_SLOW, name)
#define MESA_TRACE_END_SLOW() _MESA_TRACE_END(UTIL_PERFETTO_CATEGORY_SLOW) #define MESA_TRACE_END_SLOW() \
_MESA_COMBINED_TRACE_END(UTIL_PERFETTO_CATEGORY_SLOW)
#define MESA_TRACE_SCOPE_SLOW(name) \ #define MESA_TRACE_SCOPE_SLOW(name) \
_MESA_TRACE_SCOPE(UTIL_PERFETTO_CATEGORY_SLOW, name) _MESA_TRACE_SCOPE(UTIL_PERFETTO_CATEGORY_SLOW, name)
#define MESA_TRACE_FUNC_SLOW() \ #define MESA_TRACE_FUNC_SLOW() \
_MESA_TRACE_SCOPE(UTIL_PERFETTO_CATEGORY_SLOW, __func__) _MESA_TRACE_SCOPE(UTIL_PERFETTO_CATEGORY_SLOW, __func__)
static inline void
util_cpu_trace_init()
{
util_perfetto_init();
util_gpuvis_init();
}
#endif /* CPU_TRACE_H */ #endif /* CPU_TRACE_H */

View File

@@ -0,0 +1,795 @@
//////////////////////////////////////////////////////////////////////////////
// gpuvis_trace_utils.h - v0.10 - public domain
// no warranty is offered or implied; use this code at your own risk
//
// This is a single header file with useful utilities for gpuvis linux tracing
//
// ============================================================================
// You MUST define GPUVIS_TRACE_IMPLEMENTATION in EXACTLY _one_ C or C++ file
// that includes this header, BEFORE the include, like this:
//
// #define GPUVIS_TRACE_IMPLEMENTATION
// #include "gpuvis_trace_utils.h"
//
// All other files should just #include "gpuvis_trace_utils.h" w/o the #define.
// ============================================================================
//
// Credits
//
// Michael Sartain
//
// LICENSE
//
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
//////////////////////////////////////////////////////////////////////////////
//
// INCLUDE SECTION
//
#ifndef _GPUVIS_TRACE_UTILS_H_
#define _GPUVIS_TRACE_UTILS_H_
#include <stdarg.h>
#if !defined( __linux__ )
#define GPUVIS_TRACE_UTILS_DISABLE
#endif
#if defined( __clang__ ) || defined( __GNUC__ )
// printf-style warnings for user functions.
#define GPUVIS_ATTR_PRINTF( _x, _y ) __attribute__( ( __format__( __printf__, _x, _y ) ) )
#define GPUVIS_MAY_BE_UNUSED __attribute__( ( unused ) )
#define GPUVIS_CLEANUP_FUNC( x ) __attribute__( ( __cleanup__( x ) ) )
#else
#define GPUVIS_ATTR_PRINTF( _x, _y )
#define GPUVIS_MAY_BE_UNUSED
#define GPUVIS_CLEANUP_FUNC( x )
#endif
#if !defined( GPUVIS_TRACE_UTILS_DISABLE )
#include <time.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef __cplusplus
#define GPUVIS_EXTERN extern "C"
#if __cplusplus>=201103L
#define THREAD_LOCAL thread_local
#else
#define THREAD_LOCAL __thread
#endif
#else
#define GPUVIS_EXTERN extern
#endif
// From kernel/trace/trace.h
#ifndef TRACE_BUF_SIZE
#define TRACE_BUF_SIZE 1024
#endif
// Try to open tracefs trace_marker file for writing. Returns -1 on error.
GPUVIS_EXTERN int gpuvis_trace_init( void );
// Close tracefs trace_marker file.
GPUVIS_EXTERN void gpuvis_trace_shutdown( void );
// Write user event to tracefs trace_marker.
GPUVIS_EXTERN int gpuvis_trace_printf( const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 1, 2 );
GPUVIS_EXTERN int gpuvis_trace_vprintf( const char *fmt, va_list ap ) GPUVIS_ATTR_PRINTF( 1, 0 );
// Write user event (with duration=XXms) to tracefs trace_marker.
GPUVIS_EXTERN int gpuvis_trace_duration_printf( float duration, const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 2, 3 );
GPUVIS_EXTERN int gpuvis_trace_duration_vprintf( float duration, const char *fmt, va_list ap ) GPUVIS_ATTR_PRINTF( 2, 0 );
// Write user event (with begin_ctx=XX) to tracefs trace_marker.
GPUVIS_EXTERN int gpuvis_trace_begin_ctx_printf( unsigned int ctx, const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 2, 3 );
GPUVIS_EXTERN int gpuvis_trace_begin_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap ) GPUVIS_ATTR_PRINTF( 2, 0 );
// Write user event (with end_ctx=XX) to tracefs trace_marker.
GPUVIS_EXTERN int gpuvis_trace_end_ctx_printf( unsigned int ctx, const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 2, 3 );
GPUVIS_EXTERN int gpuvis_trace_end_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap ) GPUVIS_ATTR_PRINTF( 2, 0 );
// Execute "trace-cmd start -b 2000 -D -i -e sched:sched_switch -e ..."
GPUVIS_EXTERN int gpuvis_start_tracing( unsigned int kbuffersize );
// Execute "trace-cmd extract"
GPUVIS_EXTERN int gpuvis_trigger_capture_and_keep_tracing( char *filename, size_t size );
// Execute "trace-cmd reset"
GPUVIS_EXTERN int gpuvis_stop_tracing( void );
// -1: tracing not setup, 0: tracing disabled, 1: tracing enabled.
GPUVIS_EXTERN int gpuvis_tracing_on( void );
// Get tracefs directory. Ie: /sys/kernel/tracing. Returns "" on error.
GPUVIS_EXTERN const char *gpuvis_get_tracefs_dir( void );
// Get tracefs file path in buf. Ie: /sys/kernel/tracing/trace_marker. Returns NULL on error.
GPUVIS_EXTERN const char *gpuvis_get_tracefs_filename( char *buf, size_t buflen, const char *file );
// Internal function used by GPUVIS_COUNT_HOT_FUNC_CALLS macro
GPUVIS_EXTERN void gpuvis_count_hot_func_calls_internal_( const char *func );
struct GpuvisTraceBlock;
static inline void gpuvis_trace_block_begin( struct GpuvisTraceBlock *block, const char *str );
static inline void gpuvis_trace_block_end( struct GpuvisTraceBlock *block );
struct GpuvisTraceBlockf;
static inline void gpuvis_trace_blockf_vbegin( struct GpuvisTraceBlockf *block, const char *fmt, va_list ap );
static inline void gpuvis_trace_blockf_begin( struct GpuvisTraceBlockf *block, const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 2, 3 );
static inline void gpuvis_trace_blockf_end( struct GpuvisTraceBlockf *block );
#define LNAME3( _name, _line ) _name ## _line
#define LNAME2( _name, _line ) LNAME3( _name, _line )
#define LNAME( _name ) LNAME2( _name, __LINE__ )
struct GpuvisTraceBlock
{
uint64_t m_t0;
const char *m_str;
#ifdef __cplusplus
GpuvisTraceBlock( const char *str )
{
gpuvis_trace_block_begin( this, str );
}
~GpuvisTraceBlock()
{
gpuvis_trace_block_end( this );
}
#endif
};
struct GpuvisTraceBlockf
{
uint64_t m_t0;
char m_buf[ TRACE_BUF_SIZE ];
#ifdef __cplusplus
GpuvisTraceBlockf( const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 2, 3 )
{
va_list args;
va_start( args, fmt );
gpuvis_trace_blockf_vbegin( this, fmt, args );
va_end( args );
}
~GpuvisTraceBlockf()
{
gpuvis_trace_blockf_end( this );
}
#endif
};
#ifdef __cplusplus
#define GPUVIS_TRACE_BLOCK( _conststr ) GpuvisTraceBlock LNAME( gpuvistimeblock )( _conststr )
#define GPUVIS_TRACE_BLOCKF( _fmt, ... ) GpuvisTraceBlockf LNAME( gpuvistimeblock )( _fmt, __VA_ARGS__ )
#else
#if defined( __clang__ ) || defined( __GNUC__ )
#define GPUVIS_TRACE_BLOCKF_INIT( _unique, _fmt, ... ) \
({ \
struct GpuvisTraceBlockf _unique; \
gpuvis_trace_blockf_begin( & _unique, _fmt, __VA_ARGS__ ); \
_unique; \
})
#define GPUVIS_TRACE_BLOCKF( _fmt, ...) \
GPUVIS_CLEANUP_FUNC( gpuvis_trace_blockf_end ) GPUVIS_MAY_BE_UNUSED struct GpuvisTraceBlockf LNAME( gpuvistimeblock ) = \
GPUVIS_TRACE_BLOCKF_INIT( LNAME( gpuvistimeblock_init ), _fmt, __VA_ARGS__ )
#define GPUVIS_TRACE_BLOCK( _conststr ) \
GPUVIS_CLEANUP_FUNC( gpuvis_trace_block_end ) GPUVIS_MAY_BE_UNUSED struct GpuvisTraceBlock LNAME( gpuvistimeblock ) = \
{\
.m_t0 = gpuvis_gettime_u64(), \
.m_str = _conststr \
}
#else
#define GPUVIS_TRACE_BLOCKF( _fmt, ... )
#define GPUVIS_TRACE_BLOCK( _conststr )
#endif // __clang__ || __GNUC__
#endif // __cplusplus
static inline uint64_t gpuvis_gettime_u64( void )
{
struct timespec ts;
clock_gettime( CLOCK_MONOTONIC, &ts );
return ( ( uint64_t )ts.tv_sec * 1000000000LL) + ts.tv_nsec;
}
static inline void gpuvis_trace_block_finalize( uint64_t m_t0, const char *str )
{
uint64_t dt = gpuvis_gettime_u64() - m_t0;
// The cpu clock_gettime() functions seems to vary compared to the
// ftrace event timestamps. If we don't reduce the duration here,
// scopes oftentimes won't stack correctly when they're drawn.
if ( dt > 11000 )
dt -= 11000;
gpuvis_trace_printf( "%s (lduration=-%lu)", str, dt );
}
static inline void gpuvis_trace_block_begin( struct GpuvisTraceBlock* block, const char *str )
{
block->m_str = str;
block->m_t0 = gpuvis_gettime_u64();
}
static inline void gpuvis_trace_block_end( struct GpuvisTraceBlock *block )
{
gpuvis_trace_block_finalize(block->m_t0, block->m_str);
}
static inline void gpuvis_trace_blockf_vbegin( struct GpuvisTraceBlockf *block, const char *fmt, va_list ap)
{
vsnprintf(block->m_buf, sizeof(block->m_buf), fmt, ap);
block->m_t0 = gpuvis_gettime_u64();
}
static inline void gpuvis_trace_blockf_begin( struct GpuvisTraceBlockf *block, const char *fmt, ... )
{
va_list args;
va_start( args, fmt );
gpuvis_trace_blockf_vbegin( block, fmt, args );
va_end( args );
}
static inline void gpuvis_trace_blockf_end( struct GpuvisTraceBlockf *block )
{
gpuvis_trace_block_finalize( block->m_t0, block->m_buf );
}
#define GPUVIS_COUNT_HOT_FUNC_CALLS() gpuvis_count_hot_func_calls_internal_( __func__ );
#else
static inline int gpuvis_trace_init() { return -1; }
static inline void gpuvis_trace_shutdown() {}
static inline int gpuvis_trace_printf( const char *fmt, ... ) { return 0; }
static inline int gpuvis_trace_vprintf( const char *fmt, va_list ap ) { return 0; }
static inline int gpuvis_trace_duration_printf( float duration, const char *fmt, ... ) { return 0; }
static inline int gpuvis_trace_duration_vprintf( float duration, const char *fmt, va_list ap ) { return 0; }
static inline int gpuvis_trace_begin_ctx_printf( unsigned int ctx, const char *fmt, ... ) { return 0; }
static inline int gpuvis_trace_begin_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap ) { return 0; }
static inline int gpuvis_trace_end_ctx_printf( unsigned int ctx, const char *fmt, ... ) { return 0; }
static inline int gpuvis_trace_end_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap ) { return 0; }
static inline int gpuvis_start_tracing( unsigned int kbuffersize ) { return 0; }
static inline int gpuvis_trigger_capture_and_keep_tracing( char *filename, size_t size ) { return 0; }
static inline int gpuvis_stop_tracing() { return 0; }
static inline int gpuvis_tracing_on() { return -1; }
static inline const char *gpuvis_get_tracefs_dir() { return ""; }
static inline const char *gpuvis_get_tracefs_filename( char *buf, size_t buflen, const char *file ) { return NULL; }
struct GpuvisTraceBlock;
static inline void gpuvis_trace_block_begin( struct GpuvisTraceBlock *block, const char *str ) {}
static inline void gpuvis_trace_block_end( struct GpuvisTraceBlock *block ) {}
struct GpuvisTraceBlockf;
static inline void gpuvis_trace_blockf_vbegin( struct GpuvisTraceBlockf *block, const char *fmt, va_list ap ) {}
static inline void gpuvis_trace_blockf_begin( struct GpuvisTraceBlockf *block, const char *fmt, ... ) {}
static inline void gpuvis_trace_blockf_end( struct GpuvisTraceBlockf *block ) {}
#define GPUVIS_TRACE_BLOCK( _conststr )
#define GPUVIS_TRACE_BLOCKF( _fmt, ... )
#define GPUVIS_COUNT_HOT_FUNC_CALLS()
#endif // !GPUVIS_TRACE_UTILS_DISABLE
#if defined( GPUVIS_TRACE_IMPLEMENTATION ) && !defined( GPUVIS_TRACE_UTILS_DISABLE )
//////////////////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION SECTION
//
#define _GNU_SOURCE 1
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/vfs.h>
#include <linux/magic.h>
#include <sys/syscall.h>
#undef GPUVIS_EXTERN
#ifdef __cplusplus
#define GPUVIS_EXTERN extern "C"
#else
#define GPUVIS_EXTERN
#endif
#ifndef TRACEFS_MAGIC
#define TRACEFS_MAGIC 0x74726163
#endif
#define GPUVIS_STR( x ) #x
#define GPUVIS_STR_VALUE( x ) GPUVIS_STR( x )
static int g_trace_fd = -2;
static int g_tracefs_dir_inited = 0;
static char g_tracefs_dir[ PATH_MAX ];
#ifdef __cplusplus
#include <unordered_map>
struct funcinfo_t
{
uint64_t tfirst = 0;
uint64_t tlast = 0;
uint32_t count = 0;
};
static std::unordered_map< pid_t, std::unordered_map< const char *, funcinfo_t > > g_hotfuncs;
#endif // __cplusplus
static pid_t gpuvis_gettid()
{
return ( pid_t )syscall( SYS_gettid );
}
static int exec_tracecmd( const char *cmd )
{
int ret;
FILE *fh = popen( cmd, "r" );
if ( !fh )
{
//$ TODO: popen() failed: errno
ret = -1;
}
else
{
char buf[ 8192 ];
while ( fgets( buf, sizeof( buf ), fh ) )
{
//$ TODO
printf( "%s: %s", __func__, buf );
}
if ( feof( fh ) )
{
int pclose_ret = pclose( fh );
ret = WEXITSTATUS( pclose_ret );
}
else
{
//$ TODO: Failed to read pipe to end: errno
pclose( fh );
ret = -1;
}
}
return ret;
}
GPUVIS_EXTERN int gpuvis_trace_init()
{
if ( g_trace_fd == -2 )
{
char filename[ PATH_MAX ];
// The "trace_marker" file allows userspace to write into the ftrace buffer.
if ( !gpuvis_get_tracefs_filename( filename, sizeof( filename ), "trace_marker" ) )
g_trace_fd = -1;
else
g_trace_fd = open( filename, O_WRONLY );
}
return g_trace_fd;
}
#if !defined( __cplusplus )
static void flush_hot_func_calls()
{
//$ TODO: hot func calls for C
}
#else
static void flush_hot_func_calls()
{
if ( g_hotfuncs.empty() )
return;
uint64_t t0 = gpuvis_gettime_u64();
for ( auto &x : g_hotfuncs )
{
for ( auto &y : x.second )
{
if ( y.second.count )
{
pid_t tid = x.first;
const char *func = y.first;
uint64_t offset = t0 - y.second.tfirst;
uint64_t duration = y.second.tlast - y.second.tfirst;
gpuvis_trace_printf( "%s calls:%u (lduration=%lu tid=%d offset=-%lu)\n",
func, y.second.count, duration, tid, offset );
}
}
}
g_hotfuncs.clear();
}
GPUVIS_EXTERN void gpuvis_count_hot_func_calls_internal_( const char *func )
{
static THREAD_LOCAL pid_t s_tid = gpuvis_gettid();
uint64_t t0 = gpuvis_gettime_u64();
auto &x = g_hotfuncs[ s_tid ];
auto &y = x[ func ];
if ( !y.count )
{
y.count = 1;
y.tfirst = t0;
y.tlast = t0 + 1;
}
else if ( t0 - y.tlast >= 3 * 1000000 ) // 3ms
{
gpuvis_trace_printf( "%s calls:%u (lduration=%lu offset=-%lu)\n",
func, y.count, y.tlast - y.tfirst, t0 - y.tfirst );
y.count = 1;
y.tfirst = t0;
y.tlast = t0 + 1;
}
else
{
y.tlast = t0;
y.count++;
}
}
#endif // __cplusplus
GPUVIS_EXTERN void gpuvis_trace_shutdown()
{
flush_hot_func_calls();
if ( g_trace_fd >= 0 )
close( g_trace_fd );
g_trace_fd = -2;
g_tracefs_dir_inited = 0;
g_tracefs_dir[ 0 ] = 0;
}
static int trace_printf_impl( const char *keystr, const char *fmt, va_list ap ) GPUVIS_ATTR_PRINTF( 2, 0 );
static int trace_printf_impl( const char *keystr, const char *fmt, va_list ap )
{
int ret = -1;
if ( gpuvis_trace_init() >= 0 )
{
int n;
char buf[ TRACE_BUF_SIZE ];
n = vsnprintf( buf, sizeof( buf ), fmt, ap );
if ( ( n > 0 ) || ( !n && keystr ) )
{
if ( ( size_t )n >= sizeof( buf ) )
n = sizeof( buf ) - 1;
if ( keystr && keystr[ 0 ] )
{
int keystrlen = strlen( keystr );
if ( ( size_t )n + keystrlen >= sizeof( buf ) )
n = sizeof( buf ) - keystrlen - 1;
strcpy( buf + n, keystr );
n += keystrlen;
}
ret = write( g_trace_fd, buf, n );
}
}
return ret;
}
GPUVIS_EXTERN int gpuvis_trace_printf( const char *fmt, ... )
{
int ret;
va_list ap;
va_start( ap, fmt );
ret = gpuvis_trace_vprintf( fmt, ap );
va_end( ap );
return ret;
}
GPUVIS_EXTERN int gpuvis_trace_vprintf( const char *fmt, va_list ap )
{
return trace_printf_impl( NULL, fmt, ap );
}
GPUVIS_EXTERN int gpuvis_trace_duration_printf( float duration, const char *fmt, ... )
{
int ret;
va_list ap;
va_start( ap, fmt );
ret = gpuvis_trace_duration_vprintf( duration, fmt, ap );
va_end( ap );
return ret;
}
GPUVIS_EXTERN int gpuvis_trace_duration_vprintf( float duration, const char *fmt, va_list ap )
{
char keystr[ 128 ];
snprintf( keystr, sizeof( keystr ), " (duration=%f)", duration ); //$ TODO: Try this with more precision?
return trace_printf_impl( keystr, fmt, ap );
}
GPUVIS_EXTERN int gpuvis_trace_begin_ctx_printf( unsigned int ctx, const char *fmt, ... )
{
int ret;
va_list ap;
va_start( ap, fmt );
ret = gpuvis_trace_begin_ctx_vprintf( ctx, fmt, ap );
va_end( ap );
return ret;
}
GPUVIS_EXTERN int gpuvis_trace_begin_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap )
{
char keystr[ 128 ];
snprintf( keystr, sizeof( keystr ), " (begin_ctx=%u)", ctx );
return trace_printf_impl( keystr, fmt, ap );
}
GPUVIS_EXTERN int gpuvis_trace_end_ctx_printf( unsigned int ctx, const char *fmt, ... )
{
int ret;
va_list ap;
va_start( ap, fmt );
ret = gpuvis_trace_end_ctx_vprintf( ctx, fmt, ap );
va_end( ap );
return ret;
}
GPUVIS_EXTERN int gpuvis_trace_end_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap )
{
char keystr[ 128 ];
snprintf( keystr, sizeof( keystr ), " (end_ctx=%u)", ctx );
return trace_printf_impl( keystr, fmt, ap );
}
GPUVIS_EXTERN int gpuvis_start_tracing( unsigned int kbuffersize )
{
static const char fmt[] =
"trace-cmd start -b %u -D -i "
// https://github.com/mikesart/gpuvis/wiki/TechDocs-Linux-Scheduler
" -e sched:sched_switch"
" -e sched:sched_process_fork"
" -e sched:sched_process_exec"
" -e sched:sched_process_exit"
" -e drm:drm_vblank_event"
" -e drm:drm_vblank_event_queued"
" -e drm:drm_vblank_event_delivered"
// https://github.com/mikesart/gpuvis/wiki/TechDocs-AMDGpu
" -e amdgpu:amdgpu_vm_flush"
" -e amdgpu:amdgpu_cs_ioctl"
" -e amdgpu:amdgpu_sched_run_job"
" -e *fence:*fence_signaled"
// https://github.com/mikesart/gpuvis/wiki/TechDocs-Intel
" -e i915:i915_flip_request"
" -e i915:i915_flip_complete"
" -e i915:intel_gpu_freq_change"
" -e i915:i915_gem_request_add"
" -e i915:i915_gem_request_submit" // Require CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS
" -e i915:i915_gem_request_in" // Kconfig option to be enabled.
" -e i915:i915_gem_request_out" //
" -e i915:intel_engine_notify"
" -e i915:i915_gem_request_wait_begin"
" -e i915:i915_gem_request_wait_end 2>&1";
char cmd[ 8192 ];
if ( !kbuffersize )
kbuffersize = 16 * 1024;
snprintf( cmd, sizeof( cmd ), fmt, kbuffersize );
return exec_tracecmd( cmd );
}
GPUVIS_EXTERN int gpuvis_trigger_capture_and_keep_tracing( char *filename, size_t size )
{
int ret = -1;
if ( filename )
filename[ 0 ] = 0;
flush_hot_func_calls();
if ( gpuvis_tracing_on() )
{
char datetime[ 128 ];
char cmd[ PATH_MAX ];
char exebuf[ PATH_MAX ];
const char *exename = NULL;
time_t t = time( NULL );
struct tm *tmp = localtime( &t );
strftime( datetime, sizeof( datetime ), "%Y-%m-%d_%H-%M-%S", tmp );
datetime[ sizeof( datetime ) - 1 ] = 0;
ssize_t cbytes = readlink( "/proc/self/exe", exebuf, sizeof( exebuf ) - 1 );
if ( cbytes > 0 )
{
exebuf[ cbytes ] = 0;
exename = strrchr( exebuf, '/' );
}
exename = exename ? ( exename + 1 ) : "trace";
// Stop tracing
exec_tracecmd( "trace-cmd stop 2>&1" );
// Save the trace data to something like "glxgears_2017-10-13_17-52-56.dat"
snprintf( cmd, sizeof( cmd ),
"trace-cmd extract -k -o \"%s_%s.dat\" > /tmp/blah.log 2>&1 &",
exename, datetime );
cmd[ sizeof( cmd ) - 1 ] = 0;
ret = system( cmd );
if ( filename && !ret )
snprintf( filename, size, "%s_%s.dat", exename, datetime );
// Restart tracing
exec_tracecmd( "trace-cmd restart 2>&1" );
}
return ret;
}
GPUVIS_EXTERN int gpuvis_stop_tracing()
{
flush_hot_func_calls();
int ret = exec_tracecmd( "trace-cmd reset 2>&1");
// Try freeing any snapshot buffers as well
exec_tracecmd( "trace-cmd snapshot -f 2>&1" );
return ret;
}
GPUVIS_EXTERN int gpuvis_tracing_on()
{
int ret = -1;
char buf[ 32 ];
char filename[ PATH_MAX ];
if ( gpuvis_get_tracefs_filename( filename, PATH_MAX, "tracing_on" ) )
{
int fd = open( filename, O_RDONLY );
if ( fd >= 0 )
{
if ( read( fd, buf, sizeof( buf ) ) > 0 )
ret = atoi( buf );
close( fd );
}
}
return ret;
}
static int is_tracefs_dir( const char *dir )
{
struct statfs stat;
return !statfs( dir, &stat ) && ( stat.f_type == TRACEFS_MAGIC );
}
GPUVIS_EXTERN const char *gpuvis_get_tracefs_dir()
{
if ( !g_tracefs_dir_inited )
{
size_t i;
static const char *tracefs_dirs[] =
{
"/sys/kernel/tracing",
"/sys/kernel/debug/tracing",
"/tracing",
"/trace",
};
for ( i = 0; i < sizeof( tracefs_dirs ) / sizeof( tracefs_dirs[ 0 ] ); i++ )
{
if ( is_tracefs_dir( tracefs_dirs[ i ] ) )
{
strncpy( g_tracefs_dir, tracefs_dirs[ i ], PATH_MAX );
g_tracefs_dir[ PATH_MAX - 1 ] = 0;
break;
}
}
if ( !g_tracefs_dir[ 0 ] )
{
FILE *fp;
char type[ 128 ];
char dir[ PATH_MAX + 1 ];
fp = fopen( "/proc/mounts", "r" );
if ( fp )
{
while ( fscanf( fp, "%*s %" GPUVIS_STR_VALUE( PATH_MAX ) "s %127s %*s %*d %*d\n", dir, type ) == 2 )
{
if ( !strcmp( type, "tracefs" ) && is_tracefs_dir( dir ) )
{
strncpy( g_tracefs_dir, dir, PATH_MAX );
g_tracefs_dir[ PATH_MAX - 1 ] = 0;
break;
}
}
fclose( fp );
}
}
g_tracefs_dir_inited = 1;
}
return g_tracefs_dir;
}
GPUVIS_EXTERN const char *gpuvis_get_tracefs_filename( char *buf, size_t buflen, const char *file )
{
const char *tracefs_dir = gpuvis_get_tracefs_dir();
if ( tracefs_dir[ 0 ] )
{
snprintf( buf, buflen, "%s/%s", tracefs_dir, file );
buf[ buflen - 1 ] = 0;
return buf;
}
return NULL;
}
#endif // GPUVIS_TRACE_IMPLEMENTATION
#endif // _GPUVIS_TRACE_UTILS_H_

53
src/util/perf/u_gpuvis.c Normal file
View File

@@ -0,0 +1,53 @@
/*
* Copyright 2023 Bas Nieuwenhuizen
* SPDX-License-Identifier: MIT
*/
#include "u_gpuvis.h"
#include <threads.h>
#define GPUVIS_TRACE_IMPLEMENTATION
#include "gpuvis_trace_utils.h"
/* Random base value to prevent collisions. As contexts are considered thread
* global by gpuvis, collisions are quite likely if we start at 0 and there
* are independent libraries tacing
*/
static unsigned int gpuvis_base_ctx;
static _Thread_local unsigned int gpuvis_current_ctx;
static once_flag gpuvis_once_flag = ONCE_FLAG_INIT;
static void
util_gpuvis_init_once()
{
gpuvis_trace_init();
/* Initialize it by address to avoid collisions between libraries using
* this code (e.g. GL & vulkan) */
gpuvis_base_ctx = (uintptr_t) util_gpuvis_init_once >> 12;
}
void
util_gpuvis_init(void)
{
call_once(&gpuvis_once_flag, util_gpuvis_init_once);
}
void
util_gpuvis_begin(const char *name)
{
unsigned int ctx = gpuvis_base_ctx + ++gpuvis_current_ctx;
gpuvis_trace_begin_ctx_printf(ctx, "mesa:%s", name);
}
void
util_gpuvis_end(void)
{
unsigned int ctx = gpuvis_base_ctx + gpuvis_current_ctx--;
/* Use an empty string to avoid warnings about an empty format string. */
gpuvis_trace_end_ctx_printf(ctx, "%s", "");
}

35
src/util/perf/u_gpuvis.h Normal file
View File

@@ -0,0 +1,35 @@
/*
* Copyright 2023 Bas Nieuwenhuizen
* SPDX-License-Identifier: MIT
*/
#ifndef U_GPUVIS_H
#define U_GPUVIS_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef HAVE_GPUVIS
void util_gpuvis_init(void);
void util_gpuvis_begin(const char *name);
/* ctx needs to be the return value from begin*/
void util_gpuvis_end(void);
#else
static inline void
util_gpuvis_init(void)
{
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* U_GPUVIS_H */