Skip to content

Commit 010222d

Browse files
bnoordhuistjfontaine
authored andcommitted
src: add tracing.v8.on('gc') statistics hooks
Add a new 'tracing' module with a v8 property that lets the user register listeners for gc events. The listeners are invoked after every garbage collection cycle with 'before' and 'after' statistics. Useful for monitoring tools that want to keep track of memory usage.
1 parent 4dc6f4a commit 010222d

File tree

7 files changed

+403
-4
lines changed

7 files changed

+403
-4
lines changed

lib/repl.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ exports.writer = util.inspect;
7373
exports._builtinLibs = ['assert', 'buffer', 'child_process', 'cluster',
7474
'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'https', 'net',
7575
'os', 'path', 'punycode', 'querystring', 'readline', 'stream',
76-
'string_decoder', 'tls', 'tty', 'url', 'util', 'vm', 'zlib', 'smalloc'];
76+
'string_decoder', 'tls', 'tty', 'url', 'util', 'vm', 'zlib', 'smalloc',
77+
'tracing'];
7778

7879

7980
function REPLServer(prompt, stream, eval_, useGlobal, ignoreUndefined) {

lib/tracing.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
var EventEmitter = require('events');
23+
var binding = process.binding('v8');
24+
25+
var v8 = exports.v8 = new EventEmitter();
26+
27+
28+
function emitGC(before, after) {
29+
v8.emit('gc', before, after);
30+
}
31+
32+
33+
v8.on('newListener', function(name) {
34+
if (name === 'gc' && EventEmitter.listenerCount(this, name) === 0) {
35+
binding.startGarbageCollectionTracking(emitGC);
36+
}
37+
});
38+
39+
40+
v8.on('removeListener', function(name) {
41+
if (name === 'gc' && EventEmitter.listenerCount(this, name) === 0) {
42+
binding.stopGarbageCollectionTracking();
43+
}
44+
});

node.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
'lib/string_decoder.js',
5757
'lib/sys.js',
5858
'lib/timers.js',
59+
'lib/tracing.js',
5960
'lib/tls.js',
6061
'lib/_tls_legacy.js',
6162
'lib/_tls_wrap.js',
@@ -96,6 +97,7 @@
9697
'src/node_javascript.cc',
9798
'src/node_main.cc',
9899
'src/node_os.cc',
100+
'src/node_v8.cc',
99101
'src/node_stat_watcher.cc',
100102
'src/node_watchdog.cc',
101103
'src/node_zlib.cc',

src/env-inl.h

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,48 @@
3333

3434
namespace node {
3535

36+
inline Environment::GCInfo::GCInfo()
37+
: type_(static_cast<v8::GCType>(0)),
38+
flags_(static_cast<v8::GCCallbackFlags>(0)),
39+
timestamp_(0) {
40+
}
41+
42+
inline Environment::GCInfo::GCInfo(v8::Isolate* isolate,
43+
v8::GCType type,
44+
v8::GCCallbackFlags flags,
45+
uint64_t timestamp)
46+
: type_(type),
47+
flags_(flags),
48+
timestamp_(timestamp) {
49+
isolate->GetHeapStatistics(&stats_);
50+
}
51+
52+
inline v8::GCType Environment::GCInfo::type() const {
53+
return type_;
54+
}
55+
56+
inline v8::GCCallbackFlags Environment::GCInfo::flags() const {
57+
return flags_;
58+
}
59+
60+
inline v8::HeapStatistics* Environment::GCInfo::stats() const {
61+
// TODO(bnoordhuis) Const-ify once https://codereview.chromium.org/63693005
62+
// lands and makes it way into a stable release.
63+
return const_cast<v8::HeapStatistics*>(&stats_);
64+
}
65+
66+
inline uint64_t Environment::GCInfo::timestamp() const {
67+
return timestamp_;
68+
}
69+
70+
inline Environment::IsolateData* Environment::IsolateData::Get(
71+
v8::Isolate* isolate) {
72+
return static_cast<IsolateData*>(isolate->GetData());
73+
}
74+
3675
inline Environment::IsolateData* Environment::IsolateData::GetOrCreate(
3776
v8::Isolate* isolate) {
38-
IsolateData* isolate_data = static_cast<IsolateData*>(isolate->GetData());
77+
IsolateData* isolate_data = Get(isolate);
3978
if (isolate_data == NULL) {
4079
isolate_data = new IsolateData(isolate);
4180
isolate->SetData(isolate_data);
@@ -59,6 +98,7 @@ inline Environment::IsolateData::IsolateData(v8::Isolate* isolate)
5998
PER_ISOLATE_STRING_PROPERTIES(V)
6099
#undef V
61100
ref_count_(0) {
101+
QUEUE_INIT(&gc_tracker_queue_);
62102
}
63103

64104
inline uv_loop_t* Environment::IsolateData::event_loop() const {
@@ -187,6 +227,7 @@ inline Environment::Environment(v8::Local<v8::Context> context)
187227
set_binding_cache_object(v8::Object::New());
188228
set_module_load_list_array(v8::Array::New());
189229
RB_INIT(&cares_task_list_);
230+
QUEUE_INIT(&gc_tracker_queue_);
190231
}
191232

192233
inline Environment::~Environment() {

src/env.h

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "util.h"
2828
#include "uv.h"
2929
#include "v8.h"
30+
#include "queue.h"
3031

3132
#include <stdint.h>
3233

@@ -52,9 +53,9 @@ namespace node {
5253
// for the sake of convenience.
5354
#define PER_ISOLATE_STRING_PROPERTIES(V) \
5455
V(address_string, "address") \
55-
V(atime_string, "atime") \
56-
V(async, "async") \
5756
V(async_queue_string, "_asyncQueue") \
57+
V(async, "async") \
58+
V(atime_string, "atime") \
5859
V(birthtime_string, "birthtime") \
5960
V(blksize_string, "blksize") \
6061
V(blocks_string, "blocks") \
@@ -78,16 +79,19 @@ namespace node {
7879
V(family_string, "family") \
7980
V(fatal_exception_string, "_fatalException") \
8081
V(fingerprint_string, "fingerprint") \
82+
V(flags_string, "flags") \
8183
V(gid_string, "gid") \
8284
V(handle_string, "handle") \
8385
V(headers_string, "headers") \
86+
V(heap_size_limit_string, "heap_size_limit") \
8487
V(heap_total_string, "heapTotal") \
8588
V(heap_used_string, "heapUsed") \
8689
V(immediate_callback_string, "_immediateCallback") \
8790
V(ino_string, "ino") \
8891
V(ipv4_string, "IPv4") \
8992
V(ipv6_string, "IPv6") \
9093
V(issuer_string, "issuer") \
94+
V(mark_sweep_compact_string, "mark-sweep-compact") \
9195
V(method_string, "method") \
9296
V(mode_string, "mode") \
9397
V(modulus_string, "modulus") \
@@ -114,6 +118,7 @@ namespace node {
114118
V(rdev_string, "rdev") \
115119
V(rename_string, "rename") \
116120
V(rss_string, "rss") \
121+
V(scavenge_string, "scavenge") \
117122
V(serial_number_string, "serialNumber") \
118123
V(servername_string, "servername") \
119124
V(session_id_string, "sessionId") \
@@ -127,10 +132,16 @@ namespace node {
127132
V(subject_string, "subject") \
128133
V(subjectaltname_string, "subjectaltname") \
129134
V(syscall_string, "syscall") \
135+
V(timestamp_string, "timestamp") \
130136
V(tls_ticket_string, "tlsTicket") \
137+
V(total_heap_size_executable_string, "total_heap_size_executable") \
138+
V(total_heap_size_string, "total_heap_size") \
139+
V(total_physical_size_string, "total_physical_size") \
140+
V(type_string, "type") \
131141
V(uid_string, "uid") \
132142
V(upgrade_string, "upgrade") \
133143
V(url_string, "url") \
144+
V(used_heap_size_string, "used_heap_size") \
134145
V(valid_from_string, "valid_from") \
135146
V(valid_to_string, "valid_to") \
136147
V(version_major_string, "versionMajor") \
@@ -146,6 +157,7 @@ namespace node {
146157
V(buffer_constructor_function, v8::Function) \
147158
V(context, v8::Context) \
148159
V(domain_array, v8::Array) \
160+
V(gc_info_callback_function, v8::Function) \
149161
V(module_load_list_array, v8::Array) \
150162
V(pipe_constructor_template, v8::FunctionTemplate) \
151163
V(process_object, v8::Object) \
@@ -252,6 +264,10 @@ class Environment {
252264
static inline Environment* New(v8::Local<v8::Context> context);
253265
inline void Dispose();
254266

267+
// Defined in src/node_profiler.cc.
268+
void StartGarbageCollectionTracking(v8::Local<v8::Function> callback);
269+
void StopGarbageCollectionTracking();
270+
255271
inline v8::Isolate* isolate() const;
256272
inline uv_loop_t* event_loop() const;
257273
inline bool has_async_listener() const;
@@ -297,10 +313,13 @@ class Environment {
297313
#undef V
298314

299315
private:
316+
class GCInfo;
300317
class IsolateData;
301318
inline explicit Environment(v8::Local<v8::Context> context);
302319
inline ~Environment();
303320
inline IsolateData* isolate_data() const;
321+
void AfterGarbageCollectionCallback(const GCInfo* before,
322+
const GCInfo* after);
304323

305324
enum ContextEmbedderDataIndex {
306325
kContextEmbedderDataIndex = NODE_CONTEXT_EMBEDDER_DATA_INDEX
@@ -320,28 +339,64 @@ class Environment {
320339
ares_task_list cares_task_list_;
321340
bool using_smalloc_alloc_cb_;
322341
bool using_domains_;
342+
QUEUE gc_tracker_queue_;
323343

324344
#define V(PropertyName, TypeName) \
325345
v8::Persistent<TypeName> PropertyName ## _;
326346
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
327347
#undef V
328348

349+
class GCInfo {
350+
public:
351+
inline GCInfo();
352+
inline GCInfo(v8::Isolate* isolate,
353+
v8::GCType type,
354+
v8::GCCallbackFlags flags,
355+
uint64_t timestamp);
356+
inline v8::GCType type() const;
357+
inline v8::GCCallbackFlags flags() const;
358+
// TODO(bnoordhuis) Const-ify once https://codereview.chromium.org/63693005
359+
// lands and makes it way into a stable release.
360+
inline v8::HeapStatistics* stats() const;
361+
inline uint64_t timestamp() const;
362+
private:
363+
v8::GCType type_;
364+
v8::GCCallbackFlags flags_;
365+
v8::HeapStatistics stats_;
366+
uint64_t timestamp_;
367+
};
368+
329369
// Per-thread, reference-counted singleton.
330370
class IsolateData {
331371
public:
332372
static inline IsolateData* GetOrCreate(v8::Isolate* isolate);
333373
inline void Put();
334374
inline uv_loop_t* event_loop() const;
335375

376+
// Defined in src/node_profiler.cc.
377+
void StartGarbageCollectionTracking(Environment* env);
378+
void StopGarbageCollectionTracking(Environment* env);
379+
336380
#define V(PropertyName, StringValue) \
337381
inline v8::Local<v8::String> PropertyName() const;
338382
PER_ISOLATE_STRING_PROPERTIES(V)
339383
#undef V
340384

341385
private:
386+
inline static IsolateData* Get(v8::Isolate* isolate);
342387
inline explicit IsolateData(v8::Isolate* isolate);
343388
inline v8::Isolate* isolate() const;
344389

390+
// Defined in src/node_profiler.cc.
391+
static void BeforeGarbageCollection(v8::Isolate* isolate,
392+
v8::GCType type,
393+
v8::GCCallbackFlags flags);
394+
static void AfterGarbageCollection(v8::Isolate* isolate,
395+
v8::GCType type,
396+
v8::GCCallbackFlags flags);
397+
void BeforeGarbageCollection(v8::GCType type, v8::GCCallbackFlags flags);
398+
void AfterGarbageCollection(v8::GCType type, v8::GCCallbackFlags flags);
399+
345400
uv_loop_t* const event_loop_;
346401
v8::Isolate* const isolate_;
347402

@@ -351,6 +406,9 @@ class Environment {
351406
#undef V
352407

353408
unsigned int ref_count_;
409+
QUEUE gc_tracker_queue_;
410+
GCInfo gc_info_before_;
411+
GCInfo gc_info_after_;
354412

355413
DISALLOW_COPY_AND_ASSIGN(IsolateData);
356414
};

0 commit comments

Comments
 (0)