Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,82 @@ describe('Functions page', () => {
const firstRowSelector = '[data-grid-row-index="0"] [data-test-subj="dataGridRowCell"]';
cy.get(firstRowSelector).eq(2).contains('libjvm.so');
});

it('Sorting grid', () => {
cy.intercept('GET', '/internal/profiling/topn/functions?*').as('getTopNFunctions');
cy.visitKibana('/app/profiling/functions', { rangeFrom, rangeTo });
cy.wait('@getTopNFunctions');
[
{
columnKey: 'rank',
columnIndex: 1,
highRank: 388,
lowRank: 1,
highValue: 388,
lowValue: 1,
},
{
columnKey: 'samples',
columnIndex: 7,
highRank: 1,
lowRank: 44,
highValue: 28,
lowValue: 1,
},
{
columnKey: 'selfCPU',
columnIndex: 3,
highRank: 1,
lowRank: 44,
highValue: '5.46%',
lowValue: '0.19%',
},
{
columnKey: 'totalCPU',
columnIndex: 4,
highRank: 338,
lowRank: 44,
highValue: '10.33%',
lowValue: '0.19%',
},
{
columnKey: 'annualizedCo2',
columnIndex: 5,
highRank: 1,
lowRank: 44,
highValue: '1.84 lbs / 0.84 kg',
lowValue: '0.07 lbs / 0.03 kg',
},
{
columnKey: 'annualizedDollarCost',
columnIndex: 6,
highRank: 1,
lowRank: 44,
highValue: '$17.37',
lowValue: '$0.62',
},
].forEach(({ columnKey, columnIndex, highRank, highValue, lowRank, lowValue }) => {
cy.get(`[data-test-subj="dataGridHeaderCell-${columnKey}"]`).click();
cy.contains('Sort High-Low').click();
const firstRowSelector = '[data-grid-row-index="0"] [data-test-subj="dataGridRowCell"]';
cy.get(firstRowSelector).eq(1).contains(highRank);
cy.get(firstRowSelector).eq(columnIndex).contains(highValue);

cy.get(`[data-test-subj="dataGridHeaderCell-${columnKey}"]`).click();
cy.contains('Sort Low-High').click();
cy.get(firstRowSelector).eq(1).contains(lowRank);
cy.get(firstRowSelector).eq(columnIndex).contains(lowValue);
});

cy.get(`[data-test-subj="dataGridHeaderCell-frame"]`).click();
cy.contains('Sort Z-A').click();
const firstRowSelector = '[data-grid-row-index="0"] [data-test-subj="dataGridRowCell"]';
cy.get(firstRowSelector).eq(1).contains('1');
cy.get(firstRowSelector).eq(2).contains('vmlinux');

cy.get('[data-test-subj="dataGridHeaderCell-frame"]').click();
cy.contains('Sort A-Z').click();
cy.get(firstRowSelector).eq(1).contains('371');
cy.get(firstRowSelector).eq(2).contains('/');
});
});
73 changes: 41 additions & 32 deletions x-pack/plugins/profiling/public/components/topn_functions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import {
EuiScreenReaderOnly,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { last } from 'lodash';
import { useUiTracker } from '@kbn/observability-shared-plugin/public';
import { getCalleeFunction, TopNFunctions, TopNFunctionSortField } from '@kbn/profiling-utils';
import { last, orderBy } from 'lodash';
import React, { forwardRef, Ref, useMemo, useState } from 'react';
import { GridOnScrollProps } from 'react-window';
import { useUiTracker } from '@kbn/observability-shared-plugin/public';
import { TopNFunctions, TopNFunctionSortField } from '@kbn/profiling-utils';
import { CPULabelWithHint } from '../cpu_label_with_hint';
import { FrameInformationTooltip } from '../frame_information_window/frame_information_tooltip';
import { LabelWithHint } from '../label_with_hint';
Expand Down Expand Up @@ -102,25 +102,50 @@ export const TopNFunctionsGrid = forwardRef(
totalSeconds,
]);

const sortedRows = useMemo(() => {
switch (sortField) {
case TopNFunctionSortField.Frame:
return orderBy(rows, (row) => getCalleeFunction(row.frame), sortDirection);
case TopNFunctionSortField.SelfCPU:
return orderBy(rows, (row) => row.selfCPUPerc, sortDirection);
case TopNFunctionSortField.TotalCPU:
return orderBy(rows, (row) => row.totalCPUPerc, sortDirection);
case TopNFunctionSortField.AnnualizedCo2:
return orderBy(rows, (row) => row.impactEstimates?.selfCPU.annualizedCo2, sortDirection);
case TopNFunctionSortField.AnnualizedDollarCost:
return orderBy(
rows,
(row) => row.impactEstimates?.selfCPU.annualizedDollarCost,
sortDirection
);
default:
return orderBy(rows, sortField, sortDirection);
}
}, [rows, sortDirection, sortField]);

const { columns, leadingControlColumns } = useMemo(() => {
const gridColumns: EuiDataGridColumn[] = [
{
id: TopNFunctionSortField.Rank,
schema: 'numeric',
actions: { showHide: false },
initialWidth: isDifferentialView ? 50 : 90,
displayAsText: i18n.translate('xpack.profiling.functionsView.rankColumnLabel', {
defaultMessage: 'Rank',
}),
},
{
id: TopNFunctionSortField.Frame,
actions: { showHide: false },
displayAsText: i18n.translate('xpack.profiling.functionsView.functionColumnLabel', {
defaultMessage: 'Function',
}),
},
{
id: TopNFunctionSortField.Samples,
initialWidth: isDifferentialView ? 100 : 200,
schema: 'samples',
schema: 'numeric',
actions: { showHide: false },
display: (
<LabelWithHint
label={i18n.translate('xpack.profiling.functionsView.samplesColumnLabel', {
Expand All @@ -137,6 +162,8 @@ export const TopNFunctionsGrid = forwardRef(
},
{
id: TopNFunctionSortField.SelfCPU,
schema: 'numeric',
actions: { showHide: false },
initialWidth: isDifferentialView ? 100 : 200,
display: (
<CPULabelWithHint
Expand All @@ -149,6 +176,8 @@ export const TopNFunctionsGrid = forwardRef(
},
{
id: TopNFunctionSortField.TotalCPU,
schema: 'numeric',
actions: { showHide: false },
initialWidth: isDifferentialView ? 100 : 200,
display: (
<CPULabelWithHint
Expand All @@ -166,6 +195,7 @@ export const TopNFunctionsGrid = forwardRef(
gridColumns.push({
initialWidth: 60,
id: TopNFunctionSortField.Diff,
actions: { showHide: false },
displayAsText: i18n.translate('xpack.profiling.functionsView.diffColumnLabel', {
defaultMessage: 'Diff',
}),
Expand All @@ -176,6 +206,7 @@ export const TopNFunctionsGrid = forwardRef(
gridColumns.push(
{
id: TopNFunctionSortField.AnnualizedCo2,
actions: { showHide: false },
initialWidth: isDifferentialView ? 100 : 200,
schema: 'numeric',
display: (
Expand All @@ -195,6 +226,8 @@ export const TopNFunctionsGrid = forwardRef(
},
{
id: TopNFunctionSortField.AnnualizedDollarCost,
schema: 'numeric',
actions: { showHide: false },
initialWidth: isDifferentialView ? 100 : 200,
display: (
<LabelWithHint
Expand Down Expand Up @@ -225,7 +258,7 @@ export const TopNFunctionsGrid = forwardRef(
rowCellRender: function RowCellRender({ rowIndex }) {
function handleOnClick() {
trackProfilingEvent({ metric: 'topN_function_details_click' });
setSelectedRow(rows[rowIndex]);
setSelectedRow(sortedRows[rowIndex]);
}
return (
<EuiButtonIcon
Expand All @@ -240,7 +273,7 @@ export const TopNFunctionsGrid = forwardRef(
});
}
return { columns: gridColumns, leadingControlColumns: gridLeadingControlColumns };
}, [isDifferentialView, rows, showDiffColumn, trackProfilingEvent]);
}, [isDifferentialView, sortedRows, showDiffColumn, trackProfilingEvent]);

const [visibleColumns, setVisibleColumns] = useState(columns.map(({ id }) => id));

Expand All @@ -249,7 +282,7 @@ export const TopNFunctionsGrid = forwardRef(
columnId,
setCellProps,
}: EuiDataGridCellValueElementProps) {
const data = rows[rowIndex];
const data = sortedRows[rowIndex];
if (data) {
return (
<FunctionRow
Expand All @@ -272,9 +305,8 @@ export const TopNFunctionsGrid = forwardRef(
aria-label="TopN functions"
columns={columns}
columnVisibility={{ visibleColumns, setVisibleColumns }}
rowCount={rows.length}
rowCount={sortedRows.length > 100 ? 100 : sortedRows.length}
renderCellValue={RenderCellValue}
inMemory={{ level: 'sorting' }}
sorting={{ columns: [{ id: sortField, direction: sortDirection }], onSort }}
leadingControlColumns={leadingControlColumns}
pagination={{
Expand All @@ -296,29 +328,6 @@ export const TopNFunctionsGrid = forwardRef(
virtualizationOptions={{
onScroll,
}}
schemaDetectors={[
{
type: 'samples',
comparator: (a, b, direction) => {
const aNumber = parseFloat(a.replace(/,/g, ''));
const bNumber = parseFloat(b.replace(/,/g, ''));

if (aNumber < bNumber) {
return direction === 'desc' ? 1 : -1;
}
if (aNumber > bNumber) {
return direction === 'desc' ? -1 : 1;
}
return 0;
},
detector: (a) => {
return 1;
},
icon: '',
sortTextAsc: 'Low-High',
sortTextDesc: 'High-Low',
},
]}
/>
{selectedRow && (
<FrameInformationTooltip
Expand Down
111 changes: 54 additions & 57 deletions x-pack/plugins/profiling/public/components/topn_functions/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,65 +70,62 @@ export function getFunctionsRows({
? keyBy(comparisonTopNFunctions.TopN, 'Id')
: {};

return topNFunctions.TopN.filter((topN) => topN.CountExclusive > 0)
.slice(0, 100)
.map((topN, i) => {
const comparisonRow = comparisonDataById?.[topN.Id];

const scaledSelfCPU = scaleValue({
value: topN.CountExclusive,
scaleFactor: baselineScaleFactor,
});

const totalCPUPerc = (topN.CountInclusive / topNFunctions.TotalCount) * 100;
const selfCPUPerc = (topN.CountExclusive / topNFunctions.TotalCount) * 100;

const impactEstimates =
totalSeconds > 0
? calculateImpactEstimates({
countExclusive: topN.CountExclusive,
countInclusive: topN.CountInclusive,
totalSamples: topNFunctions.TotalCount,
totalSeconds,
})
: undefined;

function calculateDiff() {
if (comparisonTopNFunctions && comparisonRow) {
const comparisonScaledSelfCPU = scaleValue({
value: comparisonRow.CountExclusive,
scaleFactor: comparisonScaleFactor,
});

const scaledDiffSamples = scaledSelfCPU - comparisonScaledSelfCPU;

return {
rank: topN.Rank - comparisonRow.Rank,
samples: scaledDiffSamples,
selfCPU: comparisonRow.CountExclusive,
totalCPU: comparisonRow.CountInclusive,
selfCPUPerc:
selfCPUPerc -
(comparisonRow.CountExclusive / comparisonTopNFunctions.TotalCount) * 100,
totalCPUPerc:
totalCPUPerc -
(comparisonRow.CountInclusive / comparisonTopNFunctions.TotalCount) * 100,
};
}
}
return topNFunctions.TopN.filter((topN) => topN.CountExclusive > 0).map((topN, i) => {
const comparisonRow = comparisonDataById?.[topN.Id];

return {
rank: topN.Rank,
frame: topN.Frame,
samples: scaledSelfCPU,
selfCPUPerc,
totalCPUPerc,
selfCPU: topN.CountExclusive,
totalCPU: topN.CountInclusive,
impactEstimates,
diff: calculateDiff(),
};
const scaledSelfCPU = scaleValue({
value: topN.CountExclusive,
scaleFactor: baselineScaleFactor,
});

const totalCPUPerc = (topN.CountInclusive / topNFunctions.TotalCount) * 100;
const selfCPUPerc = (topN.CountExclusive / topNFunctions.TotalCount) * 100;

const impactEstimates =
totalSeconds > 0
? calculateImpactEstimates({
countExclusive: topN.CountExclusive,
countInclusive: topN.CountInclusive,
totalSamples: topNFunctions.TotalCount,
totalSeconds,
})
: undefined;

function calculateDiff() {
if (comparisonTopNFunctions && comparisonRow) {
const comparisonScaledSelfCPU = scaleValue({
value: comparisonRow.CountExclusive,
scaleFactor: comparisonScaleFactor,
});

const scaledDiffSamples = scaledSelfCPU - comparisonScaledSelfCPU;

return {
rank: topN.Rank - comparisonRow.Rank,
samples: scaledDiffSamples,
selfCPU: comparisonRow.CountExclusive,
totalCPU: comparisonRow.CountInclusive,
selfCPUPerc:
selfCPUPerc - (comparisonRow.CountExclusive / comparisonTopNFunctions.TotalCount) * 100,
totalCPUPerc:
totalCPUPerc -
(comparisonRow.CountInclusive / comparisonTopNFunctions.TotalCount) * 100,
};
}
}

return {
rank: topN.Rank,
frame: topN.Frame,
samples: scaledSelfCPU,
selfCPUPerc,
totalCPUPerc,
selfCPU: topN.CountExclusive,
totalCPU: topN.CountInclusive,
impactEstimates,
diff: calculateDiff(),
};
});
}

export function calculateBaseComparisonDiff({
Expand Down
6 changes: 5 additions & 1 deletion x-pack/plugins/profiling/scripts/test/e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ function runTests() {

return childProcess.spawnSync('node', spawnArgs, {
cwd: e2eDir,
env: { ...process.env, CYPRESS_CLI_ARGS: JSON.stringify(cypressCliArgs) },
env: {
...process.env,
CYPRESS_CLI_ARGS: JSON.stringify(cypressCliArgs),
NODE_OPTIONS: '--openssl-legacy-provider',
},
encoding: 'utf8',
stdio: 'inherit',
});
Expand Down