From 56001da581d21ca9278790a300c4cc84ee46389d Mon Sep 17 00:00:00 2001 From: Andrey Sidorov Date: Sat, 8 Mar 2014 18:04:09 +1100 Subject: [PATCH] use property setter in debug mode to map seq num to stack trace --- lib/xcore.js | 71 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/lib/xcore.js b/lib/xcore.js index cee0ea4..be12cba 100644 --- a/lib/xcore.js +++ b/lib/xcore.js @@ -64,8 +64,26 @@ function XClient(stream, displayNum, screenNum, options) this.pack_stream = pack_stream; this.rsrc_id = 0; // generated for each new resource - this.seq_num = 0; // incremented in each request. (even if we don't expect reply) - this.seq2stack = {}; // debug: map seq_num to stack at the moment request was issued + var cli = this; + if (cli.options.debug) { + this.seq_num_ = 0; + this.seq2stack = {}; // debug: map seq_num to stack at the moment request was issued + Object.defineProperty(cli, "seq_num", { + set : function(v) { + cli.seq_num_ = v; + var err = new Error(); + Error.captureStackTrace(err, arguments.callee); + err.timestamp = Date.now(); + cli.seq2stack[client.seq_num] = err; + }, + get: function() { + return cli.seq_num_; + } + }); + } else { + this.seq_num = 0; + } + // in/out packets indexed by sequence ID this.replies = {}; @@ -83,6 +101,7 @@ function XClient(stream, displayNum, screenNum, options) this.event_consumers = {}; // maps window id to eventemitter TODO: bad name this.eventParsers = {}; + this.errorParsers = {}; this.importRequestsFromTemplates(this, coreRequests); @@ -138,18 +157,6 @@ XClient.prototype.importRequestsFromTemplates = function(target, reqs) else client.seq_num++; - // disable long stack trace for the moment, it's too expensive - // performance when enabled (travis-ci worker, Xfvb): 70000 requests finished in 52196 ms, 1341.0989347842747 req/sec - // without: 70000 requests finished in 14904 ms, 4696.725711218465 req/sec - // MBPro, XQuartz: with 3600 req/sec, without 24200 req/sec - if (this.options.debug === true) { - var err = new Error(); - err.name = reqName; //??? - Error.captureStackTrace(err, arguments.callee); - err.timestamp = Date.now(); - client.seq2stack[client.seq_num] = err; - } - // is it fast? var args = Array.prototype.slice.call(req_proxy.arguments); @@ -171,7 +178,7 @@ XClient.prototype.importRequestsFromTemplates = function(target, reqs) var value = req_proxy.arguments[1]; if (client.atoms[value]) { -- client.seq_num; - return process.nextTick(function() { + return setImmediate(function() { callback(undefined, client.atoms[value]); }); } else { @@ -197,7 +204,7 @@ XClient.prototype.importRequestsFromTemplates = function(target, reqs) var atom = req_proxy.arguments[0]; if (client.atom_names[atom]) { -- client.seq_num; - return process.nextTick(function() { + return setImmediate(function() { callback(undefined, client.atom_names[atom]); }); } else { @@ -412,12 +419,22 @@ XClient.prototype.expectReplyHeader = function() error.error = error_code; error.seq = seq_num; error.message = xerrors.errorText[error_code]; - if (client.options.debug) - error.stack = client.seq2stack[error.seq] + if (client.options.debug) { + error.longstack = client.seq2stack[error.seq] + console.log(client.seq2stack[error.seq].stack); + } // unpack error packet (32 bytes for all error types, 8 of them in CCSL header) client.pack_stream.get(24, function(buf) { + var extUnpacker = client.errorParsers[type]; + if (extUnpacker) { + var event = extUnpacker(type, seq_num, bad_value, body); + client.emit(error, event); + client.expectReplyHeader(); + return; + } + // TODO: dispatch, use sequence number //TODO: add more generic way to read common values // if (error_code == 14) @@ -569,26 +586,30 @@ module.exports.createClient = function(options, initCb) var client = new XClient(stream, displayNum, screenNum, options); if (initCb) { + var cbCalled = false; client.on('connect', function(display) { - // Once connected don't call initCb on error, just emit - stream.removeListener('error', initCb); - stream.on('error', function(err) { - client.emit('error', err); - }); // opt-in BigReq if (!options.disableBigRequests) { client.require('big-requests', function(BigReq) { BigReq.Enable(function(err, maxLen) { display.max_request_length = maxLen; + cbCalled = true; initCb(undefined, display); }); }); } else { + cbCalled = true; initCb(undefined, display); } }); - - stream.on('error', initCb); + stream.on('error', function(err) { + if (cbCalled) + client.emit('error', err); + else { + cbCalled = true; + initCb(err); + } + }); } return client; }