@@ -409,13 +409,10 @@ func (d *Debugger) LastModified() time.Time {
409409	return  d .target .Selected .BinInfo ().LastModified ()
410410}
411411
412- // FunctionReturnLocations returns all return locations 
413- // for the given function, a list of addresses corresponding 
414- // to 'ret' or 'call runtime.deferreturn'. 
415- func  (d  * Debugger ) FunctionReturnLocations (fnName  string ) ([]uint64 , error ) {
416- 	d .targetMutex .Lock ()
417- 	defer  d .targetMutex .Unlock ()
418- 
412+ // functionReturnLocationsInternal is same as FunctionReturnLocations 
413+ // except that it does not have a lock and unlock as its called from 
414+ // within the callback which has already acquired a lock. 
415+ func  (d  * Debugger ) functionReturnLocationsInternal (fnName  string ) ([]uint64 , error ) {
419416	if  len (d .target .Targets ()) >  1  {
420417		return  nil , ErrNotImplementedWithMultitarget 
421418	}
@@ -454,6 +451,15 @@ func (d *Debugger) FunctionReturnLocations(fnName string) ([]uint64, error) {
454451	return  addrs , nil 
455452}
456453
454+ // FunctionReturnLocations returns all return locations 
455+ // for the given function, a list of addresses corresponding 
456+ // to 'ret' or 'call runtime.deferreturn'. 
457+ func  (d  * Debugger ) FunctionReturnLocations (fnName  string ) ([]uint64 , error ) {
458+ 	d .targetMutex .Lock ()
459+ 	defer  d .targetMutex .Unlock ()
460+ 	return  d .functionReturnLocationsInternal (fnName )
461+ }
462+ 
457463// Detach detaches from the target process. 
458464// If `kill` is true we will kill the process after 
459465// detaching. 
@@ -1372,7 +1378,7 @@ func (d *Debugger) Functions(filter string, followCalls int) ([]string, error) {
13721378		for  _ , f  :=  range  t .BinInfo ().Functions  {
13731379			if  regex .MatchString (f .Name ) {
13741380				if  followCalls  >  0  {
1375- 					newfuncs , err  :=  traverse (t , & f , 1 , followCalls )
1381+ 					newfuncs , err  :=  d . traverse (t , & f , 1 , followCalls ,  filter )
13761382					if  err  !=  nil  {
13771383						return  nil , fmt .Errorf ("traverse failed with error %w" , err )
13781384					}
@@ -1388,12 +1394,13 @@ func (d *Debugger) Functions(filter string, followCalls int) ([]string, error) {
13881394	return  funcs , nil 
13891395}
13901396
1391- func  traverse (t  proc.ValidTargets , f  * proc.Function , depth  int , followCalls  int ) ([]string , error ) {
1397+ func  ( d   * Debugger )  traverse (t  proc.ValidTargets , f  * proc.Function , depth  int , followCalls  int ,  rootstr   string ) ([]string , error ) {
13921398	type  TraceFunc  struct  {
13931399		Func     * proc.Function 
13941400		Depth    int 
13951401		visited  bool 
13961402	}
1403+ 
13971404	type  TraceFuncptr  * TraceFunc 
13981405
13991406	TraceMap  :=  make (map [string ]TraceFuncptr )
@@ -1409,7 +1416,7 @@ func traverse(t proc.ValidTargets, f *proc.Function, depth int, followCalls int)
14091416		parent  :=  queue [0 ]
14101417		queue  =  queue [1 :]
14111418		if  parent  ==  nil  {
1412- 			panic ("attempting to open file Delve  cannot parse " )
1419+ 			panic ("queue has a nil node,  cannot traverse! " )
14131420		}
14141421		if  parent .Depth  >  followCalls  {
14151422			continue 
@@ -1427,10 +1434,89 @@ func traverse(t proc.ValidTargets, f *proc.Function, depth int, followCalls int)
14271434		}
14281435		f  :=  parent .Func 
14291436		text , err  :=  proc .Disassemble (t .Memory (), nil , t .Breakpoints (), t .BinInfo (), f .Entry , f .End )
1437+ 
14301438		if  err  !=  nil  {
14311439			return  nil , fmt .Errorf ("disassemble failed with error %w" , err )
14321440		}
14331441		for  _ , instr  :=  range  text  {
1442+ 			// Dynamic functions need to be handled specially as their destination location 
1443+ 			// is not known statically, hence its required to put a breakpoint in order to 
1444+ 			// acquire the address of the function at runtime and we do this via a 
1445+ 			// call back mechanism 
1446+ 			if  instr .IsCall () &&  instr .DestLoc  ==  nil  {
1447+ 				dynbp , err  :=  t .SetBreakpoint (0 , instr .Loc .PC , proc .NextBreakpoint , nil )
1448+ 				if  err  !=  nil  {
1449+ 					return  nil , fmt .Errorf ("error setting breakpoint inside deferreturn" )
1450+ 				}
1451+ 				dynCallback  :=  func (th  proc.Thread , tgt  * proc.Target ) (bool , error ) {
1452+ 					// TODO(optimization): Consider using an iterator to avoid materializing 
1453+ 					// the full stack when we only need frames up to the root function 
1454+ 					rawlocs , err  :=  proc .ThreadStacktrace (tgt , tgt .CurrentThread (), followCalls + 2 )
1455+ 					if  err  !=  nil  {
1456+ 						return  false , fmt .Errorf ("thread stack trace returned error" )
1457+ 					}
1458+ 					// Since the dynamic function is known only at runtime, the depth is likewise 
1459+ 					// calculated by referring to the stack and the mechanism is similar to that 
1460+ 					// used in pkg/terminal/command.go:printTraceOutput 
1461+ 					rootindex  :=  - 1 
1462+ 					for  i  :=  len (rawlocs ) -  1 ; i  >=  0 ; i --  {
1463+ 						if  rawlocs [i ].Call .Fn .Name  ==  rootstr  {
1464+ 							if  rootindex  ==  - 1  {
1465+ 								rootindex  =  i 
1466+ 								break 
1467+ 							}
1468+ 						}
1469+ 					}
1470+ 					sdepth  :=  rootindex  +  1 
1471+ 
1472+ 					if  sdepth + 1  >  followCalls  {
1473+ 						return  false , nil 
1474+ 					}
1475+ 					regs , err  :=  th .Registers ()
1476+ 					if  err  !=  nil  {
1477+ 						return  false , fmt .Errorf ("registers inside callback returned err" )
1478+ 
1479+ 					}
1480+ 					// Disassemble the instruction at the current PC to get the call destination 
1481+ 					pc  :=  instr .Loc .PC 
1482+ 					maxInstLen  :=  uint64 (tgt .BinInfo ().Arch .MaxInstructionLength ())
1483+ 					disasm , err  :=  proc .Disassemble (t .Memory (), regs , t .Breakpoints (), tgt .BinInfo (), pc , pc + maxInstLen )
1484+ 					if  err  !=  nil  {
1485+ 						return  false , fmt .Errorf ("failed to disassemble instruction: %w" , err )
1486+ 					}
1487+ 
1488+ 					// Extract address from the decoded instruction's destination location 
1489+ 					var  addr  uint64 
1490+ 					if  len (disasm ) >  0  &&  disasm [0 ].DestLoc  !=  nil  {
1491+ 						addr  =  disasm [0 ].DestLoc .PC 
1492+ 					} else  {
1493+ 						return  false , fmt .Errorf ("failed to extract call destination from instruction at PC %#x" , pc )
1494+ 					}
1495+ 					fn  :=  tgt .BinInfo ().PCToFunc (addr )
1496+ 					if  fn  ==  nil  {
1497+ 						return  false , fmt .Errorf ("PCToFunc returned nil" )
1498+ 					}
1499+ 					err  =  createFunctionTracepoints (d , fn .Name , rootstr , followCalls )
1500+ 					if  err  !=  nil  {
1501+ 						return  false , fmt .Errorf ("error creating tracepoint in function %s" , fn .Name )
1502+ 					}
1503+ 					dynchildren , err  :=  d .traverse (t , fn , sdepth + 1 , followCalls , rootstr )
1504+ 					if  err  !=  nil  {
1505+ 						return  false , fmt .Errorf ("error calling traverse on dynamic children" )
1506+ 					}
1507+ 					for  _ , child  :=  range  dynchildren  {
1508+ 						err  :=  createFunctionTracepoints (d , child , rootstr , followCalls )
1509+ 						if  err  !=  nil  {
1510+ 							return  false , fmt .Errorf ("error creating tracepoint in function %s" , child )
1511+ 						}
1512+ 					}
1513+ 					return  false , nil 
1514+ 				}
1515+ 				for  _ , dynBrklet  :=  range  dynbp .Breaklets  {
1516+ 					dynBrklet .SetCallback (dynCallback )
1517+ 				}
1518+ 			}
1519+ 
14341520			if  instr .IsCall () &&  instr .DestLoc  !=  nil  &&  instr .DestLoc .Fn  !=  nil  {
14351521				cf  :=  instr .DestLoc .Fn 
14361522				if  (strings .HasPrefix (cf .Name , "runtime." ) ||  strings .HasPrefix (cf .Name , "runtime/internal" )) &&  cf .Name  !=  "runtime.deferreturn"  &&  cf .Name  !=  "runtime.gorecover"  &&  cf .Name  !=  "runtime.gopanic"  {
@@ -1449,6 +1535,70 @@ func traverse(t proc.ValidTargets, f *proc.Function, depth int, followCalls int)
14491535	return  funcs , nil 
14501536}
14511537
1538+ // createFunctionTracepoints is a way to create a trace point late in the cycle but just in time that 
1539+ // it can be included in the trace output as in the case of dynamic functions, we get to know the 
1540+ // functions that are being called from deferreturn quite late in execution. This might create multiple 
1541+ // tracepoints 
1542+ func  createFunctionTracepoints (d  * Debugger , fname  string , rootstr  string , followCalls  int ) error  {
1543+ 
1544+ 	// Helper function to create breakpoints for tracepoints 
1545+ 	// Ignore BreakpointExistsError since duplicate tracepoints 
1546+ 	// are expected during dynamic function discovery 
1547+ 	createBrkForTracepoint  :=  func (lbp  * proc.LogicalBreakpoint ) error  {
1548+ 		d .breakpointIDCounter ++ 
1549+ 		lbp .LogicalID  =  d .breakpointIDCounter 
1550+ 		lbp .HitCount  =  make (map [int64 ]uint64 )
1551+ 		d .target .LogicalBreakpoints [lbp .LogicalID ] =  lbp 
1552+ 		err  :=  d .target .SetBreakpointEnabled (lbp , true )
1553+ 		if  err  !=  nil  {
1554+ 			delete (d .target .LogicalBreakpoints , lbp .LogicalID )
1555+ 			// Silently ignore BreakpointExistsError - this is expected when 
1556+ 			// creating tracepoints for functions that may already have them 
1557+ 			if  _ , exists  :=  err .(proc.BreakpointExistsError ); exists  {
1558+ 				return  nil 
1559+ 			}
1560+ 			return  err 
1561+ 		}
1562+ 		return  nil 
1563+ 	}
1564+ 
1565+ 	// Create tracepoint for function entry 
1566+ 	lbp  :=  & proc.LogicalBreakpoint {
1567+ 		Set : proc.SetBreakpoint {
1568+ 			FunctionName : fname ,
1569+ 		},
1570+ 		Tracepoint :       true ,
1571+ 		RootFuncName :     rootstr ,
1572+ 		Stacktrace :       20 ,
1573+ 		TraceFollowCalls : followCalls ,
1574+ 	}
1575+ 
1576+ 	err  :=  createBrkForTracepoint (lbp )
1577+ 	if  err  !=  nil  {
1578+ 		return  fmt .Errorf ("error creating breakpoint at function %s: %w" , fname , err )
1579+ 	}
1580+ 
1581+ 	// Create tracepoints for function return locations 
1582+ 	raddrs , _  :=  d .functionReturnLocationsInternal (fname )
1583+ 	for  i  :=  range  raddrs  {
1584+ 		retLbp  :=  & proc.LogicalBreakpoint {
1585+ 			Set : proc.SetBreakpoint {
1586+ 				PidAddrs : []proc.PidAddr {{Pid : d .target .Selected .Pid (), Addr : raddrs [i ]}},
1587+ 			},
1588+ 			TraceReturn :      true ,
1589+ 			RootFuncName :     rootstr ,
1590+ 			Stacktrace :       20 ,
1591+ 			TraceFollowCalls : followCalls ,
1592+ 		}
1593+ 
1594+ 		err  :=  createBrkForTracepoint (retLbp )
1595+ 		if  err  !=  nil  {
1596+ 			return  fmt .Errorf ("error creating breakpoint at function return %s: %w" , fname , err )
1597+ 		}
1598+ 	}
1599+ 	return  nil 
1600+ }
1601+ 
14521602// Types returns all type information in the binary. 
14531603func  (d  * Debugger ) Types (filter  string ) ([]string , error ) {
14541604	d .targetMutex .Lock ()
0 commit comments