• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //////////////////////////////////////////////////////////////////////////////
2 // gpuvis_trace_utils.h - v0.10 - public domain
3 //   no warranty is offered or implied; use this code at your own risk
4 //
5 // This is a single header file with useful utilities for gpuvis linux tracing
6 //
7 // ============================================================================
8 // You MUST define GPUVIS_TRACE_IMPLEMENTATION in EXACTLY _one_ C or C++ file
9 // that includes this header, BEFORE the include, like this:
10 //
11 //   #define GPUVIS_TRACE_IMPLEMENTATION
12 //   #include "gpuvis_trace_utils.h"
13 //
14 // All other files should just #include "gpuvis_trace_utils.h" w/o the #define.
15 // ============================================================================
16 //
17 // Credits
18 //
19 //    Michael Sartain
20 //
21 // LICENSE
22 //
23 //   This software is dual-licensed to the public domain and under the following
24 //   license: you are granted a perpetual, irrevocable license to copy, modify,
25 //   publish, and distribute this file as you see fit.
26 
27 //////////////////////////////////////////////////////////////////////////////
28 //
29 //       INCLUDE SECTION
30 //
31 
32 #ifndef _GPUVIS_TRACE_UTILS_H_
33 #define _GPUVIS_TRACE_UTILS_H_
34 
35 #include <stdarg.h>
36 
37 #if !defined( __linux__ )
38 #define GPUVIS_TRACE_UTILS_DISABLE
39 #endif
40 
41 #if defined( __clang__ ) || defined( __GNUC__ )
42 // printf-style warnings for user functions.
43 #define GPUVIS_ATTR_PRINTF( _x, _y ) __attribute__( ( __format__( __printf__, _x, _y ) ) )
44 #define GPUVIS_MAY_BE_UNUSED __attribute__( ( unused ) )
45 #define GPUVIS_CLEANUP_FUNC( x ) __attribute__( ( __cleanup__( x ) ) )
46 #else
47 #define GPUVIS_ATTR_PRINTF( _x, _y )
48 #define GPUVIS_MAY_BE_UNUSED
49 #define GPUVIS_CLEANUP_FUNC( x )
50 #endif
51 
52 #if !defined( GPUVIS_TRACE_UTILS_DISABLE )
53 
54 #include <inttypes.h>
55 #include <time.h>
56 #include <stdint.h>
57 #include <stdlib.h>
58 #include <stdio.h>
59 
60 #ifdef __cplusplus
61   #define GPUVIS_EXTERN   extern "C"
62   #if __cplusplus>=201103L
63     #define THREAD_LOCAL thread_local
64   #else
65     #define THREAD_LOCAL __thread
66   #endif
67 #else
68   #define GPUVIS_EXTERN   extern
69 #endif
70 
71 // From kernel/trace/trace.h
72 #ifndef TRACE_BUF_SIZE
73 #define TRACE_BUF_SIZE     1024
74 #endif
75 
76 // Try to open tracefs trace_marker file for writing. Returns -1 on error.
77 GPUVIS_EXTERN int gpuvis_trace_init( void );
78 // Close tracefs trace_marker file.
79 GPUVIS_EXTERN void gpuvis_trace_shutdown( void );
80 
81 // Write user event to tracefs trace_marker.
82 GPUVIS_EXTERN int gpuvis_trace_printf( const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 1, 2 );
83 GPUVIS_EXTERN int gpuvis_trace_vprintf( const char *fmt, va_list ap ) GPUVIS_ATTR_PRINTF( 1, 0 );
84 
85 // Write user event (with duration=XXms) to tracefs trace_marker.
86 GPUVIS_EXTERN int gpuvis_trace_duration_printf( float duration, const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 2, 3 );
87 GPUVIS_EXTERN int gpuvis_trace_duration_vprintf( float duration, const char *fmt, va_list ap ) GPUVIS_ATTR_PRINTF( 2, 0 );
88 
89 // Write user event (with begin_ctx=XX) to tracefs trace_marker.
90 GPUVIS_EXTERN int gpuvis_trace_begin_ctx_printf( unsigned int ctx, const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 2, 3 );
91 GPUVIS_EXTERN int gpuvis_trace_begin_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap ) GPUVIS_ATTR_PRINTF( 2, 0 );
92 
93 // Write user event (with end_ctx=XX) to tracefs trace_marker.
94 GPUVIS_EXTERN int gpuvis_trace_end_ctx_printf( unsigned int ctx, const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 2, 3 );
95 GPUVIS_EXTERN int gpuvis_trace_end_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap ) GPUVIS_ATTR_PRINTF( 2, 0 );
96 
97 // Execute "trace-cmd start -b 2000 -D -i -e sched:sched_switch -e ..."
98 GPUVIS_EXTERN int gpuvis_start_tracing( unsigned int kbuffersize );
99 // Execute "trace-cmd extract"
100 GPUVIS_EXTERN int gpuvis_trigger_capture_and_keep_tracing( char *filename, size_t size );
101 // Execute "trace-cmd reset"
102 GPUVIS_EXTERN int gpuvis_stop_tracing( void );
103 
104 // -1: tracing not setup, 0: tracing disabled, 1: tracing enabled.
105 GPUVIS_EXTERN int gpuvis_tracing_on( void );
106 
107 // Get tracefs directory. Ie: /sys/kernel/tracing. Returns "" on error.
108 GPUVIS_EXTERN const char *gpuvis_get_tracefs_dir( void );
109 
110 // Get tracefs file path in buf. Ie: /sys/kernel/tracing/trace_marker. Returns NULL on error.
111 GPUVIS_EXTERN const char *gpuvis_get_tracefs_filename( char *buf, size_t buflen, const char *file );
112 
113 // Internal function used by GPUVIS_COUNT_HOT_FUNC_CALLS macro
114 GPUVIS_EXTERN void gpuvis_count_hot_func_calls_internal_( const char *func );
115 
116 struct GpuvisTraceBlock;
117 static inline void gpuvis_trace_block_begin( struct GpuvisTraceBlock *block, const char *str );
118 static inline void gpuvis_trace_block_end( struct GpuvisTraceBlock *block );
119 
120 struct GpuvisTraceBlockf;
121 static inline void gpuvis_trace_blockf_vbegin( struct GpuvisTraceBlockf *block, const char *fmt, va_list ap );
122 static inline void gpuvis_trace_blockf_begin( struct GpuvisTraceBlockf *block, const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 2, 3 );
123 static inline void gpuvis_trace_blockf_end( struct GpuvisTraceBlockf *block );
124 
125 #define LNAME3( _name, _line ) _name ## _line
126 #define LNAME2( _name, _line ) LNAME3( _name, _line )
127 #define LNAME( _name ) LNAME2( _name, __LINE__ )
128 
129 struct GpuvisTraceBlock
130 {
131     uint64_t m_t0;
132     const char *m_str;
133 
134 #ifdef __cplusplus
GpuvisTraceBlockGpuvisTraceBlock135     GpuvisTraceBlock( const char *str )
136     {
137         gpuvis_trace_block_begin( this, str );
138     }
139 
~GpuvisTraceBlockGpuvisTraceBlock140     ~GpuvisTraceBlock()
141     {
142         gpuvis_trace_block_end( this );
143     }
144 #endif
145 };
146 
147 struct GpuvisTraceBlockf
148 {
149     uint64_t m_t0;
150     char m_buf[ TRACE_BUF_SIZE ];
151 
152 #ifdef __cplusplus
GpuvisTraceBlockfGpuvisTraceBlockf153     GpuvisTraceBlockf( const char *fmt, ... ) GPUVIS_ATTR_PRINTF( 2, 3 )
154     {
155         va_list args;
156         va_start( args, fmt );
157         gpuvis_trace_blockf_vbegin( this, fmt, args );
158         va_end( args );
159     }
160 
~GpuvisTraceBlockfGpuvisTraceBlockf161     ~GpuvisTraceBlockf()
162     {
163         gpuvis_trace_blockf_end( this );
164     }
165 #endif
166 };
167 
168 #ifdef __cplusplus
169 
170 #define GPUVIS_TRACE_BLOCK( _conststr ) GpuvisTraceBlock LNAME( gpuvistimeblock )( _conststr )
171 #define GPUVIS_TRACE_BLOCKF( _fmt, ...  ) GpuvisTraceBlockf LNAME( gpuvistimeblock )( _fmt, __VA_ARGS__ )
172 
173 #else
174 
175 #if defined( __clang__ ) || defined( __GNUC__ )
176 
177 #define GPUVIS_TRACE_BLOCKF_INIT( _unique, _fmt, ...  ) \
178     ({ \
179         struct GpuvisTraceBlockf _unique; \
180         gpuvis_trace_blockf_begin( & _unique, _fmt, __VA_ARGS__ ); \
181         _unique; \
182      })
183 
184 #define GPUVIS_TRACE_BLOCKF( _fmt,  ...) \
185     GPUVIS_CLEANUP_FUNC( gpuvis_trace_blockf_end ) GPUVIS_MAY_BE_UNUSED struct GpuvisTraceBlockf LNAME( gpuvistimeblock ) = \
186         GPUVIS_TRACE_BLOCKF_INIT( LNAME( gpuvistimeblock_init ), _fmt, __VA_ARGS__ )
187 
188 #define GPUVIS_TRACE_BLOCK( _conststr ) \
189     GPUVIS_CLEANUP_FUNC( gpuvis_trace_block_end ) GPUVIS_MAY_BE_UNUSED struct GpuvisTraceBlock LNAME( gpuvistimeblock ) = \
190         {\
191             .m_t0 = gpuvis_gettime_u64(), \
192             .m_str = _conststr \
193         }
194 
195 #else
196 
197 #define GPUVIS_TRACE_BLOCKF( _fmt,  ... )
198 #define GPUVIS_TRACE_BLOCK( _conststr )
199 
200 #endif // __clang__ || __GNUC__
201 
202 #endif // __cplusplus
203 
gpuvis_gettime_u64(void)204 static inline uint64_t gpuvis_gettime_u64( void )
205 {
206     struct timespec ts;
207 
208     clock_gettime( CLOCK_MONOTONIC, &ts );
209     return ( ( uint64_t )ts.tv_sec * 1000000000LL) + ts.tv_nsec;
210 }
211 
gpuvis_trace_block_finalize(uint64_t m_t0,const char * str)212 static inline void gpuvis_trace_block_finalize( uint64_t m_t0, const char *str )
213 {
214     uint64_t dt = gpuvis_gettime_u64() - m_t0;
215 
216     // The cpu clock_gettime() functions seems to vary compared to the
217     // ftrace event timestamps. If we don't reduce the duration here,
218     // scopes oftentimes won't stack correctly when they're drawn.
219     if ( dt > 11000 )
220         dt -= 11000;
221 
222     gpuvis_trace_printf( "%s (lduration=-%" PRIu64 ")", str, dt );
223 }
224 
gpuvis_trace_block_begin(struct GpuvisTraceBlock * block,const char * str)225 static inline void gpuvis_trace_block_begin( struct GpuvisTraceBlock* block, const char *str )
226 {
227     block->m_str = str;
228     block->m_t0 = gpuvis_gettime_u64();
229 }
230 
gpuvis_trace_block_end(struct GpuvisTraceBlock * block)231 static inline void gpuvis_trace_block_end( struct GpuvisTraceBlock *block )
232 {
233     gpuvis_trace_block_finalize(block->m_t0, block->m_str);
234 }
235 
gpuvis_trace_blockf_vbegin(struct GpuvisTraceBlockf * block,const char * fmt,va_list ap)236 static inline void gpuvis_trace_blockf_vbegin( struct GpuvisTraceBlockf *block, const char *fmt, va_list ap)
237 {
238     vsnprintf(block->m_buf, sizeof(block->m_buf), fmt, ap);
239     block->m_t0 = gpuvis_gettime_u64();
240 }
241 
gpuvis_trace_blockf_begin(struct GpuvisTraceBlockf * block,const char * fmt,...)242 static inline void gpuvis_trace_blockf_begin( struct GpuvisTraceBlockf *block, const char *fmt, ... )
243 {
244     va_list args;
245 
246     va_start( args, fmt );
247     gpuvis_trace_blockf_vbegin( block, fmt, args );
248     va_end( args );
249 }
250 
gpuvis_trace_blockf_end(struct GpuvisTraceBlockf * block)251 static inline void gpuvis_trace_blockf_end( struct GpuvisTraceBlockf *block )
252 {
253     gpuvis_trace_block_finalize( block->m_t0, block->m_buf );
254 }
255 
256 #define GPUVIS_COUNT_HOT_FUNC_CALLS() gpuvis_count_hot_func_calls_internal_( __func__ );
257 
258 #else
259 
gpuvis_trace_init()260 static inline int gpuvis_trace_init() { return -1; }
gpuvis_trace_shutdown()261 static inline void gpuvis_trace_shutdown() {}
262 
gpuvis_trace_printf(const char * fmt,...)263 static inline int gpuvis_trace_printf( const char *fmt, ... ) { return 0; }
gpuvis_trace_vprintf(const char * fmt,va_list ap)264 static inline int gpuvis_trace_vprintf( const char *fmt, va_list ap ) { return 0; }
265 
gpuvis_trace_duration_printf(float duration,const char * fmt,...)266 static inline int gpuvis_trace_duration_printf( float duration, const char *fmt, ... ) { return 0; }
gpuvis_trace_duration_vprintf(float duration,const char * fmt,va_list ap)267 static inline int gpuvis_trace_duration_vprintf( float duration, const char *fmt, va_list ap ) { return 0; }
268 
gpuvis_trace_begin_ctx_printf(unsigned int ctx,const char * fmt,...)269 static inline int gpuvis_trace_begin_ctx_printf( unsigned int ctx, const char *fmt, ... ) { return 0; }
gpuvis_trace_begin_ctx_vprintf(unsigned int ctx,const char * fmt,va_list ap)270 static inline int gpuvis_trace_begin_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap ) { return 0; }
271 
gpuvis_trace_end_ctx_printf(unsigned int ctx,const char * fmt,...)272 static inline int gpuvis_trace_end_ctx_printf( unsigned int ctx, const char *fmt, ... ) { return 0; }
gpuvis_trace_end_ctx_vprintf(unsigned int ctx,const char * fmt,va_list ap)273 static inline int gpuvis_trace_end_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap ) { return 0; }
274 
gpuvis_start_tracing(unsigned int kbuffersize)275 static inline int gpuvis_start_tracing( unsigned int kbuffersize ) { return 0; }
gpuvis_trigger_capture_and_keep_tracing(char * filename,size_t size)276 static inline int gpuvis_trigger_capture_and_keep_tracing( char *filename, size_t size ) { return 0; }
gpuvis_stop_tracing()277 static inline int gpuvis_stop_tracing() { return 0; }
278 
gpuvis_tracing_on()279 static inline int gpuvis_tracing_on() { return -1; }
280 
gpuvis_get_tracefs_dir()281 static inline const char *gpuvis_get_tracefs_dir() { return ""; }
gpuvis_get_tracefs_filename(char * buf,size_t buflen,const char * file)282 static inline const char *gpuvis_get_tracefs_filename( char *buf, size_t buflen, const char *file ) { return NULL; }
283 
284 struct GpuvisTraceBlock;
gpuvis_trace_block_begin(struct GpuvisTraceBlock * block,const char * str)285 static inline void gpuvis_trace_block_begin( struct GpuvisTraceBlock *block, const char *str ) {}
gpuvis_trace_block_end(struct GpuvisTraceBlock * block)286 static inline void gpuvis_trace_block_end( struct GpuvisTraceBlock *block ) {}
287 
288 struct GpuvisTraceBlockf;
gpuvis_trace_blockf_vbegin(struct GpuvisTraceBlockf * block,const char * fmt,va_list ap)289 static inline void gpuvis_trace_blockf_vbegin( struct GpuvisTraceBlockf *block, const char *fmt, va_list ap ) {}
gpuvis_trace_blockf_begin(struct GpuvisTraceBlockf * block,const char * fmt,...)290 static inline void gpuvis_trace_blockf_begin( struct GpuvisTraceBlockf *block, const char *fmt, ... ) {}
gpuvis_trace_blockf_end(struct GpuvisTraceBlockf * block)291 static inline void gpuvis_trace_blockf_end( struct GpuvisTraceBlockf *block ) {}
292 
293 #define GPUVIS_TRACE_BLOCK( _conststr )
294 #define GPUVIS_TRACE_BLOCKF( _fmt, ...  )
295 
296 #define GPUVIS_COUNT_HOT_FUNC_CALLS()
297 
298 #endif // !GPUVIS_TRACE_UTILS_DISABLE
299 
300 #if defined( GPUVIS_TRACE_IMPLEMENTATION ) && !defined( GPUVIS_TRACE_UTILS_DISABLE )
301 
302 //////////////////////////////////////////////////////////////////////////////
303 //
304 //     IMPLEMENTATION SECTION
305 //
306 
307 #define _GNU_SOURCE 1
308 #include <stdio.h>
309 #include <unistd.h>
310 #include <string.h>
311 #include <errno.h>
312 #include <limits.h>
313 #include <fcntl.h>
314 #include <sys/vfs.h>
315 #include <linux/magic.h>
316 #include <sys/syscall.h>
317 
318 #undef GPUVIS_EXTERN
319 #ifdef __cplusplus
320 #define GPUVIS_EXTERN extern "C"
321 #else
322 #define GPUVIS_EXTERN
323 #endif
324 
325 #ifndef TRACEFS_MAGIC
326 #define TRACEFS_MAGIC      0x74726163
327 #endif
328 
329 #define GPUVIS_STR( x ) #x
330 #define GPUVIS_STR_VALUE( x ) GPUVIS_STR( x )
331 
332 static int g_trace_fd = -2;
333 static int g_tracefs_dir_inited = 0;
334 static char g_tracefs_dir[ PATH_MAX ];
335 
336 #ifdef __cplusplus
337 #include <unordered_map>
338 
339 struct funcinfo_t
340 {
341     uint64_t tfirst = 0;
342     uint64_t tlast = 0;
343     uint32_t count = 0;
344 };
345 static std::unordered_map< pid_t, std::unordered_map< const char *, funcinfo_t > > g_hotfuncs;
346 #endif // __cplusplus
347 
gpuvis_gettid()348 static pid_t gpuvis_gettid()
349 {
350     return ( pid_t )syscall( SYS_gettid );
351 }
352 
exec_tracecmd(const char * cmd)353 static int exec_tracecmd( const char *cmd )
354 {
355     int ret;
356 
357     FILE *fh = popen( cmd, "r" );
358     if ( !fh )
359     {
360         //$ TODO: popen() failed: errno
361         ret = -1;
362     }
363     else
364     {
365         char buf[ 8192 ];
366 
367         while ( fgets( buf, sizeof( buf ), fh ) )
368         {
369             //$ TODO
370             printf( "%s: %s", __func__, buf );
371         }
372 
373         if ( feof( fh ) )
374         {
375             int pclose_ret = pclose( fh );
376 
377             ret = WEXITSTATUS( pclose_ret );
378         }
379         else
380         {
381             //$ TODO: Failed to read pipe to end: errno
382             pclose( fh );
383             ret = -1;
384         }
385     }
386 
387     return ret;
388 }
389 
gpuvis_trace_init()390 GPUVIS_EXTERN int gpuvis_trace_init()
391 {
392     if ( g_trace_fd == -2 )
393     {
394         char filename[ PATH_MAX ];
395 
396         // The "trace_marker" file allows userspace to write into the ftrace buffer.
397         if ( !gpuvis_get_tracefs_filename( filename, sizeof( filename ), "trace_marker" ) )
398             g_trace_fd = -1;
399         else
400             g_trace_fd = open( filename, O_WRONLY );
401     }
402 
403     return g_trace_fd;
404 }
405 
406 #if !defined( __cplusplus )
flush_hot_func_calls()407 static void flush_hot_func_calls()
408 {
409     //$ TODO: hot func calls for C
410 }
411 #else
flush_hot_func_calls()412 static void flush_hot_func_calls()
413 {
414     if ( g_hotfuncs.empty() )
415         return;
416 
417     uint64_t t0 = gpuvis_gettime_u64();
418 
419     for ( auto &x : g_hotfuncs )
420     {
421         for ( auto &y : x.second )
422         {
423             if ( y.second.count )
424             {
425                 pid_t tid = x.first;
426                 const char *func = y.first;
427                 uint64_t offset = t0 - y.second.tfirst;
428                 uint64_t duration = y.second.tlast - y.second.tfirst;
429 
430                 gpuvis_trace_printf( "%s calls:%u (lduration=%" PRIu64 " tid=%d offset=-%" PRIu64 ")\n",
431                                      func, y.second.count, duration, tid, offset );
432             }
433         }
434     }
435 
436     g_hotfuncs.clear();
437 }
438 
gpuvis_count_hot_func_calls_internal_(const char * func)439 GPUVIS_EXTERN void gpuvis_count_hot_func_calls_internal_( const char *func )
440 {
441     static THREAD_LOCAL pid_t s_tid = gpuvis_gettid();
442 
443     uint64_t t0 = gpuvis_gettime_u64();
444     auto &x = g_hotfuncs[ s_tid ];
445     auto &y = x[ func ];
446 
447     if ( !y.count )
448     {
449         y.count = 1;
450         y.tfirst = t0;
451         y.tlast = t0 + 1;
452     }
453     else if ( t0 - y.tlast >= 3 * 1000000 ) // 3ms
454     {
455         gpuvis_trace_printf( "%s calls:%u (lduration=%" PRIu64 " offset=-%" PRIu64 ")\n",
456                              func, y.count, y.tlast - y.tfirst, t0 - y.tfirst );
457 
458         y.count = 1;
459         y.tfirst = t0;
460         y.tlast = t0 + 1;
461     }
462     else
463     {
464         y.tlast = t0;
465         y.count++;
466     }
467 }
468 #endif // __cplusplus
469 
gpuvis_trace_shutdown()470 GPUVIS_EXTERN void gpuvis_trace_shutdown()
471 {
472     flush_hot_func_calls();
473 
474     if ( g_trace_fd >= 0 )
475         close( g_trace_fd );
476     g_trace_fd = -2;
477 
478     g_tracefs_dir_inited = 0;
479     g_tracefs_dir[ 0 ] = 0;
480 }
481 
482 static int trace_printf_impl( const char *keystr, const char *fmt, va_list ap ) GPUVIS_ATTR_PRINTF( 2, 0 );
trace_printf_impl(const char * keystr,const char * fmt,va_list ap)483 static int trace_printf_impl( const char *keystr, const char *fmt, va_list ap )
484 {
485     int ret = -1;
486 
487     if ( gpuvis_trace_init() >= 0 )
488     {
489         int n;
490         char buf[ TRACE_BUF_SIZE ];
491 
492         n = vsnprintf( buf, sizeof( buf ), fmt, ap );
493 
494         if ( ( n > 0 ) || ( !n && keystr ) )
495         {
496             if ( ( size_t )n >= sizeof( buf ) )
497                 n = sizeof( buf ) - 1;
498 
499             if ( keystr && keystr[ 0 ] )
500             {
501                 int keystrlen = strlen( keystr );
502 
503                 if ( ( size_t )n + keystrlen >= sizeof( buf ) )
504                     n = sizeof( buf ) - keystrlen - 1;
505 
506                 strcpy( buf + n, keystr );
507 
508                 n += keystrlen;
509             }
510 
511             ret = write( g_trace_fd, buf, n );
512         }
513     }
514 
515     return ret;
516 }
517 
gpuvis_trace_printf(const char * fmt,...)518 GPUVIS_EXTERN int gpuvis_trace_printf( const char *fmt, ... )
519 {
520     int ret;
521     va_list ap;
522 
523     va_start( ap, fmt );
524     ret = gpuvis_trace_vprintf( fmt, ap );
525     va_end( ap );
526 
527     return ret;
528 }
529 
gpuvis_trace_vprintf(const char * fmt,va_list ap)530 GPUVIS_EXTERN int gpuvis_trace_vprintf( const char *fmt, va_list ap )
531 {
532     return trace_printf_impl( NULL, fmt, ap );
533 }
534 
gpuvis_trace_duration_printf(float duration,const char * fmt,...)535 GPUVIS_EXTERN int gpuvis_trace_duration_printf( float duration, const char *fmt, ... )
536 {
537     int ret;
538     va_list ap;
539 
540     va_start( ap, fmt );
541     ret = gpuvis_trace_duration_vprintf( duration, fmt, ap );
542     va_end( ap );
543 
544     return ret;
545 }
546 
gpuvis_trace_duration_vprintf(float duration,const char * fmt,va_list ap)547 GPUVIS_EXTERN int gpuvis_trace_duration_vprintf( float duration, const char *fmt, va_list ap )
548 {
549     char keystr[ 128 ];
550 
551     snprintf( keystr, sizeof( keystr ), " (duration=%f)", duration ); //$ TODO: Try this with more precision?
552 
553     return trace_printf_impl( keystr, fmt, ap );
554 }
555 
gpuvis_trace_begin_ctx_printf(unsigned int ctx,const char * fmt,...)556 GPUVIS_EXTERN int gpuvis_trace_begin_ctx_printf( unsigned int ctx, const char *fmt, ... )
557 {
558     int ret;
559     va_list ap;
560 
561     va_start( ap, fmt );
562     ret = gpuvis_trace_begin_ctx_vprintf( ctx, fmt, ap );
563     va_end( ap );
564 
565     return ret;
566 }
567 
gpuvis_trace_begin_ctx_vprintf(unsigned int ctx,const char * fmt,va_list ap)568 GPUVIS_EXTERN int gpuvis_trace_begin_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap )
569 {
570     char keystr[ 128 ];
571 
572     snprintf( keystr, sizeof( keystr ), " (begin_ctx=%u)", ctx );
573 
574     return trace_printf_impl( keystr, fmt, ap );
575 }
576 
gpuvis_trace_end_ctx_printf(unsigned int ctx,const char * fmt,...)577 GPUVIS_EXTERN int gpuvis_trace_end_ctx_printf( unsigned int ctx, const char *fmt, ... )
578 {
579     int ret;
580     va_list ap;
581 
582     va_start( ap, fmt );
583     ret = gpuvis_trace_end_ctx_vprintf( ctx, fmt, ap );
584     va_end( ap );
585 
586     return ret;
587 }
588 
gpuvis_trace_end_ctx_vprintf(unsigned int ctx,const char * fmt,va_list ap)589 GPUVIS_EXTERN int gpuvis_trace_end_ctx_vprintf( unsigned int ctx, const char *fmt, va_list ap )
590 {
591     char keystr[ 128 ];
592 
593     snprintf( keystr, sizeof( keystr ), " (end_ctx=%u)", ctx );
594 
595     return trace_printf_impl( keystr, fmt, ap );
596 }
597 
gpuvis_start_tracing(unsigned int kbuffersize)598 GPUVIS_EXTERN int gpuvis_start_tracing( unsigned int kbuffersize )
599 {
600     static const char fmt[] =
601         "trace-cmd start -b %u -D -i "
602         // https://github.com/mikesart/gpuvis/wiki/TechDocs-Linux-Scheduler
603         " -e sched:sched_switch"
604         " -e sched:sched_process_fork"
605         " -e sched:sched_process_exec"
606         " -e sched:sched_process_exit"
607         " -e drm:drm_vblank_event"
608         " -e drm:drm_vblank_event_queued"
609         " -e drm:drm_vblank_event_delivered"
610         // https://github.com/mikesart/gpuvis/wiki/TechDocs-AMDGpu
611         " -e amdgpu:amdgpu_vm_flush"
612         " -e amdgpu:amdgpu_cs_ioctl"
613         " -e amdgpu:amdgpu_sched_run_job"
614         " -e *fence:*fence_signaled"
615         // https://github.com/mikesart/gpuvis/wiki/TechDocs-Intel
616         " -e i915:i915_flip_request"
617         " -e i915:i915_flip_complete"
618         " -e i915:intel_gpu_freq_change"
619         " -e i915:i915_gem_request_add"
620         " -e i915:i915_gem_request_submit"  // Require CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS
621         " -e i915:i915_gem_request_in"      // Kconfig option to be enabled.
622         " -e i915:i915_gem_request_out"     //
623         " -e i915:intel_engine_notify"
624         " -e i915:i915_gem_request_wait_begin"
625         " -e i915:i915_gem_request_wait_end 2>&1";
626     char cmd[ 8192 ];
627 
628     if ( !kbuffersize )
629         kbuffersize = 16 * 1024;
630 
631     snprintf( cmd, sizeof( cmd ), fmt, kbuffersize );
632 
633     return exec_tracecmd( cmd );
634 }
635 
gpuvis_trigger_capture_and_keep_tracing(char * filename,size_t size)636 GPUVIS_EXTERN int gpuvis_trigger_capture_and_keep_tracing( char *filename, size_t size )
637 {
638     int ret = -1;
639 
640     if ( filename )
641         filename[ 0 ] = 0;
642 
643     flush_hot_func_calls();
644 
645     if ( gpuvis_tracing_on() )
646     {
647         char datetime[ 128 ];
648         char cmd[ PATH_MAX ];
649         char exebuf[ PATH_MAX ];
650         const char *exename = NULL;
651         time_t t = time( NULL );
652         struct tm *tmp = localtime( &t );
653 
654         strftime( datetime, sizeof( datetime ), "%Y-%m-%d_%H-%M-%S", tmp );
655         datetime[ sizeof( datetime ) - 1 ] = 0;
656 
657         ssize_t cbytes = readlink( "/proc/self/exe", exebuf, sizeof( exebuf ) - 1 );
658         if ( cbytes > 0 )
659         {
660             exebuf[ cbytes ] = 0;
661             exename = strrchr( exebuf, '/' );
662         }
663         exename = exename ? ( exename + 1 ) : "trace";
664 
665         // Stop tracing
666         exec_tracecmd( "trace-cmd stop 2>&1" );
667 
668         // Save the trace data to something like "glxgears_2017-10-13_17-52-56.dat"
669         snprintf( cmd, sizeof( cmd ),
670                   "trace-cmd extract -k -o \"%s_%s.dat\" > /tmp/blah.log 2>&1 &",
671                   exename, datetime );
672         cmd[ sizeof( cmd ) - 1 ] = 0;
673 
674         ret = system( cmd );
675 
676         if ( filename && !ret )
677             snprintf( filename, size, "%s_%s.dat", exename, datetime );
678 
679         // Restart tracing
680         exec_tracecmd( "trace-cmd restart 2>&1" );
681     }
682 
683     return ret;
684 }
685 
gpuvis_stop_tracing()686 GPUVIS_EXTERN int gpuvis_stop_tracing()
687 {
688     flush_hot_func_calls();
689 
690     int ret = exec_tracecmd( "trace-cmd reset 2>&1");
691 
692     // Try freeing any snapshot buffers as well
693     exec_tracecmd( "trace-cmd snapshot -f 2>&1" );
694 
695     return ret;
696 }
697 
gpuvis_tracing_on()698 GPUVIS_EXTERN int gpuvis_tracing_on()
699 {
700     int ret = -1;
701     char buf[ 32 ];
702     char filename[ PATH_MAX ];
703 
704     if ( gpuvis_get_tracefs_filename( filename, PATH_MAX, "tracing_on" ) )
705     {
706         int fd = open( filename, O_RDONLY );
707 
708         if ( fd >= 0 )
709         {
710             if ( read( fd, buf, sizeof( buf ) ) > 0 )
711                 ret = atoi( buf );
712 
713             close( fd );
714         }
715     }
716 
717     return ret;
718 }
719 
is_tracefs_dir(const char * dir)720 static int is_tracefs_dir( const char *dir )
721 {
722     struct statfs stat;
723 
724     return !statfs( dir, &stat ) && ( stat.f_type == TRACEFS_MAGIC );
725 }
726 
gpuvis_get_tracefs_dir()727 GPUVIS_EXTERN const char *gpuvis_get_tracefs_dir()
728 {
729     if ( !g_tracefs_dir_inited )
730     {
731         size_t i;
732         static const char *tracefs_dirs[] =
733         {
734             "/sys/kernel/tracing",
735             "/sys/kernel/debug/tracing",
736             "/tracing",
737             "/trace",
738         };
739 
740         for ( i = 0; i < sizeof( tracefs_dirs ) / sizeof( tracefs_dirs[ 0 ] ); i++ )
741         {
742             if ( is_tracefs_dir( tracefs_dirs[ i ] ) )
743             {
744                 strncpy( g_tracefs_dir, tracefs_dirs[ i ], PATH_MAX );
745                 g_tracefs_dir[ PATH_MAX - 1 ] = 0;
746                 break;
747             }
748         }
749 
750         if ( !g_tracefs_dir[ 0 ] )
751         {
752             FILE *fp;
753             char type[ 128 ];
754             char dir[ PATH_MAX + 1 ];
755 
756             fp = fopen( "/proc/mounts", "r" );
757             if ( fp )
758             {
759                 while ( fscanf( fp, "%*s %" GPUVIS_STR_VALUE( PATH_MAX ) "s %127s %*s %*d %*d\n", dir, type ) == 2 )
760                 {
761                     if ( !strcmp( type, "tracefs" ) && is_tracefs_dir( dir ) )
762                     {
763                         strncpy( g_tracefs_dir, dir, PATH_MAX );
764                         g_tracefs_dir[ PATH_MAX - 1 ] = 0;
765                         break;
766                     }
767                 }
768 
769                 fclose( fp );
770             }
771         }
772 
773         g_tracefs_dir_inited = 1;
774     }
775 
776     return g_tracefs_dir;
777 }
778 
gpuvis_get_tracefs_filename(char * buf,size_t buflen,const char * file)779 GPUVIS_EXTERN const char *gpuvis_get_tracefs_filename( char *buf, size_t buflen, const char *file )
780 {
781     const char *tracefs_dir = gpuvis_get_tracefs_dir();
782 
783     if ( tracefs_dir[ 0 ] )
784     {
785         snprintf( buf, buflen, "%s/%s", tracefs_dir, file );
786         buf[ buflen - 1 ] = 0;
787 
788         return buf;
789     }
790 
791     return NULL;
792 }
793 
794 #endif // GPUVIS_TRACE_IMPLEMENTATION
795 
796 #endif // _GPUVIS_TRACE_UTILS_H_
797