LCOV - code coverage report
Current view: top level - app/shared_dev/commands - flame.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 189 0.0 %
Date: 2026-08-13 04:56:22 Functions: 0 8 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "../../shared/fd_config.h"
       3             : #include "../../shared/fd_bootinfo.h"
       4             : #include "../../shared/fd_action.h"
       5             : #include "../../platform/fd_sys_util.h"
       6             : #include "../../../disco/metrics/fd_metrics.h"
       7             : 
       8             : #include <errno.h>
       9             : #include <stdio.h>
      10             : #include <stdlib.h>
      11             : #include <unistd.h>
      12             : #include <sys/mman.h>
      13             : #include <sys/prctl.h>
      14             : #include <sys/wait.h>
      15             : #include <linux/capability.h>
      16             : 
      17             : FD_IMPORT_BINARY( fd_flamegraph_template, "src/app/shared_dev/commands/flamegraph_template.html" );
      18             : FD_IMPORT_BINARY( fd_flamegraph_script,   "src/app/shared_dev/commands/flamegraph.py" );
      19             : 
      20             : static int
      21             : memfd_from( char const *  name,
      22             :             uchar const * data,
      23           0 :             ulong         data_sz ) {
      24           0 :   int fd = memfd_create( name, 0U );
      25           0 :   if( FD_UNLIKELY( -1==fd ) ) FD_LOG_ERR(( "memfd_create() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      26           0 :   if( FD_LIKELY( data_sz ) ) {
      27           0 :     int err = fd_io_write( fd, data, data_sz, data_sz, &(ulong){0} );
      28           0 :     if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "write() failed (%i-%s)", err, fd_io_strerror( err ) ));
      29           0 :   }
      30           0 :   return fd;
      31           0 : }
      32             : 
      33             : static void
      34           0 : dump_child_output( int fd ) {
      35           0 :   if( FD_UNLIKELY( -1==lseek( fd, 0L, SEEK_SET ) ) ) return;
      36           0 :   char buf[ 4096 ];
      37           0 :   long sz;
      38           0 :   while( (sz = read( fd, buf, sizeof(buf) ))>0L ) {
      39           0 :     ulong written = 0UL;
      40           0 :     while( written<(ulong)sz ) {
      41           0 :       long w = write( STDERR_FILENO, buf+written, (ulong)sz-written );
      42           0 :       if( FD_UNLIKELY( w<=0L ) ) return;
      43           0 :       written += (ulong)w;
      44           0 :     }
      45           0 :   }
      46           0 : }
      47             : 
      48             : static int record_pid;
      49             : 
      50             : static void
      51           0 : parent_signal( int sig ) {
      52           0 :   FD_LOG_NOTICE(( "Received signal %s%s%s %s(%s)%s", fd_log_style_bold(), fd_io_strsignal_name( sig ), fd_log_style_normal(), fd_log_style_dim(), fd_io_strsignal_desc( sig ), fd_log_style_normal() ));
      53           0 :   if( FD_LIKELY( record_pid ) ) {
      54           0 :     if( FD_UNLIKELY( -1==kill( record_pid, SIGINT ) ) ) FD_LOG_ERR(( "kill() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      55           0 :   }
      56           0 : }
      57             : 
      58             : static void
      59           0 : install_parent_signals( void ) {
      60           0 :   struct sigaction sa = {
      61           0 :     .sa_handler = parent_signal,
      62           0 :     .sa_flags   = 0,
      63           0 :   };
      64           0 :   if( FD_UNLIKELY( sigaction( SIGTERM, &sa, NULL ) ) )
      65           0 :     FD_LOG_ERR(( "sigaction(SIGTERM) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      66           0 :   if( FD_UNLIKELY( sigaction( SIGINT, &sa, NULL ) ) )
      67           0 :     FD_LOG_ERR(( "sigaction(SIGINT) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      68           0 : }
      69             : 
      70             : void
      71             : flame_cmd_perm( args_t *         args   FD_PARAM_UNUSED,
      72             :                 fd_cap_chk_t *   chk,
      73           0 :                 config_t const * config FD_PARAM_UNUSED ) {
      74           0 :   fd_cap_chk_root( chk, "flame", "read system performance counters with `/usr/bin/perf`" );
      75           0 : }
      76             : 
      77             : void
      78             : flame_cmd_args( int *    pargc,
      79             :                 char *** pargv,
      80           0 :                 args_t * args ) {
      81           0 :   args->flame.freq = fd_env_strip_cmdline_uint( pargc, pargv, "--freq", NULL, 997U );
      82           0 :   if( FD_UNLIKELY( !args->flame.freq ) ) FD_LOG_ERR(( "--freq must be non-zero" ));
      83             : 
      84           0 :   if( FD_UNLIKELY( !*pargc ) ) {
      85           0 :     strncpy( args->flame.name, "all", sizeof( args->flame.name ) - 1 );
      86           0 :     return;
      87           0 :   }
      88           0 :   strncpy( args->flame.name, **pargv, sizeof( args->flame.name ) - 1 );
      89             : 
      90           0 :   (*pargc)--;
      91           0 :   (*pargv)++;
      92           0 : }
      93             : 
      94             : void
      95             : flame_cmd_fn( args_t *   args,
      96           0 :               config_t * config ) {
      97           0 :   install_parent_signals();
      98             : 
      99           0 :   fd_bootinfo_adopt( config );
     100           0 :   fd_bootinfo_check_layout( config );
     101           0 :   fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_ONLY, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
     102           0 :   fd_topo_fill( &config->topo );
     103             : 
     104           0 :   ulong tile_cnt = 0UL;
     105           0 :   ulong tile_idxs[ 128UL ];
     106             : 
     107           0 :   int whole_process = 0;
     108           0 :   if( FD_UNLIKELY( !strcmp( "all", args->flame.name ) ) ) {
     109           0 :     FD_TEST( config->topo.tile_cnt<sizeof(tile_idxs)/sizeof(tile_idxs[0]) );
     110           0 :     for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     111           0 :       tile_idxs[ tile_cnt ] = i;
     112           0 :       tile_cnt++;
     113           0 :     }
     114           0 :   } else if( FD_UNLIKELY( !strcmp( "agave", args->flame.name ) ) ) {
     115             :     /* Find the bank tile so we can get the Agave PID */
     116           0 :     ulong bank_tile_idx = fd_topo_find_tile( &config->topo, "bank", 0UL );
     117           0 :     if( FD_UNLIKELY( bank_tile_idx==ULONG_MAX ) ) FD_LOG_ERR(( "tile `bank` not found" ));
     118           0 :     whole_process = 1;
     119           0 :     tile_idxs[ 0 ] = bank_tile_idx;
     120           0 :     tile_cnt = 1UL;
     121           0 :   } else {
     122           0 :     char * sep = strchr( args->flame.name, ':' );
     123             : 
     124           0 :     ulong tile_idx;
     125           0 :     if( FD_UNLIKELY( !sep ) ) {
     126           0 :       tile_idx = fd_topo_find_tile( &config->topo, args->flame.name, 0UL );
     127           0 :     } else {
     128           0 :       char * endptr;
     129           0 :       *sep = '\0';
     130           0 :       ulong kind_id = strtoul( sep+1, &endptr, 10 );
     131           0 :       if( FD_UNLIKELY( *endptr!='\0' || kind_id==ULONG_MAX ) ) FD_LOG_ERR(( "invalid tile kind id provided `%s`", sep+1 ));
     132           0 :       tile_idx = fd_topo_find_tile( &config->topo, args->flame.name, kind_id );
     133           0 :     }
     134             : 
     135           0 :     if( FD_UNLIKELY( tile_idx==ULONG_MAX ) ) FD_LOG_ERR(( "tile %s%s%s not found", fd_log_style_bold(), args->flame.name, fd_log_style_normal() ));
     136           0 :     tile_idxs[ 0 ] = tile_idx;
     137           0 :     tile_cnt = 1UL;
     138           0 :   }
     139             : 
     140           0 :   char threads[ 4096 ] = {0};
     141           0 :   ulong len = 0UL;
     142           0 :   for( ulong i=0UL; i<tile_cnt; i++ ) {
     143           0 :     ulong tid = fd_metrics_tile( config->topo.tiles[ tile_idxs[ i ] ].metrics )[ FD_METRICS_GAUGE_TILE_TID_OFF ];
     144           0 :     ulong pid = fd_metrics_tile( config->topo.tiles[ tile_idxs[ i ] ].metrics )[ FD_METRICS_GAUGE_TILE_PID_OFF ];
     145             : 
     146           0 :     FD_TEST( pid<=INT_MAX );
     147           0 :     if( FD_UNLIKELY( -1==kill( (int)tid, 0 ) ) ) {
     148           0 :       if( FD_LIKELY( config->topo.tiles[ tile_idxs[ i ] ].allow_shutdown ) ) continue;
     149             : 
     150           0 :       if( FD_UNLIKELY( errno==ESRCH ) ) FD_LOG_ERR(( "tile %s%s:%lu%s is not running", fd_log_style_bold(), config->topo.tiles[ tile_idxs[ i ] ].name, config->topo.tiles[ tile_idxs[ i ] ].kind_id, fd_log_style_normal() ));
     151           0 :       else                              FD_LOG_ERR(( "kill() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     152           0 :     }
     153             : 
     154           0 :     ulong arg_len;
     155           0 :     FD_TEST( fd_cstr_printf_check( threads+len, sizeof(threads)-len, &arg_len, "%lu", fd_ulong_if( whole_process, pid, tid ) ) );
     156           0 :     len += arg_len;
     157             : 
     158           0 :     if( FD_LIKELY( i!=tile_cnt-1UL ) ) {
     159           0 :       FD_TEST( fd_cstr_printf_check( threads+len, sizeof(threads)-len, NULL, "," ) );
     160           0 :       len += 1UL;
     161           0 :     }
     162           0 :   }
     163           0 :   FD_TEST( len<sizeof(threads) );
     164             : 
     165           0 :   char freq[ 16UL ];
     166           0 :   FD_TEST( fd_cstr_printf_check( freq, sizeof(freq), NULL, "%u", args->flame.freq ) );
     167             : 
     168           0 :   FD_LOG_NOTICE(( "%s/usr/bin/perf record -e task-clock --off-cpu -g -F %s -%c %s && /usr/bin/perf script report flamegraph%s", fd_log_style_bold(), freq, fd_char_if( whole_process, 'p', 't' ), threads, fd_log_style_normal() ));
     169             : 
     170           0 :   int record_out = memfd_from( "perf_record_output", NULL, 0UL );
     171             : 
     172           0 :   record_pid = fork();
     173           0 :   if( FD_UNLIKELY( -1==record_pid ) ) FD_LOG_ERR(( "fork() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     174           0 :   if( FD_LIKELY( !record_pid ) ) {
     175             :     /* Silence perf unless it fails */
     176           0 :     if( FD_UNLIKELY( -1==dup2( record_out, STDOUT_FILENO ) || -1==dup2( record_out, STDERR_FILENO ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     177           0 :     char * perf_args[ 13 ] = {
     178           0 :       "/usr/bin/perf",
     179           0 :       "record",
     180           0 :       "-e",
     181           0 :       "task-clock",
     182           0 :       "--off-cpu",
     183           0 :       "-g",
     184           0 :       "-F",
     185           0 :       freq,
     186           0 :       whole_process ? "-p" : "-t",
     187           0 :       threads,
     188           0 :       NULL,
     189           0 :     };
     190           0 :     if( FD_UNLIKELY( -1==execve( "/usr/bin/perf", (char * const *)perf_args, NULL ) ) ) FD_LOG_ERR(( "execve() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     191           0 :   }
     192             : 
     193           0 :   FD_LOG_NOTICE(( "perf collection running, %sCtrl+C%s to stop", fd_log_style_bold(), fd_log_style_normal() ));
     194             : 
     195           0 :   for(;;) {
     196           0 :     int wstatus;
     197           0 :     int exited_pid = waitpid( -1, &wstatus, 0 );
     198           0 :     if( FD_UNLIKELY( -1==exited_pid ) ) {
     199           0 :       if( FD_LIKELY( errno==EAGAIN || errno==EINTR ) ) continue;
     200           0 :       FD_LOG_ERR(( "waitpid() failed (%d-%s)", errno, fd_io_strerror( errno ) ));
     201           0 :     }
     202             : 
     203           0 :     int graceful_exit = !WIFEXITED( wstatus ) && WTERMSIG( wstatus )==SIGINT;
     204           0 :     if( FD_UNLIKELY( !graceful_exit ) ) {
     205           0 :       dump_child_output( record_out );
     206           0 :       if( FD_UNLIKELY( !WIFEXITED( wstatus ) ) ) FD_LOG_ERR(( "%sperf record%s exited unexpectedly with signal %s%s%s %s(%s)%s", fd_log_style_bold(), fd_log_style_normal(), fd_log_style_bold(), fd_io_strsignal_name( WTERMSIG( wstatus ) ), fd_log_style_normal(), fd_log_style_dim(), fd_io_strsignal_desc( WTERMSIG( wstatus ) ), fd_log_style_normal() ));
     207           0 :       if( FD_UNLIKELY( WEXITSTATUS( wstatus ) ) ) FD_LOG_ERR(( "%sperf record%s exited unexpectedly with code %d", fd_log_style_bold(), fd_log_style_normal(), WEXITSTATUS( wstatus ) ));
     208           0 :     }
     209           0 :     break;
     210           0 :   }
     211             : 
     212           0 :   int template_fd = memfd_from( "flamegraph_template", fd_flamegraph_template, fd_flamegraph_template_sz );
     213           0 :   int script_fd   = memfd_from( "flamegraph_script",   fd_flamegraph_script,   fd_flamegraph_script_sz   );
     214             : 
     215           0 :   char template_env[ PATH_MAX ];
     216           0 :   FD_TEST( fd_cstr_printf_check( template_env, sizeof(template_env), NULL, "FD_FLAMEGRAPH_TEMPLATE=/dev/fd/%d", template_fd ) );
     217           0 :   char script_path[ PATH_MAX ];
     218           0 :   FD_TEST( fd_cstr_printf_check( script_path, sizeof(script_path), NULL, "py:/dev/fd/%d", script_fd ) );
     219             : 
     220           0 :   int report_out = memfd_from( "perf_report_output", NULL, 0UL );
     221             : 
     222           0 :   int report_pid = fork();
     223           0 :   if( FD_UNLIKELY( -1==report_pid ) ) FD_LOG_ERR(( "fork() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     224           0 :   if( FD_LIKELY( !report_pid ) ) {
     225           0 :     if( FD_UNLIKELY( -1==dup2( report_out, STDOUT_FILENO ) || -1==dup2( report_out, STDERR_FILENO ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     226             :     /* Sandboxed tiles pivot_root into an empty mount namespace.  perf
     227             :        with CAP_SYS_ADMIN setns()es into it to resolve symbols and finds
     228             :        nothing, so all stacks show as [unknown].  Drop the capability so
     229             :        perf falls back to the host namespace paths. */
     230           0 :     if( FD_UNLIKELY( -1==prctl( PR_CAPBSET_DROP, CAP_SYS_ADMIN, 0, 0, 0 ) ) ) FD_LOG_ERR(( "prctl(PR_CAPBSET_DROP) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     231           0 :     char * args[ 5 ] = {
     232           0 :       "/usr/bin/perf",
     233           0 :       "script",
     234           0 :       "-s",
     235           0 :       script_path,
     236           0 :       NULL,
     237           0 :     };
     238           0 :     char * envp[ 2 ] = { template_env, NULL };
     239           0 :     if( FD_UNLIKELY( -1==execve( "/usr/bin/perf", (char * const *)args, envp ) ) ) FD_LOG_ERR(( "execve() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     240           0 :   }
     241             : 
     242           0 :   for(;;) {
     243           0 :     int wstatus;
     244           0 :     int exited_pid = waitpid( -1, &wstatus, 0 );
     245           0 :     if( FD_UNLIKELY( -1==exited_pid ) ) {
     246           0 :       if( FD_LIKELY( errno==EAGAIN || errno==EINTR ) ) continue;
     247           0 :       FD_LOG_ERR(( "waitpid() failed (%d-%s)", errno, fd_io_strerror( errno ) ));
     248           0 :     }
     249             : 
     250           0 :     if( FD_UNLIKELY( !WIFEXITED( wstatus ) || WEXITSTATUS( wstatus ) ) ) dump_child_output( report_out );
     251           0 :     if( FD_UNLIKELY( !WIFEXITED( wstatus ) ) ) FD_LOG_ERR(( "%sperf report%s exited unexpectedly with signal %s%s%s %s(%s)%s", fd_log_style_bold(), fd_log_style_normal(), fd_log_style_bold(), fd_io_strsignal_name( WTERMSIG( wstatus ) ), fd_log_style_normal(), fd_log_style_dim(), fd_io_strsignal_desc( WTERMSIG( wstatus ) ), fd_log_style_normal() ));
     252           0 :     if( FD_UNLIKELY( WEXITSTATUS( wstatus ) ) ) FD_LOG_ERR(( "%sperf report%s exited unexpectedly with code %d", fd_log_style_bold(), fd_log_style_normal(), WEXITSTATUS( wstatus ) ));
     253           0 :     break;
     254           0 :   }
     255             : 
     256           0 :   char cwd[ PATH_MAX ];
     257           0 :   if( FD_LIKELY( getcwd( cwd, sizeof(cwd) ) ) ) FD_LOG_NOTICE(( "flamegraph written to %s%s/flamegraph.html%s", fd_log_style_bold(), cwd, fd_log_style_normal() ));
     258             : 
     259           0 :   fd_sys_util_exit_group( 0 );
     260           0 : }
     261             : 
     262             : static void
     263           0 : flame_args_help( fd_action_help_t * help ) {
     264           0 :   fd_action_help_arg( help, "--freq", "<hz>", "Sampling frequency in Hz (default 997)" );
     265           0 : }
     266             : 
     267             : action_t fd_action_flame = {
     268             :   .name          = "flame",
     269             :   .args          = flame_cmd_args,
     270             :   .fn            = flame_cmd_fn,
     271             :   .perm          = flame_cmd_perm,
     272             :   .description   = "Capture a perf flamegraph",
     273             :   .usage         = "flame [all|tile|tile:idx|agave] [--freq <hz>]",
     274             :   .args_help     = flame_args_help,
     275             :   .is_diagnostic = 1
     276             : };

Generated by: LCOV version 1.14