Re: BREAKing into Euphoria

new topic     » goto parent     » topic index » view thread      » older message » newer message

Quoted code was ok, but you can't invoke trace in a callback. You can queue a trace by patching TraceOn=1 in eu client app (i got the virtual address from gnu disasm - sorry just cut and paste the asm into euphoria - anyone rewrite this in euphoria?); so this works for official eu 4.1.0 download (https://github.com/EuphoriaLanguages/Friendly_Flexible_Fast/raw/OpenEuphoria/euphoria-4.1.0-x86.exe). Update allow_break() machine call should be enhanced to take &2= set TraceOn, &4= color_trace=1, but i'm too lazy to recompile the sources. Anyway my code below correctly pops up the debugger at the next trace'able code whenever the user presses the break keys. It also demonstrates iup timeslicing some heavy foreground processing while updating the iup (windows) gui message pipe to minimize iup callback 'involvement'. If you press 'q' when entering text into the control, you'll break out to the debugger which allows you focused debugging of gui elements; see the 'tr()' conditional trace func. If press break in the console, you'll popup the debugger at the next iup client callback event handler. Now only if the debugger had pageup/down???

 
-- test.ex   IupText Example in C 
--Creates a IupText that shows asterisks instead of characters (password-like)./ 
 
include euphoria/debug/debug.e 
 
include std/os.e 
--include dll.e 
--include machine.e 
 
include gui/iup/iup.e 
include gui/iup/iup_config.e 
 
include std/console.e 
 
sequence add_code = {   --do trace(1) in "without trace" code, so the debugger pops up in the next "with trace" code. 
  -- Eu trace(1) in "without trace" code is optimized away, and debugger never pops up. 
  #c7, #05, #20, #1a, #5b, #00, -- movl   $0x1,0x5b1a20  ie TraceOn=1 
    #01, #00, #00, #00, 
  #c7, #05, #40, #14, #5b, #00, -- movl   $0x1,0x5b1440  ie color_trace = TRUE;  --ie ensures the debugger pops up in color (not static uninit value of 0 ie B/W) 
    #01, #00, #00, #00, 
--#C2, #00,  #08 * (platform() = WINDOWS) -- ret 8 
  #c3   -- ret 
} 
 
atom code_space = allocate_code(add_code) 
integer code_space_rid = define_c_proc("", code_space, {}) 
 
global procedure tr(object condition={}, sequence errorMessage={}) 
--atom condition <> 0, then print errorMessage, else ignore. Eg tr(1>0, {"%s %d", "five=", 5}) 
--string 'condition' for simple error message 
  if atom(condition) and condition = 0 then return end if         --failed condition if its numeric 
  if length(errorMessage) >= 2 and sequence(errorMessage[1]) then	--formatting string, args ... 
      errorMessage=sprintf(errorMessage[1], errorMessage[2..$]) 
  end if 
  if atom(condition) then 
    condition=sprintf("%d", condition) 
  elsif length(condition) then 
    errorMessage=sprintf("%s %s", {condition, errorMessage}) 
    condition="" 
  end if 
  if length(errorMessage) or length(condition) then --non-empty string, so elaborate error 
    printf(1, "\ntr(%s): %s\n", {condition, errorMessage}) 
  end if 
  c_func(xSetForegroundWindow, {hCon} )        --switch to (debugger) console window 
  c_proc(code_space_rid, {})    --trace out to the debugger at next "with trace" code. 
end procedure 
 
 
function breakProc(integer event) 
  c_proc(code_space_rid, {})    --trace out to the debugger at next "with trace" code. 
  return 1  --handled the control signal so return TRUE for no further action. 
end function 
 
 
type Ihandle( object x ) 
    return 1 
end type 
 
with trace 
 
Ihandle text, dlg, pwd 
sequence password 
 
 
function k_any( Ihandle self, atom c ) 
  tr(c='q', {"Debugging IUP client callback, c=%d", c}) 
  switch (c) do 
 
  case K_BS then 
 
      integer size = length(password) 
      if equal(size , 0) then 
        return IUP_IGNORE 
      end if 
      password[size-1] = 0 
      IupSetAttribute(pwd, "VALUE", password) 
      return IUP_DEFAULT 
 
  case K_CR then 
  case K_SP then 
  case K_ESC then 
  case K_INS then 
  case K_DEL then 
  case K_TAB then 
  case K_HOME then 
  case K_UP then 
  case K_PGUP then 
  case K_LEFT then 
  case K_MIDDLE then 
  case K_RIGHT then 
  case K_END then 
  case K_DOWN then 
  case K_PGDN then 
    return IUP_IGNORE 
  case else 
    return IUP_DEFAULT 
  end switch 
  return 1 
end function 
 
function action( Ihandle self, integer c, atom after ) 
 
  if (c) then 
    integer size = length(password) 
    password[size-1] = c 
    password[size] = 0 
    IupSetAttribute(pwd, "VALUE", password) 
  end if 
 
  return K_asterisk 
end function 
 
--atom kernel32, xSetConsoleCtrlHandler, cbHR 
atom xSetConsoleCtrlHandler, cbHR 
 
 
procedure noIup() 
  while 1 do 
    puts(1,"sleeping press q to quit, break to debug\n") 
    sleep(2) 
    puts(1,"awake\n") 
    if get_key() = 'q' then 
      exit 
    end if 
  end while 
end procedure 
 
 
 
without trace 
function main( object argc, object argv ) 
  IupOpen( argc, argv ) 
 
  password = repeat(0,100) 
 
 
  xSetConsoleCtrlHandler = define_c_func(kernel32,"SetConsoleCtrlHandler", 
    {C_POINTER, --	PHANDLER_ROUTINE  pHandlerRoutine,  // address of handler function 
    C_INT},    --	BOOL  fAdd  // handler to add or remove 
    C_INT)	    -- BOOL 
 
  cbHR = call_back(routine_id("breakProc"))   --{’+’, id} for cdecl 
  if not c_func(xSetConsoleCtrlHandler,{cbHR,1}) then ?9/0 end if 
 
  noIup() 
 
  text = IupText(NULL) 
  IupSetAttribute(text, "SIZE",  "200x") 
  IupSetCallback(text, "ACTION", Icallback("action")) 
  IupSetCallback(text, "K_ANY", Icallback("k_any")) 
 
  pwd = IupText(NULL) 
  IupSetAttribute(pwd, "READONLY", "YES") 
  IupSetAttribute(pwd, "SIZE", "200x") 
 
  dlg = IupDialog(IupVbox({text, pwd, NULL})) 
  IupSetAttribute(dlg, "TITLE", "IupText") 
 
  IupShowXY(dlg, IUP_CENTER, IUP_CENTER) 
 
--  IupMainLoop()   --blocks this foreground process until user closes GUI 
  loop do 
    ? hCon & date() 
    printf(1, "Press break to debug ") 
    sleep(.01)      --do a short (10mSec) bit of forground timeslicing here 
    until IupLoopStep() = IUP_CLOSE --docs say this should be called within a callback 
  end loop 
  IupClose() 
  return EXIT_SUCCESS 
end function 
 
constant kernel32 = open_dll( "kernel32.dll" ) 
constant xGetConsoleWindow = define_c_func( kernel32, "GetConsoleWindow", {}, C_ULONG ) 
atom hCon = c_func( xGetConsoleWindow, {} ) 
 
constant user32 = open_dll( "user32" ) 
--constant xSetForegroundWindow = define_c_func( user32, "SetForegroundWindow", {C_ULONG}, C_ULONG ) 
constant xSetForegroundWindow = define_c_func( user32, "SetForegroundWindow", {C_ULONG}, C_UINT  ) -- BOOL SetForegroundWindow(HWND) 
 
sequence cmd = command_line() 
sequence argv = cmd[3..$] 
integer argc = length( argv ) 
 
main( argc, argv ) 
 
 
Andy Drummond <andy at k?strel?ele.com> said...

Pete Lomax wrote: 
>  
> I got some "interesting" results (random crashes mainly) from this, however 
> enough of it works to show that something is possible (comments, Rob?): 
> Maybe there is a generic problem with trace in call_back routines?? 
>  
> }}} 
<eucode> 
> -- NOTE: this is probably incompatible with allow_break/check_break(). 
> with trace 
>  
> include misc.e 
>  
> constant CTRL_C_EVENT	    = 0, 
> 	 CTRL_BREAK_EVENT   = 1 
>  
> function HandlerRoutine(integer event) 
>     if event = CTRL_C_EVENT 
>     or event = CTRL_BREAK_EVENT then 
> trace(1) 
> sleep(2) 
>         return 1	    -- handled 
>     end if 
>     return 0		    -- not handled 
> end function 
>  
> atom kernel32, xSetConsoleCtrlHandler, cbHR 
> include dll.e 
>  
>     kernel32 = open_dll("kernel32.dll") 
>  
>     xSetConsoleCtrlHandler = define_c_func(kernel32,"SetConsoleCtrlHandler", 
> 	{C_POINTER, --	PHANDLER_ROUTINE  pHandlerRoutine,  // address of handler 
> function 
> 	 C_INT},    --	BOOL  fAdd  // handler to add or remove 
> 	C_INT)	    -- BOOL 
>  
>     cbHR = call_back(routine_id("HandlerRoutine")) 
>     if not c_func(xSetConsoleCtrlHandler,{cbHR,1}) then ?9/0 end if 
>  
> with trace 
>  
> while 1 do 
>     puts(1,"sleeping\n") 
>     sleep(2) 
>     puts(1,"awake\n") 
> end while 
> </eucode> 
{{{ 
 
>  
> I suspect that if you could somehow call the trace(1) in HandlerRoutine but 
> not then trace any lines in that code, it might actually work. 
>  
> When not interpreting source, or under an allow_break(False), then Ctrl Break 
> should not attempt to fire up the trace window, otherwise it should. 
> I don't think changing Ctrl C is a good idea, too easy to accidentally hit, 
> plus eg Edita happily uses it for Copy w/out problems as is. 
>  
> Regards, 
> Pete 
 
Pete, 
That look pretty much what I was after. I understand that it might well not be  
possible to trace once the break has been executed, but if I can tell where I 
am in the program then I can start the safer but more laborious technique of  
putting in trace(1) - or even trace(3) - calls. At present I have no idea 
where I am executing when everything dies on me. I just need to go Click! 
and find out which line of the program was executing at the time. 
We will see... 
Andy 

new topic     » goto parent     » topic index » view thread      » older message » newer message

Search



Quick Links

User menu

Not signed in.

Misc Menu