@depup/node-memwatch 1.0.1-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,351 @@
1
+ /*
2
+ * 2012|lloyd|http://wtfpl.org
3
+ */
4
+ #include <map>
5
+ #include <string>
6
+ #include <set>
7
+ #include <vector>
8
+
9
+ #include <stdlib.h> // abs()
10
+ #include <time.h> // time()
11
+
12
+ #include "heapdiff.hh"
13
+ #include "util.hh"
14
+
15
+ using namespace v8;
16
+ using namespace node;
17
+ using namespace std;
18
+
19
+ static bool s_inProgress = false;
20
+ static time_t s_startTime;
21
+
22
+ bool heapdiff::HeapDiff::InProgress()
23
+ {
24
+ return s_inProgress;
25
+ }
26
+
27
+ heapdiff::HeapDiff::HeapDiff() : ObjectWrap(), before(NULL), after(NULL),
28
+ ended(false)
29
+ {
30
+ }
31
+
32
+ heapdiff::HeapDiff::~HeapDiff()
33
+ {
34
+ if (before) {
35
+ ((HeapSnapshot *) before)->Delete();
36
+ before = NULL;
37
+ }
38
+
39
+ if (after) {
40
+ ((HeapSnapshot *) after)->Delete();
41
+ after = NULL;
42
+ }
43
+ }
44
+
45
+ void
46
+ heapdiff::HeapDiff::Initialize ( v8::Handle<v8::Object> target )
47
+ {
48
+ Nan::HandleScope scope;
49
+
50
+ v8::Local<v8::FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(New);
51
+ t->InstanceTemplate()->SetInternalFieldCount(1);
52
+ t->SetClassName(Nan::New<v8::String>("HeapDiff").ToLocalChecked());
53
+
54
+ Nan::SetPrototypeMethod(t, "end", End);
55
+
56
+ target->Set(Nan::New<v8::String>("HeapDiff").ToLocalChecked(), t->GetFunction());
57
+ }
58
+
59
+ NAN_METHOD(heapdiff::HeapDiff::New)
60
+ {
61
+ // Don't blow up when the caller says "new require('memwatch').HeapDiff()"
62
+ // issue #30
63
+ // stolen from: https://github.com/kkaefer/node-cpp-modules/commit/bd9432026affafd8450ecfd9b49b7dc647b6d348
64
+ if (!info.IsConstructCall()) {
65
+ return Nan::ThrowTypeError("Use the new operator to create instances of this object.");
66
+ }
67
+
68
+ Nan::HandleScope scope;
69
+
70
+ // allocate the underlying c++ class and wrap it up in the this pointer
71
+ HeapDiff * self = new HeapDiff();
72
+ self->Wrap(info.This());
73
+
74
+ // take a snapshot and save a pointer to it
75
+ s_inProgress = true;
76
+ s_startTime = time(NULL);
77
+
78
+ #if (NODE_MODULE_VERSION >= 0x002D)
79
+ self->before = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(NULL);
80
+ #else
81
+ #if (NODE_MODULE_VERSION > 0x000B)
82
+ self->before = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(Nan::New<v8::String>("").ToLocalChecked(), NULL);
83
+ #else
84
+ self->before = v8::HeapProfiler::TakeSnapshot(Nan::New<v8::String>("").ToLocalChecked(), HeapSnapshot::kFull, NULL);
85
+ #endif
86
+ #endif
87
+
88
+ s_inProgress = false;
89
+
90
+ info.GetReturnValue().Set(info.This());
91
+ }
92
+
93
+ static string handleToStr(const Local<Value> & str)
94
+ {
95
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
96
+
97
+ String::Utf8Value utfString(isolate, str->ToString());
98
+
99
+ return *utfString;
100
+ }
101
+
102
+ static void
103
+ buildIDSet(set<uint64_t> * seen, const HeapGraphNode* cur, int & s)
104
+ {
105
+ Nan::HandleScope scope;
106
+
107
+ // cycle detection
108
+ if (seen->find(cur->GetId()) != seen->end()) {
109
+ return;
110
+ }
111
+ // always ignore HeapDiff related memory
112
+ if (cur->GetType() == HeapGraphNode::kObject &&
113
+ handleToStr(cur->GetName()).compare("HeapDiff") == 0)
114
+ {
115
+ return;
116
+ }
117
+
118
+ // update memory usage as we go
119
+ #if (NODE_MODULE_VERSION >= 0x002D)
120
+ s += cur->GetShallowSize();
121
+ #else
122
+ s += cur->GetSelfSize();
123
+ #endif
124
+ seen->insert(cur->GetId());
125
+
126
+ for (int i=0; i < cur->GetChildrenCount(); i++) {
127
+ buildIDSet(seen, cur->GetChild(i)->GetToNode(), s);
128
+ }
129
+ }
130
+
131
+ typedef set<uint64_t> idset;
132
+
133
+ // why doesn't STL work?
134
+ // XXX: improve this algorithm
135
+ void setDiff(idset a, idset b, vector<uint64_t> &c)
136
+ {
137
+ for (idset::iterator i = a.begin(); i != a.end(); i++) {
138
+ if (b.find(*i) == b.end()) c.push_back(*i);
139
+ }
140
+ }
141
+
142
+
143
+ class example
144
+ {
145
+ public:
146
+ HeapGraphEdge::Type context;
147
+ HeapGraphNode::Type type;
148
+ std::string name;
149
+ std::string value;
150
+ std::string heap_value;
151
+ int self_size;
152
+ int retained_size;
153
+ int retainers;
154
+
155
+ example() : context(HeapGraphEdge::kHidden),
156
+ type(HeapGraphNode::kHidden),
157
+ self_size(0), retained_size(0), retainers(0) { };
158
+ };
159
+
160
+ class change
161
+ {
162
+ public:
163
+ long int size;
164
+ long int added;
165
+ long int released;
166
+ std::vector<example> examples;
167
+
168
+ change() : size(0), added(0), released(0) { }
169
+ };
170
+
171
+ typedef std::map<std::string, change>changeset;
172
+
173
+ static void manageChange(changeset & changes, const HeapGraphNode * node, bool added)
174
+ {
175
+ std::string type;
176
+
177
+ switch(node->GetType()) {
178
+ case HeapGraphNode::kArray:
179
+ type.append("Array");
180
+ break;
181
+ case HeapGraphNode::kString:
182
+ type.append("String");
183
+ break;
184
+ case HeapGraphNode::kObject:
185
+ type.append(handleToStr(node->GetName()));
186
+ break;
187
+ case HeapGraphNode::kCode:
188
+ type.append("Code");
189
+ break;
190
+ case HeapGraphNode::kClosure:
191
+ type.append("Closure");
192
+ break;
193
+ case HeapGraphNode::kRegExp:
194
+ type.append("RegExp");
195
+ break;
196
+ case HeapGraphNode::kHeapNumber:
197
+ type.append("Number");
198
+ break;
199
+ case HeapGraphNode::kNative:
200
+ type.append("Native");
201
+ break;
202
+ case HeapGraphNode::kHidden:
203
+ default:
204
+ return;
205
+ }
206
+
207
+ if (changes.find(type) == changes.end()) {
208
+ changes[type] = change();
209
+ }
210
+
211
+ changeset::iterator i = changes.find(type);
212
+
213
+ #if (NODE_MODULE_VERSION >= 0x002D)
214
+ i->second.size += node->GetShallowSize() * (added ? 1 : -1);
215
+ #else
216
+ i->second.size += node->GetSelfSize() * (added ? 1 : -1);
217
+ #endif
218
+ if (added) i->second.added++;
219
+ else i->second.released++;
220
+
221
+ // XXX: example
222
+
223
+ return;
224
+ }
225
+
226
+ static Handle<Value> changesetToObject(changeset & changes)
227
+ {
228
+ Nan::EscapableHandleScope scope;
229
+ Local<Array> a = Nan::New<v8::Array>();
230
+
231
+ for (changeset::iterator i = changes.begin(); i != changes.end(); i++) {
232
+ Local<Object> d = Nan::New<v8::Object>();
233
+ d->Set(Nan::New("what").ToLocalChecked(), Nan::New(i->first.c_str()).ToLocalChecked());
234
+ d->Set(Nan::New("size_bytes").ToLocalChecked(), Nan::New<v8::Number>(i->second.size));
235
+ d->Set(Nan::New("size").ToLocalChecked(), Nan::New(mw_util::niceSize(i->second.size).c_str()).ToLocalChecked());
236
+ d->Set(Nan::New("+").ToLocalChecked(), Nan::New<v8::Number>(i->second.added));
237
+ d->Set(Nan::New("-").ToLocalChecked(), Nan::New<v8::Number>(i->second.released));
238
+ a->Set(a->Length(), d);
239
+ }
240
+
241
+ return scope.Escape(a);
242
+ }
243
+
244
+
245
+ static v8::Local<Value>
246
+ compare(const v8::HeapSnapshot * before, const v8::HeapSnapshot * after)
247
+ {
248
+ Nan::EscapableHandleScope scope;
249
+ int s, diffBytes;
250
+
251
+ Local<Object> o = Nan::New<v8::Object>();
252
+
253
+ // first let's append summary information
254
+ Local<Object> b = Nan::New<v8::Object>();
255
+ b->Set(Nan::New("nodes").ToLocalChecked(), Nan::New(before->GetNodesCount()));
256
+ //b->Set(Nan::New("time"), s_startTime);
257
+ o->Set(Nan::New("before").ToLocalChecked(), b);
258
+
259
+ Local<Object> a = Nan::New<v8::Object>();
260
+ a->Set(Nan::New("nodes").ToLocalChecked(), Nan::New(after->GetNodesCount()));
261
+ //a->Set(Nan::New("time"), time(NULL));
262
+ o->Set(Nan::New("after").ToLocalChecked(), a);
263
+
264
+ // now let's get allocations by name
265
+ set<uint64_t> beforeIDs, afterIDs;
266
+ s = 0;
267
+ buildIDSet(&beforeIDs, before->GetRoot(), s);
268
+ b->Set(Nan::New("size_bytes").ToLocalChecked(), Nan::New(s));
269
+ b->Set(Nan::New("size").ToLocalChecked(), Nan::New(mw_util::niceSize(s).c_str()).ToLocalChecked());
270
+
271
+ diffBytes = s;
272
+ s = 0;
273
+ buildIDSet(&afterIDs, after->GetRoot(), s);
274
+ a->Set(Nan::New("size_bytes").ToLocalChecked(), Nan::New(s));
275
+ a->Set(Nan::New("size").ToLocalChecked(), Nan::New(mw_util::niceSize(s).c_str()).ToLocalChecked());
276
+
277
+ diffBytes = s - diffBytes;
278
+
279
+ Local<Object> c = Nan::New<v8::Object>();
280
+ c->Set(Nan::New("size_bytes").ToLocalChecked(), Nan::New(diffBytes));
281
+ c->Set(Nan::New("size").ToLocalChecked(), Nan::New(mw_util::niceSize(diffBytes).c_str()).ToLocalChecked());
282
+ o->Set(Nan::New("change").ToLocalChecked(), c);
283
+
284
+ // before - after will reveal nodes released (memory freed)
285
+ vector<uint64_t> changedIDs;
286
+ setDiff(beforeIDs, afterIDs, changedIDs);
287
+ c->Set(Nan::New("freed_nodes").ToLocalChecked(), Nan::New<v8::Number>(changedIDs.size()));
288
+
289
+ // here's where we'll collect all the summary information
290
+ changeset changes;
291
+
292
+ // for each of these nodes, let's aggregate the change information
293
+ for (unsigned long i = 0; i < changedIDs.size(); i++) {
294
+ const HeapGraphNode * n = before->GetNodeById(changedIDs[i]);
295
+ manageChange(changes, n, false);
296
+ }
297
+
298
+ changedIDs.clear();
299
+
300
+ // after - before will reveal nodes added (memory allocated)
301
+ setDiff(afterIDs, beforeIDs, changedIDs);
302
+
303
+ c->Set(Nan::New("allocated_nodes").ToLocalChecked(), Nan::New<v8::Number>(changedIDs.size()));
304
+
305
+ for (unsigned long i = 0; i < changedIDs.size(); i++) {
306
+ const HeapGraphNode * n = after->GetNodeById(changedIDs[i]);
307
+ manageChange(changes, n, true);
308
+ }
309
+
310
+ c->Set(Nan::New("details").ToLocalChecked(), changesetToObject(changes));
311
+
312
+ return scope.Escape(o);
313
+ }
314
+
315
+ NAN_METHOD(heapdiff::HeapDiff::End)
316
+ {
317
+ // take another snapshot and compare them
318
+ Nan::HandleScope scope;
319
+
320
+ HeapDiff *t = Unwrap<HeapDiff>( info.This() );
321
+
322
+ // How shall we deal with double .end()ing? The only reasonable
323
+ // approach seems to be an exception, cause nothing else makes
324
+ // sense.
325
+ if (t->ended) {
326
+ return Nan::ThrowError("attempt to end() a HeapDiff that was already ended");
327
+ }
328
+ t->ended = true;
329
+
330
+ s_inProgress = true;
331
+ #if (NODE_MODULE_VERSION >= 0x002D)
332
+ t->after = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(NULL);
333
+ #else
334
+ #if (NODE_MODULE_VERSION > 0x000B)
335
+ t->after = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(Nan::New<v8::String>("").ToLocalChecked(), NULL);
336
+ #else
337
+ t->after = v8::HeapProfiler::TakeSnapshot(Nan::New<v8::String>("").ToLocalChecked(), HeapSnapshot::kFull, NULL);
338
+ #endif
339
+ #endif
340
+ s_inProgress = false;
341
+
342
+ v8::Local<Value> comparison = compare(t->before, t->after);
343
+ // free early, free often. I mean, after all, this process we're in is
344
+ // probably having memory problems. We want to help her.
345
+ ((HeapSnapshot *) t->before)->Delete();
346
+ t->before = NULL;
347
+ ((HeapSnapshot *) t->after)->Delete();
348
+ t->after = NULL;
349
+
350
+ info.GetReturnValue().Set(comparison);
351
+ }
@@ -0,0 +1,34 @@
1
+ /*
2
+ * 2012|lloyd|http://wtfpl.org
3
+ */
4
+
5
+ #ifndef __HEADDIFF_H
6
+ #define __HEADDIFF_H
7
+
8
+ #include <v8.h>
9
+ #include <v8-profiler.h>
10
+ #include <node.h>
11
+ #include <nan.h>
12
+
13
+ namespace heapdiff
14
+ {
15
+ class HeapDiff : public Nan::ObjectWrap
16
+ {
17
+ public:
18
+ static void Initialize ( v8::Handle<v8::Object> target );
19
+
20
+ static NAN_METHOD(New);
21
+ static NAN_METHOD(End);
22
+ static bool InProgress();
23
+
24
+ protected:
25
+ HeapDiff();
26
+ ~HeapDiff();
27
+ private:
28
+ const v8::HeapSnapshot * before;
29
+ const v8::HeapSnapshot * after;
30
+ bool ended;
31
+ };
32
+ };
33
+
34
+ #endif
package/src/init.cc ADDED
@@ -0,0 +1,26 @@
1
+ /*
2
+ * 2012|lloyd|do what the fuck you want to
3
+ */
4
+
5
+ #include <v8.h>
6
+ #include <node.h>
7
+
8
+ #include "heapdiff.hh"
9
+ #include "memwatch.hh"
10
+
11
+ extern "C" {
12
+ void init (v8::Handle<v8::Object> target)
13
+ {
14
+ v8::Isolate * isolate = target->GetIsolate();
15
+
16
+ Nan::HandleScope scope;
17
+ heapdiff::HeapDiff::Initialize(target);
18
+
19
+ Nan::SetMethod(target, "upon_gc", memwatch::upon_gc);
20
+ Nan::SetMethod(target, "gc", memwatch::trigger_gc);
21
+
22
+ isolate->AddGCEpilogueCallback(memwatch::after_gc);
23
+ }
24
+
25
+ NODE_MODULE(memwatch, init);
26
+ };
@@ -0,0 +1,263 @@
1
+ /*
2
+ * 2012|lloyd|http://wtfpl.org
3
+ */
4
+
5
+ #include "platformcompat.hh"
6
+ #include "memwatch.hh"
7
+ #include "heapdiff.hh"
8
+ #include "util.hh"
9
+
10
+ #include <node.h>
11
+ #include <node_version.h>
12
+
13
+ #include <string>
14
+ #include <cstring>
15
+ #include <sstream>
16
+
17
+ #include <math.h> // for pow
18
+ #include <time.h> // for time
19
+
20
+ using namespace v8;
21
+ using namespace node;
22
+
23
+ Handle<Object> g_context;
24
+ Nan::Callback *g_cb;
25
+
26
+ struct Baton {
27
+ uv_work_t req;
28
+ size_t heapUsage;
29
+ GCType type;
30
+ GCCallbackFlags flags;
31
+ };
32
+
33
+ static const unsigned int RECENT_PERIOD = 10;
34
+ static const unsigned int ANCIENT_PERIOD = 120;
35
+
36
+ static struct
37
+ {
38
+ // counts of different types of gc events
39
+ unsigned int gc_full;
40
+ unsigned int gc_inc;
41
+ unsigned int gc_compact;
42
+
43
+ // last base heap size as measured *right* after GC
44
+ unsigned int last_base;
45
+
46
+ // the estimated "base memory" usage of the javascript heap
47
+ // over the RECENT_PERIOD number of GC runs
48
+ unsigned int base_recent;
49
+
50
+ // the estimated "base memory" usage of the javascript heap
51
+ // over the ANCIENT_PERIOD number of GC runs
52
+ unsigned int base_ancient;
53
+
54
+ // the most extreme values we've seen for base heap size
55
+ unsigned int base_max;
56
+ unsigned int base_min;
57
+
58
+ // leak detection!
59
+
60
+ // the period from which this leak analysis starts
61
+ time_t leak_time_start;
62
+ // the base memory for the detection period
63
+ time_t leak_base_start;
64
+ // the number of consecutive compactions for which we've grown
65
+ unsigned int consecutive_growth;
66
+ } s_stats;
67
+
68
+ static Local<Value> getLeakReport(size_t heapUsage)
69
+ {
70
+ Nan::EscapableHandleScope scope;
71
+
72
+ size_t growth = heapUsage - s_stats.leak_base_start;
73
+ int now = time(NULL);
74
+ int delta = now - s_stats.leak_time_start;
75
+
76
+ Local<Object> leakReport = Nan::New<v8::Object>();
77
+ //leakReport->Set(Nan::New("start").ToLocalChecked(), NODE_UNIXTIME_V8(s_stats.leak_time_start));
78
+ //leakReport->Set(Nan::New("end").ToLocalChecked(), NODE_UNIXTIME_V8(now));
79
+ leakReport->Set(Nan::New("growth").ToLocalChecked(), Nan::New<v8::Number>(growth));
80
+
81
+ std::stringstream ss;
82
+ ss << "heap growth over 5 consecutive GCs ("
83
+ << mw_util::niceDelta(delta) << ") - "
84
+ << mw_util::niceSize(growth / ((double) delta / (60.0 * 60.0))) << "/hr";
85
+
86
+ leakReport->Set(Nan::New("reason").ToLocalChecked(), Nan::New(ss.str().c_str()).ToLocalChecked());
87
+
88
+ return scope.Escape(leakReport);
89
+ }
90
+
91
+ static void AsyncMemwatchAfter(uv_work_t* request) {
92
+ Nan::HandleScope scope;
93
+
94
+ Baton * b = (Baton *) request->data;
95
+
96
+ // do the math in C++, permanent
97
+ // record the type of GC event that occured
98
+ if (b->type == kGCTypeMarkSweepCompact) s_stats.gc_full++;
99
+ else s_stats.gc_inc++;
100
+
101
+ if (
102
+ #if NODE_VERSION_AT_LEAST(0,8,0)
103
+ b->type == kGCTypeMarkSweepCompact
104
+ #else
105
+ b->flags == kGCCallbackFlagCompacted
106
+ #endif
107
+ ) {
108
+ // leak detection code. has the heap usage grown?
109
+ if (s_stats.last_base < b->heapUsage) {
110
+ if (s_stats.consecutive_growth == 0) {
111
+ s_stats.leak_time_start = time(NULL);
112
+ s_stats.leak_base_start = b->heapUsage;
113
+ }
114
+
115
+ s_stats.consecutive_growth++;
116
+
117
+ // consecutive growth over 5 GCs suggests a leak
118
+ if (s_stats.consecutive_growth >= 5) {
119
+ // reset to zero
120
+ s_stats.consecutive_growth = 0;
121
+
122
+ // emit a leak report!
123
+ Local<Value> argv[3];
124
+ argv[0] = Nan::New<v8::Boolean>(false);
125
+ // the type of event to emit
126
+ argv[1] = Nan::New("leak").ToLocalChecked();
127
+ argv[2] = getLeakReport(b->heapUsage);
128
+
129
+ Nan::AsyncResource async("nan:Callback:Call");
130
+ g_cb->Call(3, argv, &async);
131
+ }
132
+ } else {
133
+ s_stats.consecutive_growth = 0;
134
+ }
135
+
136
+ // update last_base
137
+ s_stats.last_base = b->heapUsage;
138
+
139
+ // update compaction count
140
+ s_stats.gc_compact++;
141
+
142
+ // the first ten compactions we'll use a different algorithm to
143
+ // dampen out wider memory fluctuation at startup
144
+ if (s_stats.gc_compact < RECENT_PERIOD) {
145
+ double decay = pow(s_stats.gc_compact / RECENT_PERIOD, 2.5);
146
+ decay *= s_stats.gc_compact;
147
+ if (ISINF(decay) || ISNAN(decay)) decay = 0;
148
+ s_stats.base_recent = ((s_stats.base_recent * decay) +
149
+ s_stats.last_base) / (decay + 1);
150
+
151
+ decay = pow(s_stats.gc_compact / RECENT_PERIOD, 2.4);
152
+ decay *= s_stats.gc_compact;
153
+ s_stats.base_ancient = ((s_stats.base_ancient * decay) +
154
+ s_stats.last_base) / (1 + decay);
155
+
156
+ } else {
157
+ s_stats.base_recent = ((s_stats.base_recent * (RECENT_PERIOD - 1)) +
158
+ s_stats.last_base) / RECENT_PERIOD;
159
+ double decay = FMIN(ANCIENT_PERIOD, s_stats.gc_compact);
160
+ s_stats.base_ancient = ((s_stats.base_ancient * (decay - 1)) +
161
+ s_stats.last_base) / decay;
162
+ }
163
+
164
+ // only record min/max after 3 gcs to let initial instability settle
165
+ if (s_stats.gc_compact >= 3) {
166
+ if (!s_stats.base_min || s_stats.base_min > s_stats.last_base) {
167
+ s_stats.base_min = s_stats.last_base;
168
+ }
169
+
170
+ if (!s_stats.base_max || s_stats.base_max < s_stats.last_base) {
171
+ s_stats.base_max = s_stats.last_base;
172
+ }
173
+ }
174
+
175
+ // if there are any listeners, it's time to emit!
176
+ if (!g_cb->IsEmpty()) {
177
+ Local<Value> argv[3];
178
+ // magic argument to indicate to the callback all we want to know is whether there are
179
+ // listeners (here we don't)
180
+ argv[0] = Nan::New<v8::Boolean>(true);
181
+
182
+ //Handle<Value> haveListeners = g_cb->call(1, argv);
183
+
184
+
185
+ double ut= 0.0;
186
+ if (s_stats.base_ancient) {
187
+ ut = (double) ROUND(((double) (s_stats.base_recent - s_stats.base_ancient) /
188
+ (double) s_stats.base_ancient) * 1000.0) / 10.0;
189
+ }
190
+
191
+ // ok, there are listeners, we actually must serialize and emit this stats event
192
+ Local<Object> stats = Nan::New<v8::Object>();
193
+ stats->Set(Nan::New("num_full_gc").ToLocalChecked(), Nan::New(s_stats.gc_full));
194
+ stats->Set(Nan::New("num_inc_gc").ToLocalChecked(), Nan::New(s_stats.gc_inc));
195
+ stats->Set(Nan::New("heap_compactions").ToLocalChecked(), Nan::New(s_stats.gc_compact));
196
+ stats->Set(Nan::New("usage_trend").ToLocalChecked(), Nan::New(ut));
197
+ stats->Set(Nan::New("estimated_base").ToLocalChecked(), Nan::New(s_stats.base_recent));
198
+ stats->Set(Nan::New("current_base").ToLocalChecked(), Nan::New(s_stats.last_base));
199
+ stats->Set(Nan::New("min").ToLocalChecked(), Nan::New(s_stats.base_min));
200
+ stats->Set(Nan::New("max").ToLocalChecked(), Nan::New(s_stats.base_max));
201
+ argv[0] = Nan::New<v8::Boolean>(false);
202
+ // the type of event to emit
203
+ argv[1] = Nan::New("stats").ToLocalChecked();
204
+ argv[2] = stats;
205
+
206
+ Nan::AsyncResource async("nan:Callback:Call");
207
+ g_cb->Call(3, argv, &async);
208
+ }
209
+ }
210
+
211
+ delete b;
212
+ }
213
+
214
+ static void noop_work_func(uv_work_t *) { }
215
+
216
+ void memwatch::after_gc(Isolate* isolate, GCType type, GCCallbackFlags flags)
217
+ {
218
+ if (heapdiff::HeapDiff::InProgress()) return;
219
+
220
+ Nan::HandleScope scope;
221
+
222
+ Baton * baton = new Baton;
223
+ v8::HeapStatistics hs;
224
+
225
+ Nan::GetHeapStatistics(&hs);
226
+
227
+ baton->heapUsage = hs.used_heap_size();
228
+ baton->type = type;
229
+ baton->flags = flags;
230
+ baton->req.data = (void *) baton;
231
+
232
+ // schedule our work to run in a moment, once gc has fully completed.
233
+ //
234
+ // here we pass a noop work function to work around a flaw in libuv,
235
+ // uv_queue_work on unix works fine, but will will crash on
236
+ // windows. see: https://github.com/joyent/libuv/pull/629
237
+ uv_queue_work(uv_default_loop(), &(baton->req),
238
+ noop_work_func, (uv_after_work_cb)AsyncMemwatchAfter);
239
+ }
240
+
241
+ NAN_METHOD(memwatch::upon_gc) {
242
+ Nan::HandleScope scope;
243
+ if (info.Length() >= 1 && info[0]->IsFunction()) {
244
+ g_cb = new Nan::Callback(info[0].As<v8::Function>());
245
+ }
246
+ info.GetReturnValue().Set(Nan::Undefined());
247
+ }
248
+
249
+ NAN_METHOD(memwatch::trigger_gc) {
250
+ Nan::HandleScope scope;
251
+ int deadline_in_ms = 500;
252
+ if (info.Length() >= 1 && info[0]->IsNumber()) {
253
+ deadline_in_ms = (int)(info[0]->Int32Value());
254
+ }
255
+ #if (NODE_MODULE_VERSION >= 0x002D)
256
+ Nan::IdleNotification(deadline_in_ms);
257
+ Nan::LowMemoryNotification();
258
+ #else
259
+ while(!Nan::IdleNotification(deadline_in_ms)) {};
260
+ Nan::LowMemoryNotification();
261
+ #endif
262
+ info.GetReturnValue().Set(Nan::Undefined());
263
+ }
@@ -0,0 +1,18 @@
1
+ /*
2
+ * 2012|lloyd|http://wtfpl.org
3
+ */
4
+
5
+ #ifndef __MEMWATCH_HH
6
+ #define __MEMWATCH_HH
7
+
8
+ #include <node.h>
9
+ #include <nan.h>
10
+
11
+ namespace memwatch
12
+ {
13
+ NAN_METHOD(upon_gc);
14
+ NAN_METHOD(trigger_gc);
15
+ void after_gc(v8::Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags);
16
+ };
17
+
18
+ #endif