From 261febcf310552c2280a369f6f740c85bae7d85a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Thu, 28 Sep 2023 12:43:02 +0100 Subject: [PATCH] [Profiling] fixing TopN functions sorting (#167242) ![sorting](https://github.com/elastic/kibana/assets/55978943/f5ad2b2c-0dff-4517-ac0f-88c9d99d6b2e) --- .../e2e/profiling_views/functions.cy.ts | 78 ++++++++++++ .../components/topn_functions/index.tsx | 73 +++++++----- .../public/components/topn_functions/utils.ts | 111 +++++++++--------- x-pack/plugins/profiling/scripts/test/e2e.js | 6 +- 4 files changed, 178 insertions(+), 90 deletions(-) diff --git a/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/functions.cy.ts b/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/functions.cy.ts index 7ad922ceb7936..f928db33e2f19 100644 --- a/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/functions.cy.ts +++ b/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/functions.cy.ts @@ -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('/'); + }); }); diff --git a/x-pack/plugins/profiling/public/components/topn_functions/index.tsx b/x-pack/plugins/profiling/public/components/topn_functions/index.tsx index 7dbc6c38fb0eb..e1318471913d9 100644 --- a/x-pack/plugins/profiling/public/components/topn_functions/index.tsx +++ b/x-pack/plugins/profiling/public/components/topn_functions/index.tsx @@ -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'; @@ -102,10 +102,33 @@ 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', @@ -113,6 +136,7 @@ export const TopNFunctionsGrid = forwardRef( }, { id: TopNFunctionSortField.Frame, + actions: { showHide: false }, displayAsText: i18n.translate('xpack.profiling.functionsView.functionColumnLabel', { defaultMessage: 'Function', }), @@ -120,7 +144,8 @@ export const TopNFunctionsGrid = forwardRef( { id: TopNFunctionSortField.Samples, initialWidth: isDifferentialView ? 100 : 200, - schema: 'samples', + schema: 'numeric', + actions: { showHide: false }, display: ( id)); @@ -249,7 +282,7 @@ export const TopNFunctionsGrid = forwardRef( columnId, setCellProps, }: EuiDataGridCellValueElementProps) { - const data = rows[rowIndex]; + const data = sortedRows[rowIndex]; if (data) { return ( 100 ? 100 : sortedRows.length} renderCellValue={RenderCellValue} - inMemory={{ level: 'sorting' }} sorting={{ columns: [{ id: sortField, direction: sortDirection }], onSort }} leadingControlColumns={leadingControlColumns} pagination={{ @@ -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 && ( 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({ diff --git a/x-pack/plugins/profiling/scripts/test/e2e.js b/x-pack/plugins/profiling/scripts/test/e2e.js index d5cd56175d223..8a584a84bdc87 100644 --- a/x-pack/plugins/profiling/scripts/test/e2e.js +++ b/x-pack/plugins/profiling/scripts/test/e2e.js @@ -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', });