1------------------------------------------------------------------------------ 2-- -- 3-- GNU ADA RUN-TIME LIBRARY (GNARL) COMPONENTS -- 4-- -- 5-- S Y S T E M - S T A C K _ U S A G E -- 6-- -- 7-- S p e c -- 8-- -- 9-- Copyright (C) 2004-2020, Free Software Foundation, Inc. -- 10-- -- 11-- GNARL is free software; you can redistribute it and/or modify it under -- 12-- terms of the GNU General Public License as published by the Free Soft- -- 13-- ware Foundation; either version 3, or (at your option) any later ver- -- 14-- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- 15-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- 16-- or FITNESS FOR A PARTICULAR PURPOSE. -- 17-- -- 18-- As a special exception under Section 7 of GPL version 3, you are granted -- 19-- additional permissions described in the GCC Runtime Library Exception, -- 20-- version 3.1, as published by the Free Software Foundation. -- 21-- -- 22-- You should have received a copy of the GNU General Public License and -- 23-- a copy of the GCC Runtime Library Exception along with this program; -- 24-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- 25-- <http://www.gnu.org/licenses/>. -- 26-- -- 27-- GNARL was developed by the GNARL team at Florida State University. -- 28-- Extensive contributions were provided by Ada Core Technologies, Inc. -- 29-- -- 30------------------------------------------------------------------------------ 31 32with System; 33with System.Storage_Elements; 34with System.Address_To_Access_Conversions; 35with Interfaces; 36 37package System.Stack_Usage is 38 pragma Preelaborate; 39 40 package SSE renames System.Storage_Elements; 41 42 subtype Stack_Address is SSE.Integer_Address; 43 -- Address on the stack 44 45 function To_Stack_Address 46 (Value : System.Address) return Stack_Address 47 renames System.Storage_Elements.To_Integer; 48 49 Task_Name_Length : constant := 32; 50 -- The maximum length of task name displayed. 51 -- ??? Consider merging this variable with Max_Task_Image_Length. 52 53 type Task_Result is record 54 Task_Name : String (1 .. Task_Name_Length); 55 56 Value : Natural; 57 -- Amount of stack used. The value is calculated on the basis of the 58 -- mechanism used by GNAT to allocate it, and it is NOT a precise value. 59 60 Stack_Size : Natural; 61 -- Size of the stack 62 end record; 63 64 type Result_Array_Type is array (Positive range <>) of Task_Result; 65 66 type Stack_Analyzer is private; 67 -- Type of the stack analyzer tool. It is used to fill a portion of the 68 -- stack with Pattern, and to compute the stack used after some execution. 69 70 -- Note that Fill_Stack writes data past the current top of the stack 71 -- (i.e. at addresses less than the stack pointer register, assuming the 72 -- stack grows downward). Therefore, this package is incompatible with 73 -- tools like Valgrind and DrMemory. 74 75 -- Usage: 76 77 -- A typical use of the package is something like: 78 79 -- A : Stack_Analyzer; 80 81 -- task T is 82 -- pragma Storage_Size (A_Storage_Size); 83 -- end T; 84 85 -- [...] 86 87 -- Bottom_Of_Stack : aliased Integer; 88 -- -- Bottom_Of_Stack'Address will be used as an approximation of 89 -- -- the bottom of stack. A good practise is to avoid allocating 90 -- -- other local variables on this stack, as it would degrade 91 -- -- the quality of this approximation. 92 93 -- begin 94 -- Initialize_Analyzer (A, 95 -- "Task t", 96 -- A_Storage_Size, 97 -- 0, 98 -- A_Storage_Size - A_Guard, 99 -- To_Stack_Address (Bottom_Of_Stack'Address)); 100 -- Fill_Stack (A); 101 -- Some_User_Code; 102 -- Compute_Result (A); 103 -- Report_Result (A); 104 -- end T; 105 106 -- Errors: 107 -- 108 -- We are instrumenting the code to measure the stack used by the user 109 -- code. This method has a number of systematic errors, but several methods 110 -- can be used to evaluate or reduce those errors. Here are those errors 111 -- and the strategy that we use to deal with them: 112 113 -- Bottom offset: 114 115 -- Description: The procedure used to fill the stack with a given 116 -- pattern will itself have a stack frame. The value of the stack 117 -- pointer in this procedure is, therefore, different from the value 118 -- before the call to the instrumentation procedure. 119 120 -- Strategy: The user of this package should measure the bottom of stack 121 -- before the call to Fill_Stack and pass it in parameter. The impact 122 -- is very minor unless the stack used is very small, but in this case 123 -- you aren't very interested by the figure. 124 125 -- Instrumentation threshold at writing: 126 127 -- Description: The procedure used to fill the stack with a given 128 -- pattern will itself have a stack frame. Therefore, it will 129 -- fill the stack after this stack frame. This part of the stack will 130 -- appear as used in the final measure. 131 132 -- Strategy: As the user passes the value of the bottom of stack to 133 -- the instrumentation to deal with the bottom offset error, and as 134 -- the instrumentation procedure knows where the pattern filling start 135 -- on the stack, the difference between the two values is the minimum 136 -- stack usage that the method can measure. If, when the results are 137 -- computed, the pattern zone has been left untouched, we conclude 138 -- that the stack usage is inferior to this minimum stack usage. 139 140 -- Instrumentation threshold at reading: 141 142 -- Description: The procedure used to read the stack at the end of the 143 -- execution clobbers the stack by allocating its stack frame. If this 144 -- stack frame is bigger than the total stack used by the user code at 145 -- this point, it will increase the measured stack size. 146 147 -- Strategy: We could augment this stack frame and see if it changes the 148 -- measure. However, this error should be negligible. 149 150 -- Pattern zone overflow: 151 152 -- Description: The stack grows outer than the topmost bound of the 153 -- pattern zone. In that case, the topmost region modified in the 154 -- pattern is not the maximum value of the stack pointer during the 155 -- execution. 156 157 -- Strategy: At the end of the execution, the difference between the 158 -- topmost memory region modified in the pattern zone and the 159 -- topmost bound of the pattern zone can be understood as the 160 -- biggest allocation that the method could have detect, provided 161 -- that there is no "Untouched allocated zone" error and no "Pattern 162 -- usage in user code" error. If no object in the user code is likely 163 -- to have this size, this is not likely to happen. 164 165 -- Pattern usage in user code: 166 167 -- Description: The pattern can be found in the object of the user code. 168 -- Therefore, the address space where this object has been allocated 169 -- will appear as untouched. 170 171 -- Strategy: Choose a pattern that is uncommon. 16#0000_0000# is the 172 -- worst choice; 16#DEAD_BEEF# can be a good one. A good choice is an 173 -- address which is not a multiple of 2, and which is not in the 174 -- target address space. You can also change the pattern to see if it 175 -- changes the measure. Note that this error *very* rarely influence 176 -- the measure of the total stack usage: to have some influence, the 177 -- pattern has to be used in the object that has been allocated on the 178 -- topmost address of the used stack. 179 180 -- Stack overflow: 181 182 -- Description: The pattern zone does not fit on the stack. This may 183 -- lead to an erroneous execution. 184 185 -- Strategy: Specify a storage size that is bigger than the size of the 186 -- pattern. 2 times bigger should be enough. 187 188 -- Augmentation of the user stack frames: 189 190 -- Description: The use of instrumentation object or procedure may 191 -- augment the stack frame of the caller. 192 193 -- Strategy: Do *not* inline the instrumentation procedures. Do *not* 194 -- allocate the Stack_Analyzer object on the stack. 195 196 -- Untouched allocated zone: 197 198 -- Description: The user code may allocate objects that it will never 199 -- touch. In that case, the pattern will not be changed. 200 201 -- Strategy: There are no way to detect this error. Fortunately, this 202 -- error is really rare, and it is most probably a bug in the user 203 -- code, e.g. some uninitialized variable. It is (most of the time) 204 -- harmless: it influences the measure only if the untouched allocated 205 -- zone happens to be located at the topmost value of the stack 206 -- pointer for the whole execution. 207 208 procedure Initialize (Buffer_Size : Natural); 209 pragma Export (C, Initialize, "__gnat_stack_usage_initialize"); 210 -- Initializes the size of the buffer that stores the results. Only the 211 -- first Buffer_Size results are stored. Any results that do not fit in 212 -- this buffer will be displayed on the fly. 213 214 procedure Fill_Stack (Analyzer : in out Stack_Analyzer); 215 -- Fill an area of the stack with the pattern Analyzer.Pattern. The size 216 -- of this area is Analyzer.Size. After the call to this procedure, 217 -- the memory will look like that: 218 -- 219 -- Stack growing 220 -- ----------------------------------------------------------------------> 221 -- |<--------------------->|<----------------------------------->| 222 -- | Stack frames to | Memory filled with Analyzer.Pattern | 223 -- | Fill_Stack | | 224 -- ^ | ^ 225 -- Analyzer.Stack_Base | Analyzer.Pattern_Limit 226 -- ^ 227 -- Analyzer.Pattern_Limit +/- Analyzer.Pattern_Size 228 -- 229 230 procedure Initialize_Analyzer 231 (Analyzer : in out Stack_Analyzer; 232 Task_Name : String; 233 Stack_Size : Natural; 234 Stack_Base : Stack_Address; 235 Pattern_Size : Natural; 236 Pattern : Interfaces.Unsigned_32 := 16#DEAD_BEEF#); 237 -- Should be called before any use of a Stack_Analyzer, to initialize it. 238 -- Max_Pattern_Size is the size of the pattern zone, might be smaller than 239 -- the full stack size Stack_Size in order to take into account e.g. the 240 -- secondary stack and a guard against overflow. The actual size taken 241 -- will be readjusted with data already used at the time the stack is 242 -- actually filled. 243 244 Is_Enabled : Boolean := False; 245 -- When this flag is true, then stack analysis is enabled 246 247 procedure Compute_Result (Analyzer : in out Stack_Analyzer); 248 -- Read the pattern zone and deduce the stack usage. It should be called 249 -- from the same frame as Fill_Stack. If Analyzer.Probe is not null, an 250 -- array of Unsigned_32 with Analyzer.Probe elements is allocated on 251 -- Compute_Result's stack frame. Probe can be used to detect the error: 252 -- "instrumentation threshold at reading". See above. After the call 253 -- to this procedure, the memory will look like: 254 -- 255 -- Stack growing 256 -- -----------------------------------------------------------------------> 257 -- |<---------------------->|<-------------->|<--------->|<--------->| 258 -- | Stack frames | Array of | used | Memory | 259 -- | to Compute_Result | Analyzer.Probe | during | filled | 260 -- | | elements | the | with | 261 -- | | | execution | pattern | 262 -- | | | 263 -- |<----------------------------------------------------> | 264 -- Stack used ^ 265 -- Pattern_Limit 266 267 procedure Report_Result (Analyzer : Stack_Analyzer); 268 -- Store the results of the computation in memory, at the address 269 -- corresponding to the symbol __gnat_stack_usage_results. This is not 270 -- done inside Compute_Result in order to use as less stack as possible 271 -- within a task. 272 273 procedure Output_Results; 274 -- Print the results computed so far on the standard output. Should be 275 -- called when all tasks are dead. 276 277 pragma Export (C, Output_Results, "__gnat_stack_usage_output_results"); 278 279private 280 281 package Unsigned_32_Addr is 282 new System.Address_To_Access_Conversions (Interfaces.Unsigned_32); 283 284 subtype Pattern_Type is Interfaces.Unsigned_32; 285 Bytes_Per_Pattern : constant := Pattern_Type'Object_Size / Storage_Unit; 286 287 type Stack_Analyzer is record 288 Task_Name : String (1 .. Task_Name_Length); 289 -- Name of the task 290 291 Stack_Base : Stack_Address; 292 -- Address of the base of the stack, as given by the caller of 293 -- Initialize_Analyzer. 294 295 Stack_Size : Natural; 296 -- Entire size of the analyzed stack 297 298 Pattern_Size : Natural; 299 -- Size of the pattern zone 300 301 Pattern : Pattern_Type; 302 -- Pattern used to recognize untouched memory 303 304 Pattern_Limit : Stack_Address; 305 -- Bound of the pattern area farthest to the base 306 307 Topmost_Touched_Mark : Stack_Address; 308 -- Topmost address of the pattern area whose value it is pointing 309 -- at has been modified during execution. If the systematic error are 310 -- compensated, it is the topmost value of the stack pointer during 311 -- the execution. 312 313 Pattern_Overlay_Address : System.Address; 314 -- Address of the stack abstraction object we overlay over a 315 -- task's real stack, typically a pattern-initialized array. 316 317 Result_Id : Positive; 318 -- Id of the result. If less than value given to gnatbind -u corresponds 319 -- to the location in the result array of result for the current task. 320 end record; 321 322 Environment_Task_Analyzer : Stack_Analyzer; 323 324 Compute_Environment_Task : Boolean; 325 326 type Result_Array_Ptr is access all Result_Array_Type; 327 328 Result_Array : Result_Array_Ptr; 329 pragma Export (C, Result_Array, "__gnat_stack_usage_results"); 330 -- Exported in order to have an easy accessible symbol in when debugging 331 332 Next_Id : Positive := 1; 333 -- Id of the next stack analyzer 334 335 function Stack_Size 336 (SP_Low : Stack_Address; 337 SP_High : Stack_Address) return Natural; 338 pragma Inline (Stack_Size); 339 -- Return the size of a portion of stack delimited by SP_High and SP_Low 340 -- (), i.e. the difference between SP_High and SP_Low. The storage element 341 -- pointed by SP_Low is not included in the size. Inlined to reduce the 342 -- size of the stack used by the instrumentation code. 343 344end System.Stack_Usage; 345