diff --git a/.travis.yml b/.travis.yml index 0eca6e7..4f3d5cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,15 @@ language: node_js - +env: + - CXX=g++-4.8 +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 node_js: - - 0.10 - - 0.11 + - "0.10" + - "0.12" + - "4" + - "5" + - "6" diff --git a/README.md b/README.md index 10b2cbf..a20f9ad 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +# This project is DEPRECATED + +See https://github.com/airbnb/node-memwatch for an actively maintained fork. + + `node-memwatch`: Leak Detection and Heap Diffing for Node.JS ============================================================ @@ -19,11 +24,11 @@ Node.JS code. It provides: Installation ------------ -- `npm install memwatch` +- `npm install memwatch-next` or -- `git clone git://github.com/lloyd/node-memwatch.git` +- `git clone git://github.com/marcominetti/node-memwatch.git` Description @@ -37,7 +42,7 @@ instrumentation. This module attempts to satisfy that need. To get started, import `node-memwatch` like so: ```javascript -var memwatch = require('memwatch'); +var memwatch = require('memwatch-next'); ``` ### Leak Detection @@ -63,7 +68,7 @@ The `info` object will look something like: ### Heap Usage The best way to evaluate your memory footprint is to look at heap -usage right aver V8 performs garbage collection. `memwatch` does +usage right after V8 performs garbage collection. `memwatch` does exactly this - it checks heap usage only after GC to give you a stable baseline of your actual memory usage. @@ -136,6 +141,7 @@ The contents of `diff` will look something like: } ] } +} ``` The diff shows that during the sample period, the total number of @@ -156,4 +162,4 @@ Please see the Issues to share suggestions and contribute! License ------- -http://wtfpl.org +http://wtfpl.net diff --git a/binding.gyp b/binding.gyp index 43f1482..4581c8e 100644 --- a/binding.gyp +++ b/binding.gyp @@ -3,7 +3,7 @@ { 'target_name': 'memwatch', 'include_dirs': [ - '= 0.8.0" }, "repository": { "type": "git", - "url": "https://github.com/lloyd/node-memwatch.git" + "url": "https://github.com/marcominetti/node-memwatch.git" }, "main": "include.js", "licenses": [ @@ -17,15 +17,15 @@ } ], "bugs": { - "url": "https://github.com/lloyd/node-memwatch/issues" + "url": "https://github.com/marcominetti/node-memwatch/issues" }, "scripts": { "install": "node-gyp rebuild", "test": "mocha tests --reporter spec" }, "devDependencies": { - "mocha": "1.2.2", - "should": "0.6.3" + "mocha": "^2.4.5", + "should": "^8.3.1" }, "contributors": [ "Jed Parsons (@jedp)", @@ -33,7 +33,7 @@ "Justin Matthews (@jmatthewsr-ms)" ], "dependencies": { - "bindings": "^1.2.0", - "nan": "^1.2.0" + "bindings": "^1.2.1", + "nan": "^2.3.2" } } diff --git a/src/heapdiff.cc b/src/heapdiff.cc index da5a0ae..aeb2621 100644 --- a/src/heapdiff.cc +++ b/src/heapdiff.cc @@ -45,15 +45,15 @@ heapdiff::HeapDiff::~HeapDiff() void heapdiff::HeapDiff::Initialize ( v8::Handle target ) { - NanScope(); + Nan::HandleScope scope; - v8::Local t = NanNew(New); + v8::Local t = Nan::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(NanNew("HeapDiff")); + t->SetClassName(Nan::New("HeapDiff").ToLocalChecked()); - NODE_SET_PROTOTYPE_METHOD(t, "end", End); + Nan::SetPrototypeMethod(t, "end", End); - target->Set(NanNew( "HeapDiff"), t->GetFunction()); + target->Set(Nan::New("HeapDiff").ToLocalChecked(), t->GetFunction()); } NAN_METHOD(heapdiff::HeapDiff::New) @@ -61,29 +61,33 @@ NAN_METHOD(heapdiff::HeapDiff::New) // Don't blow up when the caller says "new require('memwatch').HeapDiff()" // issue #30 // stolen from: https://github.com/kkaefer/node-cpp-modules/commit/bd9432026affafd8450ecfd9b49b7dc647b6d348 - if (!args.IsConstructCall()) { - return NanThrowTypeError("Use the new operator to create instances of this object."); + if (!info.IsConstructCall()) { + return Nan::ThrowTypeError("Use the new operator to create instances of this object."); } - NanScope(); + Nan::HandleScope scope; // allocate the underlying c++ class and wrap it up in the this pointer HeapDiff * self = new HeapDiff(); - self->Wrap(args.This()); + self->Wrap(info.This()); // take a snapshot and save a pointer to it s_inProgress = true; s_startTime = time(NULL); +#if (NODE_MODULE_VERSION >= 0x002D) + self->before = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(NULL); +#else #if (NODE_MODULE_VERSION > 0x000B) - self->before = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(NanNew(""), NULL); + self->before = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(Nan::New("").ToLocalChecked(), NULL); #else - self->before = v8::HeapProfiler::TakeSnapshot(NanNew(""), HeapSnapshot::kFull, NULL); + self->before = v8::HeapProfiler::TakeSnapshot(Nan::New("").ToLocalChecked(), HeapSnapshot::kFull, NULL); +#endif #endif s_inProgress = false; - NanReturnValue(args.This()); + info.GetReturnValue().Set(info.This()); } static string handleToStr(const Handle & str) @@ -95,7 +99,7 @@ static string handleToStr(const Handle & str) static void buildIDSet(set * seen, const HeapGraphNode* cur, int & s) { - NanScope(); + Nan::HandleScope scope; // cycle detection if (seen->find(cur->GetId()) != seen->end()) { @@ -109,8 +113,11 @@ buildIDSet(set * seen, const HeapGraphNode* cur, int & s) } // update memory usage as we go +#if (NODE_MODULE_VERSION >= 0x002D) + s += cur->GetShallowSize(); +#else s += cur->GetSelfSize(); - +#endif seen->insert(cur->GetId()); for (int i=0; i < cur->GetChildrenCount(); i++) { @@ -200,7 +207,11 @@ static void manageChange(changeset & changes, const HeapGraphNode * node, bool a changeset::iterator i = changes.find(type); +#if (NODE_MODULE_VERSION >= 0x002D) + i->second.size += node->GetShallowSize() * (added ? 1 : -1); +#else i->second.size += node->GetSelfSize() * (added ? 1 : -1); +#endif if (added) i->second.added++; else i->second.released++; @@ -211,66 +222,66 @@ static void manageChange(changeset & changes, const HeapGraphNode * node, bool a static Handle changesetToObject(changeset & changes) { - NanEscapableScope(); - Local a = NanNew(); + Nan::EscapableHandleScope scope; + Local a = Nan::New(); for (changeset::iterator i = changes.begin(); i != changes.end(); i++) { - Local d = NanNew(); - d->Set(NanNew("what"), NanNew(i->first.c_str())); - d->Set(NanNew("size_bytes"), NanNew(i->second.size)); - d->Set(NanNew("size"), NanNew(mw_util::niceSize(i->second.size).c_str())); - d->Set(NanNew("+"), NanNew(i->second.added)); - d->Set(NanNew("-"), NanNew(i->second.released)); + Local d = Nan::New(); + d->Set(Nan::New("what").ToLocalChecked(), Nan::New(i->first.c_str()).ToLocalChecked()); + d->Set(Nan::New("size_bytes").ToLocalChecked(), Nan::New(i->second.size)); + d->Set(Nan::New("size").ToLocalChecked(), Nan::New(mw_util::niceSize(i->second.size).c_str()).ToLocalChecked()); + d->Set(Nan::New("+").ToLocalChecked(), Nan::New(i->second.added)); + d->Set(Nan::New("-").ToLocalChecked(), Nan::New(i->second.released)); a->Set(a->Length(), d); } - return NanEscapeScope(a); + return scope.Escape(a); } -static v8::Handle +static v8::Local compare(const v8::HeapSnapshot * before, const v8::HeapSnapshot * after) { - NanEscapableScope(); + Nan::EscapableHandleScope scope; int s, diffBytes; - Local o = NanNew(); + Local o = Nan::New(); // first let's append summary information - Local b = NanNew(); - b->Set(NanNew("nodes"), NanNew(before->GetNodesCount())); - //b->Set(NanNew("time"), s_startTime); - o->Set(NanNew("before"), b); + Local b = Nan::New(); + b->Set(Nan::New("nodes").ToLocalChecked(), Nan::New(before->GetNodesCount())); + //b->Set(Nan::New("time"), s_startTime); + o->Set(Nan::New("before").ToLocalChecked(), b); - Local a = NanNew(); - a->Set(NanNew("nodes"), NanNew(after->GetNodesCount())); - //a->Set(NanNew("time"), time(NULL)); - o->Set(NanNew("after"), a); + Local a = Nan::New(); + a->Set(Nan::New("nodes").ToLocalChecked(), Nan::New(after->GetNodesCount())); + //a->Set(Nan::New("time"), time(NULL)); + o->Set(Nan::New("after").ToLocalChecked(), a); // now let's get allocations by name set beforeIDs, afterIDs; s = 0; buildIDSet(&beforeIDs, before->GetRoot(), s); - b->Set(NanNew("size_bytes"), NanNew(s)); - b->Set(NanNew("size"), NanNew(mw_util::niceSize(s).c_str())); + b->Set(Nan::New("size_bytes").ToLocalChecked(), Nan::New(s)); + b->Set(Nan::New("size").ToLocalChecked(), Nan::New(mw_util::niceSize(s).c_str()).ToLocalChecked()); diffBytes = s; s = 0; buildIDSet(&afterIDs, after->GetRoot(), s); - a->Set(NanNew("size_bytes"), NanNew(s)); - a->Set(NanNew("size"), NanNew(mw_util::niceSize(s).c_str())); + a->Set(Nan::New("size_bytes").ToLocalChecked(), Nan::New(s)); + a->Set(Nan::New("size").ToLocalChecked(), Nan::New(mw_util::niceSize(s).c_str()).ToLocalChecked()); diffBytes = s - diffBytes; - Local c = NanNew(); - c->Set(NanNew("size_bytes"), NanNew(diffBytes)); - c->Set(NanNew("size"), NanNew(mw_util::niceSize(diffBytes).c_str())); - o->Set(NanNew("change"), c); + Local c = Nan::New(); + c->Set(Nan::New("size_bytes").ToLocalChecked(), Nan::New(diffBytes)); + c->Set(Nan::New("size").ToLocalChecked(), Nan::New(mw_util::niceSize(diffBytes).c_str()).ToLocalChecked()); + o->Set(Nan::New("change").ToLocalChecked(), c); // before - after will reveal nodes released (memory freed) vector changedIDs; setDiff(beforeIDs, afterIDs, changedIDs); - c->Set(NanNew("freed_nodes"), NanNew(changedIDs.size())); + c->Set(Nan::New("freed_nodes").ToLocalChecked(), Nan::New(changedIDs.size())); // here's where we'll collect all the summary information changeset changes; @@ -286,42 +297,46 @@ compare(const v8::HeapSnapshot * before, const v8::HeapSnapshot * after) // after - before will reveal nodes added (memory allocated) setDiff(afterIDs, beforeIDs, changedIDs); - c->Set(NanNew("allocated_nodes"), NanNew(changedIDs.size())); + c->Set(Nan::New("allocated_nodes").ToLocalChecked(), Nan::New(changedIDs.size())); for (unsigned long i = 0; i < changedIDs.size(); i++) { const HeapGraphNode * n = after->GetNodeById(changedIDs[i]); manageChange(changes, n, true); } - c->Set(NanNew("details"), changesetToObject(changes)); + c->Set(Nan::New("details").ToLocalChecked(), changesetToObject(changes)); - return NanEscapeScope(o); + return scope.Escape(o); } NAN_METHOD(heapdiff::HeapDiff::End) { // take another snapshot and compare them - NanScope(); + Nan::HandleScope scope; - HeapDiff *t = Unwrap( args.This() ); + HeapDiff *t = Unwrap( info.This() ); // How shall we deal with double .end()ing? The only reasonable // approach seems to be an exception, cause nothing else makes // sense. if (t->ended) { - return NanThrowError("attempt to end() a HeapDiff that was already ended"); + return Nan::ThrowError("attempt to end() a HeapDiff that was already ended"); } t->ended = true; s_inProgress = true; +#if (NODE_MODULE_VERSION >= 0x002D) + t->after = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(NULL); +#else #if (NODE_MODULE_VERSION > 0x000B) - t->after = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(NanNew(""), NULL); + t->after = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(Nan::New("").ToLocalChecked(), NULL); #else - t->after = v8::HeapProfiler::TakeSnapshot(NanNew(""), HeapSnapshot::kFull, NULL); + t->after = v8::HeapProfiler::TakeSnapshot(Nan::New("").ToLocalChecked(), HeapSnapshot::kFull, NULL); +#endif #endif s_inProgress = false; - v8::Handle comparison = compare(t->before, t->after); + v8::Local comparison = compare(t->before, t->after); // free early, free often. I mean, after all, this process we're in is // probably having memory problems. We want to help her. ((HeapSnapshot *) t->before)->Delete(); @@ -329,5 +344,5 @@ NAN_METHOD(heapdiff::HeapDiff::End) ((HeapSnapshot *) t->after)->Delete(); t->after = NULL; - NanReturnValue(comparison); + info.GetReturnValue().Set(comparison); } diff --git a/src/heapdiff.hh b/src/heapdiff.hh index 592858c..917d7a6 100644 --- a/src/heapdiff.hh +++ b/src/heapdiff.hh @@ -12,7 +12,7 @@ namespace heapdiff { - class HeapDiff : public node::ObjectWrap + class HeapDiff : public Nan::ObjectWrap { public: static void Initialize ( v8::Handle target ); diff --git a/src/init.cc b/src/init.cc index 00df1a2..36feae9 100644 --- a/src/init.cc +++ b/src/init.cc @@ -11,11 +11,11 @@ extern "C" { void init (v8::Handle target) { - NanScope(); + Nan::HandleScope scope; heapdiff::HeapDiff::Initialize(target); - NODE_SET_METHOD(target, "upon_gc", memwatch::upon_gc); - NODE_SET_METHOD(target, "gc", memwatch::trigger_gc); + Nan::SetMethod(target, "upon_gc", memwatch::upon_gc); + Nan::SetMethod(target, "gc", memwatch::trigger_gc); v8::V8::AddGCEpilogueCallback(memwatch::after_gc); } diff --git a/src/memwatch.cc b/src/memwatch.cc index abbaefb..6ebed41 100644 --- a/src/memwatch.cc +++ b/src/memwatch.cc @@ -21,7 +21,7 @@ using namespace v8; using namespace node; Handle g_context; -NanCallback *g_cb; +Nan::Callback *g_cb; struct Baton { uv_work_t req; @@ -65,31 +65,31 @@ static struct unsigned int consecutive_growth; } s_stats; -static Handle getLeakReport(size_t heapUsage) +static Local getLeakReport(size_t heapUsage) { - NanEscapableScope(); + Nan::EscapableHandleScope scope; size_t growth = heapUsage - s_stats.leak_base_start; int now = time(NULL); int delta = now - s_stats.leak_time_start; - Local leakReport = NanNew(); - //leakReport->Set(NanNew("start"), NODE_UNIXTIME_V8(s_stats.leak_time_start)); - //leakReport->Set(NanNew("end"), NODE_UNIXTIME_V8(now)); - leakReport->Set(NanNew("growth"), NanNew(growth)); + Local leakReport = Nan::New(); + //leakReport->Set(Nan::New("start").ToLocalChecked(), NODE_UNIXTIME_V8(s_stats.leak_time_start)); + //leakReport->Set(Nan::New("end").ToLocalChecked(), NODE_UNIXTIME_V8(now)); + leakReport->Set(Nan::New("growth").ToLocalChecked(), Nan::New(growth)); std::stringstream ss; ss << "heap growth over 5 consecutive GCs (" << mw_util::niceDelta(delta) << ") - " << mw_util::niceSize(growth / ((double) delta / (60.0 * 60.0))) << "/hr"; - leakReport->Set(NanNew("reason"), NanNew(ss.str().c_str())); + leakReport->Set(Nan::New("reason").ToLocalChecked(), Nan::New(ss.str().c_str()).ToLocalChecked()); - return NanEscapeScope(leakReport); + return scope.Escape(leakReport); } static void AsyncMemwatchAfter(uv_work_t* request) { - NanScope(); + Nan::HandleScope scope; Baton * b = (Baton *) request->data; @@ -120,10 +120,10 @@ static void AsyncMemwatchAfter(uv_work_t* request) { s_stats.consecutive_growth = 0; // emit a leak report! - Handle argv[3]; - argv[0] = NanNew(false); + Local argv[3]; + argv[0] = Nan::New(false); // the type of event to emit - argv[1] = NanNew("leak"); + argv[1] = Nan::New("leak").ToLocalChecked(); argv[2] = getLeakReport(b->heapUsage); g_cb->Call(3, argv); } @@ -172,10 +172,10 @@ static void AsyncMemwatchAfter(uv_work_t* request) { // if there are any listeners, it's time to emit! if (!g_cb->IsEmpty()) { - Handle argv[3]; + Local argv[3]; // magic argument to indicate to the callback all we want to know is whether there are // listeners (here we don't) - argv[0] = NanNew(true); + argv[0] = Nan::New(true); //Handle haveListeners = g_cb->call(1, argv); @@ -187,18 +187,18 @@ static void AsyncMemwatchAfter(uv_work_t* request) { } // ok, there are listeners, we actually must serialize and emit this stats event - Local stats = NanNew(); - stats->Set(NanNew("num_full_gc"), NanNew(s_stats.gc_full)); - stats->Set(NanNew("num_inc_gc"), NanNew(s_stats.gc_inc)); - stats->Set(NanNew("heap_compactions"), NanNew(s_stats.gc_compact)); - stats->Set(NanNew("usage_trend"), NanNew(ut)); - stats->Set(NanNew("estimated_base"), NanNew(s_stats.base_recent)); - stats->Set(NanNew("current_base"), NanNew(s_stats.last_base)); - stats->Set(NanNew("min"), NanNew(s_stats.base_min)); - stats->Set(NanNew("max"), NanNew(s_stats.base_max)); - argv[0] = NanNew(false); + Local stats = Nan::New(); + stats->Set(Nan::New("num_full_gc").ToLocalChecked(), Nan::New(s_stats.gc_full)); + stats->Set(Nan::New("num_inc_gc").ToLocalChecked(), Nan::New(s_stats.gc_inc)); + stats->Set(Nan::New("heap_compactions").ToLocalChecked(), Nan::New(s_stats.gc_compact)); + stats->Set(Nan::New("usage_trend").ToLocalChecked(), Nan::New(ut)); + stats->Set(Nan::New("estimated_base").ToLocalChecked(), Nan::New(s_stats.base_recent)); + stats->Set(Nan::New("current_base").ToLocalChecked(), Nan::New(s_stats.last_base)); + stats->Set(Nan::New("min").ToLocalChecked(), Nan::New(s_stats.base_min)); + stats->Set(Nan::New("max").ToLocalChecked(), Nan::New(s_stats.base_max)); + argv[0] = Nan::New(false); // the type of event to emit - argv[1] = NanNew("stats"); + argv[1] = Nan::New("stats").ToLocalChecked(); argv[2] = stats; g_cb->Call(3, argv); } @@ -213,12 +213,12 @@ void memwatch::after_gc(GCType type, GCCallbackFlags flags) { if (heapdiff::HeapDiff::InProgress()) return; - NanScope(); + Nan::HandleScope scope; Baton * baton = new Baton; v8::HeapStatistics hs; - NanGetHeapStatistics(&hs); + Nan::GetHeapStatistics(&hs); baton->heapUsage = hs.used_heap_size(); baton->type = type; @@ -235,15 +235,25 @@ void memwatch::after_gc(GCType type, GCCallbackFlags flags) } NAN_METHOD(memwatch::upon_gc) { - NanScope(); - if (args.Length() >= 1 && args[0]->IsFunction()) { - g_cb = new NanCallback(args[0].As()); + Nan::HandleScope scope; + if (info.Length() >= 1 && info[0]->IsFunction()) { + g_cb = new Nan::Callback(info[0].As()); } - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } NAN_METHOD(memwatch::trigger_gc) { - NanScope(); - while(!V8::IdleNotification()) {}; - NanReturnValue(NanUndefined()); + Nan::HandleScope scope; + int deadline_in_ms = 500; + if (info.Length() >= 1 && info[0]->IsNumber()) { + deadline_in_ms = (int)(info[0]->Int32Value()); + } +#if (NODE_MODULE_VERSION >= 0x002D) + Nan::IdleNotification(deadline_in_ms); + Nan::LowMemoryNotification(); +#else + while(!Nan::IdleNotification(deadline_in_ms)) {}; + Nan::LowMemoryNotification(); +#endif + info.GetReturnValue().Set(Nan::Undefined()); } diff --git a/src/platformcompat.hh b/src/platformcompat.hh index fb41fd5..ad49e20 100644 --- a/src/platformcompat.hh +++ b/src/platformcompat.hh @@ -1,8 +1,6 @@ #ifndef __PLATFORMCOMPAT_H #define __PLATFORMCOMPAT_H -#include // round() - #if defined(_MSC_VER) #include //isinf, isnan #include //min @@ -11,10 +9,11 @@ #define FMIN __min #define ROUND(x) floor(x + 0.5) #else -#define ISINF isinf -#define ISNAN isnan +#include // round(), isinf, isnan +#define ISINF std::isinf +#define ISNAN std::isnan #define FMIN fmin #define ROUND round #endif -#endif \ No newline at end of file +#endif diff --git a/tests.js b/tests.js index 498d752..2b8a93f 100644 --- a/tests.js +++ b/tests.js @@ -15,7 +15,7 @@ describe('the library', function() { describe('calling .gc()', function() { it('should cause a stats() event to be emitted', function(done) { memwatch.once('stats', function(s) { - s.should.be.a('object'); + s.should.be.object; done(); }); memwatch.gc();