mirror of
https://github.com/jlfwong/speedscope.git
synced 2024-11-23 06:22:41 +03:00
0e654801b5
![image](https://user-images.githubusercontent.com/150329/41837387-25417bae-7812-11e8-83cb-d3e6782b734e.png) This provides information about the caller & callees of individual functions selected in the table view.
177 lines
5.4 KiB
TypeScript
177 lines
5.4 KiB
TypeScript
import {h} from 'preact'
|
|
import {css} from 'aphrodite'
|
|
import {ReloadableComponent} from './reloadable'
|
|
|
|
import {CallTreeNode, Frame} from './profile'
|
|
import {Flamechart} from './flamechart'
|
|
|
|
import {Rect, Vec2, AffineTransform} from './math'
|
|
import {formatPercent} from './utils'
|
|
import {FlamechartMinimapView} from './flamechart-minimap-view'
|
|
|
|
import {style} from './flamechart-style'
|
|
import {Sizes, commonStyle} from './style'
|
|
import {CanvasContext} from './canvas-context'
|
|
import {FlamechartRenderer} from './flamechart-renderer'
|
|
import {FlamechartDetailView} from './flamechart-detail-view'
|
|
import {FlamechartPanZoomView} from './flamechart-pan-zoom-view'
|
|
import {Hovertip} from './hovertip'
|
|
|
|
interface FlamechartViewProps {
|
|
flamechart: Flamechart
|
|
canvasContext: CanvasContext
|
|
flamechartRenderer: FlamechartRenderer
|
|
getCSSColorForFrame: (frame: Frame) => string
|
|
}
|
|
|
|
interface FlamechartViewState {
|
|
hover: {
|
|
node: CallTreeNode
|
|
event: MouseEvent
|
|
} | null
|
|
selectedNode: CallTreeNode | null
|
|
configSpaceViewportRect: Rect
|
|
}
|
|
|
|
export class FlamechartView extends ReloadableComponent<FlamechartViewProps, FlamechartViewState> {
|
|
constructor() {
|
|
super()
|
|
this.state = {
|
|
hover: null,
|
|
selectedNode: null,
|
|
configSpaceViewportRect: Rect.empty,
|
|
}
|
|
}
|
|
|
|
private configSpaceSize() {
|
|
return new Vec2(
|
|
this.props.flamechart.getTotalWeight(),
|
|
this.props.flamechart.getLayers().length,
|
|
)
|
|
}
|
|
|
|
private minConfigSpaceViewportRectWidth() {
|
|
return Math.min(
|
|
this.props.flamechart.getTotalWeight(),
|
|
3 * this.props.flamechart.getMinFrameWidth(),
|
|
)
|
|
}
|
|
|
|
private setConfigSpaceViewportRect = (viewportRect: Rect): void => {
|
|
const configSpaceDetailViewHeight = Sizes.DETAIL_VIEW_HEIGHT / Sizes.FRAME_HEIGHT
|
|
|
|
const configSpaceOriginBounds = new Rect(
|
|
new Vec2(0, -1),
|
|
Vec2.max(
|
|
new Vec2(0, 0),
|
|
this.configSpaceSize()
|
|
.minus(viewportRect.size)
|
|
.plus(new Vec2(0, configSpaceDetailViewHeight + 1)),
|
|
),
|
|
)
|
|
|
|
const configSpaceSizeBounds = new Rect(
|
|
new Vec2(this.minConfigSpaceViewportRectWidth(), viewportRect.height()),
|
|
new Vec2(this.configSpaceSize().x, viewportRect.height()),
|
|
)
|
|
|
|
this.setState({
|
|
configSpaceViewportRect: new Rect(
|
|
configSpaceOriginBounds.closestPointTo(viewportRect.origin),
|
|
configSpaceSizeBounds.closestPointTo(viewportRect.size),
|
|
),
|
|
})
|
|
}
|
|
|
|
private transformViewport = (transform: AffineTransform): void => {
|
|
const viewportRect = transform.transformRect(this.state.configSpaceViewportRect)
|
|
this.setConfigSpaceViewportRect(viewportRect)
|
|
}
|
|
|
|
onNodeHover = (hover: {node: CallTreeNode; event: MouseEvent} | null) => {
|
|
this.setState({hover})
|
|
}
|
|
|
|
onNodeClick = (node: CallTreeNode | null) => {
|
|
this.setState({
|
|
selectedNode: node,
|
|
})
|
|
}
|
|
|
|
formatValue(weight: number) {
|
|
const totalWeight = this.props.flamechart.getTotalWeight()
|
|
const percent = 100 * weight / totalWeight
|
|
const formattedPercent = formatPercent(percent)
|
|
return `${this.props.flamechart.formatValue(weight)} (${formattedPercent})`
|
|
}
|
|
|
|
renderTooltip() {
|
|
if (!this.container) return null
|
|
|
|
const {hover} = this.state
|
|
if (!hover) return null
|
|
const {width, height, left, top} = this.container.getBoundingClientRect()
|
|
const offset = new Vec2(hover.event.clientX - left, hover.event.clientY - top)
|
|
|
|
return (
|
|
<Hovertip containerSize={new Vec2(width, height)} offset={offset}>
|
|
<span className={css(style.hoverCount)}>
|
|
{this.formatValue(hover.node.getTotalWeight())}
|
|
</span>{' '}
|
|
{hover.node.frame.name}
|
|
</Hovertip>
|
|
)
|
|
}
|
|
|
|
container: HTMLDivElement | null = null
|
|
containerRef = (container?: Element) => {
|
|
this.container = (container as HTMLDivElement) || null
|
|
}
|
|
|
|
panZoomView: FlamechartPanZoomView | null = null
|
|
panZoomRef = (view: FlamechartPanZoomView | null) => {
|
|
this.panZoomView = view
|
|
}
|
|
subcomponents() {
|
|
return {
|
|
panZoom: this.panZoomView,
|
|
}
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<div className={css(style.fill, commonStyle.vbox)} ref={this.containerRef}>
|
|
<FlamechartMinimapView
|
|
configSpaceViewportRect={this.state.configSpaceViewportRect}
|
|
transformViewport={this.transformViewport}
|
|
flamechart={this.props.flamechart}
|
|
flamechartRenderer={this.props.flamechartRenderer}
|
|
canvasContext={this.props.canvasContext}
|
|
setConfigSpaceViewportRect={this.setConfigSpaceViewportRect}
|
|
/>
|
|
<FlamechartPanZoomView
|
|
ref={this.panZoomRef}
|
|
canvasContext={this.props.canvasContext}
|
|
flamechart={this.props.flamechart}
|
|
flamechartRenderer={this.props.flamechartRenderer}
|
|
renderInverted={false}
|
|
onNodeHover={this.onNodeHover}
|
|
onNodeSelect={this.onNodeClick}
|
|
selectedNode={this.state.selectedNode}
|
|
transformViewport={this.transformViewport}
|
|
configSpaceViewportRect={this.state.configSpaceViewportRect}
|
|
setConfigSpaceViewportRect={this.setConfigSpaceViewportRect}
|
|
/>
|
|
{this.renderTooltip()}
|
|
{this.state.selectedNode && (
|
|
<FlamechartDetailView
|
|
flamechart={this.props.flamechart}
|
|
getCSSColorForFrame={this.props.getCSSColorForFrame}
|
|
selectedNode={this.state.selectedNode}
|
|
/>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
}
|