From 1199c5ade914ee3608f953c2c57eb87bd3fa2ded Mon Sep 17 00:00:00 2001 From: Charles Dick Date: Tue, 16 Aug 2016 06:55:48 -0700 Subject: [PATCH] Add function name, url, line, column to heap capture Reviewed By: bnham Differential Revision: D3703012 fbshipit-source-id: 8e15deeeabe15da2a87a71c2baf0fa72d5bc6568 --- .../server/middleware/heapCapture/Makefile | 3 + .../middleware/heapCapture/out/aggrow.js | 553 +++++++++--------- .../middleware/heapCapture/out/heapCapture.js | 200 ++++--- .../middleware/heapCapture/out/table.js | 187 +++--- .../middleware/heapCapture/src/heapCapture.js | 13 +- .../middleware/heapCaptureMiddleware.js | 160 ++++- 6 files changed, 621 insertions(+), 495 deletions(-) diff --git a/local-cli/server/middleware/heapCapture/Makefile b/local-cli/server/middleware/heapCapture/Makefile index c0374ee63..736c16625 100644 --- a/local-cli/server/middleware/heapCapture/Makefile +++ b/local-cli/server/middleware/heapCapture/Makefile @@ -1,5 +1,8 @@ +SHELL := /bin/bash + all: NODE_PATH="../../../../node_modules/" babel --presets babel-preset-react-native -d out src + for f in out/*.js; do echo "// @generated" >> $$f; done watch: NODE_PATH="../../../../node_modules/" babel --watch --presets babel-preset-react-native -d out src diff --git a/local-cli/server/middleware/heapCapture/out/aggrow.js b/local-cli/server/middleware/heapCapture/out/aggrow.js index b7edd0fa3..83306850b 100644 --- a/local-cli/server/middleware/heapCapture/out/aggrow.js +++ b/local-cli/server/middleware/heapCapture/out/aggrow.js @@ -16,38 +16,38 @@ // pivot around frames in the middle of a stack by callers / callees // graphing? -function stringInterner(){ // eslint-disable-line no-unused-vars +function stringInterner(){// eslint-disable-line no-unused-vars var strings=[]; var ids={}; -return { +return{ intern:function internString(s){ var find=ids[s]; if(find===undefined){ var id=strings.length; ids[s]=id; strings.push(s); -return id;}else -{ -return find;}}, - - +return id; +}else{ +return find; +} +}, get:function getString(id){ -return strings[id];}};} +return strings[id]; +}}; +} - - -function stackData(stackIdMap,maxDepth){ // eslint-disable-line no-unused-vars -return { +function stackData(stackIdMap,maxDepth){// eslint-disable-line no-unused-vars +return{ maxDepth:maxDepth, get:function getStack(id){ -return stackIdMap[id];}};} +return stackIdMap[id]; +}}; +} - - -function stackRegistry(interner){ // eslint-disable-line no-unused-vars -return { +function stackRegistry(interner){// eslint-disable-line no-unused-vars +return{ root:{id:0}, nodeCount:1, insert:function insertNode(parent,label){ @@ -56,24 +56,24 @@ var node=parent[labelId]; if(node===undefined){ node={id:this.nodeCount}; this.nodeCount++; -parent[labelId]=node;} - -return node;}, - +parent[labelId]=node; +} +return node; +}, flatten:function flattenStacks(){ var stackFrameCount=0; function countStacks(tree,depth){ var leaf=true; for(var frameId in tree){ if(frameId!=='id'){ -leaf=countStacks(tree[frameId],depth+1);}} - - +leaf=countStacks(tree[frameId],depth+1); +} +} if(leaf){ -stackFrameCount+=depth;} - -return false;} - +stackFrameCount+=depth; +} +return false; +} countStacks(this.root,0); console.log('size needed to store stacks: '+(stackFrameCount*4).toString()+'B'); var stackIdMap=new Array(this.nodeCount); @@ -87,36 +87,36 @@ for(var frameId in tree){ if(frameId!=='id'){ stack.push(Number(frameId)); childStack=flattenStacksImpl(tree[frameId],stack); -stack.pop();}} - - +stack.pop(); +} +} var id=tree.id; if(id<0||id>=stackIdMap.length||stackIdMap[id]!==undefined){ -throw 'invalid stack id!';} - +throw'invalid stack id!'; +} if(childStack!==undefined){ // each child must have our stack as a prefix, so just use that -stackIdMap[id]=childStack.subarray(0,stack.length);}else -{ +stackIdMap[id]=childStack.subarray(0,stack.length); +}else{ var newStack=stackArray.subarray(stackFrameCount,stackFrameCount+stack.length); stackFrameCount+=stack.length; for(var i=0;i' bucket if(a.length<=captureDepth&&b.length<=captureDepth){ -return 0;}else -if(a.length<=captureDepth){ -return -1;}else -if(b.length<=captureDepth){ -return 1;} - -return frameGetter(a,captureDepth)-frameGetter(b,captureDepth);};};for(var depth=0;depth>>NODE_INDENT_SHIFT)+1; @@ -178,35 +178,35 @@ var state=NODE_REPOSITION_BIT| NODE_REAGGREGATE_BIT| NODE_REORDER_BIT| indent<',indices,INVALID_ACTIVE_EXPANDER)}; @@ -215,35 +215,35 @@ var activeAggregators=state.activeAggregators; var aggregates=new Array(activeAggregators.length); for(var j=0;j=top&&row.top var begin=0; @@ -341,17 +341,17 @@ row.children=[]; while(begindepth){ -break;} - -begin++;} - +break; +} +begin++; +} if(begin>0){ row.children.push(createTreeNode( row, columnName+'', rowIndices.subarray(0,begin), -nextActiveIndex));} - +nextActiveIndex)); +} // aggregate the rest under frames if(begin=FIELD_EXPANDER_ID_MAX){ -throw 'too many field expanders!';} - +throw'too many field expanders!'; +} state.fieldExpanders.push({ -name:name, // name for column -formatter:formatter, // row index -> display string -comparer:comparer}); // compares by two row indices - -return FIELD_EXPANDER_ID_MIN+state.fieldExpanders.length-1;}, +name:name,// name for column +formatter:formatter,// row index -> display string +comparer:comparer}); +return FIELD_EXPANDER_ID_MIN+state.fieldExpanders.length-1; +}, addCalleeStackExpander:function addCalleeStackExpander(name,stackGetter){ if(STACK_EXPANDER_ID_MIN+state.fieldExpanders.length>=STACK_EXPANDER_ID_MAX){ -throw 'too many stack expanders!';} - +throw'too many stack expanders!'; +} state.stackExpanders.push({ -name:name, // name for column -stackGetter:stackGetter, // row index -> stack array -comparers:createStackComparers(stackGetter,calleeFrameGetter), // depth -> comparer -frameGetter:calleeFrameGetter}); // (stack, depth) -> string id - -return STACK_EXPANDER_ID_MIN+state.stackExpanders.length-1;}, +name:name,// name for column +stackGetter:stackGetter,// row index -> stack array +comparers:createStackComparers(stackGetter,calleeFrameGetter),// depth -> comparer +frameGetter:calleeFrameGetter}); +return STACK_EXPANDER_ID_MIN+state.stackExpanders.length-1; +}, addCallerStackExpander:function addCallerStackExpander(name,stackGetter){ if(STACK_EXPANDER_ID_MIN+state.fieldExpanders.length>=STACK_EXPANDER_ID_MAX){ -throw 'too many stack expanders!';} - +throw'too many stack expanders!'; +} state.stackExpanders.push({ name:name, stackGetter:stackGetter, comparers:createStackComparers(stackGetter,callerFrameGetter), frameGetter:callerFrameGetter}); -return STACK_EXPANDER_ID_MIN+state.stackExpanders.length-1;}, - +return STACK_EXPANDER_ID_MIN+state.stackExpanders.length-1; +}, getExpanders:function getExpanders(){ var expanders=[]; for(var _i6=0;_i6=FIELD_EXPANDER_ID_MIN&&id<=FIELD_EXPANDER_ID_MAX){ -return state.fieldExpanders[id-FIELD_EXPANDER_ID_MIN].name;}else -if(id>=STACK_EXPANDER_ID_MIN&&id<=STACK_EXPANDER_ID_MAX){ -return state.stackExpanders[id-STACK_EXPANDER_ID_MIN].name;} - -throw 'Unknown expander ID '+id.toString();}, - +return state.fieldExpanders[id-FIELD_EXPANDER_ID_MIN].name; +}else if(id>=STACK_EXPANDER_ID_MIN&&id<=STACK_EXPANDER_ID_MAX){ +return state.stackExpanders[id-STACK_EXPANDER_ID_MIN].name; +} +throw'Unknown expander ID '+id.toString(); +}, setActiveExpanders:function setActiveExpanders(ids){ for(var _i8=0;_i8=FIELD_EXPANDER_ID_MIN&&id<=FIELD_EXPANDER_ID_MAX){ if(id-FIELD_EXPANDER_ID_MIN>=state.fieldExpanders.length){ -throw 'field expander for id '+id.toString()+' does not exist!';}}else - -if(id>=STACK_EXPANDER_ID_MIN&&id<=STACK_EXPANDER_ID_MAX){ +throw'field expander for id '+id.toString()+' does not exist!'; +} +}else if(id>=STACK_EXPANDER_ID_MIN&&id<=STACK_EXPANDER_ID_MAX){ if(id-STACK_EXPANDER_ID_MIN>=state.stackExpanders.length){ -throw 'stack expander for id '+id.toString()+' does not exist!';}}} - - - +throw'stack expander for id '+id.toString()+' does not exist!'; +} +} +} for(var _i9=0;_i9=AGGREGATOR_ID_MAX){ -throw 'too many aggregators!';} - +throw'too many aggregators!'; +} state.aggregators.push({ -name:name, // name for column -aggregator:aggregator, // index array -> aggregate value -formatter:formatter, // aggregate value -> display string -sorter:sorter}); // compare two aggregate values - -return state.aggregators.length-1;}, +name:name,// name for column +aggregator:aggregator,// index array -> aggregate value +formatter:formatter,// aggregate value -> display string +sorter:sorter}); +return state.aggregators.length-1; +}, getAggregators:function getAggregators(){ var aggregators=[]; for(var _i10=0;_i10state.aggregators.length){ -throw 'aggregator id '+id.toString()+' not valid';}} - - +throw'aggregator id '+id.toString()+' not valid'; +} +} state.activeAggregators=ids.slice(); // NB: evaluate root here because dirty bit is for children // so someone has to start with root, and it might as well be right away @@ -531,86 +531,89 @@ var captureIndex=_i12; sorter=function sorter(a,b){ var c=comparer(a.aggregates[captureIndex],b.aggregates[captureIndex]); if(c===0){ -return captureSorter(a,b);} - -return ascending?-c:c;};};for(var _i12=ids.length-1;_i12>=0;_i12--){_loop2(_i12);} - - +return captureSorter(a,b); +} +return ascending?-c:c; +};};for(var _i12=ids.length-1;_i12>=0;_i12--){_loop2(_i12); +} state.sorter=sorter; -state.root.state|=NODE_REORDER_BIT;}, - +state.root.state|=NODE_REORDER_BIT; +}, getActiveAggregators:function getActiveAggregators(){ -return state.activeAggregators.slice();}, - +return state.activeAggregators.slice(); +}, getRows:function getRows(top,height){ var result=new Array(height); for(var _i13=0;_i13>>NODE_INDENT_SHIFT;}, - +return row.state>>>NODE_INDENT_SHIFT; +}, getRowAggregate:function getRowAggregate(row,index){ var aggregator=state.aggregators[state.activeAggregators[index]]; -return aggregator.formatter(row.aggregates[index]);}, - +return aggregator.formatter(row.aggregates[index]); +}, getHeight:function getHeight(){ -return state.root.height;}, - +return state.root.height; +}, canExpand:function canExpand(row){ -return (row.state&NODE_EXPANDED_BIT)===0&&row.expander!==INVALID_ACTIVE_EXPANDER;}, - +return(row.state&NODE_EXPANDED_BIT)===0&&row.expander!==INVALID_ACTIVE_EXPANDER; +}, canContract:function canContract(row){ -return (row.state&NODE_EXPANDED_BIT)!==0;}, - +return(row.state&NODE_EXPANDED_BIT)!==0; +}, expand:function expand(row){ if((row.state&NODE_EXPANDED_BIT)!==0){ -throw 'can not expand row, already expanded';} - +throw'can not expand row, already expanded'; +} if(row.height!==1){ -throw 'unexpanded row has height '+row.height.toString()+' != 1';} - -if(row.children===null){ // first expand, generate children +throw'unexpanded row has height '+row.height.toString()+' != 1'; +} +if(row.children===null){// first expand, generate children var activeIndex=row.expander&ACTIVE_EXPANDER_MASK; -var nextActiveIndex=activeIndex+1; // NB: if next is stack, frame is 0 +var nextActiveIndex=activeIndex+1;// NB: if next is stack, frame is 0 if(nextActiveIndex>=state.activeExpanders.length){ -nextActiveIndex=INVALID_ACTIVE_EXPANDER;} - +nextActiveIndex=INVALID_ACTIVE_EXPANDER; +} if(activeIndex>=state.activeExpanders.length){ -throw 'invalid active expander index '+activeIndex.toString();} - +throw'invalid active expander index '+activeIndex.toString(); +} var exId=state.activeExpanders[activeIndex]; if(exId>=FIELD_EXPANDER_ID_MIN&& exId=STACK_EXPANDER_ID_MIN&& +addChildrenWithFieldExpander(row,expander,nextActiveIndex); +}else if(exId>=STACK_EXPANDER_ID_MIN&& exId>>ACTIVE_EXPANDER_FRAME_SHIFT; var _expander=state.stackExpanders[exId-STACK_EXPANDER_ID_MIN]; -addChildrenWithStackExpander(row,_expander,activeIndex,depth,nextActiveIndex);}else -{ -throw 'state.activeIndex '+activeIndex.toString()+ -' has invalid expander'+exId.toString();}} - - +addChildrenWithStackExpander(row,_expander,activeIndex,depth,nextActiveIndex); +}else{ +throw'state.activeIndex '+activeIndex.toString()+ +' has invalid expander'+exId.toString(); +} +} row.state|=NODE_EXPANDED_BIT| NODE_REAGGREGATE_BIT|NODE_REORDER_BIT|NODE_REPOSITION_BIT; var heightChange=0; for(var _i14=0;_i14');}else -{ +trees[id]=registry.insert(registry.root,''); +}else{ var parent=parents[id]; var inEdgeName=inEdgeNames[id]; var parentTree=trees[parent]; @@ -26,12 +33,12 @@ registry, parents, inEdgeNames, trees, -parent);} - -trees[id]=registry.insert(parentTree,inEdgeName);} - -return trees[id];} - +parent); +} +trees[id]=registry.insert(parentTree,inEdgeName); +} +return trees[id]; +} // TODO: make it easier to query the heap graph, it's super annoying to deal with edges directly function registerReactComponentTree(refs,registry){ @@ -46,31 +53,31 @@ var name=ref.edges[linkId]; if(name==='_renderedChildren'){ if(parents[id]===undefined){ // mark that we are a react component, even if we don't have a parent -parents[id]=null;} - +parents[id]=null; +} var childrenRef=refs[linkId]; for(var childId in childrenRef.edges){ var linkName=childrenRef.edges[childId]; if(linkName.startsWith('.')){ parents[childId]=id; -inEdgeNames[childId]=linkName;}}}else - - -if(name==='_renderedComponent'){ +inEdgeNames[childId]=linkName; +} +} +}else if(name==='_renderedComponent'){ if(parents[id]===undefined){ -parents[id]=null;} - +parents[id]=null; +} parents[linkId]=id; -inEdgeNames[linkId]='_renderedComponent';}}}} - - - - +inEdgeNames[linkId]='_renderedComponent'; +} +} +} +} // build tree of react internal instances (since that's what has the structure) var trees={}; for(var _id in refs){ -registerReactComponentTreeImpl(refs,registry,parents,inEdgeNames,trees,_id);} - +registerReactComponentTreeImpl(refs,registry,parents,inEdgeNames,trees,_id); +} // hook in components by looking at their _reactInternalInstance fields for(var _id2 in refs){ var _ref=refs[_id2]; @@ -78,13 +85,13 @@ for(var _linkId in _ref.edges){ var _name=_ref.edges[_linkId]; if(_name==='_reactInternalInstance'){ if(trees[_linkId]!==undefined){ -trees[_id2]=registry.insert(trees[_linkId],'');}}}} - - - - -return trees;} - +trees[_id2]=registry.insert(trees[_linkId],''); +} +} +} +} +return trees; +} function registerPathToRoot(roots,refs,registry,reactComponentTree){ var visited={}; @@ -93,10 +100,10 @@ for(var i=0;i0){ var nextBreadth=[];var _loop=function _loop( @@ -111,41 +118,41 @@ edges.sort(function putUnknownLast(a,b){ var aName=ref.edges[a]; var bName=ref.edges[b]; if(aName===null&&bName!==null){ -return 1;}else -if(aName!==null&&bName===null){ -return -1;}else -if(aName===null&&bName===null){ -return 0;}else -{ -return a.localeCompare(b);}}); - - +return 1; +}else if(aName!==null&&bName===null){ +return-1; +}else if(aName===null&&bName===null){ +return 0; +}else{ +return a.localeCompare(b); +} +}); for(var j=0;j').id;}else -{ -newData[dataOffset+reactField]=reactTree.id;} - -dataOffset+=numFields;} - -this.data=newData;}, - +this.stacks.insert(this.stacks.root,'').id; +}else{ +newData[dataOffset+reactField]=reactTree.id; +} +dataOffset+=numFields; +} +this.data=newData; +}, getAggrow:function getAggrow(){ var agStrings=this.strings; var agStacks=this.stacks.flatten(); @@ -219,31 +228,31 @@ var idExpander=ag.addFieldExpander('Id', function getId(row){ var id=agData[row*numFields+idField]; if(id<0){ -id+=0x100000000; // data is int32, id is uint32 +id+=0x100000000;// data is int32, id is uint32 } -return '0x'+id.toString(16);}, - +return'0x'+id.toString(16); +}, function compareAddress(rowA,rowB){ -return agData[rowA*numFields+idField]-agData[rowB*numFields+idField];}); - +return agData[rowA*numFields+idField]-agData[rowB*numFields+idField]; +}); var typeExpander=ag.addFieldExpander('Type', function getSize(row){return agStrings.get(agData[row*numFields+typeField]);}, function compareSize(rowA,rowB){ -return agData[rowA*numFields+typeField]-agData[rowB*numFields+typeField];}); - +return agData[rowA*numFields+typeField]-agData[rowB*numFields+typeField]; +}); ag.addFieldExpander('Size', function getSize(row){return agData[row*numFields+sizeField].toString();}, function compareSize(rowA,rowB){ -return agData[rowA*numFields+sizeField]-agData[rowB*numFields+sizeField];}); - +return agData[rowA*numFields+sizeField]-agData[rowB*numFields+sizeField]; +}); var traceExpander=ag.addFieldExpander('Trace', function getSize(row){return agStrings.get(agData[row*numFields+traceField]);}, function compareSize(rowA,rowB){ -return agData[rowA*numFields+traceField]-agData[rowB*numFields+traceField];}); - +return agData[rowA*numFields+traceField]-agData[rowB*numFields+traceField]; +}); var pathExpander=ag.addCalleeStackExpander('Path', function getStack(row){return agStacks.get(agData[row*numFields+pathField]);}); @@ -256,29 +265,30 @@ function aggregateSize(indices){ var size=0; for(var i=0;isIndex){ -dIndex--;} - +dIndex--; +} active.splice(sIndex,1); active.splice(dIndex,0,dragged); aggrow.setActiveAggregators(active); -this.forceUpdate();}else -if(s.startsWith('expander:active:')){ +this.forceUpdate(); +}else if(s.startsWith('expander:active:')){ var _sIndex=parseInt(s.substr(16),10); var _dIndex=-1; var _active=aggrow.getActiveExpanders(); var _dragged=_active[_sIndex]; if(d.startsWith('expander:insert:')){ -_dIndex=parseInt(d.substr(16),10);}else -if(d==='divider:insert'){ -_dIndex=0;}else -{ -throw 'not allowed to drag '+s+' to '+d;} - +_dIndex=parseInt(d.substr(16),10); +}else if(d==='divider:insert'){ +_dIndex=0; +}else{ +throw'not allowed to drag '+s+' to '+d; +} if(_dIndex>_sIndex){ -_dIndex--;} - +_dIndex--; +} _active.splice(_sIndex,1); _active.splice(_dIndex,0,_dragged); aggrow.setActiveExpanders(_active); -this.forceUpdate();}}},{key:'render',value:function render() - - +this.forceUpdate(); +} +}},{key:'render',value:function render() {var _this4=this; var headers=[]; @@ -149,41 +149,41 @@ headers.push( React.createElement(DropTarget,{ id:'aggregate:insert:'+i.toString(), dropFilter:function dropFilter(){return true;}, -dropAction:function dropAction(s,d){_this4.dropAggregator(s,d);},__source:{fileName:_jsxFileName,lineNumber:149}}, +dropAction:function dropAction(s,d){_this4.dropAggregator(s,d);}}, React.createElement('div',{style:{ width:'16px', height:'inherit', backgroundColor:'darkGray', -flexShrink:'0'},__source:{fileName:_jsxFileName,lineNumber:154}}))); +flexShrink:'0'}}))); -headers.push(React.createElement(Draggable,{id:'aggregate:active:'+i.toString(),__source:{fileName:_jsxFileName,lineNumber:161}}, -React.createElement('div',{style:{width:'128px',textAlign:'center',flexShrink:'0'},__source:{fileName:_jsxFileName,lineNumber:162}},name)));} - +headers.push(React.createElement(Draggable,{id:'aggregate:active:'+i.toString()}, +React.createElement('div',{style:{width:'128px',textAlign:'center',flexShrink:'0'}},name))); +} headers.push( React.createElement(DropTarget,{ id:'divider:insert', dropFilter:function dropFilter(){return true;}, -dropAction:function dropAction(s,d){_this4.dropAggregator(s,d);},__source:{fileName:_jsxFileName,lineNumber:166}}, +dropAction:function dropAction(s,d){_this4.dropAggregator(s,d);}}, React.createElement('div',{style:{ width:'16px', height:'inherit', backgroundColor:'gold', -flexShrink:'0'},__source:{fileName:_jsxFileName,lineNumber:171}}))); +flexShrink:'0'}}))); for(var _i=0;_i { + res.setEncoding('utf8'); + let sawEnd = false; + let resBody = ''; + res.on('data', (chunk) => { + resBody += chunk; + }).on('end', () => { + sawEnd = true; + onSuccess(new SourceMapConsumer(resBody)); + }).on('close', (err) => { + if (!sawEnd) { + onFailure('Connection terminated prematurely because of: ' + + err.code + ' for url: ' + url); + } + }); + }).on('error', (err) => { + onFailure('Could not get response from: ' + url + ', error: ' + err.message); + }); +} + +// capture: capture object +// onSuccess: function (Map of url -> SourceMapConsumer) +// onFailure: function (string) +function getSourceMapsForCapture(capture, onFailure, onSuccess) { + const urls = new Set(); + const sourcemaps = new Map(); + for (const id in capture.refs) { + const ref = capture.refs[id]; + if (ref.type === 'Function' && ref.value && !!ref.value.url) { + urls.add(ref.value.url); + } + } + urls.forEach((url) => { + getSourceMapForUrl(url, onFailure, (sourcemap) => { + sourcemaps.set(url, sourcemap); + urls.delete(url); + if (urls.size === 0) { + onSuccess(sourcemaps); + } + }); + }); + if (urls.size === 0) { + console.warn('No source information found in capture'); + onSuccess(sourcemaps); + } +} + +// capture: capture object +// onSuccess: function (capture object) +// onFailure: function (string) +function symbolocateHeapCaptureFunctions(capture, onFailure, onSuccess) { + getSourceMapsForCapture(capture, onFailure, (sourcemaps) => { + for (const id in capture.refs) { + const ref = capture.refs[id]; + if (ref.type === 'Function' && ref.value && !!ref.value.url) { + const sourcemap = sourcemaps.get(ref.value.url); + const original = sourcemap.originalPositionFor({ + line: ref.value.line, + column: ref.value.col, + }); + if (original.name) { + ref.value.name = original.name; + } else if (!ref.value.name) { + ref.value.name = path.posix.basename(original.source) + ':' + original.line; + } + ref.value.url = 'file://' + original.source; + ref.value.line = original.line; + ref.value.col = original.column; + } + } + onSuccess(capture); + }); +} module.exports = function(req, res, next) { if (req.url !== '/jscheapcaptureupload') { @@ -19,35 +113,41 @@ module.exports = function(req, res, next) { return; } - console.log('Downloading Heap Capture'); - var preload = path.join(__dirname, 'heapCapture/preLoadedCapture.js'); - fs.writeFileSync(preload, 'var preLoadedCapture = '); - fs.appendFileSync(preload, req.rawBody); - fs.appendFileSync(preload, ';'); - res.end(); - const captureDir = path.join(__dirname, 'heapCapture/captures'); - if (!fs.existsSync(captureDir)) { - fs.mkdirSync(captureDir); - } - console.log('Packaging Trace'); - var captureHtml = captureDir + '/capture_' + Date.now() + '.html'; - var capture = fs.createWriteStream(captureHtml); - var inliner = spawn( - 'inliner', - ['--nocompress', 'heapCapture.html'], - { cwd: path.join(__dirname, '/heapCapture/'), - stdio: [ process.stdin, 'pipe', process.stderr ], - }); - inliner.stdout.pipe(capture); - inliner.on('error', (err) => { - console.error('Error processing heap capture: ' + err.message); - console.error('make sure you have installed inliner with \'npm install inliner -g\''); - }); - inliner.on('exit', (code, signal) => { - if (code === 0) { - console.log('Heap capture written to: ' + captureHtml); - } else { - console.error('Error processing heap capture, inliner returned code: ' + code); + console.log('Symbolocating Heap Capture'); + symbolocateHeapCaptureFunctions(JSON.parse(req.rawBody), (err) => { + console.error('Error when symbolicating: ' + err); + }, + (capture) => { + res.end(); + const preload = path.join(__dirname, 'heapCapture/preLoadedCapture.js'); + fs.writeFileSync(preload, 'var preLoadedCapture = '); + fs.appendFileSync(preload, JSON.stringify(capture)); + fs.appendFileSync(preload, ';'); + const captureDir = path.join(__dirname, 'heapCapture/captures'); + if (!fs.existsSync(captureDir)) { + fs.mkdirSync(captureDir); + } + console.log('Packaging Trace'); + var captureHtml = captureDir + '/capture_' + Date.now() + '.html'; + var capture = fs.createWriteStream(captureHtml); + var inliner = spawn( + 'inliner', + ['--nocompress', 'heapCapture.html'], + { cwd: path.join(__dirname, '/heapCapture/'), + stdio: [ process.stdin, 'pipe', process.stderr ], + }); + inliner.stdout.pipe(capture); + inliner.on('error', (err) => { + console.error('Error processing heap capture: ' + err.message); + console.error('make sure you have installed inliner with \'npm install inliner -g\''); + }); + inliner.on('exit', (code, signal) => { + if (code === 0) { + console.log('Heap capture written to: ' + captureHtml); + } else { + console.error('Error processing heap capture, inliner returned code: ' + code); + } + }); } - }); + ); };