1

I'm trying to put Adacore's Gem on dynamic stack size analysis into action on our project.

Our project uses a lot of different tasks and is killed using

procedure C_Exit (Status : Interfaces.C.int);
pragma Import (C, C_Exit, "exit");

This is rather equivalent to GNAT's specific solution suggested by Simon Wright which would lead to the MWE

with GNAT.OS_Lib;
procedure Main is

   task T is
      entry E (Size : Integer);
   end T;

   task body T is
   begin
      accept E (Size : Integer) do
         declare
            V : array (1 .. Size) of Integer := (others => 0);
         begin
            GNAT.OS_Lib.OS_Exit (0); -- this is the only difference to Adacores example
         end;
      end E;
   end T;

begin
   T.E (500_000);
end Main;

Binding this program with the -u10 option (using Gnat 19.1 and 20.1) won't output any report (while it does when not exiting).

Is there a solution to get the report without changing "too much" my existent code base?

Vser
  • 578
  • 4
  • 18

2 Answers2

2

The problem is that you need to run the program to completion in order to generate the report (as mentioned in de Gem):

The -bargs -u10 switch causes -u10 to be passed to the GNAT binder, which will allow up to ten tasks to be instrumented and will output their stack usage upon program completion.

When you call OS_Exit (or C_Exit), the program is mere aborted (instead of completed) as normal program termination / finalization is skipped:

   procedure OS_Exit (Status : Integer);
   pragma No_Return (OS_Exit);
   --  Exit to OS with given status code (program is terminated). Note that
   --  this is abrupt termination. All tasks are immediately terminated. There
   --  are no finalization or other Ada-specific cleanup actions performed. On
   --  systems with atexit handlers (such as Unix and Windows), atexit handlers
   --  are called.

I don't have any concrete solution here (maybe others have) other than to make sure not to call OS_Exit and to ensure that tasks are terminated properly. Below is the effect of OS_Exit on the code flow within the binder generated code:

b__main.adb (binder generated code, partial)

function main
  (argc : Integer;
   argv : System.Address;
   envp : System.Address)
   return Integer
is
  --  [...]
begin
   
   --  [...]

   Initialize_Stack_Analysis (10);  <<< Initialize stack analysis.
   Initialize (SEH'Address);
   adainit;
   Ada_Main_Program;                <<< Calls "your" Main program. ---+
   adafinal;                                                          |
   Output_Results;                  <<< Emit stack analysis report.   |
   Finalize;                                                          | 
   return (gnat_exit_status);                                    [ OS_Exit ] 
                                                                      |  
end; <----------------------------------------------------------------+  
DeeDee
  • 5,654
  • 7
  • 14
  • This is exactly what I've feared… So from the 4 options that were listed [here](https://stackoverflow.com/q/34765528/2622010), 1 is more or less what I've done on my project, 2 is not applicable as I'm not in ravenscar (and I'm using a lot of unbounded strings…), 3 is out per your answer. So I'm up to see if I have gnatstack available :) – Vser Jul 20 '20 at 09:55
1

How about -

  1. Fill stacks with known pattern
  2. Let C_Exit call - if available - kill() to trigger coredump
  3. Analyze stacks of coredump with debugger
Jesper Quorning
  • 156
  • 2
  • 5
  • How would you list all the tasks you have to abort then? I see no function for this in `Ada.Task_Identification`. Is there a solution to get this list dynamically to ensure no maintenance of the list is necessary? – Vser Jul 20 '20 at 14:10
  • What is an 'Abort-Program' exception? You can’t raise a specific exception in another task; you can abort the environment task using `Ada.Task_Identification.Environment_Task`, but note this [aborts the entire partition](http://www.ada-auth.org/standards/rm12_w_tc1/html/RM-C-7-1.html#p9) so there’s no way to take any further action. – Simon Wright Jul 20 '20 at 20:20
  • 1
    @jesper-quorning You totally changed your answer so my comment and @simon-wright are now obsoletes: sad, you could have added your update to your message :) As of the current state of the answer, I must say I do not understand it. I do not need a coredump as I can simply stop on the `exit` function with gdb. But what would you do with the stack next? Do you mean analyze the stack sizes? If so, this wouldn't give me a max size but the current size which could be absolutely not correlated to the max size. – Vser Jul 23 '20 at 12:38
  • @Vser You are right. I should have made another answer. Still learning. – Jesper Quorning Jul 24 '20 at 06:07
  • @Vser I would exercise the program and examine the stacks to see max usage. The parts still containing the pattern has not been used. – Jesper Quorning Jul 24 '20 at 06:13