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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4349 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 9b54227c
// 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_
......@@ -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 @@ Local<Context> Debug::GetDebugContext() {
#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 {
......
......@@ -33,6 +33,8 @@
#include "log-inl.h"
#include "../include/v8-profiler.h"
namespace v8 {
namespace internal {
......@@ -156,6 +158,23 @@ void ProfilerEventsProcessor::FunctionDeleteEvent(Address 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) {
if (!events_buffer_.IsEmpty()) {
CodeEventsContainer record;
......@@ -287,7 +306,7 @@ void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
tag,
name,
Heap::empty_string(),
CodeEntry::kNoLineNumberInfo,
v8::CpuProfileNode::kNoLineNumberInfo,
code->address(),
code->ExecutableSize());
}
......@@ -349,11 +368,10 @@ void CpuProfiler::GetterCallbackEvent(String* name, Address entry_point) {
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 @@ CpuProfiler::~CpuProfiler() {
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();
}
}
......
......@@ -150,6 +150,9 @@ class ProfilerEventsProcessor : public Thread {
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
......
......@@ -391,6 +391,8 @@ DEFINE_bool(prof_auto, true,
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.")
......
......@@ -771,13 +771,9 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
// 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
......
......@@ -40,7 +40,8 @@ CodeEntry::CodeEntry(Logger::LogEventsAndTags tag,
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),
......
......@@ -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 @@ ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
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)) {
......@@ -88,6 +93,12 @@ class DeleteNodesCallback {
} // namespace
ProfileTree::ProfileTree()
: root_entry_(Logger::FUNCTION_TAG, "", "(root)", "", 0),
root_(new ProfileNode(&root_entry_)) {
}
ProfileTree::~ProfileTree() {
DeleteNodesCallback cb;
TraverseBreadthFirstPostOrder(&cb);
......@@ -360,7 +371,7 @@ CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
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* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
CodeEntry::kEmptyNamePrefix,
name,
"",
CodeEntry::kNoLineNumberInfo);
v8::CpuProfileNode::kNoLineNumberInfo);
code_entries_.Add(entry);
return entry;
}
......@@ -387,7 +398,7 @@ CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
name_prefix,
GetName(name),
"",
CodeEntry::kNoLineNumberInfo);
v8::CpuProfileNode::kNoLineNumberInfo);
code_entries_.Add(entry);
return entry;
}
......@@ -399,12 +410,19 @@ CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
"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) {
if (name->IsString()) {
char* c_name =
......@@ -455,14 +473,17 @@ void CpuProfilesCollection::AddPathToCurrentProfiles(
}
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);
......@@ -487,6 +508,10 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
*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);
}
......
......@@ -50,17 +50,20 @@ class CodeEntry {
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 ProfileNode {
class ProfileTree {
public:
ProfileTree() : root_(new ProfileNode(NULL)) { }
ProfileTree();
~ProfileTree();
void AddPathFromEnd(const Vector<CodeEntry*>& path);
......@@ -121,6 +124,7 @@ class ProfileTree {
template <typename Callback>
void TraverseBreadthFirstPostOrder(Callback* callback);
CodeEntry root_entry_;
ProfileNode* root_;
DISALLOW_COPY_AND_ASSIGN(ProfileTree);
......@@ -219,6 +223,7 @@ class CpuProfilesCollection {
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 @@ class ProfileGenerator {
private:
CpuProfilesCollection* profiles_;
CodeMap code_map_;
CodeEntry* program_entry_;
DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment