Add function name, url, line, column to heap capture

Reviewed By: bnham

Differential Revision: D3703012

fbshipit-source-id: 8e15deeeabe15da2a87a71c2baf0fa72d5bc6568
This commit is contained in:
Charles Dick 2016-08-16 06:55:48 -07:00 committed by Facebook Github Bot 7
parent 30847b2b35
commit 1199c5ade9
6 changed files with 621 additions and 495 deletions

View File

@ -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

View File

@ -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<stack.length;i++){
newStack[i]=stack[i];}
stackIdMap[id]=newStack;}
return stackIdMap[id];}
newStack[i]=stack[i];
}
stackIdMap[id]=newStack;
}
return stackIdMap[id];
}
flattenStacksImpl(this.root,[]);
return new stackData(stackIdMap,maxStackDepth);}};}
return new stackData(stackIdMap,maxStackDepth);
}};
}
function aggrow(strings,stacks,numRows){ // eslint-disable-line no-unused-vars
function aggrow(strings,stacks,numRows){// eslint-disable-line no-unused-vars
// expander ID definitions
var FIELD_EXPANDER_ID_MIN=0x0000;
var FIELD_EXPANDER_ID_MAX=0x7fff;
@ -136,41 +136,41 @@ var ACTIVE_AGGREGATOR_MASK=0xffff;
var ACTIVE_AGGREGATOR_ASC_BIT=0x10000;
// tree node state definitions
var NODE_EXPANDED_BIT=0x0001; // this row is expanded
var NODE_REAGGREGATE_BIT=0x0002; // children need aggregates
var NODE_REORDER_BIT=0x0004; // children need to be sorted
var NODE_REPOSITION_BIT=0x0008; // children need position
var NODE_EXPANDED_BIT=0x0001;// this row is expanded
var NODE_REAGGREGATE_BIT=0x0002;// children need aggregates
var NODE_REORDER_BIT=0x0004;// children need to be sorted
var NODE_REPOSITION_BIT=0x0008;// children need position
var NODE_INDENT_SHIFT=16;
function calleeFrameGetter(stack,depth){
return stack[depth];}
return stack[depth];
}
function callerFrameGetter(stack,depth){
return stack[stack.length-depth-1];}
return stack[stack.length-depth-1];
}
function createStackComparers(stackGetter,frameGetter){
var comparers=new Array(stacks.maxDepth);var _loop=function _loop(
depth){
var captureDepth=depth; // NB: to capture depth per loop iteration
var captureDepth=depth;// NB: to capture depth per loop iteration
comparers[depth]=function calleeStackComparer(rowA,rowB){
var a=stackGetter(rowA);
var b=stackGetter(rowB);
// NB: we put the stacks that are too short at the top,
// so they can be grouped into the '<exclusive>' 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<stacks.maxDepth;depth++){_loop(depth);}
return comparers;}
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<stacks.maxDepth;depth++){_loop(depth);
}
return comparers;
}
function createTreeNode(parent,label,indices,expander){
var indent=parent===null?0:(parent.state>>>NODE_INDENT_SHIFT)+1;
@ -178,35 +178,35 @@ var state=NODE_REPOSITION_BIT|
NODE_REAGGREGATE_BIT|
NODE_REORDER_BIT|
indent<<NODE_INDENT_SHIFT;
return {
parent:parent, // null if root
children:null, // array of children nodes
label:label, // string to show in UI
indices:indices, // row indices under this node
aggregates:null, // result of aggregate on indices
expander:expander, // index into state.activeExpanders
top:0, // y position of top row (in rows)
height:1, // number of rows including children
state:state};} // see NODE_* definitions above
return{
parent:parent,// null if root
children:null,// array of children nodes
label:label,// string to show in UI
indices:indices,// row indices under this node
aggregates:null,// result of aggregate on indices
expander:expander,// index into state.activeExpanders
top:0,// y position of top row (in rows)
height:1,// number of rows including children
state:state};
}
function noSortOrder(a,b){
return 0;}
return 0;
}
var indices=new Int32Array(numRows);
for(var i=0;i<numRows;i++){
indices[i]=i;}
indices[i]=i;
}
var state={
fieldExpanders:[], // tree expanders that expand on simple values
stackExpanders:[], // tree expanders that expand stacks
activeExpanders:[], // index into field or stack expanders, hierarchy of tree
aggregators:[], // all available aggregators, might not be used
activeAggregators:[], // index into aggregators, to actually compute
sorter:noSortOrder, // compare function that uses sortOrder to sort row.children
fieldExpanders:[],// tree expanders that expand on simple values
stackExpanders:[],// tree expanders that expand stacks
activeExpanders:[],// index into field or stack expanders, hierarchy of tree
aggregators:[],// all available aggregators, might not be used
activeAggregators:[],// index into aggregators, to actually compute
sorter:noSortOrder,// compare function that uses sortOrder to sort row.children
root:createTreeNode(null,'<root>',indices,INVALID_ACTIVE_EXPANDER)};
@ -215,35 +215,35 @@ var activeAggregators=state.activeAggregators;
var aggregates=new Array(activeAggregators.length);
for(var j=0;j<activeAggregators.length;j++){
var aggregator=state.aggregators[activeAggregators[j]];
aggregates[j]=aggregator.aggregator(row.indices);}
aggregates[j]=aggregator.aggregator(row.indices);
}
row.aggregates=aggregates;
row.state|=NODE_REAGGREGATE_BIT;}
row.state|=NODE_REAGGREGATE_BIT;
}
function evaluateAggregates(row){
if((row.state&NODE_EXPANDED_BIT)!==0){
var children=row.children;
for(var _i=0;_i<children.length;_i++){
evaluateAggregate(children[_i]);}
row.state|=NODE_REORDER_BIT;}
row.state^=NODE_REAGGREGATE_BIT;}
evaluateAggregate(children[_i]);
}
row.state|=NODE_REORDER_BIT;
}
row.state^=NODE_REAGGREGATE_BIT;
}
function evaluateOrder(row){
if((row.state&NODE_EXPANDED_BIT)!==0){
var children=row.children;
for(var _i2=0;_i2<children.length;_i2++){
var child=children[_i2];
child.state|=NODE_REORDER_BIT;}
child.state|=NODE_REORDER_BIT;
}
children.sort(state.sorter);
row.state|=NODE_REPOSITION_BIT;}
row.state^=NODE_REORDER_BIT;}
row.state|=NODE_REPOSITION_BIT;
}
row.state^=NODE_REORDER_BIT;
}
function evaluatePosition(row){
if((row.state&NODE_EXPANDED_BIT)!==0){
@ -253,49 +253,49 @@ for(var _i3=0;_i3<children.length;_i3++){
var child=children[_i3];
if(child.top!==childTop){
child.top=childTop;
child.state|=NODE_REPOSITION_BIT;}
childTop+=child.height;}}
row.state^=NODE_REPOSITION_BIT;}
child.state|=NODE_REPOSITION_BIT;
}
childTop+=child.height;
}
}
row.state^=NODE_REPOSITION_BIT;
}
function getRowsImpl(row,top,height,result){
if((row.state&NODE_REAGGREGATE_BIT)!==0){
evaluateAggregates(row);}
evaluateAggregates(row);
}
if((row.state&NODE_REORDER_BIT)!==0){
evaluateOrder(row);}
evaluateOrder(row);
}
if((row.state&NODE_REPOSITION_BIT)!==0){
evaluatePosition(row);}
evaluatePosition(row);
}
if(row.top>=top&&row.top<top+height){
if(result[row.top-top]!=null){
throw 'getRows put more than one row at position '+row.top+' into result';}
result[row.top-top]=row;}
throw'getRows put more than one row at position '+row.top+' into result';
}
result[row.top-top]=row;
}
if((row.state&NODE_EXPANDED_BIT)!==0){
var children=row.children;
for(var _i4=0;_i4<children.length;_i4++){
var child=children[_i4];
if(child.top<top+height&&top<child.top+child.height){
getRowsImpl(child,top,height,result);}}}}
getRowsImpl(child,top,height,result);
}
}
}
}
function updateHeight(row,heightChange){
while(row!==null){
row.height+=heightChange;
row.state|=NODE_REPOSITION_BIT;
row=row.parent;}}
row=row.parent;
}
}
function addChildrenWithFieldExpander(row,expander,nextActiveIndex){
var rowIndices=row.indices;
@ -311,16 +311,16 @@ row,
expander.name+': '+expander.formatter(rowIndices[begin]),
rowIndices.subarray(begin,end),
nextActiveIndex));
begin=end;}
end++;}
begin=end;
}
end++;
}
row.children.push(createTreeNode(
row,
expander.name+': '+expander.formatter(rowIndices[begin]),
rowIndices.subarray(begin,end),
nextActiveIndex));}
nextActiveIndex));
}
function addChildrenWithStackExpander(row,expander,activeIndex,depth,nextActiveIndex){
var rowIndices=row.indices;
@ -331,8 +331,8 @@ var expandNextFrame=activeIndex|depth+1<<ACTIVE_EXPANDER_FRAME_SHIFT;
rowIndices.sort(comparer);
var columnName='';
if(depth===0){
columnName=expander.name+': ';}
columnName=expander.name+': ';
}
// put all the too-short stacks under <exclusive>
var begin=0;
@ -341,17 +341,17 @@ row.children=[];
while(begin<rowIndices.length){
beginStack=stackGetter(rowIndices[begin]);
if(beginStack.length>depth){
break;}
begin++;}
break;
}
begin++;
}
if(begin>0){
row.children.push(createTreeNode(
row,
columnName+'<exclusive>',
rowIndices.subarray(0,begin),
nextActiveIndex));}
nextActiveIndex));
}
// aggregate the rest under frames
if(begin<rowIndices.length){
var end=begin+1;
@ -364,159 +364,159 @@ columnName+strings.get(frameGetter(beginStack,depth)),
rowIndices.subarray(begin,end),
expandNextFrame));
begin=end;
beginStack=endStack;}
end++;}
beginStack=endStack;
}
end++;
}
row.children.push(createTreeNode(
row,
columnName+strings.get(frameGetter(beginStack,depth)),
rowIndices.subarray(begin,end),
expandNextFrame));}}
expandNextFrame));
}
}
function contractRow(row){
if((row.state&NODE_EXPANDED_BIT)===0){
throw 'can not contract row, already contracted';}
throw'can not contract row, already contracted';
}
row.state^=NODE_EXPANDED_BIT;
var heightChange=1-row.height;
updateHeight(row,heightChange);}
updateHeight(row,heightChange);
}
function pruneExpanders(row,oldExpander,newExpander){
row.state|=NODE_REPOSITION_BIT;
if(row.expander===oldExpander){
row.state|=NODE_REAGGREGATE_BIT|NODE_REORDER_BIT|NODE_REPOSITION_BIT;
if((row.state&NODE_EXPANDED_BIT)!==0){
contractRow(row);}
contractRow(row);
}
row.children=null;
row.expander=newExpander;}else
{
row.expander=newExpander;
}else{
row.state|=NODE_REPOSITION_BIT;
var children=row.children;
if(children!=null){
for(var _i5=0;_i5<children.length;_i5++){
var child=children[_i5];
pruneExpanders(child,oldExpander,newExpander);}}}}
pruneExpanders(child,oldExpander,newExpander);
}
}
}
}
return {
return{
addFieldExpander:function addFieldExpander(name,formatter,comparer){
if(FIELD_EXPANDER_ID_MIN+state.fieldExpanders.length>=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<state.fieldExpanders.length;_i6++){
expanders.push(FIELD_EXPANDER_ID_MIN+_i6);}
expanders.push(FIELD_EXPANDER_ID_MIN+_i6);
}
for(var _i7=0;_i7<state.stackExpanders.length;_i7++){
expanders.push(STACK_EXPANDER_ID_MIN+_i7);}
return expanders;},
expanders.push(STACK_EXPANDER_ID_MIN+_i7);
}
return expanders;
},
getExpanderName:function getExpanderName(id){
if(id>=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<ids.length;_i8++){
var id=ids[_i8];
if(id>=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<ids.length;_i9++){
if(state.activeExpanders.length<=_i9){
pruneExpanders(state.root,INVALID_ACTIVE_EXPANDER,_i9);
break;}else
if(ids[_i9]!==state.activeExpanders[_i9]){
break;
}else if(ids[_i9]!==state.activeExpanders[_i9]){
pruneExpanders(state.root,_i9,_i9);
break;}}
break;
}
}
// TODO: if ids is prefix of activeExpanders, we need to make an expander invalid
state.activeExpanders=ids.slice();},
state.activeExpanders=ids.slice();
},
getActiveExpanders:function getActiveExpanders(){
return state.activeExpanders.slice();},
return state.activeExpanders.slice();
},
addAggregator:function addAggregator(name,aggregator,formatter,sorter){
if(state.aggregators.length>=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;_i10<state.aggregators.length;_i10++){
aggregators.push(_i10);}
return aggregators;},
aggregators.push(_i10);
}
return aggregators;
},
getAggregatorName:function getAggregatorName(id){
return state.aggregators[id&ACTIVE_AGGREGATOR_MASK].name;},
return state.aggregators[id&ACTIVE_AGGREGATOR_MASK].name;
},
setActiveAggregators:function setActiveAggregators(ids){
for(var _i11=0;_i11<ids.length;_i11++){
var id=ids[_i11]&ACTIVE_AGGREGATOR_MASK;
if(id<0||id>state.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<height;_i13++){
result[_i13]=null;}
result[_i13]=null;
}
getRowsImpl(state.root,top,height,result);
return result;},
return result;
},
getRowLabel:function getRowLabel(row){
return row.label;},
return row.label;
},
getRowIndent:function getRowIndent(row){
return row.state>>>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<FIELD_EXPANDER_ID_MIN+state.fieldExpanders.length){
var expander=state.fieldExpanders[exId-FIELD_EXPANDER_ID_MIN];
addChildrenWithFieldExpander(row,expander,nextActiveIndex);}else
if(exId>=STACK_EXPANDER_ID_MIN&&
addChildrenWithFieldExpander(row,expander,nextActiveIndex);
}else if(exId>=STACK_EXPANDER_ID_MIN&&
exId<STACK_EXPANDER_ID_MIN+state.stackExpanders.length){
var depth=row.expander>>>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<row.children.length;_i14++){
heightChange+=row.children[_i14].height;}
heightChange+=row.children[_i14].height;
}
updateHeight(row,heightChange);
// if children only contains one node, then expand it as well
if(row.children.length===1&&this.canExpand(row.children[0])){
this.expand(row.children[0]);}},
this.expand(row.children[0]);
}
},
contract:function contract(row){
contractRow(row);}};}
contractRow(row);
}};
}// @generated

View File

@ -8,14 +8,21 @@
*/
'use strict';
/*eslint no-console-disallow: "off"*/
/*global React ReactDOM Table stringInterner stackRegistry aggrow preLoadedCapture:true*/var _jsxFileName='src/heapCapture.js';
/*global React ReactDOM Table stringInterner stackRegistry aggrow preLoadedCapture:true*/
function getTypeName(ref){
if(ref.type==='Function'&&!!ref.value){
return'Function '+ref.value.name;
}
return ref.type;
}
function registerReactComponentTreeImpl(refs,registry,parents,inEdgeNames,trees,id){
if(parents[id]===undefined){
// not a component
}else if(parents[id]===null){
trees[id]=registry.insert(registry.root,'<internalInstance>');}else
{
trees[id]=registry.insert(registry.root,'<internalInstance>');
}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],'<component>');}}}}
return trees;}
trees[_id2]=registry.insert(trees[_linkId],'<component>');
}
}
}
}
return trees;
}
function registerPathToRoot(roots,refs,registry,reactComponentTree){
var visited={};
@ -93,10 +100,10 @@ for(var i=0;i<roots.length;i++){
var id=roots[i];
if(visited[id]===undefined){
var ref=refs[id];
visited[id]=registry.insert(registry.root,ref.type);
breadth.push(id);}}
visited[id]=registry.insert(registry.root,getTypeName(ref));
breadth.push(id);
}
}
while(breadth.length>0){
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<edges.length;j++){
var edgeId=edges[j];
var edgeName='';
if(ref.edges[edgeId]){
edgeName=ref.edges[edgeId]+': ';}
edgeName=ref.edges[edgeId]+': ';
}
if(visited[edgeId]===undefined){
var edgeRef=refs[edgeId];
if(edgeRef===undefined){
// TODO: figure out why we have edges that point to things not JSCell
//console.log('registerPathToRoot unable to follow edge from ' + id + ' to ' + edgeId);
}else {
visited[edgeId]=registry.insert(node,edgeName+edgeRef.type);
}else{
visited[edgeId]=registry.insert(node,edgeName+getTypeName(edgeRef));
nextBreadth.push(edgeId);
if(reactComponentTree[edgeId]===undefined){
reactComponentTree[edgeId]=reactComponentTree[id];}}}}};for(var _i=0;_i<breadth.length;_i++){_loop(_i);}
breadth=nextBreadth;}
return visited;}
reactComponentTree[edgeId]=reactComponentTree[id];
}
}
}
}};for(var _i=0;_i<breadth.length;_i++){_loop(_i);
}
breadth=nextBreadth;
}
return visited;
}
function captureRegistry(){
var strings=stringInterner();
@ -160,15 +167,17 @@ var pathField=4;
var reactField=5;
var numFields=6;
return {
return{
strings:strings,
stacks:stacks,
data:data,
register:function registerCapture(captureId,capture){
// NB: capture.refs is potentially VERY large, so we try to avoid making
// copies, even of iteration is a bit more annoying.
var rowCount=0;
for(var id in capture.refs){
rowCount++;}
for(var id in capture.refs){// eslint-disable-line no-unused-vars
rowCount++;
}
console.log(
'increasing row data from '+(this.data.length*4).toString()+'B to '+
(this.data.length*4+rowCount*numFields*4).toString()+'B');
@ -189,25 +198,25 @@ var internedCaptureId=this.strings.intern(captureId);
for(var _id3 in capture.refs){
var ref=capture.refs[_id3];
newData[dataOffset+idField]=parseInt(_id3,16);
newData[dataOffset+typeField]=this.strings.intern(ref.type);
newData[dataOffset+typeField]=this.strings.intern(getTypeName(ref));
newData[dataOffset+sizeField]=ref.size;
newData[dataOffset+traceField]=internedCaptureId;
var pathNode=rootPathMap[_id3];
if(pathNode===undefined){
throw 'did not find path for ref!';}
throw'did not find path for ref!';
}
newData[dataOffset+pathField]=pathNode.id;
var reactTree=reactComponentTreeMap[_id3];
if(reactTree===undefined){
newData[dataOffset+reactField]=
this.stacks.insert(this.stacks.root,'<not-under-tree>').id;}else
{
newData[dataOffset+reactField]=reactTree.id;}
dataOffset+=numFields;}
this.data=newData;},
this.stacks.insert(this.stacks.root,'<not-under-tree>').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;i<indices.length;i++){
var row=indices[i];
size+=agData[row*numFields+sizeField];}
return size;},
size+=agData[row*numFields+sizeField];
}
return size;
},
function formatSize(value){return value.toString();},
function sortSize(a,b){return b-a;});
var countAggregator=ag.addAggregator('Count',
function aggregateCount(indices){
return indices.length;},
return indices.length;
},
function formatCount(value){return value.toString();},
function sortCount(a,b){return b-a;});
ag.setActiveExpanders([pathExpander,reactExpander,typeExpander,idExpander,traceExpander]);
ag.setActiveAggregators([sizeAggregator,countAggregator]);
return ag;}};}
return ag;
}};
}
if(preLoadedCapture){
var r=new captureRegistry();
r.register('trace',preLoadedCapture);
preLoadedCapture=undefined; // let GG clean up the capture
ReactDOM.render(React.createElement(Table,{aggrow:r.getAggrow(),__source:{fileName:_jsxFileName,lineNumber:284}}),document.body);}
preLoadedCapture=undefined;// let GG clean up the capture
ReactDOM.render(React.createElement(Table,{aggrow:r.getAggrow()}),document.body);
}// @generated

View File

@ -13,35 +13,35 @@
// TODO:
// selection and arrow keys for navigating
var _jsxFileName='src/table.js';var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if("value" in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor);}}return function(Constructor,protoProps,staticProps){if(protoProps)defineProperties(Constructor.prototype,protoProps);if(staticProps)defineProperties(Constructor,staticProps);return Constructor;};}();function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call&&(typeof call==="object"||typeof call==="function")?call:self;}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function, not "+typeof superClass);}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass;}
var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if("value"in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor);}}return function(Constructor,protoProps,staticProps){if(protoProps)defineProperties(Constructor.prototype,protoProps);if(staticProps)defineProperties(Constructor,staticProps);return Constructor;};}();function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call&&(typeof call==="object"||typeof call==="function")?call:self;}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function, not "+typeof superClass);}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass;}
var rowHeight=20;
var treeIndent=16;var
var treeIndent=16;var
Draggable=function(_React$Component){_inherits(Draggable,_React$Component); // eslint-disable-line no-unused-vars
Draggable=function(_React$Component){_inherits(Draggable,_React$Component);// eslint-disable-line no-unused-vars
function Draggable(props){_classCallCheck(this,Draggable);return _possibleConstructorReturn(this,Object.getPrototypeOf(Draggable).call(this,
props));}_createClass(Draggable,[{key:'render',value:function render()
props));
}_createClass(Draggable,[{key:'render',value:function render()
{
var id=this.props.id;
function dragStart(e){
e.dataTransfer.setData('text/plain',id);}
e.dataTransfer.setData('text/plain',id);
}
return React.cloneElement(
this.props.children,
{draggable:'true',onDragStart:dragStart});}}]);return Draggable;}(React.Component);
{draggable:'true',onDragStart:dragStart});
}}]);return Draggable;}(React.Component);
Draggable.propTypes={
children:React.PropTypes.element.isRequired,
id:React.PropTypes.string.isRequired};var
id:React.PropTypes.string.isRequired};var
DropTarget=function(_React$Component2){_inherits(DropTarget,_React$Component2); // eslint-disable-line no-unused-vars
DropTarget=function(_React$Component2){_inherits(DropTarget,_React$Component2);// eslint-disable-line no-unused-vars
function DropTarget(props){_classCallCheck(this,DropTarget);return _possibleConstructorReturn(this,Object.getPrototypeOf(DropTarget).call(this,
props));}_createClass(DropTarget,[{key:'render',value:function render()
props));
}_createClass(DropTarget,[{key:'render',value:function render()
{
var thisId=this.props.id;
@ -53,36 +53,36 @@ this.props.children,
onDragOver:function onDragOver(e){
var sourceId=e.dataTransfer.getData('text/plain');
if(dropFilter(sourceId)){
e.preventDefault();}},
e.preventDefault();
}
},
onDrop:function onDrop(e){
var sourceId=e.dataTransfer.getData('text/plain');
if(dropFilter(sourceId)){
e.preventDefault();
dropAction(sourceId,thisId);}}});}}]);return DropTarget;}(React.Component);
dropAction(sourceId,thisId);
}
}});
}}]);return DropTarget;}(React.Component);
DropTarget.propTypes={
children:React.PropTypes.element.isRequired,
id:React.PropTypes.string.isRequired,
dropFilter:React.PropTypes.func.isRequired,
dropAction:React.PropTypes.func.isRequired};var
dropAction:React.PropTypes.func.isRequired};var
Table=function(_React$Component3){_inherits(Table,_React$Component3); // eslint-disable-line no-unused-vars
Table=function(_React$Component3){_inherits(Table,_React$Component3);// eslint-disable-line no-unused-vars
function Table(props){_classCallCheck(this,Table);var _this3=_possibleConstructorReturn(this,Object.getPrototypeOf(Table).call(this,
props));
_this3.state={
aggrow:props.aggrow,
viewport:{top:0,height:100}};return _this3;}_createClass(Table,[{key:'scroll',value:function scroll(
viewport:{top:0,height:100}};return _this3;
}_createClass(Table,[{key:'scroll',value:function scroll(
e){
var viewport=e.target;
@ -90,8 +90,8 @@ var top=Math.floor((viewport.scrollTop-viewport.clientHeight*1.0)/rowHeight);
var height=Math.ceil(viewport.clientHeight*3.0/rowHeight);
this.state.viewport.top=top;
this.state.viewport.height=height;
this.forceUpdate();}},{key:'dropAggregator',value:function dropAggregator(
this.forceUpdate();
}},{key:'dropAggregator',value:function dropAggregator(
s,d){
var aggrow=this.state.aggrow;
@ -102,40 +102,40 @@ var dIndex=-1;
var active=aggrow.getActiveAggregators();
var dragged=active[sIndex];
if(d.startsWith('aggregate:insert:')){
dIndex=parseInt(d.substr(17),10);}else
if(d==='divider:insert'){
dIndex=active.length;}else
{
throw 'not allowed to drag '+s+' to '+d;}
dIndex=parseInt(d.substr(17),10);
}else if(d==='divider:insert'){
dIndex=active.length;
}else{
throw'not allowed to drag '+s+' to '+d;
}
if(dIndex>sIndex){
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<expanders.length;_i++){
var _name=aggrow.getExpanderName(expanders[_i]);
var bg=_i%2===0?'white':'lightGray';
headers.push(React.createElement(Draggable,{id:'expander:active:'+_i.toString(),__source:{fileName:_jsxFileName,lineNumber:181}},
headers.push(React.createElement(Draggable,{id:'expander:active:'+_i.toString()},
React.createElement('div',{style:{
width:'128px',
textAlign:'center',
backgroundColor:bg,
flexShrink:'0'},__source:{fileName:_jsxFileName,lineNumber:182}},
flexShrink:'0'}},
_name)));
@ -193,28 +193,28 @@ headers.push(
React.createElement(DropTarget,{
id:'expander:insert:'+(_i+1).toString(),
dropFilter:function dropFilter(){return true;},
dropAction:function dropAction(s,d){_this4.dropAggregator(s,d);},__source:{fileName:_jsxFileName,lineNumber:193}},
dropAction:function dropAction(s,d){_this4.dropAggregator(s,d);}},
React.createElement('div',{style:{
height:'inherit',
backgroundColor:'darkGray',
flexShrink:'0'},__source:{fileName:_jsxFileName,lineNumber:198}},
flexShrink:'0'}},
sep)));}
sep)));
}
return (
React.createElement('div',{style:{width:'100%',height:'100%',display:'flex',flexDirection:'column'},__source:{fileName:_jsxFileName,lineNumber:210}},
return(
React.createElement('div',{style:{width:'100%',height:'100%',display:'flex',flexDirection:'column'}},
React.createElement('div',{style:{
width:'100%',
height:'26px',
display:'flex',
flexDirection:'row',
alignItems:'center',
borderBottom:'2px solid black'},__source:{fileName:_jsxFileName,lineNumber:211}},
borderBottom:'2px solid black'}},
headers),
@ -222,34 +222,34 @@ React.createElement('div',{style:{
width:'100%',
flexGrow:'1',
overflow:'scroll'},
onScroll:function onScroll(e){return _this4.scroll(e);},__source:{fileName:_jsxFileName,lineNumber:221}},
React.createElement('div',{style:{position:'relative'},__source:{fileName:_jsxFileName,lineNumber:226}},
this.renderVirtualizedRows()))));}},{key:'renderVirtualizedRows',value:function renderVirtualizedRows()
onScroll:function onScroll(e){return _this4.scroll(e);}},
React.createElement('div',{style:{position:'relative'}},
this.renderVirtualizedRows()))));
}},{key:'renderVirtualizedRows',value:function renderVirtualizedRows()
{var _this5=this;
var aggrow=this.state.aggrow;
var viewport=this.state.viewport;
var rows=aggrow.getRows(viewport.top,viewport.height);
return (
return(
React.createElement('div',{style:{
position:'absolute',
width:'100%',
height:(rowHeight*(aggrow.getHeight()+20)).toString()+'px'},__source:{fileName:_jsxFileName,lineNumber:239}},
rows.map(function(child){return _this5.renderRow(child);})));}},{key:'renderRow',value:function renderRow(
height:(rowHeight*(aggrow.getHeight()+20)).toString()+'px'}},
rows.map(function(child){return _this5.renderRow(child);})));
}},{key:'renderRow',value:function renderRow(
row){var _this6=this;
if(row===null){
return null;}
return null;
}
var bg='lightGray';
var aggrow=this.state.aggrow;
var columns=[];
@ -257,8 +257,8 @@ var rowText='';
var indent=4+aggrow.getRowIndent(row)*treeIndent;
var aggregates=aggrow.getActiveAggregators();
if(row.parent!==null&&row.parent.expander%2===0){
bg='white';}
bg='white';
}
for(var i=0;i<aggregates.length;i++){
var aggregate=aggrow.getRowAggregate(row,i);
columns.push(
@ -266,45 +266,45 @@ React.createElement('div',{style:{
width:'16px',
height:'inherit',
backgroundColor:'darkGray',
flexShrink:'0'},__source:{fileName:_jsxFileName,lineNumber:265}}));
flexShrink:'0'}}));
columns.push(
React.createElement('div',{style:{
width:'128px',
textAlign:'right',
flexShrink:'0'},__source:{fileName:_jsxFileName,lineNumber:273}},
aggregate));}
flexShrink:'0'}},
aggregate));
}
columns.push(
React.createElement('div',{style:{
width:'16px',
height:'inherit',
backgroundColor:'gold',
flexShrink:'0'},__source:{fileName:_jsxFileName,lineNumber:283}}));
flexShrink:'0'}}));
if(aggrow.canExpand(row)){
rowText+='+';}else
if(aggrow.canContract(row)){
rowText+='-';}else
{
rowText+=' ';}
rowText+='+';
}else if(aggrow.canContract(row)){
rowText+='-';
}else{
rowText+=' ';
}
rowText+=aggrow.getRowLabel(row);
columns.push(
React.createElement('div',{style:{
marginLeft:indent.toString()+'px',
flexShrink:'0',
whiteSpace:'nowrap'},__source:{fileName:_jsxFileName,lineNumber:299}},
whiteSpace:'nowrap'}},
rowText));
return (
return(
React.createElement('div',{style:{
position:'absolute',
height:(rowHeight-1).toString()+'px',
@ -317,10 +317,13 @@ borderBottom:'1px solid gray'},
onClick:function onClick(){
if(aggrow.canExpand(row)){
aggrow.expand(row);
_this6.forceUpdate();}else
if(aggrow.canContract(row)){
_this6.forceUpdate();
}else if(aggrow.canContract(row)){
aggrow.contract(row);
_this6.forceUpdate();}},__source:{fileName:_jsxFileName,lineNumber:308}},
_this6.forceUpdate();
}
}},
columns));
columns));}}]);return Table;}(React.Component);
}}]);return Table;}(React.Component);// @generated

View File

@ -10,6 +10,13 @@
/*eslint no-console-disallow: "off"*/
/*global React ReactDOM Table stringInterner stackRegistry aggrow preLoadedCapture:true*/
function getTypeName(ref) {
if (ref.type === 'Function' && !!ref.value) {
return 'Function ' + ref.value.name;
}
return ref.type;
}
function registerReactComponentTreeImpl(refs, registry, parents, inEdgeNames, trees, id) {
if (parents[id] === undefined) {
// not a component
@ -93,7 +100,7 @@ function registerPathToRoot(roots, refs, registry, reactComponentTree) {
const id = roots[i];
if (visited[id] === undefined) {
const ref = refs[id];
visited[id] = registry.insert(registry.root, ref.type);
visited[id] = registry.insert(registry.root, getTypeName(ref));
breadth.push(id);
}
}
@ -133,7 +140,7 @@ function registerPathToRoot(roots, refs, registry, reactComponentTree) {
// TODO: figure out why we have edges that point to things not JSCell
//console.log('registerPathToRoot unable to follow edge from ' + id + ' to ' + edgeId);
} else {
visited[edgeId] = registry.insert(node, edgeName + edgeRef.type);
visited[edgeId] = registry.insert(node, edgeName + getTypeName(edgeRef));
nextBreadth.push(edgeId);
if (reactComponentTree[edgeId] === undefined) {
reactComponentTree[edgeId] = reactComponentTree[id];
@ -191,7 +198,7 @@ function captureRegistry() {
for (const id in capture.refs) {
const ref = capture.refs[id];
newData[dataOffset + idField] = parseInt(id, 16);
newData[dataOffset + typeField] = this.strings.intern(ref.type);
newData[dataOffset + typeField] = this.strings.intern(getTypeName(ref));
newData[dataOffset + sizeField] = ref.size;
newData[dataOffset + traceField] = internedCaptureId;
const pathNode = rootPathMap[id];

View File

@ -12,6 +12,100 @@
const spawn = require('child_process').spawn;
const fs = require('fs');
const path = require('path');
const http = require('http');
const urlLib = require('url');
const SourceMapConsumer = require('source-map').SourceMapConsumer;
// url: string
// onSuccess: function (SourceMapConsumer)
// onFailure: function (string)
function getSourceMapForUrl(url, onFailure, onSuccess) {
if (!url) {
onFailure('must provide a URL');
return;
}
const parsedUrl = urlLib.parse(url);
const options = {
host: 'localhost',
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;
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);
}
});
}
});
);
};