1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include "nsStopwatch.h"
6 
7 #include <stdio.h>
8 #include <time.h>
9 #if defined(XP_UNIX)
10 #  include <unistd.h>
11 #  include <sys/times.h>
12 #  include <sys/time.h>
13 #  include <errno.h>
14 #elif defined(XP_WIN)
15 #  include "windows.h"
16 #endif  // elif defined(XP_WIN)
17 
18 #include "nsMemory.h"
19 /*
20  * This basis for the logic in this file comes from (will used to come from):
21  *  (mozilla/)modules/libutil/public/stopwatch.cpp.
22  *
23  * It was no longer used in the mozilla tree, and is being migrated to
24  * comm-central where we actually have a need for it.  ("Being" in the sense
25  * that it will not be removed immediately from mozilla-central.)
26  *
27  * Simplification and general clean-up has been performed and the fix for
28  * bug 96669 has been integrated.
29  */
30 
31 NS_IMPL_ISUPPORTS(nsStopwatch, nsIStopwatch)
32 
33 #if defined(XP_UNIX)
34 /** the number of ticks per second */
35 static double gTicks = 0;
36 #  define MICRO_SECONDS_TO_SECONDS_MULT static_cast<double>(1.0e-6)
37 #elif defined(WIN32)
38 #  ifdef DEBUG
39 #    include "nsPrintfCString.h"
40 #  endif
41 // 1 tick per 100ns = 10 per us = 10 * 1,000 per ms = 10 * 1,000 * 1,000 per
42 // sec.
43 #  define WIN32_TICK_RESOLUTION static_cast<double>(1.0e-7)
44 // subtract off to get to the unix epoch
45 #  define UNIX_EPOCH_IN_FILE_TIME 116444736000000000L
46 #endif  // elif defined(WIN32)
47 
nsStopwatch()48 nsStopwatch::nsStopwatch()
49     : fTotalRealTimeSecs(0.0), fTotalCpuTimeSecs(0.0), fRunning(false) {
50 #if defined(XP_UNIX)
51   // idempotent in the event of a race under all coherency models
52   if (!gTicks) {
53     // we need to clear errno because sysconf's spec says it leaves it the same
54     //  on success and only sets it on failure.
55     errno = 0;
56     gTicks = (clock_t)sysconf(_SC_CLK_TCK);
57     // in event of failure, pick an arbitrary value so we don't divide by zero.
58     if (errno) gTicks = 1000000L;
59   }
60 #endif
61 }
62 
~nsStopwatch()63 nsStopwatch::~nsStopwatch() {}
64 
Start()65 NS_IMETHODIMP nsStopwatch::Start() {
66   fTotalRealTimeSecs = 0.0;
67   fTotalCpuTimeSecs = 0.0;
68   return Resume();
69 }
70 
Stop()71 NS_IMETHODIMP nsStopwatch::Stop() {
72   fStopRealTimeSecs = GetRealTime();
73   fStopCpuTimeSecs = GetCPUTime();
74   if (fRunning) {
75     fTotalCpuTimeSecs += fStopCpuTimeSecs - fStartCpuTimeSecs;
76     fTotalRealTimeSecs += fStopRealTimeSecs - fStartRealTimeSecs;
77   }
78   fRunning = false;
79   return NS_OK;
80 }
81 
Resume()82 NS_IMETHODIMP nsStopwatch::Resume() {
83   if (!fRunning) {
84     fStartRealTimeSecs = GetRealTime();
85     fStartCpuTimeSecs = GetCPUTime();
86   }
87   fRunning = true;
88   return NS_OK;
89 }
90 
GetCpuTimeSeconds(double * result)91 NS_IMETHODIMP nsStopwatch::GetCpuTimeSeconds(double* result) {
92   NS_ENSURE_ARG_POINTER(result);
93   *result = fTotalCpuTimeSecs;
94   return NS_OK;
95 }
96 
GetRealTimeSeconds(double * result)97 NS_IMETHODIMP nsStopwatch::GetRealTimeSeconds(double* result) {
98   NS_ENSURE_ARG_POINTER(result);
99   *result = fTotalRealTimeSecs;
100   return NS_OK;
101 }
102 
GetRealTime()103 double nsStopwatch::GetRealTime() {
104 #if defined(XP_UNIX)
105   struct timeval t;
106   gettimeofday(&t, NULL);
107   return t.tv_sec + t.tv_usec * MICRO_SECONDS_TO_SECONDS_MULT;
108 #elif defined(WIN32)
109   union {
110     FILETIME ftFileTime;
111     __int64 ftInt64;
112   } ftRealTime;  // time the process has spent in kernel mode
113   SYSTEMTIME st;
114   GetSystemTime(&st);
115   SystemTimeToFileTime(&st, &ftRealTime.ftFileTime);
116   return (ftRealTime.ftInt64 - UNIX_EPOCH_IN_FILE_TIME) * WIN32_TICK_RESOLUTION;
117 #else
118 #  error "nsStopwatch not supported on this platform."
119 #endif
120 }
121 
GetCPUTime()122 double nsStopwatch::GetCPUTime() {
123 #if defined(XP_UNIX)
124   struct tms cpt;
125   times(&cpt);
126   return (double)(cpt.tms_utime + cpt.tms_stime) / gTicks;
127 #elif defined(WIN32)
128   FILETIME ftCreate,  // when the process was created
129       ftExit;         // when the process exited
130 
131   union {
132     FILETIME ftFileTime;
133     __int64 ftInt64;
134   } ftKernel;  // time the process has spent in kernel mode
135 
136   union {
137     FILETIME ftFileTime;
138     __int64 ftInt64;
139   } ftUser;  // time the process has spent in user mode
140 
141   HANDLE hProcess = GetCurrentProcess();
142 #  ifdef DEBUG
143   BOOL ret =
144 #  endif
145       GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel.ftFileTime,
146                       &ftUser.ftFileTime);
147 #  ifdef DEBUG
148   if (!ret)
149     NS_ERROR(nsPrintfCString("GetProcessTimes() failed, error=0x%lx.",
150                              GetLastError())
151                  .get());
152 #  endif
153 
154   /*
155    * Process times are returned in a 64-bit structure, as the number of
156    * 100 nanosecond ticks since 1 January 1601.  User mode and kernel mode
157    * times for this process are in separate 64-bit structures.
158    * Add them and convert the result to seconds.
159    */
160   return (ftKernel.ftInt64 + ftUser.ftInt64) * WIN32_TICK_RESOLUTION;
161 #else
162 #  error "nsStopwatch not supported on this platform."
163 #endif
164 }
165