1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/v8.h"
6
7#include "src/cpu-profiler-inl.h"
8
9#include "src/compiler.h"
10#include "src/frames-inl.h"
11#include "src/hashmap.h"
12#include "src/log-inl.h"
13#include "src/vm-state-inl.h"
14
15#include "include/v8-profiler.h"
16
17namespace v8 {
18namespace internal {
19
20static const int kProfilerStackSize = 64 * KB;
21
22
23ProfilerEventsProcessor::ProfilerEventsProcessor(
24    ProfileGenerator* generator,
25    Sampler* sampler,
26    base::TimeDelta period)
27    : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
28      generator_(generator),
29      sampler_(sampler),
30      running_(true),
31      period_(period),
32      last_code_event_id_(0), last_processed_code_event_id_(0) {
33}
34
35
36void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
37  event.generic.order = ++last_code_event_id_;
38  events_buffer_.Enqueue(event);
39}
40
41
42void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) {
43  TickSampleEventRecord record(last_code_event_id_);
44  RegisterState regs;
45  StackFrameIterator it(isolate);
46  if (!it.done()) {
47    StackFrame* frame = it.frame();
48    regs.sp = frame->sp();
49    regs.fp = frame->fp();
50    regs.pc = frame->pc();
51  }
52  record.sample.Init(isolate, regs);
53  ticks_from_vm_buffer_.Enqueue(record);
54}
55
56
57void ProfilerEventsProcessor::StopSynchronously() {
58  if (!running_) return;
59  running_ = false;
60  Join();
61}
62
63
64bool ProfilerEventsProcessor::ProcessCodeEvent() {
65  CodeEventsContainer record;
66  if (events_buffer_.Dequeue(&record)) {
67    switch (record.generic.type) {
68#define PROFILER_TYPE_CASE(type, clss)                          \
69      case CodeEventRecord::type:                               \
70        record.clss##_.UpdateCodeMap(generator_->code_map());   \
71        break;
72
73      CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE)
74
75#undef PROFILER_TYPE_CASE
76      default: return true;  // Skip record.
77    }
78    last_processed_code_event_id_ = record.generic.order;
79    return true;
80  }
81  return false;
82}
83
84ProfilerEventsProcessor::SampleProcessingResult
85    ProfilerEventsProcessor::ProcessOneSample() {
86  if (!ticks_from_vm_buffer_.IsEmpty()
87      && ticks_from_vm_buffer_.Peek()->order ==
88         last_processed_code_event_id_) {
89    TickSampleEventRecord record;
90    ticks_from_vm_buffer_.Dequeue(&record);
91    generator_->RecordTickSample(record.sample);
92    return OneSampleProcessed;
93  }
94
95  const TickSampleEventRecord* record = ticks_buffer_.Peek();
96  if (record == NULL) {
97    if (ticks_from_vm_buffer_.IsEmpty()) return NoSamplesInQueue;
98    return FoundSampleForNextCodeEvent;
99  }
100  if (record->order != last_processed_code_event_id_) {
101    return FoundSampleForNextCodeEvent;
102  }
103  generator_->RecordTickSample(record->sample);
104  ticks_buffer_.Remove();
105  return OneSampleProcessed;
106}
107
108
109void ProfilerEventsProcessor::Run() {
110  while (running_) {
111    base::ElapsedTimer timer;
112    timer.Start();
113    // Keep processing existing events until we need to do next sample.
114    do {
115      if (FoundSampleForNextCodeEvent == ProcessOneSample()) {
116        // All ticks of the current last_processed_code_event_id_ are
117        // processed, proceed to the next code event.
118        ProcessCodeEvent();
119      }
120    } while (!timer.HasExpired(period_));
121
122    // Schedule next sample. sampler_ is NULL in tests.
123    if (sampler_) sampler_->DoSample();
124  }
125
126  // Process remaining tick events.
127  do {
128    SampleProcessingResult result;
129    do {
130      result = ProcessOneSample();
131    } while (result == OneSampleProcessed);
132  } while (ProcessCodeEvent());
133}
134
135
136void* ProfilerEventsProcessor::operator new(size_t size) {
137  return AlignedAlloc(size, V8_ALIGNOF(ProfilerEventsProcessor));
138}
139
140
141void ProfilerEventsProcessor::operator delete(void* ptr) {
142  AlignedFree(ptr);
143}
144
145
146int CpuProfiler::GetProfilesCount() {
147  // The count of profiles doesn't depend on a security token.
148  return profiles_->profiles()->length();
149}
150
151
152CpuProfile* CpuProfiler::GetProfile(int index) {
153  return profiles_->profiles()->at(index);
154}
155
156
157void CpuProfiler::DeleteAllProfiles() {
158  if (is_profiling_) StopProcessor();
159  ResetProfiles();
160}
161
162
163void CpuProfiler::DeleteProfile(CpuProfile* profile) {
164  profiles_->RemoveProfile(profile);
165  delete profile;
166  if (profiles_->profiles()->is_empty() && !is_profiling_) {
167    // If this was the last profile, clean up all accessory data as well.
168    ResetProfiles();
169  }
170}
171
172
173static bool FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag) {
174  return FLAG_prof_browser_mode
175      && (tag != Logger::CALLBACK_TAG
176          && tag != Logger::FUNCTION_TAG
177          && tag != Logger::LAZY_COMPILE_TAG
178          && tag != Logger::REG_EXP_TAG
179          && tag != Logger::SCRIPT_TAG);
180}
181
182
183void CpuProfiler::CallbackEvent(Name* name, Address entry_point) {
184  if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
185  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
186  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
187  rec->start = entry_point;
188  rec->entry = profiles_->NewCodeEntry(
189      Logger::CALLBACK_TAG,
190      profiles_->GetName(name));
191  rec->size = 1;
192  rec->shared = NULL;
193  processor_->Enqueue(evt_rec);
194}
195
196
197void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
198                                  Code* code,
199                                  const char* name) {
200  if (FilterOutCodeCreateEvent(tag)) return;
201  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
202  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
203  rec->start = code->address();
204  rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
205  rec->size = code->ExecutableSize();
206  rec->shared = NULL;
207  processor_->Enqueue(evt_rec);
208}
209
210
211void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
212                                  Code* code,
213                                  Name* name) {
214  if (FilterOutCodeCreateEvent(tag)) return;
215  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
216  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
217  rec->start = code->address();
218  rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
219  rec->size = code->ExecutableSize();
220  rec->shared = NULL;
221  processor_->Enqueue(evt_rec);
222}
223
224
225void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code,
226                                  SharedFunctionInfo* shared,
227                                  CompilationInfo* info, Name* script_name) {
228  if (FilterOutCodeCreateEvent(tag)) return;
229  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
230  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
231  rec->start = code->address();
232  rec->entry = profiles_->NewCodeEntry(
233      tag, profiles_->GetFunctionName(shared->DebugName()),
234      CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name));
235  if (info) {
236    rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
237  }
238  if (shared->script()->IsScript()) {
239    DCHECK(Script::cast(shared->script()));
240    Script* script = Script::cast(shared->script());
241    rec->entry->set_script_id(script->id()->value());
242    rec->entry->set_bailout_reason(
243        GetBailoutReason(shared->DisableOptimizationReason()));
244  }
245  rec->size = code->ExecutableSize();
246  rec->shared = shared->address();
247  processor_->Enqueue(evt_rec);
248}
249
250
251void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code,
252                                  SharedFunctionInfo* shared,
253                                  CompilationInfo* info, Name* script_name,
254                                  int line, int column) {
255  if (FilterOutCodeCreateEvent(tag)) return;
256  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
257  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
258  rec->start = code->address();
259  rec->entry = profiles_->NewCodeEntry(
260      tag, profiles_->GetFunctionName(shared->DebugName()),
261      CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name), line,
262      column);
263  if (info) {
264    rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
265  }
266  DCHECK(Script::cast(shared->script()));
267  Script* script = Script::cast(shared->script());
268  rec->entry->set_script_id(script->id()->value());
269  rec->size = code->ExecutableSize();
270  rec->shared = shared->address();
271  rec->entry->set_bailout_reason(
272      GetBailoutReason(shared->DisableOptimizationReason()));
273  processor_->Enqueue(evt_rec);
274}
275
276
277void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
278                                  Code* code,
279                                  int args_count) {
280  if (FilterOutCodeCreateEvent(tag)) return;
281  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
282  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
283  rec->start = code->address();
284  rec->entry = profiles_->NewCodeEntry(
285      tag,
286      profiles_->GetName(args_count),
287      "args_count: ");
288  rec->size = code->ExecutableSize();
289  rec->shared = NULL;
290  processor_->Enqueue(evt_rec);
291}
292
293
294void CpuProfiler::CodeMoveEvent(Address from, Address to) {
295  CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
296  CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
297  rec->from = from;
298  rec->to = to;
299  processor_->Enqueue(evt_rec);
300}
301
302
303void CpuProfiler::CodeDisableOptEvent(Code* code, SharedFunctionInfo* shared) {
304  CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
305  CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
306  rec->start = code->address();
307  rec->bailout_reason = GetBailoutReason(shared->DisableOptimizationReason());
308  processor_->Enqueue(evt_rec);
309}
310
311
312void CpuProfiler::CodeDeleteEvent(Address from) {
313}
314
315
316void CpuProfiler::SharedFunctionInfoMoveEvent(Address from, Address to) {
317  CodeEventsContainer evt_rec(CodeEventRecord::SHARED_FUNC_MOVE);
318  SharedFunctionInfoMoveEventRecord* rec =
319      &evt_rec.SharedFunctionInfoMoveEventRecord_;
320  rec->from = from;
321  rec->to = to;
322  processor_->Enqueue(evt_rec);
323}
324
325
326void CpuProfiler::GetterCallbackEvent(Name* name, Address entry_point) {
327  if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
328  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
329  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
330  rec->start = entry_point;
331  rec->entry = profiles_->NewCodeEntry(
332      Logger::CALLBACK_TAG,
333      profiles_->GetName(name),
334      "get ");
335  rec->size = 1;
336  rec->shared = NULL;
337  processor_->Enqueue(evt_rec);
338}
339
340
341void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) {
342  if (FilterOutCodeCreateEvent(Logger::REG_EXP_TAG)) return;
343  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
344  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
345  rec->start = code->address();
346  rec->entry = profiles_->NewCodeEntry(
347      Logger::REG_EXP_TAG,
348      profiles_->GetName(source),
349      "RegExp: ");
350  rec->size = code->ExecutableSize();
351  processor_->Enqueue(evt_rec);
352}
353
354
355void CpuProfiler::SetterCallbackEvent(Name* name, Address entry_point) {
356  if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
357  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
358  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
359  rec->start = entry_point;
360  rec->entry = profiles_->NewCodeEntry(
361      Logger::CALLBACK_TAG,
362      profiles_->GetName(name),
363      "set ");
364  rec->size = 1;
365  rec->shared = NULL;
366  processor_->Enqueue(evt_rec);
367}
368
369
370CpuProfiler::CpuProfiler(Isolate* isolate)
371    : isolate_(isolate),
372      sampling_interval_(base::TimeDelta::FromMicroseconds(
373          FLAG_cpu_profiler_sampling_interval)),
374      profiles_(new CpuProfilesCollection(isolate->heap())),
375      generator_(NULL),
376      processor_(NULL),
377      is_profiling_(false) {
378}
379
380
381CpuProfiler::CpuProfiler(Isolate* isolate,
382                         CpuProfilesCollection* test_profiles,
383                         ProfileGenerator* test_generator,
384                         ProfilerEventsProcessor* test_processor)
385    : isolate_(isolate),
386      sampling_interval_(base::TimeDelta::FromMicroseconds(
387          FLAG_cpu_profiler_sampling_interval)),
388      profiles_(test_profiles),
389      generator_(test_generator),
390      processor_(test_processor),
391      is_profiling_(false) {
392}
393
394
395CpuProfiler::~CpuProfiler() {
396  DCHECK(!is_profiling_);
397  delete profiles_;
398}
399
400
401void CpuProfiler::set_sampling_interval(base::TimeDelta value) {
402  DCHECK(!is_profiling_);
403  sampling_interval_ = value;
404}
405
406
407void CpuProfiler::ResetProfiles() {
408  delete profiles_;
409  profiles_ = new CpuProfilesCollection(isolate()->heap());
410}
411
412
413void CpuProfiler::StartProfiling(const char* title, bool record_samples) {
414  if (profiles_->StartProfiling(title, record_samples)) {
415    StartProcessorIfNotStarted();
416  }
417}
418
419
420void CpuProfiler::StartProfiling(String* title, bool record_samples) {
421  StartProfiling(profiles_->GetName(title), record_samples);
422}
423
424
425void CpuProfiler::StartProcessorIfNotStarted() {
426  if (processor_ != NULL) {
427    processor_->AddCurrentStack(isolate_);
428    return;
429  }
430  Logger* logger = isolate_->logger();
431  // Disable logging when using the new implementation.
432  saved_is_logging_ = logger->is_logging_;
433  logger->is_logging_ = false;
434  generator_ = new ProfileGenerator(profiles_);
435  Sampler* sampler = logger->sampler();
436  processor_ = new ProfilerEventsProcessor(
437      generator_, sampler, sampling_interval_);
438  is_profiling_ = true;
439  // Enumerate stuff we already have in the heap.
440  DCHECK(isolate_->heap()->HasBeenSetUp());
441  if (!FLAG_prof_browser_mode) {
442    logger->LogCodeObjects();
443  }
444  logger->LogCompiledFunctions();
445  logger->LogAccessorCallbacks();
446  LogBuiltins();
447  // Enable stack sampling.
448  sampler->SetHasProcessingThread(true);
449  sampler->IncreaseProfilingDepth();
450  processor_->AddCurrentStack(isolate_);
451  processor_->StartSynchronously();
452}
453
454
455CpuProfile* CpuProfiler::StopProfiling(const char* title) {
456  if (!is_profiling_) return NULL;
457  StopProcessorIfLastProfile(title);
458  CpuProfile* result = profiles_->StopProfiling(title);
459  if (result != NULL) {
460    result->Print();
461  }
462  return result;
463}
464
465
466CpuProfile* CpuProfiler::StopProfiling(String* title) {
467  if (!is_profiling_) return NULL;
468  const char* profile_title = profiles_->GetName(title);
469  StopProcessorIfLastProfile(profile_title);
470  return profiles_->StopProfiling(profile_title);
471}
472
473
474void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
475  if (profiles_->IsLastProfile(title)) StopProcessor();
476}
477
478
479void CpuProfiler::StopProcessor() {
480  Logger* logger = isolate_->logger();
481  Sampler* sampler = reinterpret_cast<Sampler*>(logger->ticker_);
482  is_profiling_ = false;
483  processor_->StopSynchronously();
484  delete processor_;
485  delete generator_;
486  processor_ = NULL;
487  generator_ = NULL;
488  sampler->SetHasProcessingThread(false);
489  sampler->DecreaseProfilingDepth();
490  logger->is_logging_ = saved_is_logging_;
491}
492
493
494void CpuProfiler::LogBuiltins() {
495  Builtins* builtins = isolate_->builtins();
496  DCHECK(builtins->is_initialized());
497  for (int i = 0; i < Builtins::builtin_count; i++) {
498    CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN);
499    ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
500    Builtins::Name id = static_cast<Builtins::Name>(i);
501    rec->start = builtins->builtin(id)->address();
502    rec->builtin_id = id;
503    processor_->Enqueue(evt_rec);
504  }
505}
506
507
508} }  // namespace v8::internal
509