Add initial semi-working producers profile.

Turned on with '--log-producers' flag, also needs '--noinline-new' (this is temporarily), '--log-code', '--log-gc'. Not all allocations are traced (I'm investigating.)

Stacks are stored using weak handles. Thus, when an object is collected, its allocation stack is deleted.

Review URL: http://codereview.chromium.org/267077

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3069 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 0bcb8adf
...@@ -350,6 +350,7 @@ DEFINE_bool(log_gc, false, ...@@ -350,6 +350,7 @@ DEFINE_bool(log_gc, false,
DEFINE_bool(log_handles, false, "Log global handle events.") DEFINE_bool(log_handles, false, "Log global handle events.")
DEFINE_bool(log_state_changes, false, "Log state changes.") DEFINE_bool(log_state_changes, false, "Log state changes.")
DEFINE_bool(log_suspect, false, "Log suspect operations.") DEFINE_bool(log_suspect, false, "Log suspect operations.")
DEFINE_bool(log_producers, false, "Log stack traces of JS objects allocations.")
DEFINE_bool(compress_log, false, DEFINE_bool(compress_log, false,
"Compress log to save space (makes log less human-readable).") "Compress log to save space (makes log less human-readable).")
DEFINE_bool(prof, false, DEFINE_bool(prof, false,
......
...@@ -264,6 +264,16 @@ void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) { ...@@ -264,6 +264,16 @@ void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
} }
void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f,
WeakReferenceCallback callback) {
for (Node* current = head_; current != NULL; current = current->next()) {
if (current->IsWeak() && current->callback() == callback) {
f(current->object_, current->parameter());
}
}
}
void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) { void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
for (Node* current = head_; current != NULL; current = current->next()) { for (Node* current = head_; current != NULL; current = current->next()) {
if (current->state_ == Node::WEAK) { if (current->state_ == Node::WEAK) {
......
...@@ -54,6 +54,8 @@ class ObjectGroup : public Malloced { ...@@ -54,6 +54,8 @@ class ObjectGroup : public Malloced {
}; };
typedef void (*WeakReferenceGuest)(Object* object, void* parameter);
class GlobalHandles : public AllStatic { class GlobalHandles : public AllStatic {
public: public:
// Creates a new global handle that is alive until Destroy is called. // Creates a new global handle that is alive until Destroy is called.
...@@ -99,6 +101,10 @@ class GlobalHandles : public AllStatic { ...@@ -99,6 +101,10 @@ class GlobalHandles : public AllStatic {
// Iterates over all weak roots in heap. // Iterates over all weak roots in heap.
static void IterateWeakRoots(ObjectVisitor* v); static void IterateWeakRoots(ObjectVisitor* v);
// Iterates over weak roots that are bound to a given callback.
static void IterateWeakRoots(WeakReferenceGuest f,
WeakReferenceCallback callback);
// Find all weak handles satisfying the callback predicate, mark // Find all weak handles satisfying the callback predicate, mark
// them as pending. // them as pending.
static void IdentifyWeakHandles(WeakSlotCallback f); static void IdentifyWeakHandles(WeakSlotCallback f);
......
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include "v8.h" #include "v8.h"
#include "heap-profiler.h" #include "heap-profiler.h"
#include "frames-inl.h"
#include "global-handles.h"
#include "string-stream.h" #include "string-stream.h"
namespace v8 { namespace v8 {
...@@ -327,6 +329,11 @@ void ConstructorHeapProfile::PrintStats() { ...@@ -327,6 +329,11 @@ void ConstructorHeapProfile::PrintStats() {
} }
static const char* GetConstructorName(const char* name) {
return name[0] != '\0' ? name : "(anonymous)";
}
void JSObjectsCluster::Print(StringStream* accumulator) const { void JSObjectsCluster::Print(StringStream* accumulator) const {
ASSERT(!is_null()); ASSERT(!is_null());
if (constructor_ == FromSpecialCase(ROOTS)) { if (constructor_ == FromSpecialCase(ROOTS)) {
...@@ -338,7 +345,7 @@ void JSObjectsCluster::Print(StringStream* accumulator) const { ...@@ -338,7 +345,7 @@ void JSObjectsCluster::Print(StringStream* accumulator) const {
} else { } else {
SmartPointer<char> s_name( SmartPointer<char> s_name(
constructor_->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)); constructor_->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL));
accumulator->Add("%s", (*s_name)[0] != '\0' ? *s_name : "(anonymous)"); accumulator->Add("%s", GetConstructorName(*s_name));
if (instance_ != NULL) { if (instance_ != NULL) {
accumulator->Add(":%p", static_cast<void*>(instance_)); accumulator->Add(":%p", static_cast<void*>(instance_));
} }
...@@ -574,6 +581,23 @@ void HeapProfiler::CollectStats(HeapObject* obj, HistogramInfo* info) { ...@@ -574,6 +581,23 @@ void HeapProfiler::CollectStats(HeapObject* obj, HistogramInfo* info) {
} }
static void StackWeakReferenceCallback(Persistent<Value> object,
void* trace) {
DeleteArray(static_cast<Address*>(trace));
object.Dispose();
}
static void PrintProducerStackTrace(Object* obj, void* trace) {
if (!obj->IsJSObject()) return;
String* constructor = JSObject::cast(obj)->constructor_name();
SmartPointer<char> s_name(
constructor->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL));
LOG(HeapSampleJSProducerEvent(GetConstructorName(*s_name),
reinterpret_cast<Address*>(trace)));
}
void HeapProfiler::WriteSample() { void HeapProfiler::WriteSample() {
LOG(HeapSampleBeginEvent("Heap", "allocated")); LOG(HeapSampleBeginEvent("Heap", "allocated"));
LOG(HeapSampleStats( LOG(HeapSampleStats(
...@@ -616,10 +640,40 @@ void HeapProfiler::WriteSample() { ...@@ -616,10 +640,40 @@ void HeapProfiler::WriteSample() {
js_cons_profile.PrintStats(); js_cons_profile.PrintStats();
js_retainer_profile.PrintStats(); js_retainer_profile.PrintStats();
GlobalHandles::IterateWeakRoots(PrintProducerStackTrace,
StackWeakReferenceCallback);
LOG(HeapSampleEndEvent("Heap", "allocated")); LOG(HeapSampleEndEvent("Heap", "allocated"));
} }
bool ProducerHeapProfile::can_log_ = false;
void ProducerHeapProfile::Setup() {
can_log_ = true;
}
void ProducerHeapProfile::RecordJSObjectAllocation(Object* obj) {
if (!can_log_ || !FLAG_log_producers) return;
int framesCount = 0;
for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
++framesCount;
}
if (framesCount == 0) return;
++framesCount; // Reserve place for the terminator item.
Vector<Address> stack(NewArray<Address>(framesCount), framesCount);
int i = 0;
for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
stack[i++] = it.frame()->pc();
}
stack[i] = NULL;
Handle<Object> handle = GlobalHandles::Create(obj);
GlobalHandles::MakeWeak(handle.location(),
static_cast<void*>(stack.start()),
StackWeakReferenceCallback);
}
#endif // ENABLE_LOGGING_AND_PROFILING #endif // ENABLE_LOGGING_AND_PROFILING
......
...@@ -256,6 +256,14 @@ class RetainerHeapProfile BASE_EMBEDDED { ...@@ -256,6 +256,14 @@ class RetainerHeapProfile BASE_EMBEDDED {
}; };
class ProducerHeapProfile : public AllStatic {
public:
static void Setup();
static void RecordJSObjectAllocation(Object* obj);
private:
static bool can_log_;
};
#endif // ENABLE_LOGGING_AND_PROFILING #endif // ENABLE_LOGGING_AND_PROFILING
} } // namespace v8::internal } } // namespace v8::internal
......
...@@ -2021,6 +2021,7 @@ Object* Heap::Allocate(Map* map, AllocationSpace space) { ...@@ -2021,6 +2021,7 @@ Object* Heap::Allocate(Map* map, AllocationSpace space) {
TargetSpaceId(map->instance_type())); TargetSpaceId(map->instance_type()));
if (result->IsFailure()) return result; if (result->IsFailure()) return result;
HeapObject::cast(result)->set_map(map); HeapObject::cast(result)->set_map(map);
ProducerHeapProfile::RecordJSObjectAllocation(result);
return result; return result;
} }
...@@ -2342,6 +2343,7 @@ Object* Heap::CopyJSObject(JSObject* source) { ...@@ -2342,6 +2343,7 @@ Object* Heap::CopyJSObject(JSObject* source) {
JSObject::cast(clone)->set_properties(FixedArray::cast(prop)); JSObject::cast(clone)->set_properties(FixedArray::cast(prop));
} }
// Return the new clone. // Return the new clone.
ProducerHeapProfile::RecordJSObjectAllocation(clone);
return clone; return clone;
} }
...@@ -3308,6 +3310,9 @@ bool Heap::Setup(bool create_heap_objects) { ...@@ -3308,6 +3310,9 @@ bool Heap::Setup(bool create_heap_objects) {
LOG(IntEvent("heap-capacity", Capacity())); LOG(IntEvent("heap-capacity", Capacity()));
LOG(IntEvent("heap-available", Available())); LOG(IntEvent("heap-available", Available()));
// This should be called only after initial objects have been created.
ProducerHeapProfile::Setup();
return true; return true;
} }
......
...@@ -934,6 +934,21 @@ void Logger::HeapSampleJSRetainersEvent( ...@@ -934,6 +934,21 @@ void Logger::HeapSampleJSRetainersEvent(
} }
void Logger::HeapSampleJSProducerEvent(const char* constructor,
Address* stack) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!Log::IsEnabled() || !FLAG_log_gc) return;
LogMessageBuilder msg;
msg.Append("heap-js-prod-item,%s", constructor);
while (*stack != NULL) {
msg.Append(",0x%" V8PRIxPTR, *stack++);
}
msg.Append("\n");
msg.WriteToLogFile();
#endif
}
void Logger::DebugTag(const char* call_site_tag) { void Logger::DebugTag(const char* call_site_tag) {
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
if (!Log::IsEnabled() || !FLAG_log) return; if (!Log::IsEnabled() || !FLAG_log) return;
......
...@@ -223,6 +223,8 @@ class Logger { ...@@ -223,6 +223,8 @@ class Logger {
int number, int bytes); int number, int bytes);
static void HeapSampleJSRetainersEvent(const char* constructor, static void HeapSampleJSRetainersEvent(const char* constructor,
const char* event); const char* event);
static void HeapSampleJSProducerEvent(const char* constructor,
Address* stack);
static void HeapSampleStats(const char* space, const char* kind, static void HeapSampleStats(const char* space, const char* kind,
int capacity, int used); int capacity, int used);
......
...@@ -75,7 +75,18 @@ function TickProcessor( ...@@ -75,7 +75,18 @@ function TickProcessor(
'tick': { parsers: [this.createAddressParser('code'), 'tick': { parsers: [this.createAddressParser('code'),
this.createAddressParser('stack'), parseInt, 'var-args'], this.createAddressParser('stack'), parseInt, 'var-args'],
processor: this.processTick, backrefs: true }, processor: this.processTick, backrefs: true },
'heap-sample-begin': { parsers: [null, null, parseInt],
processor: this.processHeapSampleBegin },
'heap-sample-end': { parsers: [null, null],
processor: this.processHeapSampleEnd },
'heap-js-prod-item': { parsers: [null, 'var-args'],
processor: this.processJSProducer, backrefs: true },
// Ignored events.
'profiler': null, 'profiler': null,
'heap-sample-stats': null,
'heap-sample-item': null,
'heap-js-cons-item': null,
'heap-js-ret-item': null,
// Obsolete row types. // Obsolete row types.
'code-allocate': null, 'code-allocate': null,
'begin-code-region': null, 'begin-code-region': null,
...@@ -113,6 +124,9 @@ function TickProcessor( ...@@ -113,6 +124,9 @@ function TickProcessor(
// Count each tick as a time unit. // Count each tick as a time unit.
this.viewBuilder_ = new devtools.profiler.ViewBuilder(1); this.viewBuilder_ = new devtools.profiler.ViewBuilder(1);
this.lastLogFileName_ = null; this.lastLogFileName_ = null;
this.generation_ = 1;
this.currentProducerProfile_ = null;
}; };
inherits(TickProcessor, devtools.profiler.LogReader); inherits(TickProcessor, devtools.profiler.LogReader);
...@@ -220,6 +234,41 @@ TickProcessor.prototype.processTick = function(pc, sp, vmState, stack) { ...@@ -220,6 +234,41 @@ TickProcessor.prototype.processTick = function(pc, sp, vmState, stack) {
}; };
TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) {
if (space != 'Heap') return;
this.currentProducerProfile_ = new devtools.profiler.CallTree();
};
TickProcessor.prototype.processHeapSampleEnd = function(space, state) {
if (space != 'Heap' || !this.currentProducerProfile_) return;
print('Generation ' + this.generation_ + ':');
var tree = this.currentProducerProfile_;
tree.computeTotalWeights();
var producersView = this.viewBuilder_.buildView(tree);
// Sort by total time, desc, then by name, desc.
producersView.sort(function(rec1, rec2) {
return rec2.totalTime - rec1.totalTime ||
(rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
this.printHeavyProfile(producersView.head.children);
this.currentProducerProfile_ = null;
this.generation_++;
};
TickProcessor.prototype.processJSProducer = function(constructor, stack) {
if (!this.currentProducerProfile_) return;
if (stack.length == 0) return;
var first = stack.shift();
var processedStack =
this.profile_.resolveAndFilterFuncs_(this.processStack(first, stack));
processedStack.unshift(constructor);
this.currentProducerProfile_.addPath(processedStack);
};
TickProcessor.prototype.printStatistics = function() { TickProcessor.prototype.printStatistics = function() {
print('Statistical profiling result from ' + this.lastLogFileName_ + print('Statistical profiling result from ' + this.lastLogFileName_ +
', (' + this.ticks_.total + ', (' + this.ticks_.total +
......
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