|
8 | 8 | */
|
9 | 9 | 'use strict';
|
10 | 10 |
|
| 11 | +const SourceMapConsumer = require('../../../node_modules/source-map').SourceMapConsumer; |
11 | 12 | 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 | +} |
12 | 109 |
|
13 | 110 | module.exports = function(req, res, next) {
|
14 | 111 | if (req.url !== '/jsc-profile') {
|
15 | 112 | next();
|
16 | 113 | return;
|
17 | 114 | }
|
18 | 115 |
|
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 | + }); |
35 | 139 | });
|
36 | 140 | };
|
0 commit comments