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