|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 // Implement TimeStamp::Now() with QueryPerformanceCounter() controlled with |
|
8 // values of GetTickCount(). |
|
9 |
|
10 #include "mozilla/MathAlgorithms.h" |
|
11 #include "mozilla/Mutex.h" |
|
12 #include "mozilla/TimeStamp.h" |
|
13 #include "nsWindowsHelpers.h" |
|
14 #include <windows.h> |
|
15 |
|
16 #include "nsCRT.h" |
|
17 #include "prlog.h" |
|
18 #include "prprf.h" |
|
19 #include <stdio.h> |
|
20 |
|
21 #include <intrin.h> |
|
22 |
|
23 #if defined(PR_LOGGING) |
|
24 // Log module for mozilla::TimeStamp for Windows logging... |
|
25 // |
|
26 // To enable logging (see prlog.h for full details): |
|
27 // |
|
28 // set NSPR_LOG_MODULES=TimeStampWindows:5 |
|
29 // set NSPR_LOG_FILE=nspr.log |
|
30 // |
|
31 // this enables PR_LOG_DEBUG level information and places all output in |
|
32 // the file nspr.log |
|
33 static PRLogModuleInfo* |
|
34 GetTimeStampLog() |
|
35 { |
|
36 static PRLogModuleInfo *sLog; |
|
37 if (!sLog) |
|
38 sLog = PR_NewLogModule("TimeStampWindows"); |
|
39 return sLog; |
|
40 } |
|
41 #define LOG(x) PR_LOG(GetTimeStampLog(), PR_LOG_DEBUG, x) |
|
42 #else |
|
43 #define LOG(x) |
|
44 #endif /* PR_LOGGING */ |
|
45 |
|
46 // Estimate of the smallest duration of time we can measure. |
|
47 static volatile ULONGLONG sResolution; |
|
48 static volatile ULONGLONG sResolutionSigDigs; |
|
49 static const double kNsPerSecd = 1000000000.0; |
|
50 static const LONGLONG kNsPerSec = 1000000000; |
|
51 static const LONGLONG kNsPerMillisec = 1000000; |
|
52 |
|
53 // ---------------------------------------------------------------------------- |
|
54 // Global constants |
|
55 // ---------------------------------------------------------------------------- |
|
56 |
|
57 // Tolerance to failures settings. |
|
58 // |
|
59 // What is the interval we want to have failure free. |
|
60 // in [ms] |
|
61 static const uint32_t kFailureFreeInterval = 5000; |
|
62 // How many failures we are willing to tolerate in the interval. |
|
63 static const uint32_t kMaxFailuresPerInterval = 4; |
|
64 // What is the threshold to treat fluctuations as actual failures. |
|
65 // in [ms] |
|
66 static const uint32_t kFailureThreshold = 50; |
|
67 |
|
68 // If we are not able to get the value of GTC time increment, use this value |
|
69 // which is the most usual increment. |
|
70 static const DWORD kDefaultTimeIncrement = 156001; |
|
71 |
|
72 // ---------------------------------------------------------------------------- |
|
73 // Global variables, not changing at runtime |
|
74 // ---------------------------------------------------------------------------- |
|
75 |
|
76 /** |
|
77 * The [mt] unit: |
|
78 * |
|
79 * Many values are kept in ticks of the Performance Coutner x 1000, |
|
80 * further just referred as [mt], meaning milli-ticks. |
|
81 * |
|
82 * This is needed to preserve maximum precision of the performance frequency |
|
83 * representation. GetTickCount values in milliseconds are multiplied with |
|
84 * frequency per second. Therefor we need to multiply QPC value by 1000 to |
|
85 * have the same units to allow simple arithmentic with both QPC and GTC. |
|
86 */ |
|
87 |
|
88 #define ms2mt(x) ((x) * sFrequencyPerSec) |
|
89 #define mt2ms(x) ((x) / sFrequencyPerSec) |
|
90 #define mt2ms_f(x) (double(x) / sFrequencyPerSec) |
|
91 |
|
92 // Result of QueryPerformanceFrequency |
|
93 static LONGLONG sFrequencyPerSec = 0; |
|
94 |
|
95 // How much we are tolerant to GTC occasional loose of resoltion. |
|
96 // This number says how many multiples of the minimal GTC resolution |
|
97 // detected on the system are acceptable. This number is empirical. |
|
98 static const LONGLONG kGTCTickLeapTolerance = 4; |
|
99 |
|
100 // Base tolerance (more: "inability of detection" range) threshold is calculated |
|
101 // dynamically, and kept in sGTCResulutionThreshold. |
|
102 // |
|
103 // Schematically, QPC worked "100%" correctly if ((GTC_now - GTC_epoch) - |
|
104 // (QPC_now - QPC_epoch)) was in [-sGTCResulutionThreshold, sGTCResulutionThreshold] |
|
105 // interval every time we'd compared two time stamps. |
|
106 // If not, then we check the overflow behind this basic threshold |
|
107 // is in kFailureThreshold. If not, we condider it as a QPC failure. If too many |
|
108 // failures in short time are detected, QPC is considered faulty and disabled. |
|
109 // |
|
110 // Kept in [mt] |
|
111 static LONGLONG sGTCResulutionThreshold; |
|
112 |
|
113 // If QPC is found faulty for two stamps in this interval, we engage |
|
114 // the fault detection algorithm. For duration larger then this limit |
|
115 // we bypass using durations calculated from QPC when jitter is detected, |
|
116 // but don't touch the sUseQPC flag. |
|
117 // |
|
118 // Value is in [ms]. |
|
119 static const uint32_t kHardFailureLimit = 2000; |
|
120 // Conversion to [mt] |
|
121 static LONGLONG sHardFailureLimit; |
|
122 |
|
123 // Conversion of kFailureFreeInterval and kFailureThreshold to [mt] |
|
124 static LONGLONG sFailureFreeInterval; |
|
125 static LONGLONG sFailureThreshold; |
|
126 |
|
127 // ---------------------------------------------------------------------------- |
|
128 // Systemm status flags |
|
129 // ---------------------------------------------------------------------------- |
|
130 |
|
131 // Flag for stable TSC that indicates platform where QPC is stable. |
|
132 static bool sHasStableTSC = false; |
|
133 |
|
134 // ---------------------------------------------------------------------------- |
|
135 // Global state variables, changing at runtime |
|
136 // ---------------------------------------------------------------------------- |
|
137 |
|
138 // Initially true, set to false when QPC is found unstable and never |
|
139 // returns back to true since that time. |
|
140 static bool volatile sUseQPC = true; |
|
141 |
|
142 // ---------------------------------------------------------------------------- |
|
143 // Global lock |
|
144 // ---------------------------------------------------------------------------- |
|
145 |
|
146 // Thread spin count before entering the full wait state for sTimeStampLock. |
|
147 // Inspired by Rob Arnold's work on PRMJ_Now(). |
|
148 static const DWORD kLockSpinCount = 4096; |
|
149 |
|
150 // Common mutex (thanks the relative complexity of the logic, this is better |
|
151 // then using CMPXCHG8B.) |
|
152 // It is protecting the globals bellow. |
|
153 static CRITICAL_SECTION sTimeStampLock; |
|
154 |
|
155 // ---------------------------------------------------------------------------- |
|
156 // Global lock protected variables |
|
157 // ---------------------------------------------------------------------------- |
|
158 |
|
159 // Timestamp in future until QPC must behave correctly. |
|
160 // Set to now + kFailureFreeInterval on first QPC failure detection. |
|
161 // Set to now + E * kFailureFreeInterval on following errors, |
|
162 // where E is number of errors detected during last kFailureFreeInterval |
|
163 // milliseconds, calculated simply as: |
|
164 // E = (sFaultIntoleranceCheckpoint - now) / kFailureFreeInterval + 1. |
|
165 // When E > kMaxFailuresPerInterval -> disable QPC. |
|
166 // |
|
167 // Kept in [mt] |
|
168 static ULONGLONG sFaultIntoleranceCheckpoint = 0; |
|
169 |
|
170 // Used only when GetTickCount64 is not available on the platform. |
|
171 // Last result of GetTickCount call. |
|
172 // |
|
173 // Kept in [ms] |
|
174 static DWORD sLastGTCResult = 0; |
|
175 |
|
176 // Higher part of the 64-bit value of MozGetTickCount64, |
|
177 // incremented atomically. |
|
178 static DWORD sLastGTCRollover = 0; |
|
179 |
|
180 namespace mozilla { |
|
181 |
|
182 typedef ULONGLONG (WINAPI* GetTickCount64_t)(); |
|
183 static GetTickCount64_t sGetTickCount64 = nullptr; |
|
184 |
|
185 // Function protecting GetTickCount result from rolling over, |
|
186 // result is in [ms] |
|
187 static ULONGLONG WINAPI |
|
188 MozGetTickCount64() |
|
189 { |
|
190 DWORD GTC = ::GetTickCount(); |
|
191 |
|
192 // Cheaper then CMPXCHG8B |
|
193 AutoCriticalSection lock(&sTimeStampLock); |
|
194 |
|
195 // Pull the rollover counter forward only if new value of GTC goes way |
|
196 // down under the last saved result |
|
197 if ((sLastGTCResult > GTC) && ((sLastGTCResult - GTC) > (1UL << 30))) |
|
198 ++sLastGTCRollover; |
|
199 |
|
200 sLastGTCResult = GTC; |
|
201 return ULONGLONG(sLastGTCRollover) << 32 | sLastGTCResult; |
|
202 } |
|
203 |
|
204 // Result is in [mt] |
|
205 static inline ULONGLONG |
|
206 PerformanceCounter() |
|
207 { |
|
208 LARGE_INTEGER pc; |
|
209 ::QueryPerformanceCounter(&pc); |
|
210 return pc.QuadPart * 1000ULL; |
|
211 } |
|
212 |
|
213 static void |
|
214 InitThresholds() |
|
215 { |
|
216 DWORD timeAdjustment = 0, timeIncrement = 0; |
|
217 BOOL timeAdjustmentDisabled; |
|
218 GetSystemTimeAdjustment(&timeAdjustment, |
|
219 &timeIncrement, |
|
220 &timeAdjustmentDisabled); |
|
221 |
|
222 LOG(("TimeStamp: timeIncrement=%d [100ns]", timeIncrement)); |
|
223 |
|
224 if (!timeIncrement) |
|
225 timeIncrement = kDefaultTimeIncrement; |
|
226 |
|
227 // Ceiling to a millisecond |
|
228 // Example values: 156001, 210000 |
|
229 DWORD timeIncrementCeil = timeIncrement; |
|
230 // Don't want to round up if already rounded, values will be: 156000, 209999 |
|
231 timeIncrementCeil -= 1; |
|
232 // Convert to ms, values will be: 15, 20 |
|
233 timeIncrementCeil /= 10000; |
|
234 // Round up, values will be: 16, 21 |
|
235 timeIncrementCeil += 1; |
|
236 // Convert back to 100ns, values will be: 160000, 210000 |
|
237 timeIncrementCeil *= 10000; |
|
238 |
|
239 // How many milli-ticks has the interval rounded up |
|
240 LONGLONG ticksPerGetTickCountResolutionCeiling = |
|
241 (int64_t(timeIncrementCeil) * sFrequencyPerSec) / 10000LL; |
|
242 |
|
243 // GTC may jump by 32 (2*16) ms in two steps, therefor use the ceiling value. |
|
244 sGTCResulutionThreshold = |
|
245 LONGLONG(kGTCTickLeapTolerance * ticksPerGetTickCountResolutionCeiling); |
|
246 |
|
247 sHardFailureLimit = ms2mt(kHardFailureLimit); |
|
248 sFailureFreeInterval = ms2mt(kFailureFreeInterval); |
|
249 sFailureThreshold = ms2mt(kFailureThreshold); |
|
250 } |
|
251 |
|
252 static void |
|
253 InitResolution() |
|
254 { |
|
255 // 10 total trials is arbitrary: what we're trying to avoid by |
|
256 // looping is getting unlucky and being interrupted by a context |
|
257 // switch or signal, or being bitten by paging/cache effects |
|
258 |
|
259 ULONGLONG minres = ~0ULL; |
|
260 int loops = 10; |
|
261 do { |
|
262 ULONGLONG start = PerformanceCounter(); |
|
263 ULONGLONG end = PerformanceCounter(); |
|
264 |
|
265 ULONGLONG candidate = (end - start); |
|
266 if (candidate < minres) |
|
267 minres = candidate; |
|
268 } while (--loops && minres); |
|
269 |
|
270 if (0 == minres) { |
|
271 minres = 1; |
|
272 } |
|
273 |
|
274 // Converting minres that is in [mt] to nanosecods, multiplicating |
|
275 // the argument to preserve resolution. |
|
276 ULONGLONG result = mt2ms(minres * kNsPerMillisec); |
|
277 if (0 == result) { |
|
278 result = 1; |
|
279 } |
|
280 |
|
281 sResolution = result; |
|
282 |
|
283 // find the number of significant digits in mResolution, for the |
|
284 // sake of ToSecondsSigDigits() |
|
285 ULONGLONG sigDigs; |
|
286 for (sigDigs = 1; |
|
287 !(sigDigs == result |
|
288 || 10*sigDigs > result); |
|
289 sigDigs *= 10); |
|
290 |
|
291 sResolutionSigDigs = sigDigs; |
|
292 } |
|
293 |
|
294 // ---------------------------------------------------------------------------- |
|
295 // TimeStampValue implementation |
|
296 // ---------------------------------------------------------------------------- |
|
297 |
|
298 TimeStampValue::TimeStampValue(ULONGLONG aGTC, ULONGLONG aQPC, bool aHasQPC) |
|
299 : mGTC(aGTC) |
|
300 , mQPC(aQPC) |
|
301 , mHasQPC(aHasQPC) |
|
302 , mIsNull(false) |
|
303 { |
|
304 } |
|
305 |
|
306 TimeStampValue& |
|
307 TimeStampValue::operator+=(const int64_t aOther) |
|
308 { |
|
309 mGTC += aOther; |
|
310 mQPC += aOther; |
|
311 return *this; |
|
312 } |
|
313 |
|
314 TimeStampValue& |
|
315 TimeStampValue::operator-=(const int64_t aOther) |
|
316 { |
|
317 mGTC -= aOther; |
|
318 mQPC -= aOther; |
|
319 return *this; |
|
320 } |
|
321 |
|
322 // If the duration is less then two seconds, perform check of QPC stability |
|
323 // by comparing both GTC and QPC calculated durations of this and aOther. |
|
324 uint64_t |
|
325 TimeStampValue::CheckQPC(const TimeStampValue &aOther) const |
|
326 { |
|
327 uint64_t deltaGTC = mGTC - aOther.mGTC; |
|
328 |
|
329 if (!mHasQPC || !aOther.mHasQPC) // Both not holding QPC |
|
330 return deltaGTC; |
|
331 |
|
332 uint64_t deltaQPC = mQPC - aOther.mQPC; |
|
333 |
|
334 if (sHasStableTSC) // For stable TSC there is no need to check |
|
335 return deltaQPC; |
|
336 |
|
337 if (!sUseQPC) // QPC globally disabled |
|
338 return deltaGTC; |
|
339 |
|
340 // Check QPC is sane before using it. |
|
341 int64_t diff = DeprecatedAbs(int64_t(deltaQPC) - int64_t(deltaGTC)); |
|
342 if (diff <= sGTCResulutionThreshold) |
|
343 return deltaQPC; |
|
344 |
|
345 // Treat absolutely for calibration purposes |
|
346 int64_t duration = DeprecatedAbs(int64_t(deltaGTC)); |
|
347 int64_t overflow = diff - sGTCResulutionThreshold; |
|
348 |
|
349 LOG(("TimeStamp: QPC check after %llums with overflow %1.4fms", |
|
350 mt2ms(duration), mt2ms_f(overflow))); |
|
351 |
|
352 if (overflow <= sFailureThreshold) // We are in the limit, let go. |
|
353 return deltaQPC; // XXX Should we return GTC here? |
|
354 |
|
355 // QPC deviates, don't use it, since now this method may only return deltaGTC. |
|
356 LOG(("TimeStamp: QPC jittered over failure threshold")); |
|
357 |
|
358 if (duration < sHardFailureLimit) { |
|
359 // Interval between the two time stamps is very short, consider |
|
360 // QPC as unstable and record a failure. |
|
361 uint64_t now = ms2mt(sGetTickCount64()); |
|
362 |
|
363 AutoCriticalSection lock(&sTimeStampLock); |
|
364 |
|
365 if (sFaultIntoleranceCheckpoint && sFaultIntoleranceCheckpoint > now) { |
|
366 // There's already been an error in the last fault intollerant interval. |
|
367 // Time since now to the checkpoint actually holds information on how many |
|
368 // failures there were in the failure free interval we have defined. |
|
369 uint64_t failureCount = (sFaultIntoleranceCheckpoint - now + sFailureFreeInterval - 1) / |
|
370 sFailureFreeInterval; |
|
371 if (failureCount > kMaxFailuresPerInterval) { |
|
372 sUseQPC = false; |
|
373 LOG(("TimeStamp: QPC disabled")); |
|
374 } |
|
375 else { |
|
376 // Move the fault intolerance checkpoint more to the future, prolong it |
|
377 // to reflect the number of detected failures. |
|
378 ++failureCount; |
|
379 sFaultIntoleranceCheckpoint = now + failureCount * sFailureFreeInterval; |
|
380 LOG(("TimeStamp: recording %dth QPC failure", failureCount)); |
|
381 } |
|
382 } |
|
383 else { |
|
384 // Setup fault intolerance checkpoint in the future for first detected error. |
|
385 sFaultIntoleranceCheckpoint = now + sFailureFreeInterval; |
|
386 LOG(("TimeStamp: recording 1st QPC failure")); |
|
387 } |
|
388 } |
|
389 |
|
390 return deltaGTC; |
|
391 } |
|
392 |
|
393 uint64_t |
|
394 TimeStampValue::operator-(const TimeStampValue &aOther) const |
|
395 { |
|
396 if (mIsNull && aOther.mIsNull) |
|
397 return uint64_t(0); |
|
398 |
|
399 return CheckQPC(aOther); |
|
400 } |
|
401 |
|
402 // ---------------------------------------------------------------------------- |
|
403 // TimeDuration and TimeStamp implementation |
|
404 // ---------------------------------------------------------------------------- |
|
405 |
|
406 double |
|
407 TimeDuration::ToSeconds() const |
|
408 { |
|
409 // Converting before arithmetic avoids blocked store forward |
|
410 return double(mValue) / (double(sFrequencyPerSec) * 1000.0); |
|
411 } |
|
412 |
|
413 double |
|
414 TimeDuration::ToSecondsSigDigits() const |
|
415 { |
|
416 // don't report a value < mResolution ... |
|
417 LONGLONG resolution = sResolution; |
|
418 LONGLONG resolutionSigDigs = sResolutionSigDigs; |
|
419 LONGLONG valueSigDigs = resolution * (mValue / resolution); |
|
420 // and chop off insignificant digits |
|
421 valueSigDigs = resolutionSigDigs * (valueSigDigs / resolutionSigDigs); |
|
422 return double(valueSigDigs) / kNsPerSecd; |
|
423 } |
|
424 |
|
425 TimeDuration |
|
426 TimeDuration::FromMilliseconds(double aMilliseconds) |
|
427 { |
|
428 return TimeDuration::FromTicks(int64_t(ms2mt(aMilliseconds))); |
|
429 } |
|
430 |
|
431 TimeDuration |
|
432 TimeDuration::Resolution() |
|
433 { |
|
434 return TimeDuration::FromTicks(int64_t(sResolution)); |
|
435 } |
|
436 |
|
437 static bool |
|
438 HasStableTSC() |
|
439 { |
|
440 union { |
|
441 int regs[4]; |
|
442 struct { |
|
443 int nIds; |
|
444 char cpuString[12]; |
|
445 }; |
|
446 } cpuInfo; |
|
447 |
|
448 __cpuid(cpuInfo.regs, 0); |
|
449 // Only allow Intel CPUs for now |
|
450 // The order of the registers is reg[1], reg[3], reg[2]. We just adjust the |
|
451 // string so that we can compare in one go. |
|
452 if (_strnicmp(cpuInfo.cpuString, "GenuntelineI", sizeof(cpuInfo.cpuString))) |
|
453 return false; |
|
454 |
|
455 int regs[4]; |
|
456 |
|
457 // detect if the Advanced Power Management feature is supported |
|
458 __cpuid(regs, 0x80000000); |
|
459 if (regs[0] < 0x80000007) |
|
460 return false; |
|
461 |
|
462 __cpuid(regs, 0x80000007); |
|
463 // if bit 8 is set than TSC will run at a constant rate |
|
464 // in all ACPI P-state, C-states and T-states |
|
465 return regs[3] & (1 << 8); |
|
466 } |
|
467 |
|
468 nsresult |
|
469 TimeStamp::Startup() |
|
470 { |
|
471 // Decide which implementation to use for the high-performance timer. |
|
472 |
|
473 HMODULE kernelDLL = GetModuleHandleW(L"kernel32.dll"); |
|
474 sGetTickCount64 = reinterpret_cast<GetTickCount64_t> |
|
475 (GetProcAddress(kernelDLL, "GetTickCount64")); |
|
476 if (!sGetTickCount64) { |
|
477 // If the platform does not support the GetTickCount64 (Windows XP doesn't), |
|
478 // then use our fallback implementation based on GetTickCount. |
|
479 sGetTickCount64 = MozGetTickCount64; |
|
480 } |
|
481 |
|
482 InitializeCriticalSectionAndSpinCount(&sTimeStampLock, kLockSpinCount); |
|
483 |
|
484 sHasStableTSC = HasStableTSC(); |
|
485 LOG(("TimeStamp: HasStableTSC=%d", sHasStableTSC)); |
|
486 |
|
487 LARGE_INTEGER freq; |
|
488 sUseQPC = ::QueryPerformanceFrequency(&freq); |
|
489 if (!sUseQPC) { |
|
490 // No Performance Counter. Fall back to use GetTickCount. |
|
491 InitResolution(); |
|
492 |
|
493 LOG(("TimeStamp: using GetTickCount")); |
|
494 return NS_OK; |
|
495 } |
|
496 |
|
497 sFrequencyPerSec = freq.QuadPart; |
|
498 LOG(("TimeStamp: QPC frequency=%llu", sFrequencyPerSec)); |
|
499 |
|
500 InitThresholds(); |
|
501 InitResolution(); |
|
502 |
|
503 return NS_OK; |
|
504 } |
|
505 |
|
506 void |
|
507 TimeStamp::Shutdown() |
|
508 { |
|
509 DeleteCriticalSection(&sTimeStampLock); |
|
510 } |
|
511 |
|
512 TimeStamp |
|
513 TimeStamp::Now(bool aHighResolution) |
|
514 { |
|
515 // sUseQPC is volatile |
|
516 bool useQPC = (aHighResolution && sUseQPC); |
|
517 |
|
518 // Both values are in [mt] units. |
|
519 ULONGLONG QPC = useQPC ? PerformanceCounter() : uint64_t(0); |
|
520 ULONGLONG GTC = ms2mt(sGetTickCount64()); |
|
521 return TimeStamp(TimeStampValue(GTC, QPC, useQPC)); |
|
522 } |
|
523 |
|
524 // Computes and returns the process uptime in microseconds. |
|
525 // Returns 0 if an error was encountered. |
|
526 |
|
527 uint64_t |
|
528 TimeStamp::ComputeProcessUptime() |
|
529 { |
|
530 SYSTEMTIME nowSys; |
|
531 GetSystemTime(&nowSys); |
|
532 |
|
533 FILETIME now; |
|
534 bool success = SystemTimeToFileTime(&nowSys, &now); |
|
535 |
|
536 if (!success) |
|
537 return 0; |
|
538 |
|
539 FILETIME start, foo, bar, baz; |
|
540 success = GetProcessTimes(GetCurrentProcess(), &start, &foo, &bar, &baz); |
|
541 |
|
542 if (!success) |
|
543 return 0; |
|
544 |
|
545 ULARGE_INTEGER startUsec = { |
|
546 start.dwLowDateTime, |
|
547 start.dwHighDateTime |
|
548 }; |
|
549 ULARGE_INTEGER nowUsec = { |
|
550 now.dwLowDateTime, |
|
551 now.dwHighDateTime |
|
552 }; |
|
553 |
|
554 return (nowUsec.QuadPart - startUsec.QuadPart) / 10ULL; |
|
555 } |
|
556 |
|
557 } // namespace mozilla |