avoid Foreign.unsafePerformIO
[ghc-base.git] / System / CPUTime.hsc
1 -----------------------------------------------------------------------------
2 -- |
3 -- Module      :  System.CPUTime
4 -- Copyright   :  (c) The University of Glasgow 2001
5 -- License     :  BSD-style (see the file libraries/base/LICENSE)
6 -- 
7 -- Maintainer  :  libraries@haskell.org
8 -- Stability   :  provisional
9 -- Portability :  portable
10 --
11 -- The standard CPUTime library.
12 --
13 -----------------------------------------------------------------------------
14
15 module System.CPUTime 
16         (
17          getCPUTime,       -- :: IO Integer
18          cpuTimePrecision  -- :: Integer
19         ) where
20
21 import Prelude
22
23 import Data.Ratio
24
25 #ifdef __HUGS__
26 import Hugs.Time ( getCPUTime, clockTicks )
27 #endif
28
29 #ifdef __NHC__
30 import CPUTime ( getCPUTime, cpuTimePrecision )
31 #endif
32
33 #ifdef __GLASGOW_HASKELL__
34 import Foreign hiding (unsafePerformIO)
35 import Foreign.C
36 import System.IO.Unsafe (unsafePerformIO)
37
38 #include "HsBaseConfig.h"
39
40 -- For _SC_CLK_TCK
41 #if HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44
45 -- For struct rusage
46 #if !defined(mingw32_HOST_OS) && !defined(irix_HOST_OS)
47 # if HAVE_SYS_RESOURCE_H
48 #  include <sys/resource.h>
49 # endif
50 #endif
51
52 -- For FILETIME etc. on Windows
53 #if HAVE_WINDOWS_H
54 #include <windows.h>
55 #endif
56
57 -- for CLK_TCK
58 #if HAVE_TIME_H
59 #include <time.h>
60 #endif
61
62 -- for struct tms
63 #if HAVE_SYS_TIMES_H
64 #include <sys/times.h>
65 #endif
66
67 #endif
68
69 #if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS)
70 realToInteger :: Real a => a -> Integer
71 realToInteger ct = round (realToFrac ct :: Double)
72   -- CTime, CClock, CUShort etc are in Real but not Fractional, 
73   -- so we must convert to Double before we can round it
74 #endif
75
76 #ifdef __GLASGOW_HASKELL__
77 -- -----------------------------------------------------------------------------
78 -- |Computation 'getCPUTime' returns the number of picoseconds CPU time
79 -- used by the current program.  The precision of this result is
80 -- implementation-dependent.
81
82 getCPUTime :: IO Integer
83 getCPUTime = do
84
85 #if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS)
86 -- getrusage() is right royal pain to deal with when targetting multiple
87 -- versions of Solaris, since some versions supply it in libc (2.3 and 2.5),
88 -- while 2.4 has got it in libucb (I wouldn't be too surprised if it was back
89 -- again in libucb in 2.6..)
90 --
91 -- Avoid the problem by resorting to times() instead.
92 --
93 #if defined(HAVE_GETRUSAGE) && ! irix_HOST_OS && ! solaris2_HOST_OS
94     allocaBytes (#const sizeof(struct rusage)) $ \ p_rusage -> do
95     throwErrnoIfMinus1_ "getrusage" $ getrusage (#const RUSAGE_SELF) p_rusage
96
97     let ru_utime = (#ptr struct rusage, ru_utime) p_rusage
98     let ru_stime = (#ptr struct rusage, ru_stime) p_rusage
99     u_sec  <- (#peek struct timeval,tv_sec)  ru_utime :: IO CTime
100     u_usec <- (#peek struct timeval,tv_usec) ru_utime :: IO CTime
101     s_sec  <- (#peek struct timeval,tv_sec)  ru_stime :: IO CTime
102     s_usec <- (#peek struct timeval,tv_usec) ru_stime :: IO CTime
103     return ((realToInteger u_sec * 1000000 + realToInteger u_usec + 
104              realToInteger s_sec * 1000000 + realToInteger s_usec) 
105                 * 1000000)
106
107 type CRUsage = ()
108 foreign import ccall unsafe getrusage :: CInt -> Ptr CRUsage -> IO CInt
109 #else
110 # if defined(HAVE_TIMES)
111     allocaBytes (#const sizeof(struct tms)) $ \ p_tms -> do
112     _ <- times p_tms
113     u_ticks  <- (#peek struct tms,tms_utime) p_tms :: IO CClock
114     s_ticks  <- (#peek struct tms,tms_stime) p_tms :: IO CClock
115     return (( (realToInteger u_ticks + realToInteger s_ticks) * 1000000000000) 
116                         `div` fromIntegral clockTicks)
117
118 type CTms = ()
119 foreign import ccall unsafe times :: Ptr CTms -> IO CClock
120 # else
121     ioException (IOError Nothing UnsupportedOperation 
122                          "getCPUTime"
123                          "can't get CPU time"
124                          Nothing)
125 # endif
126 #endif
127
128 #else /* win32 */
129      -- NOTE: GetProcessTimes() is only supported on NT-based OSes.
130      -- The counts reported by GetProcessTimes() are in 100-ns (10^-7) units.
131     allocaBytes (#const sizeof(FILETIME)) $ \ p_creationTime -> do
132     allocaBytes (#const sizeof(FILETIME)) $ \ p_exitTime -> do
133     allocaBytes (#const sizeof(FILETIME)) $ \ p_kernelTime -> do
134     allocaBytes (#const sizeof(FILETIME)) $ \ p_userTime -> do
135     pid <- getCurrentProcess
136     ok <- getProcessTimes pid p_creationTime p_exitTime p_kernelTime p_userTime
137     if toBool ok then do
138       ut <- ft2psecs p_userTime
139       kt <- ft2psecs p_kernelTime
140       return (ut + kt)
141      else return 0
142   where 
143         ft2psecs :: Ptr FILETIME -> IO Integer
144         ft2psecs ft = do
145           high <- (#peek FILETIME,dwHighDateTime) ft :: IO Word32
146           low  <- (#peek FILETIME,dwLowDateTime)  ft :: IO Word32
147             -- Convert 100-ns units to picosecs (10^-12) 
148             -- => multiply by 10^5.
149           return (((fromIntegral high) * (2^(32::Int)) + (fromIntegral low)) * 100000)
150
151     -- ToDo: pin down elapsed times to just the OS thread(s) that
152     -- are evaluating/managing Haskell code.
153
154 type FILETIME = ()
155 type HANDLE = ()
156 -- need proper Haskell names (initial lower-case character)
157 foreign import stdcall unsafe "GetCurrentProcess" getCurrentProcess :: IO (Ptr HANDLE)
158 foreign import stdcall unsafe "GetProcessTimes" getProcessTimes :: Ptr HANDLE -> Ptr FILETIME -> Ptr FILETIME -> Ptr FILETIME -> Ptr FILETIME -> IO CInt
159
160 #endif /* not _WIN32 */
161 #endif /* __GLASGOW_HASKELL__ */
162
163 -- |The 'cpuTimePrecision' constant is the smallest measurable difference
164 -- in CPU time that the implementation can record, and is given as an
165 -- integral number of picoseconds.
166
167 #ifndef __NHC__
168 cpuTimePrecision :: Integer
169 cpuTimePrecision = round ((1000000000000::Integer) % fromIntegral (clockTicks))
170 #endif
171
172 #ifdef __GLASGOW_HASKELL__
173 clockTicks :: Int
174 clockTicks =
175 #if defined(CLK_TCK)
176     (#const CLK_TCK)
177 #else
178     unsafePerformIO (sysconf (#const _SC_CLK_TCK) >>= return . fromIntegral)
179 foreign import ccall unsafe sysconf :: CInt -> IO CLong
180 #endif
181 #endif /* __GLASGOW_HASKELL__ */