@@ -76,9 +76,12 @@ def main():
7676 parser = argparse .ArgumentParser (description = "Run QEMU via west and automatically decode crashes." )
7777 parser .add_argument ("--build-dir" , default = "build" , help = "Path to the build directory containing zephyr.elf, linker.cmd, etc. Defaults to 'build'." )
7878 parser .add_argument ("--log-file" , default = "qemu-run.log" , help = "Path to save the QEMU output log. Defaults to 'qemu-run.log'." )
79+ parser .add_argument ("--valgrind" , action = "store_true" , help = "Run the executable under Valgrind (only valid for native_sim)." )
7980 args = parser .parse_args ()
8081
8182 # Make absolute path just in case
83+ # The shell script cd's into `args.build_dir` before executing us, so `args.build_dir` might be relative to the shell script's pwd.
84+ # We resolve it relative to the python script's original invocation cwd.
8285 build_dir = os .path .abspath (args .build_dir )
8386
8487 print (f"Starting QEMU test runner. Monitoring for crashes (Build Dir: { args .build_dir } )..." )
@@ -91,7 +94,53 @@ def main():
9194 print ("Please ensure you have sourced the Zephyr environment (e.g., source zephyr-env.sh)." )
9295 sys .exit (1 )
9396
94- child = pexpect .spawn (west_path , ["-v" , "build" , "-t" , "run" ], encoding = 'utf-8' )
97+ # Detect the board configuration from CMakeCache.txt
98+ is_native_sim = False
99+
100+ cmake_cache = os .path .join (build_dir , "CMakeCache.txt" )
101+
102+ if os .path .isfile (cmake_cache ):
103+ with open (cmake_cache , "r" ) as f :
104+ for line in f :
105+ if line .startswith ("CACHED_BOARD:STRING=" ) or line .startswith ("BOARD:STRING=" ):
106+ if "native_sim" in line .split ("=" , 1 )[1 ].strip ():
107+ is_native_sim = True
108+ break
109+
110+ # Determine execution command
111+ # If the user is running the python script directly from outside the workspace, we need to provide the source directory.
112+ # But if west finds it automatically (or we are in the build dir), providing `-s` might clear the CACHED_BOARD config.
113+ run_cmd = [west_path , "-v" , "build" , "-d" , build_dir ]
114+
115+ # Check if we are physically sitting inside the build directory
116+ if os .path .abspath ("." ) != os .path .abspath (build_dir ):
117+ # We need to explicitly supply the app source to prevent west from crashing
118+ app_source_dir = os .path .abspath (os .path .join (os .path .dirname (__file__ ), ".." , "app" ))
119+ run_cmd .extend (["-s" , app_source_dir ])
120+
121+ run_cmd .extend (["-t" , "run" ])
122+
123+ if args .valgrind :
124+ if not is_native_sim :
125+ print ("[sof-qemu-run] Error: --valgrind is only supported for the native_sim board." )
126+ sys .exit (1 )
127+
128+ print ("[sof-qemu-run] Rebuilding before valgrind..." )
129+ subprocess .run ([west_path , "build" , "-d" , build_dir ], check = True )
130+
131+ valgrind_path = shutil .which ("valgrind" )
132+ if not valgrind_path :
133+ print ("[sof-qemu-run] Error: 'valgrind' command not found in PATH." )
134+ sys .exit (1 )
135+
136+ exe_path = os .path .join (build_dir , "zephyr" , "zephyr.exe" )
137+ if not os .path .isfile (exe_path ):
138+ print (f"[sof-qemu-run] Error: Executable not found at { exe_path } " )
139+ sys .exit (1 )
140+
141+ run_cmd = [valgrind_path , exe_path ]
142+
143+ child = pexpect .spawn (run_cmd [0 ], run_cmd [1 :], encoding = 'utf-8' )
95144
96145 # We will accumulate output to check for crashes
97146 full_output = ""
@@ -157,36 +206,39 @@ def main():
157206
158207 run_sof_crash_decode (build_dir , full_output )
159208 else :
160- print ("\n [sof-qemu-run] No crash detected. Interacting with QEMU Monitor to grab registers..." )
161-
162- # We need to send Ctrl-A c to enter the monitor
163- if child .isalive ():
164- child .send ("\x01 c" ) # Ctrl-A c
165- try :
166- # Wait for (qemu) prompt
167- child .expect (r"\(qemu\)" , timeout = 5 )
168- # Send "info registers"
169- child .sendline ("info registers" )
170- # Wait for the next prompt
171- child .expect (r"\(qemu\)" , timeout = 5 )
172-
173- info_regs_output = child .before
174- print ("\n [sof-qemu-run] Successfully extracted registers from QEMU monitor.\n " )
175-
176- # Quit qemu safely
177- child .sendline ("quit" )
178- child .expect (pexpect .EOF , timeout = 2 )
179- child .close ()
180-
181- # Run the decoder on the intercepted register output
182- run_sof_crash_decode (build_dir , info_regs_output )
183- except pexpect .TIMEOUT :
184- print ("\n [sof-qemu-run] Timed out waiting for QEMU monitor. Is it running?" )
185- child .close (force = True )
186- except pexpect .EOF :
187- print ("\n [sof-qemu-run] QEMU terminated before we could run monitor commands." )
209+ if is_native_sim :
210+ print ("\n [sof-qemu-run] No crash detected. (Skipping QEMU monitor interaction for native_sim)" )
188211 else :
189- print ("\n [sof-qemu-run] Process is no longer alive, cannot extract registers." )
212+ print ("\n [sof-qemu-run] No crash detected. Interacting with QEMU Monitor to grab registers..." )
213+
214+ # We need to send Ctrl-A c to enter the monitor
215+ if child .isalive ():
216+ child .send ("\x01 c" ) # Ctrl-A c
217+ try :
218+ # Wait for (qemu) prompt
219+ child .expect (r"\(qemu\)" , timeout = 5 )
220+ # Send "info registers"
221+ child .sendline ("info registers" )
222+ # Wait for the next prompt
223+ child .expect (r"\(qemu\)" , timeout = 5 )
224+
225+ info_regs_output = child .before
226+ print ("\n [sof-qemu-run] Successfully extracted registers from QEMU monitor.\n " )
227+
228+ # Quit qemu safely
229+ child .sendline ("quit" )
230+ child .expect (pexpect .EOF , timeout = 2 )
231+ child .close ()
232+
233+ # Run the decoder on the intercepted register output
234+ run_sof_crash_decode (build_dir , info_regs_output )
235+ except pexpect .TIMEOUT :
236+ print ("\n [sof-qemu-run] Timed out waiting for QEMU monitor. Is it running?" )
237+ child .close (force = True )
238+ except pexpect .EOF :
239+ print ("\n [sof-qemu-run] QEMU terminated before we could run monitor commands." )
240+ else :
241+ print ("\n [sof-qemu-run] Process is no longer alive, cannot extract registers." )
190242
191243if __name__ == "__main__" :
192244 main ()
0 commit comments