@@ -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,11 +206,14 @@ 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..." )
209+ if is_native_sim :
210+ print ("\n [sof-qemu-run] No crash detected. (Skipping QEMU monitor interaction for native_sim)" )
211+ else :
212+ print ("\n [sof-qemu-run] No crash detected. Interacting with QEMU Monitor to grab registers..." )
161213
162- # We need to send Ctrl-A c to enter the monitor
163- if child .isalive ():
164- child .send ("\x01 c" ) # Ctrl-A c
214+ # We need to send Ctrl-A c to enter the monitor
215+ if child .isalive ():
216+ child .send ("\x01 c" ) # Ctrl-A c
165217 try :
166218 # Wait for (qemu) prompt
167219 child .expect (r"\(qemu\)" , timeout = 5 )
@@ -185,8 +237,8 @@ def main():
185237 child .close (force = True )
186238 except pexpect .EOF :
187239 print ("\n [sof-qemu-run] QEMU terminated before we could run monitor commands." )
188- else :
189- print ("\n [sof-qemu-run] Process is no longer alive, cannot extract registers." )
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