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