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