Skip to content

Commit

Permalink
[Profiling] Adding normalize by time and scale factor on Diff TopN fu…
Browse files Browse the repository at this point in the history
…nctions page (#159394)

Relevant changes:
- Adding **Normalize by** field on the Diff TopN functions page.
- Refactoring functions route/pages.


<img width="1469" alt="Screenshot 2023-06-09 at 2 34 48 PM"
src="https://github.com/elastic/kibana/assets/55978943/b2c824bd-94ff-4f8b-a6b2-b9c30c51881e">


<img width="1353" alt="Screenshot 2023-06-09 at 2 34 58 PM"
src="https://github.com/elastic/kibana/assets/55978943/9d1de932-8669-4ddf-968a-057199211931">

---------

Co-authored-by: kibanamachine <[email protected]>
cauemarcondes and kibanamachine authored Jun 14, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent b85d92c commit c6bca36
Showing 16 changed files with 524 additions and 364 deletions.
10 changes: 0 additions & 10 deletions x-pack/plugins/profiling/common/flamegraph.ts
Original file line number Diff line number Diff line change
@@ -10,16 +10,6 @@ import { createFrameGroupID } from './frame_group';
import { fnv1a64 } from './hash';
import { createStackFrameMetadata, getCalleeLabel } from './profiling';

export enum FlameGraphComparisonMode {
Absolute = 'absolute',
Relative = 'relative',
}

export enum FlameGraphNormalizationMode {
Scale = 'scale',
Time = 'time',
}

export interface BaseFlameGraph {
Size: number;
Edges: number[][];
Original file line number Diff line number Diff line change
@@ -7,11 +7,11 @@
import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { FlameGraphComparisonMode } from '../../../common/flamegraph';
import { ComparisonMode } from '../normalization_menu';

interface Props {
comparisonMode: FlameGraphComparisonMode;
onChange: (nextComparisonMode: FlameGraphComparisonMode) => void;
comparisonMode: ComparisonMode;
onChange: (nextComparisonMode: ComparisonMode) => void;
}
export function DifferentialComparisonMode({ comparisonMode, onChange }: Props) {
return (
@@ -20,45 +20,37 @@ export function DifferentialComparisonMode({ comparisonMode, onChange }: Props)
<EuiFlexItem grow={false}>
<EuiTitle size="xxs">
<h3>
{i18n.translate(
'xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeTitle',
{ defaultMessage: 'Format' }
)}
{i18n.translate('xpack.profiling.differentialComparisonMode.title', {
defaultMessage: 'Format',
})}
</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonGroup
legend={i18n.translate(
'xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeLegend',
{
defaultMessage:
'This switch allows you to switch between an absolute and relative comparison between both graphs',
}
)}
legend={i18n.translate('xpack.profiling.differentialComparisonMode.legend', {
defaultMessage:
'This switch allows you to switch between an absolute and relative comparison between both graphs',
})}
type="single"
buttonSize="s"
idSelected={comparisonMode}
onChange={(nextComparisonMode) => {
onChange(nextComparisonMode as FlameGraphComparisonMode);
onChange(nextComparisonMode as ComparisonMode);
}}
options={[
{
id: FlameGraphComparisonMode.Absolute,
id: ComparisonMode.Absolute,
label: i18n.translate(
'xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeAbsoluteButtonLabel',
{
defaultMessage: 'Abs',
}
'xpack.profiling.differentialComparisonMode.absoluteButtonLabel',
{ defaultMessage: 'Abs' }
),
},
{
id: FlameGraphComparisonMode.Relative,
id: ComparisonMode.Relative,
label: i18n.translate(
'xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeRelativeButtonLabel',
{
defaultMessage: 'Rel',
}
'xpack.profiling.differentialComparisonMode.relativeButtonLabel',
{ defaultMessage: 'Rel' }
),
},
]}
Original file line number Diff line number Diff line change
@@ -9,16 +9,17 @@ import { Chart, Datum, Flame, FlameLayerValue, PartialTheme, Settings } from '@e
import { EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui';
import { Maybe } from '@kbn/observability-plugin/common/typings';
import React, { useEffect, useMemo, useState } from 'react';
import { ElasticFlameGraph, FlameGraphComparisonMode } from '../../../common/flamegraph';
import { ElasticFlameGraph } from '../../../common/flamegraph';
import { getFlamegraphModel } from '../../utils/get_flamegraph_model';
import { FlameGraphLegend } from './flame_graph_legend';
import { FrameInformationWindow } from '../frame_information_window';
import { FrameInformationTooltip } from '../frame_information_window/frame_information_tooltip';
import { FlameGraphTooltip } from './flamegraph_tooltip';
import { ComparisonMode } from '../normalization_menu';

interface Props {
id: string;
comparisonMode: FlameGraphComparisonMode;
comparisonMode: ComparisonMode;
primaryFlamegraph?: ElasticFlameGraph;
comparisonFlamegraph?: ElasticFlameGraph;
baseline?: number;
Original file line number Diff line number Diff line change
@@ -25,35 +25,41 @@ import {
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import React, { useEffect, useState } from 'react';
import { FlameGraphNormalizationMode } from '../../../common/flamegraph';

export interface FlameGraphNormalizationOptions {
export interface NormalizationOptions {
baselineScale: number;
baselineTime: number;
comparisonScale: number;
comparisonTime: number;
}

export enum ComparisonMode {
Absolute = 'absolute',
Relative = 'relative',
}

export enum NormalizationMode {
Scale = 'scale',
Time = 'time',
}

interface Props {
mode: FlameGraphNormalizationMode;
options: FlameGraphNormalizationOptions;
onChange: (mode: FlameGraphNormalizationMode, options: FlameGraphNormalizationOptions) => void;
mode: NormalizationMode;
options: NormalizationOptions;
onChange: (mode: NormalizationMode, options: NormalizationOptions) => void;
}

const SCALE_LABEL = i18n.translate('xpack.profiling.flameGraphNormalizationMenu.scale', {
const SCALE_LABEL = i18n.translate('xpack.profiling.normalizationMenu.scale', {
defaultMessage: 'Scale factor',
});

const TIME_LABEL = i18n.translate('xpack.profiling.flameGraphNormalizationMenu.time', {
const TIME_LABEL = i18n.translate('xpack.profiling.normalizationMenu.time', {
defaultMessage: 'Time',
});

const NORMALIZE_BY_LABEL = i18n.translate(
'xpack.profiling.flameGraphNormalizationMenu.normalizeBy',
{
defaultMessage: 'Normalize by',
}
);
const NORMALIZE_BY_LABEL = i18n.translate('xpack.profiling.normalizationMenu.normalizeBy', {
defaultMessage: 'Normalize by',
});

export function NormalizationMenu(props: Props) {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
@@ -72,7 +78,7 @@ export function NormalizationMenu(props: Props) {
}, [props.mode, props.options]);

const { baseline, comparison } =
mode === FlameGraphNormalizationMode.Time
mode === NormalizationMode.Time
? { comparison: options.comparisonTime, baseline: options.baselineTime }
: { comparison: options.comparisonScale, baseline: options.baselineScale };

@@ -110,7 +116,7 @@ export function NormalizationMenu(props: Props) {
padding: '0 16px',
}}
>
{props.mode === FlameGraphNormalizationMode.Scale ? SCALE_LABEL : TIME_LABEL}
{props.mode === NormalizationMode.Scale ? SCALE_LABEL : TIME_LABEL}
</EuiFlexItem>
</EuiFormControlLayout>
}
@@ -129,24 +135,18 @@ export function NormalizationMenu(props: Props) {
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem>
<span>
{i18n.translate(
'xpack.profiling.flameGraphNormalizationMenu.normalizeByTimeTooltip',
{
defaultMessage:
'Select Normalize by Scale factor and set your Baseline and Comparison scale factors to compare a set of machines of different sizes. For example, you can compare a deployment of 10% of machines to a deployment of 90% of machines.',
}
)}
{i18n.translate('xpack.profiling.normalizationMenu.normalizeByTimeTooltip', {
defaultMessage:
'Select Normalize by Scale factor and set your Baseline and Comparison scale factors to compare a set of machines of different sizes. For example, you can compare a deployment of 10% of machines to a deployment of 90% of machines.',
})}
</span>
</EuiFlexItem>
<EuiFlexItem>
<span>
{i18n.translate(
'xpack.profiling.flameGraphNormalizationMenu.normalizeByScaleTooltip',
{
defaultMessage:
'Select Normalize by Time to compare a set of machines across different time periods. For example, if you compare the last hour to the last 24 hours, the shorter timeframe (1 hour) is multiplied to match the longer timeframe (24 hours).',
}
)}
{i18n.translate('xpack.profiling.normalizationMenu.normalizeByScaleTooltip', {
defaultMessage:
'Select Normalize by Time to compare a set of machines across different time periods. For example, if you compare the last hour to the last 24 hours, the shorter timeframe (1 hour) is multiplied to match the longer timeframe (24 hours).',
})}
</span>
</EuiFlexItem>
</EuiFlexGroup>
@@ -160,19 +160,19 @@ export function NormalizationMenu(props: Props) {
buttonSize="compressed"
isFullWidth
onChange={(id, value) => {
setMode(id as FlameGraphNormalizationMode);
setMode(id as NormalizationMode);
}}
legend={i18n.translate('xpack.profiling.flameGraphNormalizationMode.selectModeLegend', {
legend={i18n.translate('xpack.profiling.normalizationMode.selectModeLegend', {
defaultMessage: 'Select a normalization mode for the flamegraph',
})}
idSelected={mode}
options={[
{
id: FlameGraphNormalizationMode.Scale,
id: NormalizationMode.Scale,
label: SCALE_LABEL,
},
{
id: FlameGraphNormalizationMode.Time,
id: NormalizationMode.Time,
label: TIME_LABEL,
},
]}
@@ -195,14 +195,14 @@ export function NormalizationMenu(props: Props) {
id={baselineScaleFactorInputId}
value={baseline}
onChange={(e) => {
if (mode === FlameGraphNormalizationMode.Scale) {
if (mode === NormalizationMode.Scale) {
setOptions((prevOptions) => ({
...prevOptions,
baselineScale: e.target.valueAsNumber,
}));
}
}}
disabled={mode === FlameGraphNormalizationMode.Time}
disabled={mode === NormalizationMode.Time}
/>
</EuiFormControlLayout>
<EuiSpacer size="m" />
@@ -223,14 +223,14 @@ export function NormalizationMenu(props: Props) {
id={comparisonScaleFactorInputId}
value={comparison}
onChange={(e) => {
if (mode === FlameGraphNormalizationMode.Scale) {
if (mode === NormalizationMode.Scale) {
setOptions((prevOptions) => ({
...prevOptions,
comparisonScale: e.target.valueAsNumber,
}));
}
}}
disabled={mode === FlameGraphNormalizationMode.Time}
disabled={mode === NormalizationMode.Time}
/>
</EuiFormControlLayout>
<EuiSpacer size="m" />
59 changes: 42 additions & 17 deletions x-pack/plugins/profiling/public/components/topn_functions/index.tsx
Original file line number Diff line number Diff line change
@@ -92,7 +92,7 @@ function SampleStat({
diffSamples?: number;
totalSamples: number;
}) {
const samplesLabel = `${samples.toLocaleString()}`;
const samplesLabel = samples.toLocaleString();

if (diffSamples === undefined || diffSamples === 0 || totalSamples === 0) {
return <>{samplesLabel}</>;
@@ -142,6 +142,12 @@ interface Props {
comparisonTopNFunctions?: TopNFunctions;
totalSeconds: number;
isDifferentialView: boolean;
baselineScaleFactor?: number;
comparisonScaleFactor?: number;
}

function scaleValue({ value, scaleFactor = 1 }: { value: number; scaleFactor?: number }) {
return value * scaleFactor;
}

export function TopNFunctionsTable({
@@ -152,6 +158,8 @@ export function TopNFunctionsTable({
comparisonTopNFunctions,
totalSeconds,
isDifferentialView,
baselineScaleFactor,
comparisonScaleFactor,
}: Props) {
const [selectedRow, setSelectedRow] = useState<Row | undefined>();

@@ -175,6 +183,11 @@ export function TopNFunctionsTable({
return topNFunctions.TopN.filter((topN) => topN.CountExclusive > 0).map((topN, i) => {
const comparisonRow = comparisonDataById?.[topN.Id];

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

const inclusiveCPU = (topN.CountInclusive / topNFunctions.TotalCount) * 100;
const exclusiveCPU = (topN.CountExclusive / topNFunctions.TotalCount) * 100;
const totalSamples = topN.CountExclusive;
@@ -189,31 +202,43 @@ export function TopNFunctionsTable({
})
: undefined;

const diff =
comparisonTopNFunctions && comparisonRow
? {
rank: topN.Rank - comparisonRow.Rank,
samples: topN.CountExclusive - comparisonRow.CountExclusive,
exclusiveCPU:
exclusiveCPU -
(comparisonRow.CountExclusive / comparisonTopNFunctions.TotalCount) * 100,
inclusiveCPU:
inclusiveCPU -
(comparisonRow.CountInclusive / comparisonTopNFunctions.TotalCount) * 100,
}
: undefined;
function calculateDiff() {
if (comparisonTopNFunctions && comparisonRow) {
const comparisonCountExclusiveScaled = scaleValue({
value: comparisonRow.CountExclusive,
scaleFactor: comparisonScaleFactor,
});

return {
rank: topN.Rank - comparisonRow.Rank,
samples: topNCountExclusiveScaled - comparisonCountExclusiveScaled,
exclusiveCPU:
exclusiveCPU -
(comparisonRow.CountExclusive / comparisonTopNFunctions.TotalCount) * 100,
inclusiveCPU:
inclusiveCPU -
(comparisonRow.CountInclusive / comparisonTopNFunctions.TotalCount) * 100,
};
}
}

return {
rank: topN.Rank,
frame: topN.Frame,
samples: topN.CountExclusive,
samples: topNCountExclusiveScaled,
exclusiveCPU,
inclusiveCPU,
impactEstimates,
diff,
diff: calculateDiff(),
};
});
}, [topNFunctions, comparisonTopNFunctions, totalSeconds]);
}, [
topNFunctions,
comparisonTopNFunctions,
totalSeconds,
comparisonScaleFactor,
baselineScaleFactor,
]);

const theme = useEuiTheme();

50 changes: 35 additions & 15 deletions x-pack/plugins/profiling/public/routing/index.tsx
Original file line number Diff line number Diff line change
@@ -9,12 +9,14 @@ import { toNumberRt } from '@kbn/io-ts-utils';
import { createRouter, Outlet } from '@kbn/typed-react-router-config';
import * as t from 'io-ts';
import React from 'react';
import { FlameGraphComparisonMode, FlameGraphNormalizationMode } from '../../common/flamegraph';
import { TopNFunctionSortField, topNFunctionSortFieldRt } from '../../common/functions';
import { StackTracesDisplayOption, TopNType } from '../../common/stack_traces';
import { ComparisonMode, NormalizationMode } from '../components/normalization_menu';
import { RedirectTo } from '../components/redirect_to';
import { FlameGraphsView } from '../views/flame_graphs_view';
import { FunctionsView } from '../views/functions_view';
import { FunctionsView } from '../views/functions';
import { DifferentialTopNFunctionsView } from '../views/functions/differential_topn';
import { TopNFunctionsView } from '../views/functions/topn';
import { NoDataView } from '../views/no_data_view';
import { StackTracesView } from '../views/stack_traces_view';
import { RouteBreadcrumb } from './route_breadcrumb';
@@ -117,14 +119,14 @@ const routes = {
comparisonRangeTo: t.string,
comparisonKuery: t.string,
comparisonMode: t.union([
t.literal(FlameGraphComparisonMode.Absolute),
t.literal(FlameGraphComparisonMode.Relative),
t.literal(ComparisonMode.Absolute),
t.literal(ComparisonMode.Relative),
]),
}),
t.partial({
normalizationMode: t.union([
t.literal(FlameGraphNormalizationMode.Scale),
t.literal(FlameGraphNormalizationMode.Time),
t.literal(NormalizationMode.Scale),
t.literal(NormalizationMode.Time),
]),
baseline: toNumberRt,
comparison: toNumberRt,
@@ -133,8 +135,8 @@ const routes = {
}),
defaults: {
query: {
comparisonMode: FlameGraphComparisonMode.Absolute,
normalizationMode: FlameGraphNormalizationMode.Time,
comparisonMode: ComparisonMode.Absolute,
normalizationMode: NormalizationMode.Time,
},
},
},
@@ -174,7 +176,7 @@ const routes = {
})}
href="/functions/topn"
>
<Outlet />
<TopNFunctionsView />
</RouteBreadcrumb>
),
},
@@ -186,16 +188,34 @@ const routes = {
})}
href="/functions/differential"
>
<Outlet />
<DifferentialTopNFunctionsView />
</RouteBreadcrumb>
),
params: t.type({
query: t.type({
comparisonRangeFrom: t.string,
comparisonRangeTo: t.string,
comparisonKuery: t.string,
}),
query: t.intersection([
t.type({
comparisonRangeFrom: t.string,
comparisonRangeTo: t.string,
comparisonKuery: t.string,
normalizationMode: t.union([
t.literal(NormalizationMode.Scale),
t.literal(NormalizationMode.Time),
]),
}),
t.partial({
baseline: toNumberRt,
comparison: toNumberRt,
}),
]),
}),
defaults: {
query: {
comparisonRangeFrom: 'now-15m',
comparisonRangeTo: 'now',
comparisonKuery: '',
normalizationMode: NormalizationMode.Time,
},
},
},
},
},
Original file line number Diff line number Diff line change
@@ -9,9 +9,10 @@ import { i18n } from '@kbn/i18n';
import d3 from 'd3';
import { compact, range, sum, uniqueId } from 'lodash';
import { createColumnarViewModel } from '../../../common/columnar_view_model';
import { ElasticFlameGraph, FlameGraphComparisonMode } from '../../../common/flamegraph';
import { ElasticFlameGraph } from '../../../common/flamegraph';
import { FRAME_TYPE_COLOR_MAP, rgbToRGBA } from '../../../common/frame_type_colors';
import { describeFrameType, FrameType } from '../../../common/profiling';
import { ComparisonMode } from '../../components/normalization_menu';
import { getInterpolationValue } from './get_interpolation_value';

const nullColumnarViewModel = {
@@ -39,7 +40,7 @@ export function getFlamegraphModel({
colorSuccess: string;
colorDanger: string;
colorNeutral: string;
comparisonMode: FlameGraphComparisonMode;
comparisonMode: ComparisonMode;
baseline?: number;
comparison?: number;
}): {
@@ -135,23 +136,20 @@ export function getFlamegraphModel({
const comparisonTotalSamples = sum(comparisonFlamegraph.CountExclusive);

const weightComparisonSide =
comparisonMode === FlameGraphComparisonMode.Relative
? 1
: (comparison ?? 1) / (baseline ?? 1);
comparisonMode === ComparisonMode.Relative ? 1 : (comparison ?? 1) / (baseline ?? 1);

primaryFlamegraph.ID.forEach((nodeID, index) => {
const samples = primaryFlamegraph.CountInclusive[index];
const comparisonSamples = comparisonNodesById[nodeID]?.CountInclusive as number | undefined;

const foreground =
comparisonMode === FlameGraphComparisonMode.Absolute ? samples : samples / totalSamples;
comparisonMode === ComparisonMode.Absolute ? samples : samples / totalSamples;
const background =
comparisonMode === FlameGraphComparisonMode.Absolute
comparisonMode === ComparisonMode.Absolute
? comparisonSamples
: (comparisonSamples ?? 0) / comparisonTotalSamples;

const denominator =
comparisonMode === FlameGraphComparisonMode.Absolute ? totalSamples : foreground;
const denominator = comparisonMode === ComparisonMode.Absolute ? totalSamples : foreground;

const interpolationValue = getInterpolationValue(
foreground,
Original file line number Diff line number Diff line change
@@ -6,20 +6,24 @@
*/
import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiPanel } from '@elastic/eui';
import React from 'react';
import { FlameGraphComparisonMode, FlameGraphNormalizationMode } from '../../../common/flamegraph';
import { useProfilingParams } from '../../hooks/use_profiling_params';
import { useProfilingRouter } from '../../hooks/use_profiling_router';
import { useProfilingRoutePath } from '../../hooks/use_profiling_route_path';
import { PrimaryAndComparisonSearchBar } from '../../components/primary_and_comparison_search_bar';
import { PrimaryProfilingSearchBar } from '../../components/profiling_app_page_template/primary_profiling_search_bar';
import { DifferentialComparisonMode } from './differential_comparison_mode';
import { FlameGraphNormalizationOptions, NormalizationMenu } from './normalization_menu';
import {
ComparisonMode,
NormalizationMode,
NormalizationOptions,
NormalizationMenu,
} from '../../components/normalization_menu';
import { DifferentialComparisonMode } from '../../components/differential_comparison_mode';

interface Props {
isDifferentialView: boolean;
comparisonMode: FlameGraphComparisonMode;
normalizationMode: FlameGraphNormalizationMode;
normalizationOptions: FlameGraphNormalizationOptions;
comparisonMode: ComparisonMode;
normalizationMode: NormalizationMode;
normalizationOptions: NormalizationOptions;
}

export function FlameGraphSearchPanel({
@@ -32,7 +36,7 @@ export function FlameGraphSearchPanel({
const routePath = useProfilingRoutePath();
const profilingRouter = useProfilingRouter();

function onChangeComparisonMode(nextComparisonMode: FlameGraphComparisonMode) {
function onChangeComparisonMode(nextComparisonMode: ComparisonMode) {
if (!('comparisonRangeFrom' in query)) {
return;
}
@@ -41,24 +45,24 @@ export function FlameGraphSearchPanel({
path,
query: {
...query,
...(nextComparisonMode === FlameGraphComparisonMode.Absolute
...(nextComparisonMode === ComparisonMode.Absolute
? {
comparisonMode: FlameGraphComparisonMode.Absolute,
comparisonMode: ComparisonMode.Absolute,
normalizationMode,
}
: { comparisonMode: FlameGraphComparisonMode.Relative }),
: { comparisonMode: ComparisonMode.Relative }),
},
});
}

function onChangeNormalizationMode(
nextNormalizationMode: FlameGraphNormalizationMode,
options: FlameGraphNormalizationOptions
nextNormalizationMode: NormalizationMode,
options: NormalizationOptions
) {
profilingRouter.push(routePath, {
path: routePath,
query:
nextNormalizationMode === FlameGraphNormalizationMode.Scale
nextNormalizationMode === NormalizationMode.Scale
? {
...query,
baseline: options.baselineScale,
@@ -82,7 +86,7 @@ export function FlameGraphSearchPanel({
comparisonMode={comparisonMode}
onChange={onChangeComparisonMode}
/>
{comparisonMode === FlameGraphComparisonMode.Absolute && (
{comparisonMode === ComparisonMode.Absolute && (
<EuiFlexItem grow={false}>
<EuiFlexGroup direction="row" gutterSize="m" alignItems="center">
<EuiFlexItem grow={false}>
30 changes: 13 additions & 17 deletions x-pack/plugins/profiling/public/views/flame_graphs_view/index.tsx
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiPageHeaderContentProps } from '@elastic/e
import { i18n } from '@kbn/i18n';
import { get } from 'lodash';
import React, { useState } from 'react';
import { FlameGraphComparisonMode, FlameGraphNormalizationMode } from '../../../common/flamegraph';
import { useProfilingParams } from '../../hooks/use_profiling_params';
import { useProfilingRouter } from '../../hooks/use_profiling_router';
import { useProfilingRoutePath } from '../../hooks/use_profiling_route_path';
@@ -20,7 +19,11 @@ import { FlameGraph } from '../../components/flamegraph';
import { ProfilingAppPageTemplate } from '../../components/profiling_app_page_template';
import { RedirectTo } from '../../components/redirect_to';
import { FlameGraphSearchPanel } from './flame_graph_search_panel';
import { FlameGraphNormalizationOptions } from './normalization_menu';
import {
ComparisonMode,
NormalizationMode,
NormalizationOptions,
} from '../../components/normalization_menu';

export function FlameGraphsView({ children }: { children: React.ReactElement }) {
const {
@@ -37,13 +40,12 @@ export function FlameGraphsView({ children }: { children: React.ReactElement })
);

const comparisonKuery = 'comparisonKuery' in query ? query.comparisonKuery : '';
const comparisonMode =
'comparisonMode' in query ? query.comparisonMode : FlameGraphComparisonMode.Absolute;
const comparisonMode = 'comparisonMode' in query ? query.comparisonMode : ComparisonMode.Absolute;

const normalizationMode: FlameGraphNormalizationMode = get(
const normalizationMode: NormalizationMode = get(
query,
'normalizationMode',
FlameGraphNormalizationMode.Time
NormalizationMode.Time
);

const baselineScale: number = get(query, 'baseline', 1);
@@ -58,7 +60,7 @@ export function FlameGraphsView({ children }: { children: React.ReactElement })
const baselineTime = 1;
const comparisonTime = totalSeconds / totalComparisonSeconds;

const normalizationOptions: FlameGraphNormalizationOptions = {
const normalizationOptions: NormalizationOptions = {
baselineScale,
baselineTime,
comparisonScale,
@@ -146,6 +148,8 @@ export function FlameGraphsView({ children }: { children: React.ReactElement })
return <RedirectTo pathname="/flamegraphs/flamegraph" />;
}

const isNormalizedByTime = normalizationMode === NormalizationMode.Time;

return (
<ProfilingAppPageTemplate tabs={tabs} hideSearchBar={true}>
<EuiFlexGroup direction="column">
@@ -164,16 +168,8 @@ export function FlameGraphsView({ children }: { children: React.ReactElement })
primaryFlamegraph={data?.primaryFlamegraph}
comparisonFlamegraph={data?.comparisonFlamegraph}
comparisonMode={comparisonMode}
baseline={
normalizationMode === FlameGraphNormalizationMode.Time
? baselineTime
: baselineScale
}
comparison={
normalizationMode === FlameGraphNormalizationMode.Time
? comparisonTime
: comparisonScale
}
baseline={isNormalizedByTime ? baselineTime : baselineScale}
comparison={isNormalizedByTime ? comparisonTime : comparisonScale}
showInformationWindow={showInformationWindow}
toggleShowInformationWindow={toggleShowInformationWindow}
/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/*
* 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 { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { TypeOf } from '@kbn/typed-react-router-config';
import React from 'react';
import { AsyncComponent } from '../../../components/async_component';
import { useProfilingDependencies } from '../../../components/contexts/profiling_dependencies/use_profiling_dependencies';
import {
NormalizationMenu,
NormalizationMode,
NormalizationOptions,
} from '../../../components/normalization_menu';
import { PrimaryAndComparisonSearchBar } from '../../../components/primary_and_comparison_search_bar';
import { TopNFunctionsTable } from '../../../components/topn_functions';
import { useProfilingParams } from '../../../hooks/use_profiling_params';
import { useProfilingRouter } from '../../../hooks/use_profiling_router';
import { useProfilingRoutePath } from '../../../hooks/use_profiling_route_path';
import { useTimeRange } from '../../../hooks/use_time_range';
import { useTimeRangeAsync } from '../../../hooks/use_time_range_async';
import { ProfilingRoutes } from '../../../routing';

export function DifferentialTopNFunctionsView() {
const {
path,
query,
query: {
rangeFrom,
rangeTo,
kuery,
sortDirection,
sortField,
comparisonKuery,
normalizationMode,
comparisonRangeFrom,
comparisonRangeTo,
baseline = 1,
comparison = 1,
},
} = useProfilingParams('/functions/differential');

const timeRange = useTimeRange({ rangeFrom, rangeTo });
const comparisonTimeRange = useTimeRange({
rangeFrom: comparisonRangeFrom,
rangeTo: comparisonRangeTo,
optional: true,
});

const totalSeconds = timeRange.inSeconds.end - timeRange.inSeconds.start;
const totalComparisonSeconds =
(new Date(comparisonTimeRange.end!).getTime() -
new Date(comparisonTimeRange.start!).getTime()) /
1000;

const comparisonTime = totalSeconds / totalComparisonSeconds;

const baselineTime = 1;
const normalizationOptions: NormalizationOptions = {
baselineScale: baseline,
baselineTime,
comparisonScale: comparison,
comparisonTime,
};

const {
services: { fetchTopNFunctions },
} = useProfilingDependencies();

const state = useTimeRangeAsync(
({ http }) => {
return fetchTopNFunctions({
http,
timeFrom: timeRange.inSeconds.start,
timeTo: timeRange.inSeconds.end,
startIndex: 0,
endIndex: 100000,
kuery,
});
},
[timeRange.inSeconds.start, timeRange.inSeconds.end, kuery, fetchTopNFunctions]
);

const comparisonState = useTimeRangeAsync(
({ http }) => {
if (!comparisonTimeRange.inSeconds.start || !comparisonTimeRange.inSeconds.end) {
return undefined;
}
return fetchTopNFunctions({
http,
timeFrom: comparisonTimeRange.inSeconds.start,
timeTo: comparisonTimeRange.inSeconds.end,
startIndex: 0,
endIndex: 100000,
kuery: comparisonKuery,
});
},
[
comparisonTimeRange.inSeconds.start,
comparisonTimeRange.inSeconds.end,
comparisonKuery,
fetchTopNFunctions,
]
);

const routePath = useProfilingRoutePath() as
| '/functions'
| '/functions/topn'
| '/functions/differential';

const profilingRouter = useProfilingRouter();

function onChangeNormalizationMode(
nextNormalizationMode: NormalizationMode,
options: NormalizationOptions
) {
profilingRouter.push(routePath, {
path: routePath,
query:
nextNormalizationMode === NormalizationMode.Scale
? {
...query,
baseline: options.baselineScale,
comparison: options.comparisonScale,
normalizationMode: nextNormalizationMode,
}
: {
...query,
normalizationMode: nextNormalizationMode,
},
});
}

const isNormalizedByTime = normalizationMode === NormalizationMode.Time;

return (
<>
<EuiFlexGroup direction="column">
<EuiFlexItem grow={false}>
<PrimaryAndComparisonSearchBar />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<NormalizationMenu
mode={normalizationMode}
options={normalizationOptions}
onChange={onChangeNormalizationMode}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup direction="row" gutterSize="s">
<EuiFlexItem>
<AsyncComponent {...state} size="xl" alignTop>
<TopNFunctionsTable
topNFunctions={state.data}
sortDirection={sortDirection}
sortField={sortField}
onSortChange={(nextSort) => {
profilingRouter.push(routePath, {
path,
query: {
...query,
sortField: nextSort.sortField,
sortDirection: nextSort.sortDirection,
},
});
}}
totalSeconds={timeRange.inSeconds.end - timeRange.inSeconds.start}
isDifferentialView={true}
baselineScaleFactor={isNormalizedByTime ? baselineTime : baseline}
/>
</AsyncComponent>
</EuiFlexItem>
{comparisonTimeRange.inSeconds.start && comparisonTimeRange.inSeconds.end ? (
<EuiFlexItem>
<AsyncComponent {...comparisonState} size="xl" alignTop>
<TopNFunctionsTable
sortDirection={sortDirection}
sortField={sortField}
onSortChange={(nextSort) => {
profilingRouter.push(routePath, {
path,
query: {
...(query as TypeOf<ProfilingRoutes, '/functions/differential'>['query']),
sortField: nextSort.sortField,
sortDirection: nextSort.sortDirection,
},
});
}}
topNFunctions={comparisonState.data}
comparisonTopNFunctions={state.data}
totalSeconds={
comparisonTimeRange.inSeconds.end - comparisonTimeRange.inSeconds.start
}
isDifferentialView={true}
baselineScaleFactor={isNormalizedByTime ? comparisonTime : comparison}
comparisonScaleFactor={isNormalizedByTime ? baselineTime : baseline}
/>
</AsyncComponent>
</EuiFlexItem>
) : null}
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</>
);
}
64 changes: 64 additions & 0 deletions x-pack/plugins/profiling/public/views/functions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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 { EuiPageHeaderContentProps } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { NormalizationMode } from '../../components/normalization_menu';
import { ProfilingAppPageTemplate } from '../../components/profiling_app_page_template';
import { RedirectTo } from '../../components/redirect_to';
import { useProfilingParams } from '../../hooks/use_profiling_params';
import { useProfilingRouter } from '../../hooks/use_profiling_router';
import { useProfilingRoutePath } from '../../hooks/use_profiling_route_path';

export function FunctionsView({ children }: { children: React.ReactElement }) {
const { query } = useProfilingParams('/functions/*');
const routePath = useProfilingRoutePath() as
| '/functions'
| '/functions/topn'
| '/functions/differential';

const profilingRouter = useProfilingRouter();

if (routePath === '/functions') {
return <RedirectTo pathname="/functions/topn" />;
}

const isDifferentialView = routePath === '/functions/differential';

const tabs: Required<EuiPageHeaderContentProps>['tabs'] = [
{
label: i18n.translate('xpack.profiling.functionsView.functionsTabLabel', {
defaultMessage: 'TopN functions',
}),
isSelected: !isDifferentialView,
href: profilingRouter.link('/functions/topn', { query }),
},
{
label: i18n.translate('xpack.profiling.functionsView.differentialFunctionsTabLabel', {
defaultMessage: 'Differential TopN functions',
}),
isSelected: isDifferentialView,
href: profilingRouter.link('/functions/differential', {
query: {
...query,
comparisonRangeFrom: query.rangeFrom,
comparisonRangeTo: query.rangeTo,
comparisonKuery: query.kuery,
normalizationMode:
'normalizationMode' in query ? query.normalizationMode : NormalizationMode.Time,
},
}),
},
];

return (
<ProfilingAppPageTemplate tabs={tabs} hideSearchBar={isDifferentialView}>
{children}
</ProfilingAppPageTemplate>
);
}
80 changes: 80 additions & 0 deletions x-pack/plugins/profiling/public/views/functions/topn/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* 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 { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React from 'react';
import { AsyncComponent } from '../../../components/async_component';
import { useProfilingDependencies } from '../../../components/contexts/profiling_dependencies/use_profiling_dependencies';
import { TopNFunctionsTable } from '../../../components/topn_functions';
import { useProfilingParams } from '../../../hooks/use_profiling_params';
import { useProfilingRouter } from '../../../hooks/use_profiling_router';
import { useProfilingRoutePath } from '../../../hooks/use_profiling_route_path';
import { useTimeRange } from '../../../hooks/use_time_range';
import { useTimeRangeAsync } from '../../../hooks/use_time_range_async';

export function TopNFunctionsView() {
const {
path,
query,
query: { rangeFrom, rangeTo, kuery, sortDirection, sortField },
} = useProfilingParams('/functions/topn');

const timeRange = useTimeRange({ rangeFrom, rangeTo });

const {
services: { fetchTopNFunctions },
} = useProfilingDependencies();

const state = useTimeRangeAsync(
({ http }) => {
return fetchTopNFunctions({
http,
timeFrom: timeRange.inSeconds.start,
timeTo: timeRange.inSeconds.end,
startIndex: 0,
endIndex: 100000,
kuery,
});
},
[timeRange.inSeconds.start, timeRange.inSeconds.end, kuery, fetchTopNFunctions]
);

const routePath = useProfilingRoutePath() as '/functions/topn';

const profilingRouter = useProfilingRouter();

return (
<>
<EuiFlexGroup direction="column">
<EuiFlexItem>
<EuiFlexGroup direction="row" gutterSize="s">
<EuiFlexItem>
<AsyncComponent {...state} size="xl" alignTop>
<TopNFunctionsTable
topNFunctions={state.data}
sortDirection={sortDirection}
sortField={sortField}
onSortChange={(nextSort) => {
profilingRouter.push(routePath, {
path,
query: {
...query,
sortField: nextSort.sortField,
sortDirection: nextSort.sortDirection,
},
});
}}
totalSeconds={timeRange.inSeconds.end - timeRange.inSeconds.start}
isDifferentialView={false}
/>
</AsyncComponent>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</>
);
}
188 changes: 0 additions & 188 deletions x-pack/plugins/profiling/public/views/functions_view/index.tsx

This file was deleted.

10 changes: 0 additions & 10 deletions x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
@@ -27434,16 +27434,6 @@
"xpack.profiling.flameGraphLegend.improvement": "Amélioration",
"xpack.profiling.flameGraphLegend.regression": "Régression",
"xpack.profiling.flamegraphModel.noChange": "Aucune modification",
"xpack.profiling.flameGraphNormalizationMenu.normalizeBy": "Normaliser par",
"xpack.profiling.flameGraphNormalizationMenu.normalizeByScaleTooltip": "Sélectionnez Normaliser par heure pour comparer un ensemble de machines sur différentes périodes. Par exemple, si vous comparez la dernière heure aux dernières 24 heures, la période la plus courte (1 heure) est multipliée pour égaler la période la plus longue (24 heures).",
"xpack.profiling.flameGraphNormalizationMenu.normalizeByTimeTooltip": "Sélectionnez Normaliser par facteur d'échelle et définissez vos facteurs d'échelle de référence et de comparaison pour comparer un ensemble de machines de différentes tailles. Par exemple, vous pouvez comparer un déploiement de 10 % de machines à un déploiement de 90 % de machines.",
"xpack.profiling.flameGraphNormalizationMenu.scale": "Facteur de montée en charge",
"xpack.profiling.flameGraphNormalizationMenu.time": "Heure",
"xpack.profiling.flameGraphNormalizationMode.selectModeLegend": "Sélectionner un mode de normalisation pour le flame-graph",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeAbsoluteButtonLabel": "Abs",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeLegend": "Ce commutateur vous permet de basculer entre comparaison absolue et comparaison relative entre les deux graphes",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeRelativeButtonLabel": "Rel",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeTitle": "Format",
"xpack.profiling.flameGraphsView.differentialFlameGraphTabLabel": "Flame-graph différentiel",
"xpack.profiling.flameGraphsView.flameGraphTabLabel": "Flame-graph",
"xpack.profiling.flameGraphTooltip.annualizedCo2": "CO2 annualisé",
10 changes: 0 additions & 10 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
@@ -27416,16 +27416,6 @@
"xpack.profiling.flameGraphLegend.improvement": "改善",
"xpack.profiling.flameGraphLegend.regression": "回帰",
"xpack.profiling.flamegraphModel.noChange": "変更なし",
"xpack.profiling.flameGraphNormalizationMenu.normalizeBy": "正規化",
"xpack.profiling.flameGraphNormalizationMenu.normalizeByScaleTooltip": "[時間で正規化]を選択すると、異なる期間でコンピューターのセットを比較します。たとえば、過去1時間と過去24時間を比較する場合は、短い方の時間枠(1時間)が乗算され、長い方の時間枠(24時間)と照合されます。",
"xpack.profiling.flameGraphNormalizationMenu.normalizeByTimeTooltip": "[比率で正規化]を選択し、ベースラインと比較比率を設定すると、異なるサイズのコンピューターのセットを比較します。たとえば、コンピューターの10%のデプロイをコンピューターの90%のデプロイと比較できます。",
"xpack.profiling.flameGraphNormalizationMenu.scale": "倍率",
"xpack.profiling.flameGraphNormalizationMenu.time": "時間",
"xpack.profiling.flameGraphNormalizationMode.selectModeLegend": "フレームグラフの正規化モードを選択",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeAbsoluteButtonLabel": "Abs",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeLegend": "このスイッチでは、両方のグラフの絶対比較と相対比較を切り替えることができます",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeRelativeButtonLabel": "Rel",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeTitle": "フォーマット",
"xpack.profiling.flameGraphsView.differentialFlameGraphTabLabel": "差分flamegraph",
"xpack.profiling.flameGraphsView.flameGraphTabLabel": "Flamegraph",
"xpack.profiling.flameGraphTooltip.annualizedCo2": "年間換算CO2",
10 changes: 0 additions & 10 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
@@ -27414,16 +27414,6 @@
"xpack.profiling.flameGraphLegend.improvement": "提升",
"xpack.profiling.flameGraphLegend.regression": "回归",
"xpack.profiling.flamegraphModel.noChange": "无更改",
"xpack.profiling.flameGraphNormalizationMenu.normalizeBy": "标准化依据",
"xpack.profiling.flameGraphNormalizationMenu.normalizeByScaleTooltip": "选择按时间标准化以跨不同时段比较一组机器。例如,如果您比较过去 1 小时与过去 24 小时的情况,则会将较短时间范围(1 小时)加倍以匹配更长时间范围(24 小时)。",
"xpack.profiling.flameGraphNormalizationMenu.normalizeByTimeTooltip": "选择按缩放因数标准化并设置基线和比较缩放因数,以比较不同大小的一组机器。例如,您可以将 10% 的机器部署与 90% 的机器部署进行比较。",
"xpack.profiling.flameGraphNormalizationMenu.scale": "缩放因数",
"xpack.profiling.flameGraphNormalizationMenu.time": "时间",
"xpack.profiling.flameGraphNormalizationMode.selectModeLegend": "为火焰图选择标准化模式",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeAbsoluteButtonLabel": "绝对",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeLegend": "此开关允许您在两个图表的绝对与相对比较之间进行切换",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeRelativeButtonLabel": "相对",
"xpack.profiling.flameGraphsView.differentialFlameGraphComparisonModeTitle": "格式",
"xpack.profiling.flameGraphsView.differentialFlameGraphTabLabel": "差异火焰图",
"xpack.profiling.flameGraphsView.flameGraphTabLabel": "火焰图",
"xpack.profiling.flameGraphTooltip.annualizedCo2": "年化 CO2",

0 comments on commit c6bca36

Please sign in to comment.