summaryrefslogtreecommitdiff
path: root/gcc/ada/g-debpoo.adb
diff options
context:
space:
mode:
authorcharlet <charlet@138bc75d-0d04-0410-961f-82ee72b054a4>2007-04-06 09:22:07 +0000
committercharlet <charlet@138bc75d-0d04-0410-961f-82ee72b054a4>2007-04-06 09:22:07 +0000
commit4a5aa7590e0aa28105617c24c052629da5d4a151 (patch)
tree229cb58e4d3f0b9616d46b11dfa66bbd7faf9bb6 /gcc/ada/g-debpoo.adb
parentc80139245feaab9a849ac1678c87caff817a58eb (diff)
downloadgcc-4a5aa7590e0aa28105617c24c052629da5d4a151.tar.gz
2007-04-06 Vincent Celier <celier@adacore.com>
* g-debpoo.adb (Validity): New package with a complete new implementation of subprograms Is_Valid and Set_Valid. (Is_Valid): Move to local package Validity (Set_Valid): Move to local package Validity git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@123572 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/ada/g-debpoo.adb')
-rw-r--r--gcc/ada/g-debpoo.adb341
1 files changed, 139 insertions, 202 deletions
diff --git a/gcc/ada/g-debpoo.adb b/gcc/ada/g-debpoo.adb
index 770f731aa1e..030a235e30f 100644
--- a/gcc/ada/g-debpoo.adb
+++ b/gcc/ada/g-debpoo.adb
@@ -53,12 +53,6 @@ package body GNAT.Debug_Pools is
-- and at the same time makes it easy to find the location of the extra
-- header allocated for each chunk.
- Initial_Memory_Size : constant Storage_Offset := 2 ** 26; -- 64 Mb
- -- Initial size of memory that the debug pool can handle. This is used to
- -- compute the size of the htable used to monitor the blocks, but this is
- -- dynamic and will grow as needed. Having a bigger size here means a
- -- longer setup time, but less time spent later on to grow the array.
-
Max_Ignored_Levels : constant Natural := 10;
-- Maximum number of levels that will be ignored in backtraces. This is so
-- that we still have enough significant levels in the tracebacks returned
@@ -211,64 +205,6 @@ package body GNAT.Debug_Pools is
-- multiple of default alignment + worst-case padding.
-----------------------
- -- Allocations table --
- -----------------------
-
- -- This table is indexed on addresses modulo Default_Alignment, and for
- -- each index it indicates whether that memory block is valid. Its behavior
- -- is similar to GNAT.Table, except that we need to pack the table to save
- -- space, so we cannot reuse GNAT.Table as is.
-
- -- This table is the reason why all alignments have to be forced to common
- -- value (Default_Alignment), so that this table can be kept to a
- -- reasonnable size.
-
- type Byte is mod 2 ** System.Storage_Unit;
-
- Big_Table_Size : constant Storage_Offset :=
- (Storage_Offset'Last - 1) / Default_Alignment;
- type Big_Table is array (0 .. Big_Table_Size) of Byte;
- -- A simple, flat-array type used to access memory bytes (see the comment
- -- for Valid_Blocks below).
- --
- -- It would be cleaner to represent this as a packed array of Boolean.
- -- However, we cannot specify pragma Pack for such an array, since the
- -- total size on a 64 bit machine would be too big (> Integer'Last).
- --
- -- Given an address, we know if it is under control of the debug pool if
- -- the byte at index:
- -- ((Address - Edata'Address) / Default_Alignment)
- -- / Storage_unit
- -- has the bit
- -- ((Address - Edata'Address) / Default_Alignment)
- -- mod Storage_Unit
- -- set to 1.
- --
- -- See the subprograms Is_Valid and Set_Valid for proper manipulation of
- -- this array.
-
- type Table_Ptr is access Big_Table;
- function To_Pointer is new Ada.Unchecked_Conversion
- (System.Address, Table_Ptr);
-
- Valid_Blocks : Table_Ptr := null;
- Valid_Blocks_Size : Storage_Offset := 0;
- -- These two variables represents a mapping of the currently allocated
- -- memory. Every time the pool works on an address, we first check that the
- -- index Address / Default_Alignment is True. If not, this means that this
- -- address is not under control of the debug pool and thus this is probably
- -- an invalid memory access (it could also be a general access type).
- --
- -- Note that in fact we never allocate the full size of Big_Table, only a
- -- slice big enough to manage the currently allocated memory.
-
- Edata : System.Address := System.Null_Address;
- -- Address in memory that matches the index 0 in Valid_Blocks. It is named
- -- after the symbol _edata, which, on most systems, indicate the lowest
- -- possible address returned by malloc. Unfortunately, this symbol doesn't
- -- exist on windows, so we cannot use it instead of this variable.
-
- -----------------------
-- Local subprograms --
-----------------------
@@ -297,16 +233,19 @@ package body GNAT.Debug_Pools is
-- addresses up to the first one in the range
-- Ignored_Frame_Start .. Ignored_Frame_End
- function Is_Valid (Storage : System.Address) return Boolean;
- pragma Inline (Is_Valid);
- -- Return True if Storage is an address that the debug pool has under its
- -- control.
+ package Validity is
+ function Is_Valid (Storage : System.Address) return Boolean;
+ pragma Inline (Is_Valid);
+ -- Return True if Storage is an address that the debug pool has under
+ -- its control.
- procedure Set_Valid (Storage : System.Address; Value : Boolean);
- pragma Inline (Set_Valid);
- -- Mark the address Storage as being under control of the memory pool (if
- -- Value is True), or not (if Value is False). This procedure will
- -- reallocate the table Valid_Blocks as needed.
+ procedure Set_Valid (Storage : System.Address; Value : Boolean);
+ pragma Inline (Set_Valid);
+ -- Mark the address Storage as being under control of the memory pool
+ -- (if Value is True), or not (if Value is False).
+ end Validity;
+
+ use Validity;
procedure Set_Dead_Beef
(Storage_Address : System.Address;
@@ -551,143 +490,129 @@ package body GNAT.Debug_Pools is
end Find_Or_Create_Traceback;
--------------
- -- Is_Valid --
+ -- Validity --
--------------
- function Is_Valid (Storage : System.Address) return Boolean is
+ package body Validity is
- -- We use the following constant declaration, instead of
- -- Offset : constant Storage_Offset :=
- -- (Storage - Edata) / Default_Alignment;
- -- See comments in Set_Valid for details.
+ -- The validity bits of the allocated blocks are kept in a has table.
+ -- Each component of the hash table contains the validity bits for a
+ -- 16 Mbyte memory chunk.
- Offset : constant Storage_Offset :=
- Storage_Offset ((To_Integer (Storage) - To_Integer (Edata)) /
- Default_Alignment);
+ -- The reason the validity bits are kept for chunks of memory rather
+ -- than in a big array is that on some 64 bit platforms, it may happen
+ -- that two chunk of allocated data are very far from each other.
- Bit : constant Byte := 2 ** Natural (Offset mod System.Storage_Unit);
+ Memory_Chunk_Size : constant Integer_Address := 2 ** 24; -- 16 MB
+ Validity_Divisor : constant := Default_Alignment * System.Storage_Unit;
- begin
- return (Storage mod Default_Alignment) = 0
- and then Offset >= 0
- and then Offset < Valid_Blocks_Size * Storage_Unit
- and then (Valid_Blocks (Offset / Storage_Unit) and Bit) /= 0;
- end Is_Valid;
+ Max_Validity_Byte_Index : constant :=
+ Memory_Chunk_Size / Validity_Divisor;
- ---------------
- -- Set_Valid --
- ---------------
+ subtype Validity_Byte_Index is Integer_Address
+ range 0 .. Max_Validity_Byte_Index - 1;
- procedure Set_Valid (Storage : System.Address; Value : Boolean) is
- Offset : Storage_Offset;
- Bit : Byte;
- Bytes : Storage_Offset;
- Tmp : constant Table_Ptr := Valid_Blocks;
+ type Byte is mod 2 ** System.Storage_Unit;
- Edata_Align : constant Storage_Offset :=
- Default_Alignment * Storage_Unit;
+ type Validity_Bits is array (Validity_Byte_Index) of Byte;
- procedure Memset (A : Address; C : Integer; N : size_t);
- pragma Import (C, Memset, "memset");
+ type Validity_Bits_Ref is access all Validity_Bits;
+ No_Validity_Bits : constant Validity_Bits_Ref := null;
- procedure Memmove (Dest, Src : Address; N : size_t);
- pragma Import (C, Memmove, "memmove");
+ Max_Header_Num : constant := 1023;
- begin
- -- Allocate, or reallocate, the valid blocks table as needed. We start
- -- with a size big enough to handle Initial_Memory_Size bytes of memory,
- -- to avoid too many reallocations. The table will typically be around
- -- 16Mb in that case, which is still small enough.
+ type Header_Num is range 0 .. Max_Header_Num - 1;
- if Valid_Blocks_Size = 0 then
- Valid_Blocks_Size := (Initial_Memory_Size / Default_Alignment)
- / Storage_Unit;
- Valid_Blocks := To_Pointer (Alloc (size_t (Valid_Blocks_Size)));
- Edata := Storage;
+ function Hash (F : Integer_Address) return Header_Num;
- -- Reset the memory using memset, which is much faster than the
- -- standard Ada code with "when others"
-
- Memset (Valid_Blocks.all'Address, 0, size_t (Valid_Blocks_Size));
- end if;
-
- -- First case : the new address is outside of the current scope of
- -- Valid_Blocks, before the current start address. We need to reallocate
- -- the table accordingly. This should be a rare occurence, since in most
- -- cases, the first allocation will also have the lowest address. But
- -- there is no garantee...
-
- if Storage < Edata then
-
- -- The difference between the new Edata and the current one must be
- -- a multiple of Default_Alignment * Storage_Unit, so that the bit
- -- representing an address in Valid_Blocks are kept the same.
-
- Offset := ((Edata - Storage) / Edata_Align + 1) * Edata_Align;
- Offset := Offset / Default_Alignment;
- Bytes := Offset / Storage_Unit;
- Valid_Blocks :=
- To_Pointer (Alloc (Size => size_t (Valid_Blocks_Size + Bytes)));
- Memmove (Dest => Valid_Blocks.all'Address + Bytes,
- Src => Tmp.all'Address,
- N => size_t (Valid_Blocks_Size));
- Memset (A => Valid_Blocks.all'Address,
- C => 0,
- N => size_t (Bytes));
- Free (Tmp.all'Address);
- Valid_Blocks_Size := Valid_Blocks_Size + Bytes;
-
- -- Take into the account the new start address
-
- Edata := Storage - Edata_Align + (Edata - Storage) mod Edata_Align;
- end if;
+ package Validy_Htable is new GNAT.HTable.Simple_HTable
+ (Header_Num => Header_Num,
+ Element => Validity_Bits_Ref,
+ No_Element => No_Validity_Bits,
+ Key => Integer_Address,
+ Hash => Hash,
+ Equal => "=");
+ -- Table to keep the validity bit blocks for the allocated data
- -- Second case : the new address is outside of the current scope of
- -- Valid_Blocks, so we have to grow the table as appropriate.
+ function To_Pointer is new Ada.Unchecked_Conversion
+ (System.Address, Validity_Bits_Ref);
- -- Note: it might seem more natural for the following statement to
- -- be written:
+ procedure Memset (A : Address; C : Integer; N : size_t);
+ pragma Import (C, Memset, "memset");
- -- Offset := (Storage - Edata) / Default_Alignment;
+ ----------
+ -- Hash --
+ ----------
- -- but that won't work since Storage_Offset is signed, and it is
- -- possible to subtract a small address from a large address and
- -- get a negative value. This may seem strange, but it is quite
- -- specifically allowed in the RM, and is what most implementations
- -- including GNAT actually do. Hence the conversion to Integer_Address
- -- which is a full range modular type, not subject to this glitch.
+ function Hash (F : Integer_Address) return Header_Num is
+ begin
+ return Header_Num (F mod Max_Header_Num);
+ end Hash;
+
+ --------------
+ -- Is_Valid --
+ --------------
+
+ function Is_Valid (Storage : System.Address) return Boolean is
+ Int_Storage : constant Integer_Address := To_Integer (Storage);
+ Block_Number : constant Integer_Address :=
+ Int_Storage / Memory_Chunk_Size;
+ Ptr : constant Validity_Bits_Ref :=
+ Validy_Htable.Get (Block_Number);
+ Offset : constant Integer_Address :=
+ (Int_Storage - (Block_Number * Memory_Chunk_Size)) /
+ Default_Alignment;
+ Bit : constant Byte :=
+ 2 ** Natural (Offset mod System.Storage_Unit);
+ begin
+ if Ptr = No_Validity_Bits then
+ return False;
+ else
+ return (Ptr (Offset / System.Storage_Unit) and Bit) /= 0;
+ end if;
+ end Is_Valid;
+
+ ---------------
+ -- Set_Valid --
+ ---------------
+
+ procedure Set_Valid (Storage : System.Address; Value : Boolean) is
+ Int_Storage : constant Integer_Address := To_Integer (Storage);
+ Block_Number : constant Integer_Address :=
+ Int_Storage / Memory_Chunk_Size;
+ Ptr : Validity_Bits_Ref := Validy_Htable.Get (Block_Number);
+ Offset : constant Integer_Address :=
+ (Int_Storage - (Block_Number * Memory_Chunk_Size)) /
+ Default_Alignment;
+ Bit : constant Byte :=
+ 2 ** Natural (Offset mod System.Storage_Unit);
- Offset := Storage_Offset ((To_Integer (Storage) - To_Integer (Edata)) /
- Default_Alignment);
+ begin
+ if Ptr = No_Validity_Bits then
- if Offset >= Valid_Blocks_Size * System.Storage_Unit then
- Bytes := Valid_Blocks_Size;
- loop
- Bytes := 2 * Bytes;
- exit when Offset <= Bytes * System.Storage_Unit;
- end loop;
+ -- First time in this memory area: allocate a new block and put
+ -- it in the table.
- Valid_Blocks := To_Pointer
- (Realloc (Ptr => Valid_Blocks.all'Address,
- Size => size_t (Bytes)));
- Memset
- (Valid_Blocks.all'Address + Valid_Blocks_Size,
- 0,
- size_t (Bytes - Valid_Blocks_Size));
- Valid_Blocks_Size := Bytes;
- end if;
+ if Value then
+ Ptr := To_Pointer (Alloc (size_t (Max_Validity_Byte_Index)));
+ Validy_Htable.Set (Block_Number, Ptr);
+ Memset (Ptr.all'Address, 0, size_t (Max_Validity_Byte_Index));
+ Ptr (Offset / System.Storage_Unit) := Bit;
+ end if;
- Bit := 2 ** Natural (Offset mod System.Storage_Unit);
- Bytes := Offset / Storage_Unit;
+ else
+ if Value then
+ Ptr (Offset / System.Storage_Unit) :=
+ Ptr (Offset / System.Storage_Unit) or Bit;
- -- Then set the value as valid
+ else
+ Ptr (Offset / System.Storage_Unit) :=
+ Ptr (Offset / System.Storage_Unit) and (not Bit);
+ end if;
+ end if;
+ end Set_Valid;
- if Value then
- Valid_Blocks (Bytes) := Valid_Blocks (Bytes) or Bit;
- else
- Valid_Blocks (Bytes) := Valid_Blocks (Bytes) and (not Bit);
- end if;
- end Set_Valid;
+ end Validity;
--------------
-- Allocate --
@@ -706,11 +631,10 @@ package body GNAT.Debug_Pools is
(1 .. Size_In_Storage_Elements + Minimum_Allocation);
type Ptr is access Local_Storage_Array;
- -- On some systems, we might want to physically protect pages
- -- against writing when they have been freed (of course, this is
- -- expensive in terms of wasted memory). To do that, all we should
- -- have to do it to set the size of this array to the page size.
- -- See mprotect().
+ -- On some systems, we might want to physically protect pages against
+ -- writing when they have been freed (of course, this is expensive in
+ -- terms of wasted memory). To do that, all we should have to do it to
+ -- set the size of this array to the page size. See mprotect().
P : Ptr;
@@ -723,10 +647,10 @@ package body GNAT.Debug_Pools is
-- If necessary, start physically releasing memory. The reason this is
-- done here, although Pool.Logically_Deallocated has not changed above,
- -- is so that we do this only after a series of deallocations (e.g a
- -- loop that deallocates a big array). If we were doing that in
- -- Deallocate, we might be physically freeing memory several times
- -- during the loop, which is expensive.
+ -- is so that we do this only after a series of deallocations (e.g loop
+ -- that deallocates a big array). If we were doing that in Deallocate,
+ -- we might be physically freeing memory several times during the loop,
+ -- which is expensive.
if Pool.Logically_Deallocated >
Byte_Count (Pool.Maximum_Logically_Freed_Memory)
@@ -764,8 +688,8 @@ package body GNAT.Debug_Pools is
Allocate_Label'Address, Code_Address_For_Allocate_End);
pragma Warnings (Off);
- -- Turn warning on alignment for convert call off. We know that in
- -- fact this conversion is safe since P itself is always aligned on
+ -- Turn warning on alignment for convert call off. We know that in fact
+ -- this conversion is safe since P itself is always aligned on
-- Default_Alignment.
Header_Of (Storage_Address).all :=
@@ -822,9 +746,9 @@ package body GNAT.Debug_Pools is
-- Allocate_End --
------------------
- -- DO NOT MOVE, this must be right after Allocate. This is similar to
- -- what is done in a-except, so that we can hide the traceback frames
- -- internal to this package
+ -- DO NOT MOVE, this must be right after Allocate. This is similar to what
+ -- is done in a-except, so that we can hide the traceback frames internal
+ -- to this package
procedure Allocate_End is
begin
@@ -946,7 +870,7 @@ package body GNAT.Debug_Pools is
Header := Header_Of (Tmp);
-- If we know, or at least assume, the block is no longer
- -- reference anywhere, we can free it physically.
+ -- referenced anywhere, we can free it physically.
if Ignore_Marks or else not Marked (Tmp) then
@@ -1043,6 +967,7 @@ package body GNAT.Debug_Pools is
-- Do not even attempt to mark blocks in use. That would
-- screw up the whole application, of course.
+
if Header.Block_Size < 0 then
Mark (Header, Pointed, In_Use => True);
end if;
@@ -1085,7 +1010,11 @@ package body GNAT.Debug_Pools is
Lock_Task.all;
if Pool.Advanced_Scanning then
- Reset_Marks; -- Reset the mark for each freed block
+
+ -- Reset the mark for each freed block
+
+ Reset_Marks;
+
Mark_Blocks;
end if;
@@ -1232,8 +1161,11 @@ package body GNAT.Debug_Pools is
--------------------
-- DO NOT MOVE, this must be right after Deallocate
+
-- See Allocate_End
+ -- This is making assumptions about code order that may be invalid ???
+
procedure Deallocate_End is
begin
<<Deallocate_End_Label>>
@@ -1301,8 +1233,11 @@ package body GNAT.Debug_Pools is
---------------------
-- DO NOT MOVE: this must be right after Dereference
+
-- See Allocate_End
+ -- This is making assumptions about code order that may be invalid ???
+
procedure Dereference_End is
begin
<<Dereference_End_Label>>
@@ -1651,6 +1586,8 @@ package body GNAT.Debug_Pools is
fclose (File);
end Dump_Gnatmem;
+-- Package initialization
+
begin
Allocate_End;
Deallocate_End;