Skip to content

Commit e2651e9

Browse files
lukaspiatkowskiMorgan Pretty
authored andcommitted
facebook#18 Using the bundle url resolve origin source in jsc profiler middleware
Reviewed By: bestander Differential Revision: D3620292 fbshipit-source-id: e3c54fb30bc65ff1ddd650340a2905a9f0e16231
1 parent c04cb3b commit e2651e9

File tree

1 file changed

+120
-16
lines changed

1 file changed

+120
-16
lines changed

local-cli/server/middleware/jscProfilerMiddleware.js

Lines changed: 120 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,133 @@
88
*/
99
'use strict';
1010

11+
const SourceMapConsumer = require('../../../node_modules/source-map').SourceMapConsumer;
1112
const fs = require('fs');
13+
const http = require('http');
14+
const urlLib = require('url');
15+
16+
class TreeTransformator {
17+
constructor() {
18+
this.urlResults = {};
19+
}
20+
21+
transform(tree, callback) {
22+
this.afterUrlsCacheBuild(tree, () => {
23+
callback(this.transformNode(tree));
24+
});
25+
}
26+
27+
// private
28+
transformNode(tree) {
29+
if (tree.url in this.urlResults) {
30+
const original = this.urlResults[tree.url].originalPositionFor({
31+
line: tree.lineNumber,
32+
column: tree.columnNumber,
33+
});
34+
tree.scriptId = tree.id;
35+
tree.url = 'file://' + original.source;
36+
tree.lineNumber = original.line;
37+
tree.columnNumber = original.column;
38+
}
39+
tree.children = tree.children.map((t) => this.transformNode(t));
40+
return tree;
41+
}
42+
43+
// private
44+
afterUrlsCacheBuild(tree, callback) {
45+
let urls = new Set();
46+
this.gatherUrls(tree, urls);
47+
48+
let size = urls.size;
49+
if (size === 0) {
50+
callback();
51+
} else {
52+
urls.forEach((url) => {
53+
this.callUrlCached(url, () => {
54+
--size;
55+
if (size === 0) {
56+
callback();
57+
}
58+
});
59+
});
60+
}
61+
}
62+
63+
// private
64+
gatherUrls(tree, urls) {
65+
urls.add(tree.url);
66+
tree.children.map((t) => this.gatherUrls(t, urls));
67+
}
68+
69+
// private
70+
callUrlCached(url, callback) {
71+
if (url === '' || url === null || url in this.urlResults) {
72+
callback();
73+
return;
74+
}
75+
76+
const parsedUrl = urlLib.parse(url);
77+
const options = {
78+
host: parsedUrl.hostname,
79+
port: parsedUrl.port,
80+
path: parsedUrl.pathname.replace(/\.bundle$/, '.map') + parsedUrl.search,
81+
};
82+
83+
http.get(options, (res) => {
84+
res.setEncoding('utf8');
85+
let sawEnd = false;
86+
let resBody = '';
87+
res.on('data', (chunk) => {
88+
resBody += chunk;
89+
}).on('end', () => {
90+
sawEnd = true;
91+
const map = JSON.parse(resBody.replace(/^\)\]\}'/, ''));
92+
this.urlResults[url] = new SourceMapConsumer(map);
93+
callback();
94+
}).on('close', (err) => {
95+
if (!sawEnd) {
96+
console.error('Connection terminated prematurely because of: '
97+
+ err.code + ' for url: ' + url);
98+
this.urlResults[url] = null;
99+
callback();
100+
}
101+
});
102+
}).on('error', (err) => {
103+
console.error('Could not get response from: ' + url);
104+
this.urlResults[url] = null;
105+
callback();
106+
});
107+
}
108+
}
12109

13110
module.exports = function(req, res, next) {
14111
if (req.url !== '/jsc-profile') {
15112
next();
16113
return;
17114
}
18115

19-
console.log('Dumping JSC profile information...');
20-
const dumpName = '/tmp/jsc-profile_' + Date.now() + '.cpuprofile';
21-
fs.writeFile(dumpName, req.rawBody, (err) => {
22-
var response = '';
23-
if (err) {
24-
response =
25-
'An error occured when trying to save the profile at ' + dumpName;
26-
console.error(response, err);
27-
} else {
28-
response =
29-
'Your profile was generated at\n\n' + dumpName + '\n\n' +
30-
'Open `Chrome Dev Tools > Profiles > Load` '
31-
+ 'and select the profile to visualize it.';
32-
console.log(response);
33-
}
34-
res.end(response);
116+
console.log('Received request from JSC profiler, post processing it...');
117+
let profile = JSON.parse(req.rawBody);
118+
(new TreeTransformator()).transform(profile.head, (newHead) => {
119+
profile.head = newHead;
120+
121+
console.log('Dumping JSC profile information...');
122+
const dumpName = '/tmp/jsc-profile_' + Date.now() + '.cpuprofile';
123+
124+
fs.writeFile(dumpName, JSON.stringify(profile), (err) => {
125+
let response = '';
126+
if (err) {
127+
response =
128+
'An error occured when trying to save the profile at ' + dumpName;
129+
console.error(response, err);
130+
} else {
131+
response =
132+
'Your profile was generated at\n\n' + dumpName + '\n\n' +
133+
'Open `Chrome/Atom Dev Tools > Profiles > Load` '
134+
+ 'and select the profile to visualize it.';
135+
console.log(response);
136+
}
137+
res.end(response);
138+
});
35139
});
36140
};

0 commit comments

Comments
 (0)