Cauê Marcondes 2025-02-17 15:22:58 -03:00 committed by GitHub
parent c5857f929d
commit 929cd7de4f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 338 additions and 219 deletions

View file

@ -10,19 +10,27 @@ import type {
EuiDataGridColumn,
EuiDataGridSorting,
} from '@elastic/eui';
import { EuiButtonIcon, EuiDataGrid, EuiScreenReaderOnly, useEuiTheme } from '@elastic/eui';
import {
EuiButtonIcon,
EuiDataGrid,
EuiFlexGroup,
EuiFlexItem,
EuiScreenReaderOnly,
useEuiTheme,
} from '@elastic/eui';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import type { StackFrameMetadata, TopNFunctions } from '@kbn/profiling-utils';
import {
getCalleeFunction,
TopNComparisonFunctionSortField,
TopNFunctionSortField,
getCalleeFunction,
} from '@kbn/profiling-utils';
import { orderBy } from 'lodash';
import React, { useEffect, useMemo, useState } from 'react';
import { useCalculateImpactEstimate } from '../../hooks/use_calculate_impact_estimates';
import { FrameInformationTooltip } from '../frame_information_window/frame_information_tooltip';
import { SearchFunctionsInput } from '../search_functions_input';
import { FunctionRow } from '../topn_functions/function_row';
import type { IFunctionRow } from '../topn_functions/utils';
import { convertRowToFrame, getFunctionsRows, getTotalCount } from '../topn_functions/utils';
@ -84,6 +92,8 @@ interface Props {
comparisonSortField: TopNComparisonFunctionSortField;
totalSeconds: number;
comparisonTotalSeconds: number;
searchFunctionName: string;
onSearchFunctionNameChange: (functionName: string) => void;
}
export function DifferentialTopNFunctionsGrid({
@ -101,6 +111,8 @@ export function DifferentialTopNFunctionsGrid({
sortDirection,
sortField,
totalSeconds,
searchFunctionName,
onSearchFunctionNameChange,
}: Props) {
const theme = useEuiTheme();
const calculateImpactEstimates = useCalculateImpactEstimate();
@ -145,6 +157,7 @@ export function DifferentialTopNFunctionsGrid({
calculateImpactEstimates,
topNFunctions: base,
totalSeconds,
functionNameSearchQuery: searchFunctionName,
}),
comparisonRows: getFunctionsRows({
baselineScaleFactor,
@ -153,6 +166,7 @@ export function DifferentialTopNFunctionsGrid({
comparisonTopNFunctions: base,
topNFunctions: comparison,
totalSeconds: comparisonTotalSeconds,
functionNameSearchQuery: searchFunctionName,
}),
};
}, [
@ -162,6 +176,7 @@ export function DifferentialTopNFunctionsGrid({
comparison,
comparisonScaleFactor,
comparisonTotalSeconds,
searchFunctionName,
totalSeconds,
]);
@ -226,109 +241,114 @@ export function DifferentialTopNFunctionsGrid({
const rowCount = Math.max(sortedBaseRows.length, sortedComparisonRows.length);
return (
<>
<EuiDataGrid
data-test-subj="profilingDiffTopNFunctionsGrid"
css={css`
.thickBorderLeft {
border-left: ${theme.euiTheme.border.thick} !important;
}
`}
aria-label={i18n.translate(
'xpack.profiling.onWeelkDiffTopN.euiDataGrid.topNFunctionsLabel',
{
defaultMessage: 'TopN functions',
}
)}
columns={columns}
leadingControlColumns={[
{
id: 'actions',
width: 40,
headerCellRender() {
return (
<EuiScreenReaderOnly>
<span>
{i18n.translate('xpack.profiling.topNFunctionsGrid.span.controlsLabel', {
defaultMessage: 'Controls',
})}
</span>
</EuiScreenReaderOnly>
);
},
rowCellRender: function RowCellRender({ rowIndex }) {
function handleOnClick() {
const row = sortedBaseRows[rowIndex];
const currentFrameId = getFrameIdentification(row.frame);
const compareRow = sortedComparisonRows.find(
(item) => getFrameIdentification(item.frame) === currentFrameId
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem grow={false}>
<SearchFunctionsInput onChange={onSearchFunctionNameChange} value={searchFunctionName} />
</EuiFlexItem>
<EuiFlexItem>
<EuiDataGrid
data-test-subj="profilingDiffTopNFunctionsGrid"
css={css`
.thickBorderLeft {
border-left: ${theme.euiTheme.border.thick};
}
`}
aria-label={i18n.translate(
'xpack.profiling.onWeelkDiffTopN.euiDataGrid.topNFunctionsLabel',
{
defaultMessage: 'TopN functions',
}
)}
columns={columns}
leadingControlColumns={[
{
id: 'actions',
width: 40,
headerCellRender() {
return (
<EuiScreenReaderOnly>
<span>
{i18n.translate('xpack.profiling.topNFunctionsGrid.span.controlsLabel', {
defaultMessage: 'Controls',
})}
</span>
</EuiScreenReaderOnly>
);
setRowsInformation({
baseRow: row,
comparisonRow: compareRow,
});
}
return (
<EuiButtonIcon
data-test-subj="profilingTopNFunctionsGridButton"
aria-label={i18n.translate(
'xpack.profiling.topNFunctionsGrid.euiButtonIcon.showActionsLabel',
{ defaultMessage: 'Show actions' }
)}
iconType="expand"
color="text"
onClick={handleOnClick}
/>
);
},
rowCellRender: function RowCellRender({ rowIndex }) {
function handleOnClick() {
const row = sortedBaseRows[rowIndex];
const currentFrameId = getFrameIdentification(row.frame);
const compareRow = sortedComparisonRows.find(
(item) => getFrameIdentification(item.frame) === currentFrameId
);
setRowsInformation({
baseRow: row,
comparisonRow: compareRow,
});
}
return (
<EuiButtonIcon
data-test-subj="profilingTopNFunctionsGridButton"
aria-label={i18n.translate(
'xpack.profiling.topNFunctionsGrid.euiButtonIcon.showActionsLabel',
{ defaultMessage: 'Show actions' }
)}
iconType="expand"
color="text"
onClick={handleOnClick}
/>
);
},
},
},
]}
columnVisibility={{ visibleColumns, setVisibleColumns }}
rowCount={rowCount}
renderCellValue={CellValue}
sorting={{
columns: [
{ id: sortField, direction: sortDirection },
{ id: comparisonSortField, direction: comparisonSortDirection },
],
onSort,
}}
pagination={{
pageIndex,
pageSize: 100,
// Left it empty on purpose as it is a required property on the pagination
onChangeItemsPerPage: () => {},
onChangePage,
pageSizeOptions: [],
}}
rowHeightsOptions={{ defaultHeight: 'auto' }}
toolbarVisibility={{
showColumnSelector: false,
showKeyboardShortcuts: false,
showDisplaySelector: false,
showSortSelector: false,
}}
/>
{rowsInformation && (
<FrameInformationTooltip
compressed
comparisonRank={rowsInformation.comparisonRow?.rank}
comparisonFrame={
rowsInformation.comparisonRow
? convertRowToFrame(rowsInformation.comparisonRow)
: undefined
}
comparisonTotalSamples={comparisonTotalCount}
comparisonTotalSeconds={comparisonTotalSeconds}
rank={rowsInformation.baseRow.rank}
frame={convertRowToFrame(rowsInformation.baseRow)}
totalSamples={totalCount}
totalSeconds={totalSeconds}
onClose={() => {
setRowsInformation(undefined);
]}
columnVisibility={{ visibleColumns, setVisibleColumns }}
rowCount={rowCount}
renderCellValue={CellValue}
sorting={{
columns: [
{ id: sortField, direction: sortDirection },
{ id: comparisonSortField, direction: comparisonSortDirection },
],
onSort,
}}
pagination={{
pageIndex,
pageSize: 100,
// Left it empty on purpose as it is a required property on the pagination
onChangeItemsPerPage: () => {},
onChangePage,
pageSizeOptions: [],
}}
rowHeightsOptions={{ defaultHeight: 'auto' }}
toolbarVisibility={{
showColumnSelector: false,
showKeyboardShortcuts: false,
showDisplaySelector: false,
showSortSelector: false,
}}
/>
)}
</>
{rowsInformation && (
<FrameInformationTooltip
compressed
comparisonRank={rowsInformation.comparisonRow?.rank}
comparisonFrame={
rowsInformation.comparisonRow
? convertRowToFrame(rowsInformation.comparisonRow)
: undefined
}
comparisonTotalSamples={comparisonTotalCount}
comparisonTotalSeconds={comparisonTotalSeconds}
rank={rowsInformation.baseRow.rank}
frame={convertRowToFrame(rowsInformation.baseRow)}
totalSamples={totalCount}
totalSeconds={totalSeconds}
onClose={() => {
setRowsInformation(undefined);
}}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
);
}

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EuiFieldSearch } from '@elastic/eui';
import { debounce } from 'lodash';
import React, { useCallback, useMemo, useState } from 'react';
interface Props {
value: string;
onChange: (functionName: string) => void;
}
export function SearchFunctionsInput({ value, onChange }: Props) {
const [searchQuery, setSearchQuery] = useState(value);
const debouncedOnChange = useMemo(() => debounce(onChange, 300), [onChange]);
const handleSearchChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setSearchQuery(e.target.value);
debouncedOnChange(e.target.value);
},
[debouncedOnChange]
);
return (
<EuiFieldSearch
data-test-subj="tableSearchInput"
placeholder="Search functions by name"
fullWidth={true}
value={searchQuery}
onChange={handleSearchChange}
/>
);
}

View file

@ -11,11 +11,17 @@ import type {
EuiDataGridControlColumn,
EuiDataGridSorting,
} from '@elastic/eui';
import { EuiButtonIcon, EuiDataGrid, EuiScreenReaderOnly } from '@elastic/eui';
import {
EuiButtonIcon,
EuiDataGrid,
EuiFlexGroup,
EuiFlexItem,
EuiScreenReaderOnly,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useUiTracker } from '@kbn/observability-shared-plugin/public';
import type { TopNFunctions } from '@kbn/profiling-utils';
import { getCalleeFunction, TopNFunctionSortField } from '@kbn/profiling-utils';
import { TopNFunctionSortField, getCalleeFunction } from '@kbn/profiling-utils';
import { last, orderBy } from 'lodash';
import React, { useMemo, useState } from 'react';
import type { GridOnScrollProps } from 'react-window';
@ -23,6 +29,7 @@ import { useCalculateImpactEstimate } from '../../hooks/use_calculate_impact_est
import { CPULabelWithHint } from '../cpu_label_with_hint';
import { FrameInformationTooltip } from '../frame_information_window/frame_information_tooltip';
import { LabelWithHint } from '../label_with_hint';
import { SearchFunctionsInput } from '../search_functions_input';
import { FunctionRow } from './function_row';
import type { IFunctionRow } from './utils';
import { convertRowToFrame, getFunctionsRows, getTotalCount } from './utils';
@ -45,6 +52,8 @@ interface Props {
onChangeSort: (sorting: EuiDataGridSorting['columns'][0]) => void;
dataTestSubj?: string;
isEmbedded?: boolean;
searchFunctionName: string;
onSearchFunctionNameChange: (functionName: string) => void;
}
export const TopNFunctionsGrid = ({
@ -65,6 +74,8 @@ export const TopNFunctionsGrid = ({
onChangeSort,
dataTestSubj = 'topNFunctionsGrid',
isEmbedded = false,
searchFunctionName,
onSearchFunctionNameChange,
}: Props) => {
const [selectedRow, setSelectedRow] = useState<IFunctionRow | undefined>();
const trackProfilingEvent = useUiTracker({ app: 'profiling' });
@ -87,12 +98,14 @@ export const TopNFunctionsGrid = ({
topNFunctions,
totalSeconds,
calculateImpactEstimates,
functionNameSearchQuery: searchFunctionName,
});
}, [
baselineScaleFactor,
calculateImpactEstimates,
comparisonScaleFactor,
comparisonTopNFunctions,
searchFunctionName,
topNFunctions,
totalSeconds,
]);
@ -288,55 +301,59 @@ export const TopNFunctionsGrid = ({
/>
);
}
return null;
}
return (
<>
<EuiDataGrid
data-test-subj={dataTestSubj}
aria-label={i18n.translate(
'xpack.profiling.topNFunctionsGrid.euiDataGrid.topNFunctionsLabel',
{ defaultMessage: 'TopN functions' }
)}
columns={columns}
columnVisibility={{ visibleColumns, setVisibleColumns }}
rowCount={sortedRows.length}
renderCellValue={RenderCellValue}
sorting={{ columns: [{ id: sortField, direction: sortDirection }], onSort }}
leadingControlColumns={leadingControlColumns}
pagination={{
pageIndex,
pageSize: 100,
// Left it empty on purpose as it is a required property on the pagination
onChangeItemsPerPage: () => {},
onChangePage,
pageSizeOptions: [],
}}
rowHeightsOptions={{ defaultHeight: 'auto' }}
toolbarVisibility={{
showColumnSelector: false,
showKeyboardShortcuts: !isDifferentialView,
showDisplaySelector: !isDifferentialView,
showFullScreenSelector: showFullScreenSelector && !isDifferentialView,
showSortSelector: false,
}}
virtualizationOptions={{
onScroll,
}}
/>
{selectedRow && (
<FrameInformationTooltip
compressed
onClose={() => {
setSelectedRow(undefined);
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem grow={false}>
<SearchFunctionsInput onChange={onSearchFunctionNameChange} value={searchFunctionName} />
</EuiFlexItem>
<EuiFlexItem>
<EuiDataGrid
data-test-subj={dataTestSubj}
aria-label={i18n.translate(
'xpack.profiling.topNFunctionsGrid.euiDataGrid.topNFunctionsLabel',
{ defaultMessage: 'TopN functions' }
)}
columns={columns}
columnVisibility={{ visibleColumns, setVisibleColumns }}
rowCount={sortedRows.length}
renderCellValue={RenderCellValue}
sorting={{ columns: [{ id: sortField, direction: sortDirection }], onSort }}
leadingControlColumns={leadingControlColumns}
pagination={{
pageIndex,
pageSize: 100,
// Left it empty on purpose as it is a required property on the pagination
onChangeItemsPerPage: () => {},
onChangePage,
pageSizeOptions: [],
}}
frame={convertRowToFrame(selectedRow)}
totalSeconds={totalSeconds}
totalSamples={totalCount}
showSymbolsStatus={!isEmbedded}
rowHeightsOptions={{ defaultHeight: 'auto' }}
toolbarVisibility={{
showColumnSelector: false,
showKeyboardShortcuts: !isDifferentialView,
showDisplaySelector: !isDifferentialView,
showFullScreenSelector: showFullScreenSelector && !isDifferentialView,
showSortSelector: false,
}}
virtualizationOptions={{ onScroll }}
/>
)}
</>
{selectedRow && (
<FrameInformationTooltip
compressed
onClose={() => {
setSelectedRow(undefined);
}}
frame={convertRowToFrame(selectedRow)}
totalSeconds={totalSeconds}
totalSamples={totalCount}
showSymbolsStatus={!isEmbedded}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -5,7 +5,11 @@
* 2.0.
*/
import { keyBy } from 'lodash';
import type { StackFrameMetadata, TopNFunctions } from '@kbn/profiling-utils';
import {
getCalleeFunction,
type StackFrameMetadata,
type TopNFunctions,
} from '@kbn/profiling-utils';
import type {
CalculateImpactEstimates,
ImpactEstimates,
@ -71,6 +75,7 @@ export function getFunctionsRows({
topNFunctions,
totalSeconds,
calculateImpactEstimates,
functionNameSearchQuery,
}: {
baselineScaleFactor?: number;
comparisonScaleFactor?: number;
@ -78,6 +83,7 @@ export function getFunctionsRows({
topNFunctions?: TopNFunctions;
totalSeconds: number;
calculateImpactEstimates: CalculateImpactEstimates;
functionNameSearchQuery: string;
}): IFunctionRow[] {
if (!topNFunctions || !topNFunctions.TotalCount || topNFunctions.TotalCount === 0) {
return [];
@ -87,72 +93,80 @@ export function getFunctionsRows({
? keyBy(comparisonTopNFunctions.TopN, 'Id')
: {};
return topNFunctions.TopN.filter((topN) => topN.CountExclusive >= 0).map((topN, i) => {
const comparisonRow = comparisonDataById?.[topN.Id];
const scaledSelfCPU = scaleValue({
value: topN.CountExclusive,
scaleFactor: baselineScaleFactor,
});
const totalCPUPerc = (topN.CountInclusive / topNFunctions.selfCPU) * 100;
const selfCPUPerc = (topN.CountExclusive / topNFunctions.selfCPU) * 100;
const impactEstimates =
totalSeconds > 0
? calculateImpactEstimates({
countExclusive: topN.CountExclusive,
countInclusive: topN.CountInclusive,
totalSamples: topNFunctions.selfCPU,
totalSeconds,
})
: undefined;
function calculateDiff() {
if (comparisonTopNFunctions && comparisonRow) {
const comparisonScaledSelfCPU = scaleValue({
value: comparisonRow.CountExclusive,
scaleFactor: comparisonScaleFactor,
});
const scaledDiffSamples = scaledSelfCPU - comparisonScaledSelfCPU;
return {
id: comparisonRow.Id,
rank: topN.Rank - comparisonRow.Rank,
samples: scaledDiffSamples,
selfCPU: comparisonRow.CountExclusive,
totalCPU: comparisonRow.CountInclusive,
selfCPUPerc:
selfCPUPerc - (comparisonRow.CountExclusive / comparisonTopNFunctions.selfCPU) * 100,
totalCPUPerc:
totalCPUPerc - (comparisonRow.CountInclusive / comparisonTopNFunctions.selfCPU) * 100,
selfAnnualCO2kgs: comparisonRow.selfAnnualCO2kgs,
selfAnnualCostUSD: comparisonRow.selfAnnualCostUSD,
totalAnnualCO2kgs: comparisonRow.totalAnnualCO2kgs,
totalAnnualCostUSD: comparisonRow.totalAnnualCostUSD,
};
return topNFunctions.TopN.filter((topN) => topN.CountExclusive >= 0)
.filter((topN) => {
if (functionNameSearchQuery) {
const functionName = getCalleeFunction(topN.Frame);
return functionName.toLowerCase().includes(functionNameSearchQuery.toLowerCase());
}
}
return true;
})
.map((topN, i) => {
const comparisonRow = comparisonDataById?.[topN.Id];
return {
id: topN.Id,
rank: topN.Rank,
frame: topN.Frame,
samples: scaledSelfCPU,
selfCPUPerc,
totalCPUPerc,
selfCPU: topN.CountExclusive,
totalCPU: topN.CountInclusive,
impactEstimates,
selfAnnualCO2kgs: topN.selfAnnualCO2kgs,
selfAnnualCostUSD: topN.selfAnnualCostUSD,
totalAnnualCO2kgs: topN.totalAnnualCO2kgs,
totalAnnualCostUSD: topN.totalAnnualCostUSD,
subGroups: topN.subGroups,
diff: calculateDiff(),
};
});
const scaledSelfCPU = scaleValue({
value: topN.CountExclusive,
scaleFactor: baselineScaleFactor,
});
const totalCPUPerc = (topN.CountInclusive / topNFunctions.selfCPU) * 100;
const selfCPUPerc = (topN.CountExclusive / topNFunctions.selfCPU) * 100;
const impactEstimates =
totalSeconds > 0
? calculateImpactEstimates({
countExclusive: topN.CountExclusive,
countInclusive: topN.CountInclusive,
totalSamples: topNFunctions.selfCPU,
totalSeconds,
})
: undefined;
function calculateDiff() {
if (comparisonTopNFunctions && comparisonRow) {
const comparisonScaledSelfCPU = scaleValue({
value: comparisonRow.CountExclusive,
scaleFactor: comparisonScaleFactor,
});
const scaledDiffSamples = scaledSelfCPU - comparisonScaledSelfCPU;
return {
id: comparisonRow.Id,
rank: topN.Rank - comparisonRow.Rank,
samples: scaledDiffSamples,
selfCPU: comparisonRow.CountExclusive,
totalCPU: comparisonRow.CountInclusive,
selfCPUPerc:
selfCPUPerc - (comparisonRow.CountExclusive / comparisonTopNFunctions.selfCPU) * 100,
totalCPUPerc:
totalCPUPerc - (comparisonRow.CountInclusive / comparisonTopNFunctions.selfCPU) * 100,
selfAnnualCO2kgs: comparisonRow.selfAnnualCO2kgs,
selfAnnualCostUSD: comparisonRow.selfAnnualCostUSD,
totalAnnualCO2kgs: comparisonRow.totalAnnualCO2kgs,
totalAnnualCostUSD: comparisonRow.totalAnnualCostUSD,
};
}
}
return {
id: topN.Id,
rank: topN.Rank,
frame: topN.Frame,
samples: scaledSelfCPU,
selfCPUPerc,
totalCPUPerc,
selfCPU: topN.CountExclusive,
totalCPU: topN.CountInclusive,
impactEstimates,
selfAnnualCO2kgs: topN.selfAnnualCO2kgs,
selfAnnualCostUSD: topN.selfAnnualCostUSD,
totalAnnualCO2kgs: topN.totalAnnualCO2kgs,
totalAnnualCostUSD: topN.totalAnnualCostUSD,
subGroups: topN.subGroups,
diff: calculateDiff(),
};
});
}
export function calculateBaseComparisonDiff({

View file

@ -21,6 +21,7 @@ export function EmbeddableFunctionsGrid({ data, totalSeconds, showFullScreenSele
const [sortField, setSortField] = useState(TopNFunctionSortField.Rank);
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');
const [pageIndex, setPageIndex] = useState(0);
const [searchFunctionName, setSearchFunctionName] = useState('');
return (
<TopNFunctionsGrid
@ -37,6 +38,8 @@ export function EmbeddableFunctionsGrid({ data, totalSeconds, showFullScreenSele
}}
isEmbedded
showFullScreenSelector={showFullScreenSelector}
onSearchFunctionNameChange={setSearchFunctionName}
searchFunctionName={searchFunctionName}
/>
);
}

View file

@ -240,7 +240,7 @@ const routes = {
</RouteBreadcrumb>
),
params: t.type({
query: t.partial({ pageIndex: toNumberRt }),
query: t.partial({ pageIndex: toNumberRt, searchFunctionName: t.string }),
}),
},
'/functions/differential': {
@ -271,6 +271,7 @@ const routes = {
baseline: toNumberRt,
comparison: toNumberRt,
pageIndex: toNumberRt,
searchFunctionName: t.string,
}),
]),
}),

View file

@ -40,6 +40,7 @@ export function DifferentialTopNFunctionsView() {
pageIndex = 0,
comparisonSortDirection,
comparisonSortField,
searchFunctionName = '',
} = query;
const timeRange = useTimeRange({ rangeFrom, rangeTo });
@ -128,10 +129,10 @@ export function DifferentialTopNFunctionsView() {
const isNormalizedByTime = normalizationMode === NormalizationMode.Time;
function handleOnFrameClick(functionName: string) {
function handleOnFrameClick(value: string) {
profilingRouter.push('/flamegraphs/flamegraph', {
path: {},
query: { ...query, searchText: functionName },
query: { ...query, searchText: value },
});
}
@ -149,6 +150,13 @@ export function DifferentialTopNFunctionsView() {
});
}
function handleSearchFunctionNameChange(value: string) {
profilingRouter.push('/functions/differential', {
path: {},
query: { ...query, searchFunctionName: value },
});
}
useEffect(() => {
if (state.status === AsyncStatus.Settled || comparisonState.status === AsyncStatus.Settled) {
onPageReady({
@ -233,6 +241,8 @@ export function DifferentialTopNFunctionsView() {
sortDirection={sortDirection}
sortField={sortField}
totalSeconds={totalSeconds}
searchFunctionName={searchFunctionName}
onSearchFunctionNameChange={handleSearchFunctionNameChange}
/>
</AsyncComponent>
</EuiFlexItem>

View file

@ -21,7 +21,15 @@ import { AsyncStatus } from '../../../hooks/use_async';
export function TopNFunctionsView() {
const { onPageReady } = usePerformanceContext();
const { query } = useProfilingParams('/functions/topn');
const { rangeFrom, rangeTo, kuery, sortDirection, sortField, pageIndex = 0 } = query;
const {
rangeFrom,
rangeTo,
kuery,
sortDirection,
sortField,
pageIndex = 0,
searchFunctionName = '',
} = query;
const timeRange = useTimeRange({ rangeFrom, rangeTo });
@ -45,10 +53,10 @@ export function TopNFunctionsView() {
const profilingRouter = useProfilingRouter();
function handleOnFrameClick(functionName: string) {
function handleOnFrameClick(value: string) {
profilingRouter.push('/flamegraphs/flamegraph', {
path: {},
query: { ...query, searchText: functionName },
query: { ...query, searchText: value },
});
}
@ -69,6 +77,14 @@ export function TopNFunctionsView() {
},
});
}
function handleSearchFunctionNameChange(value: string) {
profilingRouter.push('/functions/topn', {
path: {},
query: { ...query, searchFunctionName: value },
});
}
useEffect(() => {
if (state.status === AsyncStatus.Settled) {
onPageReady({
@ -83,6 +99,7 @@ export function TopNFunctionsView() {
});
}
}, [state.status, state.data, onPageReady, rangeFrom, rangeTo]);
return (
<>
<EuiFlexGroup direction="column">
@ -100,6 +117,8 @@ export function TopNFunctionsView() {
sortField={sortField}
sortDirection={sortDirection}
onChangeSort={handleSortChange}
searchFunctionName={searchFunctionName}
onSearchFunctionNameChange={handleSearchFunctionNameChange}
/>
</AsyncComponent>
</EuiFlexItem>