@@ -113,6 +113,78 @@ def read_json_file(json_path: str) -> Dict[str, Any]:
113113 logger .error ("Failed to parse JSON file %s: %s" , json_path , e )
114114 raise
115115
116+ # -----------------------
117+ # Check for process crash and dump backtraces from core files
118+ # -----------------------
119+ def check_process_crash_and_backtrace (
120+ session ,
121+ process_name = "ganesha" ,
122+ cores_dir = "/tmp/cores" ,
123+ binary_path = "/usr/bin/ganesha.nfsd" ,
124+ gdb_cmd = None ,
125+ ):
126+ """
127+ Check if a process is running; if not, look for core dumps in cores_dir,
128+ install gdb if needed, and run gdb to get backtraces for each core file.
129+
130+ Args:
131+ session: Active RemoteSession instance (same as run_cmd).
132+ process_name (str): Process name to check via pgrep (e.g. "ganesha").
133+ cores_dir (str): Directory where core dumps are stored (e.g. /tmp/cores).
134+ binary_path (str): Full path to the binary for gdb (e.g. /usr/bin/ganesha.nfsd).
135+ gdb_cmd (str, optional): Custom gdb command format string. Must use
136+ {binary_path} and {core_path} placeholders. If None, uses the default:
137+ gdb -q -batch with debuginfod, pagination off, and "thread apply all bt full".
138+
139+ Returns:
140+ str: Combined gdb backtrace output for all core files, or None if process
141+ was running or no core files found. Caller can write this to a file.
142+ """
143+ try :
144+ logger .info ("Check if %s is running" , process_name )
145+ out , code = run_cmd (session , f"pgrep { process_name } " , check = False )
146+ logger .debug ("Output: %s, Code: %s" , out , code )
147+ if code != 0 :
148+ logger .error ("%s is not running" , process_name )
149+ logger .info ("Check for crash in %s" , cores_dir )
150+ out , code = run_cmd (session , f"ls -la { cores_dir } " , check = False )
151+ logger .debug ("Output: %s, Code: %s" , out , code )
152+ list_out , _ = run_cmd (session , f"ls { cores_dir } 2>/dev/null" , check = False )
153+ core_files = [
154+ line .strip ()
155+ for line in (list_out or "" ).splitlines ()
156+ if line .strip ()
157+ ]
158+ if core_files :
159+ logger .info ("Crashes found" )
160+ logger .info ("Install debug packages and see for crashes" )
161+ run_cmd (session , "dnf install -y gdb" )
162+ default_gdb_cmd = (
163+ "gdb -q -batch "
164+ "-ex \" set debuginfod enabled on\" "
165+ "-ex \" set pagination off\" "
166+ "-ex \" thread apply all bt full\" "
167+ "{binary_path} {core_path}"
168+ )
169+ backtraces = []
170+ for core_name in core_files :
171+ core_path = f"{ cores_dir } /{ core_name } "
172+ cmd = (gdb_cmd if gdb_cmd is not None else default_gdb_cmd ).format (
173+ binary_path = binary_path ,
174+ core_path = core_path ,
175+ )
176+ bt_out , _ = run_cmd (session , cmd , check = False )
177+ section = f"--- Backtrace for { core_path } ---\n { bt_out or '' } "
178+ backtraces .append (section )
179+ return "\n \n " .join (backtraces )
180+ else :
181+ logger .info ("No crashes found" )
182+ return None
183+ except Exception as e :
184+ logger .error ("Error checking for crashes: %s" , e )
185+ return None
186+
187+
116188# -----------------------
117189# Run remote commands
118190# -----------------------
0 commit comments