text: // Copyright 2012 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "src/profiler/cpu-profiler.h"
  
  #include <unordered_map>
  #include <utility>
  
  #include "src/base/lazy-instance.h"
  #include "src/base/template-utils.h"
  #include "src/debug/debug.h"
  #include "src/frames-inl.h"
  #include "src/locked-queue-inl.h"
  #include "src/log.h"
  #include "src/profiler/cpu-profiler-inl.h"
  #include "src/vm-state-inl.h"
  #include "src/wasm/wasm-engine.h"
  
  namespace v8 {
  namespace internal {
  
  static const int kProfilerStackSize = 64 * KB;
  
  class CpuSampler : public sampler::Sampler {
   public:
    CpuSampler(Isolate* isolate, SamplingEventsProcessor* processor)
        : sampler::Sampler(reinterpret_cast<v8::Isolate*>(isolate)),
          processor_(processor) {}
  
    void SampleStack(const v8::RegisterState& regs) override {
      TickSample* sample = processor_->StartTickSample();
      if (sample == nullptr) return;
      Isolate* isolate = reinterpret_cast<Isolate*>(this->isolate());
      sample->Init(isolate, regs, TickSample::kIncludeCEntryFrame, true);
      if (is_counting_samples_ && !sample->timestamp.IsNull()) {
        if (sample->state == JS) ++js_sample_count_;
        if (sample->state == EXTERNAL) ++external_sample_count_;
      }
      processor_->FinishTickSample();
    }
  
   private:
    SamplingEventsProcessor* processor_;
  };
  
  ProfilerEventsProcessor::ProfilerEventsProcessor(Isolate* isolate,
                                                   ProfileGenerator* generator)
      : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
        generator_(generator),
        running_(1),
        last_code_event_id_(0),
        last_processed_code_event_id_(0),
        isolate_(isolate) {}
  
  SamplingEventsProcessor::SamplingEventsProcessor(Isolate* isolate,
                                                   ProfileGenerator* generator,
                                                   base::TimeDelta period)
      : ProfilerEventsProcessor(isolate, generator),
        sampler_(new CpuSampler(isolate, this)),
        period_(period) {
    sampler_->Start();
  }
  
  SamplingEventsProcessor::~SamplingEventsProcessor() { sampler_->Stop(); }
  
  ProfilerEventsProcessor::~ProfilerEventsProcessor() = default;
  
  void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
    event.generic.order = ++last_code_event_id_;
    events_buffer_.Enqueue(event);
  }
  
  void ProfilerEventsProcessor::AddDeoptStack(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 = reinterpret_cast<void*>(fp - fp_to_sp_delta);
    regs.fp = reinterpret_cast<void*>(fp);
    regs.pc = reinterpret_cast<void*>(from);
    record.sample.Init(isolate_, regs, TickSample::kSkipCEntryFrame, false,
                       false);
    ticks_from_vm_buffer_.Enqueue(record);
  }
  
  void ProfilerEventsProcessor::AddCurrentStack(bool update_stats) {
    TickSampleEventRecord record(last_code_event_id_);
    RegisterState regs;
    StackFrameIterator it(isolate_);
    if (!it.done()) {
      StackFrame* frame = it.frame();
      regs.sp = reinterpret_cast<void*>(frame->sp());
      regs.fp = reinterpret_cast<void*>(frame->fp());
      regs.pc = reinterpret_cast<void*>(frame->pc());
    }
    record.sample.Init(isolate_, regs, TickSample::kSkipCEntryFrame, update_stats,
                       false);
    ticks_from_vm_buffer_.Enqueue(record);
  }
  
  void ProfilerEventsProcessor::AddSample(TickSample sample) {
    TickSampleEventRecord record(last_code_event_id_);
    record.sample = sample;
    ticks_from_vm_buffer_.Enqueue(record);
  }
  
  void ProfilerEventsProcessor::StopSynchronously() {
    if (!base::Relaxed_AtomicExchange(&running_, 0)) return;
    {
      base::MutexGuard guard(&running_mutex_);
      running_cond_.NotifyOne();
    }
    Join();
  }
  
  
  bool ProfilerEventsProcessor::ProcessCodeEvent() {
    CodeEventsContainer record;
    if (events_buffer_.Dequeue(&record)) {
      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.
      }
      last_processed_code_event_id_ = record.generic.order;
      return true;
    }
    return false;
  }
  
  void ProfilerEventsProcessor::CodeEventHandler(
      const CodeEventsContainer& evt_rec) {
    switch (evt_rec.generic.type) {
      case CodeEventRecord::CODE_CREATION:
      case CodeEventRecord::CODE_MOVE:
      case CodeEventRecord::CODE_DISABLE_OPT:
        Enqueue(evt_rec);
        break;
      case CodeEventRecord::CODE_DEOPT: {
        const CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
        Address pc = rec->pc;
        int fp_to_sp_delta = rec->fp_to_sp_delta;
        Enqueue(evt_rec);
        AddDeoptStack(pc, fp_to_sp_delta);
        break;
      }
      case CodeEventRecord::NONE:
      case CodeEventRecord::REPORT_BUILTIN:
        UNREACHABLE();
    }
  }
  
  ProfilerEventsProcessor::SampleProcessingResult
  SamplingEventsProcessor::ProcessOneSample() {
    TickSampleEventRecord record1;
    if (ticks_from_vm_buffer_.Peek(&record1) &&
        (record1.order == last_processed_code_event_id_)) {
      TickSampleEventRecord record;
      ticks_from_vm_buffer_.Dequeue(&record);
      generator_->RecordTickSample(record.sample);
      return OneSampleProcessed;
    }
  
    const TickSampleEventRecord* record = ticks_buffer_.Peek();
    if (record == nullptr) {
      if (ticks_from_vm_buffer_.IsEmpty()) return NoSamplesInQueue;
      return FoundSampleForNextCodeEvent;
    }
    if (record->order != last_processed_code_event_id_) {
      return FoundSampleForNextCodeEvent;
    }
    generator_->RecordTickSample(record->sample);
    ticks_buffer_.Remove();
    return OneSampleProcessed;
  }
  
  void SamplingEventsProcessor::Run() {
    base::MutexGuard guard(&running_mutex_);
    while (!!base::Relaxed_Load(&running_)) {
      base::TimeTicks nextSampleTime =
          base::TimeTicks::HighResolutionNow() + period_;
      base::TimeTicks now;
      SampleProcessingResult result;
      // Keep processing existing events until we need to do next sample
      // or the ticks buffer is empty.
      do {
        result = ProcessOneSample();
        if (result == FoundSampleForNextCodeEvent) {
          // All ticks of the current last_processed_code_event_id_ are
          // processed, proceed to the next code event.
          ProcessCodeEvent();
        }
        now = base::TimeTicks::HighResolutionNow();
      } while (result != NoSamplesInQueue && now < nextSampleTime);
  
      if (nextSampleTime > now) {
  #if V8_OS_WIN
        if (nextSampleTime - now < base::TimeDelta::FromMilliseconds(100)) {
          // Do not use Sleep on Windows as it is very imprecise, with up to 16ms
          // jitter, which is unacceptable for short profile intervals.
          while (base::TimeTicks::HighResolutionNow() < nextSampleTime) {
          }
        } else  // NOLINT
  #endif
        {
          // Allow another thread to interrupt the delay between samples in the
          // event of profiler shutdown.
          while (now < nextSampleTime &&
                 running_cond_.WaitFor(&running_mutex_, nextSampleTime - now)) {
            // If true was returned, we got interrupted before the timeout
            // elapsed. If this was not due to a change in running state, a
            // spurious wakeup occurred (thus we should continue to wait).
            if (!base::Relaxed_Load(&running_)) {
              break;
            }
            now = base::TimeTicks::HighResolutionNow();
          }
        }
      }
  
      // Schedule next sample.
      sampler_->DoSample();
    }
  
    // Process remaining tick events.
    do {
      SampleProcessingResult result;
      do {
        result = ProcessOneSample();
      } while (result == OneSampleProcessed);
    } while (ProcessCodeEvent());
  }
  
  void* SamplingEventsProcessor::operator new(size_t size) {
    return AlignedAlloc(size, alignof(SamplingEventsProcessor));
  }
  
  void SamplingEventsProcessor::operator delete(void* ptr) { AlignedFree(ptr); }
  
  int CpuProfiler::GetProfilesCount() {
    // The count of profiles doesn't depend on a security token.
    return static_cast<int>(profiles_->profiles()->size());
  }
  
  
  CpuProfile* CpuProfiler::GetProfile(int index) {
    return profiles_->profiles()->at(index).get();
  }
  
  
  void CpuProfiler::DeleteAllProfiles() {
    if (is_profiling_) StopProcessor();
    ResetProfiles();
  }
  
  
  void CpuProfiler::DeleteProfile(CpuProfile* profile) {
    profiles_->RemoveProfile(profile);
    if (profiles_->profiles()->empty() && !is_profiling_) {
      // If this was the last profile, clean up all accessory data as well.
      ResetProfiles();
    }
  }
  
  namespace {
  
  class CpuProfilersManager {
   public:
    void AddProfiler(Isolate* isolate, CpuProfiler* profiler) {
      base::MutexGuard lock(&mutex_);
      profilers_.emplace(isolate, profiler);
    }
  
    void RemoveProfiler(Isolate* isolate, CpuProfiler* profiler) {
      base::MutexGuard lock(&mutex_);
      auto range = profilers_.equal_range(isolate);
      for (auto it = range.first; it != range.second; ++it) {
        if (it->second != profiler) continue;
        profilers_.erase(it);
        return;
      }
      UNREACHABLE();
    }
  
    void CallCollectSample(Isolate* isolate) {
      base::MutexGuard lock(&mutex_);
      auto range = profilers_.equal_range(isolate);
      for (auto it = range.first; it != range.second; ++it) {
        it->second->CollectSample();
      }
    }
  
   private:
    std::unordered_multimap<Isolate*, CpuProfiler*> profilers_;
    base::Mutex mutex_;
  };
  
  DEFINE_LAZY_LEAKY_OBJECT_GETTER(CpuProfilersManager, GetProfilersManager)
  
  }  // namespace
  
  CpuProfiler::CpuProfiler(Isolate* isolate)
      : CpuProfiler(isolate, new CpuProfilesCollection(isolate), nullptr,
                    nullptr) {}
  
  CpuProfiler::CpuProfiler(Isolate* isolate, CpuProfilesCollection* test_profiles,
                           ProfileGenerator* test_generator,
                           ProfilerEventsProcessor* test_processor)
      : isolate_(isolate),
        sampling_interval_(base::TimeDelta::FromMicroseconds(
            FLAG_cpu_profiler_sampling_interval)),
        profiles_(test_profiles),
        generator_(test_generator),
        processor_(test_processor),
        is_profiling_(false) {
    profiles_->set_cpu_profiler(this);
    GetProfilersManager()->AddProfiler(isolate, this);
  }
  
  CpuProfiler::~CpuProfiler() {
    DCHECK(!is_profiling_);
    GetProfilersManager()->RemoveProfiler(isolate_, this);
  }
  
  void CpuProfiler::set_sampling_interval(base::TimeDelta value) {
    DCHECK(!is_profiling_);
    sampling_interval_ = value;
  }
  
  void CpuProfiler::ResetProfiles() {
    profiles_.reset(new CpuProfilesCollection(isolate_));
    profiles_->set_cpu_profiler(this);
    profiler_listener_.reset();
    generator_.reset();
  }
  
  void CpuProfiler::CreateEntriesForRuntimeCallStats() {
    RuntimeCallStats* rcs = isolate_->counters()->runtime_call_stats();
    CodeMap* code_map = generator_->code_map();
    for (int i = 0; i < RuntimeCallStats::kNumberOfCounters; ++i) {
      RuntimeCallCounter* counter = rcs->GetCounter(i);
      DCHECK(counter->name());
      auto entry = new CodeEntry(CodeEventListener::FUNCTION_TAG, counter->name(),
                                 "native V8Runtime");
      code_map->AddCode(reinterpret_cast<Address>(counter), entry, 1);
    }
  }
  
  // static
  void CpuProfiler::CollectSample(Isolate* isolate) {
    GetProfilersManager()->CallCollectSample(isolate);
  }
  
  void CpuProfiler::CollectSample() {
    if (processor_) {
      processor_->AddCurrentStack();
    }
  }
  
  void CpuProfiler::StartProfiling(const char* title, bool record_samples,
                                   ProfilingMode mode) {
    if (profiles_->StartProfiling(title, record_samples, mode)) {
      TRACE_EVENT0("v8", "CpuProfiler::StartProfiling");
      StartProcessorIfNotStarted();
    }
  }
  
  void CpuProfiler::StartProfiling(String title, bool record_samples,
                                   ProfilingMode mode) {
    StartProfiling(profiles_->GetName(title), record_samples, mode);
    isolate_->debug()->feature_tracker()->Track(DebugFeatureTracker::kProfiler);
  }
  
  void CpuProfiler::StartProcessorIfNotStarted() {
    if (processor_) {
      processor_->AddCurrentStack();
      return;
    }
    isolate_->wasm_engine()->EnableCodeLogging(isolate_);
    Logger* logger = isolate_->logger();
    // Disable logging when using the new implementation.
    saved_is_logging_ = logger->is_logging();
    logger->set_is_logging(false);
  
    bool codemap_needs_initialization = false;
    if (!generator_) {
      generator_.reset(new ProfileGenerator(profiles_.get()));
      codemap_needs_initialization = true;
      CreateEntriesForRuntimeCallStats();
    }
    processor_.reset(new SamplingEventsProcessor(isolate_, generator_.get(),
                                                 sampling_interval_));
    if (profiler_listener_) {
      profiler_listener_->set_observer(processor_.get());
    } else {
      profiler_listener_.reset(new ProfilerListener(isolate_, processor_.get()));
    }
    logger->AddCodeEventListener(profiler_listener_.get());
    is_profiling_ = true;
    isolate_->set_is_profiling(true);
    // Enumerate stuff we already have in the heap.
    DCHECK(isolate_->heap()->HasBeenSetUp());
    if (codemap_needs_initialization) {
      if (!FLAG_prof_browser_mode) {
        logger->LogCodeObjects();
      }
      logger->LogCompiledFunctions();
      logger->LogAccessorCallbacks();
      LogBuiltins();
    }
    // Enable stack sampling.
    processor_->AddCurrentStack();
    processor_->StartSynchronously();
  }
  
  CpuProfile* CpuProfiler::StopProfiling(const char* title) {
    if (!is_profiling_) return nullptr;
    StopProcessorIfLastProfile(title);
    return profiles_->StopProfiling(title);
  }
  
  CpuProfile* CpuProfiler::StopProfiling(String title) {
    return StopProfiling(profiles_->GetName(title));
  }
  
  void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
    if (!profiles_->IsLastProfile(title)) return;
    StopProcessor();
  }
  
  void CpuProfiler::StopProcessor() {
    Logger* logger = isolate_->logger();
    is_profiling_ = false;
    isolate_->set_is_profiling(false);
    logger->RemoveCodeEventListener(profiler_listener_.get());
    processor_->StopSynchronously();
    processor_.reset();
    logger->set_is_logging(saved_is_logging_);
  }
  
  
  void CpuProfiler::LogBuiltins() {
    Builtins* builtins = isolate_->builtins();
    DCHECK(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->instruction_start = builtins->builtin(id)->InstructionStart();
      rec->builtin_id = id;
      processor_->Enqueue(evt_rec);
    }
  }
  
  }  // namespace internal
  }  // namespace v8
  