The jscProfileMiddleware require some FB only modules, so moving to internal
Reviewed By: cwdick Differential Revision: D4441898 fbshipit-source-id: 0ab5aa4158fccf190e76d3582a8a12d9b1d23fa8
This commit is contained in:
Родитель
0623a47ecd
Коммит
c5f862b913
|
@ -1,209 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const SourceMapConsumer = require('source-map').SourceMapConsumer;
|
||||
const fs = require('fs');
|
||||
const http = require('http');
|
||||
const path = require('path');
|
||||
const urlLib = require('url');
|
||||
|
||||
class TreeTransformator {
|
||||
constructor() {
|
||||
this.urlResults = {};
|
||||
this.fakeNodeId = 1073741824;
|
||||
}
|
||||
|
||||
transform(tree, callback) {
|
||||
this.afterUrlsCacheBuild(tree, () => {
|
||||
callback(this.transformNode(tree));
|
||||
});
|
||||
}
|
||||
|
||||
// private
|
||||
createFakeNode(name, id) {
|
||||
return {
|
||||
'functionName': name,
|
||||
'scriptId': 0,
|
||||
'url': '',
|
||||
'lineNumber': 0,
|
||||
'columnNumber': 0,
|
||||
'hitCount': 0,
|
||||
'callUID': id,
|
||||
'children': [],
|
||||
'deoptReason': 'fake_node',
|
||||
'id': id,
|
||||
'positionTicks': [],
|
||||
};
|
||||
}
|
||||
|
||||
// private
|
||||
getFileUrl(source, lineNumber) {
|
||||
if (!source) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const url = urlLib.parse(
|
||||
'https://our.intern.facebook.com/intern/nuclide/open/arc/',
|
||||
true);
|
||||
let project = '';
|
||||
let root = '';
|
||||
|
||||
if (source.includes('fbsource/fbandroid/')) {
|
||||
project = 'facebook-fbandroid';
|
||||
root = 'fbsource/fbandroid/';
|
||||
} else if (source.includes('fbsource/fbobjc/')) {
|
||||
project = 'fbobjc';
|
||||
root = 'fbsource/fbobjc/';
|
||||
} else {
|
||||
return 'file://' + source;
|
||||
}
|
||||
|
||||
url.query = {
|
||||
'project' : project,
|
||||
'paths[0]' : source.substring(source.indexOf(root) + root.length),
|
||||
'lines[0]' : [lineNumber],
|
||||
};
|
||||
|
||||
return urlLib.format(url);
|
||||
}
|
||||
|
||||
// private
|
||||
transformNode(tree) {
|
||||
if (tree.url in this.urlResults) {
|
||||
const original = this.urlResults[tree.url].originalPositionFor({
|
||||
line: tree.lineNumber,
|
||||
column: tree.columnNumber,
|
||||
});
|
||||
const functionName = original.name
|
||||
|| (path.posix.basename(original.source || '') + ':' + original.line);
|
||||
if (tree.functionName === '(unnamed builtin)') {
|
||||
tree.functionName += ':' + functionName;
|
||||
} else {
|
||||
tree.functionName = tree.functionName || functionName;
|
||||
}
|
||||
tree.scriptId = tree.id;
|
||||
tree.url = this.getFileUrl(original.source, original.line);
|
||||
tree.lineNumber = original.line;
|
||||
tree.columnNumber = original.column;
|
||||
} else if (tree.deoptReason === 'outside_vm') {
|
||||
tree.functionName = 'OUTSIDE VM';
|
||||
}
|
||||
tree.children = tree.children.map((t) => this.transformNode(t));
|
||||
if (tree.deoptReason.startsWith('host:')) {
|
||||
// Creating a fake node to mark not doing JS, steal the id and hitCount
|
||||
// of the original node
|
||||
const fakeNode = this.createFakeNode('INSIDE VM', tree.id);
|
||||
tree.id = tree.callUID = this.fakeNodeId++;
|
||||
fakeNode.hitCount = tree.hitCount;
|
||||
tree.hitCount = 0;
|
||||
|
||||
// Append the fake node to the tree
|
||||
fakeNode.children = tree.children;
|
||||
tree.children = [fakeNode];
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
// private
|
||||
afterUrlsCacheBuild(tree, callback) {
|
||||
const 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 mapPath = parsedUrl.pathname.replace(/\.bundle$/, '.map');
|
||||
const options = {
|
||||
host: 'localhost',
|
||||
port: parsedUrl.port,
|
||||
path: mapPath + parsedUrl.search + '&babelSourcemap=true',
|
||||
};
|
||||
|
||||
http.get(options, (res) => {
|
||||
res.setEncoding('utf8');
|
||||
let sawEnd = false;
|
||||
let resBody = '';
|
||||
res.on('data', (chunk) => {
|
||||
resBody += chunk;
|
||||
}).on('end', () => {
|
||||
sawEnd = true;
|
||||
this.urlResults[url] = new SourceMapConsumer(resBody);
|
||||
callback();
|
||||
}).on('close', (err) => {
|
||||
if (!sawEnd) {
|
||||
console.error('Connection terminated prematurely because of: '
|
||||
+ err.code + ' for url: ' + url);
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}).on('error', (err) => {
|
||||
console.error('Could not get response from: ' + url);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function(req, res, next) {
|
||||
if (req.url !== '/jsc-profile') {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Received request from JSC profiler, post processing it...');
|
||||
const 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);
|
||||
});
|
||||
});
|
||||
};
|
|
@ -23,7 +23,6 @@ const getDevToolsMiddleware = require('./middleware/getDevToolsMiddleware');
|
|||
const heapCaptureMiddleware = require('./middleware/heapCaptureMiddleware.js');
|
||||
const http = require('http');
|
||||
const indexPageMiddleware = require('./middleware/indexPage');
|
||||
const jscProfilerMiddleware = require('./middleware/jscProfilerMiddleware');
|
||||
const loadRawBodyMiddleware = require('./middleware/loadRawBodyMiddleware');
|
||||
const messageSocket = require('./util/messageSocket.js');
|
||||
const openStackFrameInEditorMiddleware = require('./middleware/openStackFrameInEditorMiddleware');
|
||||
|
@ -49,7 +48,6 @@ function runServer(args, config, readyCallback) {
|
|||
.use(systraceProfileMiddleware)
|
||||
.use(heapCaptureMiddleware)
|
||||
.use(cpuProfilerMiddleware)
|
||||
.use(jscProfilerMiddleware)
|
||||
.use(indexPageMiddleware)
|
||||
.use(unless('/inspector', inspectorProxy.processRequest.bind(inspectorProxy)))
|
||||
.use(packagerServer.processRequest.bind(packagerServer));
|
||||
|
|
Загрузка…
Ссылка в новой задаче