Commit cb5dfb7d authored by Clemens Backes's avatar Clemens Backes Committed by V8 LUCI CQ

[base] Remove TimeTicks::HighResolutionNow

TimeTicks::HighResolutionNow is identical to TimeTicks::Now since 2018
(https://crrev.com/c/997153), but the declaration still has a wrong
comment about a non-existing DCHECK.
In order to avoid confusion, remove the redundant method and just use
TimeTicks::Now everywhere.

Drive-by: Make IsHighResolutionTimer "inline" instead of "V8_INLINE"
because it will only be called once anyway.

R=mlippautz@chromium.org

Bug: v8:12425
Change-Id: I31dc65f8c1ac910862e070e60e928054d4921154
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3439909Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78944}
parent 4c2b5f4d
......@@ -116,7 +116,7 @@ class ElapsedTimer final {
private:
static V8_INLINE TimeTicks Now() {
TimeTicks now = TimeTicks::HighResolutionNow();
TimeTicks now = TimeTicks::Now();
DCHECK(!now.IsNull());
return now;
}
......
......@@ -118,7 +118,7 @@ V8_INLINE int64_t NanosecondsNow() {
ts.tv_nsec;
}
V8_INLINE bool IsHighResolutionTimer(clockid_t clk_id) {
inline bool IsHighResolutionTimer(clockid_t clk_id) {
// Currently this is only needed for CLOCK_MONOTONIC. If other clocks need
// to be checked, care must be taken to support all platforms correctly;
// see ClockNow() above for precedent.
......@@ -477,16 +477,6 @@ Time Time::NowFromSystemTime() { return Now(); }
#endif // V8_OS_STARBOARD
// static
TimeTicks TimeTicks::HighResolutionNow() {
// a DCHECK of TimeTicks::IsHighResolution() was removed from here
// as it turns out this path is used in the wild for logs and counters.
//
// TODO(hpayer) We may eventually want to split TimedHistograms based
// on low resolution clocks to avoid polluting metrics
return TimeTicks::Now();
}
Time Time::FromJsTime(double ms_since_epoch) {
// The epoch is a valid time, so this constructor doesn't interpret
// 0 as the null time.
......@@ -739,7 +729,7 @@ TimeTicks TimeTicks::Now() {
#elif V8_OS_STARBOARD
ticks = SbTimeGetMonotonicNow();
#else
#error platform does not implement TimeTicks::HighResolutionNow.
#error platform does not implement TimeTicks::Now.
#endif // V8_OS_MACOSX
// Make sure we never return 0 here.
return TimeTicks(ticks + 1);
......@@ -750,7 +740,7 @@ bool TimeTicks::IsHighResolution() {
#if V8_OS_MACOSX
return true;
#elif V8_OS_POSIX
static bool is_high_resolution = IsHighResolutionTimer(CLOCK_MONOTONIC);
static const bool is_high_resolution = IsHighResolutionTimer(CLOCK_MONOTONIC);
return is_high_resolution;
#else
return true;
......
......@@ -433,11 +433,6 @@ class V8_BASE_EXPORT TimeTicks final
// This method never returns a null TimeTicks.
static TimeTicks Now();
// This is equivalent to Now() but DCHECKs that IsHighResolution(). Useful for
// test frameworks that rely on high resolution clocks (in practice all
// platforms but low-end Windows devices have high resolution clocks).
static TimeTicks HighResolutionNow();
// Returns true if the high-resolution clock is working on this system.
static bool IsHighResolution();
......
......@@ -87,8 +87,7 @@ RandomNumberGenerator::RandomNumberGenerator() {
// which provides reasonable entropy, see:
// https://code.google.com/p/v8/issues/detail?id=2905
int64_t seed = Time::NowFromSystemTime().ToInternalValue() << 24;
seed ^= TimeTicks::HighResolutionNow().ToInternalValue() << 16;
seed ^= TimeTicks::Now().ToInternalValue() << 8;
seed ^= TimeTicks::Now().ToInternalValue();
SetSeed(seed);
#endif // V8_OS_CYGWIN || V8_OS_WIN
}
......
......@@ -34,7 +34,7 @@ void WriteToFile(const char* prefix, FILE* file, Isolate* isolate,
} // anonymous namespace
D8Console::D8Console(Isolate* isolate) : isolate_(isolate) {
default_timer_ = base::TimeTicks::HighResolutionNow();
default_timer_ = base::TimeTicks::Now();
}
void D8Console::Assert(const debug::ConsoleCallArguments& args,
......@@ -75,7 +75,7 @@ void D8Console::Time(const debug::ConsoleCallArguments& args,
const v8::debug::ConsoleContext&) {
if (internal::FLAG_correctness_fuzzer_suppressions) return;
if (args.Length() == 0) {
default_timer_ = base::TimeTicks::HighResolutionNow();
default_timer_ = base::TimeTicks::Now();
} else {
Local<Value> arg = args[0];
Local<String> label;
......@@ -85,10 +85,10 @@ void D8Console::Time(const debug::ConsoleCallArguments& args,
std::string string(*utf8);
auto find = timers_.find(string);
if (find != timers_.end()) {
find->second = base::TimeTicks::HighResolutionNow();
find->second = base::TimeTicks::Now();
} else {
timers_.insert(std::pair<std::string, base::TimeTicks>(
string, base::TimeTicks::HighResolutionNow()));
string, base::TimeTicks::Now()));
}
}
}
......@@ -98,10 +98,10 @@ void D8Console::TimeEnd(const debug::ConsoleCallArguments& args,
if (internal::FLAG_correctness_fuzzer_suppressions) return;
base::TimeDelta delta;
if (args.Length() == 0) {
delta = base::TimeTicks::HighResolutionNow() - default_timer_;
delta = base::TimeTicks::Now() - default_timer_;
printf("console.timeEnd: default, %f\n", delta.InMillisecondsF());
} else {
base::TimeTicks now = base::TimeTicks::HighResolutionNow();
base::TimeTicks now = base::TimeTicks::Now();
Local<Value> arg = args[0];
Local<String> label;
v8::TryCatch try_catch(isolate_);
......@@ -120,7 +120,7 @@ void D8Console::TimeEnd(const debug::ConsoleCallArguments& args,
void D8Console::TimeStamp(const debug::ConsoleCallArguments& args,
const v8::debug::ConsoleContext&) {
if (internal::FLAG_correctness_fuzzer_suppressions) return;
base::TimeDelta delta = base::TimeTicks::HighResolutionNow() - default_timer_;
base::TimeDelta delta = base::TimeTicks::Now() - default_timer_;
if (args.Length() == 0) {
printf("console.timeStamp: default, %f\n", delta.InMillisecondsF());
} else {
......
......@@ -462,8 +462,7 @@ base::OS::MemoryMappedFile* Shell::counters_file_ = nullptr;
CounterCollection Shell::local_counters_;
CounterCollection* Shell::counters_ = &local_counters_;
base::LazyMutex Shell::context_mutex_;
const base::TimeTicks Shell::kInitialTicks =
base::TimeTicks::HighResolutionNow();
const base::TimeTicks Shell::kInitialTicks = base::TimeTicks::Now();
Global<Function> Shell::stringify_function_;
base::LazyMutex Shell::workers_mutex_;
bool Shell::allow_new_workers_ = true;
......@@ -1636,8 +1635,7 @@ void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (i::FLAG_verify_predictable) {
args.GetReturnValue().Set(g_platform->MonotonicallyIncreasingTime());
} else {
base::TimeDelta delta =
base::TimeTicks::HighResolutionNow() - kInitialTicks;
base::TimeDelta delta = base::TimeTicks::Now() - kInitialTicks;
args.GetReturnValue().Set(delta.InMillisecondsF());
}
}
......
......@@ -305,8 +305,7 @@ Response V8ProfilerAgentImpl::startPreciseCoverage(
Maybe<bool> callCount, Maybe<bool> detailed,
Maybe<bool> allowTriggeredUpdates, double* out_timestamp) {
if (!m_enabled) return Response::ServerError("Profiler is not enabled");
*out_timestamp =
v8::base::TimeTicks::HighResolutionNow().since_origin().InSecondsF();
*out_timestamp = v8::base::TimeTicks::Now().since_origin().InSecondsF();
bool callCountValue = callCount.fromMaybe(false);
bool detailedValue = detailed.fromMaybe(false);
bool allowTriggeredUpdatesValue = allowTriggeredUpdates.fromMaybe(false);
......@@ -419,8 +418,7 @@ Response V8ProfilerAgentImpl::takePreciseCoverage(
}
v8::HandleScope handle_scope(m_isolate);
v8::debug::Coverage coverage = v8::debug::Coverage::CollectPrecise(m_isolate);
*out_timestamp =
v8::base::TimeTicks::HighResolutionNow().since_origin().InSecondsF();
*out_timestamp = v8::base::TimeTicks::Now().since_origin().InSecondsF();
return coverageToProtocol(m_session->inspector(), coverage, out_result);
}
......@@ -439,8 +437,7 @@ void V8ProfilerAgentImpl::triggerPreciseCoverageDeltaUpdate(
std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>
out_result;
coverageToProtocol(m_session->inspector(), coverage, &out_result);
double now =
v8::base::TimeTicks::HighResolutionNow().since_origin().InSecondsF();
double now = v8::base::TimeTicks::Now().since_origin().InSecondsF();
m_frontend.preciseCoverageDeltaUpdate(now, occasion, std::move(out_result));
}
......
......@@ -121,7 +121,7 @@ DefaultPlatform::~DefaultPlatform() {
namespace {
double DefaultTimeFunction() {
return base::TimeTicks::HighResolutionNow().ToInternalValue() /
return base::TimeTicks::Now().ToInternalValue() /
static_cast<double>(base::Time::kMicrosecondsPerSecond);
}
......
......@@ -106,7 +106,7 @@ void TracingController::Initialize(TraceBuffer* trace_buffer) {
}
int64_t TracingController::CurrentTimestampMicroseconds() {
return base::TimeTicks::HighResolutionNow().ToInternalValue();
return base::TimeTicks::Now().ToInternalValue();
}
int64_t TracingController::CurrentCpuTimestampMicroseconds() {
......
......@@ -132,14 +132,14 @@ class V8_NODISCARD NestedTimedHistogramScope : public BaseTimedHistogramScope {
void StartInteral() {
previous_scope_ = timed_histogram()->Enter(this);
base::TimeTicks now = base::TimeTicks::HighResolutionNow();
base::TimeTicks now = base::TimeTicks::Now();
if (previous_scope_) previous_scope_->Pause(now);
timer_.Start(now);
}
void StopInternal() {
timed_histogram()->Leave(previous_scope_);
base::TimeTicks now = base::TimeTicks::HighResolutionNow();
base::TimeTicks now = base::TimeTicks::Now();
base::TimeDelta elapsed = timer_.Elapsed(now);
histogram_->AddTimedSample(elapsed);
if (isolate_) RecordLongTaskTime(elapsed);
......@@ -194,13 +194,13 @@ class V8_NODISCARD PauseNestedTimedHistogramScope {
: histogram_(histogram) {
previous_scope_ = histogram_->Enter(nullptr);
if (isEnabled()) {
previous_scope_->Pause(base::TimeTicks::HighResolutionNow());
previous_scope_->Pause(base::TimeTicks::Now());
}
}
~PauseNestedTimedHistogramScope() {
histogram_->Leave(previous_scope_);
if (isEnabled()) {
previous_scope_->Resume(base::TimeTicks::HighResolutionNow());
previous_scope_->Resume(base::TimeTicks::Now());
}
}
......
......@@ -14,8 +14,7 @@
namespace v8 {
namespace internal {
base::TimeTicks (*RuntimeCallTimer::Now)() =
&base::TimeTicks::HighResolutionNow;
base::TimeTicks (*RuntimeCallTimer::Now)() = &base::TimeTicks::Now;
base::TimeTicks RuntimeCallTimer::NowCPUTime() {
base::ThreadTicks ticks = base::ThreadTicks::Now();
......
......@@ -268,8 +268,7 @@ SamplingEventsProcessor::ProcessOneSample() {
void SamplingEventsProcessor::Run() {
base::MutexGuard guard(&running_mutex_);
while (running_.load(std::memory_order_relaxed)) {
base::TimeTicks nextSampleTime =
base::TimeTicks::HighResolutionNow() + period_;
base::TimeTicks nextSampleTime = base::TimeTicks::Now() + period_;
base::TimeTicks now;
SampleProcessingResult result;
// Keep processing existing events until we need to do next sample
......@@ -281,7 +280,7 @@ void SamplingEventsProcessor::Run() {
// processed, proceed to the next code event.
ProcessCodeEvent();
}
now = base::TimeTicks::HighResolutionNow();
now = base::TimeTicks::Now();
} while (result != NoSamplesInQueue && now < nextSampleTime);
if (nextSampleTime > now) {
......@@ -290,7 +289,7 @@ void SamplingEventsProcessor::Run() {
nextSampleTime - now < base::TimeDelta::FromMilliseconds(100)) {
// Do not use Sleep on Windows as it is very imprecise, with up to 16ms
// jitter, which is unacceptable for short profile intervals.
while (base::TimeTicks::HighResolutionNow() < nextSampleTime) {
while (base::TimeTicks::Now() < nextSampleTime) {
}
} else // NOLINT
#else
......@@ -307,7 +306,7 @@ void SamplingEventsProcessor::Run() {
if (!running_.load(std::memory_order_relaxed)) {
break;
}
now = base::TimeTicks::HighResolutionNow();
now = base::TimeTicks::Now();
}
}
}
......
......@@ -578,7 +578,7 @@ CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title,
: title_(title),
options_(options),
delegate_(std::move(delegate)),
start_time_(base::TimeTicks::HighResolutionNow()),
start_time_(base::TimeTicks::Now()),
top_down_(profiler->isolate(), profiler->code_entries()),
profiler_(profiler),
streaming_next_sample_(0),
......@@ -750,7 +750,7 @@ void CpuProfile::StreamPendingTraceEvents() {
}
void CpuProfile::FinishProfile() {
end_time_ = base::TimeTicks::HighResolutionNow();
end_time_ = base::TimeTicks::Now();
// Stop tracking context movements after profiling stops.
context_filter_.set_native_context_address(kNullAddress);
StreamPendingTraceEvents();
......
......@@ -200,7 +200,7 @@ DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate,
tos = nullptr;
}
sampling_interval_ = sampling_interval;
timestamp = base::TimeTicks::HighResolutionNow();
timestamp = base::TimeTicks::Now();
}
bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs,
......
......@@ -2394,7 +2394,7 @@ TEST(IdleNotificationFinishMarking) {
// The next idle notification has to finish incremental marking.
const double kLongIdleTime = 1000.0;
CcTest::isolate()->IdleNotificationDeadline(
(v8::base::TimeTicks::HighResolutionNow().ToInternalValue() /
(v8::base::TimeTicks::Now().ToInternalValue() /
static_cast<double>(v8::base::Time::kMicrosecondsPerSecond)) +
kLongIdleTime);
CHECK_EQ(CcTest::heap()->gc_count(), initial_gc_count + 1);
......
......@@ -16711,7 +16711,7 @@ TEST(TestIdleNotification) {
i::GarbageCollectionReason::kTesting);
}
finished = env->GetIsolate()->IdleNotificationDeadline(
(v8::base::TimeTicks::HighResolutionNow().ToInternalValue() /
(v8::base::TimeTicks::Now().ToInternalValue() /
static_cast<double>(v8::base::Time::kMicrosecondsPerSecond)) +
IdlePauseInSeconds);
if (CcTest::heap()->mark_compact_collector()->sweeping_in_progress()) {
......@@ -120,7 +120,7 @@ static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
sample.stack[1] = reinterpret_cast<void*>(frame3);
sample.frames_count = 2;
}
sample.timestamp = base::TimeTicks::HighResolutionNow();
sample.timestamp = base::TimeTicks::Now();
proc->AddSample(sample);
}
......@@ -424,7 +424,7 @@ TEST(Issue1398) {
for (unsigned i = 0; i < sample.frames_count; ++i) {
sample.stack[i] = reinterpret_cast<void*>(code->InstructionStart());
}
sample.timestamp = base::TimeTicks::HighResolutionNow();
sample.timestamp = base::TimeTicks::Now();
processor->AddSample(sample);
processor->StopSynchronously();
......
......@@ -477,7 +477,7 @@ TEST(SampleIds) {
// (root)#1 -> aaa #2 -> bbb #4 -> ccc #5 - sample2
// -> ccc #6 -> aaa #7 - sample3
TickSample sample1;
sample1.timestamp = v8::base::TimeTicks::HighResolutionNow();
sample1.timestamp = v8::base::TimeTicks::Now();
sample1.pc = ToPointer(0x1600);
sample1.stack[0] = ToPointer(0x1510);
sample1.frames_count = 1;
......@@ -487,7 +487,7 @@ TEST(SampleIds) {
base::TimeDelta(), StateTag::JS, EmbedderStateTag::EMPTY);
TickSample sample2;
sample2.timestamp = v8::base::TimeTicks::HighResolutionNow();
sample2.timestamp = v8::base::TimeTicks::Now();
sample2.pc = ToPointer(0x1925);
sample2.stack[0] = ToPointer(0x1780);
sample2.stack[1] = ToPointer(0x10000); // non-existent.
......@@ -499,7 +499,7 @@ TEST(SampleIds) {
base::TimeDelta(), StateTag::JS, EmbedderStateTag::EMPTY);
TickSample sample3;
sample3.timestamp = v8::base::TimeTicks::HighResolutionNow();
sample3.timestamp = v8::base::TimeTicks::Now();
sample3.pc = ToPointer(0x1510);
sample3.stack[0] = ToPointer(0x1910);
sample3.stack[1] = ToPointer(0x1610);
......@@ -598,7 +598,7 @@ TEST(MaxSamplesCallback) {
CodeMap code_map(storage);
Symbolizer symbolizer(&code_map);
TickSample sample1;
sample1.timestamp = v8::base::TimeTicks::HighResolutionNow();
sample1.timestamp = v8::base::TimeTicks::Now();
sample1.pc = ToPointer(0x1600);
sample1.stack[0] = ToPointer(0x1510);
sample1.frames_count = 1;
......@@ -608,7 +608,7 @@ TEST(MaxSamplesCallback) {
base::TimeDelta(), StateTag::JS, EmbedderStateTag::EMPTY);
CHECK_EQ(0, mock_platform->posted_count());
TickSample sample2;
sample2.timestamp = v8::base::TimeTicks::HighResolutionNow();
sample2.timestamp = v8::base::TimeTicks::Now();
sample2.pc = ToPointer(0x1925);
sample2.stack[0] = ToPointer(0x1780);
sample2.frames_count = 2;
......@@ -618,7 +618,7 @@ TEST(MaxSamplesCallback) {
base::TimeDelta(), StateTag::JS, EmbedderStateTag::EMPTY);
CHECK_EQ(1, mock_platform->posted_count());
TickSample sample3;
sample3.timestamp = v8::base::TimeTicks::HighResolutionNow();
sample3.timestamp = v8::base::TimeTicks::Now();
sample3.pc = ToPointer(0x1510);
sample3.frames_count = 3;
symbolized = symbolizer.SymbolizeTickSample(sample3);
......@@ -652,10 +652,9 @@ TEST(NoSamples) {
sample1.stack[0] = ToPointer(0x1510);
sample1.frames_count = 1;
auto symbolized = symbolizer.SymbolizeTickSample(sample1);
profiles.AddPathToCurrentProfiles(v8::base::TimeTicks::HighResolutionNow(),
symbolized.stack_trace, symbolized.src_line,
true, base::TimeDelta(), StateTag::JS,
EmbedderStateTag::EMPTY);
profiles.AddPathToCurrentProfiles(
v8::base::TimeTicks::Now(), symbolized.stack_trace, symbolized.src_line,
true, base::TimeDelta(), StateTag::JS, EmbedderStateTag::EMPTY);
CpuProfile* profile = profiles.StopProfiling("");
unsigned nodeId = 1;
......
......@@ -363,19 +363,14 @@ TEST(TimeTicks, NowResolution) {
}
TEST(TimeTicks, IsMonotonic) {
TimeTicks previous_normal_ticks;
TimeTicks previous_highres_ticks;
TimeTicks previous_ticks;
ElapsedTimer timer;
timer.Start();
while (!timer.HasExpired(TimeDelta::FromMilliseconds(100))) {
TimeTicks normal_ticks = TimeTicks::Now();
TimeTicks highres_ticks = TimeTicks::HighResolutionNow();
EXPECT_GE(normal_ticks, previous_normal_ticks);
EXPECT_GE((normal_ticks - previous_normal_ticks).InMicroseconds(), 0);
EXPECT_GE(highres_ticks, previous_highres_ticks);
EXPECT_GE((highres_ticks - previous_highres_ticks).InMicroseconds(), 0);
previous_normal_ticks = normal_ticks;
previous_highres_ticks = highres_ticks;
TimeTicks ticks = TimeTicks::Now();
EXPECT_GE(ticks, previous_ticks);
EXPECT_GE((ticks - previous_ticks).InMicroseconds(), 0);
previous_ticks = ticks;
}
}
......@@ -437,14 +432,14 @@ TEST(ElapsedTimer, StartStopArgs) {
DCHECK(!timer1.IsStarted());
DCHECK(!timer2.IsStarted());
TimeTicks now = TimeTicks::HighResolutionNow();
TimeTicks now = TimeTicks::Now();
timer1.Start(now);
timer2.Start(now);
DCHECK(timer1.IsStarted());
DCHECK(timer2.IsStarted());
Sleep(wait_time);
now = TimeTicks::HighResolutionNow();
now = TimeTicks::Now();
TimeDelta delta1 = timer1.Elapsed(now);
Sleep(wait_time);
TimeDelta delta2 = timer2.Elapsed(now);
......@@ -454,20 +449,20 @@ TEST(ElapsedTimer, StartStopArgs) {
Sleep(wait_time);
EXPECT_NE(delta1, timer2.Elapsed());
TimeTicks now2 = TimeTicks::HighResolutionNow();
TimeTicks now2 = TimeTicks::Now();
EXPECT_NE(timer1.Elapsed(now), timer1.Elapsed(now2));
EXPECT_NE(delta1, timer1.Elapsed(now2));
EXPECT_NE(delta2, timer2.Elapsed(now2));
EXPECT_GE(timer1.Elapsed(now2), timer2.Elapsed(now2));
now = TimeTicks::HighResolutionNow();
now = TimeTicks::Now();
timer1.Pause(now);
timer2.Pause(now);
DCHECK(timer1.IsPaused());
DCHECK(timer2.IsPaused());
Sleep(wait_time);
now = TimeTicks::HighResolutionNow();
now = TimeTicks::Now();
timer1.Resume(now);
DCHECK(!timer1.IsPaused());
DCHECK(timer2.IsPaused());
......
......@@ -27,7 +27,7 @@ class TestTask : public v8::Task {
};
double RealTime() {
return base::TimeTicks::HighResolutionNow().ToInternalValue() /
return base::TimeTicks::Now().ToInternalValue() /
static_cast<double>(base::Time::kMicrosecondsPerSecond);
}
......
......@@ -56,7 +56,7 @@ class RuntimeCallStatsTest : public TestWithNativeContext {
static void TearDownTestCase() {
TestWithIsolate::TearDownTestCase();
// Restore the original time source.
RuntimeCallTimer::Now = &base::TimeTicks::HighResolutionNow;
RuntimeCallTimer::Now = &base::TimeTicks::Now;
}
RuntimeCallStats* stats() {
......@@ -111,10 +111,10 @@ class V8_NODISCARD NativeTimeScope {
public:
NativeTimeScope() {
CHECK_EQ(RuntimeCallTimer::Now, &RuntimeCallStatsTestNow);
RuntimeCallTimer::Now = &base::TimeTicks::HighResolutionNow;
RuntimeCallTimer::Now = &base::TimeTicks::Now;
}
~NativeTimeScope() {
CHECK_EQ(RuntimeCallTimer::Now, &base::TimeTicks::HighResolutionNow);
CHECK_EQ(RuntimeCallTimer::Now, &base::TimeTicks::Now);
RuntimeCallTimer::Now = &RuntimeCallStatsTestNow;
}
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment