home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / adav313.zip / gnat-3_13p-os2-bin-20010916.zip / emx / gnatlib / s-traceb.adb < prev    next >
Text File  |  2000-07-19  |  10KB  |  250 lines

  1. ------------------------------------------------------------------------------
  2. --                                                                          --
  3. --                         GNAT RUN-TIME COMPONENTS                         --
  4. --                                                                          --
  5. --                     S Y S T E M . T R A C E B A C K                      --
  6. --                                                                          --
  7. --                                 B o d y                                  --
  8. --                            (Version for x86)                             --
  9. --                                                                          --
  10. --                            $Revision: 1.10 $
  11. --                                                                          --
  12. --            Copyright (C) 1999-2000 Ada Core Technologies, Inc.           --
  13. --                                                                          --
  14. -- GNAT is free software;  you can  redistribute it  and/or modify it under --
  15. -- terms of the  GNU General Public License as published  by the Free Soft- --
  16. -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
  17. -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
  18. -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
  19. -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
  20. -- for  more details.  You should have  received  a copy of the GNU General --
  21. -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
  22. -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
  23. -- MA 02111-1307, USA.                                                      --
  24. --                                                                          --
  25. -- As a special exception,  if other files  instantiate  generics from this --
  26. -- unit, or you link  this unit with other files  to produce an executable, --
  27. -- this  unit  does not  by itself cause  the resulting  executable  to  be --
  28. -- covered  by the  GNU  General  Public  License.  This exception does not --
  29. -- however invalidate  any other reasons why  the executable file  might be --
  30. -- covered by the  GNU Public License.                                      --
  31. --                                                                          --
  32. -- GNAT is maintained by Ada Core Technologies Inc (http://www.gnat.com).   --
  33. --                                                                          --
  34. ------------------------------------------------------------------------------
  35.  
  36. with Ada.Unchecked_Conversion;
  37. with Ada.Unchecked_Deallocation;
  38. with System.Machine_Code;     use System.Machine_Code;
  39. with System.Soft_Links;
  40.  
  41. --  This is the x86 version of this package. The backtrace is computed directly
  42. --  by analyzing the stack. It is required that the frame pointer be included
  43. --  in the code. The code here will not work if some units are compiled with
  44. --  the -fomit-frame-pointer GCC option.
  45.  
  46. package body System.Traceback is
  47.  
  48.    Task_Wrapper_Address : Address
  49.      renames System.Soft_Links.Task_Wrapper_Address;
  50.  
  51.    --  This code does not handle the stack backtrace for foreign threads ???
  52.  
  53.    --  With a frame pointer, the prolog looks like:
  54.  
  55.    --     pushl %ebp          caller's stack address
  56.    --     movl  %esp,%ebp
  57.    --     subl  $nnn,%esp     omitted if nnn = 0
  58.    --     pushl %edi          omitted if edi not used
  59.    --     pushl %esi          omitted if esi not used
  60.    --     pushl %ebx          omitted if ebx not used
  61.  
  62.    --  A call looks like:
  63.  
  64.    --     pushl ...           push parameters
  65.    --     pushl ...
  66.    --     call  ...           perform the call
  67.    --     addl  $nnn,%esp     omitted if no parameters
  68.  
  69.    --  So a procedure call under an ix86 architecture push on the stack:
  70.  
  71.    --      -------------------
  72.    --      - Proc param n   --  the parameters
  73.    --      - Proc param n-1 --
  74.    --      - ...            --
  75.    --   8  - Proc param 1   --
  76.    --   4  - return address --
  77.    --   0  - ebp            --  ebp is the caller stack address
  78.    --      -------------------
  79.  
  80.    --  All this is sufficient to compute a full backtrace.
  81.  
  82.    type Stack_Pointer is mod 2 ** 32;
  83.    type Stack_Pointer_Access is access Stack_Pointer;
  84.  
  85.    subtype Stack_Offset is Stack_Pointer;
  86.  
  87.    function To_Machine_State is
  88.       new Ada.Unchecked_Conversion (Stack_Pointer_Access, Machine_State);
  89.  
  90.    function To_Pointer is
  91.       new Ada.Unchecked_Conversion (Machine_State, Stack_Pointer_Access);
  92.  
  93.  
  94.    procedure Main;
  95.    pragma Import (C, Main, "main");
  96.    --  Import this symbol here just to take it's address.
  97.  
  98.    function Read_Mem (Adr : in Stack_Pointer) return Stack_Pointer;
  99.    --  This is a small routine to read a word at a specific address of the
  100.    --  process virtual memory. It would have been possible to use the NT
  101.    --  ReadProcessMemory but since we have had a problem and we want to get a
  102.    --  backtrace, using a NT Win32 API call could be unsafe.
  103.  
  104.    Stop_Traceback_Offset : constant := 50;
  105.    --  Number of bytes for the stack traceback end point. The traceback is
  106.    --  stoped when we reach an address in the range:
  107.    --
  108.    --  [Stop_Traceback_Point .. Stop_Traceback_Point + Stop_Traceback_Offset]
  109.    --
  110.  
  111.    Stop_Traceback_Thread_Offset : constant := 1000;
  112.    --  Number of bytes for the stack traceback end point for a tasking
  113.    --  program. The traceback is stopped when we reach an address in the range:
  114.    --
  115.    --  [Task_Wrapper_Address
  116.    --     .. Task_Wrapper_Address + Stop_Traceback_Thread_Offset]
  117.    --
  118.    --  The number is large because there is some code inlined in the
  119.    --  Task_Wrapper procedure. So the call to the thread entry point is far
  120.    --  from the start of the Task_Wrapper procedure.
  121.  
  122.    Stop_Traceback_Point : Stack_Pointer;
  123.    --  This must be the address of the main entry point. It is used to check
  124.    --  if the stack traceback must be stopped. If we reach an address that is
  125.    --  Stop_Traceback_Offset bytes from this symbol we stop.
  126.  
  127.    ----------------------------
  128.    -- Allocate_Machine_State --
  129.    ----------------------------
  130.  
  131.    function Allocate_Machine_State return Machine_State is
  132.       SPA : Stack_Pointer_Access := new Stack_Pointer;
  133.    begin
  134.       return To_Machine_State (SPA);
  135.    end Allocate_Machine_State;
  136.  
  137.    ------------------------
  138.    -- Free_Machine_State --
  139.    ------------------------
  140.  
  141.    procedure Free_Machine_State (M : in out Machine_State) is
  142.       procedure Free is
  143.          new Ada.Unchecked_Deallocation (Stack_Pointer, Stack_Pointer_Access);
  144.       SPA : Stack_Pointer_Access := To_Pointer (M);
  145.    begin
  146.       Free (SPA);
  147.    end Free_Machine_State;
  148.  
  149.    --------------
  150.    -- Read_Mem --
  151.    --------------
  152.  
  153.    function Read_Mem (Adr : in Stack_Pointer) return Stack_Pointer is
  154.       Res : Stack_Pointer;
  155.       for Res'Address use Address (Adr);
  156.    begin
  157.       return Res;
  158.    end Read_Mem;
  159.  
  160.    ------------------
  161.    -- Get_Code_Loc --
  162.    ------------------
  163.  
  164.    function Get_Code_Loc (M : Machine_State) return Code_Loc is
  165.  
  166.       Asm_Call_Size : constant := 2;
  167.       --  Minimum size for a call instruction under ix86. Using the minimum
  168.       --  size is safe here as the call point computed from the return point
  169.       --  will always be inside the call instruction.
  170.  
  171.       SPA  : Stack_Pointer_Access := To_Pointer (M);
  172.  
  173.       Cur  : Stack_Pointer := SPA.all;
  174.       ebp  : Stack_Pointer;
  175.       Call : Stack_Pointer;
  176.  
  177.    begin
  178.       --  First word on the stack is the caller stack's address followed by
  179.       --  the return point.
  180.  
  181.       ebp := Read_Mem (Cur);
  182.       Cur := Cur + 4;
  183.  
  184.       --  Get the call point by substracting Asm_Call_Size from the return
  185.       --  point.
  186.  
  187.       declare
  188.          Ret_Point : Stack_Pointer := Read_Mem (Cur);
  189.       begin
  190.          Call := Ret_Point - Asm_Call_Size;
  191.       end;
  192.  
  193.       --  Here we suppose that the call point address is always bigger than
  194.       --  the stop points. In fact, Task_Wrapper (pointed to by
  195.       --  Task_Wrapper_Address) is defined in the GNAT library and 'main'
  196.       --  (pointed to by Stop_Traceback_Point) is defined in the binder code
  197.       --  and both symbols are always added before user's code at link stage.
  198.  
  199.       if Call - Stop_Traceback_Point < Stop_Traceback_Offset
  200.         or else
  201.         (Task_Wrapper_Address /= Null_Address
  202.          and then Call - Stack_Pointer (Task_Wrapper_Address)
  203.            < Stop_Traceback_Thread_Offset)
  204.       then
  205.          return Null_Address;
  206.       else
  207.          return Code_Loc (Call);
  208.       end if;
  209.    end Get_Code_Loc;
  210.  
  211.    ---------------
  212.    -- Pop_Frame --
  213.    ---------------
  214.  
  215.    procedure Pop_Frame (M : Machine_State) is
  216.       SPA : Stack_Pointer_Access := To_Pointer (M);
  217.    begin
  218.       --  go to the caller stack frame. The address of the caller stack is the
  219.       --  first word pointed by the machine state (the Stack_Pointer).
  220.  
  221.       SPA.all := Read_Mem (SPA.all);
  222.    end Pop_Frame;
  223.  
  224.    -----------------------
  225.    -- Set_Machine_State --
  226.    -----------------------
  227.  
  228.    procedure Set_Machine_State (M : Machine_State) is
  229.       SPA : Stack_Pointer_Access := To_Pointer (M);
  230.    begin
  231.       --  Retrieve the caller's stack address which is the Call_Chain's one
  232.       --  see GNAT.Traceback.
  233.  
  234.       Asm ("movl %%ebp, %0",
  235.            Outputs => Stack_Pointer'Asm_Output ("=m", SPA.all));
  236.  
  237.       --  Pop one more frame to get the user's function stack address which
  238.       --  has called this procedure.
  239.  
  240.       Pop_Frame (M);
  241.  
  242.       --  Initialize the traceback end regions which is delimited by two
  243.       --  functions. Main used for the main thread and Task_Wrapper for a task
  244.       --  stack traceback.
  245.  
  246.       Stop_Traceback_Point := Stack_Pointer (Main'Address);
  247.    end Set_Machine_State;
  248.  
  249. end System.Traceback;
  250.