summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/v8/src/cpu-profiler.cc
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-01-05 10:12:14 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2012-01-05 12:18:02 +0100
commitba0f3a56487cf43207ab0ef1c898fa093082287b (patch)
treee93181a50477d83c22983ea2c318150d78d06982 /src/3rdparty/v8/src/cpu-profiler.cc
parentbbfea5b3a10cad429714267d507cc90f3da6db1b (diff)
Imported v8 version 3.7.3 from https://github.com/v8/v8.git
Diffstat (limited to 'src/3rdparty/v8/src/cpu-profiler.cc')
-rw-r--r--src/3rdparty/v8/src/cpu-profiler.cc581
1 files changed, 581 insertions, 0 deletions
diff --git a/src/3rdparty/v8/src/cpu-profiler.cc b/src/3rdparty/v8/src/cpu-profiler.cc
new file mode 100644
index 0000000..d74c034
--- /dev/null
+++ b/src/3rdparty/v8/src/cpu-profiler.cc
@@ -0,0 +1,581 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// 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"
+
+#include "frames-inl.h"
+#include "hashmap.h"
+#include "log-inl.h"
+#include "vm-state-inl.h"
+
+#include "../include/v8-profiler.h"
+
+namespace v8 {
+namespace internal {
+
+static const int kEventsBufferSize = 256*KB;
+static const int kTickSamplesBufferChunkSize = 64*KB;
+static const int kTickSamplesBufferChunksCount = 16;
+
+
+ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator)
+ : Thread("v8:ProfEvntProc"),
+ generator_(generator),
+ running_(true),
+ ticks_buffer_(sizeof(TickSampleEventRecord),
+ kTickSamplesBufferChunkSize,
+ kTickSamplesBufferChunksCount),
+ enqueue_order_(0) {
+}
+
+
+void ProfilerEventsProcessor::CallbackCreateEvent(Logger::LogEventsAndTags tag,
+ const char* prefix,
+ String* name,
+ Address start) {
+ if (FilterOutCodeCreateEvent(tag)) return;
+ CodeEventsContainer evt_rec;
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->type = CodeEventRecord::CODE_CREATION;
+ rec->order = ++enqueue_order_;
+ rec->start = start;
+ rec->entry = generator_->NewCodeEntry(tag, prefix, name);
+ rec->size = 1;
+ rec->shared = NULL;
+ events_buffer_.Enqueue(evt_rec);
+}
+
+
+void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ String* name,
+ String* resource_name,
+ int line_number,
+ Address start,
+ unsigned size,
+ Address shared) {
+ if (FilterOutCodeCreateEvent(tag)) return;
+ CodeEventsContainer evt_rec;
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->type = CodeEventRecord::CODE_CREATION;
+ rec->order = ++enqueue_order_;
+ rec->start = start;
+ rec->entry = generator_->NewCodeEntry(tag, name, resource_name, line_number);
+ rec->size = size;
+ rec->shared = shared;
+ events_buffer_.Enqueue(evt_rec);
+}
+
+
+void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ const char* name,
+ Address start,
+ unsigned size) {
+ if (FilterOutCodeCreateEvent(tag)) return;
+ CodeEventsContainer evt_rec;
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->type = CodeEventRecord::CODE_CREATION;
+ rec->order = ++enqueue_order_;
+ rec->start = start;
+ rec->entry = generator_->NewCodeEntry(tag, name);
+ rec->size = size;
+ rec->shared = NULL;
+ events_buffer_.Enqueue(evt_rec);
+}
+
+
+void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ int args_count,
+ Address start,
+ unsigned size) {
+ if (FilterOutCodeCreateEvent(tag)) return;
+ CodeEventsContainer evt_rec;
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->type = CodeEventRecord::CODE_CREATION;
+ rec->order = ++enqueue_order_;
+ rec->start = start;
+ rec->entry = generator_->NewCodeEntry(tag, args_count);
+ rec->size = size;
+ rec->shared = NULL;
+ events_buffer_.Enqueue(evt_rec);
+}
+
+
+void ProfilerEventsProcessor::CodeMoveEvent(Address from, Address to) {
+ CodeEventsContainer evt_rec;
+ CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
+ rec->type = CodeEventRecord::CODE_MOVE;
+ rec->order = ++enqueue_order_;
+ rec->from = from;
+ rec->to = to;
+ events_buffer_.Enqueue(evt_rec);
+}
+
+
+void ProfilerEventsProcessor::SharedFunctionInfoMoveEvent(Address from,
+ Address to) {
+ CodeEventsContainer evt_rec;
+ SharedFunctionInfoMoveEventRecord* rec =
+ &evt_rec.SharedFunctionInfoMoveEventRecord_;
+ rec->type = CodeEventRecord::SHARED_FUNC_MOVE;
+ rec->order = ++enqueue_order_;
+ rec->from = from;
+ rec->to = to;
+ events_buffer_.Enqueue(evt_rec);
+}
+
+
+void ProfilerEventsProcessor::RegExpCodeCreateEvent(
+ Logger::LogEventsAndTags tag,
+ const char* prefix,
+ String* name,
+ Address start,
+ unsigned size) {
+ if (FilterOutCodeCreateEvent(tag)) return;
+ CodeEventsContainer evt_rec;
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->type = CodeEventRecord::CODE_CREATION;
+ rec->order = ++enqueue_order_;
+ rec->start = start;
+ rec->entry = generator_->NewCodeEntry(tag, prefix, name);
+ rec->size = size;
+ events_buffer_.Enqueue(evt_rec);
+}
+
+
+void ProfilerEventsProcessor::AddCurrentStack() {
+ TickSampleEventRecord record(enqueue_order_);
+ TickSample* sample = &record.sample;
+ Isolate* isolate = Isolate::Current();
+ sample->state = isolate->current_vm_state();
+ sample->pc = reinterpret_cast<Address>(sample); // Not NULL.
+ for (StackTraceFrameIterator it(isolate);
+ !it.done() && sample->frames_count < TickSample::kMaxFramesCount;
+ it.Advance()) {
+ sample->stack[sample->frames_count++] = it.frame()->pc();
+ }
+ ticks_from_vm_buffer_.Enqueue(record);
+}
+
+
+bool ProfilerEventsProcessor::ProcessCodeEvent(unsigned* dequeue_order) {
+ if (!events_buffer_.IsEmpty()) {
+ CodeEventsContainer record;
+ 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.
+ }
+ *dequeue_order = record.generic.order;
+ return true;
+ }
+ return false;
+}
+
+
+bool ProfilerEventsProcessor::ProcessTicks(unsigned dequeue_order) {
+ while (true) {
+ if (!ticks_from_vm_buffer_.IsEmpty()
+ && ticks_from_vm_buffer_.Peek()->order == dequeue_order) {
+ TickSampleEventRecord record;
+ ticks_from_vm_buffer_.Dequeue(&record);
+ generator_->RecordTickSample(record.sample);
+ }
+
+ const TickSampleEventRecord* rec =
+ TickSampleEventRecord::cast(ticks_buffer_.StartDequeue());
+ if (rec == NULL) return !ticks_from_vm_buffer_.IsEmpty();
+ // Make a local copy of tick sample record to ensure that it won't
+ // be modified as we are processing it. This is possible as the
+ // sampler writes w/o any sync to the queue, so if the processor
+ // will get far behind, a record may be modified right under its
+ // feet.
+ TickSampleEventRecord record = *rec;
+ if (record.order == dequeue_order) {
+ // A paranoid check to make sure that we don't get a memory overrun
+ // in case of frames_count having a wild value.
+ if (record.sample.frames_count < 0
+ || record.sample.frames_count > TickSample::kMaxFramesCount)
+ record.sample.frames_count = 0;
+ generator_->RecordTickSample(record.sample);
+ ticks_buffer_.FinishDequeue();
+ } else {
+ return true;
+ }
+ }
+}
+
+
+void ProfilerEventsProcessor::Run() {
+ unsigned dequeue_order = 0;
+
+ while (running_) {
+ // Process ticks until we have any.
+ if (ProcessTicks(dequeue_order)) {
+ // All ticks of the current dequeue_order are processed,
+ // proceed to the next code event.
+ ProcessCodeEvent(&dequeue_order);
+ }
+ YieldCPU();
+ }
+
+ // Process remaining tick events.
+ ticks_buffer_.FlushResidualRecords();
+ // Perform processing until we have tick events, skip remaining code events.
+ while (ProcessTicks(dequeue_order) && ProcessCodeEvent(&dequeue_order)) { }
+}
+
+
+void CpuProfiler::StartProfiling(const char* title) {
+ ASSERT(Isolate::Current()->cpu_profiler() != NULL);
+ Isolate::Current()->cpu_profiler()->StartCollectingProfile(title);
+}
+
+
+void CpuProfiler::StartProfiling(String* title) {
+ ASSERT(Isolate::Current()->cpu_profiler() != NULL);
+ Isolate::Current()->cpu_profiler()->StartCollectingProfile(title);
+}
+
+
+CpuProfile* CpuProfiler::StopProfiling(const char* title) {
+ Isolate* isolate = Isolate::Current();
+ return is_profiling(isolate) ?
+ isolate->cpu_profiler()->StopCollectingProfile(title) : NULL;
+}
+
+
+CpuProfile* CpuProfiler::StopProfiling(Object* security_token, String* title) {
+ Isolate* isolate = Isolate::Current();
+ return is_profiling(isolate) ?
+ isolate->cpu_profiler()->StopCollectingProfile(
+ security_token, title) : NULL;
+}
+
+
+int CpuProfiler::GetProfilesCount() {
+ ASSERT(Isolate::Current()->cpu_profiler() != NULL);
+ // The count of profiles doesn't depend on a security token.
+ return Isolate::Current()->cpu_profiler()->profiles_->Profiles(
+ TokenEnumerator::kNoSecurityToken)->length();
+}
+
+
+CpuProfile* CpuProfiler::GetProfile(Object* security_token, int index) {
+ ASSERT(Isolate::Current()->cpu_profiler() != NULL);
+ CpuProfiler* profiler = Isolate::Current()->cpu_profiler();
+ const int token = profiler->token_enumerator_->GetTokenId(security_token);
+ return profiler->profiles_->Profiles(token)->at(index);
+}
+
+
+CpuProfile* CpuProfiler::FindProfile(Object* security_token, unsigned uid) {
+ ASSERT(Isolate::Current()->cpu_profiler() != NULL);
+ CpuProfiler* profiler = Isolate::Current()->cpu_profiler();
+ const int token = profiler->token_enumerator_->GetTokenId(security_token);
+ return profiler->profiles_->GetProfile(token, uid);
+}
+
+
+TickSample* CpuProfiler::TickSampleEvent(Isolate* isolate) {
+ if (CpuProfiler::is_profiling(isolate)) {
+ return isolate->cpu_profiler()->processor_->TickSampleEvent();
+ } else {
+ return NULL;
+ }
+}
+
+
+void CpuProfiler::DeleteAllProfiles() {
+ Isolate* isolate = Isolate::Current();
+ ASSERT(isolate->cpu_profiler() != NULL);
+ if (is_profiling(isolate)) {
+ isolate->cpu_profiler()->StopProcessor();
+ }
+ isolate->cpu_profiler()->ResetProfiles();
+}
+
+
+void CpuProfiler::DeleteProfile(CpuProfile* profile) {
+ ASSERT(Isolate::Current()->cpu_profiler() != NULL);
+ Isolate::Current()->cpu_profiler()->profiles_->RemoveProfile(profile);
+ delete profile;
+}
+
+
+bool CpuProfiler::HasDetachedProfiles() {
+ ASSERT(Isolate::Current()->cpu_profiler() != NULL);
+ return Isolate::Current()->cpu_profiler()->profiles_->HasDetachedProfiles();
+}
+
+
+void CpuProfiler::CallbackEvent(String* name, Address entry_point) {
+ Isolate::Current()->cpu_profiler()->processor_->CallbackCreateEvent(
+ Logger::CALLBACK_TAG, CodeEntry::kEmptyNamePrefix, name, entry_point);
+}
+
+
+void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code, const char* comment) {
+ Isolate::Current()->cpu_profiler()->processor_->CodeCreateEvent(
+ tag, comment, code->address(), code->ExecutableSize());
+}
+
+
+void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code, String* name) {
+ Isolate* isolate = Isolate::Current();
+ isolate->cpu_profiler()->processor_->CodeCreateEvent(
+ tag,
+ name,
+ isolate->heap()->empty_string(),
+ v8::CpuProfileNode::kNoLineNumberInfo,
+ code->address(),
+ code->ExecutableSize(),
+ NULL);
+}
+
+
+void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ String* name) {
+ Isolate* isolate = Isolate::Current();
+ isolate->cpu_profiler()->processor_->CodeCreateEvent(
+ tag,
+ name,
+ isolate->heap()->empty_string(),
+ v8::CpuProfileNode::kNoLineNumberInfo,
+ code->address(),
+ code->ExecutableSize(),
+ shared->address());
+}
+
+
+void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ String* source, int line) {
+ Isolate::Current()->cpu_profiler()->processor_->CodeCreateEvent(
+ tag,
+ shared->DebugName(),
+ source,
+ line,
+ code->address(),
+ code->ExecutableSize(),
+ shared->address());
+}
+
+
+void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code, int args_count) {
+ Isolate::Current()->cpu_profiler()->processor_->CodeCreateEvent(
+ tag,
+ args_count,
+ code->address(),
+ code->ExecutableSize());
+}
+
+
+void CpuProfiler::CodeMoveEvent(Address from, Address to) {
+ Isolate::Current()->cpu_profiler()->processor_->CodeMoveEvent(from, to);
+}
+
+
+void CpuProfiler::CodeDeleteEvent(Address from) {
+}
+
+
+void CpuProfiler::SharedFunctionInfoMoveEvent(Address from, Address to) {
+ CpuProfiler* profiler = Isolate::Current()->cpu_profiler();
+ profiler->processor_->SharedFunctionInfoMoveEvent(from, to);
+}
+
+
+void CpuProfiler::GetterCallbackEvent(String* name, Address entry_point) {
+ Isolate::Current()->cpu_profiler()->processor_->CallbackCreateEvent(
+ Logger::CALLBACK_TAG, "get ", name, entry_point);
+}
+
+
+void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) {
+ Isolate::Current()->cpu_profiler()->processor_->RegExpCodeCreateEvent(
+ Logger::REG_EXP_TAG,
+ "RegExp: ",
+ source,
+ code->address(),
+ code->ExecutableSize());
+}
+
+
+void CpuProfiler::SetterCallbackEvent(String* name, Address entry_point) {
+ Isolate::Current()->cpu_profiler()->processor_->CallbackCreateEvent(
+ Logger::CALLBACK_TAG, "set ", name, entry_point);
+}
+
+
+CpuProfiler::CpuProfiler()
+ : profiles_(new CpuProfilesCollection()),
+ next_profile_uid_(1),
+ token_enumerator_(new TokenEnumerator()),
+ generator_(NULL),
+ processor_(NULL),
+ need_to_stop_sampler_(false),
+ is_profiling_(false) {
+}
+
+
+CpuProfiler::~CpuProfiler() {
+ delete token_enumerator_;
+ delete profiles_;
+}
+
+
+void CpuProfiler::ResetProfiles() {
+ delete profiles_;
+ profiles_ = new CpuProfilesCollection();
+}
+
+void CpuProfiler::StartCollectingProfile(const char* title) {
+ if (profiles_->StartProfiling(title, next_profile_uid_++)) {
+ StartProcessorIfNotStarted();
+ }
+ processor_->AddCurrentStack();
+}
+
+
+void CpuProfiler::StartCollectingProfile(String* title) {
+ StartCollectingProfile(profiles_->GetName(title));
+}
+
+
+void CpuProfiler::StartProcessorIfNotStarted() {
+ if (processor_ == NULL) {
+ Isolate* isolate = Isolate::Current();
+
+ // Disable logging when using the new implementation.
+ saved_logging_nesting_ = isolate->logger()->logging_nesting_;
+ isolate->logger()->logging_nesting_ = 0;
+ generator_ = new ProfileGenerator(profiles_);
+ processor_ = new ProfilerEventsProcessor(generator_);
+ NoBarrier_Store(&is_profiling_, true);
+ processor_->Start();
+ // Enumerate stuff we already have in the heap.
+ if (isolate->heap()->HasBeenSetup()) {
+ if (!FLAG_prof_browser_mode) {
+ bool saved_log_code_flag = FLAG_log_code;
+ FLAG_log_code = true;
+ isolate->logger()->LogCodeObjects();
+ FLAG_log_code = saved_log_code_flag;
+ }
+ isolate->logger()->LogCompiledFunctions();
+ isolate->logger()->LogAccessorCallbacks();
+ }
+ // Enable stack sampling.
+ Sampler* sampler = reinterpret_cast<Sampler*>(isolate->logger()->ticker_);
+ if (!sampler->IsActive()) {
+ sampler->Start();
+ need_to_stop_sampler_ = true;
+ }
+ sampler->IncreaseProfilingDepth();
+ }
+}
+
+
+CpuProfile* CpuProfiler::StopCollectingProfile(const char* title) {
+ const double actual_sampling_rate = generator_->actual_sampling_rate();
+ StopProcessorIfLastProfile(title);
+ CpuProfile* result =
+ profiles_->StopProfiling(TokenEnumerator::kNoSecurityToken,
+ title,
+ actual_sampling_rate);
+ if (result != NULL) {
+ result->Print();
+ }
+ return result;
+}
+
+
+CpuProfile* CpuProfiler::StopCollectingProfile(Object* security_token,
+ String* title) {
+ const double actual_sampling_rate = generator_->actual_sampling_rate();
+ const char* profile_title = profiles_->GetName(title);
+ StopProcessorIfLastProfile(profile_title);
+ int token = token_enumerator_->GetTokenId(security_token);
+ return profiles_->StopProfiling(token, profile_title, actual_sampling_rate);
+}
+
+
+void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
+ if (profiles_->IsLastProfile(title)) StopProcessor();
+}
+
+
+void CpuProfiler::StopProcessor() {
+ Logger* logger = Isolate::Current()->logger();
+ Sampler* sampler = reinterpret_cast<Sampler*>(logger->ticker_);
+ sampler->DecreaseProfilingDepth();
+ if (need_to_stop_sampler_) {
+ sampler->Stop();
+ need_to_stop_sampler_ = false;
+ }
+ NoBarrier_Store(&is_profiling_, false);
+ processor_->Stop();
+ processor_->Join();
+ delete processor_;
+ delete generator_;
+ processor_ = NULL;
+ generator_ = NULL;
+ logger->logging_nesting_ = saved_logging_nesting_;
+}
+
+
+void CpuProfiler::Setup() {
+ Isolate* isolate = Isolate::Current();
+ if (isolate->cpu_profiler() == NULL) {
+ isolate->set_cpu_profiler(new CpuProfiler());
+ }
+}
+
+
+void CpuProfiler::TearDown() {
+ Isolate* isolate = Isolate::Current();
+ if (isolate->cpu_profiler() != NULL) {
+ delete isolate->cpu_profiler();
+ }
+ isolate->set_cpu_profiler(NULL);
+}
+
+} } // namespace v8::internal