Revision: 4349
Author: [email protected]
Date: Tue Apr 6 07:54:20 2010
Log: C++ profiler: publish the new API, make compatible with WebKit /
Chromium.
I succeeded at connecting the new implementation to Chromium, this
commit includes required (although, not all) adjustments.
Review URL: http://codereview.chromium.org/1547023
http://code.google.com/p/v8/source/detail?r=4349
Added:
/branches/bleeding_edge/include/v8-profiler.h
Modified:
/branches/bleeding_edge/src/api.cc
/branches/bleeding_edge/src/cpu-profiler.cc
/branches/bleeding_edge/src/cpu-profiler.h
/branches/bleeding_edge/src/flag-definitions.h
/branches/bleeding_edge/src/platform-linux.cc
/branches/bleeding_edge/src/profile-generator-inl.h
/branches/bleeding_edge/src/profile-generator.cc
/branches/bleeding_edge/src/profile-generator.h
=======================================
--- /dev/null
+++ /branches/bleeding_edge/include/v8-profiler.h Tue Apr 6 07:54:20 2010
@@ -0,0 +1,176 @@
+// 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.
+
+#ifndef V8_V8_PROFILER_H_
+#define V8_V8_PROFILER_H_
+
+#include "v8.h"
+
+#ifdef _WIN32
+// Setup for Windows DLL export/import. See v8.h in this directory for
+// information on how to build/use V8 as a DLL.
+#if defined(BUILDING_V8_SHARED) && defined(USING_V8_SHARED)
+#error both BUILDING_V8_SHARED and USING_V8_SHARED are set - please check
the\
+ build configuration to ensure that at most one of these is set
+#endif
+
+#ifdef BUILDING_V8_SHARED
+#define V8EXPORT __declspec(dllexport)
+#elif USING_V8_SHARED
+#define V8EXPORT __declspec(dllimport)
+#else
+#define V8EXPORT
+#endif
+
+#else // _WIN32
+
+// Setup for Linux shared library export. See v8.h in this directory for
+// information on how to build/use V8 as shared library.
+#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(V8_SHARED)
+#define V8EXPORT __attribute__ ((visibility("default")))
+#else // defined(__GNUC__) && (__GNUC__ >= 4)
+#define V8EXPORT
+#endif // defined(__GNUC__) && (__GNUC__ >= 4)
+
+#endif // _WIN32
+
+
+/**
+ * Profiler support for the V8 JavaScript engine.
+ */
+namespace v8 {
+
+
+/**
+ * CpuProfileNode represents a node in a call graph.
+ */
+class V8EXPORT CpuProfileNode {
+ public:
+ /** Returns function name (empty string for anonymous functions.) */
+ Handle<String> GetFunctionName() const;
+
+ /** Returns resource name for script from where the function originates.
*/
+ Handle<String> GetScriptResourceName() const;
+
+ /**
+ * Returns the number, 1-based, of the line where the function
originates.
+ * kNoLineNumberInfo if no line number information is available.
+ */
+ int GetLineNumber() const;
+
+ /**
+ * Returns total (self + children) execution time of the function,
+ * in milliseconds, estimated by samples count.
+ */
+ double GetTotalTime() const;
+
+ /**
+ * Returns self execution time of the function, in milliseconds,
+ * estimated by samples count.
+ */
+ double GetSelfTime() const;
+
+ /** Returns the count of samples where function exists. */
+ double GetTotalSamplesCount() const;
+
+ /** Returns the count of samples where function was currently executing.
*/
+ double GetSelfSamplesCount() const;
+
+ /** Returns function entry UID. */
+ unsigned GetCallUid() const;
+
+ /** Returns child nodes count of the node. */
+ int GetChildrenCount() const;
+
+ /** Retrieves a child node by index. */
+ const CpuProfileNode* GetChild(int index) const;
+
+ static const int kNoLineNumberInfo = 0;
+};
+
+
+/**
+ * CpuProfile contains a CPU profile in a form of two call trees:
+ * - top-down (from main() down to functions that do all the work);
+ * - bottom-up call graph (in backward direction).
+ */
+class V8EXPORT CpuProfile {
+ public:
+ /** Returns CPU profile UID (assigned by the profiler.) */
+ unsigned GetUid() const;
+
+ /** Returns CPU profile title. */
+ Handle<String> GetTitle() const;
+
+ /** Returns the root node of the bottom up call tree. */
+ const CpuProfileNode* GetBottomUpRoot() const;
+
+ /** Returns the root node of the top down call tree. */
+ const CpuProfileNode* GetTopDownRoot() const;
+};
+
+
+/**
+ * Interface for controlling CPU profiling.
+ */
+class V8EXPORT CpuProfiler {
+ public:
+ /**
+ * Returns the number of profiles collected (doesn't include
+ * profiles that are being collected at the moment of call.)
+ */
+ static int GetProfilesCount();
+
+ /** Returns a profile by index. */
+ static const CpuProfile* GetProfile(int index);
+
+ /** Returns a profile by uid. */
+ static const CpuProfile* FindProfile(unsigned uid);
+
+ /**
+ * Starts collecting CPU profile. Title may be an empty string. It
+ * is allowed to have several profiles being collected at
+ * once. Attempts to start collecting several profiles with the same
+ * title are silently ignored.
+ */
+ static void StartProfiling(Handle<String> title);
+
+ /**
+ * Stops collecting CPU profile with a given title and returns it.
+ * If the title given is empty, finishes the last profile started.
+ */
+ static const CpuProfile* StopProfiling(Handle<String> title);
+};
+
+
+} // namespace v8
+
+
+#undef V8EXPORT
+
+
+#endif // V8_V8_PROFILER_H_
=======================================
--- /branches/bleeding_edge/src/api.cc Thu Mar 25 07:07:17 2010
+++ /branches/bleeding_edge/src/api.cc Tue Apr 6 07:54:20 2010
@@ -36,6 +36,7 @@
#include "global-handles.h"
#include "messages.h"
#include "platform.h"
+#include "profile-generator-inl.h"
#include "serialize.h"
#include "snapshot.h"
#include "top.h"
@@ -43,6 +44,7 @@
#include "v8threads.h"
#include "version.h"
+#include "../include/v8-profiler.h"
#define LOG_API(expr) LOG(ApiEntryCall(expr))
@@ -3997,6 +3999,131 @@
#endif // ENABLE_DEBUGGER_SUPPORT
+
+#ifdef ENABLE_CPP_PROFILES_PROCESSOR
+
+Handle<String> CpuProfileNode::GetFunctionName() const {
+ IsDeadCheck("v8::CpuProfileNode::GetFunctionName");
+ const i::ProfileNode* node = reinterpret_cast<const
i::ProfileNode*>(this);
+ const i::CodeEntry* entry = node->entry();
+ if (!entry->has_name_prefix()) {
+ return Handle<String>(ToApi<String>(
+ i::Factory::LookupAsciiSymbol(entry->name())));
+ } else {
+ return Handle<String>(ToApi<String>(i::Factory::NewConsString(
+ i::Factory::LookupAsciiSymbol(entry->name_prefix()),
+ i::Factory::LookupAsciiSymbol(entry->name()))));
+ }
+}
+
+
+Handle<String> CpuProfileNode::GetScriptResourceName() const {
+ IsDeadCheck("v8::CpuProfileNode::GetScriptResourceName");
+ const i::ProfileNode* node = reinterpret_cast<const
i::ProfileNode*>(this);
+ return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
+ node->entry()->resource_name())));
+}
+
+
+int CpuProfileNode::GetLineNumber() const {
+ IsDeadCheck("v8::CpuProfileNode::GetLineNumber");
+ return reinterpret_cast<const
i::ProfileNode*>(this)->entry()->line_number();
+}
+
+
+double CpuProfileNode::GetTotalSamplesCount() const {
+ IsDeadCheck("v8::CpuProfileNode::GetTotalSamplesCount");
+ return reinterpret_cast<const i::ProfileNode*>(this)->total_ticks();
+}
+
+
+double CpuProfileNode::GetSelfSamplesCount() const {
+ IsDeadCheck("v8::CpuProfileNode::GetSelfSamplesCount");
+ return reinterpret_cast<const i::ProfileNode*>(this)->self_ticks();
+}
+
+
+unsigned CpuProfileNode::GetCallUid() const {
+ IsDeadCheck("v8::CpuProfileNode::GetCallUid");
+ return reinterpret_cast<const
i::ProfileNode*>(this)->entry()->call_uid();
+}
+
+
+int CpuProfileNode::GetChildrenCount() const {
+ IsDeadCheck("v8::CpuProfileNode::GetChildrenCount");
+ return reinterpret_cast<const
i::ProfileNode*>(this)->children()->length();
+}
+
+
+const CpuProfileNode* CpuProfileNode::GetChild(int index) const {
+ IsDeadCheck("v8::CpuProfileNode::GetChild");
+ const i::ProfileNode* child =
+ reinterpret_cast<const i::ProfileNode*>(this)->children()->at(index);
+ return reinterpret_cast<const CpuProfileNode*>(child);
+}
+
+
+unsigned CpuProfile::GetUid() const {
+ IsDeadCheck("v8::CpuProfile::GetUid");
+ return reinterpret_cast<const i::CpuProfile*>(this)->uid();
+}
+
+
+Handle<String> CpuProfile::GetTitle() const {
+ IsDeadCheck("v8::CpuProfile::GetTitle");
+ const i::CpuProfile* profile = reinterpret_cast<const
i::CpuProfile*>(this);
+ return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
+ profile->title())));
+}
+
+
+const CpuProfileNode* CpuProfile::GetBottomUpRoot() const {
+ IsDeadCheck("v8::CpuProfile::GetBottomUpRoot");
+ const i::CpuProfile* profile = reinterpret_cast<const
i::CpuProfile*>(this);
+ return reinterpret_cast<const
CpuProfileNode*>(profile->bottom_up()->root());
+}
+
+
+const CpuProfileNode* CpuProfile::GetTopDownRoot() const {
+ IsDeadCheck("v8::CpuProfile::GetTopDownRoot");
+ const i::CpuProfile* profile = reinterpret_cast<const
i::CpuProfile*>(this);
+ return reinterpret_cast<const
CpuProfileNode*>(profile->top_down()->root());
+}
+
+
+int CpuProfiler::GetProfilesCount() {
+ IsDeadCheck("v8::CpuProfiler::GetProfilesCount");
+ return i::CpuProfiler::GetProfilesCount();
+}
+
+
+const CpuProfile* CpuProfiler::GetProfile(int index) {
+ IsDeadCheck("v8::CpuProfiler::GetProfile");
+ return reinterpret_cast<const
CpuProfile*>(i::CpuProfiler::GetProfile(index));
+}
+
+
+const CpuProfile* CpuProfiler::FindProfile(unsigned uid) {
+ IsDeadCheck("v8::CpuProfiler::FindProfile");
+ return reinterpret_cast<const
CpuProfile*>(i::CpuProfiler::FindProfile(uid));
+}
+
+
+void CpuProfiler::StartProfiling(Handle<String> title) {
+ IsDeadCheck("v8::CpuProfiler::StartProfiling");
+ i::CpuProfiler::StartProfiling(*Utils::OpenHandle(*title));
+}
+
+
+const CpuProfile* CpuProfiler::StopProfiling(Handle<String> title) {
+ IsDeadCheck("v8::CpuProfiler::StopProfiling");
+ return reinterpret_cast<const CpuProfile*>(
+ i::CpuProfiler::StopProfiling(*Utils::OpenHandle(*title)));
+}
+
+#endif // ENABLE_CPP_PROFILES_PROCESSOR
+
+
namespace internal {
=======================================
--- /branches/bleeding_edge/src/cpu-profiler.cc Tue Apr 6 03:36:38 2010
+++ /branches/bleeding_edge/src/cpu-profiler.cc Tue Apr 6 07:54:20 2010
@@ -33,6 +33,8 @@
#include "log-inl.h"
+#include "../include/v8-profiler.h"
+
namespace v8 {
namespace internal {
@@ -154,6 +156,23 @@
void ProfilerEventsProcessor::FunctionDeleteEvent(Address from) {
CodeDeleteEvent(from);
}
+
+
+void ProfilerEventsProcessor::RegExpCodeCreateEvent(
+ Logger::LogEventsAndTags tag,
+ const char* prefix,
+ String* name,
+ Address start,
+ unsigned size) {
+ 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);
+}
bool ProfilerEventsProcessor::ProcessCodeEvent(unsigned* dequeue_order) {
@@ -287,7 +306,7 @@
tag,
name,
Heap::empty_string(),
- CodeEntry::kNoLineNumberInfo,
+ v8::CpuProfileNode::kNoLineNumberInfo,
code->address(),
code->ExecutableSize());
}
@@ -349,11 +368,10 @@
void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) {
- singleton_->processor_->CodeCreateEvent(
+ singleton_->processor_->RegExpCodeCreateEvent(
Logger::REG_EXP_TAG,
+ "RegExp: ",
source,
- Heap::empty_string(),
- CodeEntry::kNoLineNumberInfo,
code->address(),
code->ExecutableSize());
}
@@ -379,14 +397,14 @@
void CpuProfiler::StartCollectingProfile(const char* title) {
- if (profiles_->StartProfiling(title, ++next_profile_uid_)) {
+ if (profiles_->StartProfiling(title, next_profile_uid_++)) {
StartProcessorIfNotStarted();
}
}
void CpuProfiler::StartCollectingProfile(String* title) {
- if (profiles_->StartProfiling(title, ++next_profile_uid_)) {
+ if (profiles_->StartProfiling(title, next_profile_uid_++)) {
StartProcessorIfNotStarted();
}
}
=======================================
--- /branches/bleeding_edge/src/cpu-profiler.h Tue Apr 6 03:36:38 2010
+++ /branches/bleeding_edge/src/cpu-profiler.h Tue Apr 6 07:54:20 2010
@@ -150,6 +150,9 @@
void FunctionCreateEvent(Address alias, Address start);
void FunctionMoveEvent(Address from, Address to);
void FunctionDeleteEvent(Address from);
+ void RegExpCodeCreateEvent(Logger::LogEventsAndTags tag,
+ const char* prefix, String* name,
+ Address start, unsigned size);
// Tick sample events are filled directly in the buffer of the circular
// queue (because the structure is of fixed width, but usually not all
=======================================
--- /branches/bleeding_edge/src/flag-definitions.h Wed Mar 24 03:32:23 2010
+++ /branches/bleeding_edge/src/flag-definitions.h Tue Apr 6 07:54:20 2010
@@ -391,6 +391,8 @@
DEFINE_bool(prof_lazy, false,
"Used with --prof, only does sampling and logging"
" when profiler is active (implies --noprof_auto).")
+DEFINE_bool(prof_browser_mode, false,
+ "Used with --prof, turns on browser-compatible mode for
profiling.")
DEFINE_bool(log_regexp, false, "Log regular expression execution.")
DEFINE_bool(sliding_state_window, false,
"Update sliding state window counters.")
=======================================
--- /branches/bleeding_edge/src/platform-linux.cc Tue Apr 6 03:36:38 2010
+++ /branches/bleeding_edge/src/platform-linux.cc Tue Apr 6 07:54:20 2010
@@ -771,13 +771,9 @@
// Implement this on MIPS.
UNIMPLEMENTED();
#endif
-#ifdef ENABLE_CPP_PROFILES_PROCESSOR
- active_sampler_->SampleStack(sample);
-#else
if (IsVmThread()) {
active_sampler_->SampleStack(sample);
}
-#endif
}
}
#ifndef ENABLE_CPP_PROFILES_PROCESSOR
=======================================
--- /branches/bleeding_edge/src/profile-generator-inl.h Tue Apr 6 03:36:38
2010
+++ /branches/bleeding_edge/src/profile-generator-inl.h Tue Apr 6 07:54:20
2010
@@ -40,7 +40,8 @@
const char* name,
const char* resource_name,
int line_number)
- : tag_(tag),
+ : call_uid_(next_call_uid_++),
+ tag_(tag),
name_prefix_(name_prefix),
name_(name),
resource_name_(resource_name),
=======================================
--- /branches/bleeding_edge/src/profile-generator.cc Tue Apr 6 03:36:38
2010
+++ /branches/bleeding_edge/src/profile-generator.cc Tue Apr 6 07:54:20
2010
@@ -31,12 +31,14 @@
#include "profile-generator-inl.h"
+#include "../include/v8-profiler.h"
+
namespace v8 {
namespace internal {
const char* CodeEntry::kEmptyNamePrefix = "";
-const int CodeEntry::kNoLineNumberInfo = -1;
+unsigned CodeEntry::next_call_uid_ = 1;
ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
@@ -61,11 +63,14 @@
void ProfileNode::Print(int indent) {
- OS::Print("%5u %5u %*c %s%s\n",
+ OS::Print("%5u %5u %*c %s%s",
total_ticks_, self_ticks_,
indent, ' ',
- entry_ != NULL ? entry_->name_prefix() : "",
- entry_ != NULL ? entry_->name() : "");
+ entry_->name_prefix(),
+ entry_->name());
+ if (entry_->resource_name()[0] != '\0')
+ OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
+ OS::Print("\n");
for (HashMap::Entry* p = children_.Start();
p != NULL;
p = children_.Next(p)) {
@@ -86,6 +91,12 @@
};
} // namespace
+
+
+ProfileTree::ProfileTree()
+ : root_entry_(Logger::FUNCTION_TAG, "", "(root)", "", 0),
+ root_(new ProfileNode(&root_entry_)) {
+}
ProfileTree::~ProfileTree() {
@@ -360,7 +371,7 @@
int line_number) {
CodeEntry* entry = new CodeEntry(tag,
CodeEntry::kEmptyNamePrefix,
- GetName(name),
+ GetFunctionName(name),
GetName(resource_name),
line_number);
code_entries_.Add(entry);
@@ -374,7 +385,7 @@
CodeEntry::kEmptyNamePrefix,
name,
"",
- CodeEntry::kNoLineNumberInfo);
+ v8::CpuProfileNode::kNoLineNumberInfo);
code_entries_.Add(entry);
return entry;
}
@@ -387,7 +398,7 @@
name_prefix,
GetName(name),
"",
- CodeEntry::kNoLineNumberInfo);
+ v8::CpuProfileNode::kNoLineNumberInfo);
code_entries_.Add(entry);
return entry;
}
@@ -399,10 +410,17 @@
"args_count: ",
GetName(args_count),
"",
- CodeEntry::kNoLineNumberInfo);
+ v8::CpuProfileNode::kNoLineNumberInfo);
code_entries_.Add(entry);
return entry;
}
+
+
+const char* CpuProfilesCollection::GetFunctionName(String* name) {
+ const char* maybe_empty_name = GetName(name);
+ return strlen(maybe_empty_name) > 0 ?
+ maybe_empty_name : "(anonymous function)";
+}
const char* CpuProfilesCollection::GetName(String* name) {
@@ -453,16 +471,19 @@
}
current_profiles_semaphore_->Signal();
}
+
ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
- : profiles_(profiles) {
+ : profiles_(profiles),
+ program_entry_(
+ profiles->NewCodeEntry(Logger::FUNCTION_TAG, "(program)")) {
}
void ProfileGenerator::RecordTickSample(const TickSample& sample) {
- // Allocate space for stack frames + pc + function.
- ScopedVector<CodeEntry*> entries(sample.frames_count + 2);
+ // Allocate space for stack frames + pc + function + (program).
+ ScopedVector<CodeEntry*> entries(sample.frames_count + 3);
CodeEntry** entry = entries.start();
*entry++ = code_map_.FindEntry(sample.pc);
@@ -486,6 +507,10 @@
++stack_pos) {
*entry++ = code_map_.FindEntry(*stack_pos);
}
+
+ // WebKit CPU profiles visualization requires "(program)" to be the
+ // topmost entry.
+ *entry++ = FLAG_prof_browser_mode ? program_entry_ : NULL;
profiles_->AddPathToCurrentProfiles(entries);
}
=======================================
--- /branches/bleeding_edge/src/profile-generator.h Tue Apr 6 03:36:38 2010
+++ /branches/bleeding_edge/src/profile-generator.h Tue Apr 6 07:54:20 2010
@@ -50,17 +50,20 @@
INLINE(const char* name() const) { return name_; }
INLINE(const char* resource_name() const) { return resource_name_; }
INLINE(int line_number() const) { return line_number_; }
+ INLINE(unsigned call_uid() const) { return call_uid_; }
static const char* kEmptyNamePrefix;
- static const int kNoLineNumberInfo;
private:
+ const unsigned call_uid_;
Logger::LogEventsAndTags tag_;
const char* name_prefix_;
const char* name_;
const char* resource_name_;
int line_number_;
+ static unsigned next_call_uid_;
+
DISALLOW_COPY_AND_ASSIGN(CodeEntry);
};
@@ -103,7 +106,7 @@
class ProfileTree {
public:
- ProfileTree() : root_(new ProfileNode(NULL)) { }
+ ProfileTree();
~ProfileTree();
void AddPathFromEnd(const Vector<CodeEntry*>& path);
@@ -121,6 +124,7 @@
template <typename Callback>
void TraverseBreadthFirstPostOrder(Callback* callback);
+ CodeEntry root_entry_;
ProfileNode* root_;
DISALLOW_COPY_AND_ASSIGN(ProfileTree);
@@ -219,6 +223,7 @@
void AddPathToCurrentProfiles(const Vector<CodeEntry*>& path);
private:
+ const char* GetFunctionName(String* name);
const char* GetName(String* name);
const char* GetName(int args_count);
@@ -282,6 +287,7 @@
private:
CpuProfilesCollection* profiles_;
CodeMap code_map_;
+ CodeEntry* program_entry_;
DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
};
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
To unsubscribe, reply using "remove me" as the subject.