TimelineProfileTree.js 10.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. export class Node{constructor(id,event){this.totalTime=0;this.selfTime=0;this.id=id;this.event=event;this.parent;this._groupId='';this._isGroupNode=false;}
  2. isGroupNode(){return this._isGroupNode;}
  3. hasChildren(){throw'Not implemented';}
  4. children(){throw'Not implemented';}
  5. searchTree(matchFunction,results){results=results||[];if(this.event&&matchFunction(this.event)){results.push(this);}
  6. for(const child of this.children().values()){child.searchTree(matchFunction,results);}
  7. return results;}}
  8. export class TopDownNode extends Node{constructor(id,event,parent){super(id,event);this._root=parent&&parent._root;this._hasChildren=false;this._children=null;this.parent=parent;}
  9. hasChildren(){return this._hasChildren;}
  10. children(){return this._children||this._buildChildren();}
  11. _buildChildren(){const path=[];for(let node=this;node.parent&&!node._isGroupNode;node=node.parent){path.push((node));}
  12. path.reverse();const children=new Map();const self=this;const root=this._root;const startTime=root._startTime;const endTime=root._endTime;const instantEventCallback=root._doNotAggregate?onInstantEvent:undefined;const eventIdCallback=root._doNotAggregate?undefined:_eventId;const eventGroupIdCallback=root._eventGroupIdCallback;let depth=0;let matchedDepth=0;let currentDirectChild=null;TimelineModel.TimelineModel.forEachEvent(root._events,onStartEvent,onEndEvent,instantEventCallback,startTime,endTime,root._filter);function onStartEvent(e){++depth;if(depth>path.length+2){return;}
  13. if(!matchPath(e)){return;}
  14. const duration=Math.min(endTime,e.endTime)-Math.max(startTime,e.startTime);if(duration<0){console.error('Negative event duration');}
  15. processEvent(e,duration);}
  16. function onInstantEvent(e){++depth;if(matchedDepth===path.length&&depth<=path.length+2){processEvent(e,0);}
  17. --depth;}
  18. function processEvent(e,duration){if(depth===path.length+2){currentDirectChild._hasChildren=true;currentDirectChild.selfTime-=duration;return;}
  19. let id;let groupId='';if(!eventIdCallback){id=Symbol('uniqueId');}else{id=eventIdCallback(e);groupId=eventGroupIdCallback?eventGroupIdCallback(e):'';if(groupId){id+='/'+groupId;}}
  20. let node=children.get(id);if(!node){node=new TopDownNode(id,e,self);node._groupId=groupId;children.set(id,node);}
  21. node.selfTime+=duration;node.totalTime+=duration;currentDirectChild=node;}
  22. function matchPath(e){if(matchedDepth===path.length){return true;}
  23. if(matchedDepth!==depth-1){return false;}
  24. if(!e.endTime){return false;}
  25. if(!eventIdCallback){if(e===path[matchedDepth].event){++matchedDepth;}
  26. return false;}
  27. let id=eventIdCallback(e);const groupId=eventGroupIdCallback?eventGroupIdCallback(e):'';if(groupId){id+='/'+groupId;}
  28. if(id===path[matchedDepth].id){++matchedDepth;}
  29. return false;}
  30. function onEndEvent(e){--depth;if(matchedDepth>depth){matchedDepth=depth;}}
  31. this._children=children;return children;}}
  32. export class TopDownRootNode extends TopDownNode{constructor(events,filters,startTime,endTime,doNotAggregate,eventGroupIdCallback){super('',null,null);this._root=this;this._events=events;this._filter=e=>filters.every(f=>f.accept(e));this._startTime=startTime;this._endTime=endTime;this._eventGroupIdCallback=eventGroupIdCallback;this._doNotAggregate=doNotAggregate;this.totalTime=endTime-startTime;this.selfTime=this.totalTime;}
  33. children(){return this._children||this._grouppedTopNodes();}
  34. _grouppedTopNodes(){const flatNodes=super.children();for(const node of flatNodes.values()){this.selfTime-=node.totalTime;}
  35. if(!this._eventGroupIdCallback){return flatNodes;}
  36. const groupNodes=new Map();for(const node of flatNodes.values()){const groupId=this._eventGroupIdCallback((node.event));let groupNode=groupNodes.get(groupId);if(!groupNode){groupNode=new GroupNode(groupId,this,(node.event));groupNodes.set(groupId,groupNode);}
  37. groupNode.addChild(node,node.selfTime,node.totalTime);}
  38. this._children=groupNodes;return groupNodes;}}
  39. export class BottomUpRootNode extends Node{constructor(events,textFilter,filters,startTime,endTime,eventGroupIdCallback){super('',null);this._children=null;this._events=events;this._textFilter=textFilter;this._filter=e=>filters.every(f=>f.accept(e));this._startTime=startTime;this._endTime=endTime;this._eventGroupIdCallback=eventGroupIdCallback;this.totalTime=endTime-startTime;}
  40. hasChildren(){return true;}
  41. _filterChildren(children){for(const[id,child]of children){if(child.event&&!this._textFilter.accept(child.event)){children.delete((id));}}
  42. return children;}
  43. children(){if(!this._children){this._children=this._filterChildren(this._grouppedTopNodes());}
  44. return this._children;}
  45. _ungrouppedTopNodes(){const root=this;const startTime=this._startTime;const endTime=this._endTime;const nodeById=new Map();const selfTimeStack=[endTime-startTime];const firstNodeStack=[];const totalTimeById=new Map();TimelineModel.TimelineModel.forEachEvent(this._events,onStartEvent,onEndEvent,undefined,startTime,endTime,this._filter);function onStartEvent(e){const duration=Math.min(e.endTime,endTime)-Math.max(e.startTime,startTime);selfTimeStack[selfTimeStack.length-1]-=duration;selfTimeStack.push(duration);const id=_eventId(e);const noNodeOnStack=!totalTimeById.has(id);if(noNodeOnStack){totalTimeById.set(id,duration);}
  46. firstNodeStack.push(noNodeOnStack);}
  47. function onEndEvent(e){const id=_eventId(e);let node=nodeById.get(id);if(!node){node=new BottomUpNode(root,id,e,false,root);nodeById.set(id,node);}
  48. node.selfTime+=selfTimeStack.pop();if(firstNodeStack.pop()){node.totalTime+=totalTimeById.get(id);totalTimeById.delete(id);}
  49. if(firstNodeStack.length){node.setHasChildren();}}
  50. this.selfTime=selfTimeStack.pop();for(const pair of nodeById){if(pair[1].selfTime<=0){nodeById.delete((pair[0]));}}
  51. return nodeById;}
  52. _grouppedTopNodes(){const flatNodes=this._ungrouppedTopNodes();if(!this._eventGroupIdCallback){return flatNodes;}
  53. const groupNodes=new Map();for(const node of flatNodes.values()){const groupId=this._eventGroupIdCallback((node.event));let groupNode=groupNodes.get(groupId);if(!groupNode){groupNode=new GroupNode(groupId,this,(node.event));groupNodes.set(groupId,groupNode);}
  54. groupNode.addChild(node,node.selfTime,node.selfTime);}
  55. return groupNodes;}}
  56. export class GroupNode extends Node{constructor(id,parent,event){super(id,event);this._children=new Map();this.parent=parent;this._isGroupNode=true;}
  57. addChild(child,selfTime,totalTime){this._children.set(child.id,child);this.selfTime+=selfTime;this.totalTime+=totalTime;child.parent=this;}
  58. hasChildren(){return true;}
  59. children(){return this._children;}}
  60. export class BottomUpNode extends Node{constructor(root,id,event,hasChildren,parent){super(id,event);this.parent=parent;this._root=root;this._depth=(parent._depth||0)+1;this._cachedChildren=null;this._hasChildren=hasChildren;}
  61. setHasChildren(){this._hasChildren=true;}
  62. hasChildren(){return this._hasChildren;}
  63. children(){if(this._cachedChildren){return this._cachedChildren;}
  64. const selfTimeStack=[0];const eventIdStack=[];const eventStack=[];const nodeById=new Map();const startTime=this._root._startTime;const endTime=this._root._endTime;let lastTimeMarker=startTime;const self=this;TimelineModel.TimelineModel.forEachEvent(this._root._events,onStartEvent,onEndEvent,undefined,startTime,endTime,this._root._filter);function onStartEvent(e){const duration=Math.min(e.endTime,endTime)-Math.max(e.startTime,startTime);if(duration<0){console.assert(false,'Negative duration of an event');}
  65. selfTimeStack[selfTimeStack.length-1]-=duration;selfTimeStack.push(duration);const id=_eventId(e);eventIdStack.push(id);eventStack.push(e);}
  66. function onEndEvent(e){const selfTime=selfTimeStack.pop();const id=eventIdStack.pop();eventStack.pop();let node;for(node=self;node._depth>1;node=node.parent){if(node.id!==eventIdStack[eventIdStack.length+1-node._depth]){return;}}
  67. if(node.id!==id||eventIdStack.length<self._depth){return;}
  68. const childId=eventIdStack[eventIdStack.length-self._depth];node=nodeById.get(childId);if(!node){const event=eventStack[eventStack.length-self._depth];const hasChildren=eventStack.length>self._depth;node=new BottomUpNode(self._root,childId,event,hasChildren,self);nodeById.set(childId,node);}
  69. const totalTime=Math.min(e.endTime,endTime)-Math.max(e.startTime,lastTimeMarker);node.selfTime+=selfTime;node.totalTime+=totalTime;lastTimeMarker=Math.min(e.endTime,endTime);}
  70. this._cachedChildren=this._root._filterChildren(nodeById);return this._cachedChildren;}
  71. searchTree(matchFunction,results){results=results||[];if(this.event&&matchFunction(this.event)){results.push(this);}
  72. return results;}}
  73. export function eventURL(event){const data=event.args['data']||event.args['beginData'];if(data&&data['url']){return data['url'];}
  74. let frame=eventStackFrame(event);while(frame){const url=frame['url'];if(url){return url;}
  75. frame=frame.parent;}
  76. return null;}
  77. export function eventStackFrame(event){if(event.name===TimelineModel.TimelineModel.RecordType.JSFrame){return(event.args['data']||null);}
  78. return TimelineModel.TimelineData.forEvent(event).topFrame();}
  79. export function _eventId(event){if(event.name===TimelineModel.TimelineModel.RecordType.TimeStamp){return`${event.name}:${event.args.data.message}`;}
  80. if(event.name!==TimelineModel.TimelineModel.RecordType.JSFrame){return event.name;}
  81. const frame=event.args['data'];const location=frame['scriptId']||frame['url']||'';const functionName=frame['functionName'];const name=TimelineModel.TimelineJSProfileProcessor.isNativeRuntimeFrame(frame)?TimelineModel.TimelineJSProfileProcessor.nativeGroup(functionName)||functionName:`${functionName}:${frame['lineNumber']}:${frame['columnNumber']}`;return`f:${name}@${location}`;}
  82. self.TimelineModel=self.TimelineModel||{};TimelineModel=TimelineModel||{};TimelineModel.TimelineProfileTree={};TimelineModel.TimelineProfileTree.Node=Node;TimelineModel.TimelineProfileTree.TopDownNode=TopDownNode;TimelineModel.TimelineProfileTree.TopDownRootNode=TopDownRootNode;TimelineModel.TimelineProfileTree.BottomUpRootNode=BottomUpRootNode;TimelineModel.TimelineProfileTree.GroupNode=GroupNode;TimelineModel.TimelineProfileTree.BottomUpNode=BottomUpNode;TimelineModel.TimelineProfileTree.eventURL=eventURL;TimelineModel.TimelineProfileTree.eventStackFrame=eventStackFrame;TimelineModel.TimelineProfileTree._eventId=_eventId;TimelineModel.TimelineProfileTree.ChildrenCache;