diff --git a/packages/kbn-profiling-utils/common/profiling.ts b/packages/kbn-profiling-utils/common/profiling.ts index 955be2e1485e6..a8786224b2231 100644 --- a/packages/kbn-profiling-utils/common/profiling.ts +++ b/packages/kbn-profiling-utils/common/profiling.ts @@ -241,14 +241,15 @@ function getExeFileName(metadata: StackFrameMetadata) { * @returns string */ export function getCalleeLabel(metadata: StackFrameMetadata) { + const inlineLabel = metadata.Inline ? '-> ' : ''; if (metadata.FunctionName !== '') { const sourceFilename = metadata.SourceFilename; const sourceURL = sourceFilename ? sourceFilename.split('/').pop() : ''; - return `${getExeFileName(metadata)}: ${getFunctionName(metadata)} in ${sourceURL}#${ - metadata.SourceLine - }`; + return `${inlineLabel}${getExeFileName(metadata)}: ${getFunctionName( + metadata + )} in ${sourceURL}#${metadata.SourceLine}`; } - return getExeFileName(metadata); + return `${inlineLabel}${getExeFileName(metadata)}`; } /** * Get callee function name diff --git a/x-pack/plugins/profiling/public/components/flamegraph/flamegraph_tooltip.tsx b/x-pack/plugins/profiling/public/components/flamegraph/flamegraph_tooltip.tsx index 9d00900c1e192..2f33170e44fcf 100644 --- a/x-pack/plugins/profiling/public/components/flamegraph/flamegraph_tooltip.tsx +++ b/x-pack/plugins/profiling/public/components/flamegraph/flamegraph_tooltip.tsx @@ -7,23 +7,26 @@ import { TooltipContainer } from '@elastic/charts'; import { EuiButtonEmpty, + EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiIcon, EuiPanel, EuiText, + EuiTitle, useEuiTheme, } from '@elastic/eui'; +import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; import { isNumber } from 'lodash'; import React from 'react'; +import { useCalculateImpactEstimate } from '../../hooks/use_calculate_impact_estimates'; import { asCost } from '../../utils/formatters/as_cost'; import { asPercentage } from '../../utils/formatters/as_percentage'; import { asWeight } from '../../utils/formatters/as_weight'; import { CPULabelWithHint } from '../cpu_label_with_hint'; import { TooltipRow } from './tooltip_row'; -import { useCalculateImpactEstimate } from '../../hooks/use_calculate_impact_estimates'; interface Props { isRoot: boolean; @@ -39,6 +42,8 @@ interface Props { comparisonTotalSamples?: number; comparisonTotalSeconds?: number; onShowMoreClick?: () => void; + inline: boolean; + parentLabel?: string; } export function FlameGraphTooltip({ @@ -55,6 +60,8 @@ export function FlameGraphTooltip({ comparisonTotalSamples, comparisonTotalSeconds, onShowMoreClick, + inline, + parentLabel, }: Props) { const theme = useEuiTheme(); const calculateImpactEstimates = useCalculateImpactEstimate(); @@ -83,8 +90,33 @@ export function FlameGraphTooltip({ - {label} + + + {label} + + + + {inline && ( + + {i18n.translate('xpack.profiling.flameGraphTooltip.inlineCallout', { + defaultMessage: 'This function has been inlined by {parentLabel}', + values: { parentLabel }, + })} + + } + size="s" + iconType="iInCircle" + /> + )} {isRoot === false && ( <> edge.includes(valueIndex)) + ] + : undefined; + return ( ); }} diff --git a/x-pack/plugins/profiling/public/components/subchart.tsx b/x-pack/plugins/profiling/public/components/subchart.tsx index dbb3db5caa343..4aaf0e683d839 100644 --- a/x-pack/plugins/profiling/public/components/subchart.tsx +++ b/x-pack/plugins/profiling/public/components/subchart.tsx @@ -19,6 +19,7 @@ import { Tooltip, } from '@elastic/charts'; import { + EuiAccordion, EuiBadge, EuiButton, EuiFlexGroup, @@ -28,11 +29,14 @@ import { EuiLink, EuiSpacer, EuiText, + EuiToolTip, useEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; import type { StackFrameMetadata } from '@kbn/profiling-utils'; +import { groupBy } from 'lodash'; +import React, { Fragment } from 'react'; +import { css } from '@emotion/react'; import { CountPerTime, OTHER_BUCKET_LABEL, TopNSample } from '../../common/topn'; import { useKibanaTimeZoneSetting } from '../hooks/use_kibana_timezone_setting'; import { useProfilingChartsTheme } from '../hooks/use_profiling_charts_theme'; @@ -40,8 +44,8 @@ import { useProfilingParams } from '../hooks/use_profiling_params'; import { useProfilingRouter } from '../hooks/use_profiling_router'; import { asNumber } from '../utils/formatters/as_number'; import { asPercentage } from '../utils/formatters/as_percentage'; -import { StackFrameSummary } from './stack_frame_summary'; import { getTracesViewRouteParams } from '../views/stack_traces_view/utils'; +import { StackFrameSummary } from './stack_frame_summary'; export interface SubChartProps { index: number; @@ -63,6 +67,19 @@ export interface SubChartProps { const NUM_DISPLAYED_FRAMES = 5; +function renderFrameItem(frame: StackFrameMetadata, parentIndex: number | string) { + return ( + + + {parentIndex} + + + + + + ); +} + export function SubChart({ index, color, @@ -97,9 +114,14 @@ export function SubChart({ const compact = !!onShowMoreClick; + const groupedMetadata = groupBy(metadata, 'AddressOrLine'); + const parentsMetadata = Object.values(groupedMetadata) + .map((items) => items.shift()) + .filter((_) => _) as StackFrameMetadata[]; + const displayedFrames = compact - ? metadata.concat().reverse().slice(0, NUM_DISPLAYED_FRAMES) - : metadata.concat().reverse(); + ? parentsMetadata.concat().reverse().slice(0, NUM_DISPLAYED_FRAMES) + : parentsMetadata.concat().reverse(); const hasMoreFrames = displayedFrames.length < metadata.length; @@ -115,23 +137,68 @@ export function SubChart({ }} > - {displayedFrames.map((frame, frameIndex) => ( - <> - - - {metadata.indexOf(frame) + 1} - - + {displayedFrames.map((frame, frameIndex) => { + const parentIndex = parentsMetadata.indexOf(frame) + 1; + const children = groupedMetadata[frame.AddressOrLine].concat().reverse(); + + return ( + <> + {children.length > 0 ? ( + + {`-> ${children.length}`} + + } + > + + {children.map((child, childIndex) => { + return ( + + {renderFrameItem( + child, + `${parentIndex}.${children.length - childIndex} ->` + )} + + ); + })} + + + ) : ( + renderFrameItem(frame, parentIndex) + )} + {frameIndex < displayedFrames.length - 1 || hasMoreFrames ? ( + + - - - {frameIndex < displayedFrames.length - 1 || hasMoreFrames ? ( - - - - ) : null} - - ))} + ) : null} + + ); + })} {hasMoreFrames && !!onShowMoreClick && ( diff --git a/x-pack/plugins/profiling/public/utils/get_flamegraph_model/index.ts b/x-pack/plugins/profiling/public/utils/get_flamegraph_model/index.ts index 32ae0471dd8d5..16d2b6a7a1644 100644 --- a/x-pack/plugins/profiling/public/utils/get_flamegraph_model/index.ts +++ b/x-pack/plugins/profiling/public/utils/get_flamegraph_model/index.ts @@ -65,21 +65,7 @@ export function getFlamegraphModel({ let legendItems: Array<{ label: string; color: string }>; - if (!comparisonFlamegraph) { - const usedFrameTypes = new Set([...primaryFlamegraph.FrameType]); - legendItems = compact( - Object.entries(FRAME_TYPE_COLOR_MAP).map(([frameTypeKey, colors]) => { - const frameType = Number(frameTypeKey) as FrameType; - - return usedFrameTypes.has(frameType) - ? { - color: `#${colors[0].toString(16)}`, - label: describeFrameType(frameType), - } - : undefined; - }) - ); - } else { + if (comparisonFlamegraph) { const positiveChangeInterpolator = d3.interpolateRgb(colorNeutral, colorSuccess); const negativeChangeInterpolator = d3.interpolateRgb(colorNeutral, colorDanger); @@ -162,6 +148,20 @@ export function getFlamegraphModel({ const rgba = rgbToRGBA(Number(nodeColor.replace('#', '0x'))); viewModel.color.set(rgba, 4 * index); }); + } else { + const usedFrameTypes = new Set([...primaryFlamegraph.FrameType]); + legendItems = compact( + Object.entries(FRAME_TYPE_COLOR_MAP).map(([frameTypeKey, colors]) => { + const frameType = Number(frameTypeKey) as FrameType; + + return usedFrameTypes.has(frameType) + ? { + color: `#${colors[0].toString(16)}`, + label: describeFrameType(frameType), + } + : undefined; + }) + ); } return {