cpu-profiler.cc 16.7 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 254
  if (info) {
    rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
  }
255
  rec->entry->FillFunctionInfo(shared);
256 257
  rec->size = code->ExecutableSize();
  processor_->Enqueue(evt_rec);
258 259 260
}


261
void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code,
262
                                  SharedFunctionInfo* shared,
263 264
                                  CompilationInfo* info, Name* script_name,
                                  int line, int column) {
265 266 267 268
  if (FilterOutCodeCreateEvent(tag)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = code->address();
269 270 271 272 273 274 275 276 277 278
  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());
279 280
          int line_number = script->GetLineNumber(position);
          line_table->SetPosition(pc_offset, line_number + 1);
281 282 283 284
        }
      }
    }
  }
285
  rec->entry = profiles_->NewCodeEntry(
286 287
      tag, profiles_->GetFunctionName(shared->DebugName()),
      CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name), line,
288
      column, line_table, code->instruction_start());
289 290 291
  if (info) {
    rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
  }
292
  rec->entry->FillFunctionInfo(shared);
293 294
  rec->size = code->ExecutableSize();
  processor_->Enqueue(evt_rec);
295 296 297 298
}


void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
299 300 301 302 303 304 305
                                  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(
306 307 308
      tag, profiles_->GetName(args_count), "args_count: ",
      CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
      CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
309 310
  rec->size = code->ExecutableSize();
  processor_->Enqueue(evt_rec);
311 312 313 314
}


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


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


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


345 346 347 348
void CpuProfiler::CodeDeleteEvent(Address from) {
}


349
void CpuProfiler::GetterCallbackEvent(Name* name, Address entry_point) {
350 351 352 353 354 355 356 357 358 359
  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);
360 361 362 363
}


void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) {
364 365 366 367 368
  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(
369 370 371
      Logger::REG_EXP_TAG, profiles_->GetName(source), "RegExp: ",
      CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
      CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
372 373
  rec->size = code->ExecutableSize();
  processor_->Enqueue(evt_rec);
374 375 376
}


377
void CpuProfiler::SetterCallbackEvent(Name* name, Address entry_point) {
378 379 380 381 382 383 384 385 386 387
  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);
388 389 390
}


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


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


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


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


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

433

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


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


void CpuProfiler::StartProcessorIfNotStarted() {
447
  if (processor_ != NULL) {
448
    processor_->AddCurrentStack(isolate_);
449 450 451 452 453 454 455 456 457 458 459 460
    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.
461
  DCHECK(isolate_->heap()->HasBeenSetUp());
462 463
  if (!FLAG_prof_browser_mode) {
    logger->LogCodeObjects();
464
  }
465 466 467 468 469 470 471 472
  logger->LogCompiledFunctions();
  logger->LogAccessorCallbacks();
  LogBuiltins();
  // Enable stack sampling.
  sampler->SetHasProcessingThread(true);
  sampler->IncreaseProfilingDepth();
  processor_->AddCurrentStack(isolate_);
  processor_->StartSynchronously();
473 474 475
}


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


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


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


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


515 516
void CpuProfiler::LogBuiltins() {
  Builtins* builtins = isolate_->builtins();
517
  DCHECK(builtins->is_initialized());
518 519 520 521 522 523 524 525 526 527 528
  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);
  }
}


529
} }  // namespace v8::internal