The Task Stack Analysis Tool (TSAT) is used to determine appropriate
sizes for task stacks within an Ada application. It does this by
analysis of the Ada library containing the compiled application. The
user can apply the stack requirements reported by TSAT to the task
stack specification in
'Size clauses and in the Linker
TSAT consists of two parts: 1) Data Collection and 2) Analysis. Data
Collection occurs during compilation of the program to be analyzed.
The Data Collection phase occurs when the Ada compiler Code Generator
saves information about subprogram stack use in the Ada library. The
"atsat" tool implements the Analysis phase. It reads the
information saved by the compiler then analyzes it and reports the
To collect data, the
-k option must be used when compiling
the units to be analyzed. The compile must be done by a TSAT
instrumented compiler. Units that have not been compiled in this way
will be flagged by the analysis tool, but no stack information will be
known for them. The information is saved in the Ada sublibrary along
with the compiled unit.
% ada -v -k arkerb.ada TSAT Phase II Release 5/31/94 Copyright 1994, Alsys. All Rights Reserved RISCAda (tm) Development System for SPARCStations and Servers Ada Compiler for Embedded 68k Targets, Version 4.1c Copyright (c) 1992 TeleSoft. All rights reserved. ...[The banner message may be different.]
The stack use of each subprogram consist of four parts:
The analysis tool,
atsat, reports stack use for all tasks
in the units specified on the command line. These tasks may include:
The specified unit is typically the main unit of the program, but may be any compilation unit. For maximum information, all units within the extended family should have been compiled as described above in the "Data Collection" section. The extended family must be executable. That is, there should be no missing units, obsolete units or circular dependencies. This is the same requirement as needed to bind a program; if the program can be bound, it can be analyzed.
Atsat performs three categories of stack analysis: stack
-s), call tree (
-c), and recursion
-r). The user should select one or more of these
categories on the
atsat command line. If none of these
options is specified,
atsat only performs consistency
checks and reports missing subprograms.
atsat tool is of the form:
atsat [<options>] <compilation-unit>
-kor non-Ada subprograms.
<file>can be either a missing data file name or an input list file name that ends in
".ilf", which consists of one or more missing data file names, but only one per line. Default: No missing data supplied.
atsat analysis tool works in several steps:
-vis specified on the command line, a banner is printed. The library is opened. Any errors encountered are reported.
-eis not specified, only the given unit is searched for tasks. If
-eis specified, each of the units found in step 1 is analyzed for tasks and task types. If
-mis specified, the main task is included in further analysis. If
-vis specified on the command line, a message is printed for each unit processed. This step results in a list of tasks and task types to be analyzed in subsequent steps. This list is in no particular order.
atsatperforms the following analysis:
This call tree is actually a directed acyclic graph because some of the subprograms may be called from more than one location. The graph will not be strictly acyclic if recursion is present, but for analysis purposes, it is treated as acyclic.
This step prints a banner for the task. For example:
================= Analysis of task sec/unique_id.id_monitor_type =================
Atsattraverses the call tree and reports subprogram that have no stack size information. These are Ada units which have not been compiled with
-k, or non-Ada units. For example:
>>> Warning: lib/ar_activation.start_activation has no stack size informationThese messages should be corrected with data in the missing data file. If the messages are not corrected, the stack size analysis will not provide a complete picture of stack use.
-ris specified. Atsat traverses the call tree to find recursive call paths in the task. Each possible recursive path is printed. Each reported path contains and ends with the subprogram that represents the entry for the recursive path. The reported call paths are only potentially recursive. In general, it is not possible to determine by static analysis whether recursion actually occurs. These reported paths should be taken into consideration when considering total stack use. For analysis purposes,
atsatassumes the recursion does not actually occur. The numbers reported in the recursive part of the path represent the stack use for one recursion of the path.
>>> Information: Recursive calls: sec/main.main calls ->lib/secondary_memory_device_driver.create calls, 40, 40 lib/secondary_memory_device_driver.createIn this example the subprogram calls itself directly. The
"->"symbol indicates the beginning of the recursive path. The recursive path requires 40 bytes of stack space for each recursion. Recursive paths that involve longer paths before the recursion takes place, e.g., A calls B calls C calls A, are also reported. Note that all recursive paths are reported, but not all ways to get to the recursive path are reported. In the above example, there may be more call paths that lead to
Atsattraverses the call tree from the leaves (those subprograms which don't call other subprograms) upward. During this traversal, it finds the stack use of the largest call path that begins with this subprogram, i.e., the largest child subtree. It then reports two values for each subprogram: the stack requirements for this subprogram and the stack requirements for the longest call path that begins with this subprogram. For example:
>>> Information: Stack use: lib/calendar.time_of, 76, 128 sec/error_services.send_to_rs232.time_to_string, 276 = 252 + time <= 24?, 444
The first number, the stack requirements for this subprogram,
consists of parameter size plus return linkage size plus local
frame size as described above. If the subprogram has dynamic stack
use, this is shown by the unconstrained parameter and its size as
specified in the missing data file. In the above example, the
"time" is not specified in the missing data
file, so its size is displayed as
parameter size reported as some valve followed by
should be corrected by including size information in the missing
The second number, the stack requirements for the largest call path
that begins with this subprogram, represents the amount of
additional stack that may be required for the calls from this
subprogram. Thus, for the subprogram which is the body of the
task, the second number shows the stack requirement for the task.
This number can be compared against the stack size specified in the
'Size clause for the task.
-sis specified. For example:
>>> Information: Dynamic stack allocations: <Elaboration Procedure> sec/dynamic.dynamic.r, 32771 sec/dynamic.dynamic.a, 32765 sec/dynamic.dynamic.e, 4098 sec/dynamic.dynamic.v, 9 sec/dynamic.dynamic.b, 7
Atsattraverses the call tree to find the call path with the largest stack requirement. If the stack size for the task accommodates this call path, there will be sufficient stack allocated to the task (see below for additional considerations). For example:
>>> Information: Call chain with largest stack requirement: <unknown> calls, 0, 44 sec/main.main calls, 16, 44 lib/os_init.system_startup calls, 20, 28 lib/stp_gpc_eeprom.initialize calls, 4, 8 lib/gpc.id, 4, 4In this example,
<unknown>represents the unnamed subprogram which calls the main program.
Atsattraverses the call tree the list of subprograms that are called from each subprogram. A given subprogram will appear only once in the called subprogram list even if is called from more than one location, e.g., if subprogram
"B"will only appear once in the list of subprograms called from
"A". For example:
>>> Information: Call tree: sec/clock_support.clock_support -> lib/time.time -> lib/calendar.time_of lib/time.time -> lib/unsigned_numbers.unsigned_64_bits_of -> lib/the_real_time_clock.overflow_count -> lib/the_real_time_clock.clock lib/unsigned_numbers.unsigned_64_bits_of
Each subprogram is listed with the list of called subprograms
beneath it. Each of the called subprograms is preceded by the
"->" symbol. In the example above,
lib/unsigned_numbers.unsigned_64_bits_of, calls no
other subprograms so it is listed by itself with no called
subprograms beneath it.
The Missing Data File(s) are used to provide additional information to that saved in the Ada Library. This additional information includes:
atsatreports that a subprogram has no saved stack information, the reported stack analysis may not reflect actual total stack requirements. This may be the case with non-Ada subprograms or third party libraries. The
atsatmay be used to supply information for these subprograms.
This option specifies a file, or an input list of files, whose contents are a sequence of lines of the form:
<subprogram_name>, <stack_requirement> -> <called_subprogram_1> -> <called_subprogram_2>where
<subprogram_name>is the name of the missing Ada or non-Ada subprogram,
<stack_requirement>is the total stack requirement for this subprogram,
<called_subprogram_i>is an Ada or non-Ada subprogram called from this subprogram. There should be one called subprogram line (beginning with
"->") for each subprogram called from
<subprogram_name>. In this fashion Ada routines can call non-Ada routines and vice versa.
The subprogram name is case insensitive. Spaces, tabs, blank lines
and Ada style comments may be used for formatting.
Atsat will report a warning and ignore lines that are
in an incorrect format. The subprogram name specified should be
the same as the one reported as missing by
Atsat will ignore lines which define stack space for
subprograms that already have stack information in the library.
Atsat will not report subprograms as missing if they
are defined in this file.
Atsatwill warn of dynamically sized objects by reporting
<dynamic_object_name> <= "<maximum_size>?"in the stack size for subprograms which have dynamic stack requirements.
To correct this warning, insert a missing data file line of the form:
<dynamic_object_name>, <expected_maximum_size>In this line,
<dynamic_object_name>should be exactly the same as the name reported in the stack size,
<expected_maximum_size>is the size of
<dynamic_object_name>in bytes. Once the information is placed in the missing data file, the stack size report will contain
<dynamic_object_name> <= <expected_maximum_size>
For example, if
sec/blob_io.put, 32807 = 40 + sec/blob_io.put.item <= 32767?, 40and you know that
sec/blob_io.put.itemis an array that will be at most 50 bytes, put the line:
sec/blob_io.put.item, 50in the missing data file.
Atsatwill then report:
sec/blob_io.put, 90 = 40 + sec/blob_io.put.item <= 50, 90
Compiler generated objects have names of the form
nnnn (or possibly
nnnnn) is the source file line number which caused the
compiler generated object to be allocated. This object is
typically an unconstrained function result. Look at line
nnnn in the Ada source to determine what the maximum
function result size might be.
runtime.tsat includes stack size information
for the Ada runtime and pSOS kernel. This file is in missing data
file format. The user should use this file as a starting point for
the missing data file. Make a copy of
and add missing information for the application to this copy.
These are some issues to consider to determine appropriate values for stack sizes.
Atsathas no knowledge of which of several stacks (user, master, interrupt) may be in use at a given time. It is up to the user to apply the information from
atsatto the correct stack.
Atsatreports possible recursive paths and the stack space consumed by one recursion of the path. The user must decide if this recursive path applies to the maximum call chain of a particular task. If so, the given size must be multiplied by the number of expected recursions and added to the stack requirements.
cgs_exception_manageris most of the stack required to process exceptions. An additional small amount may be needed by the Board Support Package to process exceptions that originate as traps rather than calls.
'Sizerepresentation clause for tasks may need to allow more space than required for the stack. Task control blocks may be allocated from this space as well.
Last update 1 December 1995. Questions, comments to Clyde Roby (CRoby@IDA.Org)