cpu-profiler.cc 16.1 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "v8.h"

#include "cpu-profiler-inl.h"

32
#include "compiler.h"
33
#include "frames-inl.h"
34
#include "hashmap.h"
35
#include "log-inl.h"
36
#include "vm-state-inl.h"
37

38 39
#include "../include/v8-profiler.h"

40 41 42
namespace v8 {
namespace internal {

43
static const int kProfilerStackSize = 64 * KB;
44 45


46 47 48
ProfilerEventsProcessor::ProfilerEventsProcessor(
    ProfileGenerator* generator,
    Sampler* sampler,
49
    TimeDelta period)
50
    : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
51
      generator_(generator),
52
      sampler_(sampler),
53
      running_(true),
54
      period_(period),
55
      last_code_event_id_(0), last_processed_code_event_id_(0) {
56
}
57 58


59
void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
60
  event.generic.order = ++last_code_event_id_;
61
  events_buffer_.Enqueue(event);
62 63 64
}


65
void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) {
66
  TickSampleEventRecord record(last_code_event_id_);
67 68 69 70 71 72 73
  RegisterState regs;
  StackFrameIterator it(isolate);
  if (!it.done()) {
    StackFrame* frame = it.frame();
    regs.sp = frame->sp();
    regs.fp = frame->fp();
    regs.pc = frame->pc();
74
  }
75
  record.sample.Init(isolate, regs);
76 77 78 79
  ticks_from_vm_buffer_.Enqueue(record);
}


80 81 82 83 84 85 86 87
void ProfilerEventsProcessor::StopSynchronously() {
  if (!running_) return;
  running_ = false;
  Join();
}


bool ProfilerEventsProcessor::ProcessCodeEvent() {
88 89
  CodeEventsContainer record;
  if (events_buffer_.Dequeue(&record)) {
90 91 92 93 94 95 96 97 98 99 100
    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.
    }
101
    last_processed_code_event_id_ = record.generic.order;
102 103 104 105 106
    return true;
  }
  return false;
}

107 108 109 110 111 112 113 114 115
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;
116
  }
117

118 119 120 121 122 123 124
  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;
125
  }
126 127 128
  generator_->RecordTickSample(record->sample);
  ticks_buffer_.Remove();
  return OneSampleProcessed;
129 130 131 132 133
}


void ProfilerEventsProcessor::Run() {
  while (running_) {
134 135 136 137 138 139 140 141 142 143 144 145 146
    ElapsedTimer timer;
    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();
147 148
  }

149
  // Process remaining tick events.
150
  do {
151 152 153 154
    SampleProcessingResult result;
    do {
      result = ProcessOneSample();
    } while (result == OneSampleProcessed);
155
  } while (ProcessCodeEvent());
156 157
}

158

159 160 161 162 163 164 165 166 167 168
void* ProfilerEventsProcessor::operator new(size_t size) {
  return AlignedAlloc(size, V8_ALIGNOF(ProfilerEventsProcessor));
}


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


169
int CpuProfiler::GetProfilesCount() {
170
  // The count of profiles doesn't depend on a security token.
171
  return profiles_->profiles()->length();
172 173 174
}


175 176
CpuProfile* CpuProfiler::GetProfile(int index) {
  return profiles_->profiles()->at(index);
177 178 179
}


180
void CpuProfiler::DeleteAllProfiles() {
181 182
  if (is_profiling_) StopProcessor();
  ResetProfiles();
183 184 185 186
}


void CpuProfiler::DeleteProfile(CpuProfile* profile) {
187
  profiles_->RemoveProfile(profile);
188
  delete profile;
189 190 191 192
  if (profiles_->profiles()->is_empty() && !is_profiling_) {
    // If this was the last profile, clean up all accessory data as well.
    ResetProfiles();
  }
193 194 195
}


196 197 198 199 200 201 202 203 204 205
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);
}


206
void CpuProfiler::CallbackEvent(Name* name, Address entry_point) {
207 208 209 210 211 212
  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,
213
      profiles_->GetName(name));
214 215 216
  rec->size = 1;
  rec->shared = NULL;
  processor_->Enqueue(evt_rec);
217 218 219 220
}


void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
221 222 223 224 225 226 227 228 229 230
                                  Code* code,
                                  const char* name) {
  if (FilterOutCodeCreateEvent(tag)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = code->address();
  rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
  rec->size = code->ExecutableSize();
  rec->shared = NULL;
  processor_->Enqueue(evt_rec);
231 232 233 234
}


void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
235 236 237 238 239 240 241 242 243 244
                                  Code* code,
                                  Name* name) {
  if (FilterOutCodeCreateEvent(tag)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = code->address();
  rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
  rec->size = code->ExecutableSize();
  rec->shared = NULL;
  processor_->Enqueue(evt_rec);
245 246 247 248
}


void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
249 250
                                  Code* code,
                                  SharedFunctionInfo* shared,
251
                                  CompilationInfo* info,
252
                                  Name* name) {
253 254 255 256 257
  if (FilterOutCodeCreateEvent(tag)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = code->address();
  rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
258 259 260 261 262 263 264
  if (info) {
    rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
  }
  if (shared->script()->IsScript()) {
    ASSERT(Script::cast(shared->script()));
    Script* script = Script::cast(shared->script());
    rec->entry->set_script_id(script->id()->value());
265 266
    rec->entry->set_bailout_reason(
        GetBailoutReason(shared->DisableOptimizationReason()));
267
  }
268 269 270
  rec->size = code->ExecutableSize();
  rec->shared = shared->address();
  processor_->Enqueue(evt_rec);
271 272 273 274 275 276
}


void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
                                  Code* code,
                                  SharedFunctionInfo* shared,
277
                                  CompilationInfo* info,
278
                                  Name* source, int line, int column) {
279 280 281 282 283
  if (FilterOutCodeCreateEvent(tag)) return;
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
  rec->start = code->address();
  rec->entry = profiles_->NewCodeEntry(
284
      tag,
285 286 287
      profiles_->GetFunctionName(shared->DebugName()),
      CodeEntry::kEmptyNamePrefix,
      profiles_->GetName(source),
288 289
      line,
      column);
290 291 292 293 294 295
  if (info) {
    rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
  }
  ASSERT(Script::cast(shared->script()));
  Script* script = Script::cast(shared->script());
  rec->entry->set_script_id(script->id()->value());
296 297
  rec->size = code->ExecutableSize();
  rec->shared = shared->address();
298 299
  rec->entry->set_bailout_reason(
      GetBailoutReason(shared->DisableOptimizationReason()));
300
  processor_->Enqueue(evt_rec);
301 302 303 304
}


void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
305 306 307 308 309 310 311
                                  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(
312
      tag,
313 314 315 316 317
      profiles_->GetName(args_count),
      "args_count: ");
  rec->size = code->ExecutableSize();
  rec->shared = NULL;
  processor_->Enqueue(evt_rec);
318 319 320 321
}


void CpuProfiler::CodeMoveEvent(Address from, Address to) {
322 323 324 325 326
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
  CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
  rec->from = from;
  rec->to = to;
  processor_->Enqueue(evt_rec);
327 328 329 330 331 332 333
}


void CpuProfiler::CodeDeleteEvent(Address from) {
}


334
void CpuProfiler::SharedFunctionInfoMoveEvent(Address from, Address to) {
335 336 337 338 339 340
  CodeEventsContainer evt_rec(CodeEventRecord::SHARED_FUNC_MOVE);
  SharedFunctionInfoMoveEventRecord* rec =
      &evt_rec.SharedFunctionInfoMoveEventRecord_;
  rec->from = from;
  rec->to = to;
  processor_->Enqueue(evt_rec);
341 342 343
}


344
void CpuProfiler::GetterCallbackEvent(Name* name, Address entry_point) {
345 346 347 348 349 350 351 352 353 354 355
  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;
  rec->shared = NULL;
  processor_->Enqueue(evt_rec);
356 357 358 359
}


void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) {
360 361 362 363 364
  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(
365
      Logger::REG_EXP_TAG,
366 367 368 369
      profiles_->GetName(source),
      "RegExp: ");
  rec->size = code->ExecutableSize();
  processor_->Enqueue(evt_rec);
370 371 372
}


373
void CpuProfiler::SetterCallbackEvent(Name* name, Address entry_point) {
374 375 376 377 378 379 380 381 382 383 384
  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;
  rec->shared = NULL;
  processor_->Enqueue(evt_rec);
385 386 387
}


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


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


CpuProfiler::~CpuProfiler() {
414
  ASSERT(!is_profiling_);
415 416 417 418
  delete profiles_;
}


419 420 421 422 423 424
void CpuProfiler::set_sampling_interval(TimeDelta value) {
  ASSERT(!is_profiling_);
  sampling_interval_ = value;
}


425 426
void CpuProfiler::ResetProfiles() {
  delete profiles_;
427
  profiles_ = new CpuProfilesCollection(isolate()->heap());
428 429
}

430

431
void CpuProfiler::StartProfiling(const char* title, bool record_samples) {
432
  if (profiles_->StartProfiling(title, record_samples)) {
433 434
    StartProcessorIfNotStarted();
  }
435
  processor_->AddCurrentStack(isolate_);
436 437 438
}


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


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


471 472
CpuProfile* CpuProfiler::StopProfiling(const char* title) {
  if (!is_profiling_) return NULL;
473
  StopProcessorIfLastProfile(title);
474
  CpuProfile* result = profiles_->StopProfiling(title);
475 476 477 478 479 480 481
  if (result != NULL) {
    result->Print();
  }
  return result;
}


482
CpuProfile* CpuProfiler::StopProfiling(String* title) {
483
  if (!is_profiling_) return NULL;
484 485
  const char* profile_title = profiles_->GetName(title);
  StopProcessorIfLastProfile(profile_title);
486
  return profiles_->StopProfiling(profile_title);
487 488 489
}


490
void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
491 492 493 494 495
  if (profiles_->IsLastProfile(title)) StopProcessor();
}


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


510 511 512 513 514 515 516 517 518 519 520 521 522 523
void CpuProfiler::LogBuiltins() {
  Builtins* builtins = isolate_->builtins();
  ASSERT(builtins->is_initialized());
  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);
  }
}


524
} }  // namespace v8::internal