!standard D.14(9/2) 10-10-07 AI05-0170-1/05 !standard D.14(11/2) !standard D.14(13/2) !standard D.14(18/2) !standard D.14.3(0) !class Amendment 09-10-22 !status Amendment 2012 10-08-09 !status ARG Approved 10-0-0 10-06-18 !status work item 09-10-22 !status received 09-10-22 !priority Medium !difficulty Easy !subject Monitoring the time spent in Interrupt Handlers !summary Facilities are added to enable the time spent in interrupt handlers to be accounted for separately from time spent in tasks. !problem Ada 2005 defines execution-time clocks to monitor execution times and execution-time timers and group budgets to trigger actions when the execution time of a task or a task group reaches a specific value. This functionality is very useful to detect timing violations as well as to implement advanced scheduling policies such as those making resource reservations. The Ada standard leaves as "implementation defined" the way in which the execution time of interrupt handlers is accounted for: "It is implementation defined which task, if any, is charged the execution time that is consumed by interrupt handlers and run-time services on behalf of the system." (RM D.14 (11/2)) A common and simple implementation will charge the time consumed by the interrupt handlers to the task executing when the interrupt is generated. This is done under the assumption that the effect of interrupt handlers on the execution time clocks is negligible since the interrupt handlers are usually very sort pieces of code. However, in real-time systems that undertake an intensive use of interrupts this assumption may not be realistic. For example, Ada 2005 introduced timed events that can execute handlers in interrupt context. The facility is convenient and has low overheads, and therefore programmers are tempted to put more code into these handlers. As is shown in two papers at the 14th IRTAW it is possible with moderate effort to monitor and manage the execution time of interrupt handlers separately from the execution time of the tasks. With this modification Execution Time Clocks provide a more realistic value of the time a task has executed and, what is more important, execution time overruns detected using Execution Time Timers and Group Execution Time Budgets are more accurate. Therefore, the proposal rewords the definition of execution time to make it clear that the execution time of interrupts can be accounted for separately. If execution time of interrupts is monitored separately from that of the tasks, it is easy to make the accounting of these execution times available to the application. This information is useful to real-time analysis; therefore this proposal adds facilities to allow the application to obtain the execution time of interrupts. !proposal (See wording.) !wording Add to package Ada.Execution_Time after D.14(9/2): Interrupt_Clocks_Supported : constant Boolean := ; Separate_Interrupt_Clocks_Supported : constant Boolean := ; function Clock_For_Interrupts return CPU_Time; Replace D.14(11/2) with: The execution time or CPU time of a given task is defined as the time spent by the system executing that task, including the time spent executing run-time or system services on its behalf. The mechanism used to measure execution time is implementation defined. The Boolean constant Interrupt_Clocks_Supported is set to True if the implementation separately accounts for the execution time of interrupt handlers. If it is set to False it is implementation defined which task, if any, is charged the execution time that is consumed by interrupt handlers. The Boolean constant Separate_Interrupt_Clocks_Supported is set to True if the implementation separately accounts for the execution time of individual interrupt handlers. Add to end of D.14(13/2): The execution time value for the function Clock_For_Interrupts is initialized to zero. Add to after D.14(18/2): The function Clock_For_Interrupts returns the total cumulative time spent executing within all interrupt handlers. This time is not allocated to any task execution time clock. If Interrupt_Clocks_Supported is set to False the function raises Program_Error. Add a new D.14.3 Execution Time of Interrupt Handlers This clause describes a language-defined package to measure the execution time of interrupt handlers. Static Semantics The following language-defined library package exists: with Ada.Interrupts; package Ada.Execution_Time.Interrupts is function Clock (I : Ada.Interrupts.Interrupt_ID) return CPU_Time; function Supported (I : Ada.Interrupts.Interrupt_ID) return Boolean; end Ada.Execution_Time.Interrupts; The execution time or CPU time of a given interrupt I is defined as the time spent by the system executing interrupt handlers identified by I, including the time spent executing run-time or system services on its behalf. The mechanism used to measure execution time is implementation defined. Time spent executing interrupt handlers is distinct from time spent executing any task. For each interrupt, the execution time value is initially set to zero. Dynamic Semantics The function Clock returns the current cumulative execution time of the interrupt identified by I. If Separate_Interrupt_Clocks_Supported is set to False the function raises Program_Error. The function Supported returns True if the implementation is monitoring the execution time of the interrupt identified by I. Otherwise it returns False. For any interrupt_Id I for which Supported(I) returns False, the function Clock(I) will return a value equal to Ada.Execution_Time.Time_Of(0). !discussion This AI allows the measurement of the execution time of interrupt handlers separately from the execution time of user tasks. Doing so can significantly enhance the accuracy of the execution time clocks and timers of user tasks, because they are not arbitrarily charged with the execution times of interrupt service handlers. Allowing separate measurement of the execution time of interrupt handlers does not require the addition of any new API. However, once the interrupt execution time is measured, it ought to be made available to Ada applications, so new facilities are needed for that purpose. Since all this functionality may not be available on all platforms, we also provide the ability to test whether it is present. In [1] and [2] the overheads incurred by the implementation of these new services have been measured, and found that they are very low in comparison with the execution times of common interrupt handlers. Regarding the proposed API, it would be possible to define a Task_Id for pseudo tasks representing interrupt handlers, but this would create conflicts because they are not normal Ada tasks. Services such as Set_Priority would need to treat them as a special case, adding the need for new checks in potentially many places. We believe that using Interrupt_ID for identifying the execution time clocks is compatible with the proposed approach for attaching interrupts to a specific processor in multiprocessor systems. It seems that using pseudo-task IDs instead would require defining these for each processor, which would add complexity to the API. The new package is always present (even though its operations may just raise Program_Error), in order that programs can be written that depend on its services if the Separate_Interrupt_Clocks_Supported value is True, and falls back to some other technique if it is not supported. Such a program will need to with the new package, and that requires it exists even if it is not usefully implemented. The third part of the original proposal (to allow notification of execution time overruns in interrupt handlers) was dropped, as it seems to add overhead to interrupt handlers even when not used. The original IRTAW Proposal follows: This proposal contains three parts, listed in order of importance. Each one is based on the previous one and adds new services that are useful to real-time applications. Part 1. Accounting of execution time of interrupts -------------------------------------------------- Add the following "Implementation Advice" in RM D.14, "Execution Time" When appropriate, the execution time consumed by interrupt handlers and run-time services on behalf of the system should not be charged to the execution time of application tasks. Part 2. Interrupt execution time clocks --------------------------------------- Add the following implementation advice in RM D.14, "Execution Time" If the execution time consumed by interrupt handlers is accounted for separately from the execution time of application tasks, the time consumed should be made available to the application by means of the following language-defined package: with Ada.Interrupts; package Ada.Execution_Time.Interrupts is function Clock (I : Ada.Interrupts.Interrupt_ID) return CPU_Time; private . . . end Ada.Execution_Time.Interrupts; The Ada.Execution_Time.Interrupts package defines one execution-time clock for each Interrupt_ID. The Clock function returns the execution time spent by interrupt handlers while handling the interrupt specified by I. Part 3. Interrupt execution time timers --------------------------------------- Add the following implementation advice in RM D.14.1, "Execution Time Timers": If the execution time consumed by interrupt handlers is accounted for separately from the execution time of application tasks, and if package Ada.Execution_Time.Interrupts is implemented, execution time timers based on the execution time of interrupt handlers should be made available to the application by means of the following language-defined package: with Ada.Interrupts; package Ada.Execution_Time.Timers.Interrupts is type Timer ( I : Ada.Interrupts.Interrupt_ID) is new Ada.Execution_Time.Timers.Timer (Ada.Task_Identification.Null_Task_Id'Access ) with private; private . . . end Ada.Execution_Time.Timers.Interrupts; The Ada.Execution_Time.Timers.Interrupts defines execution time timers based on the execution-time clocks defined in package Ada.Execution_Time.Interrupts. The Timer type extends the Timer type defined in the Timers package and inherits and/or overrides all of its primitive operations, including the ability to set handlers that will be executed if the execution-time clock associated with the interrupt identified by I reaches a specific value or consumes a specified interval. The Set_Handler procedures have the same effect as for regular execution time timers except that their expiration is associated with the execution time of the interrupt handlers that are handling the interrupt identified by I. Note that according to the scheduling rules for interrupts, the handler of an interrupt execution-time timer may not be able to execute until the interrupt handler that triggers it (and possibly other interrupt handlers) has completed its own execution. Implementation Permissions: Implementations may limit the number of timers that can be defined for each interrupt Id. If this limit is exceeded then Timer_Resource_Error is raised. Note: This proposal uses constant Null_Task_Id as the Task_ID discriminant, since a task Id is not needed for interrupt execution time timers. This would require making the Null_Task_Id constant "aliased" in the Ada.Task_Identification package. References ---------- [1] Kristoffer Nyborg Gregertsen, Amund Skavhaug "Execution-time control for interrupt handling". 14th International Real-Time Ada Workshop, Portovenere, Italy, October 2009. [2] Mario Aldea Rivas and Michael Gonzalez Harbour "Execution time monitoring and interrupt handlers". 14th International Real-Time Ada Workshop, Portovenere, Italy, October 2009. !example !corrigendum D.14(9/2) @dinsa @xcode< @b Time_Of (SC : Seconds_Count; TS : Time_Span := Time_Span_Zero) @b CPU_Time;> @dinss @xcode< Interrupt_Clocks_Supported : @b Boolean := @i<@ft>;> @xcode< Separate_Interrupt_Clocks_Supported : @b Boolean := @i<@ft>;> @xcode< @b Clock_For_Interrupts @b CPU_Time;> !corrigendum D.14(11/2) @drepl The @i or CPU time of a given task is defined as the time spent by the system executing that task, including the time spent executing run-time or system services on its behalf. The mechanism used to measure execution time is implementation defined. It is implementation defined which task, if any, is charged the execution time that is consumed by interrupt handlers and run-time services on behalf of the system. @dby The @i or CPU time of a given task is defined as the time spent by the system executing that task, including the time spent executing run-time or system services on its behalf. The mechanism used to measure execution time is implementation defined. The Boolean constant Interrupt_Clocks_Supported is set to True if the implementation separately accounts for the execution time of interrupt handlers. If it is set to False it is implementation defined which task, if any, is charged the execution time that is consumed by interrupt handlers. The Boolean constant Separate_Interrupt_Clocks_Supported is set to True if the implementation separately accounts for the execution time of individual interrupt handlers. !corrigendum D.14(13/2) @dinsa CPU_Time_First and CPU_Time_Last are the smallest and largest values of the CPU_Time type, respectively. @dinst The execution time value for the function Clock_For_Interrupts is initialized to zero. !corrigendum D.14(18/2) @dinsa The effects of the Split and Time_Of operations are defined as follows, treating values of type CPU_Time, Time_Span, and Seconds_Count as mathematical integers. The effect of Split (T, SC, TS) is to set SC and TS to values such that T*CPU_Time_Unit = SC*1.0 + TS*CPU_Time_Unit, and 0.0 <= TS*CPU_Time_Unit < 1.0. The value returned by Time_Of(SC,TS) is the execution-time value T such that T*CPU_Time_Unit=SC*1.0 + TS*CPU_Time_Unit. @dinst The function Clock_For_Interrupts returns the total cumulative time spent executing within all interrupt handlers. This time is not allocated to any task execution time clock. If Interrupt_Clocks_Supported is set to False the function raises Program_Error. !corrigendum D.14.3 @dinsc This clause describes a language-defined package to measure the execution time of interrupt handlers. @s8<@i> The following language-defined library package exists: @xcode<@b Ada.Interrupts; @b Ada.Execution_Time.Interrupts @b @b Clock (I : Ada.Interrupts.Interrupt_ID) @b CPU_Time; @b Supported (I : Ada.Interrupts.Interrupt_ID) @b Boolean; @b Ada.Execution_Time.Interrupts;> The execution time or CPU time of a given interrupt I is defined as the time spent by the system executing interrupt handlers identified by I, including the time spent executing run-time or system services on its behalf. The mechanism used to measure execution time is implementation defined. Time spent executing interrupt handlers is distinct from time spent executing any task. For each interrupt, the execution time value is initially set to zero. @s8<@i> The function Clock returns the current cumulative execution time of the interrupt identified by I. If Separate_Interrupt_Clocks_Supported is set to False the function raises Program_Error. The function Supported returns True if the implementation is monitoring the execution time of the interrupt identified by I. Otherwise it returns False. For any interrupt_Id I for which Supported(I) returns False, the function Clock(I) will return a value equal to Ada.Execution_Time.Time_Of(0). !ACATS test Add an ACATS C-Test of this package. !appendix ****************************************************************