#18 Using the bundle url resolve origin source in jsc profiler middleware

Reviewed By: bestander

Differential Revision: D3620292

fbshipit-source-id: e3c54fb30bc65ff1ddd650340a2905a9f0e16231
This commit is contained in:
Lukas Piatkowski 2016-08-02 11:10:55 -07:00 committed by Facebook Github Bot 8
parent 2231b21ed0
commit 066de9d820
1 changed files with 120 additions and 16 deletions

View File

@ -8,7 +8,104 @@
*/
'use strict';
const SourceMapConsumer = require('../../../node_modules/source-map').SourceMapConsumer;
const fs = require('fs');
const http = require('http');
const urlLib = require('url');
class TreeTransformator {
constructor() {
this.urlResults = {};
}
transform(tree, callback) {
this.afterUrlsCacheBuild(tree, () => {
callback(this.transformNode(tree));
});
}
// private
transformNode(tree) {
if (tree.url in this.urlResults) {
const original = this.urlResults[tree.url].originalPositionFor({
line: tree.lineNumber,
column: tree.columnNumber,
});
tree.scriptId = tree.id;
tree.url = 'file://' + original.source;
tree.lineNumber = original.line;
tree.columnNumber = original.column;
}
tree.children = tree.children.map((t) => this.transformNode(t));
return tree;
}
// private
afterUrlsCacheBuild(tree, callback) {
let urls = new Set();
this.gatherUrls(tree, urls);
let size = urls.size;
if (size === 0) {
callback();
} else {
urls.forEach((url) => {
this.callUrlCached(url, () => {
--size;
if (size === 0) {
callback();
}
});
});
}
}
// private
gatherUrls(tree, urls) {
urls.add(tree.url);
tree.children.map((t) => this.gatherUrls(t, urls));
}
// private
callUrlCached(url, callback) {
if (url === '' || url === null || url in this.urlResults) {
callback();
return;
}
const parsedUrl = urlLib.parse(url);
const options = {
host: parsedUrl.hostname,
port: parsedUrl.port,
path: parsedUrl.pathname.replace(/\.bundle$/, '.map') + parsedUrl.search,
};
http.get(options, (res) => {
res.setEncoding('utf8');
let sawEnd = false;
let resBody = '';
res.on('data', (chunk) => {
resBody += chunk;
}).on('end', () => {
sawEnd = true;
const map = JSON.parse(resBody.replace(/^\)\]\}'/, ''));
this.urlResults[url] = new SourceMapConsumer(map);
callback();
}).on('close', (err) => {
if (!sawEnd) {
console.error('Connection terminated prematurely because of: '
+ err.code + ' for url: ' + url);
this.urlResults[url] = null;
callback();
}
});
}).on('error', (err) => {
console.error('Could not get response from: ' + url);
this.urlResults[url] = null;
callback();
});
}
}
module.exports = function(req, res, next) {
if (req.url !== '/jsc-profile') {
@ -16,21 +113,28 @@ module.exports = function(req, res, next) {
return;
}
console.log('Dumping JSC profile information...');
const dumpName = '/tmp/jsc-profile_' + Date.now() + '.cpuprofile';
fs.writeFile(dumpName, req.rawBody, (err) => {
var response = '';
if (err) {
response =
'An error occured when trying to save the profile at ' + dumpName;
console.error(response, err);
} else {
response =
'Your profile was generated at\n\n' + dumpName + '\n\n' +
'Open `Chrome Dev Tools > Profiles > Load` '
+ 'and select the profile to visualize it.';
console.log(response);
}
res.end(response);
console.log('Received request from JSC profiler, post processing it...');
let profile = JSON.parse(req.rawBody);
(new TreeTransformator()).transform(profile.head, (newHead) => {
profile.head = newHead;
console.log('Dumping JSC profile information...');
const dumpName = '/tmp/jsc-profile_' + Date.now() + '.cpuprofile';
fs.writeFile(dumpName, JSON.stringify(profile), (err) => {
let response = '';
if (err) {
response =
'An error occured when trying to save the profile at ' + dumpName;
console.error(response, err);
} else {
response =
'Your profile was generated at\n\n' + dumpName + '\n\n' +
'Open `Chrome/Atom Dev Tools > Profiles > Load` '
+ 'and select the profile to visualize it.';
console.log(response);
}
res.end(response);
});
});
};