Commit 5e468666 authored by cbruni's avatar cbruni Committed by Commit bot

[counters] Making runtime counters reentrant.

So far counters did not work when they were reentrant and thus would lead to
wrong bookkeeping of the counter stack. Using a separate stack-allocated linked
list to track the timer stack solves this issue. This is a temporary workaround
with the limitations of the counter system in mind. Eventually we will move to
the trace-based system for these kind of statistics.

BUG=v8:4770
LOG=n

Review URL: https://codereview.chromium.org/1695733002

Cr-Commit-Position: refs/heads/master@{#34208}
parent 5ae02268
...@@ -271,23 +271,16 @@ double ClobberDoubleRegisters(double x1, double x2, double x3, double x4); ...@@ -271,23 +271,16 @@ double ClobberDoubleRegisters(double x1, double x2, double x3, double x4);
#define CLOBBER_DOUBLE_REGISTERS() #define CLOBBER_DOUBLE_REGISTERS()
#endif #endif
#define RUNTIME_FUNCTION_RETURNS_TYPE(Type, Name) \ #define RUNTIME_FUNCTION_RETURNS_TYPE(Type, Name) \
static INLINE(Type __RT_impl_##Name(Arguments args, Isolate* isolate)); \ static INLINE(Type __RT_impl_##Name(Arguments args, Isolate* isolate)); \
Type Name(int args_length, Object** args_object, Isolate* isolate) { \ Type Name(int args_length, Object** args_object, Isolate* isolate) { \
CLOBBER_DOUBLE_REGISTERS(); \ CLOBBER_DOUBLE_REGISTERS(); \
base::ElapsedTimer timer; \ RuntimeCallStats* stats = isolate->counters()->runtime_call_stats(); \
if (FLAG_runtime_call_stats) { \ RuntimeCallTimerScope timer(isolate, &stats->Name); \
RuntimeCallStats* stats = isolate->counters()->runtime_call_stats(); \ Arguments args(args_length, args_object); \
stats->Enter(&stats->Name); \ Type value = __RT_impl_##Name(args, isolate); \
timer.Start(); \ return value; \
} \ } \
Arguments args(args_length, args_object); \
Type value = __RT_impl_##Name(args, isolate); \
if (FLAG_runtime_call_stats) { \
isolate->counters()->runtime_call_stats()->Leave(timer.Elapsed()); \
} \
return value; \
} \
static Type __RT_impl_##Name(Arguments args, Isolate* isolate) static Type __RT_impl_##Name(Arguments args, Isolate* isolate)
#define RUNTIME_FUNCTION(Name) RUNTIME_FUNCTION_RETURNS_TYPE(Object*, Name) #define RUNTIME_FUNCTION(Name) RUNTIME_FUNCTION_RETURNS_TYPE(Object*, Name)
......
...@@ -143,17 +143,10 @@ BUILTIN_LIST_C(DEF_ARG_TYPE) ...@@ -143,17 +143,10 @@ BUILTIN_LIST_C(DEF_ARG_TYPE)
MUST_USE_RESULT static Object* Builtin_##name( \ MUST_USE_RESULT static Object* Builtin_##name( \
int args_length, Object** args_object, Isolate* isolate) { \ int args_length, Object** args_object, Isolate* isolate) { \
isolate->counters()->runtime_calls()->Increment(); \ isolate->counters()->runtime_calls()->Increment(); \
base::ElapsedTimer timer; \ RuntimeCallStats* stats = isolate->counters()->runtime_call_stats(); \
if (FLAG_runtime_call_stats) { \ RuntimeCallTimerScope timer(isolate, &stats->Builtin_##name); \
RuntimeCallStats* stats = isolate->counters()->runtime_call_stats(); \
stats->Enter(&stats->Builtin_##name); \
timer.Start(); \
} \
name##ArgumentsType args(args_length, args_object); \ name##ArgumentsType args(args_length, args_object); \
Object* value = Builtin_Impl_##name(args, isolate); \ Object* value = Builtin_Impl_##name(args, isolate); \
if (FLAG_runtime_call_stats) { \
isolate->counters()->runtime_call_stats()->Leave(timer.Elapsed()); \
} \
return value; \ return value; \
} \ } \
\ \
......
...@@ -198,24 +198,24 @@ void Counters::ResetHistograms() { ...@@ -198,24 +198,24 @@ void Counters::ResetHistograms() {
class RuntimeCallStatEntries { class RuntimeCallStatEntries {
public: public:
void Print(std::ostream& os) { void Print(std::ostream& os) {
if (total_call_count > 0) { if (total_call_count == 0) return;
std::sort(entries.rbegin(), entries.rend()); std::sort(entries.rbegin(), entries.rend());
os << std::setw(50) << "Runtime Function/C++ Builtin" << std::setw(10) os << std::setw(50) << "Runtime Function/C++ Builtin" << std::setw(10)
<< "Time" << std::setw(18) << "Count" << std::endl << "Time" << std::setw(18) << "Count" << std::endl
<< std::string(86, '=') << std::endl; << std::string(86, '=') << std::endl;
for (Entry& entry : entries) { for (Entry& entry : entries) {
entry.SetTotal(total_time, total_call_count); entry.SetTotal(total_time, total_call_count);
entry.Print(os); entry.Print(os);
}
os << std::string(86, '-') << std::endl;
Entry("Total", total_time, total_call_count).Print(os);
} }
os << std::string(86, '-') << std::endl;
Entry("Total", total_time, total_call_count).Print(os);
} }
void Add(const char* name, RuntimeCallCounter counter) { void Add(RuntimeCallCounter* counter) {
entries.push_back(Entry(name, counter.time, counter.count)); if (counter->count == 0) return;
total_time += counter.time; entries.push_back(Entry(counter->name, counter->time, counter->count));
total_call_count += counter.count; total_time += counter->time;
total_call_count += counter->count;
} }
private: private:
...@@ -246,7 +246,11 @@ class RuntimeCallStatEntries { ...@@ -246,7 +246,11 @@ class RuntimeCallStatEntries {
} }
void SetTotal(base::TimeDelta total_time, uint64_t total_count) { void SetTotal(base::TimeDelta total_time, uint64_t total_count) {
time_percent_ = 100.0 * time_ / total_time.InMilliseconds(); if (total_time.InMilliseconds() == 0) {
time_percent_ = 0;
} else {
time_percent_ = 100.0 * time_ / total_time.InMilliseconds();
}
count_percent_ = 100.0 * count_ / total_count; count_percent_ = 100.0 * count_ / total_count;
} }
...@@ -269,35 +273,38 @@ void RuntimeCallCounter::Reset() { ...@@ -269,35 +273,38 @@ void RuntimeCallCounter::Reset() {
} }
void RuntimeCallStats::Enter(RuntimeCallCounter* counter) { void RuntimeCallStats::Enter(RuntimeCallCounter* counter) {
counter->count++; Enter(new RuntimeCallTimer(counter, current_timer_));
counter->parent_counter = current_counter;
current_counter = counter;
} }
void RuntimeCallStats::Leave(base::TimeDelta time) {
RuntimeCallCounter* counter = current_counter; void RuntimeCallStats::Enter(RuntimeCallTimer* timer_) {
counter->time += time; current_timer_ = timer_;
current_counter = counter->parent_counter; current_timer_->Start();
counter->parent_counter = NULL; }
if (current_counter != NULL) {
current_counter->time -= time; void RuntimeCallStats::Leave() {
} RuntimeCallTimer* timer = current_timer_;
Leave(timer);
delete timer;
}
void RuntimeCallStats::Leave(RuntimeCallTimer* timer) {
current_timer_ = timer->Stop();
} }
void RuntimeCallStats::Print(std::ostream& os) { void RuntimeCallStats::Print(std::ostream& os) {
RuntimeCallStatEntries entries; RuntimeCallStatEntries entries;
#define PRINT_COUNTER(name, nargs, ressize) \ #define PRINT_COUNTER(name, nargs, ressize) entries.Add(&this->Runtime_##name);
if (this->Runtime_##name.count > 0) { \
entries.Add(#name, this->Runtime_##name); \
}
FOR_EACH_INTRINSIC(PRINT_COUNTER) FOR_EACH_INTRINSIC(PRINT_COUNTER)
#undef PRINT_COUNTER #undef PRINT_COUNTER
#define PRINT_COUNTER(name, type) \
if (this->Builtin_##name.count > 0) { \ #define PRINT_COUNTER(name, type) entries.Add(&this->Builtin_##name);
entries.Add(#name, this->Builtin_##name); \
}
BUILTIN_LIST_C(PRINT_COUNTER) BUILTIN_LIST_C(PRINT_COUNTER)
#undef PRINT_COUNTER #undef PRINT_COUNTER
entries.Add(&this->ExternalCallback);
entries.Add(&this->UnexpectedStubMiss);
entries.Print(os); entries.Print(os);
} }
...@@ -310,5 +317,19 @@ void RuntimeCallStats::Reset() { ...@@ -310,5 +317,19 @@ void RuntimeCallStats::Reset() {
#undef RESET_COUNTER #undef RESET_COUNTER
} }
RuntimeCallTimerScope::RuntimeCallTimerScope(Isolate* isolate,
RuntimeCallCounter* counter)
: isolate_(isolate),
timer_(counter,
isolate->counters()->runtime_call_stats()->current_timer()) {
if (!FLAG_runtime_call_stats) return;
isolate->counters()->runtime_call_stats()->Enter(&timer_);
}
RuntimeCallTimerScope::~RuntimeCallTimerScope() {
if (!FLAG_runtime_call_stats) return;
isolate_->counters()->runtime_call_stats()->Leave(&timer_);
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -332,8 +332,8 @@ class AggregatableHistogramTimer : public Histogram { ...@@ -332,8 +332,8 @@ class AggregatableHistogramTimer : public Histogram {
}; };
// A helper class for use with AggregatableHistogramTimer. This is the // A helper class for use with AggregatableHistogramTimer. This is the
// outer-most timer scope used with an AggregatableHistogramTimer. It will // // outer-most timer scope used with an AggregatableHistogramTimer. It will
// aggregate the information from the inner AggregatedHistogramTimerScope. // // aggregate the information from the inner AggregatedHistogramTimerScope.
class AggregatingHistogramTimerScope { class AggregatingHistogramTimerScope {
public: public:
explicit AggregatingHistogramTimerScope(AggregatableHistogramTimer* histogram) explicit AggregatingHistogramTimerScope(AggregatableHistogramTimer* histogram)
...@@ -347,7 +347,7 @@ class AggregatingHistogramTimerScope { ...@@ -347,7 +347,7 @@ class AggregatingHistogramTimerScope {
}; };
// A helper class for use with AggregatableHistogramTimer, the "inner" scope // A helper class for use with AggregatableHistogramTimer, the "inner" scope
// which defines the events to be timed. // // which defines the events to be timed.
class AggregatedHistogramTimerScope { class AggregatedHistogramTimerScope {
public: public:
explicit AggregatedHistogramTimerScope(AggregatableHistogramTimer* histogram) explicit AggregatedHistogramTimerScope(AggregatableHistogramTimer* histogram)
...@@ -479,6 +479,91 @@ double AggregatedMemoryHistogram<Histogram>::Aggregate(double current_ms, ...@@ -479,6 +479,91 @@ double AggregatedMemoryHistogram<Histogram>::Aggregate(double current_ms,
value * ((current_ms - last_ms_) / interval_ms); value * ((current_ms - last_ms_) / interval_ms);
} }
struct RuntimeCallCounter {
explicit RuntimeCallCounter(const char* name) : name(name) {}
void Reset();
const char* name;
int64_t count = 0;
base::TimeDelta time;
};
// RuntimeCallTimer is used to keep track of the stack of currently active
// timers used for properly measuring the own time of a RuntimeCallCounter.
class RuntimeCallTimer {
public:
RuntimeCallTimer(RuntimeCallCounter* counter, RuntimeCallTimer* parent)
: counter_(counter), parent_(parent) {}
inline void Start() {
timer_.Start();
counter_->count++;
}
inline RuntimeCallTimer* Stop() {
base::TimeDelta delta = timer_.Elapsed();
counter_->time += delta;
if (parent_ != NULL) {
parent_->AdjustForSubTimer(delta);
}
return parent_;
}
void AdjustForSubTimer(base::TimeDelta delta) { counter_->time -= delta; }
private:
RuntimeCallCounter* counter_;
RuntimeCallTimer* parent_;
base::ElapsedTimer timer_;
};
struct RuntimeCallStats {
// Dummy counter for the unexpected stub miss.
RuntimeCallCounter UnexpectedStubMiss =
RuntimeCallCounter("UnexpectedStubMiss");
// Counter for runtime callbacks into JavaScript.
RuntimeCallCounter ExternalCallback = RuntimeCallCounter("ExternalCallback");
#define CALL_RUNTIME_COUNTER(name, nargs, ressize) \
RuntimeCallCounter Runtime_##name = RuntimeCallCounter(#name);
FOR_EACH_INTRINSIC(CALL_RUNTIME_COUNTER)
#undef CALL_RUNTIME_COUNTER
#define CALL_BUILTIN_COUNTER(name, type) \
RuntimeCallCounter Builtin_##name = RuntimeCallCounter(#name);
BUILTIN_LIST_C(CALL_BUILTIN_COUNTER)
#undef CALL_BUILTIN_COUNTER
// Counter to track recursive time events.
RuntimeCallTimer* current_timer_ = NULL;
// Starting measuring the time for a function. This will establish the
// connection to the parent counter for properly calculating the own times.
void Enter(RuntimeCallCounter* counter);
void Enter(RuntimeCallTimer* timer);
// Leave a scope for a measured runtime function. This will properly add
// the time delta to the current_counter and subtract the delta from its
// parent.
void Leave();
void Leave(RuntimeCallTimer* timer);
RuntimeCallTimer* current_timer() { return current_timer_; }
void Reset();
void Print(std::ostream& os);
RuntimeCallStats() { Reset(); }
};
// A RuntimeCallTimerScopes wraps around a RuntimeCallTimer to measure the
// the time of C++ scope.
class RuntimeCallTimerScope {
public:
explicit RuntimeCallTimerScope(Isolate* isolate, RuntimeCallCounter* counter);
~RuntimeCallTimerScope();
private:
Isolate* isolate_;
RuntimeCallTimer timer_;
};
#define HISTOGRAM_RANGE_LIST(HR) \ #define HISTOGRAM_RANGE_LIST(HR) \
/* Generic range histograms */ \ /* Generic range histograms */ \
...@@ -703,42 +788,6 @@ double AggregatedMemoryHistogram<Histogram>::Aggregate(double current_ms, ...@@ -703,42 +788,6 @@ double AggregatedMemoryHistogram<Histogram>::Aggregate(double current_ms,
/* Total count of functions compiled using the baseline compiler. */ \ /* Total count of functions compiled using the baseline compiler. */ \
SC(total_baseline_compile_count, V8.TotalBaselineCompileCount) SC(total_baseline_compile_count, V8.TotalBaselineCompileCount)
typedef struct RuntimeCallCounter {
int64_t count = 0;
base::TimeDelta time;
RuntimeCallCounter* parent_counter;
void Reset();
} RuntimeCallCounter;
struct RuntimeCallStats {
#define CALL_RUNTIME_COUNTER(name, nargs, ressize) \
RuntimeCallCounter Runtime_##name;
FOR_EACH_INTRINSIC(CALL_RUNTIME_COUNTER)
#undef CALL_RUNTIME_COUNTER
#define CALL_BUILTIN_COUNTER(name, type) RuntimeCallCounter Builtin_##name;
BUILTIN_LIST_C(CALL_BUILTIN_COUNTER)
#undef CALL_BUILTIN_COUNTER
// Dummy counter for the unexpected stub miss.
RuntimeCallCounter UnexpectedStubMiss;
// Counter to track recursive time events.
RuntimeCallCounter* current_counter;
// Starting measuring the time for a function. This will establish the
// connection to the parent counter for properly calculating the own times.
void Enter(RuntimeCallCounter* counter);
// Leave a scope for a measured runtime function. This will properly add
// the time delta to the current_counter and subtract the delta from its
// parent.
void Leave(base::TimeDelta time);
void Reset();
void Print(std::ostream& os);
RuntimeCallStats() { Reset(); }
};
// This file contains all the v8 counters that are in use. // This file contains all the v8 counters that are in use.
class Counters { class Counters {
public: public:
......
...@@ -61,18 +61,25 @@ VMState<Tag>::~VMState() { ...@@ -61,18 +61,25 @@ VMState<Tag>::~VMState() {
isolate_->set_current_vm_state(previous_tag_); isolate_->set_current_vm_state(previous_tag_);
} }
ExternalCallbackScope::ExternalCallbackScope(Isolate* isolate, Address callback) ExternalCallbackScope::ExternalCallbackScope(Isolate* isolate, Address callback)
: isolate_(isolate), : isolate_(isolate),
callback_(callback), callback_(callback),
previous_scope_(isolate->external_callback_scope()) { previous_scope_(isolate->external_callback_scope()),
timer_(&isolate->counters()->runtime_call_stats()->ExternalCallback,
isolate->counters()->runtime_call_stats()->current_timer()) {
#ifdef USE_SIMULATOR #ifdef USE_SIMULATOR
scope_address_ = Simulator::current(isolate)->get_sp(); scope_address_ = Simulator::current(isolate)->get_sp();
#endif #endif
isolate_->set_external_callback_scope(this); isolate_->set_external_callback_scope(this);
if (FLAG_runtime_call_stats) {
isolate_->counters()->runtime_call_stats()->Enter(&timer_);
}
} }
ExternalCallbackScope::~ExternalCallbackScope() { ExternalCallbackScope::~ExternalCallbackScope() {
if (FLAG_runtime_call_stats) {
isolate_->counters()->runtime_call_stats()->Leave(&timer_);
}
isolate_->set_external_callback_scope(previous_scope_); isolate_->set_external_callback_scope(previous_scope_);
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define V8_VM_STATE_H_ #define V8_VM_STATE_H_
#include "src/allocation.h" #include "src/allocation.h"
#include "src/counters.h"
#include "src/isolate.h" #include "src/isolate.h"
namespace v8 { namespace v8 {
...@@ -48,6 +49,7 @@ class ExternalCallbackScope BASE_EMBEDDED { ...@@ -48,6 +49,7 @@ class ExternalCallbackScope BASE_EMBEDDED {
Isolate* isolate_; Isolate* isolate_;
Address callback_; Address callback_;
ExternalCallbackScope* previous_scope_; ExternalCallbackScope* previous_scope_;
RuntimeCallTimer timer_;
#ifdef USE_SIMULATOR #ifdef USE_SIMULATOR
Address scope_address_; Address scope_address_;
#endif #endif
......
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