@@ -162,6 +162,56 @@ def stop_profiling_arroyo_pids(
162162 logger .debug ("Stopped profiling for arroyo pids" )
163163
164164
165+ def start_profiling_query_engine_pids (qe_pids , experiment_output_dir ):
166+ qe_perf_procs = []
167+ qe_profiles_dir = os .path .join (experiment_output_dir , "query_engine_profiles" )
168+ os .makedirs (qe_profiles_dir , exist_ok = True )
169+
170+ for pid in qe_pids :
171+ output_file = os .path .join (qe_profiles_dir , f"perf_{ pid } .data" )
172+ cmd = [
173+ "perf" ,
174+ "record" ,
175+ "-g" ,
176+ "--call-graph" ,
177+ "dwarf" ,
178+ "-F" ,
179+ "997" ,
180+ "-o" ,
181+ output_file ,
182+ "--pid" ,
183+ str (pid ),
184+ ]
185+ logger .debug (f"Starting perf record for PID { pid } with command: { cmd } " )
186+ proc = subprocess .Popen (cmd )
187+ qe_perf_procs .append (proc )
188+
189+ logger .debug (
190+ f"Started perf record processes with PIDs: { [p .pid for p in qe_perf_procs ]} "
191+ )
192+ return qe_perf_procs
193+
194+
195+ def stop_profiling_query_engine_pids (qe_perf_procs , store : bool ):
196+ for proc in qe_perf_procs :
197+ try :
198+ os .kill (proc .pid , signal .SIGTERM )
199+ logger .debug (f"Stopped perf record process PID: { proc .pid } " )
200+ except ProcessLookupError :
201+ logger .debug (f"Perf record process PID { proc .pid } already terminated" )
202+ for proc in qe_perf_procs :
203+ try :
204+ proc .wait (timeout = 60 )
205+ logger .debug (
206+ f"Perf record process PID { proc .pid } exited with code { proc .returncode } "
207+ )
208+ except subprocess .TimeoutExpired :
209+ logger .debug (
210+ f"Perf record process PID { proc .pid } did not terminate within 60s"
211+ )
212+ logger .debug ("Stopped profiling for query engine pids" )
213+
214+
165215# TODO Provide some way of specifying which hooks will be used
166216def get_process_monitor_hooks (
167217 export_cost : bool , provider , node_offset : int
@@ -233,14 +283,23 @@ def main(args):
233283 logger .error ("No matching processes found." )
234284 return
235285
236- profile_query_engine_pid = None
286+ profile_query_engine_pid = (
287+ None # unused for Rust QE; kept for PrometheusClientService compat
288+ )
289+ qe_flamegraph_procs = None
237290 if args .profile_query_engine :
238- if (
239- constants .QUERY_ENGINE_RS_PROCESS_KEYWORD in args .keywords
240- or constants .QUERY_ENGINE_RS_CONTAINER_NAME in args .keywords
241- ):
242- raise NotImplementedError (
243- "Profiling for Rust query engine is not implemented yet"
291+ if constants .QUERY_ENGINE_RS_CONTAINER_NAME in args .keywords :
292+ raise ValueError (
293+ "Rust query engine profiling requires bare-metal mode. "
294+ "Set use_container.query_engine: false in config."
295+ )
296+ if constants .QUERY_ENGINE_RS_PROCESS_KEYWORD in args .keywords :
297+ qe_pids = get_pids (constants .QUERY_ENGINE_RS_PROCESS_KEYWORD )
298+ stop_profiling_query_engine_pids (
299+ [], store = False
300+ ) # clear any stale profilers
301+ qe_flamegraph_procs = start_profiling_query_engine_pids (
302+ qe_pids , args .experiment_output_dir
244303 )
245304
246305 logger .debug ("Starting process monitors" )
@@ -345,6 +404,10 @@ def main(args):
345404 arroyo_flamegraph_pids , args .experiment_output_dir , store = True
346405 )
347406
407+ if qe_flamegraph_procs :
408+ logger .debug ("Stopping profiling for query engine pids" )
409+ stop_profiling_query_engine_pids (qe_flamegraph_procs , store = True )
410+
348411 logger .debug ("Stopping process monitors" )
349412 monitor_info = process_monitor .stop_monitor (monitor , control_pipe , monitor_pipe )
350413
0 commit comments