├── .gitattributes ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── appveyor.yml ├── binding.gyp ├── package.json ├── readme.md ├── src ├── cpu_profile.cc ├── cpu_profile.h ├── cpu_profile_node.cc ├── cpu_profile_node.h ├── cpu_profiler.cc ├── cpu_profiler.h ├── heap_graph_edge.cc ├── heap_graph_edge.h ├── heap_graph_node.cc ├── heap_graph_node.h ├── heap_output_stream.cc ├── heap_output_stream.h ├── heap_profiler.cc ├── heap_profiler.h ├── heap_snapshot.cc ├── heap_snapshot.h └── profiler.cc ├── test ├── binding.js ├── mocha.opts └── v8-profiler.js ├── tools ├── NODE_NEXT.js ├── annotate-tag.js ├── commit-changes.js ├── exec.js ├── history.js ├── prepublish-to-npm.js ├── publish-to-npm.js ├── push-to-githab.js ├── release.js ├── update-changelog.js └── update-npm-version.js └── v8-profiler.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | node_modules/* 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | build/!(profiler) 3 | node_modules 4 | test 5 | v8-profiler-*.tgz 6 | .gitattributes 7 | .gitignore 8 | .npmignore 9 | .travis.yml 10 | appveyor.yml 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: cpp 3 | compiler: gcc 4 | 5 | os: 6 | - linux 7 | - osx 8 | 9 | addons: 10 | apt: 11 | sources: 12 | - ubuntu-toolchain-r-test 13 | packages: 14 | - gcc-4.9 15 | - g++-4.9 16 | 17 | env: 18 | matrix: 19 | - export NODE_VERSION="0.10" 20 | - export NODE_VERSION="0.12" 21 | - export NODE_VERSION="4" 22 | - export NODE_VERSION="5" 23 | - export NODE_VERSION="6" 24 | - export NODE_VERSION="7" 25 | global: 26 | - node_pre_gyp_region="eu-central-1" 27 | 28 | git: 29 | depth: 1 30 | 31 | before_install: 32 | - if [ $TRAVIS_OS_NAME == "linux" ]; then 33 | export CC=/usr/bin/gcc-4.9; 34 | export CXX=/usr/bin/g++-4.9; 35 | fi 36 | 37 | - rm -rf ~/.nvm/ && git clone --depth 1 "https://github.com/creationix/nvm.git" ~/.nvm 38 | - source ~/.nvm/nvm.sh 39 | - nvm install $NODE_VERSION 40 | - nvm use $NODE_VERSION 41 | 42 | - if [ $NODE_VERSION == "0.10" ]; then 43 | npm -g install npm@latest-2; 44 | fi 45 | 46 | - export PATH="./node_modules/.bin/:$PATH" 47 | 48 | - PUBLISH_BINARY=false 49 | - if [[ $TRAVIS_TAG ]]; then PUBLISH_BINARY=true; fi; 50 | 51 | - platform=$(uname -s | sed "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/") 52 | 53 | install: 54 | - npm install --build-from-source 55 | - npm test 56 | 57 | before_script: 58 | - if [[ $PUBLISH_BINARY == true ]]; then node-pre-gyp package testpackage publish --verbose; fi; 59 | 60 | script: 61 | - INSTALL_RESULT=0 62 | - if [[ $PUBLISH_BINARY == true ]]; then 63 | INSTALL_RESULT=$(npm install --fallback-to-build=false > /dev/null)$? || true; 64 | fi; 65 | - if [[ $INSTALL_RESULT != 0 ]]; then node-pre-gyp unpublish; false; fi 66 | - node-pre-gyp clean 67 | 68 | after_success: 69 | - node-pre-gyp info 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Danny Coates 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 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. 8 | 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 HOLDER 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. 9 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | node_pre_gyp_accessKeyId: 3 | secure: vioM9wfAwkcAV2Btx0T6sdI+NxwnbSJLm00V49KzLJY= 4 | node_pre_gyp_secretAccessKey: 5 | secure: jJxKoyWputzRz2EjAT9i/vBzYt2+lcjKZ5D6O5TBaS9+anpYHn2XWbOut5dkOgh0 6 | node_pre_gyp_region: eu-central-1 7 | matrix: 8 | - NODE_VERSION: 7 9 | platform: x64 10 | - NODE_VERSION: 7 11 | platform: x86 12 | - NODE_VERSION: 6 13 | platform: x64 14 | - NODE_VERSION: 6 15 | platform: x86 16 | - NODE_VERSION: 5 17 | platform: x64 18 | - NODE_VERSION: 5 19 | platform: x86 20 | - NODE_VERSION: 4 21 | platform: x64 22 | - NODE_VERSION: 4 23 | platform: x86 24 | - NODE_VERSION: 0.12 25 | platform: x64 26 | - NODE_VERSION: 0.12 27 | platform: x86 28 | - NODE_VERSION: 0.10 29 | platform: x64 30 | - NODE_VERSION: 0.10 31 | platform: x86 32 | 33 | os: unstable 34 | 35 | install: 36 | - SET PATH=%cd%\node_modules\.bin\;%PATH% 37 | 38 | - ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:NODE_VERSION) $env:platform 39 | 40 | - IF %NODE_VERSION% LSS 1 npm -g install npm 41 | - IF %NODE_VERSION% LSS 1 set PATH=%APPDATA%\npm;%PATH% 42 | # work around an issue with node-gyp v3.3.1 and node 4x 43 | # package.json has no certificates in it so we're cool 44 | # https://github.com/nodejs/node-gyp/issues/921 45 | - IF %NODE_VERSION% == 4 npm config set -g cafile=package.json 46 | - IF %NODE_VERSION% == 4 npm config set -g strict-ssl=false 47 | 48 | # Check if new tag released and publish binary in this case. 49 | - SET PUBLISH_BINARY=%APPVEYOR_REPO_TAG% 50 | 51 | build_script: 52 | - npm install --build-from-source --msvs_version=2013 53 | 54 | test_script: 55 | - npm test 56 | 57 | on_success: 58 | - IF %PUBLISH_BINARY% == true (node-pre-gyp package publish 2>&1) 59 | - IF %PUBLISH_BINARY% == true (node-pre-gyp clean) 60 | - IF %PUBLISH_BINARY% == true (npm install --fallback-to-build=false) 61 | - IF %PUBLISH_BINARY% == true (node-pre-gyp info) 62 | 63 | deploy: OFF 64 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'profiler', 5 | 'win_delay_load_hook': 'false', 6 | 'sources': [ 7 | 'src/profiler.cc', 8 | 'src/cpu_profiler.cc', 9 | 'src/cpu_profile.cc', 10 | 'src/cpu_profile_node.cc', 11 | 'src/heap_profiler.cc', 12 | 'src/heap_snapshot.cc', 13 | 'src/heap_output_stream.cc', 14 | 'src/heap_graph_node.cc', 15 | 'src/heap_graph_edge.cc' 16 | ], 17 | 'include_dirs' : [ 18 | "", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/node-inspector/v8-profiler.git" 10 | }, 11 | "contributors": [ 12 | { 13 | "name": "Miroslav Bajtoš" 14 | }, 15 | { 16 | "name": "3y3", 17 | "email": "3y3@bk.ru" 18 | } 19 | ], 20 | "license": "BSD-2-Clause", 21 | "binary": { 22 | "module_name": "profiler", 23 | "module_path": "./build/{module_name}/v{version}/{node_abi}-{platform}-{arch}/", 24 | "remote_path": "./{module_name}/v{version}/", 25 | "package_name": "{node_abi}-{platform}-{arch}.tar.gz", 26 | "host": "https://node-inspector.s3.amazonaws.com/" 27 | }, 28 | "keywords": [ 29 | "profiler", 30 | "inspector" 31 | ], 32 | "engines": { 33 | "node": ">=0.10" 34 | }, 35 | "main": "v8-profiler", 36 | "dependencies": { 37 | "nan": "^2.5.1", 38 | "node-pre-gyp": "^0.6.34" 39 | }, 40 | "devDependencies": { 41 | "aws-sdk": "^2.0.0", 42 | "chai": "^1.9.1", 43 | "co": "^4.6.0", 44 | "mocha": "^1.20.1", 45 | "rimraf": "^2.4.4" 46 | }, 47 | "scripts": { 48 | "preinstall": "node -e 'process.exit(0)'", 49 | "install": "node-pre-gyp install --fallback-to-build", 50 | "rebuild": "node-pre-gyp rebuild", 51 | "release": "node ./tools/release.js $@", 52 | "test": "mocha --debug" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://secure.travis-ci.org/node-inspector/v8-profiler.png?branch=master)](http://travis-ci.org/node-inspector/v8-profiler) 2 | [![Build status](https://ci.appveyor.com/api/projects/status/hhgloy5smkl5i8fd/branch/master?svg=true)](https://ci.appveyor.com/project/3y3/v8-profiler/branch/master) 3 | [![npm version](https://badge.fury.io/js/v8-profiler.svg)](http://badge.fury.io/js/v8-profiler) 4 | 5 | v8-profiler provides [node](http://github.com/ry/node) bindings for the v8 6 | profiler and integration with [node-inspector](http://github.com/dannycoates/node-inspector) 7 | 8 | ## Installation 9 | ```sh 10 | npm install v8-profiler 11 | ``` 12 | ## Usage 13 | ```js 14 | var profiler = require('v8-profiler'); 15 | ``` 16 | ## API 17 | `takeSnapshot([name])` - returns new HEAP Snapshot instance. `name` is optional argument, by default snapshot name will be constructed from his uid. 18 | 19 | `deleteAllSnapshots()` - works as described in name. 20 | 21 | ```js 22 | var snapshot1 = profiler.takeSnapshot('1'); 23 | var snapshot2 = profiler.takeSnapshot(); 24 | profiler.deleteAllSnapshots(); 25 | ``` 26 | 27 | `startProfiling([name], [recsamples])` - start CPU profiling. `name` is optional argument, by default profile name will be constructed from his uid. `recsamples` is true by default. 28 | 29 | `stopProfiling([name])` - returns new CPU Profile instance. There is no strictly described behavior for usage without `name` argument. 30 | 31 | `setSamplingInterval([num])` - Changes default CPU profiler sampling interval to the specified number of microseconds. Default interval is 1000us. This method must be called when there are no profiles being recorded. If called without arguments it resets interval to default. 32 | 33 | `deleteAllProfiles()` - works as described in name. 34 | 35 | ```js 36 | profiler.startProfiling('', true); 37 | setTimeout(function() { 38 | var profile = profiler.stopProfiling(''); 39 | profiler.deleteAllProfiles(); 40 | }, 1000); 41 | ``` 42 | 43 | ### HEAP Snapshot API 44 | `Snapshot.getHeader()` - provides short information about snapshot. 45 | 46 | `Snapshot.compare(snapshot)` - creates HEAP diff for two snapshots. 47 | 48 | `Snapshot.delete()` - removes snapshot from memory. 49 | 50 | `Snapshot.export([callback])` - provides simple export API for snapshot. `callback(error, data)` receives serialized snapshot as second argument. (Serialization is not equal to `JSON.stringify` result). 51 | 52 | If callback will not be passed, `export` returns transform stream. 53 | 54 | `Snapshot.serialize` - low level serialization method. Look `Snapshot.export` source for usage example. 55 | 56 | ```js 57 | var fs = require('fs'); 58 | var profiler = require('v8-profiler'); 59 | var snapshot1 = profiler.takeSnapshot(); 60 | var snapshot2 = profiler.takeSnapshot(); 61 | 62 | console.log(snapshot1.getHeader(), snapshot2.getHeader()); 63 | 64 | console.log(snapshot1.compare(snapshot2)); 65 | 66 | // Export snapshot to file file 67 | snapshot1.export(function(error, result) { 68 | fs.writeFileSync('snapshot1.json', result); 69 | snapshot1.delete(); 70 | }); 71 | 72 | // Export snapshot to file stream 73 | snapshot2.export() 74 | .pipe(fs.createWriteStream('snapshot2.json')) 75 | .on('finish', snapshot2.delete); 76 | ``` 77 | 78 | ## CPU Profile API 79 | `Profile.getHeader()` - provides short information about profile. 80 | 81 | `Profile.delete()` - removes profile from memory. 82 | 83 | `Profile.export([callback])` - provides simple export API for profile. `callback(error, data)` receives serialized profile as second argument. (Serialization is equal to `JSON.stringify` result). 84 | 85 | ```js 86 | var fs = require('fs'); 87 | var profiler = require('v8-profiler'); 88 | profiler.startProfiling('1', true); 89 | var profile1 = profiler.stopProfiling(); 90 | profiler.startProfiling('2', true); 91 | var profile2 = profiler.stopProfiling(); 92 | 93 | console.log(snapshot1.getHeader(), snapshot2.getHeader()); 94 | 95 | profile1.export(function(error, result) { 96 | fs.writeFileSync('profile1.json', result); 97 | profile1.delete(); 98 | }); 99 | 100 | profile2.export() 101 | .pipe(fs.createWriteStream('profile2.json')) 102 | .on('finish', function() { 103 | profile2.delete(); 104 | }); 105 | ``` 106 | 107 | ## node-inspector 108 | 109 | Cpu profiles can be viewed and heap snapshots may be taken and viewed from the 110 | profiles panel. 111 | -------------------------------------------------------------------------------- /src/cpu_profile.cc: -------------------------------------------------------------------------------- 1 | #include "cpu_profile.h" 2 | #include "cpu_profile_node.h" 3 | 4 | namespace nodex { 5 | using v8::Array; 6 | using v8::CpuProfile; 7 | using v8::CpuProfileNode; 8 | using v8::Handle; 9 | using v8::Number; 10 | using v8::Integer; 11 | using v8::Local; 12 | using v8::Object; 13 | using v8::ObjectTemplate; 14 | using v8::FunctionTemplate; 15 | using v8::String; 16 | using v8::Function; 17 | using v8::Value; 18 | 19 | Nan::Persistent Profile::profile_template_; 20 | Nan::Persistent Profile::profiles; 21 | uint32_t Profile::uid_counter = 0; 22 | 23 | NAN_METHOD(Profile_EmptyMethod) { 24 | } 25 | 26 | void Profile::Initialize () { 27 | Nan::HandleScope scope; 28 | 29 | Local f = Nan::New(Profile_EmptyMethod); 30 | Local o = f->InstanceTemplate(); 31 | o->SetInternalFieldCount(1); 32 | Nan::SetMethod(o, "delete", Profile::Delete); 33 | profile_template_.Reset(o); 34 | } 35 | 36 | NAN_METHOD(Profile::Delete) { 37 | Local self = info.This(); 38 | void* ptr = Nan::GetInternalFieldPointer(self, 0); 39 | Local profiles = Nan::New(Profile::profiles); 40 | Local _uid = info.This()->Get(Nan::New("uid").ToLocalChecked()); 41 | Local uid = Nan::To(_uid).ToLocalChecked(); 42 | profiles->Delete(uid); 43 | static_cast(ptr)->Delete(); 44 | } 45 | 46 | Local Profile::New (const CpuProfile* node) { 47 | Nan::EscapableHandleScope scope; 48 | 49 | if (profile_template_.IsEmpty()) { 50 | Profile::Initialize(); 51 | } 52 | 53 | uid_counter++; 54 | 55 | Local profile = Nan::New(profile_template_)->NewInstance(); 56 | Nan::SetInternalFieldPointer(profile, 0, const_cast(node)); 57 | 58 | const uint32_t uid_length = (((sizeof uid_counter) * 8) + 2)/3 + 2; 59 | char _uid[uid_length]; 60 | sprintf(_uid, "%d", uid_counter); 61 | 62 | Local CPU = Nan::New("CPU").ToLocalChecked(); 63 | Local uid = Nan::New(_uid).ToLocalChecked(); 64 | #if (NODE_MODULE_VERSION >= 45) 65 | Local title = node->GetTitle(); 66 | #else 67 | Local title = Nan::New(node->GetTitle()); 68 | #endif 69 | if (!title->Length()) { 70 | char _title[8 + uid_length]; 71 | sprintf(_title, "Profile %i", uid_counter); 72 | title = Nan::New(_title).ToLocalChecked(); 73 | } 74 | Local head = ProfileNode::New(node->GetTopDownRoot()); 75 | 76 | profile->Set(Nan::New("typeId").ToLocalChecked(), CPU); 77 | profile->Set(Nan::New("uid").ToLocalChecked(), uid); 78 | profile->Set(Nan::New("title").ToLocalChecked(), title); 79 | profile->Set(Nan::New("head").ToLocalChecked(), head); 80 | 81 | #if (NODE_MODULE_VERSION > 0x000B) 82 | Local start_time = Nan::New(node->GetStartTime()/1000000); 83 | Local end_time = Nan::New(node->GetEndTime()/1000000); 84 | Local samples = Nan::New(); 85 | Local timestamps = Nan::New(); 86 | 87 | uint32_t count = node->GetSamplesCount(); 88 | for (uint32_t index = 0; index < count; ++index) { 89 | samples->Set(index, Nan::New(node->GetSample(index)->GetNodeId())); 90 | timestamps->Set(index, Nan::New(static_cast(node->GetSampleTimestamp(index)))); 91 | } 92 | 93 | profile->Set(Nan::New("startTime").ToLocalChecked(), start_time); 94 | profile->Set(Nan::New("endTime").ToLocalChecked(), end_time); 95 | profile->Set(Nan::New("samples").ToLocalChecked(), samples); 96 | profile->Set(Nan::New("timestamps").ToLocalChecked(), timestamps); 97 | #endif 98 | 99 | Local profiles = Nan::New(Profile::profiles); 100 | profiles->Set(uid, profile); 101 | 102 | return scope.Escape(profile); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/cpu_profile.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_PROFILE_ 2 | #define NODE_PROFILE_ 3 | 4 | #include "v8-profiler.h" 5 | #include "nan.h" 6 | 7 | namespace nodex { 8 | 9 | class Profile { 10 | public: 11 | static v8::Local New(const v8::CpuProfile* node); 12 | static Nan::Persistent profiles; 13 | private: 14 | static NAN_METHOD(Delete); 15 | static void Initialize(); 16 | static Nan::Persistent profile_template_; 17 | static uint32_t uid_counter; 18 | }; 19 | 20 | } //namespace nodex 21 | #endif // NODE_PROFILE_ 22 | -------------------------------------------------------------------------------- /src/cpu_profile_node.cc: -------------------------------------------------------------------------------- 1 | #include "cpu_profile_node.h" 2 | 3 | namespace nodex { 4 | using v8::CpuProfileNode; 5 | using v8::Handle; 6 | using v8::String; 7 | using v8::Number; 8 | using v8::Integer; 9 | using v8::Value; 10 | using v8::Local; 11 | using v8::Object; 12 | using v8::Array; 13 | 14 | uint32_t ProfileNode::UIDCounter = 1; 15 | 16 | #if (NODE_MODULE_VERSION >= 42) 17 | Local ProfileNode::GetLineTicks_(const CpuProfileNode* node) { 18 | Nan::EscapableHandleScope scope; 19 | 20 | uint32_t count = node->GetHitLineCount(); 21 | v8::CpuProfileNode::LineTick *entries = new v8::CpuProfileNode::LineTick[count]; 22 | bool result = node->GetLineTicks(entries, count); 23 | 24 | Local lineTicks; 25 | if (result) { 26 | Local array = Nan::New(count); 27 | for (uint32_t index = 0; index < count; index++) { 28 | Local tick = Nan::New(); 29 | tick->Set(Nan::New("line").ToLocalChecked(), Nan::New(entries[index].line)); 30 | tick->Set(Nan::New("hitCount").ToLocalChecked(), Nan::New(entries[index].hit_count)); 31 | array->Set(index, tick); 32 | } 33 | lineTicks = array; 34 | } else { 35 | lineTicks = Nan::Null(); 36 | } 37 | 38 | delete[] entries; 39 | return scope.Escape(lineTicks); 40 | } 41 | #endif 42 | 43 | Local ProfileNode::New (const CpuProfileNode* node) { 44 | Nan::EscapableHandleScope scope; 45 | 46 | int32_t count = node->GetChildrenCount(); 47 | Local profile_node = Nan::New(); 48 | Local children = Nan::New(count); 49 | 50 | for (int32_t index = 0; index < count; index++) { 51 | children->Set(index, ProfileNode::New(node->GetChild(index))); 52 | } 53 | 54 | profile_node->Set(Nan::New("functionName").ToLocalChecked(), node->GetFunctionName()); 55 | profile_node->Set(Nan::New("url").ToLocalChecked(), node->GetScriptResourceName()); 56 | profile_node->Set(Nan::New("lineNumber").ToLocalChecked(), Nan::New(node->GetLineNumber())); 57 | profile_node->Set(Nan::New("callUID").ToLocalChecked(), Nan::New(node->GetCallUid())); 58 | #if (NODE_MODULE_VERSION > 0x000B) 59 | profile_node->Set(Nan::New("bailoutReason").ToLocalChecked(), Nan::New(node->GetBailoutReason()).ToLocalChecked()); 60 | profile_node->Set(Nan::New("id").ToLocalChecked(), Nan::New(node->GetNodeId())); 61 | profile_node->Set(Nan::New("scriptId").ToLocalChecked(), Nan::New(node->GetScriptId())); 62 | profile_node->Set(Nan::New("hitCount").ToLocalChecked(), Nan::New(node->GetHitCount())); 63 | #else 64 | profile_node->Set(Nan::New("bailoutReason").ToLocalChecked(), Nan::New("no reason").ToLocalChecked()); 65 | profile_node->Set(Nan::New("id").ToLocalChecked(), Nan::New(UIDCounter++)); 66 | //TODO(3y3): profile_node->Set(Nan::New("scriptId").ToLocalChecked(), Nan::New(node->GetScriptId())); 67 | profile_node->Set(Nan::New("hitCount").ToLocalChecked(), Nan::New(static_cast(node->GetSelfSamplesCount()))); 68 | #endif 69 | profile_node->Set(Nan::New("children").ToLocalChecked(), children); 70 | 71 | #if (NODE_MODULE_VERSION >= 42) 72 | Local lineTicks = GetLineTicks_(node); 73 | if (!lineTicks->IsNull()) { 74 | profile_node->Set(Nan::New("lineTicks").ToLocalChecked(), lineTicks); 75 | } 76 | #endif 77 | 78 | return scope.Escape(profile_node); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/cpu_profile_node.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_PROFILE_NODE_ 2 | #define NODE_PROFILE_NODE_ 3 | 4 | #include "v8-profiler.h" 5 | #include "nan.h" 6 | 7 | namespace nodex { 8 | 9 | class ProfileNode { 10 | public: 11 | static v8::Local New(const v8::CpuProfileNode* node); 12 | static uint32_t UIDCounter; 13 | 14 | private: 15 | #if (NODE_MODULE_VERSION >= 42) 16 | static v8::Local GetLineTicks_(const v8::CpuProfileNode* node); 17 | #endif 18 | }; 19 | 20 | } 21 | #endif // NODE_PROFILE_NODE_ 22 | -------------------------------------------------------------------------------- /src/cpu_profiler.cc: -------------------------------------------------------------------------------- 1 | #include "cpu_profiler.h" 2 | #include "cpu_profile.h" 3 | 4 | namespace nodex { 5 | using v8::CpuProfile; 6 | using v8::Handle; 7 | using v8::Local; 8 | using v8::Object; 9 | using v8::Array; 10 | using v8::String; 11 | 12 | CpuProfiler::CpuProfiler () {} 13 | CpuProfiler::~CpuProfiler () {} 14 | 15 | void CpuProfiler::Initialize (Local target) { 16 | Nan::HandleScope scope; 17 | 18 | Local cpuProfiler = Nan::New(); 19 | Local profiles = Nan::New(); 20 | 21 | Nan::SetMethod(cpuProfiler, "startProfiling", CpuProfiler::StartProfiling); 22 | Nan::SetMethod(cpuProfiler, "stopProfiling", CpuProfiler::StopProfiling); 23 | Nan::SetMethod(cpuProfiler, "setSamplingInterval", CpuProfiler::SetSamplingInterval); 24 | cpuProfiler->Set(Nan::New("profiles").ToLocalChecked(), profiles); 25 | 26 | Profile::profiles.Reset(profiles); 27 | target->Set(Nan::New("cpu").ToLocalChecked(), cpuProfiler); 28 | } 29 | 30 | NAN_METHOD(CpuProfiler::StartProfiling) { 31 | Local title = info[0]->ToString(); 32 | 33 | #if (NODE_MODULE_VERSION > 0x000B) 34 | bool recsamples = info[1]->ToBoolean()->Value(); 35 | v8::Isolate::GetCurrent()->GetCpuProfiler()->StartProfiling(title, recsamples); 36 | #else 37 | v8::CpuProfiler::StartProfiling(title); 38 | #endif 39 | } 40 | 41 | NAN_METHOD(CpuProfiler::StopProfiling) { 42 | const CpuProfile* profile; 43 | 44 | Local title = Nan::EmptyString(); 45 | if (info.Length()) { 46 | if (info[0]->IsString()) { 47 | title = info[0]->ToString(); 48 | } else if (!info[0]->IsUndefined()) { 49 | return Nan::ThrowTypeError("Wrong argument [0] type (wait String)"); 50 | } 51 | } 52 | 53 | #if (NODE_MODULE_VERSION > 0x000B) 54 | profile = v8::Isolate::GetCurrent()->GetCpuProfiler()->StopProfiling(title); 55 | #else 56 | profile = v8::CpuProfiler::StopProfiling(title); 57 | #endif 58 | 59 | info.GetReturnValue().Set(Profile::New(profile)); 60 | } 61 | 62 | NAN_METHOD(CpuProfiler::SetSamplingInterval) { 63 | #if (NODE_MODULE_VERSION > 0x000B) 64 | v8::Isolate::GetCurrent()->GetCpuProfiler()->SetSamplingInterval(info[0]->Uint32Value()); 65 | #endif 66 | } 67 | } //namespace nodex 68 | -------------------------------------------------------------------------------- /src/cpu_profiler.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_CPU_PROFILER_ 2 | #define NODE_CPU_PROFILER_ 3 | 4 | #include "v8-profiler.h" 5 | #include "node.h" 6 | #include "nan.h" 7 | 8 | namespace nodex { 9 | class CpuProfiler { 10 | public: 11 | static void Initialize(v8::Local target); 12 | 13 | CpuProfiler(); 14 | virtual ~CpuProfiler(); 15 | 16 | protected: 17 | static NAN_METHOD(StartProfiling); 18 | static NAN_METHOD(StopProfiling); 19 | static NAN_METHOD(SetSamplingInterval); 20 | }; 21 | } //namespace nodex 22 | 23 | #endif // NODE_CPU_PROFILER_H 24 | -------------------------------------------------------------------------------- /src/heap_graph_edge.cc: -------------------------------------------------------------------------------- 1 | #include "heap_graph_node.h" 2 | #include "heap_graph_edge.h" 3 | 4 | namespace nodex { 5 | using v8::Handle; 6 | using v8::HeapGraphEdge; 7 | using v8::Integer; 8 | using v8::Local; 9 | using v8::Object; 10 | using v8::String; 11 | using v8::Value; 12 | 13 | Local GraphEdge::New(const HeapGraphEdge* node) { 14 | Nan::EscapableHandleScope scope; 15 | 16 | Local graph_edge = Nan::New(); 17 | 18 | Local type; 19 | switch (node->GetType()) { 20 | case HeapGraphEdge::kContextVariable : 21 | type = Nan::New("ContextVariable").ToLocalChecked(); 22 | break; 23 | case HeapGraphEdge::kElement : 24 | type = Nan::New("Element").ToLocalChecked(); 25 | break; 26 | case HeapGraphEdge::kProperty : 27 | type = Nan::New("Property").ToLocalChecked(); 28 | break; 29 | case HeapGraphEdge::kInternal : 30 | type = Nan::New("Internal").ToLocalChecked(); 31 | break; 32 | case HeapGraphEdge::kHidden : 33 | type = Nan::New("Hidden").ToLocalChecked(); 34 | break; 35 | case HeapGraphEdge::kShortcut : 36 | type = Nan::New("Shortcut").ToLocalChecked(); 37 | break; 38 | case HeapGraphEdge::kWeak : 39 | type = Nan::New("Weak").ToLocalChecked(); 40 | break; 41 | default : 42 | type = Nan::New("Undefined").ToLocalChecked(); 43 | } 44 | #if (NODE_MODULE_VERSION >= 45) 45 | Local name = node->GetName(); 46 | #else 47 | Local name = Nan::New(node->GetName()); 48 | #endif 49 | Local from = GraphNode::New(node->GetFromNode()); 50 | Local to = GraphNode::New(node->GetToNode()); 51 | 52 | graph_edge->Set(Nan::New("type").ToLocalChecked(), type); 53 | graph_edge->Set(Nan::New("name").ToLocalChecked(), name); 54 | graph_edge->Set(Nan::New("from").ToLocalChecked(), from); 55 | graph_edge->Set(Nan::New("to").ToLocalChecked(), to); 56 | 57 | return scope.Escape(graph_edge); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/heap_graph_edge.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_GRAPH_EDGE_ 2 | #define NODE_GRAPH_EDGE_ 3 | 4 | #include "v8-profiler.h" 5 | #include "nan.h" 6 | 7 | namespace nodex { 8 | 9 | class GraphEdge { 10 | public: 11 | static v8::Local New(const v8::HeapGraphEdge* node); 12 | }; 13 | } //namespace nodex 14 | #endif // NODE_GRAPH_EDGE_ 15 | -------------------------------------------------------------------------------- /src/heap_graph_node.cc: -------------------------------------------------------------------------------- 1 | #include "heap_graph_node.h" 2 | #include "heap_graph_edge.h" 3 | 4 | namespace nodex { 5 | using v8::Array; 6 | using v8::Handle; 7 | using v8::HeapGraphNode; 8 | using v8::Number; 9 | using v8::Local; 10 | using v8::Object; 11 | using v8::ObjectTemplate; 12 | using v8::FunctionTemplate; 13 | using v8::String; 14 | using v8::Value; 15 | 16 | 17 | Nan::Persistent GraphNode::graph_node_template_; 18 | Nan::Persistent GraphNode::graph_node_cache; 19 | 20 | NAN_METHOD(GraphNode_EmptyMethod) { 21 | } 22 | 23 | void GraphNode::Initialize () { 24 | Nan::HandleScope scope; 25 | 26 | Local f = Nan::New(GraphNode_EmptyMethod); 27 | Local o = f->InstanceTemplate(); 28 | Local _cache = Nan::New(); 29 | o->SetInternalFieldCount(1); 30 | #if (NODE_MODULE_VERSION <= 0x000B) 31 | Nan::SetMethod(o, "getHeapValue", GraphNode::GetHeapValue); 32 | #endif 33 | Nan::SetAccessor(o, Nan::New("children").ToLocalChecked(), GraphNode::GetChildren); 34 | graph_node_template_.Reset(o); 35 | graph_node_cache.Reset(_cache); 36 | } 37 | 38 | #if (NODE_MODULE_VERSION <= 0x000B) 39 | NAN_METHOD(GraphNode::GetHeapValue) { 40 | void* ptr = Nan::GetInternalFieldPointer(info.This(), 0); 41 | HeapGraphNode* node = static_cast(ptr); 42 | info.GetReturnValue().Set(Nan::New(node->GetHeapValue())); 43 | } 44 | #endif 45 | 46 | NAN_GETTER(GraphNode::GetChildren) { 47 | void* ptr = Nan::GetInternalFieldPointer(info.This(), 0); 48 | HeapGraphNode* node = static_cast(ptr); 49 | uint32_t count = node->GetChildrenCount(); 50 | Local children = Nan::New(count); 51 | for (uint32_t index = 0; index < count; ++index) { 52 | Local child = GraphEdge::New(node->GetChild(index)); 53 | children->Set(index, child); 54 | } 55 | info.This()->Set(Nan::New("children").ToLocalChecked(), children); 56 | info.GetReturnValue().Set(children); 57 | } 58 | 59 | Local GraphNode::New(const HeapGraphNode* node) { 60 | Nan::EscapableHandleScope scope; 61 | 62 | if (graph_node_template_.IsEmpty()) { 63 | GraphNode::Initialize(); 64 | } 65 | 66 | Local graph_node; 67 | Local _cache = Nan::New(graph_node_cache); 68 | int32_t _id = node->GetId(); 69 | if (_cache->Has(_id)) { 70 | graph_node = _cache->Get(_id)->ToObject(); 71 | } else { 72 | graph_node = Nan::New(graph_node_template_)->NewInstance(); 73 | Nan::SetInternalFieldPointer(graph_node, 0, const_cast(node)); 74 | 75 | Local type; 76 | switch (node->GetType()) { 77 | case HeapGraphNode::kArray : 78 | type = Nan::New("Array").ToLocalChecked(); 79 | break; 80 | case HeapGraphNode::kString : 81 | type = Nan::New("String").ToLocalChecked(); 82 | break; 83 | case HeapGraphNode::kObject : 84 | type = Nan::New("Object").ToLocalChecked(); 85 | break; 86 | case HeapGraphNode::kCode : 87 | type = Nan::New("Code").ToLocalChecked(); 88 | break; 89 | case HeapGraphNode::kClosure : 90 | type = Nan::New("Closure").ToLocalChecked(); 91 | break; 92 | case HeapGraphNode::kRegExp : 93 | type = Nan::New("RegExp").ToLocalChecked(); 94 | break; 95 | case HeapGraphNode::kHeapNumber : 96 | type = Nan::New("HeapNumber").ToLocalChecked(); 97 | break; 98 | case HeapGraphNode::kNative : 99 | type = Nan::New("Native").ToLocalChecked(); 100 | break; 101 | case HeapGraphNode::kSynthetic : 102 | type = Nan::New("Synthetic").ToLocalChecked(); 103 | break; 104 | #if (NODE_MODULE_VERSION > 0x000B) 105 | case HeapGraphNode::kConsString : 106 | type = Nan::New("ConsString").ToLocalChecked(); 107 | break; 108 | case HeapGraphNode::kSlicedString : 109 | type = Nan::New("SlicedString").ToLocalChecked(); 110 | break; 111 | #endif 112 | default : 113 | type = Nan::New("Hidden").ToLocalChecked(); 114 | } 115 | #if (NODE_MODULE_VERSION >= 45) 116 | Local name = node->GetName(); 117 | #else 118 | Local name = Nan::New(node->GetName()); 119 | #endif 120 | Local id = Nan::New(_id); 121 | graph_node->Set(Nan::New("type").ToLocalChecked(), type); 122 | graph_node->Set(Nan::New("name").ToLocalChecked(), name); 123 | graph_node->Set(Nan::New("id").ToLocalChecked(), id); 124 | 125 | #if (NODE_MODULE_VERSION > 0x000B) 126 | Local shallowSize = Nan::New(node->GetShallowSize()); 127 | graph_node->Set(Nan::New("shallowSize").ToLocalChecked(), shallowSize); 128 | #endif 129 | 130 | _cache->Set(_id, graph_node); 131 | } 132 | 133 | return scope.Escape(graph_node); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/heap_graph_node.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_GRAPH_NODE_ 2 | #define NODE_GRAPH_NODE_ 3 | 4 | #include "v8-profiler.h" 5 | #include "nan.h" 6 | 7 | namespace nodex { 8 | 9 | class GraphNode { 10 | public: 11 | static v8::Local New(const v8::HeapGraphNode* node); 12 | private: 13 | static void Initialize(); 14 | static NAN_METHOD(GetHeapValue); 15 | static NAN_GETTER(GetChildren); 16 | static Nan::Persistent graph_node_template_; 17 | static Nan::Persistent graph_node_cache; 18 | }; 19 | } //namespace nodex 20 | #endif // NODE_GRAPH_NODE_ 21 | -------------------------------------------------------------------------------- /src/heap_output_stream.cc: -------------------------------------------------------------------------------- 1 | #include "heap_output_stream.h" 2 | 3 | namespace nodex { 4 | using v8::Array; 5 | using v8::Handle; 6 | using v8::HeapStatsUpdate; 7 | using v8::Local; 8 | using v8::Value; 9 | using v8::String; 10 | using v8::OutputStream; 11 | using v8::Function; 12 | using v8::TryCatch;; 13 | using v8::Integer; 14 | 15 | void OutputStreamAdapter::EndOfStream() { 16 | Nan::HandleScope scope; 17 | TryCatch try_catch; 18 | callback->Call(Nan::GetCurrentContext()->Global(), 0, NULL); 19 | 20 | if (try_catch.HasCaught()) { 21 | Nan::ThrowError(try_catch.Exception()); 22 | } 23 | } 24 | 25 | int OutputStreamAdapter::GetChunkSize() { 26 | return 51200; 27 | } 28 | 29 | OutputStream::WriteResult OutputStreamAdapter::WriteAsciiChunk(char* data, int size) { 30 | Nan::HandleScope scope; 31 | 32 | Local argv[2] = { 33 | Nan::New(data, size).ToLocalChecked(), 34 | Nan::New(size) 35 | }; 36 | 37 | TryCatch try_catch; 38 | abort = iterator->Call(Nan::GetCurrentContext()->Global(), 2, argv); 39 | 40 | if (try_catch.HasCaught()) { 41 | Nan::ThrowError(try_catch.Exception()); 42 | return kAbort; 43 | } 44 | 45 | return abort->IsFalse() ? kAbort : kContinue; 46 | } 47 | 48 | OutputStream::WriteResult OutputStreamAdapter::WriteHeapStatsChunk(HeapStatsUpdate* data, int count) { 49 | Nan::HandleScope scope; 50 | 51 | Local samples = Nan::New(); 52 | for (int index = 0; index < count; index++) { 53 | int offset = index * 3; 54 | samples->Set(offset, Nan::New(data[index].index)); 55 | samples->Set(offset+1, Nan::New(data[index].count)); 56 | samples->Set(offset+2, Nan::New(data[index].size)); 57 | } 58 | 59 | Local argv[1] = {samples}; 60 | 61 | TryCatch try_catch; 62 | abort = iterator->Call(Nan::GetCurrentContext()->Global(), 1, argv); 63 | 64 | if (try_catch.HasCaught()) { 65 | Nan::ThrowError(try_catch.Exception()); 66 | return kAbort; 67 | } 68 | 69 | return abort->IsFalse() ? kAbort : kContinue; 70 | } 71 | } //namespace nodex 72 | -------------------------------------------------------------------------------- /src/heap_output_stream.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_HEAP_OUTPUT_STREAM_ 2 | #define NODE_HEAP_OUTPUT_STREAM_ 3 | 4 | #include "v8-profiler.h" 5 | #include "nan.h" 6 | 7 | namespace nodex { 8 | 9 | class OutputStreamAdapter : public v8::OutputStream { 10 | public: 11 | OutputStreamAdapter( 12 | v8::Local _iterator, 13 | v8::Local _callback) 14 | : abort(Nan::False()) 15 | , iterator(_iterator) 16 | , callback(_callback) {}; 17 | 18 | void EndOfStream(); 19 | 20 | int GetChunkSize(); 21 | 22 | WriteResult WriteAsciiChunk(char* data, int size); 23 | 24 | WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* data, int count); 25 | private: 26 | v8::Local abort; 27 | v8::Local iterator; 28 | v8::Local callback; 29 | }; 30 | } //namespace nodex 31 | #endif // NODE_PROFILE_ 32 | -------------------------------------------------------------------------------- /src/heap_profiler.cc: -------------------------------------------------------------------------------- 1 | #include "heap_profiler.h" 2 | #include "heap_snapshot.h" 3 | #include "heap_output_stream.h" 4 | 5 | namespace nodex { 6 | using v8::ActivityControl; 7 | using v8::Array; 8 | using v8::Function; 9 | using v8::Handle; 10 | using v8::HeapSnapshot; 11 | using v8::Integer; 12 | using v8::Local; 13 | using v8::Object; 14 | using v8::SnapshotObjectId; 15 | using v8::String; 16 | using v8::TryCatch; 17 | using v8::Value; 18 | 19 | HeapProfiler::HeapProfiler() {} 20 | HeapProfiler::~HeapProfiler() {} 21 | 22 | class ActivityControlAdapter : public ActivityControl { 23 | public: 24 | ActivityControlAdapter(Local progress) 25 | : reportProgress(Local::Cast(progress)), 26 | abort(Nan::False()) 27 | {} 28 | 29 | ControlOption ReportProgressValue(int done, int total) { 30 | Local argv[2] = { 31 | Nan::New(done), 32 | Nan::New(total) 33 | }; 34 | 35 | TryCatch try_catch; 36 | abort = reportProgress->Call(Nan::GetCurrentContext()->Global(), 2, argv); 37 | 38 | if (try_catch.HasCaught()) { 39 | Nan::ThrowError(try_catch.Exception()); 40 | return kAbort; 41 | } 42 | 43 | return abort->IsFalse() ? kAbort : kContinue; 44 | } 45 | 46 | private: 47 | Local reportProgress; 48 | Local abort; 49 | }; 50 | 51 | void HeapProfiler::Initialize (Local target) { 52 | Nan::HandleScope scope; 53 | 54 | Local heapProfiler = Nan::New(); 55 | Local snapshots = Nan::New(); 56 | 57 | Nan::SetMethod(heapProfiler, "takeSnapshot", HeapProfiler::TakeSnapshot); 58 | Nan::SetMethod(heapProfiler, "startTrackingHeapObjects", HeapProfiler::StartTrackingHeapObjects); 59 | Nan::SetMethod(heapProfiler, "stopTrackingHeapObjects", HeapProfiler::StopTrackingHeapObjects); 60 | Nan::SetMethod(heapProfiler, "getHeapStats", HeapProfiler::GetHeapStats); 61 | Nan::SetMethod(heapProfiler, "getObjectByHeapObjectId", HeapProfiler::GetObjectByHeapObjectId); 62 | Nan::SetMethod(heapProfiler, "getHeapObjectId", HeapProfiler::GetHeapObjectId); 63 | heapProfiler->Set(Nan::New("snapshots").ToLocalChecked(), snapshots); 64 | 65 | Snapshot::snapshots.Reset(snapshots); 66 | target->Set(Nan::New("heap").ToLocalChecked(), heapProfiler); 67 | } 68 | 69 | NAN_METHOD(HeapProfiler::TakeSnapshot) { 70 | ActivityControlAdapter* control = new ActivityControlAdapter(info[1]); 71 | #if (NODE_MODULE_VERSION < 0x000F) 72 | Local title = info[0]->ToString(); 73 | #endif 74 | 75 | #if (NODE_MODULE_VERSION > 0x002C) 76 | const HeapSnapshot* snapshot = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(control); 77 | #elif (NODE_MODULE_VERSION > 0x000B) 78 | const HeapSnapshot* snapshot = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(title, control); 79 | #else 80 | const HeapSnapshot* snapshot = v8::HeapProfiler::TakeSnapshot(title, HeapSnapshot::kFull, control); 81 | #endif 82 | 83 | info.GetReturnValue().Set(Snapshot::New(snapshot)); 84 | } 85 | 86 | NAN_METHOD(HeapProfiler::StartTrackingHeapObjects) { 87 | #if (NODE_MODULE_VERSION > 0x000B) 88 | v8::Isolate::GetCurrent()->GetHeapProfiler()->StartTrackingHeapObjects(); 89 | #else 90 | v8::HeapProfiler::StartHeapObjectsTracking(); 91 | #endif 92 | 93 | return; 94 | } 95 | 96 | NAN_METHOD(HeapProfiler::GetHeapObjectId) { 97 | if (info[0].IsEmpty()) return; 98 | 99 | SnapshotObjectId id; 100 | #if (NODE_MODULE_VERSION > 0x000B) 101 | id = v8::Isolate::GetCurrent()->GetHeapProfiler()->GetObjectId(info[0]); 102 | #else 103 | id = v8::HeapProfiler::GetSnapshotObjectId(info[0]); 104 | #endif 105 | 106 | info.GetReturnValue().Set(Nan::New(id)); 107 | } 108 | 109 | NAN_METHOD(HeapProfiler::GetObjectByHeapObjectId) { 110 | SnapshotObjectId id = info[0]->Uint32Value(); 111 | Local object; 112 | #if (NODE_MODULE_VERSION > 0x000B) 113 | object = v8::Isolate::GetCurrent()->GetHeapProfiler()->FindObjectById(id); 114 | #else 115 | Local snapshots = Local::Cast(info.This()->Get(Nan::New("snapshots").ToLocalChecked())); 116 | Local snapshot; 117 | 118 | Local names = Nan::GetOwnPropertyNames(snapshots).ToLocalChecked(); 119 | uint32_t length = names->Length(); 120 | if (length == 0) return; 121 | 122 | for (uint32_t i = 0; i < length; ++i) { 123 | Local name = Nan::Get(names, i).ToLocalChecked(); 124 | uint32_t uid = Nan::To(name).ToLocalChecked()->Value(); 125 | if (uid >= id) { 126 | snapshot = Nan::To(Nan::Get(snapshots, uid).ToLocalChecked()).ToLocalChecked(); 127 | 128 | Local argv[] = { info[0] }; 129 | Local graph_node = Function::Cast(*snapshot->Get(Nan::New("getNodeById").ToLocalChecked())) 130 | ->Call(snapshot, 1, argv)->ToObject(); 131 | object = Function::Cast(*graph_node->Get(Nan::New("getHeapValue").ToLocalChecked())) 132 | ->Call(graph_node, 0, NULL); 133 | break; 134 | } 135 | } 136 | #endif 137 | 138 | if (object.IsEmpty()) { 139 | return; 140 | } else if (object->IsObject() 141 | || object->IsNumber() 142 | || object->IsString() 143 | #if (NODE_MODULE_VERSION > 0x000B) 144 | || object->IsSymbol() 145 | #endif 146 | || object->IsBoolean()) { 147 | info.GetReturnValue().Set(object); 148 | } else { 149 | info.GetReturnValue().Set(Nan::New("Preview is not available").ToLocalChecked()); 150 | } 151 | } 152 | 153 | NAN_METHOD(HeapProfiler::StopTrackingHeapObjects) { 154 | #if (NODE_MODULE_VERSION > 0x000B) 155 | v8::Isolate::GetCurrent()->GetHeapProfiler()->StopTrackingHeapObjects(); 156 | #else 157 | v8::HeapProfiler::StopHeapObjectsTracking(); 158 | #endif 159 | } 160 | 161 | NAN_METHOD(HeapProfiler::GetHeapStats) { 162 | Local iterator = Local::Cast(info[0]); 163 | Local callback = Local::Cast(info[1]); 164 | 165 | OutputStreamAdapter* stream = new OutputStreamAdapter(iterator, callback); 166 | #if (NODE_MODULE_VERSION > 0x000B) 167 | SnapshotObjectId ID = v8::Isolate::GetCurrent()->GetHeapProfiler()->GetHeapStats(stream); 168 | #else 169 | SnapshotObjectId ID = v8::HeapProfiler::PushHeapObjectsStats(stream); 170 | #endif 171 | info.GetReturnValue().Set(Nan::New(ID)); 172 | } 173 | } //namespace nodex 174 | -------------------------------------------------------------------------------- /src/heap_profiler.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_HEAP_PROFILER_ 2 | #define NODE_HEAP_PROFILER_ 3 | 4 | #include "v8-profiler.h" 5 | #include "node.h" 6 | #include "nan.h" 7 | 8 | namespace nodex { 9 | class HeapProfiler { 10 | public: 11 | static void Initialize(v8::Local target); 12 | 13 | HeapProfiler(); 14 | virtual ~HeapProfiler(); 15 | 16 | protected: 17 | static NAN_METHOD(TakeSnapshot); 18 | static NAN_METHOD(StartTrackingHeapObjects); 19 | static NAN_METHOD(StopTrackingHeapObjects); 20 | static NAN_METHOD(GetHeapStats); 21 | static NAN_METHOD(GetObjectByHeapObjectId); 22 | static NAN_METHOD(GetHeapObjectId); 23 | }; 24 | } //namespace nodex 25 | 26 | #endif // NODE_HEAP_PROFILER_H 27 | -------------------------------------------------------------------------------- /src/heap_snapshot.cc: -------------------------------------------------------------------------------- 1 | #include "heap_snapshot.h" 2 | #include "heap_output_stream.h" 3 | #include "heap_graph_node.h" 4 | 5 | namespace nodex { 6 | using v8::Array; 7 | using v8::Handle; 8 | using v8::HeapSnapshot; 9 | using v8::HeapGraphNode; 10 | using v8::HeapGraphEdge; 11 | using v8::Integer; 12 | using v8::Local; 13 | using v8::Object; 14 | using v8::ObjectTemplate; 15 | using v8::FunctionTemplate; 16 | using v8::SnapshotObjectId; 17 | using v8::String; 18 | using v8::Function; 19 | using v8::Value; 20 | 21 | Nan::Persistent Snapshot::snapshot_template_; 22 | Nan::Persistent Snapshot::snapshots; 23 | 24 | NAN_METHOD(Snapshot_EmptyMethod) { 25 | } 26 | 27 | void Snapshot::Initialize () { 28 | Nan::HandleScope scope; 29 | 30 | Local f = Nan::New(Snapshot_EmptyMethod); 31 | Local o = f->InstanceTemplate(); 32 | o->SetInternalFieldCount(1); 33 | Nan::SetAccessor(o, Nan::New("root").ToLocalChecked(), Snapshot::GetRoot); 34 | Nan::SetMethod(o, "getNode", Snapshot::GetNode); 35 | Nan::SetMethod(o, "getNodeById", Snapshot::GetNodeById); 36 | Nan::SetMethod(o, "delete", Snapshot::Delete); 37 | Nan::SetMethod(o, "serialize", Snapshot::Serialize); 38 | snapshot_template_.Reset(o); 39 | } 40 | 41 | NAN_GETTER(Snapshot::GetRoot) { 42 | Local _root; 43 | Local __root = Nan::New("_root").ToLocalChecked(); 44 | if (info.This()->Has(__root)) { 45 | Local root = Nan::GetPrivate(info.This(), __root).ToLocalChecked(); 46 | info.GetReturnValue().Set(root); 47 | } else { 48 | void* ptr = Nan::GetInternalFieldPointer(info.This(), 0); 49 | Local _root = GraphNode::New(static_cast(ptr)->GetRoot()); 50 | Nan::SetPrivate(info.This(), __root, _root); 51 | info.GetReturnValue().Set(_root); 52 | } 53 | } 54 | 55 | NAN_METHOD(Snapshot::GetNode) { 56 | if (!info.Length()) { 57 | return Nan::ThrowError("No index specified"); 58 | } else if (!info[0]->IsInt32()) { 59 | return Nan::ThrowTypeError("Argument must be an integer"); 60 | } 61 | 62 | int32_t index = info[0]->Int32Value(); 63 | void* ptr = Nan::GetInternalFieldPointer(info.This(), 0); 64 | info.GetReturnValue().Set(GraphNode::New(static_cast(ptr)->GetNode(index))); 65 | } 66 | 67 | NAN_METHOD(Snapshot::GetNodeById) { 68 | if (!info.Length()) { 69 | return Nan::ThrowError("No id specified"); 70 | } else if (!info[0]->IsInt32()) { 71 | return Nan::ThrowTypeError("Argument must be an integer"); 72 | } 73 | 74 | SnapshotObjectId id = info[0]->Int32Value(); 75 | void* ptr = Nan::GetInternalFieldPointer(info.This(), 0); 76 | info.GetReturnValue().Set(GraphNode::New(static_cast(ptr)->GetNodeById(id))); 77 | } 78 | 79 | NAN_METHOD(Snapshot::Serialize) { 80 | void* ptr = Nan::GetInternalFieldPointer(info.This(), 0); 81 | if (info.Length() < 2) { 82 | return Nan::ThrowError("Invalid number of arguments"); 83 | } else if (!info[0]->IsFunction() || !info[1]->IsFunction()) { 84 | return Nan::ThrowTypeError("Arguments must be a functions"); 85 | } 86 | 87 | Local iterator = Local::Cast(info[0]); 88 | Local callback = Local::Cast(info[1]); 89 | 90 | OutputStreamAdapter *stream = new OutputStreamAdapter(iterator, callback); 91 | static_cast(ptr)->Serialize(stream, HeapSnapshot::kJSON); 92 | } 93 | 94 | NAN_METHOD(Snapshot::Delete) { 95 | void* ptr = Nan::GetInternalFieldPointer(info.Holder(), 0); 96 | 97 | Local snapshots = Nan::New(Snapshot::snapshots); 98 | 99 | Local __uid = Nan::New("uid").ToLocalChecked(); 100 | Local _uid = Nan::To(Nan::Get(info.Holder(), __uid).ToLocalChecked()).ToLocalChecked(); 101 | Nan::Delete(snapshots, _uid->Value()); 102 | 103 | static_cast(ptr)->Delete(); 104 | info.GetReturnValue().Set(snapshots); 105 | } 106 | 107 | Local Snapshot::New(const HeapSnapshot* node) { 108 | Nan::EscapableHandleScope scope; 109 | 110 | if (snapshot_template_.IsEmpty()) { 111 | Snapshot::Initialize(); 112 | } 113 | 114 | Local snapshot = Nan::New(snapshot_template_)->NewInstance(); 115 | Nan::SetInternalFieldPointer(snapshot, 0, const_cast(node)); 116 | 117 | Local HEAP = Nan::New("HEAP").ToLocalChecked(); 118 | 119 | // starting with iojs 3 GetUid() and GetTitle() APIs were removed 120 | uint32_t _uid = node->GetMaxSnapshotJSObjectId(); 121 | 122 | char _title[32]; 123 | sprintf(_title, "Snapshot %i", _uid); 124 | Local title = Nan::New(_title).ToLocalChecked(); 125 | 126 | Local uid = Nan::New(_uid); 127 | Local nodesCount = Nan::New(node->GetNodesCount()); 128 | Local objectId = Nan::New(node->GetMaxSnapshotJSObjectId()); 129 | 130 | snapshot->Set(Nan::New("typeId").ToLocalChecked(), HEAP); 131 | snapshot->Set(Nan::New("title").ToLocalChecked(), title); 132 | snapshot->Set(Nan::New("uid").ToLocalChecked(), uid); 133 | snapshot->Set(Nan::New("nodesCount").ToLocalChecked(), nodesCount); 134 | snapshot->Set(Nan::New("maxSnapshotJSObjectId").ToLocalChecked(), objectId); 135 | 136 | Local snapshots = Nan::New(Snapshot::snapshots); 137 | Nan::Set(snapshots, _uid, snapshot); 138 | 139 | return scope.Escape(snapshot); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/heap_snapshot.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_SNAPSHOT_ 2 | #define NODE_SNAPSHOT_ 3 | 4 | #include "v8-profiler.h" 5 | #include "nan.h" 6 | 7 | namespace nodex { 8 | 9 | class Snapshot { 10 | public: 11 | static v8::Local New(const v8::HeapSnapshot* node); 12 | static Nan::Persistent snapshots; 13 | private: 14 | static void Initialize(); 15 | static NAN_GETTER(GetRoot); 16 | static NAN_METHOD(GetNode); 17 | static NAN_METHOD(GetNodeById); 18 | static NAN_METHOD(Delete); 19 | static NAN_METHOD(Serialize); 20 | static Nan::Persistent snapshot_template_; 21 | }; 22 | } //namespace nodex 23 | #endif // NODE_SNAPSHOT_ 24 | -------------------------------------------------------------------------------- /src/profiler.cc: -------------------------------------------------------------------------------- 1 | #include "node.h" 2 | #include "nan.h" 3 | #include "heap_profiler.h" 4 | #include "cpu_profiler.h" 5 | 6 | namespace nodex { 7 | void InitializeProfiler(v8::Local target) { 8 | Nan::HandleScope scope; 9 | HeapProfiler::Initialize(target); 10 | CpuProfiler::Initialize(target); 11 | } 12 | 13 | NODE_MODULE(profiler, InitializeProfiler) 14 | } 15 | -------------------------------------------------------------------------------- /test/binding.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect, 2 | binary = require('node-pre-gyp'), 3 | path = require('path'), 4 | binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json'))), 5 | binding = require(binding_path); 6 | 7 | const NODE_V_010 = /^v0\.10\.\d+$/.test(process.version); 8 | const NODE_V_3 = /^v3\./.test(process.version); 9 | 10 | describe('binding', function() { 11 | describe('Profiler container', function() { 12 | it('has expected structure', function() { 13 | var properties = ['cpu', 'heap']; 14 | 15 | properties.forEach(function(prop) { 16 | expect(binding).to.have.property(prop); 17 | }); 18 | }); 19 | }); 20 | 21 | describe('CPU', function() { 22 | after(deleteAllProfiles); 23 | 24 | var cpu = binding.cpu; 25 | 26 | describe('Profiler', function() { 27 | 28 | it('has expected structure', function() { 29 | var properties = ['startProfiling', 'stopProfiling', 'profiles']; 30 | 31 | properties.forEach(function(prop) { 32 | expect(cpu).to.have.property(prop); 33 | }); 34 | }); 35 | }); 36 | 37 | describe('Profile', function() { 38 | it('has expected structure', function() { 39 | cpu.startProfiling('', true); 40 | var profile = cpu.stopProfiling(); 41 | var properties = NODE_V_010 ? 42 | ['delete', 'typeId', 'uid', 'title', 'head'] : 43 | ['delete', 'typeId', 'uid', 'title', 'head', 'startTime', 'endTime', 'samples', 'timestamps']; 44 | 45 | properties.forEach(function(prop) { 46 | expect(profile).to.have.property(prop); 47 | }); 48 | }); 49 | 50 | it('should delete itself from profiler cache', function() { 51 | cpu.startProfiling('', true); 52 | var profile = cpu.stopProfiling(); 53 | var oldProfilesLength = Object.keys(cpu.profiles).length; 54 | profile.delete(); 55 | expect(oldProfilesLength - Object.keys(cpu.profiles).length).to.equal(1); 56 | }); 57 | }); 58 | 59 | describe('Profile Node', function() { 60 | it('has expected structure', function() { 61 | cpu.startProfiling('P'); 62 | var profile = cpu.stopProfiling(); 63 | var mainProps = ['functionName', 'url', 'lineNumber', 'callUID', 'children', 64 | 'bailoutReason', 'id', 'hitCount']; 65 | var extendedProps = NODE_V_010 ? [] : ['scriptId']; 66 | var properties = mainProps.concat(extendedProps); 67 | 68 | properties.forEach(function(prop) { 69 | expect(profile.head).to.have.property(prop); 70 | }); 71 | }); 72 | }); 73 | 74 | function deleteAllProfiles() { 75 | cpu.profiles.slice().forEach(function(profile) { 76 | profile.delete(); 77 | }); 78 | } 79 | }); 80 | 81 | describe('HEAP', function() { 82 | after(deleteAllSnapshots); 83 | 84 | var heap = binding.heap; 85 | 86 | describe('Profiler', function() { 87 | it('has expected structure', function() { 88 | var properties = [ 89 | 'takeSnapshot', 90 | 'startTrackingHeapObjects', 91 | 'stopTrackingHeapObjects', 92 | 'getHeapStats', 93 | 'snapshots' 94 | ]; 95 | 96 | properties.forEach(function(prop) { 97 | expect(heap).to.have.property(prop); 98 | }); 99 | }); 100 | }); 101 | 102 | describe('Snapshot', function() { 103 | it('has expected structure', function() { 104 | var snapshot = heap.takeSnapshot('', function() {}); 105 | var properties = [ 106 | 'delete', 'serialize', 'getNode', 'root', 107 | 'typeId', 'uid', 'title', 'nodesCount', 'maxSnapshotJSObjectId' 108 | ]; 109 | 110 | properties.forEach(function(prop) { 111 | expect(snapshot).to.have.property(prop); 112 | }); 113 | }); 114 | }); 115 | 116 | function deleteAllSnapshots() { 117 | Object.keys(binding.heap.snapshots).forEach(function(key) { 118 | binding.heap.snapshots[key].delete(); 119 | }); 120 | } 121 | }); 122 | }); 123 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require chai -------------------------------------------------------------------------------- /test/v8-profiler.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect, 2 | profiler = require('../'); 3 | 4 | const NODE_V_010 = /^v0\.10\.\d+$/.test(process.version); 5 | 6 | describe('v8-profiler', function() { 7 | describe('CPU', function() { 8 | after(deleteAllProfiles); 9 | 10 | describe('Profiler', function() { 11 | 12 | it('should start profiling', function() { 13 | expect(profiler.startProfiling).to.not.throw(); 14 | }); 15 | 16 | it('should stop profiling', function() { 17 | expect(profiler.stopProfiling).to.not.throw(); 18 | }); 19 | 20 | it('should cache profiles', function() { 21 | expect(Object.keys(profiler.profiles)).to.have.length(1); 22 | }); 23 | 24 | it('should replace profile title, if started with name argument', function() { 25 | profiler.startProfiling('P'); 26 | var profile = profiler.stopProfiling(); 27 | expect(profile.title).to.equal('P'); 28 | }); 29 | 30 | it('should record samples, if started with recsamples argument', function() { 31 | if (NODE_V_010) return; 32 | 33 | profiler.startProfiling(true); 34 | var profile = profiler.stopProfiling(); 35 | expect(profile.samples.length > 0).to.equal(true); 36 | }); 37 | 38 | it('should throw on setSamplingInterval if profile recording in progress', function() { 39 | profiler.startProfiling(); 40 | expect(profiler.setSamplingInterval).to.throw( 41 | 'setSamplingInterval must be called when there are no profiles being recorded.'); 42 | profiler.stopProfiling(); 43 | }); 44 | 45 | it('should set sampling interval', function() { 46 | profiler.setSamplingInterval(1000); 47 | }); 48 | }); 49 | 50 | describe('Profile', function() { 51 | it('should export itself with callback', function() { 52 | profiler.startProfiling('', true); 53 | var profile = profiler.stopProfiling(); 54 | profile.export(function(error, result) { 55 | var _result = JSON.parse(result); 56 | 57 | expect(result).to.be.a('string'); 58 | expect(_result).to.be.an('object'); 59 | }); 60 | }); 61 | 62 | it('should export itself with stream', function(done) { 63 | profiler.startProfiling('', true); 64 | var profile = profiler.stopProfiling(); 65 | var fs = require('fs'), 66 | ws = fs.createWriteStream('profile.json'); 67 | profile.export().pipe(ws); 68 | 69 | ws.on('finish', function() { 70 | fs.unlink('profile.json', done); 71 | }); 72 | }); 73 | }); 74 | 75 | function deleteAllProfiles() { 76 | Object.keys(profiler.profiles).forEach(function(key) { 77 | profiler.profiles[key].delete(); 78 | }); 79 | } 80 | }); 81 | 82 | describe('HEAP', function() { 83 | after(deleteAllSnapshots); 84 | 85 | describe('Profiler', function() { 86 | 87 | it('should take snapshot without arguments', function() { 88 | expect(profiler.takeSnapshot).to.not.throw(); 89 | }); 90 | 91 | it('should cache snapshots', function() { 92 | expect(Object.keys(profiler.snapshots)).to.have.length(1); 93 | }); 94 | 95 | it('should replace snapshot title, if started with name argument', function() { 96 | var snapshot = profiler.takeSnapshot('S'); 97 | expect(snapshot.title).to.equal('S'); 98 | }); 99 | 100 | it('should use control function, if started with function argument', function(done) { 101 | // Fix for Windows 102 | var checked = false; 103 | profiler.takeSnapshot(function(progress, total) { 104 | if (progress === total) { 105 | if (checked) return; 106 | checked = true; 107 | done(); 108 | } 109 | }); 110 | }); 111 | 112 | it('should write heap stats', function(done) { 113 | expect(profiler.startTrackingHeapObjects).to.not.throw(); 114 | var lastSeenObjectId = profiler.getHeapStats( 115 | function(samples) { 116 | expect(samples).to.instanceof(Array); 117 | }, 118 | function() { 119 | expect(profiler.stopTrackingHeapObjects).to.not.throw(); 120 | done(); 121 | } 122 | ); 123 | expect(typeof lastSeenObjectId).to.be.equal('number'); 124 | }); 125 | 126 | it('should return undefined for wrong params in getObjectByHeapObjectId', function() { 127 | expect(profiler.getObjectByHeapObjectId('a')).to.be.equal(undefined); 128 | }); 129 | 130 | it('should return id for object in getHeapObjectId', function() { 131 | var obj = {}; 132 | var snapshot = profiler.takeSnapshot(); 133 | expect(profiler.getHeapObjectId(obj)).to.be.gt(0); 134 | }); 135 | 136 | it('should return id for undefined param in getHeapObjectId', function() { 137 | var snapshot = profiler.takeSnapshot(); 138 | expect(profiler.getHeapObjectId(undefined)).to.be.gt(0); 139 | }); 140 | 141 | it('should return undefined for wrong params in getHeapObjectId', function() { 142 | var snapshot = profiler.takeSnapshot(); 143 | expect(profiler.getHeapObjectId()).to.be.equal(undefined); 144 | }); 145 | }); 146 | 147 | describe('Snapshot', function() { 148 | 149 | it('should delete itself from profiler cache', function() { 150 | var snapshot = profiler.takeSnapshot(); 151 | var uid = snapshot.uid; 152 | 153 | var oldSnapshotsLength = Object.keys(profiler.snapshots).length; 154 | snapshot.delete(); 155 | 156 | expect(Object.keys(profiler.snapshots).length == oldSnapshotsLength - 1).to.equal(true); 157 | expect(profiler.snapshots[uid]).to.be.equal(undefined); 158 | }); 159 | 160 | it('should serialise itself', function(done) { 161 | var snapshot = profiler.takeSnapshot(); 162 | var buffer = ''; 163 | snapshot.serialize( 164 | function iterator(data, length) { 165 | buffer += data; 166 | }, 167 | function callback() { 168 | expect(JSON.parse.bind(JSON, buffer)).to.not.throw(); 169 | done(); 170 | } 171 | ); 172 | }); 173 | 174 | it('should pipe itself to stream', function(done) { 175 | var snapshot = profiler.takeSnapshot(); 176 | var fs = require('fs'), 177 | ws = fs.createWriteStream('snapshot.json') 178 | .on('finish', function() { 179 | fs.unlink('snapshot.json', done); 180 | }); 181 | 182 | snapshot.export().pipe(ws); 183 | }); 184 | 185 | it('should export itself to callback', function(done) { 186 | var snapshot = profiler.takeSnapshot(); 187 | 188 | snapshot.export(function(err, result) { 189 | expect(!err); 190 | expect(typeof result == 'string'); 191 | done(); 192 | }); 193 | }); 194 | 195 | it('should compare itself with other snapshot', function() { 196 | this.timeout(5000); 197 | var snapshot1 = profiler.takeSnapshot(); 198 | var snapshot2 = profiler.takeSnapshot(); 199 | 200 | expect(snapshot1.compare.bind(snapshot1, snapshot2)).to.not.throw(); 201 | }); 202 | }); 203 | 204 | function deleteAllSnapshots() { 205 | Object.keys(profiler.snapshots).forEach(function(key) { 206 | profiler.snapshots[key].delete(); 207 | }); 208 | } 209 | }); 210 | }); 211 | -------------------------------------------------------------------------------- /tools/NODE_NEXT.js: -------------------------------------------------------------------------------- 1 | return module.exports = process.versions.modules > 45; 2 | -------------------------------------------------------------------------------- /tools/annotate-tag.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const co = require('co'); 4 | const exec = require('./exec'); 5 | 6 | module.exports = co.wrap(function * (version) { 7 | const history = yield require('./history')(version); 8 | const tagname = 'v' + version; 9 | 10 | return yield exec('git tag -a "' + tagname + '" -m "' + history + '"'); 11 | }); 12 | -------------------------------------------------------------------------------- /tools/commit-changes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const exec = require('./exec'); 4 | const changed = [/*'ChangeLog.md',*/ 'package.json'].join(' '); 5 | 6 | module.exports = 7 | (version) => exec('git commit -m "' + version + '" ' + changed); 8 | -------------------------------------------------------------------------------- /tools/exec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const exec = require('child_process').exec; 4 | 5 | module.exports = 6 | (expression) => new Promise( 7 | (resolve, reject) => exec(expression, 8 | (error, result) => error ? reject(error) : resolve(String(result).trim()) 9 | )); 10 | -------------------------------------------------------------------------------- /tools/history.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const co = require('co'); 4 | const exec = require('./exec'); 5 | 6 | module.exports = co.wrap(function * () { 7 | const _lasttag = yield exec('git rev-list --tags --max-count=1'); 8 | const _version = yield exec('git describe --tags --abbrev=0 ' + _lasttag); 9 | const version = _version ? ' ' + _version + '..' : ''; 10 | 11 | return ' ' + (yield exec('git log --no-merges --pretty="format: * %s (%an) %H%n"' + version)); 12 | }); 13 | -------------------------------------------------------------------------------- /tools/prepublish-to-npm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const co = require('co'); 4 | const rimraf = require('rimraf'); 5 | const gyp = require('node-pre-gyp'); 6 | const versions = ['0.10.0', '0.12.0', '4.0.0', '5.0.0', '6.0.0']; 7 | const matrix = { 8 | x64: ['win32', 'linux', 'darwin'], 9 | ia32: ['win32'] 10 | }; 11 | 12 | class Target { 13 | constructor(arch, platform, version) { 14 | this.target = version; 15 | this.target_platform = platform; 16 | this.target_arch = arch; 17 | } 18 | } 19 | 20 | const install = target => new Promise((resolve, reject) => { 21 | const prog = Object.assign(new gyp.Run(), {opts: target}); 22 | 23 | prog.commands.install([], error => error ? reject(error) : resolve()); 24 | }); 25 | 26 | module.exports = co.wrap(function * () { 27 | rimraf.sync('./build'); 28 | 29 | const targets = []; 30 | Object.keys(matrix).forEach( 31 | (arch) => matrix[arch].forEach( 32 | (platform) => versions.forEach( 33 | (version) => targets.push(new Target(arch, platform, version)) 34 | ))); 35 | 36 | while (targets.length) yield install(targets.pop()); 37 | }); 38 | -------------------------------------------------------------------------------- /tools/publish-to-npm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const exec = require('./exec'); 4 | 5 | module.exports = 6 | (version) => exec('npm publish'); 7 | -------------------------------------------------------------------------------- /tools/push-to-githab.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const exec = require('./exec'); 4 | 5 | module.exports = 6 | (version) => exec('git push && git push origin "v' + version + '"'); 7 | -------------------------------------------------------------------------------- /tools/release.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const co = require('co'); 4 | const args = process.argv.slice(2); 5 | const version = args.splice(0, 1); 6 | 7 | const npm = require('./update-npm-version'); 8 | const changelog = require('./update-changelog'); 9 | const tag = require('./annotate-tag'); 10 | const commit = require('./commit-changes'); 11 | const push = require('./push-to-githab'); 12 | 13 | const prepublish = require('./prepublish-to-npm'); 14 | const publish = require('./publish-to-npm'); 15 | 16 | const EXAMPLE = ' Example:\n' + 17 | 'node release.js 1.0.0 --build\n' + 18 | 'node release.js 1.0.0 --publish' 19 | 20 | const SEMVER = /^\d+(\.\d+(\.\d+(-.*)?)?(-.*)?)?(-.*)?$/; 21 | 22 | console.assert(version, 'Wrong usage.' + EXAMPLE); 23 | console.assert(SEMVER.test(version), version + ' is not correct semver'); 24 | 25 | const BUILD = args.some( 26 | (arg) => /^(-b|--build)$/.test(arg)); 27 | 28 | const PUBLISH = args.some( 29 | (arg) => /^(-p|--publish)$/.test(arg)); 30 | 31 | console.assert(BUILD || PUBLISH, 'No mode selected.' + EXAMPLE); 32 | 33 | return co(function * () { 34 | if (BUILD) { 35 | console.log('--Update the version in package.json--'); 36 | yield npm(version); 37 | 38 | // TODO: enable changelog on 1.0 version 39 | // console.log('--Update ChangeLog.md--'); 40 | // changelog(); 41 | 42 | console.log('--Commit the changes--'); 43 | yield commit(version); 44 | 45 | console.log('--Tag the release--') 46 | yield tag(version); 47 | 48 | console.log('--Push to github--'); 49 | yield push(version); 50 | } else if (PUBLISH) { 51 | console.log('--Download prebuilt binaries--'); 52 | yield prepublish(); 53 | 54 | console.log('--Publish to npm--'); 55 | yield publish(); 56 | } 57 | }).catch((error) => { 58 | console.error(error.stack); 59 | return process.exit(1); 60 | }); 61 | -------------------------------------------------------------------------------- /tools/update-changelog.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var exists = fs.existsSync; 3 | var read = fs.readFileSync; 4 | var write = fs.writeFileSync; 5 | 6 | var history = require('./history'); 7 | 8 | module.exports = function changelog(filename) { 9 | filename = filename || 'CHANGELOG.md'; 10 | 11 | var _changelog = exists(filename) ? read(filename) : ''; 12 | var _history = history(); 13 | 14 | write(filename, _history + '\n' + _changelog); 15 | }; 16 | 17 | //test -n "$EDITOR" && $EDITOR $CHANGELOG 18 | -------------------------------------------------------------------------------- /tools/update-npm-version.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const exec = require('./exec'); 4 | 5 | module.exports = 6 | (version) => exec('npm version --git-tag-version=false "' + version + '"'); 7 | -------------------------------------------------------------------------------- /v8-profiler.js: -------------------------------------------------------------------------------- 1 | var pack = require('./package.json'); 2 | var binding = require('./' + [ 3 | 'build', 4 | 'profiler', 5 | 'v' + pack.version, 6 | ['node', 'v' + process.versions.modules, process.platform, process.arch].join('-'), 7 | 'profiler.node' 8 | ].join('/')); 9 | 10 | var Stream = require('stream').Stream, 11 | inherits = require('util').inherits; 12 | 13 | function Snapshot() {} 14 | 15 | Snapshot.prototype.getHeader = function() { 16 | return { 17 | typeId: this.typeId, 18 | uid: this.uid, 19 | title: this.title 20 | } 21 | } 22 | 23 | /** 24 | * @param {Snapshot} other 25 | * @returns {Object} 26 | */ 27 | Snapshot.prototype.compare = function(other) { 28 | var selfHist = nodesHist(this), 29 | otherHist = nodesHist(other), 30 | keys = Object.keys(selfHist).concat(Object.keys(otherHist)), 31 | diff = {}; 32 | 33 | keys.forEach(function(key) { 34 | if (key in diff) return; 35 | 36 | var selfCount = selfHist[key] || 0, 37 | otherCount = otherHist[key] || 0; 38 | 39 | diff[key] = otherCount - selfCount; 40 | }); 41 | 42 | return diff; 43 | }; 44 | 45 | function ExportStream() { 46 | Stream.Transform.call(this); 47 | this._transform = function noTransform(chunk, encoding, done) { 48 | done(null, chunk); 49 | } 50 | } 51 | inherits(ExportStream, Stream.Transform); 52 | 53 | /** 54 | * @param {Stream.Writable|function} dataReceiver 55 | * @returns {Stream|undefined} 56 | */ 57 | Snapshot.prototype.export = function(dataReceiver) { 58 | dataReceiver = dataReceiver || new ExportStream(); 59 | 60 | var toStream = dataReceiver instanceof Stream, 61 | chunks = toStream ? null : []; 62 | 63 | function onChunk(chunk, len) { 64 | if (toStream) dataReceiver.write(chunk); 65 | else chunks.push(chunk); 66 | } 67 | 68 | function onDone() { 69 | if (toStream) dataReceiver.end(); 70 | else dataReceiver(null, chunks.join('')); 71 | } 72 | 73 | this.serialize(onChunk, onDone); 74 | 75 | return toStream ? dataReceiver : undefined; 76 | }; 77 | 78 | function nodes(snapshot) { 79 | var n = snapshot.nodesCount, i, nodes = []; 80 | for (i = 0; i < n; i++) { 81 | nodes[i] = snapshot.getNode(i); 82 | } 83 | return nodes; 84 | }; 85 | 86 | function nodesHist(snapshot) { 87 | var objects = {}; 88 | nodes(snapshot).forEach(function(node){ 89 | var key = node.type === "Object" ? node.name : node.type; 90 | objects[key] = objects[node.name] || 0; 91 | objects[key]++; 92 | }); 93 | return objects; 94 | }; 95 | 96 | function CpuProfile() {} 97 | 98 | CpuProfile.prototype.getHeader = function() { 99 | return { 100 | typeId: this.typeId, 101 | uid: this.uid, 102 | title: this.title 103 | } 104 | } 105 | 106 | CpuProfile.prototype.export = function(dataReceiver) { 107 | dataReceiver = dataReceiver || new ExportStream(); 108 | 109 | var toStream = dataReceiver instanceof Stream; 110 | var error, result; 111 | 112 | try { 113 | result = JSON.stringify(this); 114 | } catch (err) { 115 | error = err; 116 | } 117 | 118 | process.nextTick(function() { 119 | if (toStream) { 120 | if (error) { 121 | dataReceiver.emit('error', error); 122 | } 123 | 124 | dataReceiver.end(result); 125 | } else { 126 | dataReceiver(error, result); 127 | } 128 | }); 129 | 130 | return toStream ? dataReceiver : undefined; 131 | }; 132 | 133 | var startTime, endTime; 134 | var activeProfiles = []; 135 | 136 | var profiler = { 137 | /*HEAP PROFILER API*/ 138 | 139 | get snapshots() { return binding.heap.snapshots; }, 140 | 141 | takeSnapshot: function(name, control) { 142 | if (typeof name == 'function') { 143 | control = name; 144 | name = ''; 145 | } 146 | 147 | if (typeof control !== 'function') { 148 | control = function noop() {}; 149 | } 150 | 151 | name = '' + name; 152 | 153 | var snapshot = binding.heap.takeSnapshot(name, control); 154 | snapshot.__proto__ = Snapshot.prototype; 155 | snapshot.title = name; 156 | return snapshot; 157 | }, 158 | 159 | deleteAllSnapshots: function () { 160 | Object.keys(binding.heap.snapshots).forEach(function(key) { 161 | binding.heap.snapshots[key].delete(); 162 | }); 163 | }, 164 | 165 | startTrackingHeapObjects: function() { 166 | binding.heap.startTrackingHeapObjects(); 167 | }, 168 | 169 | stopTrackingHeapObjects: function() { 170 | binding.heap.stopTrackingHeapObjects(); 171 | }, 172 | 173 | getHeapStats: function(iterator, callback) { 174 | if (typeof iterator !== 'function') 175 | iterator = function noop() {}; 176 | 177 | if (typeof callback !== 'function') 178 | callback = function noop() {}; 179 | 180 | return binding.heap.getHeapStats(iterator, callback) 181 | }, 182 | 183 | getObjectByHeapObjectId: function(id) { 184 | id = parseInt(id, 10); 185 | if (isNaN(id)) return; 186 | 187 | return binding.heap.getObjectByHeapObjectId(id); 188 | }, 189 | 190 | getHeapObjectId: function(value) { 191 | if (!arguments.length) return; 192 | return binding.heap.getHeapObjectId(value); 193 | }, 194 | 195 | /*CPU PROFILER API*/ 196 | 197 | get profiles() { return binding.cpu.profiles; }, 198 | 199 | startProfiling: function(name, recsamples) { 200 | if (activeProfiles.length == 0 && typeof process._startProfilerIdleNotifier == "function") 201 | process._startProfilerIdleNotifier(); 202 | 203 | if (typeof name == 'boolean') { 204 | recsamples = name; 205 | name = ''; 206 | } 207 | 208 | recsamples = recsamples === undefined ? true : Boolean(recsamples); 209 | name = '' + name; 210 | 211 | if (activeProfiles.indexOf(name) < 0) 212 | activeProfiles.push(name) 213 | 214 | startTime = Date.now(); 215 | binding.cpu.startProfiling(name, recsamples); 216 | }, 217 | 218 | stopProfiling: function(name) { 219 | var index = activeProfiles.indexOf(name); 220 | if (name && index < 0) 221 | return; 222 | 223 | var profile = binding.cpu.stopProfiling(name); 224 | endTime = Date.now(); 225 | profile.__proto__ = CpuProfile.prototype; 226 | if (!profile.startTime) profile.startTime = startTime; 227 | if (!profile.endTime) profile.endTime = endTime; 228 | 229 | if (name) 230 | activeProfiles.splice(index, 1); 231 | else 232 | activeProfiles.length = activeProfiles.length - 1; 233 | 234 | if (activeProfiles.length == 0 && typeof process._stopProfilerIdleNotifier == "function") 235 | process._stopProfilerIdleNotifier(); 236 | 237 | return profile; 238 | }, 239 | 240 | setSamplingInterval: function(num) { 241 | if (activeProfiles.length) { 242 | throw new Error('setSamplingInterval must be called when there are no profiles being recorded.'); 243 | } 244 | 245 | num = parseInt(num, 10) || 1000; 246 | binding.cpu.setSamplingInterval(num); 247 | }, 248 | 249 | deleteAllProfiles: function() { 250 | Object.keys(binding.cpu.profiles).forEach(function(key) { 251 | binding.cpu.profiles[key].delete(); 252 | }); 253 | } 254 | }; 255 | 256 | module.exports = profiler; 257 | process.profiler = profiler; 258 | --------------------------------------------------------------------------------