mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Profiling] Fix calculation/formatting of frame info values (#141909)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Joseph Crail <joseph.crail@elastic.co>
This commit is contained in:
parent
e1aa6a6d24
commit
e0029ed191
10 changed files with 123 additions and 44 deletions
|
@ -25,11 +25,10 @@ interface Props {
|
|||
exeFileName: string;
|
||||
functionName: string;
|
||||
sourceFileName: string;
|
||||
samples: number;
|
||||
childSamples: number;
|
||||
countInclusive: number;
|
||||
countExclusive: number;
|
||||
};
|
||||
sampledTraces: number;
|
||||
totalTraces: number;
|
||||
totalSamples: number;
|
||||
totalSeconds: number;
|
||||
onClose: () => void;
|
||||
status: AsyncStatus;
|
||||
|
@ -105,8 +104,7 @@ function FlamegraphFrameInformationPanel({
|
|||
export function FlamegraphInformationWindow({
|
||||
onClose,
|
||||
frame,
|
||||
sampledTraces,
|
||||
totalTraces,
|
||||
totalSamples,
|
||||
totalSeconds,
|
||||
status,
|
||||
}: Props) {
|
||||
|
@ -122,14 +120,13 @@ export function FlamegraphInformationWindow({
|
|||
);
|
||||
}
|
||||
|
||||
const { childSamples, exeFileName, samples, functionName, sourceFileName } = frame;
|
||||
const { exeFileName, functionName, sourceFileName, countInclusive, countExclusive } = frame;
|
||||
|
||||
const impactRows = getImpactRows({
|
||||
samples,
|
||||
childSamples,
|
||||
sampledTraces,
|
||||
countInclusive,
|
||||
countExclusive,
|
||||
totalSamples,
|
||||
totalSeconds,
|
||||
totalTraces,
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { asCost } from '../../utils/formatters/as_cost';
|
||||
import { asDuration } from '../../utils/formatters/as_duration';
|
||||
import { asNumber } from '../../utils/formatters/as_number';
|
||||
import { asPercentage } from '../../utils/formatters/as_percentage';
|
||||
import { asWeight } from '../../utils/formatters/as_weight';
|
||||
|
||||
|
@ -23,21 +24,19 @@ const CO2_PER_KWH = 0.92;
|
|||
const CORE_COST_PER_HOUR = 0.0425;
|
||||
|
||||
export function getImpactRows({
|
||||
samples,
|
||||
childSamples,
|
||||
sampledTraces,
|
||||
totalTraces,
|
||||
countInclusive,
|
||||
countExclusive,
|
||||
totalSamples,
|
||||
totalSeconds,
|
||||
}: {
|
||||
samples: number;
|
||||
childSamples: number;
|
||||
sampledTraces: number;
|
||||
totalTraces: number;
|
||||
countInclusive: number;
|
||||
countExclusive: number;
|
||||
totalSamples: number;
|
||||
totalSeconds: number;
|
||||
}) {
|
||||
const percentage = samples / sampledTraces;
|
||||
const percentageNoChildren = (samples - childSamples) / sampledTraces;
|
||||
const totalCoreSeconds = totalTraces / 20;
|
||||
const percentage = countInclusive / totalSamples;
|
||||
const percentageNoChildren = countExclusive / totalSamples;
|
||||
const totalCoreSeconds = totalSamples / 20;
|
||||
const coreSeconds = totalCoreSeconds * percentage;
|
||||
const coreSecondsNoChildren = totalCoreSeconds * percentageNoChildren;
|
||||
const coreHours = coreSeconds / (60 * 60);
|
||||
|
@ -70,10 +69,16 @@ export function getImpactRows({
|
|||
value: asPercentage(percentageNoChildren),
|
||||
},
|
||||
{
|
||||
label: i18n.translate('xpack.profiling.flameGraphInformationWindow.samplesLabel', {
|
||||
label: i18n.translate('xpack.profiling.flameGraphInformationWindow.samplesInclusiveLabel', {
|
||||
defaultMessage: 'Samples',
|
||||
}),
|
||||
value: samples,
|
||||
value: asNumber(countInclusive),
|
||||
},
|
||||
{
|
||||
label: i18n.translate('xpack.profiling.flameGraphInformationWindow.samplesExclusiveLabel', {
|
||||
defaultMessage: 'Samples (excl. children)',
|
||||
}),
|
||||
value: asNumber(countExclusive),
|
||||
},
|
||||
{
|
||||
label: i18n.translate(
|
||||
|
|
|
@ -31,11 +31,9 @@ function TooltipRow({
|
|||
formatAsPercentage: boolean;
|
||||
showChange: boolean;
|
||||
}) {
|
||||
const valueLabel = formatAsPercentage ? asPercentage(value, 2) : value.toString();
|
||||
const valueLabel = formatAsPercentage ? asPercentage(value) : value.toString();
|
||||
const comparisonLabel =
|
||||
formatAsPercentage && isNumber(comparison)
|
||||
? asPercentage(comparison, 2)
|
||||
: comparison?.toString();
|
||||
formatAsPercentage && isNumber(comparison) ? asPercentage(comparison) : comparison?.toString();
|
||||
|
||||
const diff = showChange && isNumber(comparison) ? comparison - value : undefined;
|
||||
|
||||
|
@ -46,7 +44,7 @@ function TooltipRow({
|
|||
defaultMessage: 'no change',
|
||||
});
|
||||
} else if (formatAsPercentage && diff !== undefined) {
|
||||
diffLabel = asPercentage(diff, 2);
|
||||
diffLabel = asPercentage(diff);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -226,10 +224,8 @@ export const FlameGraph: React.FC<FlameGraphProps> = ({
|
|||
exeFileName: highlightedFrame.ExeFileName,
|
||||
sourceFileName: highlightedFrame.SourceFilename,
|
||||
functionName: highlightedFrame.FunctionName,
|
||||
samples: primaryFlamegraph.Samples[highlightedVmIndex],
|
||||
childSamples:
|
||||
primaryFlamegraph.Samples[highlightedVmIndex] -
|
||||
primaryFlamegraph.CountExclusive[highlightedVmIndex],
|
||||
countInclusive: primaryFlamegraph.Samples[highlightedVmIndex],
|
||||
countExclusive: primaryFlamegraph.CountExclusive[highlightedVmIndex],
|
||||
}
|
||||
: undefined;
|
||||
|
||||
|
@ -315,8 +311,7 @@ export const FlameGraph: React.FC<FlameGraphProps> = ({
|
|||
frame={selected}
|
||||
status={highlightedFrameStatus}
|
||||
totalSeconds={primaryFlamegraph?.TotalSeconds ?? 0}
|
||||
totalTraces={primaryFlamegraph?.TotalTraces ?? 0}
|
||||
sampledTraces={primaryFlamegraph?.SampledTraces ?? 0}
|
||||
totalSamples={totalSamples}
|
||||
onClose={() => {
|
||||
setShowInformationWindow(false);
|
||||
}}
|
||||
|
|
|
@ -194,7 +194,7 @@ export const SubChart: React.FC<SubChartProps> = ({
|
|||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">{asPercentage(percentage / 100, 2)}</EuiText>
|
||||
<EuiText size="s">{asPercentage(percentage / 100)}</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export function asCost(value: number, precision: number = 2, unit: string = '$') {
|
||||
return `${value.toPrecision(precision)}${unit}`;
|
||||
import { asNumber } from './as_number';
|
||||
|
||||
export function asCost(value: number, unit: string = '$') {
|
||||
return `${asNumber(value)}${unit}`;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,25 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import moment from 'moment';
|
||||
|
||||
moment.relativeTimeRounding((t) => {
|
||||
const DIGITS = 2; // like: 2.56 minutes
|
||||
return Math.round(t * Math.pow(10, DIGITS)) / Math.pow(10, DIGITS);
|
||||
});
|
||||
moment.relativeTimeThreshold('y', 365);
|
||||
moment.relativeTimeThreshold('M', 12);
|
||||
moment.relativeTimeThreshold('w', 4);
|
||||
moment.relativeTimeThreshold('d', 31);
|
||||
moment.relativeTimeThreshold('h', 24);
|
||||
moment.relativeTimeThreshold('m', 60);
|
||||
moment.relativeTimeThreshold('s', 60);
|
||||
moment.relativeTimeThreshold('ss', 0);
|
||||
|
||||
export function asDuration(valueInSeconds: number) {
|
||||
if (valueInSeconds === 0) {
|
||||
return i18n.translate('xpack.profiling.zeroSeconds', { defaultMessage: '0 seconds' });
|
||||
}
|
||||
return moment.duration(valueInSeconds * 1000).humanize();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 { asNumber } from './as_number';
|
||||
|
||||
describe('asNumber', () => {
|
||||
it('rounds numbers appropriately', () => {
|
||||
expect(asNumber(999)).toBe('999');
|
||||
|
||||
expect(asNumber(1.11)).toBe('1.11');
|
||||
|
||||
expect(asNumber(0.001)).toBe('~0.00');
|
||||
|
||||
expect(asNumber(0)).toBe('0');
|
||||
});
|
||||
|
||||
it('adds k/m/b where needed', () => {
|
||||
expect(asNumber(999.999)).toBe('1k');
|
||||
|
||||
expect(asNumber(4.5e5)).toBe('450k');
|
||||
|
||||
expect(asNumber(4.5001e5)).toBe('450.01k');
|
||||
|
||||
expect(asNumber(2.4991e7)).toBe('24.99m');
|
||||
|
||||
expect(asNumber(9e9)).toBe('9b');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export function asNumber(value: number): string {
|
||||
if (value === 0) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
value = Math.round(value * 100) / 100;
|
||||
if (value < 0.01) {
|
||||
return '~0.00';
|
||||
}
|
||||
if (value < 1e3) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
if (value < 1e6) {
|
||||
return `${asNumber(value / 1e3)}k`;
|
||||
}
|
||||
|
||||
if (value < 1e9) {
|
||||
return `${asNumber(value / 1e6)}m`;
|
||||
}
|
||||
|
||||
return `${asNumber(value / 1e9)}b`;
|
||||
}
|
|
@ -5,6 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export function asPercentage(value: number, precision: number = 0) {
|
||||
return `${Number(value * 100).toFixed(precision)}%`;
|
||||
import { asNumber } from './as_number';
|
||||
|
||||
export function asPercentage(value: number) {
|
||||
return `${asNumber(value * 100)}%`;
|
||||
}
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { asNumber } from './as_number';
|
||||
|
||||
const ONE_POUND_TO_A_KILO = 0.45359237;
|
||||
|
||||
export function asWeight(valueInPounds: number, precision: number = 2) {
|
||||
const lbs = valueInPounds.toPrecision(precision);
|
||||
const kgs = Number(valueInPounds * ONE_POUND_TO_A_KILO).toPrecision(precision);
|
||||
export function asWeight(valueInPounds: number) {
|
||||
const lbs = asNumber(valueInPounds);
|
||||
const kgs = asNumber(Number(valueInPounds * ONE_POUND_TO_A_KILO));
|
||||
|
||||
return i18n.translate('xpack.profiling.formatters.weight', {
|
||||
defaultMessage: `{lbs} lbs / {kgs} kg`,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue