File : s-bbexti.adb
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS --
4 -- --
5 -- S Y S T E M . B B . E X E C U T I O N _ T I M E --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2011-2016, AdaCore --
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. GNARL 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 -- --
19 -- --
20 -- --
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 ------------------------------------------------------------------------------
28
29 with System.BB.Parameters;
30 with System.BB.Board_Support;
31 with System.BB.Threads.Queues;
32 with System.BB.Protection;
33
34 with System.Multiprocessors;
35 with System.Multiprocessors.Fair_Locks;
36 with System.Multiprocessors.Spin_Locks;
37
38 with System.OS_Interface;
39
40 ------------------------------
41 -- System.BB.Execution_Time --
42 ------------------------------
43
44 package body System.BB.Execution_Time is
45
46 use type System.BB.Time.Time;
47 use System.BB.Interrupts;
48 use System.BB.Threads;
49 use System.Multiprocessors;
50 use System.Multiprocessors.Fair_Locks;
51 use System.Multiprocessors.Spin_Locks;
52
53 Interrupts_Execution_Time : array (Interrupt_ID) of System.BB.Time.Time :=
54 (others => System.BB.Time.Time'First);
55 -- Time counter for each interrupt
56
57 Interrupt_Exec_Time_Lock : Fair_Lock := (Spinning => (others => False),
58 Lock => (Flag => Unlocked));
59 -- Protect access to interrupt time counters on multiprocessor systems
60
61 CPU_Clock : array (CPU) of System.BB.Time.Time;
62 -- Date of the last Interrupt
63
64 procedure Scheduling_Event;
65 -- Assign elapsed time to the executing Task/Interrupt and reset CPU clock.
66 -- This must be called at the end of an execution period:
67 --
68 -- When the run-time switches from a task to another task
69 -- a task to an interrupt
70 -- an interrupt to a task
71 -- an interrupt to another interrupt
72
73 function Elapsed_Time return System.BB.Time.Time;
74 -- Function returning the time elapsed since the last scheduling event,
75 -- i.e. the execution time of the currently executing entity (thread or
76 -- interrupt) that has not yet been added to the global counters.
77
78 function Read_Execution_Time_Atomic
79 (Th : System.BB.Threads.Thread_Id) return System.BB.Time.Time;
80 -- Read the execution time of thread Th. The result is a coherent value:
81 -- if the execution time has changed from A to B (while being read by this
82 -- function), the result will be between A and B. The error is less
83 -- than 2**32 and less than the increment.
84
85 function To_Time (High, Low : Time.Word) return Time.Time;
86 -- Low level routine to convert a composite execution time to type Time
87
88 function To_Composite_Execution_Time
89 (T : Time.Time) return Time.Composite_Execution_Time;
90 -- Low level routine to convert a time to a composite execution time
91
92 ------------------
93 -- Elapsed_Time --
94 ------------------
95
96 function Elapsed_Time return System.BB.Time.Time is
97 CPU_Id : constant CPU := System.OS_Interface.Current_CPU;
98
99 Now : constant BB.Time.Time := System.BB.Time.Clock;
100 pragma Assert (Now >= CPU_Clock (CPU_Id));
101
102 begin
103 return Now - CPU_Clock (CPU_Id);
104 end Elapsed_Time;
105
106 ----------------------------
107 -- Global_Interrupt_Clock --
108 ----------------------------
109
110 function Global_Interrupt_Clock return System.BB.Time.Time is
111 Sum : System.BB.Time.Time := System.BB.Time.Time'First;
112
113 begin
114 -- Protect shared access on multiprocessor systems
115
116 if System.BB.Parameters.Multiprocessor then
117 Lock (Interrupt_Exec_Time_Lock);
118 end if;
119
120 for Interrupt in Interrupt_ID loop
121 Sum := Sum + Interrupts_Execution_Time (Interrupt);
122 end loop;
123
124 -- If any interrupt is executing, we need to add the elapsed time
125 -- between the last scheduling and now.
126
127 if System.BB.Interrupts.Current_Interrupt /= No_Interrupt then
128 Sum := Sum + Elapsed_Time;
129 end if;
130
131 if System.BB.Parameters.Multiprocessor then
132 Unlock (Interrupt_Exec_Time_Lock);
133 end if;
134
135 return Sum;
136 end Global_Interrupt_Clock;
137
138 ---------------------
139 -- Interrupt_Clock --
140 ---------------------
141
142 function Interrupt_Clock
143 (Interrupt : Interrupt_ID) return System.BB.Time.Time
144 is
145 Value : System.BB.Time.Time;
146
147 begin
148 -- Protect against interruption the addition between Execution_Time
149 -- and Elapsed_Time.
150
151 System.BB.Protection.Enter_Kernel;
152
153 -- Protect shared access on multiprocessor systems
154
155 if System.BB.Parameters.Multiprocessor then
156 Lock (Interrupt_Exec_Time_Lock);
157 end if;
158
159 Value := Interrupts_Execution_Time (Interrupt);
160
161 -- If the interrupt is executing (i.e., if we are requesting the
162 -- interrupt clock from the interrupt handler), we need to add the
163 -- elapsed time between the last scheduling and now.
164
165 if System.BB.Interrupts.Current_Interrupt = Interrupt then
166 Value := Value + Elapsed_Time;
167 end if;
168
169 if System.BB.Parameters.Multiprocessor then
170 Unlock (Interrupt_Exec_Time_Lock);
171 end if;
172
173 System.BB.Protection.Leave_Kernel;
174
175 return Value;
176 end Interrupt_Clock;
177
178 --------------------------------
179 -- Read_Execution_Time_Atomic --
180 --------------------------------
181
182 function Read_Execution_Time_Atomic
183 (Th : System.BB.Threads.Thread_Id) return System.BB.Time.Time
184 is
185 use Time;
186 H1 : Word;
187 L : Word;
188 H2 : Word;
189
190 begin
191 -- Read parts in that order
192
193 H1 := Th.Execution_Time.High;
194 L := Th.Execution_Time.Low;
195 H2 := Th.Execution_Time.High;
196
197 -- If value has changed while being read, keep the latest high part,
198 -- but use 0 for the low part. So the result will be greater than
199 -- the old value and less than the new value.
200
201 if H1 /= H2 then
202 L := 0;
203 end if;
204
205 return Time.Time (H2) * 2 ** 32 + Time.Time (L);
206 end Read_Execution_Time_Atomic;
207
208 ----------------------
209 -- Scheduling_Event --
210 ----------------------
211
212 procedure Scheduling_Event is
213 Now : constant System.BB.Time.Time := System.BB.Time.Clock;
214 CPU_Id : constant CPU :=
215 System.OS_Interface.Current_CPU;
216 Last_CPU_Clock : constant System.BB.Time.Time := CPU_Clock (CPU_Id);
217 Elapsed_Time : System.BB.Time.Time;
218
219 begin
220 pragma Assert (Now >= Last_CPU_Clock);
221 Elapsed_Time := Now - Last_CPU_Clock;
222
223 -- Reset the clock
224
225 CPU_Clock (CPU_Id) := Now;
226
227 -- Case of CPU currently executing an interrupt
228
229 if Current_Interrupt /= No_Interrupt then
230
231 -- Protect shared access on multiprocessor systems
232
233 if System.BB.Parameters.Multiprocessor then
234 Lock (Interrupt_Exec_Time_Lock);
235 end if;
236
237 Interrupts_Execution_Time (Current_Interrupt) :=
238 Interrupts_Execution_Time (Current_Interrupt) + Elapsed_Time;
239
240 if System.BB.Parameters.Multiprocessor then
241 Unlock (Interrupt_Exec_Time_Lock);
242 end if;
243
244 -- This CPU is currently executing a task
245
246 else
247 declare
248 T : Time.Time;
249 begin
250 T := To_Time (Thread_Self.Execution_Time.High,
251 Thread_Self.Execution_Time.Low);
252 T := T + Elapsed_Time;
253 Thread_Self.Execution_Time := To_Composite_Execution_Time (T);
254 end;
255 end if;
256 end Scheduling_Event;
257
258 ------------------
259 -- Thread_Clock --
260 ------------------
261
262 function Thread_Clock
263 (Th : System.BB.Threads.Thread_Id) return System.BB.Time.Time
264 is
265 Res : System.BB.Time.Time;
266
267 Sec : System.BB.Time.Time;
268 -- Second read of execution time
269
270 ET : System.BB.Time.Time;
271 -- Elapsed time
272
273 begin
274 pragma Assert (Th /= Null_Thread_Id);
275
276 Res := Read_Execution_Time_Atomic (Th);
277
278 -- If the thread Th is running, we need to add the elapsed time between
279 -- the last scheduling and now. The thread Th is running if it is the
280 -- current one and no interrupt is executed
281
282 if Th = Thread_Self
283 and then System.BB.Interrupts.Current_Interrupt = No_Interrupt
284 then
285
286 ET := Elapsed_Time;
287
288 Sec := Read_Execution_Time_Atomic (Th);
289
290 if Res /= Sec then
291
292 -- The whole set of values (Res, ET and Sec) isn't coherent, as
293 -- the execution time has been updated (might happen in case of
294 -- interrupt). Unfortunately, the error in Sec might be as large
295 -- as ET. So lets read again. The error will be small, as the time
296 -- spent between this third read and the second one is small.
297
298 Res := Read_Execution_Time_Atomic (Th);
299
300 else
301 Res := Res + ET;
302 end if;
303 end if;
304
305 return Res;
306 end Thread_Clock;
307
308 ---------------------------------
309 -- To_Composite_Execution_Time --
310 ---------------------------------
311
312 function To_Composite_Execution_Time
313 (T : Time.Time) return Time.Composite_Execution_Time is
314 begin
315 return (High => Time.Word (T / 2 ** 32),
316 Low => Time.Word (T mod 2 ** 32));
317 end To_Composite_Execution_Time;
318
319 -------------
320 -- To_Time --
321 -------------
322
323 function To_Time (High, Low : Time.Word) return Time.Time is
324 begin
325 return Time.Time (High) * 2 ** 32 + Time.Time (Low);
326 end To_Time;
327
328 begin
329 -- Set the hooks to enable computation
330
331 System.BB.Time.Scheduling_Event_Hook := Scheduling_Event'Access;
332
333 -- Initialize CPU_Clock
334
335 declare
336 Now : constant BB.Time.Time := System.BB.Time.Epoch;
337 -- Calling Clock here is tricky because we may be doing so before timer
338 -- initialization. Hence, we simply get the startup time.
339
340 begin
341 CPU_Clock := (others => Now);
342 end;
343 end System.BB.Execution_Time;