Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fddev.h"
3 : #include "../../util/net/fd_pcap.h"
4 :
5 : #include <stdio.h>
6 : #include <unistd.h>
7 : #include <sys/wait.h>
8 :
9 : static int record_pid;
10 :
11 : static void
12 0 : parent_signal( int sig ) {
13 0 : FD_LOG_NOTICE(( "Received signal %s\n", fd_io_strsignal( sig ) ));
14 0 : if( FD_LIKELY( record_pid ) ) {
15 0 : if( FD_UNLIKELY( -1==kill( record_pid, SIGINT ) ) ) FD_LOG_ERR(( "kill() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
16 0 : }
17 0 : }
18 :
19 : static void
20 0 : install_parent_signals( void ) {
21 0 : struct sigaction sa = {
22 0 : .sa_handler = parent_signal,
23 0 : .sa_flags = 0,
24 0 : };
25 0 : if( FD_UNLIKELY( sigaction( SIGTERM, &sa, NULL ) ) )
26 0 : FD_LOG_ERR(( "sigaction(SIGTERM) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
27 0 : if( FD_UNLIKELY( sigaction( SIGINT, &sa, NULL ) ) )
28 0 : FD_LOG_ERR(( "sigaction(SIGINT) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
29 0 : }
30 :
31 : void
32 : flame_cmd_perm( args_t * args,
33 : fd_caps_ctx_t * caps,
34 0 : config_t * const config ) {
35 0 : (void)args;
36 0 : (void)config;
37 :
38 0 : fd_caps_check_root( caps, "flame", "read system performance counters with `/usr/bin/perf`" );
39 0 : }
40 :
41 : void
42 : flame_cmd_args( int * pargc,
43 : char *** pargv,
44 0 : args_t * args ) {
45 :
46 0 : if( FD_UNLIKELY( !*pargc ) ) FD_LOG_ERR(( "usage: flame [all|tile|tile:idx|agave]" ));
47 0 : strncpy( args->flame.name, **pargv, sizeof( args->flame.name ) - 1 );
48 :
49 0 : (*pargc)--;
50 0 : (*pargv)++;
51 0 : }
52 :
53 : void
54 : flame_cmd_fn( args_t * args,
55 0 : config_t * const config ) {
56 0 : install_parent_signals();
57 :
58 0 : fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_ONLY );
59 0 : fd_topo_fill( &config->topo );
60 :
61 0 : ulong tile_cnt = 0UL;
62 0 : ulong tile_idxs[ 128UL ];
63 :
64 0 : int whole_process = 0;
65 0 : if( FD_UNLIKELY( !strcmp( "all", args->flame.name ) ) ) {
66 0 : FD_TEST( config->topo.tile_cnt<sizeof(tile_idxs)/sizeof(tile_idxs[0]) );
67 0 : for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
68 0 : tile_idxs[ tile_cnt ] = i;
69 0 : tile_cnt++;
70 0 : }
71 0 : } else if( FD_UNLIKELY( !strcmp( "agave", args->flame.name ) ) ) {
72 : /* Find the bank tile so we can get the Agave PID */
73 0 : ulong bank_tile_idx = fd_topo_find_tile( &config->topo, "bank", 0UL );
74 0 : if( FD_UNLIKELY( bank_tile_idx==ULONG_MAX ) ) FD_LOG_ERR(( "tile `bank` not found" ));
75 0 : whole_process = 1;
76 0 : tile_idxs[ 0 ] = bank_tile_idx;
77 0 : tile_cnt = 1UL;
78 0 : } else {
79 0 : char * sep = strchr( args->flame.name, ':' );
80 :
81 0 : ulong tile_idx;
82 0 : if( FD_UNLIKELY( !sep ) ) {
83 0 : tile_idx = fd_topo_find_tile( &config->topo, args->flame.name, 0UL );
84 0 : } else {
85 0 : char * endptr;
86 0 : *sep = '\0';
87 0 : ulong kind_id = strtoul( sep+1, &endptr, 10 );
88 0 : if( FD_UNLIKELY( *endptr!='\0' || kind_id==ULONG_MAX ) ) FD_LOG_ERR(( "invalid tile kind id provided `%s`", sep+1 ));
89 0 : tile_idx = fd_topo_find_tile( &config->topo, args->flame.name, kind_id );
90 0 : }
91 :
92 0 : if( FD_UNLIKELY( tile_idx==ULONG_MAX ) ) FD_LOG_ERR(( "tile `%s` not found", args->flame.name ));
93 0 : tile_idxs[ 0 ] = tile_idx;
94 0 : tile_cnt = 1UL;
95 0 : }
96 :
97 0 : char threads[ 4096 ] = {0};
98 0 : ulong len = 0UL;
99 0 : for( ulong i=0UL; i<tile_cnt; i++ ) {
100 0 : if( FD_LIKELY( i!=0UL ) ) {
101 0 : FD_TEST( fd_cstr_printf_check( threads+len, sizeof(threads)-len, NULL, "," ) );
102 0 : len += 1UL;
103 0 : }
104 :
105 0 : ulong tid = fd_metrics_tile( config->topo.tiles[ tile_idxs[ i ] ].metrics )[ FD_METRICS_GAUGE_TILE_TID_OFF ];
106 0 : ulong pid = fd_metrics_tile( config->topo.tiles[ tile_idxs[ i ] ].metrics )[ FD_METRICS_GAUGE_TILE_PID_OFF ];
107 :
108 0 : FD_TEST( pid<=INT_MAX );
109 0 : if( FD_UNLIKELY( -1==kill( (int)pid, 0 ) ) ) {
110 0 : if( FD_UNLIKELY( errno==ESRCH ) ) FD_LOG_ERR(( "tile %s:%lu is not running", config->topo.tiles[ i ].name, config->topo.tiles[ i ].kind_id ));
111 0 : else FD_LOG_ERR(( "kill() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
112 0 : }
113 :
114 0 : ulong arg_len;
115 0 : FD_TEST( fd_cstr_printf_check( threads+len, sizeof(threads)-len, &arg_len, "%lu", fd_ulong_if( whole_process, pid, tid ) ) );
116 0 : len += arg_len;
117 0 : }
118 0 : FD_TEST( len<sizeof(threads) );
119 :
120 0 : FD_LOG_NOTICE(( "/usr/bin/perf script record flamegraph -o - -F 99 -%c %s | /usr/bin/perf script report flamegraph -i -", fd_char_if( whole_process, 'p', 't' ), threads ));
121 :
122 0 : int pipefd[ 2 ];
123 0 : if( FD_UNLIKELY( -1==pipe( pipefd ) ) ) FD_LOG_ERR(( "pipe() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
124 :
125 0 : record_pid = fork();
126 0 : if( FD_UNLIKELY( -1==record_pid ) ) FD_LOG_ERR(( "fork() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
127 0 : if( FD_LIKELY( !record_pid ) ) {
128 0 : if( FD_UNLIKELY( -1==close( pipefd[ 0 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
129 0 : if( FD_UNLIKELY( -1==dup2( pipefd[ 1 ], STDOUT_FILENO ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
130 0 : if( FD_UNLIKELY( -1==close( pipefd[ 1 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
131 :
132 0 : char * args[ 11 ] = {
133 0 : "/usr/bin/perf",
134 0 : "script",
135 0 : "record",
136 0 : "flamegraph",
137 0 : "-o",
138 0 : "-",
139 0 : "-F",
140 0 : "99",
141 0 : whole_process ? "-p" : "-t",
142 0 : threads,
143 0 : NULL,
144 0 : };
145 0 : if( FD_UNLIKELY( -1==execve( "/usr/bin/perf", (char * const *)args, NULL ) ) ) FD_LOG_ERR(( "execve() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
146 0 : }
147 :
148 0 : int report_pid = fork();
149 0 : if( FD_UNLIKELY( -1==report_pid ) ) FD_LOG_ERR(( "fork() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
150 0 : if( FD_LIKELY( !report_pid ) ) {
151 0 : if( FD_UNLIKELY( -1==close( pipefd[ 1 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
152 0 : if( FD_UNLIKELY( -1==dup2( pipefd[ 0 ], STDIN_FILENO ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
153 0 : if( FD_UNLIKELY( -1==close( pipefd[ 0 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
154 0 : if( FD_UNLIKELY( -1==setpgid( 0, 0 ) ) ) FD_LOG_ERR(( "setpgid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
155 :
156 0 : char * args[ 7 ] = {
157 0 : "/usr/bin/perf",
158 0 : "script",
159 0 : "report",
160 0 : "flamegraph",
161 0 : "-i",
162 0 : "-",
163 0 : NULL,
164 0 : };
165 0 : if( FD_UNLIKELY( -1==execve( "/usr/bin/perf", (char * const *)args, NULL ) ) ) FD_LOG_ERR(( "execve() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
166 0 : }
167 :
168 0 : if( FD_UNLIKELY( -1==close( pipefd[ 0 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
169 0 : if( FD_UNLIKELY( -1==close( pipefd[ 1 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
170 :
171 0 : FD_LOG_NOTICE(( "Perf collection running. Send SIGINT / Crl+C to stop." ));
172 :
173 0 : ulong exited_cnt = 0UL;
174 0 : while( FD_UNLIKELY( exited_cnt<2UL ) ) {
175 0 : int wstatus;
176 0 : int exited_pid = waitpid( -1, &wstatus, 0 );
177 0 : if( FD_UNLIKELY( -1==exited_pid ) ) {
178 0 : if( FD_LIKELY( errno==EAGAIN || errno==EINTR ) ) continue;
179 0 : FD_LOG_ERR(( "waitpid() failed (%d-%s)", errno, fd_io_strerror( errno ) ));
180 0 : }
181 :
182 0 : char const * process_name = exited_pid==record_pid ? "record" : "report";
183 :
184 0 : int record_graceful = exited_pid==record_pid && !WIFEXITED( wstatus ) && WTERMSIG( wstatus )==SIGINT;
185 :
186 0 : if( FD_UNLIKELY( !WIFEXITED( wstatus ) && !record_graceful ) ) FD_LOG_ERR(( "perf %s exited unexpectedly with signal %d (%s)", process_name, WTERMSIG( wstatus ), fd_io_strsignal( WTERMSIG( wstatus ) ) ));
187 0 : if( FD_UNLIKELY( WEXITSTATUS( wstatus ) && !record_graceful ) ) FD_LOG_ERR(( "perf %s exited unexpectedly with code %d", process_name, WEXITSTATUS( wstatus ) ));
188 :
189 0 : exited_cnt++;
190 0 : }
191 :
192 0 : exit_group( 0 );
193 0 : }
|