├── .gitignore ├── package.json ├── wscript ├── snapshot_diff.h ├── readme.md ├── graph_path.h ├── profile.h ├── graph_edge.h ├── snapshot.h ├── profile_node.h ├── graph_node.h ├── snapshot_diff.cc ├── profile.cc ├── graph_path.cc ├── graph_edge.cc ├── snapshot.cc ├── v8-profiler.js ├── profiler.cc ├── profile_node.cc └── graph_node.cc /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | .lock-wscript -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v8-profiler", 3 | "version": "0.0.2", 4 | "description": "node bindings for the v8 profiler", 5 | "homepage": "http://github.com/dannycoates/v8-profiler", 6 | "author": "Danny Coates ", 7 | "keywords": ["profiler", "inspector"], 8 | "engines" : { "node": ">=0.3.1" }, 9 | "main" : "v8-profiler" 10 | } -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | srcdir = '.' 2 | blddir = 'build' 3 | VERSION = '0.1.0' 4 | 5 | def set_options(ctx): 6 | ctx.tool_options('compiler_cxx') 7 | 8 | def configure(ctx): 9 | ctx.check_tool('compiler_cxx') 10 | ctx.check_tool('node_addon') 11 | 12 | def build(ctx): 13 | t = ctx.new_task_gen('cxx', 'shlib', 'node_addon') 14 | t.target = 'profiler' 15 | t.source = """ 16 | snapshot.cc 17 | graph_edge.cc 18 | graph_node.cc 19 | graph_path.cc 20 | snapshot_diff.cc 21 | profile.cc 22 | profile_node.cc 23 | profiler.cc 24 | """ -------------------------------------------------------------------------------- /snapshot_diff.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef NODE_SNAPSHOT_DIFF_ 4 | #define NODE_SNAPSHOT_DIFF_ 5 | 6 | #include 7 | #include 8 | 9 | using namespace v8; 10 | 11 | namespace nodex { 12 | 13 | class SnapshotDiff { 14 | public: 15 | static Handle New(const HeapSnapshotsDiff* diff); 16 | 17 | private: 18 | static Handle GetAdditions(Local property, const AccessorInfo& info); 19 | static Handle GetDeletions(Local property, const AccessorInfo& info); 20 | static void Initialize(); 21 | static Persistent diff_template_; 22 | }; 23 | 24 | } 25 | #endif // NODE_SNAPSHOT_DIFF_ -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | v8-profiler provides [node](http://github.com/ry/node) bindings for the v8 2 | profiler and integration with [node-inspector](http://github.com/dannycoates/node-inspector) 3 | 4 | ## Installation 5 | 6 | npm install v8-profiler 7 | 8 | ## Usage 9 | 10 | var profiler = require('v8-profiler'); 11 | 12 | ## API 13 | 14 | var snapshot = profiler.takeSnapshot([name]) //takes a heap snapshot 15 | 16 | profiler.startProfiling([name]) //begin cpu profiling 17 | var cpuProfile = profiler.stopProfiling([name]) //finish cpu profiling 18 | 19 | ## node-inspector 20 | 21 | Cpu profiles can be viewed and heap snapshots may be taken and viewed from the 22 | profiles panel. 23 | -------------------------------------------------------------------------------- /graph_path.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef NODE_GRAPH_PATH_ 4 | #define NODE_GRAPH_PATH_ 5 | 6 | #include 7 | #include 8 | 9 | using namespace v8; 10 | 11 | namespace nodex { 12 | 13 | class GraphPath { 14 | public: 15 | static Handle New(const HeapGraphPath* path); 16 | 17 | private: 18 | static Handle GetEdgesCount(Local property, const AccessorInfo& info); 19 | static Handle GetToNode(Local property, const AccessorInfo& info); 20 | static Handle GetFromNode(Local property, const AccessorInfo& info); 21 | static Handle GetEdge(const Arguments& args); 22 | static void Initialize(); 23 | static Persistent path_template_; 24 | }; 25 | 26 | } 27 | #endif // NODE_GRAPH_PATH_ -------------------------------------------------------------------------------- /profile.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef NODE_PROFILE_ 4 | #define NODE_PROFILE_ 5 | 6 | #include 7 | #include 8 | 9 | using namespace v8; 10 | 11 | namespace nodex { 12 | 13 | class Profile { 14 | public: 15 | static Handle New(const CpuProfile* profile); 16 | 17 | private: 18 | static Handle GetUid(Local property, const AccessorInfo& info); 19 | static Handle GetTitle(Local property, const AccessorInfo& info); 20 | static Handle GetTopRoot(Local property, const AccessorInfo& info); 21 | static Handle GetBottomRoot(Local property, const AccessorInfo& info); 22 | static void Initialize(); 23 | static Persistent profile_template_; 24 | }; 25 | 26 | } 27 | #endif // NODE_PROFILE_ -------------------------------------------------------------------------------- /graph_edge.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef NODE_GRAPH_EDGE_ 4 | #define NODE_GRAPH_EDGE_ 5 | 6 | #include 7 | #include 8 | 9 | using namespace v8; 10 | 11 | namespace nodex { 12 | 13 | class GraphEdge { 14 | public: 15 | static Handle New(const HeapGraphEdge* edge); 16 | 17 | private: 18 | static Handle GetType(Local property, const AccessorInfo& info); 19 | static Handle GetName(Local property, const AccessorInfo& info); 20 | static Handle GetFrom(Local property, const AccessorInfo& info); 21 | static Handle GetTo(Local property, const AccessorInfo& info); 22 | static void Initialize(); 23 | static Persistent edge_template_; 24 | }; 25 | 26 | } 27 | #endif // NODE_GRAPH_EDGE_ -------------------------------------------------------------------------------- /snapshot.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef NODE_SNAPSHOT_ 4 | #define NODE_SNAPSHOT_ 5 | 6 | #include 7 | #include 8 | 9 | using namespace v8; 10 | 11 | namespace nodex { 12 | 13 | class Snapshot { 14 | public: 15 | static Handle New(const HeapSnapshot* snapshot); 16 | 17 | private: 18 | static Handle GetUid(Local property, const AccessorInfo& info); 19 | static Handle GetTitle(Local property, const AccessorInfo& info); 20 | static Handle GetRoot(Local property, const AccessorInfo& info); 21 | static Handle GetType(Local property, const AccessorInfo& info); 22 | static Handle CompareWith(const Arguments& args); 23 | static void Initialize(); 24 | static Persistent snapshot_template_; 25 | }; 26 | 27 | } 28 | #endif // NODE_SNAPSHOT_ -------------------------------------------------------------------------------- /profile_node.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef NODE_PROFILE_NODE_ 4 | #define NODE_PROFILE_NODE_ 5 | 6 | #include 7 | #include 8 | 9 | using namespace v8; 10 | 11 | namespace nodex { 12 | 13 | class ProfileNode { 14 | public: 15 | static Handle New(const CpuProfileNode* node); 16 | 17 | private: 18 | static Handle GetFunctionName(Local property, const AccessorInfo& info); 19 | static Handle GetScriptName(Local property, const AccessorInfo& info); 20 | static Handle GetLineNumber(Local property, const AccessorInfo& info); 21 | static Handle GetTotalTime(Local property, const AccessorInfo& info); 22 | static Handle GetSelfTime(Local property, const AccessorInfo& info); 23 | static Handle GetTotalSamplesCount(Local property, const AccessorInfo& info); 24 | static Handle GetSelfSamplesCount(Local property, const AccessorInfo& info); 25 | static Handle GetCallUid(Local property, const AccessorInfo& info); 26 | static Handle GetChildrenCount(Local property, const AccessorInfo& info); 27 | static Handle GetChild(const Arguments& args); 28 | static void Initialize(); 29 | static Persistent node_template_; 30 | }; 31 | 32 | } 33 | #endif // NODE_PROFILE_NODE_ -------------------------------------------------------------------------------- /graph_node.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef NODE_GRAPH_NODE_ 4 | #define NODE_GRAPH_NODE_ 5 | 6 | #include 7 | #include 8 | 9 | using namespace v8; 10 | 11 | namespace nodex { 12 | 13 | class GraphNode { 14 | public: 15 | static Handle New(const HeapGraphNode* node); 16 | 17 | private: 18 | static Handle GetType(Local property, const AccessorInfo& info); 19 | static Handle GetName(Local property, const AccessorInfo& info); 20 | static Handle GetId(Local property, const AccessorInfo& info); 21 | static Handle GetPtr(Local property, const AccessorInfo& info); 22 | static Handle GetInstancesCount(Local property, const AccessorInfo& info); 23 | static Handle GetChildrenCount(Local property, const AccessorInfo& info); 24 | static Handle GetRetainersCount(Local property, const AccessorInfo& info); 25 | static Handle GetSize(Local property, const AccessorInfo& info); 26 | static Handle GetChild(const Arguments& args); 27 | static Handle GetRetainedSize(const Arguments& args); 28 | static Handle GetRetainer(const Arguments& args); 29 | static Handle GetRetainingPathsCount(Local property, const AccessorInfo& info); 30 | static Handle GetRetainingPath(const Arguments& args); 31 | static Handle GetDominator(Local property, const AccessorInfo& info); 32 | static void Initialize(); 33 | static Persistent node_template_; 34 | }; 35 | 36 | } 37 | #endif // NODE_GRAPH_NODE_ -------------------------------------------------------------------------------- /snapshot_diff.cc: -------------------------------------------------------------------------------- 1 | #include "snapshot_diff.h" 2 | #include "graph_node.h" 3 | 4 | using namespace v8; 5 | 6 | namespace nodex { 7 | 8 | Persistent SnapshotDiff::diff_template_; 9 | 10 | void SnapshotDiff::Initialize() { 11 | diff_template_ = Persistent::New(ObjectTemplate::New()); 12 | diff_template_->SetInternalFieldCount(1); 13 | diff_template_->SetAccessor(String::New("additions"), SnapshotDiff::GetAdditions); 14 | diff_template_->SetAccessor(String::New("deletions"), SnapshotDiff::GetDeletions); 15 | } 16 | 17 | Handle SnapshotDiff::GetAdditions(Local property, const AccessorInfo& info) { 18 | HandleScope scope; 19 | Local self = info.Holder(); 20 | void* ptr = self->GetPointerFromInternalField(0); 21 | const HeapGraphNode* node = static_cast(ptr)->GetAdditionsRoot(); 22 | return scope.Close(GraphNode::New(node)); 23 | } 24 | 25 | Handle SnapshotDiff::GetDeletions(Local property, const AccessorInfo& info) { 26 | HandleScope scope; 27 | Local self = info.Holder(); 28 | void* ptr = self->GetPointerFromInternalField(0); 29 | const HeapGraphNode* node = static_cast(ptr)->GetDeletionsRoot(); 30 | return scope.Close(GraphNode::New(node)); 31 | } 32 | 33 | Handle SnapshotDiff::New(const HeapSnapshotsDiff* diff) { 34 | HandleScope scope; 35 | 36 | if (diff_template_.IsEmpty()) { 37 | SnapshotDiff::Initialize(); 38 | } 39 | 40 | if(!diff) { 41 | return Undefined(); 42 | } 43 | else { 44 | Local obj = diff_template_->NewInstance(); 45 | obj->SetPointerInInternalField(0, const_cast(diff)); 46 | return scope.Close(obj); 47 | } 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /profile.cc: -------------------------------------------------------------------------------- 1 | #include "profile.h" 2 | #include "profile_node.h" 3 | 4 | using namespace v8; 5 | 6 | namespace nodex { 7 | 8 | Persistent Profile::profile_template_; 9 | 10 | void Profile::Initialize() { 11 | profile_template_ = Persistent::New(ObjectTemplate::New()); 12 | profile_template_->SetInternalFieldCount(1); 13 | profile_template_->SetAccessor(String::New("title"), Profile::GetTitle); 14 | profile_template_->SetAccessor(String::New("uid"), Profile::GetUid); 15 | profile_template_->SetAccessor(String::New("topRoot"), Profile::GetTopRoot); 16 | profile_template_->SetAccessor(String::New("bottomRoot"), Profile::GetBottomRoot); 17 | } 18 | 19 | Handle Profile::GetUid(Local property, const AccessorInfo& info) { 20 | HandleScope scope; 21 | Local self = info.Holder(); 22 | void* ptr = self->GetPointerFromInternalField(0); 23 | uint32_t uid = static_cast(ptr)->GetUid(); 24 | return scope.Close(Integer::NewFromUnsigned(uid)); 25 | } 26 | 27 | 28 | Handle Profile::GetTitle(Local property, const AccessorInfo& info) { 29 | HandleScope scope; 30 | Local self = info.Holder(); 31 | void* ptr = self->GetPointerFromInternalField(0); 32 | Handle title = static_cast(ptr)->GetTitle(); 33 | return scope.Close(title); 34 | } 35 | 36 | Handle Profile::GetTopRoot(Local property, const AccessorInfo& info) { 37 | HandleScope scope; 38 | Local self = info.Holder(); 39 | void* ptr = self->GetPointerFromInternalField(0); 40 | const CpuProfileNode* node = static_cast(ptr)->GetTopDownRoot(); 41 | return scope.Close(ProfileNode::New(node)); 42 | } 43 | 44 | 45 | Handle Profile::GetBottomRoot(Local property, const AccessorInfo& info) { 46 | HandleScope scope; 47 | Local self = info.Holder(); 48 | void* ptr = self->GetPointerFromInternalField(0); 49 | const CpuProfileNode* node = static_cast(ptr)->GetBottomUpRoot(); 50 | return scope.Close(ProfileNode::New(node)); 51 | } 52 | 53 | Handle Profile::New(const CpuProfile* profile) { 54 | HandleScope scope; 55 | 56 | if (profile_template_.IsEmpty()) { 57 | Profile::Initialize(); 58 | } 59 | 60 | if(!profile) { 61 | return Undefined(); 62 | } 63 | else { 64 | Local obj = profile_template_->NewInstance(); 65 | obj->SetPointerInInternalField(0, const_cast(profile)); 66 | return scope.Close(obj); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /graph_path.cc: -------------------------------------------------------------------------------- 1 | #include "graph_path.h" 2 | #include "graph_node.h" 3 | #include "graph_edge.h" 4 | 5 | using namespace v8; 6 | 7 | namespace nodex { 8 | 9 | Persistent GraphPath::path_template_; 10 | 11 | void GraphPath::Initialize() { 12 | path_template_ = Persistent::New(ObjectTemplate::New()); 13 | path_template_->SetInternalFieldCount(1); 14 | path_template_->SetAccessor(String::New("edgesCount"), GraphPath::GetEdgesCount); 15 | path_template_->SetAccessor(String::New("from"), GraphPath::GetFromNode); 16 | path_template_->SetAccessor(String::New("to"), GraphPath::GetToNode); 17 | path_template_->Set(String::New("getEdge"), FunctionTemplate::New(GraphPath::GetEdge)); 18 | } 19 | 20 | Handle GraphPath::GetEdgesCount(Local property, const AccessorInfo& info) { 21 | HandleScope scope; 22 | Local self = info.Holder(); 23 | void* ptr = self->GetPointerFromInternalField(0); 24 | int32_t count = static_cast(ptr)->GetEdgesCount(); 25 | return scope.Close(Integer::New(count)); 26 | } 27 | 28 | Handle GraphPath::GetEdge(const Arguments& args) { 29 | HandleScope scope; 30 | if (args.Length() < 1) { 31 | return ThrowException(Exception::Error(String::New("No index specified"))); 32 | } else if (!args[0]->IsInt32()) { 33 | return ThrowException(Exception::Error(String::New("Argument must be integer"))); 34 | } 35 | int32_t index = args[0]->Int32Value(); 36 | Handle self = args.This(); 37 | void* ptr = self->GetPointerFromInternalField(0); 38 | const HeapGraphEdge* edge = static_cast(ptr)->GetEdge(index); 39 | return scope.Close(GraphEdge::New(edge)); 40 | } 41 | 42 | Handle GraphPath::GetFromNode(Local property, const AccessorInfo& info) { 43 | HandleScope scope; 44 | Local self = info.Holder(); 45 | void* ptr = self->GetPointerFromInternalField(0); 46 | const HeapGraphNode* node = static_cast(ptr)->GetFromNode(); 47 | return scope.Close(GraphNode::New(node)); 48 | } 49 | 50 | Handle GraphPath::GetToNode(Local property, const AccessorInfo& info) { 51 | HandleScope scope; 52 | Local self = info.Holder(); 53 | void* ptr = self->GetPointerFromInternalField(0); 54 | const HeapGraphNode* node = static_cast(ptr)->GetToNode(); 55 | return scope.Close(GraphNode::New(node)); 56 | } 57 | 58 | Handle GraphPath::New(const HeapGraphPath* path) { 59 | HandleScope scope; 60 | 61 | if (path_template_.IsEmpty()) { 62 | GraphPath::Initialize(); 63 | } 64 | 65 | if(!path) { 66 | return Undefined(); 67 | } 68 | else { 69 | Local obj = path_template_->NewInstance(); 70 | obj->SetPointerInInternalField(0, const_cast(path)); 71 | return scope.Close(obj); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /graph_edge.cc: -------------------------------------------------------------------------------- 1 | #include "graph_edge.h" 2 | #include "graph_node.h" 3 | 4 | using namespace v8; 5 | 6 | namespace nodex { 7 | 8 | Persistent GraphEdge::edge_template_; 9 | 10 | void GraphEdge::Initialize() { 11 | edge_template_ = Persistent::New(ObjectTemplate::New()); 12 | edge_template_->SetInternalFieldCount(1); 13 | edge_template_->SetAccessor(String::New("type"), GraphEdge::GetType); 14 | edge_template_->SetAccessor(String::New("name"), GraphEdge::GetName); 15 | edge_template_->SetAccessor(String::New("from"), GraphEdge::GetFrom); 16 | edge_template_->SetAccessor(String::New("to"), GraphEdge::GetTo); 17 | } 18 | 19 | Handle GraphEdge::GetType(Local property, const AccessorInfo& info) { 20 | HandleScope scope; 21 | Local self = info.Holder(); 22 | void* ptr = self->GetPointerFromInternalField(0); 23 | int32_t type = static_cast(static_cast(ptr)->GetType()); 24 | Local t; 25 | switch(type) { 26 | case 0: //HeapGraphEdge::kContextVariable : 27 | t = String::New("ContextVariable"); 28 | break; 29 | case 1: //HeapGraphEdge::kElement : 30 | t = String::New("Element"); 31 | break; 32 | case 2: //HeapGraphEdge::kProperty : 33 | t = String::New("Property"); 34 | break; 35 | case 3: //HeapGraphEdge::kInternal : 36 | t = String::New("Internal"); 37 | break; 38 | case 4: //HeapGraphEdge::kHidden : 39 | t = String::New("Hidden"); 40 | break; 41 | case 5: //HeapGraphEdge::kShortcut : 42 | t = String::New("Shortcut"); 43 | break; 44 | default: 45 | t = String::New("Unknown"); 46 | break; 47 | } 48 | return scope.Close(t); 49 | } 50 | 51 | Handle GraphEdge::GetName(Local property, const AccessorInfo& info) { 52 | HandleScope scope; 53 | Local self = info.Holder(); 54 | void* ptr = self->GetPointerFromInternalField(0); 55 | Handle title = static_cast(ptr)->GetName(); 56 | return scope.Close(title); 57 | } 58 | 59 | Handle GraphEdge::GetFrom(Local property, const AccessorInfo& info) { 60 | HandleScope scope; 61 | Local self = info.Holder(); 62 | void* ptr = self->GetPointerFromInternalField(0); 63 | const HeapGraphNode* node = static_cast(ptr)->GetFromNode(); 64 | return scope.Close(GraphNode::New(node)); 65 | } 66 | 67 | Handle GraphEdge::GetTo(Local property, const AccessorInfo& info) { 68 | HandleScope scope; 69 | Local self = info.Holder(); 70 | void* ptr = self->GetPointerFromInternalField(0); 71 | const HeapGraphNode* node = static_cast(ptr)->GetToNode(); 72 | return scope.Close(GraphNode::New(node)); 73 | } 74 | 75 | Handle GraphEdge::New(const HeapGraphEdge* edge) { 76 | HandleScope scope; 77 | 78 | if (edge_template_.IsEmpty()) { 79 | GraphEdge::Initialize(); 80 | } 81 | 82 | if(!edge) { 83 | return Undefined(); 84 | } 85 | else { 86 | Local obj = edge_template_->NewInstance(); 87 | obj->SetPointerInInternalField(0, const_cast(edge)); 88 | return scope.Close(obj); 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /snapshot.cc: -------------------------------------------------------------------------------- 1 | #include "snapshot.h" 2 | #include "snapshot_diff.h" 3 | #include "graph_node.h" 4 | 5 | using namespace v8; 6 | 7 | namespace nodex { 8 | 9 | Persistent Snapshot::snapshot_template_; 10 | 11 | void Snapshot::Initialize() { 12 | snapshot_template_ = Persistent::New(ObjectTemplate::New()); 13 | snapshot_template_->SetInternalFieldCount(1); 14 | snapshot_template_->SetAccessor(String::New("title"), Snapshot::GetTitle); 15 | snapshot_template_->SetAccessor(String::New("uid"), Snapshot::GetUid); 16 | snapshot_template_->SetAccessor(String::New("root"), Snapshot::GetRoot); 17 | snapshot_template_->SetAccessor(String::New("type"), Snapshot::GetType); 18 | snapshot_template_->Set(String::New("compareWith"), FunctionTemplate::New(Snapshot::CompareWith)); 19 | } 20 | 21 | Handle Snapshot::GetUid(Local property, const AccessorInfo& info) { 22 | HandleScope scope; 23 | Local self = info.Holder(); 24 | void* ptr = self->GetPointerFromInternalField(0); 25 | uint32_t uid = static_cast(ptr)->GetUid(); 26 | return scope.Close(Integer::NewFromUnsigned(uid)); 27 | } 28 | 29 | 30 | Handle Snapshot::GetTitle(Local property, const AccessorInfo& info) { 31 | HandleScope scope; 32 | Local self = info.Holder(); 33 | void* ptr = self->GetPointerFromInternalField(0); 34 | Handle title = static_cast(ptr)->GetTitle(); 35 | return scope.Close(title); 36 | } 37 | 38 | Handle Snapshot::GetRoot(Local property, const AccessorInfo& info) { 39 | HandleScope scope; 40 | Local self = info.Holder(); 41 | void* ptr = self->GetPointerFromInternalField(0); 42 | const HeapGraphNode* node = static_cast(ptr)->GetRoot(); 43 | return scope.Close(GraphNode::New(node)); 44 | } 45 | 46 | Handle Snapshot::GetType(Local property, const AccessorInfo& info) { 47 | HandleScope scope; 48 | Local self = info.Holder(); 49 | void* ptr = self->GetPointerFromInternalField(0); 50 | HeapSnapshot::Type type = static_cast(ptr)->GetType(); 51 | Local t; 52 | switch(type) { 53 | case HeapSnapshot::kFull : 54 | t = String::New("Full"); 55 | break; 56 | case HeapSnapshot::kAggregated : 57 | t = String::New("Aggregated"); 58 | break; 59 | default: 60 | t = String::New("Unknown"); 61 | break; 62 | } 63 | return scope.Close(t); 64 | } 65 | 66 | Handle Snapshot::CompareWith(const Arguments& args) { 67 | HandleScope scope; 68 | if (args.Length() < 1) { 69 | return ThrowException(Exception::Error(String::New("No index specified"))); 70 | } 71 | Handle other = args[0]->ToObject(); 72 | Handle self = args.This(); 73 | 74 | void* ptr = self->GetPointerFromInternalField(0); 75 | 76 | void* optr = other->GetPointerFromInternalField(0); 77 | 78 | const HeapSnapshotsDiff* diff = static_cast(ptr)->CompareWith(static_cast(optr)); 79 | return scope.Close(SnapshotDiff::New(diff)); 80 | } 81 | 82 | Handle Snapshot::New(const HeapSnapshot* snapshot) { 83 | HandleScope scope; 84 | 85 | if (snapshot_template_.IsEmpty()) { 86 | Snapshot::Initialize(); 87 | } 88 | 89 | if(!snapshot) { 90 | return Undefined(); 91 | } 92 | else { 93 | Local obj = snapshot_template_->NewInstance(); 94 | obj->SetPointerInInternalField(0, const_cast(snapshot)); 95 | return scope.Close(obj); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /v8-profiler.js: -------------------------------------------------------------------------------- 1 | var binding = require("./build/default/profiler"); 2 | 3 | function Snapshot() {} 4 | 5 | //adapted from WebCore/bindings/v8/ScriptHeapSnapshot.cpp 6 | Snapshot.prototype.stringify = function() { 7 | var root = this.root, i, j, count_i, count_j, node, 8 | lowLevels = {}, entries = {}, entry, 9 | children = {}, child, edge, result = {}; 10 | for (i = 0, count_i = root.childrenCount; i < count_i; i++) { 11 | node = root.getChild(i).to; 12 | if (node.type === 'Hidden') { 13 | lowLevels[node.name] = { 14 | count: node.instancesCount, 15 | size: node.size, 16 | type: node.name 17 | }; 18 | } 19 | else if (node.instancesCount > 0) { 20 | entries[node.name] = { 21 | constructorName: node.name, 22 | count: node.instancesCount, 23 | size: node.size 24 | }; 25 | } 26 | // FIXME: the children portion is too slow and bloats the results 27 | 28 | else { 29 | entry = { 30 | constructorName: node.name 31 | }; 32 | for(j = 0, count_j = node.childrenCount; j < count_j; j++) { 33 | edge = node.getChild(j); 34 | child = edge.to; 35 | entry[child.ptr.toString()] = { 36 | constructorName: child.name, 37 | count: parseInt(edge.name, 10) 38 | } 39 | } 40 | children[node.ptr.toString()] = entry; 41 | } 42 | } 43 | result.lowlevels = lowLevels; 44 | result.entries = entries; 45 | result.children = children; 46 | return JSON.stringify(result); 47 | } 48 | 49 | function CpuProfile() {} 50 | 51 | function inspectorObjectFor(node) { 52 | var i, count, child, 53 | result = { 54 | functionName: node.functionName, 55 | url: node.scriptName, 56 | lineNumber: node.lineNumber, 57 | totalTime: node.totalTime, 58 | selfTime: node.selfTime, 59 | numberOfCalls: 0, 60 | visible: true, 61 | callUID: node.callUid, 62 | children: [] 63 | }; 64 | for(i = 0, count = node.childrenCount; i < count; i++) { 65 | child = node.getChild(i); 66 | result.children.push(inspectorObjectFor(child)); 67 | } 68 | return result; 69 | } 70 | 71 | CpuProfile.prototype.stringify = function() { 72 | return JSON.stringify(inspectorObjectFor(this.topRoot)); 73 | } 74 | 75 | var heapCache = []; 76 | 77 | exports.takeSnapshot = function(name, mode) { 78 | var type = (mode === 'full') ? 0 : 1; 79 | var snapshot = binding.takeSnapshot(name, type); 80 | snapshot.__proto__ = Snapshot.prototype; 81 | heapCache.push(snapshot); 82 | return snapshot; 83 | } 84 | 85 | exports.getSnapshot = function(index) { 86 | return heapCache[index]; 87 | } 88 | 89 | exports.findSnapshot = function(uid) { 90 | return heapCache.filter(function(s) {return s.uid === uid;})[0]; 91 | } 92 | 93 | exports.snapshotCount = function() { 94 | return heapCache.length; 95 | } 96 | 97 | var cpuCache = []; 98 | 99 | exports.startProfiling = function(name) { 100 | binding.startProfiling(name); 101 | } 102 | 103 | exports.stopProfiling = function(name) { 104 | var profile = binding.stopProfiling(name); 105 | profile.__proto__ = CpuProfile.prototype; 106 | cpuCache.push(profile); 107 | return profile; 108 | } 109 | 110 | exports.getProfile = function(index) { 111 | return cpuCache[index]; 112 | } 113 | 114 | exports.findProfile = function(uid) { 115 | return cpuCache.filter(function(s) {return s.uid === uid;})[0]; 116 | } 117 | 118 | exports.profileCount = function() { 119 | return cpuCache.length; 120 | } 121 | 122 | process.profiler = exports; -------------------------------------------------------------------------------- /profiler.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "snapshot.h" 5 | #include "profile.h" 6 | 7 | using namespace v8; 8 | using namespace node; 9 | using namespace nodex; 10 | 11 | static Handle TakeSnapshot(const Arguments& args) { 12 | HandleScope scope; 13 | Local title = String::New(""); 14 | int32_t len = args.Length(); 15 | HeapSnapshot::Type mode = HeapSnapshot::kAggregated; 16 | if (len > 0) { 17 | title = args[0]->ToString(); 18 | } 19 | if (len > 1 && args[1]->IsInt32()) { 20 | mode = static_cast(args[1]->Int32Value()); 21 | } 22 | const HeapSnapshot* snapshot = HeapProfiler::TakeSnapshot(title, mode); 23 | return scope.Close(Snapshot::New(snapshot)); 24 | } 25 | 26 | static Handle GetSnapshot(const Arguments& args) { 27 | HandleScope scope; 28 | if (args.Length() < 1) { 29 | return ThrowException(Exception::Error(String::New("No index specified"))); 30 | } else if (!args[0]->IsInt32()) { 31 | return ThrowException(Exception::TypeError(String::New("Argument must be an integer"))); 32 | } 33 | int32_t index = args[0]->Int32Value(); 34 | const HeapSnapshot* snapshot = HeapProfiler::GetSnapshot(index); 35 | return scope.Close(Snapshot::New(snapshot)); 36 | } 37 | 38 | static Handle FindSnapshot(const Arguments& args) { 39 | HandleScope scope; 40 | if (args.Length() < 1) { 41 | return ThrowException(Exception::Error(String::New("No uid specified"))); 42 | } 43 | uint32_t uid = args[0]->Uint32Value(); 44 | const HeapSnapshot* snapshot = HeapProfiler::FindSnapshot(uid); 45 | return scope.Close(Snapshot::New(snapshot)); 46 | } 47 | 48 | static Handle GetSnapshotsCount(const Arguments& args) { 49 | HandleScope scope; 50 | return scope.Close(Integer::New(HeapProfiler::GetSnapshotsCount())); 51 | } 52 | 53 | Handle StartProfiling(const Arguments& args) { 54 | HandleScope scope; 55 | Local title = args.Length() > 0 ? args[0]->ToString() : String::New(""); 56 | v8::CpuProfiler::StartProfiling(title); 57 | return Undefined(); 58 | } 59 | 60 | Handle StopProfiling(const Arguments& args) { 61 | HandleScope scope; 62 | Local title = args.Length() > 0 ? args[0]->ToString() : String::New(""); 63 | const CpuProfile* profile = v8::CpuProfiler::StopProfiling(title); 64 | return scope.Close(Profile::New(profile)); 65 | } 66 | 67 | Handle GetProfile(const Arguments& args) { 68 | HandleScope scope; 69 | if (args.Length() < 1) { 70 | return ThrowException(Exception::Error(String::New("No index specified"))); 71 | } else if (!args[0]->IsInt32()) { 72 | return ThrowException(Exception::TypeError(String::New("Argument must be an integer"))); 73 | } 74 | int32_t index = args[0]->Int32Value(); 75 | const CpuProfile* profile = v8::CpuProfiler::GetProfile(index); 76 | return scope.Close(Profile::New(profile)); 77 | } 78 | 79 | Handle FindProfile(const Arguments& args) { 80 | HandleScope scope; 81 | if (args.Length() < 1) { 82 | return ThrowException(Exception::Error(String::New("No index specified"))); 83 | } else if (!args[0]->IsInt32()) { 84 | return ThrowException(Exception::TypeError(String::New("Argument must be an integer"))); 85 | } 86 | uint32_t uid = args[0]->Uint32Value(); 87 | const CpuProfile* profile = v8::CpuProfiler::FindProfile(uid); 88 | return scope.Close(Profile::New(profile)); 89 | } 90 | 91 | Handle GetProfilesCount(const Arguments& args) { 92 | HandleScope scope; 93 | return scope.Close(Integer::New(v8::CpuProfiler::GetProfilesCount())); 94 | } 95 | 96 | extern "C" void init(Handle target) { 97 | HandleScope scope; 98 | 99 | NODE_SET_METHOD(target, "takeSnapshot", TakeSnapshot); 100 | NODE_SET_METHOD(target, "getSnapshot", GetSnapshot); 101 | NODE_SET_METHOD(target, "findSnapshot", FindSnapshot); 102 | NODE_SET_METHOD(target, "snapshotCount", GetSnapshotsCount); 103 | 104 | NODE_SET_METHOD(target, "startProfiling", StartProfiling); 105 | NODE_SET_METHOD(target, "stopProfiling", StopProfiling); 106 | NODE_SET_METHOD(target, "getProfile", GetProfile); 107 | NODE_SET_METHOD(target, "findProfile", FindProfile); 108 | NODE_SET_METHOD(target, "getProfilesCount", GetProfilesCount); 109 | } -------------------------------------------------------------------------------- /profile_node.cc: -------------------------------------------------------------------------------- 1 | #include "profile_node.h" 2 | 3 | using namespace v8; 4 | 5 | namespace nodex { 6 | 7 | Persistent ProfileNode::node_template_; 8 | 9 | void ProfileNode::Initialize() { 10 | node_template_ = Persistent::New(ObjectTemplate::New()); 11 | node_template_->SetInternalFieldCount(1); 12 | node_template_->SetAccessor(String::New("functionName"), ProfileNode::GetFunctionName); 13 | node_template_->SetAccessor(String::New("scriptName"), ProfileNode::GetScriptName); 14 | node_template_->SetAccessor(String::New("lineNumber"), ProfileNode::GetLineNumber); 15 | node_template_->SetAccessor(String::New("totalTime"), ProfileNode::GetTotalTime); 16 | node_template_->SetAccessor(String::New("selfTime"), ProfileNode::GetSelfTime); 17 | node_template_->SetAccessor(String::New("totalSamplesCount"), ProfileNode::GetTotalSamplesCount); 18 | node_template_->SetAccessor(String::New("selfSamplesCount"), ProfileNode::GetSelfSamplesCount); 19 | node_template_->SetAccessor(String::New("callUid"), ProfileNode::GetCallUid); 20 | node_template_->SetAccessor(String::New("childrenCount"), ProfileNode::GetChildrenCount); 21 | node_template_->Set(String::New("getChild"), FunctionTemplate::New(ProfileNode::GetChild)); 22 | } 23 | 24 | Handle ProfileNode::GetFunctionName(Local property, const AccessorInfo& info) { 25 | HandleScope scope; 26 | Local self = info.Holder(); 27 | void* ptr = self->GetPointerFromInternalField(0); 28 | Handle fname = static_cast(ptr)->GetFunctionName(); 29 | return scope.Close(fname); 30 | } 31 | 32 | Handle ProfileNode::GetScriptName(Local property, const AccessorInfo& info) { 33 | HandleScope scope; 34 | Local self = info.Holder(); 35 | void* ptr = self->GetPointerFromInternalField(0); 36 | Handle sname = static_cast(ptr)->GetScriptResourceName(); 37 | return scope.Close(sname); 38 | } 39 | 40 | Handle ProfileNode::GetLineNumber(Local property, const AccessorInfo& info) { 41 | HandleScope scope; 42 | Local self = info.Holder(); 43 | void* ptr = self->GetPointerFromInternalField(0); 44 | int32_t ln = static_cast(ptr)->GetLineNumber(); 45 | return scope.Close(Integer::New(ln)); 46 | } 47 | 48 | Handle ProfileNode::GetTotalTime(Local property, const AccessorInfo& info) { 49 | HandleScope scope; 50 | Local self = info.Holder(); 51 | void* ptr = self->GetPointerFromInternalField(0); 52 | double ttime = static_cast(ptr)->GetTotalTime(); 53 | return scope.Close(Number::New(ttime)); 54 | } 55 | 56 | Handle ProfileNode::GetSelfTime(Local property, const AccessorInfo& info) { 57 | HandleScope scope; 58 | Local self = info.Holder(); 59 | void* ptr = self->GetPointerFromInternalField(0); 60 | double stime = static_cast(ptr)->GetSelfTime(); 61 | return scope.Close(Number::New(stime)); 62 | } 63 | 64 | Handle ProfileNode::GetTotalSamplesCount(Local property, const AccessorInfo& info) { 65 | HandleScope scope; 66 | Local self = info.Holder(); 67 | void* ptr = self->GetPointerFromInternalField(0); 68 | double samples = static_cast(ptr)->GetTotalSamplesCount(); 69 | return scope.Close(Number::New(samples)); 70 | } 71 | 72 | Handle ProfileNode::GetSelfSamplesCount(Local property, const AccessorInfo& info) { 73 | HandleScope scope; 74 | Local self = info.Holder(); 75 | void* ptr = self->GetPointerFromInternalField(0); 76 | double samples = static_cast(ptr)->GetSelfSamplesCount(); 77 | return scope.Close(Number::New(samples)); 78 | } 79 | 80 | Handle ProfileNode::GetCallUid(Local property, const AccessorInfo& info) { 81 | HandleScope scope; 82 | Local self = info.Holder(); 83 | void* ptr = self->GetPointerFromInternalField(0); 84 | uint32_t uid = static_cast(ptr)->GetCallUid(); 85 | return scope.Close(Integer::NewFromUnsigned(uid)); 86 | } 87 | 88 | Handle ProfileNode::GetChildrenCount(Local property, const AccessorInfo& info) { 89 | HandleScope scope; 90 | Local self = info.Holder(); 91 | void* ptr = self->GetPointerFromInternalField(0); 92 | int32_t count = static_cast(ptr)->GetChildrenCount(); 93 | return scope.Close(Integer::New(count)); 94 | } 95 | 96 | Handle ProfileNode::GetChild(const Arguments& args) { 97 | HandleScope scope; 98 | if (args.Length() < 1) { 99 | return ThrowException(Exception::Error(String::New("No index specified"))); 100 | } else if (!args[0]->IsInt32()) { 101 | return ThrowException(Exception::Error(String::New("Argument must be integer"))); 102 | } 103 | int32_t index = args[0]->Int32Value(); 104 | Handle self = args.This(); 105 | void* ptr = self->GetPointerFromInternalField(0); 106 | const CpuProfileNode* node = static_cast(ptr)->GetChild(index); 107 | return scope.Close(ProfileNode::New(node)); 108 | } 109 | 110 | Handle ProfileNode::New(const CpuProfileNode* node) { 111 | HandleScope scope; 112 | 113 | if (node_template_.IsEmpty()) { 114 | ProfileNode::Initialize(); 115 | } 116 | 117 | if(!node) { 118 | return Undefined(); 119 | } 120 | else { 121 | Local obj = node_template_->NewInstance(); 122 | obj->SetPointerInInternalField(0, const_cast(node)); 123 | return scope.Close(obj); 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /graph_node.cc: -------------------------------------------------------------------------------- 1 | #include "graph_node.h" 2 | #include "graph_edge.h" 3 | #include "graph_path.h" 4 | 5 | using namespace v8; 6 | 7 | namespace nodex { 8 | 9 | Persistent GraphNode::node_template_; 10 | 11 | void GraphNode::Initialize() { 12 | node_template_ = Persistent::New(ObjectTemplate::New()); 13 | node_template_->SetInternalFieldCount(1); 14 | node_template_->SetAccessor(String::New("type"), GraphNode::GetType); 15 | node_template_->SetAccessor(String::New("name"), GraphNode::GetName); 16 | node_template_->SetAccessor(String::New("id"), GraphNode::GetId); 17 | node_template_->SetAccessor(String::New("ptr"), GraphNode::GetPtr); 18 | node_template_->SetAccessor(String::New("instancesCount"), GraphNode::GetInstancesCount); 19 | node_template_->SetAccessor(String::New("childrenCount"), GraphNode::GetChildrenCount); 20 | node_template_->SetAccessor(String::New("retainersCount"), GraphNode::GetRetainersCount); 21 | node_template_->SetAccessor(String::New("size"), GraphNode::GetSize); 22 | node_template_->SetAccessor(String::New("retainingPathsCount"), GraphNode::GetRetainingPathsCount); 23 | node_template_->SetAccessor(String::New("dominatorNode"), GraphNode::GetDominator); 24 | node_template_->Set(String::New("getChild"), FunctionTemplate::New(GraphNode::GetChild)); 25 | node_template_->Set(String::New("retainedSize"), FunctionTemplate::New(GraphNode::GetRetainedSize)); 26 | node_template_->Set(String::New("getRetainer"), FunctionTemplate::New(GraphNode::GetRetainer)); 27 | node_template_->Set(String::New("getRetainingPath"), FunctionTemplate::New(GraphNode::GetRetainingPath)); 28 | } 29 | 30 | Handle GraphNode::GetType(Local property, const AccessorInfo& info) { 31 | HandleScope scope; 32 | Local self = info.Holder(); 33 | void* ptr = self->GetPointerFromInternalField(0); 34 | int32_t type = static_cast(static_cast(ptr)->GetType()); 35 | Local t; 36 | switch(type) { 37 | case 1: //HeapGraphNode::kArray : 38 | t = String::New("Array"); 39 | break; 40 | case 2: //HeapGraphNode::kString : 41 | t = String::New("String"); 42 | break; 43 | case 3: //HeapGraphNode::kObject : 44 | t = String::New("Object"); 45 | break; 46 | case 4: //HeapGraphNode::kCode : 47 | t = String::New("Code"); 48 | break; 49 | case 5: //HeapGraphNode::kClosure : 50 | t = String::New("Closure"); 51 | break; 52 | case 6: //HeapGraphNode::kRegExp : 53 | t = String::New("RegExp"); 54 | break; 55 | case 7: //HeapGraphNode::kHeapNumber : 56 | t = String::New("HeapNumber"); 57 | break; 58 | default: 59 | t = String::New("Hidden"); 60 | break; 61 | } 62 | return scope.Close(t); 63 | } 64 | 65 | Handle GraphNode::GetName(Local property, const AccessorInfo& info) { 66 | HandleScope scope; 67 | Local self = info.Holder(); 68 | void* ptr = self->GetPointerFromInternalField(0); 69 | Handle title = static_cast(ptr)->GetName(); 70 | return scope.Close(title); 71 | } 72 | 73 | Handle GraphNode::GetId(Local property, const AccessorInfo& info) { 74 | HandleScope scope; 75 | Local self = info.Holder(); 76 | void* ptr = self->GetPointerFromInternalField(0); 77 | uint64_t id = static_cast(ptr)->GetId(); 78 | return scope.Close(Integer::NewFromUnsigned(id)); 79 | } 80 | 81 | Handle GraphNode::GetPtr(Local property, const AccessorInfo& info) { 82 | HandleScope scope; 83 | Local self = info.Holder(); 84 | void* ptr = self->GetPointerFromInternalField(0); 85 | uint64_t id = reinterpret_cast(ptr); 86 | return scope.Close(Integer::NewFromUnsigned(id)); 87 | } 88 | 89 | Handle GraphNode::GetInstancesCount(Local property, const AccessorInfo& info) { 90 | HandleScope scope; 91 | Local self = info.Holder(); 92 | void* ptr = self->GetPointerFromInternalField(0); 93 | int32_t count = static_cast(ptr)->GetInstancesCount(); 94 | return scope.Close(Integer::New(count)); 95 | } 96 | 97 | Handle GraphNode::GetChildrenCount(Local property, const AccessorInfo& info) { 98 | HandleScope scope; 99 | Local self = info.Holder(); 100 | void* ptr = self->GetPointerFromInternalField(0); 101 | int32_t count = static_cast(ptr)->GetChildrenCount(); 102 | return scope.Close(Integer::New(count)); 103 | } 104 | 105 | Handle GraphNode::GetRetainersCount(Local property, const AccessorInfo& info) { 106 | HandleScope scope; 107 | Local self = info.Holder(); 108 | void* ptr = self->GetPointerFromInternalField(0); 109 | int32_t count = static_cast(ptr)->GetRetainersCount(); 110 | return scope.Close(Integer::New(count)); 111 | } 112 | 113 | Handle GraphNode::GetSize(Local property, const AccessorInfo& info) { 114 | HandleScope scope; 115 | Local self = info.Holder(); 116 | void* ptr = self->GetPointerFromInternalField(0); 117 | int32_t count = static_cast(ptr)->GetSelfSize(); 118 | return scope.Close(Integer::New(count)); 119 | } 120 | 121 | Handle GraphNode::GetChild(const Arguments& args) { 122 | HandleScope scope; 123 | if (args.Length() < 1) { 124 | return ThrowException(Exception::Error(String::New("No index specified"))); 125 | } else if (!args[0]->IsInt32()) { 126 | return ThrowException(Exception::Error(String::New("Argument must be integer"))); 127 | } 128 | int32_t index = args[0]->Int32Value(); 129 | Handle self = args.This(); 130 | void* ptr = self->GetPointerFromInternalField(0); 131 | const HeapGraphEdge* edge = static_cast(ptr)->GetChild(index); 132 | return scope.Close(GraphEdge::New(edge)); 133 | } 134 | 135 | Handle GraphNode::GetRetainedSize(const Arguments& args) { 136 | HandleScope scope; 137 | bool exact = false; 138 | if (args.Length() > 0) { 139 | exact = args[0]->BooleanValue(); 140 | } 141 | Handle self = args.This(); 142 | void* ptr = self->GetPointerFromInternalField(0); 143 | int32_t size = static_cast(ptr)->GetRetainedSize(exact); 144 | return scope.Close(Integer::New(size)); 145 | } 146 | 147 | Handle GraphNode::GetRetainer(const Arguments& args) { 148 | HandleScope scope; 149 | if (args.Length() < 1) { 150 | return ThrowException(Exception::Error(String::New("No index specified"))); 151 | } else if (!args[0]->IsInt32()) { 152 | return ThrowException(Exception::Error(String::New("Argument must be integer"))); 153 | } 154 | int32_t index = args[0]->Int32Value(); 155 | Handle self = args.This(); 156 | void* ptr = self->GetPointerFromInternalField(0); 157 | const HeapGraphEdge* edge = static_cast(ptr)->GetRetainer(index); 158 | return scope.Close(GraphEdge::New(edge)); 159 | } 160 | 161 | Handle GraphNode::GetRetainingPathsCount(Local property, const AccessorInfo& info) { 162 | HandleScope scope; 163 | Local self = info.Holder(); 164 | void* ptr = self->GetPointerFromInternalField(0); 165 | int32_t count = static_cast(ptr)->GetRetainingPathsCount(); 166 | return scope.Close(Integer::New(count)); 167 | } 168 | 169 | Handle GraphNode::GetRetainingPath(const Arguments& args) { 170 | HandleScope scope; 171 | if (args.Length() < 1) { 172 | return ThrowException(Exception::Error(String::New("No index specified"))); 173 | } else if (!args[0]->IsInt32()) { 174 | return ThrowException(Exception::Error(String::New("Argument must be integer"))); 175 | } 176 | int32_t index = args[0]->Int32Value(); 177 | Handle self = args.This(); 178 | void* ptr = self->GetPointerFromInternalField(0); 179 | const HeapGraphPath* path = static_cast(ptr)->GetRetainingPath(index); 180 | return scope.Close(GraphPath::New(path)); 181 | } 182 | 183 | Handle GraphNode::GetDominator(Local property, const AccessorInfo& info) { 184 | HandleScope scope; 185 | Local self = info.Holder(); 186 | void* ptr = self->GetPointerFromInternalField(0); 187 | const HeapGraphNode* node = static_cast(ptr)->GetDominatorNode(); 188 | return scope.Close(GraphNode::New(node)); 189 | } 190 | 191 | Handle GraphNode::New(const HeapGraphNode* node) { 192 | HandleScope scope; 193 | 194 | if (node_template_.IsEmpty()) { 195 | GraphNode::Initialize(); 196 | } 197 | 198 | if(!node) { 199 | return Undefined(); 200 | } 201 | else { 202 | Local obj = node_template_->NewInstance(); 203 | obj->SetPointerInInternalField(0, const_cast(node)); 204 | return scope.Close(obj); 205 | } 206 | } 207 | } --------------------------------------------------------------------------------