5#ifdef V8_RUNTIME_CALL_STATS
20base::TimeTicks RuntimeCallTimer::NowCPUTime() {
25class RuntimeCallStatEntries {
27 void Print(std::ostream& os) {
28 if (total_call_count_ == 0)
return;
30 os << std::setw(50) <<
"Runtime Function/C++ Builtin" << std::setw(12)
31 <<
"Time" << std::setw(18) <<
"Count" << std::endl
32 << std::string(88,
'=') << std::endl;
34 entry.SetTotal(total_time_, total_call_count_);
37 os << std::string(88,
'-') << std::endl;
38 Entry(
"Total", total_time_, total_call_count_).Print(os);
45 if (counter->count() == 0)
return;
47 Entry(counter->name(), counter->time(), counter->count()));
48 total_time_ += counter->time();
49 total_call_count_ += counter->count();
55 Entry(
const char* name, base::TimeDelta time, uint64_t count)
57 time_(time.InMicroseconds()),
60 count_percent_(100) {}
62 bool operator<(
const Entry& other)
const {
63 if (time_ < other.time_)
return true;
64 if (time_ > other.time_)
return false;
65 return count_ < other.count_;
70 os << std::fixed << std::setprecision(2);
71 os << std::setw(50) <<
name_;
72 os << std::setw(10) << static_cast<double>(time_) / 1000 <<
"ms ";
73 os << std::setw(6) << time_percent_ <<
"%";
74 os << std::setw(10) <<
count_ <<
" ";
75 os << std::setw(6) << count_percent_ <<
"%";
79 V8_NOINLINE void SetTotal(base::TimeDelta total_time,
80 uint64_t total_count) {
81 if (total_time.InMicroseconds() == 0) {
84 time_percent_ = 100.0 * time_ / total_time.InMicroseconds();
86 count_percent_ = 100.0 *
count_ / total_count;
94 double count_percent_;
97 uint64_t total_call_count_ = 0;
98 base::TimeDelta total_time_;
102void RuntimeCallCounter::Reset() {
108 value->BeginArray(
name_);
109 value->AppendDouble(
count_);
110 value->AppendDouble(time_);
114void RuntimeCallCounter::Add(RuntimeCallCounter* other) {
116 time_ += other->time().InMicroseconds();
119void RuntimeCallTimer::Snapshot() {
120 base::TimeTicks now = Now();
124 RuntimeCallTimer* timer =
this;
125 while (timer !=
nullptr) {
126 timer->CommitTimeToCounter();
127 timer = timer->parent();
133 : in_use_(false), thread_type_(thread_type) {
134 static const char*
const kNames[] = {
135#define CALL_BUILTIN_COUNTER(name) "GC_" #name,
136 FOR_EACH_GC_COUNTER(CALL_BUILTIN_COUNTER)
137#undef CALL_BUILTIN_COUNTER
138#define CALL_RUNTIME_COUNTER(name) #name,
139 FOR_EACH_MANUAL_COUNTER(CALL_RUNTIME_COUNTER)
140#undef CALL_RUNTIME_COUNTER
141#define CALL_RUNTIME_COUNTER(name, nargs, ressize) #name,
143#undef CALL_RUNTIME_COUNTER
144#define CALL_BUILTIN_COUNTER(name, Argc) #name,
146#undef CALL_BUILTIN_COUNTER
147#define CALL_BUILTIN_COUNTER(name) "API_" #name,
148 FOR_EACH_API_COUNTER(CALL_BUILTIN_COUNTER)
149#undef CALL_BUILTIN_COUNTER
150#define CALL_BUILTIN_COUNTER(name) #name,
151 FOR_EACH_HANDLER_COUNTER(CALL_BUILTIN_COUNTER)
152#undef CALL_BUILTIN_COUNTER
153#define THREAD_SPECIFIC_COUNTER(name) #name,
154 FOR_EACH_THREAD_SPECIFIC_COUNTER(THREAD_SPECIFIC_COUNTER)
155#undef THREAD_SPECIFIC_COUNTER
157 for (
int i = 0;
i < kNumberOfCounters;
i++) {
158 this->
counters_[
i] = RuntimeCallCounter(kNames[
i]);
161 CHECK(base::ThreadTicks::IsSupported());
162 base::ThreadTicks::WaitUntilInitialized();
163 RuntimeCallTimer::Now = &RuntimeCallTimer::NowCPUTime;
168constexpr RuntimeCallCounterId FirstCounter(RuntimeCallCounterId first, ...) {
172#define THREAD_SPECIFIC_COUNTER(name) RuntimeCallCounterId::k##name,
173constexpr RuntimeCallCounterId kFirstThreadVariantCounter =
174 FirstCounter(FOR_EACH_THREAD_SPECIFIC_COUNTER(THREAD_SPECIFIC_COUNTER) 0);
175#undef THREAD_SPECIFIC_COUNTER
177#define THREAD_SPECIFIC_COUNTER(name) +1
178constexpr int kThreadVariantCounterCount =
179 0 FOR_EACH_THREAD_SPECIFIC_COUNTER(THREAD_SPECIFIC_COUNTER);
180#undef THREAD_SPECIFIC_COUNTER
182constexpr auto kLastThreadVariantCounter =
static_cast<RuntimeCallCounterId
>(
183 static_cast<int>(kFirstThreadVariantCounter) + kThreadVariantCounterCount -
187bool RuntimeCallStats::HasThreadSpecificCounterVariants(
188 RuntimeCallCounterId
id) {
191 return id >= kFirstThreadVariantCounter &&
id <= kLastThreadVariantCounter;
194bool RuntimeCallStats::IsBackgroundThreadSpecificVariant(
195 RuntimeCallCounterId
id) {
196 return HasThreadSpecificCounterVariants(
id) &&
197 (
static_cast<int>(id) -
static_cast<int>(kFirstThreadVariantCounter)) %
202void RuntimeCallStats::Enter(RuntimeCallTimer* timer,
203 RuntimeCallCounterId counter_id) {
204 DCHECK(IsCalledOnTheSameThread());
205 RuntimeCallCounter* counter = GetCounter(counter_id);
207 timer->Start(counter, current_timer());
208 current_timer_.SetValue(timer);
209 current_counter_.SetValue(counter);
212void RuntimeCallStats::Leave(RuntimeCallTimer* timer) {
213 DCHECK(IsCalledOnTheSameThread());
214 RuntimeCallTimer* stack_top = current_timer();
215 if (stack_top ==
nullptr)
return;
216 CHECK(stack_top == timer);
217 current_timer_.SetValue(timer->Stop());
218 RuntimeCallTimer* cur_timer = current_timer();
219 current_counter_.SetValue(cur_timer ? cur_timer->counter() :
nullptr);
222void RuntimeCallStats::Add(RuntimeCallStats* other) {
223 for (
int i = 0;
i < kNumberOfCounters;
i++) {
224 GetCounter(
i)->Add(other->GetCounter(
i));
229void RuntimeCallStats::CorrectCurrentCounterId(RuntimeCallCounterId counter_id,
231 DCHECK(IsCalledOnTheSameThread());
232 if (mode == RuntimeCallStats::CounterMode::kThreadSpecific) {
233 counter_id = CounterIdForThread(counter_id);
235 DCHECK(IsCounterAppropriateForThread(counter_id));
237 RuntimeCallTimer* timer = current_timer();
238 if (timer ==
nullptr)
return;
239 RuntimeCallCounter* counter = GetCounter(counter_id);
240 timer->set_counter(counter);
241 current_counter_.SetValue(counter);
244bool RuntimeCallStats::IsCalledOnTheSameThread() {
245 if (thread_id_.IsValid())
return thread_id_ == ThreadId::Current();
246 thread_id_ = ThreadId::Current();
250void RuntimeCallStats::Print() {
255void RuntimeCallStats::Print(std::ostream& os) {
256 RuntimeCallStatEntries
entries;
257 if (current_timer_.Value() !=
nullptr) {
258 current_timer_.Value()->Snapshot();
260 for (
int i = 0;
i < kNumberOfCounters;
i++) {
266void RuntimeCallStats::Reset() {
267 if (
V8_LIKELY(!TracingFlags::is_runtime_stats_enabled()))
return;
273 while (current_timer_.Value()) {
274 current_timer_.SetValue(current_timer_.Value()->Stop());
277 for (
int i = 0;
i < kNumberOfCounters;
i++) {
278 GetCounter(
i)->Reset();
285 for (
int i = 0;
i < kNumberOfCounters;
i++) {
286 if (GetCounter(
i)->
count() > 0) GetCounter(
i)->Dump(value);
291WorkerThreadRuntimeCallStats::WorkerThreadRuntimeCallStats()
292 : isolate_thread_id_(ThreadId::Current()) {}
294WorkerThreadRuntimeCallStats::~WorkerThreadRuntimeCallStats() {
295 if (tls_key_) base::Thread::DeleteThreadLocalKey(*tls_key_);
298base::Thread::LocalStorageKey WorkerThreadRuntimeCallStats::GetKey() {
299 base::MutexGuard lock(&
mutex_);
300 if (!tls_key_) tls_key_ = base::Thread::CreateThreadLocalKey();
304RuntimeCallStats* WorkerThreadRuntimeCallStats::NewTable() {
306 DCHECK_NE(ThreadId::Current(), isolate_thread_id_);
307 std::unique_ptr<RuntimeCallStats> new_table =
308 std::make_unique<RuntimeCallStats>(RuntimeCallStats::kWorkerThread);
309 RuntimeCallStats*
result = new_table.get();
311 base::MutexGuard lock(&
mutex_);
312 tables_.push_back(std::move(new_table));
316void WorkerThreadRuntimeCallStats::AddToMainTable(
317 RuntimeCallStats* main_call_stats) {
318 base::MutexGuard lock(&
mutex_);
319 for (
auto& worker_stats : tables_) {
320 DCHECK_NE(main_call_stats, worker_stats.get());
321 main_call_stats->Add(worker_stats.get());
322 worker_stats->Reset();
326WorkerThreadRuntimeCallStatsScope::WorkerThreadRuntimeCallStatsScope(
327 WorkerThreadRuntimeCallStats* worker_stats) {
328 if (
V8_LIKELY(!TracingFlags::is_runtime_stats_enabled()))
return;
330 table_ =
reinterpret_cast<RuntimeCallStats*
>(
331 base::Thread::GetThreadLocal(worker_stats->GetKey()));
333 if (
V8_UNLIKELY(!TracingFlags::is_runtime_stats_enabled()))
return;
334 table_ = worker_stats->NewTable();
335 base::Thread::SetThreadLocal(worker_stats->GetKey(),
table_);
338 if ((TracingFlags::runtime_stats.load(std::memory_order_relaxed) &
344WorkerThreadRuntimeCallStatsScope::~WorkerThreadRuntimeCallStatsScope() {
347 if ((TracingFlags::runtime_stats.load(std::memory_order_relaxed) &
350 table_->Dump(value.get());
353 "runtime-call-stats", std::move(value));
#define BUILTIN_LIST_C(V)
static TimeTicks FromInternalValue(int64_t us)
V8_EXPORT_PRIVATE RuntimeCallStats(ThreadType thread_type)
static std::unique_ptr< TracedValue > Create()
ZoneVector< RpoNumber > & result
std::vector< EntryBuilder > entries_
ZoneVector< Entry > entries
void Add(RWDigits Z, Digits X, Digits Y)
V8_INLINE constexpr bool operator<(Builtin a, Builtin b)
V8_EXPORT_PRIVATE FlagValues v8_flags
SourcePositionTable *const table_
#define FOR_EACH_INTRINSIC(F)
#define DCHECK_NOT_NULL(val)
#define DCHECK_NE(v1, v2)
#define DCHECK(condition)
#define TRACE_EVENT_INSTANT1(category_group, name, scope, arg1_name, arg1_val)
#define TRACE_EVENT_SCOPE_THREAD
#define TRACE_DISABLED_BY_DEFAULT(name)
#define V8_LIKELY(condition)
#define V8_UNLIKELY(condition)