1 // Copyright 2011 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/log.h" 6 7 #include <cstdarg> 8 #include <memory> 9 #include <sstream> 10 11 #include "src/bailout-reason.h" 12 #include "src/base/platform/platform.h" 13 #include "src/bootstrapper.h" 14 #include "src/code-stubs.h" 15 #include "src/counters.h" 16 #include "src/deoptimizer.h" 17 #include "src/global-handles.h" 18 #include "src/interpreter/bytecodes.h" 19 #include "src/interpreter/interpreter.h" 20 #include "src/libsampler/sampler.h" 21 #include "src/log-inl.h" 22 #include "src/log-utils.h" 23 #include "src/macro-assembler.h" 24 #include "src/perf-jit.h" 25 #include "src/profiler/profiler-listener.h" 26 #include "src/profiler/tick-sample.h" 27 #include "src/runtime-profiler.h" 28 #include "src/source-position-table.h" 29 #include "src/string-stream.h" 30 #include "src/tracing/tracing-category-observer.h" 31 #include "src/vm-state-inl.h" 32 33 namespace v8 { 34 namespace internal { 35 36 #define DECLARE_EVENT(ignore1, name) name, 37 static const char* kLogEventsNames[CodeEventListener::NUMBER_OF_LOG_EVENTS] = { 38 LOG_EVENTS_AND_TAGS_LIST(DECLARE_EVENT)}; 39 #undef DECLARE_EVENT 40 41 static const char* ComputeMarker(SharedFunctionInfo* shared, 42 AbstractCode* code) { 43 switch (code->kind()) { 44 case AbstractCode::FUNCTION: 45 case AbstractCode::INTERPRETED_FUNCTION: 46 return shared->optimization_disabled() ? "" : "~"; 47 case AbstractCode::OPTIMIZED_FUNCTION: 48 return "*"; 49 default: 50 return ""; 51 } 52 } 53 54 55 class CodeEventLogger::NameBuffer { 56 public: 57 NameBuffer() { Reset(); } 58 59 void Reset() { 60 utf8_pos_ = 0; 61 } 62 63 void Init(CodeEventListener::LogEventsAndTags tag) { 64 Reset(); 65 AppendBytes(kLogEventsNames[tag]); 66 AppendByte(':'); 67 } 68 69 void AppendName(Name* name) { 70 if (name->IsString()) { 71 AppendString(String::cast(name)); 72 } else { 73 Symbol* symbol = Symbol::cast(name); 74 AppendBytes("symbol("); 75 if (!symbol->name()->IsUndefined(symbol->GetIsolate())) { 76 AppendBytes("\""); 77 AppendString(String::cast(symbol->name())); 78 AppendBytes("\" "); 79 } 80 AppendBytes("hash "); 81 AppendHex(symbol->Hash()); 82 AppendByte(')'); 83 } 84 } 85 86 void AppendString(String* str) { 87 if (str == NULL) return; 88 int uc16_length = Min(str->length(), kUtf16BufferSize); 89 String::WriteToFlat(str, utf16_buffer, 0, uc16_length); 90 int previous = unibrow::Utf16::kNoPreviousCharacter; 91 for (int i = 0; i < uc16_length && utf8_pos_ < kUtf8BufferSize; ++i) { 92 uc16 c = utf16_buffer[i]; 93 if (c <= unibrow::Utf8::kMaxOneByteChar) { 94 utf8_buffer_[utf8_pos_++] = static_cast<char>(c); 95 } else { 96 int char_length = unibrow::Utf8::Length(c, previous); 97 if (utf8_pos_ + char_length > kUtf8BufferSize) break; 98 unibrow::Utf8::Encode(utf8_buffer_ + utf8_pos_, c, previous); 99 utf8_pos_ += char_length; 100 } 101 previous = c; 102 } 103 } 104 105 void AppendBytes(const char* bytes, int size) { 106 size = Min(size, kUtf8BufferSize - utf8_pos_); 107 MemCopy(utf8_buffer_ + utf8_pos_, bytes, size); 108 utf8_pos_ += size; 109 } 110 111 void AppendBytes(const char* bytes) { 112 AppendBytes(bytes, StrLength(bytes)); 113 } 114 115 void AppendByte(char c) { 116 if (utf8_pos_ >= kUtf8BufferSize) return; 117 utf8_buffer_[utf8_pos_++] = c; 118 } 119 120 void AppendInt(int n) { 121 int space = kUtf8BufferSize - utf8_pos_; 122 if (space <= 0) return; 123 Vector<char> buffer(utf8_buffer_ + utf8_pos_, space); 124 int size = SNPrintF(buffer, "%d", n); 125 if (size > 0 && utf8_pos_ + size <= kUtf8BufferSize) { 126 utf8_pos_ += size; 127 } 128 } 129 130 void AppendHex(uint32_t n) { 131 int space = kUtf8BufferSize - utf8_pos_; 132 if (space <= 0) return; 133 Vector<char> buffer(utf8_buffer_ + utf8_pos_, space); 134 int size = SNPrintF(buffer, "%x", n); 135 if (size > 0 && utf8_pos_ + size <= kUtf8BufferSize) { 136 utf8_pos_ += size; 137 } 138 } 139 140 const char* get() { return utf8_buffer_; } 141 int size() const { return utf8_pos_; } 142 143 private: 144 static const int kUtf8BufferSize = 512; 145 static const int kUtf16BufferSize = kUtf8BufferSize; 146 147 int utf8_pos_; 148 char utf8_buffer_[kUtf8BufferSize]; 149 uc16 utf16_buffer[kUtf16BufferSize]; 150 }; 151 152 153 CodeEventLogger::CodeEventLogger() : name_buffer_(new NameBuffer) { } 154 155 CodeEventLogger::~CodeEventLogger() { delete name_buffer_; } 156 157 void CodeEventLogger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 158 AbstractCode* code, const char* comment) { 159 name_buffer_->Init(tag); 160 name_buffer_->AppendBytes(comment); 161 LogRecordedBuffer(code, NULL, name_buffer_->get(), name_buffer_->size()); 162 } 163 164 void CodeEventLogger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 165 AbstractCode* code, Name* name) { 166 name_buffer_->Init(tag); 167 name_buffer_->AppendName(name); 168 LogRecordedBuffer(code, NULL, name_buffer_->get(), name_buffer_->size()); 169 } 170 171 void CodeEventLogger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 172 AbstractCode* code, 173 SharedFunctionInfo* shared, Name* name) { 174 name_buffer_->Init(tag); 175 name_buffer_->AppendBytes(ComputeMarker(shared, code)); 176 name_buffer_->AppendName(name); 177 LogRecordedBuffer(code, shared, name_buffer_->get(), name_buffer_->size()); 178 } 179 180 void CodeEventLogger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 181 AbstractCode* code, 182 SharedFunctionInfo* shared, Name* source, 183 int line, int column) { 184 name_buffer_->Init(tag); 185 name_buffer_->AppendBytes(ComputeMarker(shared, code)); 186 name_buffer_->AppendString(shared->DebugName()); 187 name_buffer_->AppendByte(' '); 188 if (source->IsString()) { 189 name_buffer_->AppendString(String::cast(source)); 190 } else { 191 name_buffer_->AppendBytes("symbol(hash "); 192 name_buffer_->AppendHex(Name::cast(source)->Hash()); 193 name_buffer_->AppendByte(')'); 194 } 195 name_buffer_->AppendByte(':'); 196 name_buffer_->AppendInt(line); 197 LogRecordedBuffer(code, shared, name_buffer_->get(), name_buffer_->size()); 198 } 199 200 void CodeEventLogger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 201 AbstractCode* code, int args_count) { 202 name_buffer_->Init(tag); 203 name_buffer_->AppendInt(args_count); 204 LogRecordedBuffer(code, NULL, name_buffer_->get(), name_buffer_->size()); 205 } 206 207 void CodeEventLogger::RegExpCodeCreateEvent(AbstractCode* code, 208 String* source) { 209 name_buffer_->Init(CodeEventListener::REG_EXP_TAG); 210 name_buffer_->AppendString(source); 211 LogRecordedBuffer(code, NULL, name_buffer_->get(), name_buffer_->size()); 212 } 213 214 215 // Linux perf tool logging support 216 class PerfBasicLogger : public CodeEventLogger { 217 public: 218 PerfBasicLogger(); 219 ~PerfBasicLogger() override; 220 221 void CodeMoveEvent(AbstractCode* from, Address to) override {} 222 void CodeDisableOptEvent(AbstractCode* code, 223 SharedFunctionInfo* shared) override {} 224 225 private: 226 void LogRecordedBuffer(AbstractCode* code, SharedFunctionInfo* shared, 227 const char* name, int length) override; 228 229 // Extension added to V8 log file name to get the low-level log name. 230 static const char kFilenameFormatString[]; 231 static const int kFilenameBufferPadding; 232 233 FILE* perf_output_handle_; 234 }; 235 236 const char PerfBasicLogger::kFilenameFormatString[] = "/tmp/perf-%d.map"; 237 // Extra space for the PID in the filename 238 const int PerfBasicLogger::kFilenameBufferPadding = 16; 239 240 PerfBasicLogger::PerfBasicLogger() 241 : perf_output_handle_(NULL) { 242 // Open the perf JIT dump file. 243 int bufferSize = sizeof(kFilenameFormatString) + kFilenameBufferPadding; 244 ScopedVector<char> perf_dump_name(bufferSize); 245 int size = SNPrintF( 246 perf_dump_name, 247 kFilenameFormatString, 248 base::OS::GetCurrentProcessId()); 249 CHECK_NE(size, -1); 250 perf_output_handle_ = 251 base::OS::FOpen(perf_dump_name.start(), base::OS::LogFileOpenMode); 252 CHECK_NOT_NULL(perf_output_handle_); 253 setvbuf(perf_output_handle_, NULL, _IOLBF, 0); 254 } 255 256 257 PerfBasicLogger::~PerfBasicLogger() { 258 fclose(perf_output_handle_); 259 perf_output_handle_ = NULL; 260 } 261 262 void PerfBasicLogger::LogRecordedBuffer(AbstractCode* code, SharedFunctionInfo*, 263 const char* name, int length) { 264 if (FLAG_perf_basic_prof_only_functions && 265 (code->kind() != AbstractCode::FUNCTION && 266 code->kind() != AbstractCode::INTERPRETED_FUNCTION && 267 code->kind() != AbstractCode::OPTIMIZED_FUNCTION)) { 268 return; 269 } 270 271 // Linux perf expects hex literals without a leading 0x, while some 272 // implementations of printf might prepend one when using the %p format 273 // for pointers, leading to wrongly formatted JIT symbols maps. 274 // 275 // Instead, we use V8PRIxPTR format string and cast pointer to uintpr_t, 276 // so that we have control over the exact output format. 277 base::OS::FPrint(perf_output_handle_, "%" V8PRIxPTR " %x %.*s\n", 278 reinterpret_cast<uintptr_t>(code->instruction_start()), 279 code->instruction_size(), length, name); 280 } 281 282 // Low-level logging support. 283 #define LL_LOG(Call) if (ll_logger_) ll_logger_->Call; 284 285 class LowLevelLogger : public CodeEventLogger { 286 public: 287 explicit LowLevelLogger(const char* file_name); 288 ~LowLevelLogger() override; 289 290 void CodeMoveEvent(AbstractCode* from, Address to) override; 291 void CodeDisableOptEvent(AbstractCode* code, 292 SharedFunctionInfo* shared) override {} 293 void SnapshotPositionEvent(HeapObject* obj, int pos); 294 void CodeMovingGCEvent() override; 295 296 private: 297 void LogRecordedBuffer(AbstractCode* code, SharedFunctionInfo* shared, 298 const char* name, int length) override; 299 300 // Low-level profiling event structures. 301 struct CodeCreateStruct { 302 static const char kTag = 'C'; 303 304 int32_t name_size; 305 Address code_address; 306 int32_t code_size; 307 }; 308 309 310 struct CodeMoveStruct { 311 static const char kTag = 'M'; 312 313 Address from_address; 314 Address to_address; 315 }; 316 317 318 static const char kCodeMovingGCTag = 'G'; 319 320 321 // Extension added to V8 log file name to get the low-level log name. 322 static const char kLogExt[]; 323 324 void LogCodeInfo(); 325 void LogWriteBytes(const char* bytes, int size); 326 327 template <typename T> 328 void LogWriteStruct(const T& s) { 329 char tag = T::kTag; 330 LogWriteBytes(reinterpret_cast<const char*>(&tag), sizeof(tag)); 331 LogWriteBytes(reinterpret_cast<const char*>(&s), sizeof(s)); 332 } 333 334 FILE* ll_output_handle_; 335 }; 336 337 const char LowLevelLogger::kLogExt[] = ".ll"; 338 339 LowLevelLogger::LowLevelLogger(const char* name) 340 : ll_output_handle_(NULL) { 341 // Open the low-level log file. 342 size_t len = strlen(name); 343 ScopedVector<char> ll_name(static_cast<int>(len + sizeof(kLogExt))); 344 MemCopy(ll_name.start(), name, len); 345 MemCopy(ll_name.start() + len, kLogExt, sizeof(kLogExt)); 346 ll_output_handle_ = 347 base::OS::FOpen(ll_name.start(), base::OS::LogFileOpenMode); 348 setvbuf(ll_output_handle_, NULL, _IOLBF, 0); 349 350 LogCodeInfo(); 351 } 352 353 354 LowLevelLogger::~LowLevelLogger() { 355 fclose(ll_output_handle_); 356 ll_output_handle_ = NULL; 357 } 358 359 360 void LowLevelLogger::LogCodeInfo() { 361 #if V8_TARGET_ARCH_IA32 362 const char arch[] = "ia32"; 363 #elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_64_BIT 364 const char arch[] = "x64"; 365 #elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT 366 const char arch[] = "x32"; 367 #elif V8_TARGET_ARCH_ARM 368 const char arch[] = "arm"; 369 #elif V8_TARGET_ARCH_PPC 370 const char arch[] = "ppc"; 371 #elif V8_TARGET_ARCH_MIPS 372 const char arch[] = "mips"; 373 #elif V8_TARGET_ARCH_X87 374 const char arch[] = "x87"; 375 #elif V8_TARGET_ARCH_ARM64 376 const char arch[] = "arm64"; 377 #elif V8_TARGET_ARCH_S390 378 const char arch[] = "s390"; 379 #else 380 const char arch[] = "unknown"; 381 #endif 382 LogWriteBytes(arch, sizeof(arch)); 383 } 384 385 void LowLevelLogger::LogRecordedBuffer(AbstractCode* code, SharedFunctionInfo*, 386 const char* name, int length) { 387 CodeCreateStruct event; 388 event.name_size = length; 389 event.code_address = code->instruction_start(); 390 event.code_size = code->instruction_size(); 391 LogWriteStruct(event); 392 LogWriteBytes(name, length); 393 LogWriteBytes( 394 reinterpret_cast<const char*>(code->instruction_start()), 395 code->instruction_size()); 396 } 397 398 void LowLevelLogger::CodeMoveEvent(AbstractCode* from, Address to) { 399 CodeMoveStruct event; 400 event.from_address = from->instruction_start(); 401 size_t header_size = from->instruction_start() - from->address(); 402 event.to_address = to + header_size; 403 LogWriteStruct(event); 404 } 405 406 407 void LowLevelLogger::LogWriteBytes(const char* bytes, int size) { 408 size_t rv = fwrite(bytes, 1, size, ll_output_handle_); 409 DCHECK(static_cast<size_t>(size) == rv); 410 USE(rv); 411 } 412 413 414 void LowLevelLogger::CodeMovingGCEvent() { 415 const char tag = kCodeMovingGCTag; 416 417 LogWriteBytes(&tag, sizeof(tag)); 418 } 419 420 class JitLogger : public CodeEventLogger { 421 public: 422 explicit JitLogger(JitCodeEventHandler code_event_handler); 423 424 void CodeMoveEvent(AbstractCode* from, Address to) override; 425 void CodeDisableOptEvent(AbstractCode* code, 426 SharedFunctionInfo* shared) override {} 427 void AddCodeLinePosInfoEvent(void* jit_handler_data, int pc_offset, 428 int position, 429 JitCodeEvent::PositionType position_type); 430 431 void* StartCodePosInfoEvent(); 432 void EndCodePosInfoEvent(AbstractCode* code, void* jit_handler_data); 433 434 private: 435 void LogRecordedBuffer(AbstractCode* code, SharedFunctionInfo* shared, 436 const char* name, int length) override; 437 438 JitCodeEventHandler code_event_handler_; 439 base::Mutex logger_mutex_; 440 }; 441 442 443 JitLogger::JitLogger(JitCodeEventHandler code_event_handler) 444 : code_event_handler_(code_event_handler) { 445 } 446 447 void JitLogger::LogRecordedBuffer(AbstractCode* code, 448 SharedFunctionInfo* shared, const char* name, 449 int length) { 450 JitCodeEvent event; 451 memset(&event, 0, sizeof(event)); 452 event.type = JitCodeEvent::CODE_ADDED; 453 event.code_start = code->instruction_start(); 454 event.code_len = code->instruction_size(); 455 Handle<SharedFunctionInfo> shared_function_handle; 456 if (shared && shared->script()->IsScript()) { 457 shared_function_handle = Handle<SharedFunctionInfo>(shared); 458 } 459 event.script = ToApiHandle<v8::UnboundScript>(shared_function_handle); 460 event.name.str = name; 461 event.name.len = length; 462 code_event_handler_(&event); 463 } 464 465 void JitLogger::CodeMoveEvent(AbstractCode* from, Address to) { 466 base::LockGuard<base::Mutex> guard(&logger_mutex_); 467 468 JitCodeEvent event; 469 event.type = JitCodeEvent::CODE_MOVED; 470 event.code_start = from->instruction_start(); 471 event.code_len = from->instruction_size(); 472 473 // Calculate the header size. 474 const size_t header_size = from->instruction_start() - from->address(); 475 476 // Calculate the new start address of the instructions. 477 event.new_code_start = to + header_size; 478 479 code_event_handler_(&event); 480 } 481 482 void JitLogger::AddCodeLinePosInfoEvent( 483 void* jit_handler_data, 484 int pc_offset, 485 int position, 486 JitCodeEvent::PositionType position_type) { 487 JitCodeEvent event; 488 memset(&event, 0, sizeof(event)); 489 event.type = JitCodeEvent::CODE_ADD_LINE_POS_INFO; 490 event.user_data = jit_handler_data; 491 event.line_info.offset = pc_offset; 492 event.line_info.pos = position; 493 event.line_info.position_type = position_type; 494 495 code_event_handler_(&event); 496 } 497 498 499 void* JitLogger::StartCodePosInfoEvent() { 500 JitCodeEvent event; 501 memset(&event, 0, sizeof(event)); 502 event.type = JitCodeEvent::CODE_START_LINE_INFO_RECORDING; 503 504 code_event_handler_(&event); 505 return event.user_data; 506 } 507 508 void JitLogger::EndCodePosInfoEvent(AbstractCode* code, 509 void* jit_handler_data) { 510 JitCodeEvent event; 511 memset(&event, 0, sizeof(event)); 512 event.type = JitCodeEvent::CODE_END_LINE_INFO_RECORDING; 513 event.code_start = code->instruction_start(); 514 event.user_data = jit_handler_data; 515 516 code_event_handler_(&event); 517 } 518 519 520 // TODO(lpy): Keeping sampling thread inside V8 is a workaround currently, 521 // the reason is to reduce code duplication during migration to sampler library, 522 // sampling thread, as well as the sampler, will be moved to D8 eventually. 523 class SamplingThread : public base::Thread { 524 public: 525 static const int kSamplingThreadStackSize = 64 * KB; 526 527 SamplingThread(sampler::Sampler* sampler, int interval) 528 : base::Thread(base::Thread::Options("SamplingThread", 529 kSamplingThreadStackSize)), 530 sampler_(sampler), 531 interval_(interval) {} 532 void Run() override { 533 while (sampler_->IsProfiling()) { 534 sampler_->DoSample(); 535 base::OS::Sleep(base::TimeDelta::FromMilliseconds(interval_)); 536 } 537 } 538 539 private: 540 sampler::Sampler* sampler_; 541 const int interval_; 542 }; 543 544 545 // The Profiler samples pc and sp values for the main thread. 546 // Each sample is appended to a circular buffer. 547 // An independent thread removes data and writes it to the log. 548 // This design minimizes the time spent in the sampler. 549 // 550 class Profiler: public base::Thread { 551 public: 552 explicit Profiler(Isolate* isolate); 553 void Engage(); 554 void Disengage(); 555 556 // Inserts collected profiling data into buffer. 557 void Insert(v8::TickSample* sample) { 558 if (paused_) 559 return; 560 561 if (Succ(head_) == static_cast<int>(base::NoBarrier_Load(&tail_))) { 562 overflow_ = true; 563 } else { 564 buffer_[head_] = *sample; 565 head_ = Succ(head_); 566 buffer_semaphore_.Signal(); // Tell we have an element. 567 } 568 } 569 570 virtual void Run(); 571 572 // Pause and Resume TickSample data collection. 573 void pause() { paused_ = true; } 574 void resume() { paused_ = false; } 575 576 private: 577 // Waits for a signal and removes profiling data. 578 bool Remove(v8::TickSample* sample) { 579 buffer_semaphore_.Wait(); // Wait for an element. 580 *sample = buffer_[base::NoBarrier_Load(&tail_)]; 581 bool result = overflow_; 582 base::NoBarrier_Store(&tail_, static_cast<base::Atomic32>( 583 Succ(base::NoBarrier_Load(&tail_)))); 584 overflow_ = false; 585 return result; 586 } 587 588 // Returns the next index in the cyclic buffer. 589 int Succ(int index) { return (index + 1) % kBufferSize; } 590 591 Isolate* isolate_; 592 // Cyclic buffer for communicating profiling samples 593 // between the signal handler and the worker thread. 594 static const int kBufferSize = 128; 595 v8::TickSample buffer_[kBufferSize]; // Buffer storage. 596 int head_; // Index to the buffer head. 597 base::Atomic32 tail_; // Index to the buffer tail. 598 bool overflow_; // Tell whether a buffer overflow has occurred. 599 // Sempahore used for buffer synchronization. 600 base::Semaphore buffer_semaphore_; 601 602 // Tells whether profiler is engaged, that is, processing thread is stated. 603 bool engaged_; 604 605 // Tells whether worker thread should continue running. 606 base::Atomic32 running_; 607 608 // Tells whether we are currently recording tick samples. 609 bool paused_; 610 }; 611 612 613 // 614 // Ticker used to provide ticks to the profiler and the sliding state 615 // window. 616 // 617 class Ticker: public sampler::Sampler { 618 public: 619 Ticker(Isolate* isolate, int interval) 620 : sampler::Sampler(reinterpret_cast<v8::Isolate*>(isolate)), 621 profiler_(nullptr), 622 sampling_thread_(new SamplingThread(this, interval)) {} 623 624 ~Ticker() { 625 if (IsActive()) Stop(); 626 delete sampling_thread_; 627 } 628 629 void SetProfiler(Profiler* profiler) { 630 DCHECK(profiler_ == nullptr); 631 profiler_ = profiler; 632 IncreaseProfilingDepth(); 633 if (!IsActive()) Start(); 634 sampling_thread_->StartSynchronously(); 635 } 636 637 void ClearProfiler() { 638 profiler_ = nullptr; 639 if (IsActive()) Stop(); 640 DecreaseProfilingDepth(); 641 sampling_thread_->Join(); 642 } 643 644 void SampleStack(const v8::RegisterState& state) override { 645 if (!profiler_) return; 646 Isolate* isolate = reinterpret_cast<Isolate*>(this->isolate()); 647 TickSample sample; 648 sample.Init(isolate, state, TickSample::kIncludeCEntryFrame, true); 649 profiler_->Insert(&sample); 650 } 651 652 private: 653 Profiler* profiler_; 654 SamplingThread* sampling_thread_; 655 }; 656 657 658 // 659 // Profiler implementation. 660 // 661 Profiler::Profiler(Isolate* isolate) 662 : base::Thread(Options("v8:Profiler")), 663 isolate_(isolate), 664 head_(0), 665 overflow_(false), 666 buffer_semaphore_(0), 667 engaged_(false), 668 paused_(false) { 669 base::NoBarrier_Store(&tail_, 0); 670 base::NoBarrier_Store(&running_, 0); 671 } 672 673 674 void Profiler::Engage() { 675 if (engaged_) return; 676 engaged_ = true; 677 678 std::vector<base::OS::SharedLibraryAddress> addresses = 679 base::OS::GetSharedLibraryAddresses(); 680 for (size_t i = 0; i < addresses.size(); ++i) { 681 LOG(isolate_, 682 SharedLibraryEvent(addresses[i].library_path, addresses[i].start, 683 addresses[i].end, addresses[i].aslr_slide)); 684 } 685 686 // Start thread processing the profiler buffer. 687 base::NoBarrier_Store(&running_, 1); 688 Start(); 689 690 // Register to get ticks. 691 Logger* logger = isolate_->logger(); 692 logger->ticker_->SetProfiler(this); 693 694 logger->ProfilerBeginEvent(); 695 } 696 697 698 void Profiler::Disengage() { 699 if (!engaged_) return; 700 701 // Stop receiving ticks. 702 isolate_->logger()->ticker_->ClearProfiler(); 703 704 // Terminate the worker thread by setting running_ to false, 705 // inserting a fake element in the queue and then wait for 706 // the thread to terminate. 707 base::NoBarrier_Store(&running_, 0); 708 v8::TickSample sample; 709 // Reset 'paused_' flag, otherwise semaphore may not be signalled. 710 resume(); 711 Insert(&sample); 712 Join(); 713 714 LOG(isolate_, UncheckedStringEvent("profiler", "end")); 715 } 716 717 718 void Profiler::Run() { 719 v8::TickSample sample; 720 bool overflow = Remove(&sample); 721 while (base::NoBarrier_Load(&running_)) { 722 LOG(isolate_, TickEvent(&sample, overflow)); 723 overflow = Remove(&sample); 724 } 725 } 726 727 728 // 729 // Logger class implementation. 730 // 731 732 Logger::Logger(Isolate* isolate) 733 : isolate_(isolate), 734 ticker_(NULL), 735 profiler_(NULL), 736 log_events_(NULL), 737 is_logging_(false), 738 log_(new Log(this)), 739 perf_basic_logger_(NULL), 740 perf_jit_logger_(NULL), 741 ll_logger_(NULL), 742 jit_logger_(NULL), 743 listeners_(5), 744 is_initialized_(false) {} 745 746 Logger::~Logger() { 747 delete log_; 748 } 749 750 void Logger::addCodeEventListener(CodeEventListener* listener) { 751 bool result = isolate_->code_event_dispatcher()->AddListener(listener); 752 USE(result); 753 DCHECK(result); 754 } 755 756 void Logger::removeCodeEventListener(CodeEventListener* listener) { 757 isolate_->code_event_dispatcher()->RemoveListener(listener); 758 } 759 760 void Logger::ProfilerBeginEvent() { 761 if (!log_->IsEnabled()) return; 762 Log::MessageBuilder msg(log_); 763 msg.Append("profiler,\"begin\",%d", kSamplingIntervalMs); 764 msg.WriteToLogFile(); 765 } 766 767 768 void Logger::StringEvent(const char* name, const char* value) { 769 if (FLAG_log) UncheckedStringEvent(name, value); 770 } 771 772 773 void Logger::UncheckedStringEvent(const char* name, const char* value) { 774 if (!log_->IsEnabled()) return; 775 Log::MessageBuilder msg(log_); 776 msg.Append("%s,\"%s\"", name, value); 777 msg.WriteToLogFile(); 778 } 779 780 781 void Logger::IntEvent(const char* name, int value) { 782 if (FLAG_log) UncheckedIntEvent(name, value); 783 } 784 785 786 void Logger::IntPtrTEvent(const char* name, intptr_t value) { 787 if (FLAG_log) UncheckedIntPtrTEvent(name, value); 788 } 789 790 791 void Logger::UncheckedIntEvent(const char* name, int value) { 792 if (!log_->IsEnabled()) return; 793 Log::MessageBuilder msg(log_); 794 msg.Append("%s,%d", name, value); 795 msg.WriteToLogFile(); 796 } 797 798 799 void Logger::UncheckedIntPtrTEvent(const char* name, intptr_t value) { 800 if (!log_->IsEnabled()) return; 801 Log::MessageBuilder msg(log_); 802 msg.Append("%s,%" V8PRIdPTR, name, value); 803 msg.WriteToLogFile(); 804 } 805 806 807 void Logger::HandleEvent(const char* name, Object** location) { 808 if (!log_->IsEnabled() || !FLAG_log_handles) return; 809 Log::MessageBuilder msg(log_); 810 msg.Append("%s,%p", name, static_cast<void*>(location)); 811 msg.WriteToLogFile(); 812 } 813 814 815 // ApiEvent is private so all the calls come from the Logger class. It is the 816 // caller's responsibility to ensure that log is enabled and that 817 // FLAG_log_api is true. 818 void Logger::ApiEvent(const char* format, ...) { 819 DCHECK(log_->IsEnabled() && FLAG_log_api); 820 Log::MessageBuilder msg(log_); 821 va_list ap; 822 va_start(ap, format); 823 msg.AppendVA(format, ap); 824 va_end(ap); 825 msg.WriteToLogFile(); 826 } 827 828 829 void Logger::ApiSecurityCheck() { 830 if (!log_->IsEnabled() || !FLAG_log_api) return; 831 ApiEvent("api,check-security"); 832 } 833 834 void Logger::SharedLibraryEvent(const std::string& library_path, 835 uintptr_t start, uintptr_t end, 836 intptr_t aslr_slide) { 837 if (!log_->IsEnabled() || !FLAG_prof_cpp) return; 838 Log::MessageBuilder msg(log_); 839 msg.Append("shared-library,\"%s\",0x%08" V8PRIxPTR ",0x%08" V8PRIxPTR 840 ",%" V8PRIdPTR, 841 library_path.c_str(), start, end, aslr_slide); 842 msg.WriteToLogFile(); 843 } 844 845 846 void Logger::CodeDeoptEvent(Code* code, Address pc, int fp_to_sp_delta) { 847 if (!log_->IsEnabled() || !FLAG_log_internal_timer_events) return; 848 Log::MessageBuilder msg(log_); 849 int since_epoch = static_cast<int>(timer_.Elapsed().InMicroseconds()); 850 msg.Append("code-deopt,%d,%d", since_epoch, code->CodeSize()); 851 msg.WriteToLogFile(); 852 } 853 854 855 void Logger::CurrentTimeEvent() { 856 if (!log_->IsEnabled()) return; 857 DCHECK(FLAG_log_timer_events || FLAG_prof_cpp); 858 Log::MessageBuilder msg(log_); 859 int since_epoch = static_cast<int>(timer_.Elapsed().InMicroseconds()); 860 msg.Append("current-time,%d", since_epoch); 861 msg.WriteToLogFile(); 862 } 863 864 865 void Logger::TimerEvent(Logger::StartEnd se, const char* name) { 866 if (!log_->IsEnabled()) return; 867 DCHECK(FLAG_log_internal_timer_events); 868 Log::MessageBuilder msg(log_); 869 int since_epoch = static_cast<int>(timer_.Elapsed().InMicroseconds()); 870 const char* format = (se == START) ? "timer-event-start,\"%s\",%ld" 871 : "timer-event-end,\"%s\",%ld"; 872 msg.Append(format, name, since_epoch); 873 msg.WriteToLogFile(); 874 } 875 876 877 void Logger::EnterExternal(Isolate* isolate) { 878 LOG(isolate, TimerEvent(START, TimerEventExternal::name())); 879 DCHECK(isolate->current_vm_state() == JS); 880 isolate->set_current_vm_state(EXTERNAL); 881 } 882 883 884 void Logger::LeaveExternal(Isolate* isolate) { 885 LOG(isolate, TimerEvent(END, TimerEventExternal::name())); 886 DCHECK(isolate->current_vm_state() == EXTERNAL); 887 isolate->set_current_vm_state(JS); 888 } 889 890 // Instantiate template methods. 891 #define V(TimerName, expose) \ 892 template void TimerEventScope<TimerEvent##TimerName>::LogTimerEvent( \ 893 Logger::StartEnd se); 894 TIMER_EVENTS_LIST(V) 895 #undef V 896 897 void Logger::ApiNamedPropertyAccess(const char* tag, 898 JSObject* holder, 899 Object* name) { 900 DCHECK(name->IsName()); 901 if (!log_->IsEnabled() || !FLAG_log_api) return; 902 String* class_name_obj = holder->class_name(); 903 std::unique_ptr<char[]> class_name = 904 class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 905 if (name->IsString()) { 906 std::unique_ptr<char[]> property_name = 907 String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 908 ApiEvent("api,%s,\"%s\",\"%s\"", tag, class_name.get(), 909 property_name.get()); 910 } else { 911 Symbol* symbol = Symbol::cast(name); 912 uint32_t hash = symbol->Hash(); 913 if (symbol->name()->IsUndefined(symbol->GetIsolate())) { 914 ApiEvent("api,%s,\"%s\",symbol(hash %x)", tag, class_name.get(), hash); 915 } else { 916 std::unique_ptr<char[]> str = 917 String::cast(symbol->name()) 918 ->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 919 ApiEvent("api,%s,\"%s\",symbol(\"%s\" hash %x)", tag, class_name.get(), 920 str.get(), hash); 921 } 922 } 923 } 924 925 void Logger::ApiIndexedPropertyAccess(const char* tag, 926 JSObject* holder, 927 uint32_t index) { 928 if (!log_->IsEnabled() || !FLAG_log_api) return; 929 String* class_name_obj = holder->class_name(); 930 std::unique_ptr<char[]> class_name = 931 class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 932 ApiEvent("api,%s,\"%s\",%u", tag, class_name.get(), index); 933 } 934 935 936 void Logger::ApiObjectAccess(const char* tag, JSObject* object) { 937 if (!log_->IsEnabled() || !FLAG_log_api) return; 938 String* class_name_obj = object->class_name(); 939 std::unique_ptr<char[]> class_name = 940 class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 941 ApiEvent("api,%s,\"%s\"", tag, class_name.get()); 942 } 943 944 945 void Logger::ApiEntryCall(const char* name) { 946 if (!log_->IsEnabled() || !FLAG_log_api) return; 947 ApiEvent("api,%s", name); 948 } 949 950 951 void Logger::NewEvent(const char* name, void* object, size_t size) { 952 if (!log_->IsEnabled() || !FLAG_log) return; 953 Log::MessageBuilder msg(log_); 954 msg.Append("new,%s,%p,%u", name, object, static_cast<unsigned int>(size)); 955 msg.WriteToLogFile(); 956 } 957 958 959 void Logger::DeleteEvent(const char* name, void* object) { 960 if (!log_->IsEnabled() || !FLAG_log) return; 961 Log::MessageBuilder msg(log_); 962 msg.Append("delete,%s,%p", name, object); 963 msg.WriteToLogFile(); 964 } 965 966 967 void Logger::CallbackEventInternal(const char* prefix, Name* name, 968 Address entry_point) { 969 if (!FLAG_log_code || !log_->IsEnabled()) return; 970 Log::MessageBuilder msg(log_); 971 msg.Append("%s,%s,-2,", 972 kLogEventsNames[CodeEventListener::CODE_CREATION_EVENT], 973 kLogEventsNames[CodeEventListener::CALLBACK_TAG]); 974 msg.AppendAddress(entry_point); 975 if (name->IsString()) { 976 std::unique_ptr<char[]> str = 977 String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 978 msg.Append(",1,\"%s%s\"", prefix, str.get()); 979 } else { 980 Symbol* symbol = Symbol::cast(name); 981 if (symbol->name()->IsUndefined(symbol->GetIsolate())) { 982 msg.Append(",1,symbol(hash %x)", symbol->Hash()); 983 } else { 984 std::unique_ptr<char[]> str = 985 String::cast(symbol->name()) 986 ->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 987 msg.Append(",1,symbol(\"%s%s\" hash %x)", prefix, str.get(), 988 symbol->Hash()); 989 } 990 } 991 msg.WriteToLogFile(); 992 } 993 994 995 void Logger::CallbackEvent(Name* name, Address entry_point) { 996 CallbackEventInternal("", name, entry_point); 997 } 998 999 1000 void Logger::GetterCallbackEvent(Name* name, Address entry_point) { 1001 CallbackEventInternal("get ", name, entry_point); 1002 } 1003 1004 1005 void Logger::SetterCallbackEvent(Name* name, Address entry_point) { 1006 CallbackEventInternal("set ", name, entry_point); 1007 } 1008 1009 static void AppendCodeCreateHeader(Log::MessageBuilder* msg, 1010 CodeEventListener::LogEventsAndTags tag, 1011 AbstractCode* code) { 1012 DCHECK(msg); 1013 msg->Append("%s,%s,%d,", 1014 kLogEventsNames[CodeEventListener::CODE_CREATION_EVENT], 1015 kLogEventsNames[tag], code->kind()); 1016 msg->AppendAddress(code->address()); 1017 msg->Append(",%d,", code->ExecutableSize()); 1018 } 1019 1020 void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 1021 AbstractCode* code, const char* comment) { 1022 if (!is_logging_code_events()) return; 1023 if (!FLAG_log_code || !log_->IsEnabled()) return; 1024 Log::MessageBuilder msg(log_); 1025 AppendCodeCreateHeader(&msg, tag, code); 1026 msg.AppendDoubleQuotedString(comment); 1027 msg.WriteToLogFile(); 1028 } 1029 1030 void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 1031 AbstractCode* code, Name* name) { 1032 if (!is_logging_code_events()) return; 1033 if (!FLAG_log_code || !log_->IsEnabled()) return; 1034 Log::MessageBuilder msg(log_); 1035 AppendCodeCreateHeader(&msg, tag, code); 1036 if (name->IsString()) { 1037 msg.Append('"'); 1038 msg.AppendDetailed(String::cast(name), false); 1039 msg.Append('"'); 1040 } else { 1041 msg.AppendSymbolName(Symbol::cast(name)); 1042 } 1043 msg.WriteToLogFile(); 1044 } 1045 1046 void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 1047 AbstractCode* code, SharedFunctionInfo* shared, 1048 Name* name) { 1049 if (!is_logging_code_events()) return; 1050 if (!FLAG_log_code || !log_->IsEnabled()) return; 1051 if (code == AbstractCode::cast( 1052 isolate_->builtins()->builtin(Builtins::kCompileLazy))) { 1053 return; 1054 } 1055 1056 Log::MessageBuilder msg(log_); 1057 AppendCodeCreateHeader(&msg, tag, code); 1058 if (name->IsString()) { 1059 std::unique_ptr<char[]> str = 1060 String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 1061 msg.Append("\"%s\"", str.get()); 1062 } else { 1063 msg.AppendSymbolName(Symbol::cast(name)); 1064 } 1065 msg.Append(','); 1066 msg.AppendAddress(shared->address()); 1067 msg.Append(",%s", ComputeMarker(shared, code)); 1068 msg.WriteToLogFile(); 1069 } 1070 1071 1072 // Although, it is possible to extract source and line from 1073 // the SharedFunctionInfo object, we left it to caller 1074 // to leave logging functions free from heap allocations. 1075 void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 1076 AbstractCode* code, SharedFunctionInfo* shared, 1077 Name* source, int line, int column) { 1078 if (!is_logging_code_events()) return; 1079 if (!FLAG_log_code || !log_->IsEnabled()) return; 1080 Log::MessageBuilder msg(log_); 1081 AppendCodeCreateHeader(&msg, tag, code); 1082 std::unique_ptr<char[]> name = 1083 shared->DebugName()->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 1084 msg.Append("\"%s ", name.get()); 1085 if (source->IsString()) { 1086 std::unique_ptr<char[]> sourcestr = String::cast(source)->ToCString( 1087 DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 1088 msg.Append("%s", sourcestr.get()); 1089 } else { 1090 msg.AppendSymbolName(Symbol::cast(source)); 1091 } 1092 msg.Append(":%d:%d\",", line, column); 1093 msg.AppendAddress(shared->address()); 1094 msg.Append(",%s", ComputeMarker(shared, code)); 1095 msg.WriteToLogFile(); 1096 } 1097 1098 void Logger::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 1099 AbstractCode* code, int args_count) { 1100 if (!is_logging_code_events()) return; 1101 if (!FLAG_log_code || !log_->IsEnabled()) return; 1102 Log::MessageBuilder msg(log_); 1103 AppendCodeCreateHeader(&msg, tag, code); 1104 msg.Append("\"args_count: %d\"", args_count); 1105 msg.WriteToLogFile(); 1106 } 1107 1108 void Logger::CodeDisableOptEvent(AbstractCode* code, 1109 SharedFunctionInfo* shared) { 1110 if (!is_logging_code_events()) return; 1111 if (!FLAG_log_code || !log_->IsEnabled()) return; 1112 Log::MessageBuilder msg(log_); 1113 msg.Append("%s,", kLogEventsNames[CodeEventListener::CODE_DISABLE_OPT_EVENT]); 1114 std::unique_ptr<char[]> name = 1115 shared->DebugName()->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 1116 msg.Append("\"%s\",", name.get()); 1117 msg.Append("\"%s\"", GetBailoutReason(shared->disable_optimization_reason())); 1118 msg.WriteToLogFile(); 1119 } 1120 1121 1122 void Logger::CodeMovingGCEvent() { 1123 if (!is_logging_code_events()) return; 1124 if (!log_->IsEnabled() || !FLAG_ll_prof) return; 1125 base::OS::SignalCodeMovingGC(); 1126 } 1127 1128 void Logger::RegExpCodeCreateEvent(AbstractCode* code, String* source) { 1129 if (!is_logging_code_events()) return; 1130 if (!FLAG_log_code || !log_->IsEnabled()) return; 1131 Log::MessageBuilder msg(log_); 1132 AppendCodeCreateHeader(&msg, CodeEventListener::REG_EXP_TAG, code); 1133 msg.Append('"'); 1134 msg.AppendDetailed(source, false); 1135 msg.Append('"'); 1136 msg.WriteToLogFile(); 1137 } 1138 1139 void Logger::CodeMoveEvent(AbstractCode* from, Address to) { 1140 if (!is_logging_code_events()) return; 1141 MoveEventInternal(CodeEventListener::CODE_MOVE_EVENT, from->address(), to); 1142 } 1143 1144 void Logger::CodeLinePosInfoRecordEvent(AbstractCode* code, 1145 ByteArray* source_position_table) { 1146 if (jit_logger_) { 1147 void* jit_handler_data = jit_logger_->StartCodePosInfoEvent(); 1148 for (SourcePositionTableIterator iter(source_position_table); !iter.done(); 1149 iter.Advance()) { 1150 if (iter.is_statement()) { 1151 jit_logger_->AddCodeLinePosInfoEvent( 1152 jit_handler_data, iter.code_offset(), 1153 iter.source_position().ScriptOffset(), 1154 JitCodeEvent::STATEMENT_POSITION); 1155 } 1156 jit_logger_->AddCodeLinePosInfoEvent( 1157 jit_handler_data, iter.code_offset(), 1158 iter.source_position().ScriptOffset(), JitCodeEvent::POSITION); 1159 } 1160 jit_logger_->EndCodePosInfoEvent(code, jit_handler_data); 1161 } 1162 } 1163 1164 void Logger::CodeNameEvent(Address addr, int pos, const char* code_name) { 1165 if (code_name == NULL) return; // Not a code object. 1166 Log::MessageBuilder msg(log_); 1167 msg.Append("%s,%d,", 1168 kLogEventsNames[CodeEventListener::SNAPSHOT_CODE_NAME_EVENT], pos); 1169 msg.AppendDoubleQuotedString(code_name); 1170 msg.WriteToLogFile(); 1171 } 1172 1173 1174 void Logger::SharedFunctionInfoMoveEvent(Address from, Address to) { 1175 if (!is_logging_code_events()) return; 1176 MoveEventInternal(CodeEventListener::SHARED_FUNC_MOVE_EVENT, from, to); 1177 } 1178 1179 void Logger::MoveEventInternal(CodeEventListener::LogEventsAndTags event, 1180 Address from, Address to) { 1181 if (!FLAG_log_code || !log_->IsEnabled()) return; 1182 Log::MessageBuilder msg(log_); 1183 msg.Append("%s,", kLogEventsNames[event]); 1184 msg.AppendAddress(from); 1185 msg.Append(','); 1186 msg.AppendAddress(to); 1187 msg.WriteToLogFile(); 1188 } 1189 1190 1191 void Logger::ResourceEvent(const char* name, const char* tag) { 1192 if (!log_->IsEnabled() || !FLAG_log) return; 1193 Log::MessageBuilder msg(log_); 1194 msg.Append("%s,%s,", name, tag); 1195 1196 uint32_t sec, usec; 1197 if (base::OS::GetUserTime(&sec, &usec) != -1) { 1198 msg.Append("%d,%d,", sec, usec); 1199 } 1200 msg.Append("%.0f", base::OS::TimeCurrentMillis()); 1201 msg.WriteToLogFile(); 1202 } 1203 1204 1205 void Logger::SuspectReadEvent(Name* name, Object* obj) { 1206 if (!log_->IsEnabled() || !FLAG_log_suspect) return; 1207 Log::MessageBuilder msg(log_); 1208 String* class_name = obj->IsJSObject() 1209 ? JSObject::cast(obj)->class_name() 1210 : isolate_->heap()->empty_string(); 1211 msg.Append("suspect-read,"); 1212 msg.Append(class_name); 1213 msg.Append(','); 1214 if (name->IsString()) { 1215 msg.Append('"'); 1216 msg.Append(String::cast(name)); 1217 msg.Append('"'); 1218 } else { 1219 msg.AppendSymbolName(Symbol::cast(name)); 1220 } 1221 msg.WriteToLogFile(); 1222 } 1223 1224 1225 void Logger::HeapSampleBeginEvent(const char* space, const char* kind) { 1226 if (!log_->IsEnabled() || !FLAG_log_gc) return; 1227 Log::MessageBuilder msg(log_); 1228 // Using non-relative system time in order to be able to synchronize with 1229 // external memory profiling events (e.g. DOM memory size). 1230 msg.Append("heap-sample-begin,\"%s\",\"%s\",%.0f", space, kind, 1231 base::OS::TimeCurrentMillis()); 1232 msg.WriteToLogFile(); 1233 } 1234 1235 1236 void Logger::HeapSampleEndEvent(const char* space, const char* kind) { 1237 if (!log_->IsEnabled() || !FLAG_log_gc) return; 1238 Log::MessageBuilder msg(log_); 1239 msg.Append("heap-sample-end,\"%s\",\"%s\"", space, kind); 1240 msg.WriteToLogFile(); 1241 } 1242 1243 1244 void Logger::HeapSampleItemEvent(const char* type, int number, int bytes) { 1245 if (!log_->IsEnabled() || !FLAG_log_gc) return; 1246 Log::MessageBuilder msg(log_); 1247 msg.Append("heap-sample-item,%s,%d,%d", type, number, bytes); 1248 msg.WriteToLogFile(); 1249 } 1250 1251 1252 void Logger::RuntimeCallTimerEvent() { 1253 RuntimeCallStats* stats = isolate_->counters()->runtime_call_stats(); 1254 RuntimeCallTimer* timer = stats->current_timer(); 1255 if (timer == nullptr) return; 1256 RuntimeCallCounter* counter = timer->counter(); 1257 if (counter == nullptr) return; 1258 Log::MessageBuilder msg(log_); 1259 msg.Append("active-runtime-timer,"); 1260 msg.AppendDoubleQuotedString(counter->name()); 1261 msg.WriteToLogFile(); 1262 } 1263 1264 void Logger::TickEvent(v8::TickSample* sample, bool overflow) { 1265 if (!log_->IsEnabled() || !FLAG_prof_cpp) return; 1266 if (V8_UNLIKELY(FLAG_runtime_stats == 1267 v8::tracing::TracingCategoryObserver::ENABLED_BY_NATIVE)) { 1268 RuntimeCallTimerEvent(); 1269 } 1270 Log::MessageBuilder msg(log_); 1271 msg.Append("%s,", kLogEventsNames[CodeEventListener::TICK_EVENT]); 1272 msg.AppendAddress(reinterpret_cast<Address>(sample->pc)); 1273 msg.Append(",%d", static_cast<int>(timer_.Elapsed().InMicroseconds())); 1274 if (sample->has_external_callback) { 1275 msg.Append(",1,"); 1276 msg.AppendAddress( 1277 reinterpret_cast<Address>(sample->external_callback_entry)); 1278 } else { 1279 msg.Append(",0,"); 1280 msg.AppendAddress(reinterpret_cast<Address>(sample->tos)); 1281 } 1282 msg.Append(",%d", static_cast<int>(sample->state)); 1283 if (overflow) { 1284 msg.Append(",overflow"); 1285 } 1286 for (unsigned i = 0; i < sample->frames_count; ++i) { 1287 msg.Append(','); 1288 msg.AppendAddress(reinterpret_cast<Address>(sample->stack[i])); 1289 } 1290 msg.WriteToLogFile(); 1291 } 1292 1293 void Logger::ICEvent(const char* type, bool keyed, const Address pc, int line, 1294 int column, Map* map, Object* key, char old_state, 1295 char new_state, const char* modifier, 1296 const char* slow_stub_reason) { 1297 if (!log_->IsEnabled() || !FLAG_trace_ic) return; 1298 Log::MessageBuilder msg(log_); 1299 if (keyed) msg.Append("Keyed"); 1300 msg.Append("%s,", type); 1301 msg.AppendAddress(pc); 1302 msg.Append(",%d,%d,", line, column); 1303 msg.Append(old_state); 1304 msg.Append(","); 1305 msg.Append(new_state); 1306 msg.Append(","); 1307 msg.AppendAddress(reinterpret_cast<Address>(map)); 1308 msg.Append(","); 1309 if (key->IsSmi()) { 1310 msg.Append("%d", Smi::cast(key)->value()); 1311 } else if (key->IsNumber()) { 1312 msg.Append("%lf", key->Number()); 1313 } else if (key->IsString()) { 1314 msg.AppendDetailed(String::cast(key), false); 1315 } else if (key->IsSymbol()) { 1316 msg.AppendSymbolName(Symbol::cast(key)); 1317 } 1318 msg.Append(",%s,", modifier); 1319 if (slow_stub_reason != nullptr) { 1320 msg.AppendDoubleQuotedString(slow_stub_reason); 1321 } 1322 msg.WriteToLogFile(); 1323 } 1324 1325 void Logger::CompareIC(const Address pc, int line, int column, Code* stub, 1326 const char* op, const char* old_left, 1327 const char* old_right, const char* old_state, 1328 const char* new_left, const char* new_right, 1329 const char* new_state) { 1330 if (!log_->IsEnabled() || !FLAG_trace_ic) return; 1331 Log::MessageBuilder msg(log_); 1332 msg.Append("CompareIC,"); 1333 msg.AppendAddress(pc); 1334 msg.Append(",%d,%d,", line, column); 1335 msg.AppendAddress(reinterpret_cast<Address>(stub)); 1336 msg.Append(",%s,%s,%s,%s,%s,%s,%s", op, old_left, old_right, old_state, 1337 new_left, new_right, new_state); 1338 msg.WriteToLogFile(); 1339 } 1340 1341 void Logger::BinaryOpIC(const Address pc, int line, int column, Code* stub, 1342 const char* old_state, const char* new_state, 1343 AllocationSite* allocation_site) { 1344 if (!log_->IsEnabled() || !FLAG_trace_ic) return; 1345 Log::MessageBuilder msg(log_); 1346 msg.Append("BinaryOpIC,"); 1347 msg.AppendAddress(pc); 1348 msg.Append(",%d,%d,", line, column); 1349 msg.AppendAddress(reinterpret_cast<Address>(stub)); 1350 msg.Append(",%s,%s,", old_state, new_state); 1351 if (allocation_site != nullptr) { 1352 msg.AppendAddress(reinterpret_cast<Address>(allocation_site)); 1353 } 1354 msg.WriteToLogFile(); 1355 } 1356 1357 void Logger::ToBooleanIC(const Address pc, int line, int column, Code* stub, 1358 const char* old_state, const char* new_state) { 1359 if (!log_->IsEnabled() || !FLAG_trace_ic) return; 1360 Log::MessageBuilder msg(log_); 1361 msg.Append("ToBooleanIC,"); 1362 msg.AppendAddress(pc); 1363 msg.Append(",%d,%d,", line, column); 1364 msg.AppendAddress(reinterpret_cast<Address>(stub)); 1365 msg.Append(",%s,%s,", old_state, new_state); 1366 msg.WriteToLogFile(); 1367 } 1368 1369 void Logger::PatchIC(const Address pc, const Address test, int delta) { 1370 if (!log_->IsEnabled() || !FLAG_trace_ic) return; 1371 Log::MessageBuilder msg(log_); 1372 msg.Append("PatchIC,"); 1373 msg.AppendAddress(pc); 1374 msg.Append(","); 1375 msg.AppendAddress(test); 1376 msg.Append(","); 1377 msg.Append("%d,", delta); 1378 msg.WriteToLogFile(); 1379 } 1380 1381 void Logger::StopProfiler() { 1382 if (!log_->IsEnabled()) return; 1383 if (profiler_ != NULL) { 1384 profiler_->pause(); 1385 is_logging_ = false; 1386 removeCodeEventListener(this); 1387 } 1388 } 1389 1390 1391 // This function can be called when Log's mutex is acquired, 1392 // either from main or Profiler's thread. 1393 void Logger::LogFailure() { 1394 StopProfiler(); 1395 } 1396 1397 static void AddFunctionAndCode(SharedFunctionInfo* sfi, 1398 AbstractCode* code_object, 1399 Handle<SharedFunctionInfo>* sfis, 1400 Handle<AbstractCode>* code_objects, int offset) { 1401 if (sfis != NULL) { 1402 sfis[offset] = Handle<SharedFunctionInfo>(sfi); 1403 } 1404 if (code_objects != NULL) { 1405 code_objects[offset] = Handle<AbstractCode>(code_object); 1406 } 1407 } 1408 1409 class EnumerateOptimizedFunctionsVisitor: public OptimizedFunctionVisitor { 1410 public: 1411 EnumerateOptimizedFunctionsVisitor(Handle<SharedFunctionInfo>* sfis, 1412 Handle<AbstractCode>* code_objects, 1413 int* count) 1414 : sfis_(sfis), code_objects_(code_objects), count_(count) {} 1415 1416 virtual void EnterContext(Context* context) {} 1417 virtual void LeaveContext(Context* context) {} 1418 1419 virtual void VisitFunction(JSFunction* function) { 1420 SharedFunctionInfo* sfi = SharedFunctionInfo::cast(function->shared()); 1421 Object* maybe_script = sfi->script(); 1422 if (maybe_script->IsScript() 1423 && !Script::cast(maybe_script)->HasValidSource()) return; 1424 1425 DCHECK(function->abstract_code()->kind() == 1426 AbstractCode::OPTIMIZED_FUNCTION); 1427 AddFunctionAndCode(sfi, function->abstract_code(), sfis_, code_objects_, 1428 *count_); 1429 *count_ = *count_ + 1; 1430 } 1431 1432 private: 1433 Handle<SharedFunctionInfo>* sfis_; 1434 Handle<AbstractCode>* code_objects_; 1435 int* count_; 1436 }; 1437 1438 static int EnumerateCompiledFunctions(Heap* heap, 1439 Handle<SharedFunctionInfo>* sfis, 1440 Handle<AbstractCode>* code_objects) { 1441 HeapIterator iterator(heap); 1442 DisallowHeapAllocation no_gc; 1443 int compiled_funcs_count = 0; 1444 1445 // Iterate the heap to find shared function info objects and record 1446 // the unoptimized code for them. 1447 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { 1448 if (!obj->IsSharedFunctionInfo()) continue; 1449 SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj); 1450 if (sfi->is_compiled() 1451 && (!sfi->script()->IsScript() 1452 || Script::cast(sfi->script())->HasValidSource())) { 1453 // In some cases, an SFI might have (and have executing!) both bytecode 1454 // and baseline code, so check for both and add them both if needed. 1455 if (sfi->HasBytecodeArray()) { 1456 AddFunctionAndCode(sfi, AbstractCode::cast(sfi->bytecode_array()), sfis, 1457 code_objects, compiled_funcs_count); 1458 ++compiled_funcs_count; 1459 } 1460 1461 if (!sfi->IsInterpreted()) { 1462 AddFunctionAndCode(sfi, AbstractCode::cast(sfi->code()), sfis, 1463 code_objects, compiled_funcs_count); 1464 ++compiled_funcs_count; 1465 } 1466 } 1467 } 1468 1469 // Iterate all optimized functions in all contexts. 1470 EnumerateOptimizedFunctionsVisitor visitor(sfis, 1471 code_objects, 1472 &compiled_funcs_count); 1473 Deoptimizer::VisitAllOptimizedFunctions(heap->isolate(), &visitor); 1474 1475 return compiled_funcs_count; 1476 } 1477 1478 1479 void Logger::LogCodeObject(Object* object) { 1480 AbstractCode* code_object = AbstractCode::cast(object); 1481 CodeEventListener::LogEventsAndTags tag = CodeEventListener::STUB_TAG; 1482 const char* description = "Unknown code from the snapshot"; 1483 switch (code_object->kind()) { 1484 case AbstractCode::FUNCTION: 1485 case AbstractCode::INTERPRETED_FUNCTION: 1486 case AbstractCode::OPTIMIZED_FUNCTION: 1487 return; // We log this later using LogCompiledFunctions. 1488 case AbstractCode::BYTECODE_HANDLER: 1489 return; // We log it later by walking the dispatch table. 1490 case AbstractCode::BINARY_OP_IC: // fall through 1491 case AbstractCode::COMPARE_IC: // fall through 1492 case AbstractCode::TO_BOOLEAN_IC: // fall through 1493 1494 case AbstractCode::STUB: 1495 description = 1496 CodeStub::MajorName(CodeStub::GetMajorKey(code_object->GetCode())); 1497 if (description == NULL) 1498 description = "A stub from the snapshot"; 1499 tag = CodeEventListener::STUB_TAG; 1500 break; 1501 case AbstractCode::REGEXP: 1502 description = "Regular expression code"; 1503 tag = CodeEventListener::REG_EXP_TAG; 1504 break; 1505 case AbstractCode::BUILTIN: 1506 description = 1507 isolate_->builtins()->name(code_object->GetCode()->builtin_index()); 1508 tag = CodeEventListener::BUILTIN_TAG; 1509 break; 1510 case AbstractCode::HANDLER: 1511 description = "An IC handler from the snapshot"; 1512 tag = CodeEventListener::HANDLER_TAG; 1513 break; 1514 case AbstractCode::KEYED_LOAD_IC: 1515 description = "A keyed load IC from the snapshot"; 1516 tag = CodeEventListener::KEYED_LOAD_IC_TAG; 1517 break; 1518 case AbstractCode::LOAD_IC: 1519 description = "A load IC from the snapshot"; 1520 tag = CodeEventListener::LOAD_IC_TAG; 1521 break; 1522 case AbstractCode::LOAD_GLOBAL_IC: 1523 description = "A load global IC from the snapshot"; 1524 tag = Logger::LOAD_GLOBAL_IC_TAG; 1525 break; 1526 case AbstractCode::STORE_IC: 1527 description = "A store IC from the snapshot"; 1528 tag = CodeEventListener::STORE_IC_TAG; 1529 break; 1530 case AbstractCode::KEYED_STORE_IC: 1531 description = "A keyed store IC from the snapshot"; 1532 tag = CodeEventListener::KEYED_STORE_IC_TAG; 1533 break; 1534 case AbstractCode::WASM_FUNCTION: 1535 description = "A Wasm function"; 1536 tag = CodeEventListener::STUB_TAG; 1537 break; 1538 case AbstractCode::JS_TO_WASM_FUNCTION: 1539 description = "A JavaScript to Wasm adapter"; 1540 tag = CodeEventListener::STUB_TAG; 1541 break; 1542 case AbstractCode::WASM_TO_JS_FUNCTION: 1543 description = "A Wasm to JavaScript adapter"; 1544 tag = CodeEventListener::STUB_TAG; 1545 break; 1546 case AbstractCode::WASM_INTERPRETER_ENTRY: 1547 description = "A Wasm to Interpreter adapter"; 1548 tag = CodeEventListener::STUB_TAG; 1549 break; 1550 case AbstractCode::NUMBER_OF_KINDS: 1551 UNIMPLEMENTED(); 1552 } 1553 PROFILE(isolate_, CodeCreateEvent(tag, code_object, description)); 1554 } 1555 1556 1557 void Logger::LogCodeObjects() { 1558 Heap* heap = isolate_->heap(); 1559 HeapIterator iterator(heap); 1560 DisallowHeapAllocation no_gc; 1561 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { 1562 if (obj->IsCode()) LogCodeObject(obj); 1563 if (obj->IsBytecodeArray()) LogCodeObject(obj); 1564 } 1565 } 1566 1567 void Logger::LogBytecodeHandlers() { 1568 const interpreter::OperandScale kOperandScales[] = { 1569 #define VALUE(Name, _) interpreter::OperandScale::k##Name, 1570 OPERAND_SCALE_LIST(VALUE) 1571 #undef VALUE 1572 }; 1573 1574 const int last_index = static_cast<int>(interpreter::Bytecode::kLast); 1575 interpreter::Interpreter* interpreter = isolate_->interpreter(); 1576 for (auto operand_scale : kOperandScales) { 1577 for (int index = 0; index <= last_index; ++index) { 1578 interpreter::Bytecode bytecode = interpreter::Bytecodes::FromByte(index); 1579 if (interpreter::Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) { 1580 Code* code = interpreter->GetBytecodeHandler(bytecode, operand_scale); 1581 std::string bytecode_name = 1582 interpreter::Bytecodes::ToString(bytecode, operand_scale); 1583 PROFILE(isolate_, CodeCreateEvent( 1584 CodeEventListener::BYTECODE_HANDLER_TAG, 1585 AbstractCode::cast(code), bytecode_name.c_str())); 1586 } 1587 } 1588 } 1589 } 1590 1591 void Logger::LogExistingFunction(Handle<SharedFunctionInfo> shared, 1592 Handle<AbstractCode> code) { 1593 if (shared->script()->IsScript()) { 1594 Handle<Script> script(Script::cast(shared->script())); 1595 int line_num = Script::GetLineNumber(script, shared->start_position()) + 1; 1596 int column_num = 1597 Script::GetColumnNumber(script, shared->start_position()) + 1; 1598 if (script->name()->IsString()) { 1599 Handle<String> script_name(String::cast(script->name())); 1600 if (line_num > 0) { 1601 PROFILE(isolate_, 1602 CodeCreateEvent( 1603 Logger::ToNativeByScript( 1604 CodeEventListener::LAZY_COMPILE_TAG, *script), 1605 *code, *shared, *script_name, line_num, column_num)); 1606 } else { 1607 // Can't distinguish eval and script here, so always use Script. 1608 PROFILE(isolate_, 1609 CodeCreateEvent(Logger::ToNativeByScript( 1610 CodeEventListener::SCRIPT_TAG, *script), 1611 *code, *shared, *script_name)); 1612 } 1613 } else { 1614 PROFILE(isolate_, 1615 CodeCreateEvent(Logger::ToNativeByScript( 1616 CodeEventListener::LAZY_COMPILE_TAG, *script), 1617 *code, *shared, isolate_->heap()->empty_string(), 1618 line_num, column_num)); 1619 } 1620 } else if (shared->IsApiFunction()) { 1621 // API function. 1622 FunctionTemplateInfo* fun_data = shared->get_api_func_data(); 1623 Object* raw_call_data = fun_data->call_code(); 1624 if (!raw_call_data->IsUndefined(isolate_)) { 1625 CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data); 1626 Object* callback_obj = call_data->callback(); 1627 Address entry_point = v8::ToCData<Address>(callback_obj); 1628 #if USES_FUNCTION_DESCRIPTORS 1629 entry_point = *FUNCTION_ENTRYPOINT_ADDRESS(entry_point); 1630 #endif 1631 PROFILE(isolate_, CallbackEvent(shared->DebugName(), entry_point)); 1632 } 1633 } else { 1634 PROFILE(isolate_, 1635 CodeCreateEvent(CodeEventListener::LAZY_COMPILE_TAG, *code, *shared, 1636 isolate_->heap()->empty_string())); 1637 } 1638 } 1639 1640 1641 void Logger::LogCompiledFunctions() { 1642 Heap* heap = isolate_->heap(); 1643 HandleScope scope(isolate_); 1644 const int compiled_funcs_count = EnumerateCompiledFunctions(heap, NULL, NULL); 1645 ScopedVector< Handle<SharedFunctionInfo> > sfis(compiled_funcs_count); 1646 ScopedVector<Handle<AbstractCode> > code_objects(compiled_funcs_count); 1647 EnumerateCompiledFunctions(heap, sfis.start(), code_objects.start()); 1648 1649 // During iteration, there can be heap allocation due to 1650 // GetScriptLineNumber call. 1651 for (int i = 0; i < compiled_funcs_count; ++i) { 1652 if (code_objects[i].is_identical_to(isolate_->builtins()->CompileLazy())) 1653 continue; 1654 LogExistingFunction(sfis[i], code_objects[i]); 1655 } 1656 } 1657 1658 1659 void Logger::LogAccessorCallbacks() { 1660 Heap* heap = isolate_->heap(); 1661 HeapIterator iterator(heap); 1662 DisallowHeapAllocation no_gc; 1663 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { 1664 if (!obj->IsAccessorInfo()) continue; 1665 AccessorInfo* ai = AccessorInfo::cast(obj); 1666 if (!ai->name()->IsName()) continue; 1667 Address getter_entry = v8::ToCData<Address>(ai->getter()); 1668 Name* name = Name::cast(ai->name()); 1669 if (getter_entry != 0) { 1670 #if USES_FUNCTION_DESCRIPTORS 1671 getter_entry = *FUNCTION_ENTRYPOINT_ADDRESS(getter_entry); 1672 #endif 1673 PROFILE(isolate_, GetterCallbackEvent(name, getter_entry)); 1674 } 1675 Address setter_entry = v8::ToCData<Address>(ai->setter()); 1676 if (setter_entry != 0) { 1677 #if USES_FUNCTION_DESCRIPTORS 1678 setter_entry = *FUNCTION_ENTRYPOINT_ADDRESS(setter_entry); 1679 #endif 1680 PROFILE(isolate_, SetterCallbackEvent(name, setter_entry)); 1681 } 1682 } 1683 } 1684 1685 1686 static void AddIsolateIdIfNeeded(std::ostream& os, // NOLINT 1687 Isolate* isolate) { 1688 if (FLAG_logfile_per_isolate) os << "isolate-" << isolate << "-"; 1689 } 1690 1691 1692 static void PrepareLogFileName(std::ostream& os, // NOLINT 1693 Isolate* isolate, const char* file_name) { 1694 int dir_separator_count = 0; 1695 for (const char* p = file_name; *p; p++) { 1696 if (base::OS::isDirectorySeparator(*p)) dir_separator_count++; 1697 } 1698 1699 for (const char* p = file_name; *p; p++) { 1700 if (dir_separator_count == 0) { 1701 AddIsolateIdIfNeeded(os, isolate); 1702 dir_separator_count--; 1703 } 1704 if (*p == '%') { 1705 p++; 1706 switch (*p) { 1707 case '\0': 1708 // If there's a % at the end of the string we back up 1709 // one character so we can escape the loop properly. 1710 p--; 1711 break; 1712 case 'p': 1713 os << base::OS::GetCurrentProcessId(); 1714 break; 1715 case 't': 1716 // %t expands to the current time in milliseconds. 1717 os << static_cast<int64_t>(base::OS::TimeCurrentMillis()); 1718 break; 1719 case '%': 1720 // %% expands (contracts really) to %. 1721 os << '%'; 1722 break; 1723 default: 1724 // All other %'s expand to themselves. 1725 os << '%' << *p; 1726 break; 1727 } 1728 } else { 1729 if (base::OS::isDirectorySeparator(*p)) dir_separator_count--; 1730 os << *p; 1731 } 1732 } 1733 } 1734 1735 1736 bool Logger::SetUp(Isolate* isolate) { 1737 // Tests and EnsureInitialize() can call this twice in a row. It's harmless. 1738 if (is_initialized_) return true; 1739 is_initialized_ = true; 1740 1741 std::ostringstream log_file_name; 1742 PrepareLogFileName(log_file_name, isolate, FLAG_logfile); 1743 log_->Initialize(log_file_name.str().c_str()); 1744 1745 if (FLAG_perf_basic_prof) { 1746 perf_basic_logger_ = new PerfBasicLogger(); 1747 addCodeEventListener(perf_basic_logger_); 1748 } 1749 1750 if (FLAG_perf_prof) { 1751 perf_jit_logger_ = new PerfJitLogger(); 1752 addCodeEventListener(perf_jit_logger_); 1753 } 1754 1755 if (FLAG_ll_prof) { 1756 ll_logger_ = new LowLevelLogger(log_file_name.str().c_str()); 1757 addCodeEventListener(ll_logger_); 1758 } 1759 1760 ticker_ = new Ticker(isolate, kSamplingIntervalMs); 1761 1762 if (Log::InitLogAtStart()) { 1763 is_logging_ = true; 1764 } 1765 1766 if (FLAG_log_internal_timer_events || FLAG_prof_cpp) timer_.Start(); 1767 1768 if (FLAG_prof_cpp) { 1769 profiler_ = new Profiler(isolate); 1770 is_logging_ = true; 1771 profiler_->Engage(); 1772 } 1773 1774 profiler_listener_.reset(); 1775 1776 if (is_logging_) { 1777 addCodeEventListener(this); 1778 } 1779 1780 return true; 1781 } 1782 1783 1784 void Logger::SetCodeEventHandler(uint32_t options, 1785 JitCodeEventHandler event_handler) { 1786 if (jit_logger_) { 1787 removeCodeEventListener(jit_logger_); 1788 delete jit_logger_; 1789 jit_logger_ = NULL; 1790 } 1791 1792 if (event_handler) { 1793 jit_logger_ = new JitLogger(event_handler); 1794 addCodeEventListener(jit_logger_); 1795 if (options & kJitCodeEventEnumExisting) { 1796 HandleScope scope(isolate_); 1797 LogCodeObjects(); 1798 LogCompiledFunctions(); 1799 } 1800 } 1801 } 1802 1803 void Logger::SetUpProfilerListener() { 1804 if (!is_initialized_) return; 1805 if (profiler_listener_.get() == nullptr) { 1806 profiler_listener_.reset(new ProfilerListener(isolate_)); 1807 } 1808 addCodeEventListener(profiler_listener_.get()); 1809 } 1810 1811 void Logger::TearDownProfilerListener() { 1812 if (profiler_listener_->HasObservers()) return; 1813 removeCodeEventListener(profiler_listener_.get()); 1814 } 1815 1816 sampler::Sampler* Logger::sampler() { 1817 return ticker_; 1818 } 1819 1820 1821 FILE* Logger::TearDown() { 1822 if (!is_initialized_) return NULL; 1823 is_initialized_ = false; 1824 1825 // Stop the profiler before closing the file. 1826 if (profiler_ != NULL) { 1827 profiler_->Disengage(); 1828 delete profiler_; 1829 profiler_ = NULL; 1830 } 1831 1832 delete ticker_; 1833 ticker_ = NULL; 1834 1835 if (perf_basic_logger_) { 1836 removeCodeEventListener(perf_basic_logger_); 1837 delete perf_basic_logger_; 1838 perf_basic_logger_ = NULL; 1839 } 1840 1841 if (perf_jit_logger_) { 1842 removeCodeEventListener(perf_jit_logger_); 1843 delete perf_jit_logger_; 1844 perf_jit_logger_ = NULL; 1845 } 1846 1847 if (ll_logger_) { 1848 removeCodeEventListener(ll_logger_); 1849 delete ll_logger_; 1850 ll_logger_ = NULL; 1851 } 1852 1853 if (jit_logger_) { 1854 removeCodeEventListener(jit_logger_); 1855 delete jit_logger_; 1856 jit_logger_ = NULL; 1857 } 1858 1859 if (profiler_listener_.get() != nullptr) { 1860 removeCodeEventListener(profiler_listener_.get()); 1861 } 1862 1863 return log_->Close(); 1864 } 1865 1866 } // namespace internal 1867 } // namespace v8 1868