cpu-profiler.cc 16.9 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// 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/deoptimizer.h"
11 12 13 14
#include "src/frames-inl.h"
#include "src/hashmap.h"
#include "src/log-inl.h"
#include "src/vm-state-inl.h"
15

16
#include "include/v8-profiler.h"
17

18 19 20
namespace v8 {
namespace internal {

21
static const int kProfilerStackSize = 64 * KB;
22 23


24 25 26
ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator,
                                                 Sampler* sampler,
                                                 base::TimeDelta period)
27
    : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
28
      generator_(generator),
29
      sampler_(sampler),
30
      running_(1),
31
      period_(period),
32 33
      last_code_event_id_(0),
      last_processed_code_event_id_(0) {}
34 35


36
void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
37
  event.generic.order = ++last_code_event_id_;
38
  events_buffer_.Enqueue(event);
39 40 41
}


42 43 44 45 46 47 48 49 50 51 52 53 54
void ProfilerEventsProcessor::AddDeoptStack(Isolate* isolate, Address from,
                                            int fp_to_sp_delta) {
  TickSampleEventRecord record(last_code_event_id_);
  RegisterState regs;
  Address fp = isolate->c_entry_fp(isolate->thread_local_top());
  regs.sp = fp - fp_to_sp_delta;
  regs.fp = fp;
  regs.pc = from;
  record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame);
  ticks_from_vm_buffer_.Enqueue(record);
}


55
void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) {
56
  TickSampleEventRecord record(last_code_event_id_);
57 58 59 60 61 62 63
  RegisterState regs;
  StackFrameIterator it(isolate);
  if (!it.done()) {
    StackFrame* frame = it.frame();
    regs.sp = frame->sp();
    regs.fp = frame->fp();
    regs.pc = frame->pc();
64
  }
65
  record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame);
66 67 68 69
  ticks_from_vm_buffer_.Enqueue(record);
}


70
void ProfilerEventsProcessor::StopSynchronously() {
71
  if (!base::NoBarrier_AtomicExchange(&running_, 0)) return;
72 73 74 75 76
  Join();
}


bool ProfilerEventsProcessor::ProcessCodeEvent() {
77 78
  CodeEventsContainer record;
  if (events_buffer_.Dequeue(&record)) {
79 80 81 82 83 84 85 86 87 88 89
    switch (record.generic.type) {
#define PROFILER_TYPE_CASE(type, clss)                          \
      case CodeEventRecord::type:                               \
        record.clss##_.UpdateCodeMap(generator_->code_map());   \
        break;

      CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE)

#undef PROFILER_TYPE_CASE
      default: return true;  // Skip record.
    }
90
    last_processed_code_event_id_ = record.generic.order;
91 92 93 94 95
    return true;
  }
  return false;
}

96 97 98 99 100 101 102 103 104
ProfilerEventsProcessor::SampleProcessingResult
    ProfilerEventsProcessor::ProcessOneSample() {
  if (!ticks_from_vm_buffer_.IsEmpty()
      && ticks_from_vm_buffer_.Peek()->order ==
         last_processed_code_event_id_) {
    TickSampleEventRecord record;
    ticks_from_vm_buffer_.Dequeue(&record);
    generator_->RecordTickSample(record.sample);
    return OneSampleProcessed;
105
  }
106

107 108 109 110 111 112 113
  const TickSampleEventRecord* record = ticks_buffer_.Peek();
  if (record == NULL) {
    if (ticks_from_vm_buffer_.IsEmpty()) return NoSamplesInQueue;
    return FoundSampleForNextCodeEvent;
  }
  if (record->order != last_processed_code_event_id_) {
    return FoundSampleForNextCodeEvent;
114
  }
115 116 117
  generator_->RecordTickSample(record->sample);
  ticks_buffer_.Remove();
  return OneSampleProcessed;
118 119 120 121
}


void ProfilerEventsProcessor::Run() {
122
  while (!!base::NoBarrier_Load(&running_)) {
123
    base::ElapsedTimer timer;
124 125 126 127 128 129 130 131 132 133 134 135
    timer.Start();
    // Keep processing existing events until we need to do next sample.
    do {
      if (FoundSampleForNextCodeEvent == ProcessOneSample()) {
        // All ticks of the current last_processed_code_event_id_ are
        // processed, proceed to the next code event.
        ProcessCodeEvent();
      }
    } while (!timer.HasExpired(period_));

    // Schedule next sample. sampler_ is NULL in tests.
    if (sampler_) sampler_->DoSample();
136 137
  }

138
  // Process remaining tick events.
139
  do {
140 141 142 143
    SampleProcessingResult result;
    do {
      result = ProcessOneSample();
    } while (result == OneSampleProcessed);
144
  } while (ProcessCodeEvent());
145 146
}

147

148 149 150 151 152 153 154 155 156 157
void* ProfilerEventsProcessor::operator new(size_t size) {
  return AlignedAlloc(size, V8_ALIGNOF(ProfilerEventsProcessor));
}


void ProfilerEventsProcessor::operator delete(void* ptr) {
  AlignedFree(ptr);
}


158
int CpuProfiler::GetProfilesCount() {
159
  // The count of profiles doesn't depend on a security token.
160
  return profiles_->profiles()->length();
161 162 163
}


164 165
CpuProfile* CpuProfiler::GetProfile(int index) {
  return profiles_->profiles()->at(index);
166 167 168
}


169
void CpuProfiler::DeleteAllProfiles() {
170 171
  if (is_profiling_) StopProcessor();
  ResetProfiles();
172 173 174 175
}


void CpuProfiler::DeleteProfile(CpuProfile* profile) {
176
  profiles_->RemoveProfile(profile);
177
  delete profile;
178 179 180 181
  if (profiles_->profiles()->is_empty() && !is_profiling_) {
    // If this was the last profile, clean up all accessory data as well.
    ResetProfiles();
  }
182 183 184
}


185 186 187 188 189 190 191 192 193 194
static bool FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag) {
  return FLAG_prof_browser_mode
      && (tag != Logger::CALLBACK_TAG
          && tag != Logger::FUNCTION_TAG
          && tag != Logger::LAZY_COMPILE_TAG
          && tag != Logger::REG_EXP_TAG
          && tag != Logger::SCRIPT_TAG);
}


195
void CpuProfiler::CallbackEvent(Name* name, Address entry_point) {
196 197 198 199 200 201
  if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = entry_point;
  rec->entry = profiles_->NewCodeEntry(
      Logger::CALLBACK_TAG,
202
      profiles_->GetName(name));
203 204
  rec->size = 1;
  processor_->Enqueue(evt_rec);
205 206 207 208
}


void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
209 210 211 212 213 214
                                  Code* code,
                                  const char* name) {
  if (FilterOutCodeCreateEvent(tag)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = code->address();
215 216 217 218
  rec->entry = profiles_->NewCodeEntry(
      tag, profiles_->GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
      CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
      CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
219 220
  rec->size = code->ExecutableSize();
  processor_->Enqueue(evt_rec);
221 222 223 224
}


void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
225 226 227 228 229 230
                                  Code* code,
                                  Name* name) {
  if (FilterOutCodeCreateEvent(tag)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = code->address();
231 232 233 234
  rec->entry = profiles_->NewCodeEntry(
      tag, profiles_->GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
      CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
      CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
235 236
  rec->size = code->ExecutableSize();
  processor_->Enqueue(evt_rec);
237 238 239
}


240
void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code,
241
                                  SharedFunctionInfo* shared,
242
                                  CompilationInfo* info, Name* script_name) {
243 244 245 246
  if (FilterOutCodeCreateEvent(tag)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = code->address();
247 248
  rec->entry = profiles_->NewCodeEntry(
      tag, profiles_->GetFunctionName(shared->DebugName()),
249 250 251
      CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name),
      CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
      NULL, code->instruction_start());
252 253
  if (info) {
    rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
254
    rec->entry->set_inlined_function_infos(info->inlined_function_infos());
255
  }
256
  rec->entry->FillFunctionInfo(shared);
257 258
  rec->size = code->ExecutableSize();
  processor_->Enqueue(evt_rec);
259 260 261
}


262
void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code,
263
                                  SharedFunctionInfo* shared,
264 265
                                  CompilationInfo* info, Name* script_name,
                                  int line, int column) {
266 267 268 269
  if (FilterOutCodeCreateEvent(tag)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = code->address();
270 271 272 273 274 275 276 277 278 279
  Script* script = Script::cast(shared->script());
  JITLineInfoTable* line_table = NULL;
  if (script) {
    line_table = new JITLineInfoTable();
    for (RelocIterator it(code); !it.done(); it.next()) {
      RelocInfo::Mode mode = it.rinfo()->rmode();
      if (RelocInfo::IsPosition(mode)) {
        int position = static_cast<int>(it.rinfo()->data());
        if (position >= 0) {
          int pc_offset = static_cast<int>(it.rinfo()->pc() - code->address());
280 281
          int line_number = script->GetLineNumber(position) + 1;
          line_table->SetPosition(pc_offset, line_number);
282 283 284 285
        }
      }
    }
  }
286
  rec->entry = profiles_->NewCodeEntry(
287 288
      tag, profiles_->GetFunctionName(shared->DebugName()),
      CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name), line,
289
      column, line_table, code->instruction_start());
290 291
  if (info) {
    rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
292
    rec->entry->set_inlined_function_infos(info->inlined_function_infos());
293
  }
294
  rec->entry->FillFunctionInfo(shared);
295 296
  rec->size = code->ExecutableSize();
  processor_->Enqueue(evt_rec);
297 298 299 300
}


void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
301 302 303 304 305 306 307
                                  Code* code,
                                  int args_count) {
  if (FilterOutCodeCreateEvent(tag)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = code->address();
  rec->entry = profiles_->NewCodeEntry(
308 309 310
      tag, profiles_->GetName(args_count), "args_count: ",
      CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
      CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
311 312
  rec->size = code->ExecutableSize();
  processor_->Enqueue(evt_rec);
313 314 315 316
}


void CpuProfiler::CodeMoveEvent(Address from, Address to) {
317 318 319 320 321
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
  CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
  rec->from = from;
  rec->to = to;
  processor_->Enqueue(evt_rec);
322 323 324
}


325 326 327 328
void CpuProfiler::CodeDisableOptEvent(Code* code, SharedFunctionInfo* shared) {
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
  CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
  rec->start = code->address();
329
  rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason());
330 331 332 333
  processor_->Enqueue(evt_rec);
}


334
void CpuProfiler::CodeDeoptEvent(Code* code, Address pc, int fp_to_sp_delta) {
335 336
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT);
  CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
337
  Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
338 339
  rec->start = code->address();
  rec->deopt_reason = Deoptimizer::GetDeoptReason(info.deopt_reason);
340
  rec->position = info.position;
341
  rec->pc_offset = pc - code->instruction_start();
342 343 344 345 346
  processor_->Enqueue(evt_rec);
  processor_->AddDeoptStack(isolate_, pc, fp_to_sp_delta);
}


347 348 349 350
void CpuProfiler::CodeDeleteEvent(Address from) {
}


351
void CpuProfiler::GetterCallbackEvent(Name* name, Address entry_point) {
352 353 354 355 356 357 358 359 360 361
  if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = entry_point;
  rec->entry = profiles_->NewCodeEntry(
      Logger::CALLBACK_TAG,
      profiles_->GetName(name),
      "get ");
  rec->size = 1;
  processor_->Enqueue(evt_rec);
362 363 364 365
}


void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) {
366 367 368 369 370
  if (FilterOutCodeCreateEvent(Logger::REG_EXP_TAG)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = code->address();
  rec->entry = profiles_->NewCodeEntry(
371 372 373
      Logger::REG_EXP_TAG, profiles_->GetName(source), "RegExp: ",
      CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
      CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
374 375
  rec->size = code->ExecutableSize();
  processor_->Enqueue(evt_rec);
376 377 378
}


379
void CpuProfiler::SetterCallbackEvent(Name* name, Address entry_point) {
380 381 382 383 384 385 386 387 388 389
  if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = entry_point;
  rec->entry = profiles_->NewCodeEntry(
      Logger::CALLBACK_TAG,
      profiles_->GetName(name),
      "set ");
  rec->size = 1;
  processor_->Enqueue(evt_rec);
390 391 392
}


393 394
CpuProfiler::CpuProfiler(Isolate* isolate)
    : isolate_(isolate),
395
      sampling_interval_(base::TimeDelta::FromMicroseconds(
396
          FLAG_cpu_profiler_sampling_interval)),
397
      profiles_(new CpuProfilesCollection(isolate->heap())),
398
      generator_(NULL),
399
      processor_(NULL),
400 401 402 403 404 405 406 407 408
      is_profiling_(false) {
}


CpuProfiler::CpuProfiler(Isolate* isolate,
                         CpuProfilesCollection* test_profiles,
                         ProfileGenerator* test_generator,
                         ProfilerEventsProcessor* test_processor)
    : isolate_(isolate),
409
      sampling_interval_(base::TimeDelta::FromMicroseconds(
410
          FLAG_cpu_profiler_sampling_interval)),
411 412 413
      profiles_(test_profiles),
      generator_(test_generator),
      processor_(test_processor),
414
      is_profiling_(false) {
415 416 417 418
}


CpuProfiler::~CpuProfiler() {
419
  DCHECK(!is_profiling_);
420 421 422 423
  delete profiles_;
}


424
void CpuProfiler::set_sampling_interval(base::TimeDelta value) {
425
  DCHECK(!is_profiling_);
426 427 428 429
  sampling_interval_ = value;
}


430 431
void CpuProfiler::ResetProfiles() {
  delete profiles_;
432
  profiles_ = new CpuProfilesCollection(isolate()->heap());
433 434
}

435

436
void CpuProfiler::StartProfiling(const char* title, bool record_samples) {
437
  if (profiles_->StartProfiling(title, record_samples)) {
438 439 440 441 442
    StartProcessorIfNotStarted();
  }
}


443 444
void CpuProfiler::StartProfiling(String* title, bool record_samples) {
  StartProfiling(profiles_->GetName(title), record_samples);
445 446 447 448
}


void CpuProfiler::StartProcessorIfNotStarted() {
449
  if (processor_ != NULL) {
450
    processor_->AddCurrentStack(isolate_);
451 452 453 454 455 456 457 458 459 460 461 462
    return;
  }
  Logger* logger = isolate_->logger();
  // Disable logging when using the new implementation.
  saved_is_logging_ = logger->is_logging_;
  logger->is_logging_ = false;
  generator_ = new ProfileGenerator(profiles_);
  Sampler* sampler = logger->sampler();
  processor_ = new ProfilerEventsProcessor(
      generator_, sampler, sampling_interval_);
  is_profiling_ = true;
  // Enumerate stuff we already have in the heap.
463
  DCHECK(isolate_->heap()->HasBeenSetUp());
464 465
  if (!FLAG_prof_browser_mode) {
    logger->LogCodeObjects();
466
  }
467 468 469 470 471 472 473 474
  logger->LogCompiledFunctions();
  logger->LogAccessorCallbacks();
  LogBuiltins();
  // Enable stack sampling.
  sampler->SetHasProcessingThread(true);
  sampler->IncreaseProfilingDepth();
  processor_->AddCurrentStack(isolate_);
  processor_->StartSynchronously();
475 476 477
}


478 479
CpuProfile* CpuProfiler::StopProfiling(const char* title) {
  if (!is_profiling_) return NULL;
480
  StopProcessorIfLastProfile(title);
481
  CpuProfile* result = profiles_->StopProfiling(title);
482 483 484 485 486 487 488
  if (result != NULL) {
    result->Print();
  }
  return result;
}


489
CpuProfile* CpuProfiler::StopProfiling(String* title) {
490
  if (!is_profiling_) return NULL;
491 492
  const char* profile_title = profiles_->GetName(title);
  StopProcessorIfLastProfile(profile_title);
493
  return profiles_->StopProfiling(profile_title);
494 495 496
}


497
void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
498 499 500 501 502
  if (profiles_->IsLastProfile(title)) StopProcessor();
}


void CpuProfiler::StopProcessor() {
503
  Logger* logger = isolate_->logger();
504
  Sampler* sampler = reinterpret_cast<Sampler*>(logger->ticker_);
505
  is_profiling_ = false;
506
  processor_->StopSynchronously();
507 508 509 510
  delete processor_;
  delete generator_;
  processor_ = NULL;
  generator_ = NULL;
511 512
  sampler->SetHasProcessingThread(false);
  sampler->DecreaseProfilingDepth();
513
  logger->is_logging_ = saved_is_logging_;
514 515 516
}


517 518
void CpuProfiler::LogBuiltins() {
  Builtins* builtins = isolate_->builtins();
519
  DCHECK(builtins->is_initialized());
520 521 522 523 524 525 526 527 528 529 530
  for (int i = 0; i < Builtins::builtin_count; i++) {
    CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN);
    ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
    Builtins::Name id = static_cast<Builtins::Name>(i);
    rec->start = builtins->builtin(id)->address();
    rec->builtin_id = id;
    processor_->Enqueue(evt_rec);
  }
}


531
} }  // namespace v8::internal