summaryrefslogtreecommitdiff
path: root/gcc/ada/s-stausa.ads
blob: 436988eccf55c45d54cca9d47855713adb72f7c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
------------------------------------------------------------------------------
--                                                                          --
--                GNU ADA RUN-TIME LIBRARY (GNARL) COMPONENTS               --
--                                                                          --
--                   S Y S T E M - S T A C K _ U S A G E                    --
--                                                                          --
--                                 S p e c                                  --
--                                                                          --
--         Copyright (C) 2004-2007, Free Software Foundation, Inc.          --
--                                                                          --
-- GNARL is free software; you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
-- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
-- for  more details.  You should have  received  a copy of the GNU General --
-- Public License  distributed with GNARL; see file COPYING.  If not, write --
-- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
-- Boston, MA 02110-1301, USA.                                              --
--                                                                          --
-- As a special exception,  if other files  instantiate  generics from this --
-- unit, or you link  this unit with other files  to produce an executable, --
-- this  unit  does not  by itself cause  the resulting  executable  to  be --
-- covered  by the  GNU  General  Public  License.  This exception does not --
-- however invalidate  any other reasons why  the executable file  might be --
-- covered by the  GNU Public License.                                      --
--                                                                          --
-- GNARL was developed by the GNARL team at Florida State University.       --
-- Extensive contributions were provided by Ada Core Technologies, Inc.     --
--                                                                          --
------------------------------------------------------------------------------

with System;
with System.Storage_Elements;
with System.Address_To_Access_Conversions;
with Interfaces;

package System.Stack_Usage is
   pragma Preelaborate;

   package SSE renames System.Storage_Elements;

   Byte_Size : constant := 8;
   Unsigned_32_Size : constant := 4 * Byte_Size;

   --  The alignment clause seems dubious, what about architectures where
   --  the maximum alignment is less than 4???
   --  Anyway, why not use Interfaces.Unsigned_32???

   subtype Stack_Address is SSE.Integer_Address;
   --  Address on the stack

   function To_Stack_Address
     (Value : System.Address) return Stack_Address
      renames System.Storage_Elements.To_Integer;

   type Stack_Analyzer is private;
   --  Type of the stack analyzer tool. It is used to fill a portion of
   --  the stack with Pattern, and to compute the stack used after some
   --  execution.

   --  Usage:

   --  A typical use of the package is something like:

   --  A : Stack_Analyzer;

   --  task T is
   --     pragma Storage_Size (A_Storage_Size);
   --  end T;

   --  [...]

   --     Bottom_Of_Stack : aliased Integer;
   --     --  Bottom_Of_Stack'Address will be used as an approximation of
   --     --  the bottom of stack. A good practise is to avoid allocating
   --     --  other local variables on this stack, as it would degrade
   --     --  the quality of this approximation.

   --  begin
   --     Initialize_Analyzer (A,
   --                          "Task t",
   --                          A_Storage_Size - A_Guard,
   --                          A_Guard
   --                          To_Stack_Address (Bottom_Of_Stack'Address));
   --     Fill_Stack (A);
   --     Some_User_Code;
   --     Compute_Result (A);
   --     Report_Result (A);
   --  end T;

   --  Errors:
   --
   --  We are instrumenting the code to measure the stack used by the user
   --  code. This method has a number of systematic errors, but several
   --  methods can be used to evaluate or reduce those errors. Here are
   --  those errors and the strategy that we use to deal with them:

   --  Bottom offset:

   --     Description: The procedure used to fill the stack with a given
   --       pattern will itself have a stack frame. The value of the stack
   --       pointer in this procedure is, therefore, different from the value
   --       before the call to the instrumentation procedure.

   --     Strategy: The user of this package should measure the bottom of stack
   --       before the call to Fill_Stack and pass it in parameter.

   --  Instrumentation threshold at writing:

   --     Description: The procedure used to fill the stack with a given
   --       pattern will itself have a stack frame.  Therefore, it will
   --       fill the stack after this stack frame. This part of the stack will
   --       appear as used in the final measure.

   --     Strategy: As the user passes the value of the bottom of stack to
   --       the instrumentation to deal with the bottom offset error, and as as
   --       the instrumentation procedure knows where the pattern filling start
   --       on the stack, the difference between the two values is the minimum
   --       stack usage that the method can measure. If, when the results are
   --       computed, the pattern zone has been left untouched, we conclude
   --       that the stack usage is inferior to this minimum stack usage.

   --  Instrumentation threshold at reading:

   --    Description: The procedure used to read the stack at the end of the
   --      execution clobbers the stack by allocating its stack frame. If this
   --      stack frame is bigger than the total stack used by the user code at
   --      this point, it will increase the measured stack size.

   --    Strategy: We could augment this stack frame and see if it changes the
   --      measure. However, this error should be negligeable.

   --   Pattern zone overflow:

   --     Description: The stack grows outer than the topmost bound of the
   --       pattern zone. In that case, the topmost region modified in the
   --       pattern is not the maximum value of the stack pointer during the
   --       execution.

   --     Strategy: At the end of the execution, the difference between the
   --       topmost memory region modified in the pattern zone and the
   --       topmost bound of the pattern zone can be understood as the
   --       biggest allocation that the method could have detect, provided
   --       that there is no "Untouched allocated zone" error and no "Pattern
   --       usage in user code" error. If no object in the user code is likely
   --       to have this size, this is not likely to happen.

   --   Pattern usage in user code:

   --     Description: The pattern can be found in the object of the user code.
   --       Therefore, the address space where this object has been allocated
   --       will appear as untouched.

   --     Strategy: Choose a pattern that is uncommon. 16#0000_0000# is the
   --       worst choice; 16#DEAD_BEEF# can be a good one. A good choice is an
   --       address which is not a multiple of 2, and which is not in the
   --       target address space. You can also change the pattern to see if it
   --       changes the measure. Note that this error *very* rarely influence
   --       the measure of the total stack usage: to have some influence, the
   --       pattern has to be used in the object that has been allocated on the
   --       topmost address of the used stack.

   --   Stack overflow:

   --     Description: The pattern zone does not fit on the stack. This may
   --       lead to an erroneous execution.

   --    Strategy: Specify a storage size that is bigger than the size of the
   --      pattern. 2 times bigger should be enough.

   --   Augmentation of the user stack frames:

   --     Description: The use of instrumentation object or procedure may
   --       augment the stack frame of the caller.

   --     Strategy: Do *not* inline the instrumentation procedures. Do *not*
   --       allocate the Stack_Analyzer object on the stack.

   --   Untouched allocated zone:

   --     Description: The user code may allocate objects that it will never
   --       touch. In that case, the pattern will not be changed.

   --     Strategy: There are no way to detect this error. Fortunately, this
   --       error is really rare, and it is most probably a bug in the user
   --       code, e.g. some uninitialized variable. It is (most of the time)
   --       harmless: it influences the measure only if the untouched allocated
   --       zone happens to be located at the topmost value of the stack
   --       pointer for the whole execution.

   procedure Initialize (Buffer_Size : Natural);
   pragma Export (C, Initialize, "__gnat_stack_usage_initialize");
   --  Initializes the size of the buffer that stores the results. Only the
   --  first Buffer_Size results are stored. Any results that do not fit in
   --  this buffer will be displayed on the fly.

   procedure Fill_Stack (Analyzer : in out Stack_Analyzer);
   --  Fill an area of the stack with the pattern Analyzer.Pattern. The size
   --  of this area is Analyzer.Size. After the call to this procedure,
   --  the memory will look like that:
   --
   --                                                             Stack growing
   --  ----------------------------------------------------------------------->
   --  |<---------------------->|<----------------------------------->|
   --  |  Stack frame           | Memory filled with Analyzer.Pattern |
   --  |  of Fill_Stack         |                                     |
   --  |  (deallocated at       |                                     |
   --  |  the end of the call)  |                                     |
   --  ^                        |                                     |
   --  Analyzer.Bottom_Of_Stack ^                                     |
   --                    Analyzer.Bottom_Pattern_Mark                 ^
   --                                            Analyzer.Top_Pattern_Mark

   procedure Initialize_Analyzer
     (Analyzer       : in out Stack_Analyzer;
      Task_Name      : String;
      Size           : Natural;
      Overflow_Guard : Natural;
      Bottom         : Stack_Address;
      Pattern        : Interfaces.Unsigned_32 := 16#DEAD_BEEF#);
   --  Should be called before any use of a Stack_Analyzer, to initialize it.
   --  Size is the size of the pattern zone. Bottom should be a close
   --  approximation of the caller base frame address.

   Is_Enabled : Boolean := False;
   --  When this flag is true, then stack analysis is enabled

   procedure Compute_Result (Analyzer : in out Stack_Analyzer);
   --  Read the patern zone and deduce the stack usage. It should be called
   --  from the same frame as Fill_Stack. If Analyzer.Probe is not null, an
   --  array of Unsigned_32 with Analyzer.Probe elements is allocated on
   --  Compute_Result's stack frame. Probe can be used to detect  the error:
   --  "instrumentation threshold at reading". See above. After the call
   --  to this procedure, the memory will look like:
   --
   --                                                             Stack growing
   --  ----------------------------------------------------------------------->
   --  |<---------------------->|<-------------->|<--------->|<--------->|
   --  |  Stack frame           | Array of       | used      |  Memory   |
   --  |  of Compute_Result     | Analyzer.Probe | during    |   filled  |
   --  |  (deallocated at       | elements       |  the      |    with   |
   --  |  the end of the call)  |                | execution |  pattern  |
   --  |                        ^                |           |           |
   --  |                   Bottom_Pattern_Mark   |           |           |
   --  |                                                     |           |
   --  |<---------------------------------------------------->           |
   --                  Stack used                                        ^
   --                                                     Top_Pattern_Mark

   procedure Report_Result (Analyzer : Stack_Analyzer);
   --  Store the results of the computation in memory, at the address
   --  corresponding to the symbol __gnat_stack_usage_results. This is not
   --  done inside Compute_Resuls in order to use as less stack as possible
   --  within a task.

   procedure Output_Results;
   --  Print the results computed so far on the standard output. Should be
   --  called when all tasks are dead.

   pragma Export (C, Output_Results, "__gnat_stack_usage_output_results");

private

   Task_Name_Length : constant := 32;
   --  The maximum length of task name displayed.
   --  ??? Consider merging this variable with Max_Task_Image_Length.

   package Unsigned_32_Addr is
     new System.Address_To_Access_Conversions (Interfaces.Unsigned_32);

   type Stack_Analyzer is record
      Task_Name : String (1 .. Task_Name_Length);
      --  Name of the task

      Size : Natural;
      --  Size of the pattern zone

      Pattern : Interfaces.Unsigned_32;
      --  Pattern used to recognize untouched memory

      Bottom_Pattern_Mark : Stack_Address;
      --  Bound of the pattern area on the stack clostest to the bottom

      Top_Pattern_Mark : Stack_Address;
      --  Topmost bound of the pattern area on the stack

      Topmost_Touched_Mark : Stack_Address;
      --  Topmost address of the pattern area whose value it is pointing
      --  at has been modified during execution. If the systematic error are
      --  compensated, it is the topmost value of the stack pointer during
      --  the execution.

      Bottom_Of_Stack : Stack_Address;
      --  Address of the bottom of the stack, as given by the caller of
      --  Initialize_Analyzer.

      Array_Address : System.Address;
      --  Address of the array of Unsigned_32 that represents the pattern zone

      First_Is_Topmost : Boolean;
      --  Set to true if the first element of the array of Unsigned_32 that
      --  represents the pattern zone is at the topmost address of the
      --  pattern zone; false if it is the bottommost address.

      Result_Id : Positive;
      --  Id of the result. If less than value given to gnatbind -u corresponds
      --  to the location in the result array of result for the current task.

      Overflow_Guard : Natural;
      --  The amount of bytes that won't be analyzed in order to prevent
      --  writing out of the stack
   end record;

   Environment_Task_Analyzer : Stack_Analyzer;

   Compute_Environment_Task  : Boolean;

   type Task_Result is record
      Task_Name      : String (1 .. Task_Name_Length);
      Measure        : Natural;
      Max_Size       : Natural;
      Overflow_Guard : Natural;
   end record;

   type Result_Array_Type is array (Positive range <>) of Task_Result;
   type Result_Array_Ptr is access all Result_Array_Type;

   Result_Array : Result_Array_Ptr;
   pragma Export (C, Result_Array, "__gnat_stack_usage_results");
   --  Exported in order to have an easy accessible symbol in when debugging

   Next_Id : Positive := 1;
   --  Id of the next stack analyzer

   function Stack_Size
     (SP_Low  : Stack_Address;
      SP_High : Stack_Address) return Natural;
   pragma Inline (Stack_Size);
   --  Return the size of a portion of stack delimeted by SP_High and SP_Low
   --  (), i.e. the difference between SP_High and SP_Low. The storage element
   --  pointed by SP_Low is not included in the size. Inlined to reduce the
   --  size of the stack used by the instrumentation code.

end System.Stack_Usage;