@@ -379,25 +379,23 @@ def state_name(self):
379379
380380 def get_saved_registers (self ):
381381 """
382- Restore registers from saved stack.
383- Stack layout (set by Stack::init):
384- [Stack._ptr + 0] = saved_rbp (or th pointer)
385- [Stack._ptr + 8] = return_addr (rip)
382+ Get saved registers from stack.
383+ Stack layout (saved by switch_context):
384+ [Stack._ptr + 0] = saved_rbp (push %rbp)
385+ [Stack._ptr + 8] = return_addr (call's push)
386+ [Stack._ptr + 16] = caller's stack frame
387+
388+ To enable GDB bt, rsp must skip switch_context's frame entirely.
389+ Returns: (rsp, rbp, rip) for caller's context
386390 """
387391 sp = self .stack_ptr ()
388392 if sp == 0 :
389393 return None , None , None
390394
391- if is_aarch64 ():
392- # AArch64 layout may differ, needs confirmation
393- rbp = read_ptr (sp )
394- rip = read_ptr (sp + 8 )
395- rsp = sp + 16
396- else :
397- # x86-64
398- rbp = read_ptr (sp )
399- rip = read_ptr (sp + 8 )
400- rsp = sp + 16
395+ # Read saved values
396+ rbp = read_ptr (sp ) # caller's rbp
397+ rip = read_ptr (sp + 8 ) # return address (in caller)
398+ rsp = sp + 16 # skip switch_context's frame, point to caller's stack
401399
402400 return rsp , rbp , rip
403401
@@ -1070,21 +1068,19 @@ def format_backtrace_frame(idx, addr):
10701068
10711069class PhotonLs (gdb .Command ):
10721070 """
1073- List all Photon threads in current vCPU (similar to GDB 'info threads').
1071+ List all Photon threads (similar to GDB 'info threads').
1072+ Works for both live process and coredump.
10741073 Usage: photon_ls
10751074 """
10761075
10771076 def __init__ (self ):
10781077 gdb .Command .__init__ (self , "photon_ls" , gdb .COMMAND_STACK , gdb .COMPLETE_NONE )
10791078
10801079 def invoke (self , arg , tty ):
1081- global _selected_thread_idx , _in_photon_mode , _photon_threads
1080+ global _selected_thread_idx
10821081
1083- # Use _photon_threads if in photon mode, otherwise load from current vCPU only
1084- if _in_photon_mode and _photon_threads :
1085- threads = _photon_threads
1086- else :
1087- threads = load_photon_threads_current_vcpu ()
1082+ # Use collect_all_threads for pure memory read (works with coredump)
1083+ threads = collect_all_threads ()
10881084
10891085 if not threads :
10901086 cprint ('WARNING' , "No photon threads found" )
@@ -1108,13 +1104,13 @@ def invoke(self, arg, tty):
11081104
11091105 # vCPU info
11101106 extra_info = ""
1111- if 'vcpu ' in t :
1112- extra_info += f" vCPU ({ t ['vcpu ' ]:#x} )"
1107+ if 'gdb_thread' in t and 'vcpu_addr ' in t :
1108+ extra_info += f" vCPU { t [ 'gdb_thread' ] } ({ t ['vcpu_addr ' ]:#x} )"
11131109
11141110 # Frame info
11151111 frame_str = format_frame_brief (rip )
1116- if state == 'RUNNING ' and rip == 0 :
1117- frame_str = "(no context available )"
1112+ if state == 'CURRENT ' and rip == 0 :
1113+ frame_str = "(use photon_init to enable register-based debugging )"
11181114
11191115 # Format like: * 1 Thread 0x555... RUNNING vCPU 0 (LWP in thread 1) main ()
11201116 print (f"{ marker } { i :<4} { color } Thread { addr :#x} { bcolors .ENDC } "
@@ -1228,8 +1224,10 @@ def invoke(self, arg, tty):
12281224class PhotonFr (gdb .Command ):
12291225 """
12301226 Select a Photon thread and switch to its registers (similar to GDB 'thread' command).
1227+ Requires photon_init to be called first to save original registers.
12311228 Usage: photon_fr <index>
12321229 After selection, you can use GDB commands like 'bt', 'frame', 'print' on the thread's context.
1230+ Use photon_fini to restore original registers when done.
12331231 """
12341232
12351233 def __init__ (self ):
@@ -1241,11 +1239,13 @@ def invoke(self, arg, tty):
12411239 if not require_living_process ("photon_fr" ):
12421240 return
12431241
1244- # Use _photon_threads if in photon mode, otherwise load from current vCPU
1245- if _in_photon_mode and _photon_threads :
1246- threads = _photon_threads
1247- else :
1248- threads = load_photon_threads_current_vcpu ()
1242+ # Must be in photon mode (photon_init called)
1243+ if not _in_photon_mode :
1244+ cprint ('ERROR' , "Please run 'photon_init' first to save registers" )
1245+ return
1246+
1247+ # Get threads list - use collect_all_threads for consistent indexing
1248+ threads = collect_all_threads ()
12491249
12501250 if not threads :
12511251 cprint ('WARNING' , "No photon threads found" )
@@ -1273,152 +1273,104 @@ def invoke(self, arg, tty):
12731273
12741274 # Build extra info string
12751275 extra_info = ""
1276- if 'vcpu ' in t :
1277- extra_info += f" vCPU ({ t ['vcpu ' ]:#x} )"
1276+ if 'gdb_thread' in t and 'vcpu_addr ' in t :
1277+ extra_info += f" vCPU { t [ 'gdb_thread' ] } ({ t ['vcpu_addr ' ]:#x} )"
12781278
12791279 # Show switched message like GDB
12801280 print (f"[Switching to Thread { idx } , { t ['addr' ]:#x} ({ color } { state } { bcolors .ENDC } ){ extra_info } ]" )
12811281
1282+ # Determine which registers to use
1283+ if state == 'CURRENT' :
1284+ # For CURRENT thread, use saved registers from photon_init
1285+ rsp = _saved_registers ['rsp' ]
1286+ rbp = _saved_registers ['rbp' ]
1287+ rip = _saved_registers ['rip' ]
1288+ else :
1289+ # For other threads, use registers read from stack
1290+ rsp = t .get ('rsp' )
1291+ rbp = t .get ('rbp' )
1292+ rip = t .get ('rip' )
1293+
12821294 # Actually switch to the thread's registers
1283- if t . get ( ' rsp' ) and t . get ( ' rbp' ) and t . get ( ' rip' ) :
1284- if switch_to_thread_registers (t [ ' rsp' ], t [ ' rbp' ], t [ ' rip' ] ):
1295+ if rsp and rbp and rip :
1296+ if switch_to_thread_registers (rsp , rbp , rip ):
12851297 # Show top frame after switching
1286- print (format_backtrace_frame (0 , t [ ' rip' ] ))
1298+ print (format_backtrace_frame (0 , rip ))
12871299 else :
12881300 cprint ('ERROR' , "Failed to switch to thread's registers" )
12891301 else :
12901302 cprint ('WARNING' , "No saved register context available for this thread" )
12911303
12921304
12931305# =============================================================================
1294- # Legacy commands (for compatibility) - Now with full functionality
1306+ # Register manipulation commands (live process only)
12951307# =============================================================================
12961308
1297- def load_photon_threads_current_vcpu ():
1298- """Load photon threads from current vCPU only (for photon_init/fr)"""
1299- global _photon_threads
1300- _photon_threads = []
1301-
1302- # Get current thread (which gives us current vCPU)
1303- current = get_current_thread ()
1304- if current == 0 :
1305- return []
1306-
1307- # Get current vCPU from current thread
1308- th = PhotonThread (current )
1309- current_vcpu = th .vcpu ()
1310-
1311- if current_vcpu == 0 :
1312- return []
1313-
1314- # Collect threads from current vCPU only
1315- threads = []
1316- visited = set ()
1317-
1318- # From sleepq
1319- for t_addr in get_sleepq_threads (current_vcpu ):
1320- if t_addr not in visited :
1321- visited .add (t_addr )
1322- t = PhotonThread (t_addr )
1323- rsp , rbp , rip = t .get_saved_registers ()
1324- threads .append ({
1325- 'addr' : t_addr ,
1326- 'state' : t .state_name (),
1327- 'rsp' : rsp ,
1328- 'rbp' : rbp ,
1329- 'rip' : rip ,
1330- 'vcpu' : current_vcpu
1331- })
1332-
1333- # From standbyq
1334- standbyq_offset = VCPU_OFFSETS .get ('standbyq' , 56 )
1335- standbyq_head = read_ptr (current_vcpu + standbyq_offset )
1336- if standbyq_head :
1337- for t_addr in traverse_list (standbyq_head , 'next' ):
1338- if t_addr not in visited :
1339- visited .add (t_addr )
1340- t = PhotonThread (t_addr )
1341- rsp , rbp , rip = t .get_saved_registers ()
1342- threads .append ({
1343- 'addr' : t_addr ,
1344- 'state' : t .state_name (),
1345- 'rsp' : rsp ,
1346- 'rbp' : rbp ,
1347- 'rip' : rip ,
1348- 'vcpu' : current_vcpu
1349- })
1350-
1351- # Current thread
1352- if current not in visited :
1353- visited .add (current )
1354- t = PhotonThread (current )
1355- rsp , rbp , rip = t .get_saved_registers ()
1356- threads .append ({
1357- 'addr' : current ,
1358- 'state' : 'CURRENT' ,
1359- 'rsp' : rsp ,
1360- 'rbp' : rbp ,
1361- 'rip' : rip ,
1362- 'vcpu' : current_vcpu
1363- })
1364-
1365- _photon_threads = threads
1366- return threads
1367-
1368-
13691309class PhotonInit (gdb .Command ):
1370- """Save current registers and enter photon thread lookup mode"""
1310+ """Save current registers and enter photon thread debug mode.
1311+ After calling this, use photon_fr to switch between threads.
1312+ Use photon_fini or photon_rst to restore registers when done."""
13711313 def __init__ (self ):
13721314 gdb .Command .__init__ (self , "photon_init" , gdb .COMMAND_STACK , gdb .COMPLETE_NONE )
13731315 def invoke (self , arg , tty ):
13741316 global _in_photon_mode
13751317 if not require_living_process ("photon_init" ):
13761318 return
13771319
1378- threads = load_photon_threads_current_vcpu ()
1320+ if _in_photon_mode :
1321+ cprint ('WARNING' , "Already in photon debug mode. Use photon_fini to exit first." )
1322+ return
1323+
1324+ threads = collect_all_threads ()
13791325 if not threads :
13801326 cprint ('WARNING' , "No photon threads found" )
13811327 return
13821328
13831329 if save_registers ():
13841330 _in_photon_mode = True
1385- cprint ('WARNING' , f"Entered photon thread lookup mode ({ len (threads )} threads). "
1386- "PLEASE do not step-in or continue before ` photon_fini` " )
1331+ cprint ('WARNING' , f"Entered photon debug mode ({ len (threads )} threads). "
1332+ "Use photon_fr to switch threads. Do NOT step/ continue before photon_fini! " )
13871333 else :
13881334 cprint ('ERROR' , "Failed to save registers" )
13891335
13901336
13911337class PhotonFini (gdb .Command ):
1392- """Restore registers and exit photon thread lookup mode"""
1338+ """Restore original registers and exit photon debug mode. """
13931339 def __init__ (self ):
13941340 gdb .Command .__init__ (self , "photon_fini" , gdb .COMMAND_STACK , gdb .COMPLETE_NONE )
13951341 def invoke (self , arg , tty ):
1396- global _in_photon_mode , _photon_threads
1342+ global _in_photon_mode , _photon_threads , _selected_thread_idx
13971343 if not require_living_process ("photon_fini" ):
13981344 return
13991345
14001346 if not _in_photon_mode :
1401- cprint ('WARNING' , "Not in photon mode" )
1347+ cprint ('WARNING' , "Not in photon debug mode" )
14021348 return
14031349
14041350 if restore_registers ():
14051351 _in_photon_mode = False
14061352 _photon_threads = []
1407- cprint ('WARNING' , "Finished photon thread lookup mode." )
1353+ _selected_thread_idx = 0
1354+ cprint ('INFO' , "Exited photon debug mode. Registers restored." )
14081355 else :
14091356 cprint ('ERROR' , "Failed to restore registers" )
14101357
14111358
14121359class PhotonRst (gdb .Command ):
1413- """Restore registers (alias for photon_fini without clearing state)"""
1360+ """Restore saved registers without exiting photon debug mode.
1361+ Use this to quickly return to original context while staying in debug mode."""
14141362 def __init__ (self ):
14151363 gdb .Command .__init__ (self , "photon_rst" , gdb .COMMAND_STACK , gdb .COMPLETE_NONE )
14161364 def invoke (self , arg , tty ):
14171365 if not require_living_process ("photon_rst" ):
14181366 return
14191367
1368+ if not _saved_registers :
1369+ cprint ('WARNING' , "No saved registers. Run photon_init first." )
1370+ return
1371+
14201372 if restore_registers ():
1421- cprint ('INFO' , "Registers restored" )
1373+ cprint ('INFO' , "Registers restored (still in photon debug mode) " )
14221374 else :
14231375 cprint ('ERROR' , "Failed to restore registers" )
14241376
@@ -1458,6 +1410,23 @@ def invoke(self, arg, tty):
14581410# Initialization
14591411# =============================================================================
14601412
1413+ # Event handler: auto-restore registers before program continues
1414+ def _on_cont (event ):
1415+ """Called when program is about to continue (step/next/continue).
1416+ Auto-restore registers if in photon debug mode."""
1417+ global _in_photon_mode , _photon_threads , _selected_thread_idx
1418+ if _in_photon_mode and _saved_registers :
1419+ cprint ('WARNING' , "Auto-restoring registers before continue..." )
1420+ restore_registers ()
1421+ _in_photon_mode = False
1422+ _photon_threads = []
1423+ _selected_thread_idx = 0
1424+
1425+ try :
1426+ gdb .events .cont .connect (_on_cont )
1427+ except :
1428+ pass # Older GDB versions may not support this
1429+
14611430PhotonLs ()
14621431PhotonBt ()
14631432PhotonPs ()
@@ -1469,4 +1438,4 @@ def invoke(self, arg, tty):
14691438
14701439cprint ('INFO' , 'Photon-GDB-extension v2 loaded' )
14711440cprint ('INFO' , 'Commands: photon_ls, photon_fr <n>, photon_bt [n], photon_ps, photon_current' )
1472- cprint ('INFO' , 'Use photon_fr to select thread, then photon_bt to show its backtrace ' )
1441+ cprint ('INFO' , 'Register commands: photon_init -> photon_fr -> photon_fini ' )
0 commit comments