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;