Skip to content

Commit

Permalink
[Profiling] Stack traces embeddable (#173905)
Browse files Browse the repository at this point in the history
Adding Stack traces Threads to the Infra plugin

<img width="1680" alt="Screenshot 2023-12-27 at 14 28 50"
src="https://github.com/elastic/kibana/assets/55978943/4f6b82a1-5c2a-4517-ad5d-481163b7ae37">

Error:
<img width="1637" alt="Screenshot 2024-01-02 at 10 08 22"
src="https://github.com/elastic/kibana/assets/55978943/3a881c8f-5513-4d15-928d-ae29c0ef3e68">


Empty:
<img width="1625" alt="Screenshot 2024-01-02 at 10 11 01"
src="https://github.com/elastic/kibana/assets/55978943/8a240420-3dae-4a35-b4ff-989bf14a0d68">

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
cauemarcondes and kibanamachine authored Jan 3, 2024
1 parent 7850e3e commit 50a6930
Show file tree
Hide file tree
Showing 22 changed files with 644 additions and 248 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { useProfilingStatusData } from '../../hooks/use_profiling_status_data';
import { useTabSwitcherContext } from '../../hooks/use_tab_switcher';
import { ContentTabIds } from '../../types';
import { ErrorPrompt } from './error_prompt';
import { Threads } from './threads';

export function Profiling() {
const { activeTabId } = useTabSwitcherContext();
Expand Down Expand Up @@ -50,6 +51,18 @@ export function Profiling() {
</>
),
},
{
id: 'threads',
name: i18n.translate('xpack.infra.tabs.profiling.threadsTabName', {
defaultMessage: 'Threads',
}),
content: (
<>
<EuiSpacer />
<Threads />
</>
),
},
];

if (loading) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n';
import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui';
import { FlamegraphLocator } from '@kbn/observability-shared-plugin/public/locators/profiling/flamegraph_locator';
import { TopNFunctionsLocator } from '@kbn/observability-shared-plugin/public/locators/profiling/topn_functions_locator';
import { StacktracesLocator } from '@kbn/observability-shared-plugin/public/locators/profiling/stacktraces_locator';
import { HOST_FIELD } from '../../../../../common/constants';

const PROFILING_FEEDBACK_URL = 'https://ela.st/profiling-feedback';
Expand All @@ -18,7 +19,7 @@ interface Props {
hostname: string;
from: string;
to: string;
profilingLinkLocator: FlamegraphLocator | TopNFunctionsLocator;
profilingLinkLocator: FlamegraphLocator | TopNFunctionsLocator | StacktracesLocator;
profilingLinkLabel: string;
}

Expand All @@ -43,7 +44,12 @@ export function ProfilingLinks({
</EuiLink>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink data-test-subj="infraFlamegraphTestLink" href={PROFILING_FEEDBACK_URL} external>
<EuiLink
data-test-subj="infraFlamegraphTestLink"
href={PROFILING_FEEDBACK_URL}
external
target="_blank"
>
{i18n.translate('xpack.infra.flamegraph.profilingFeedbackLink', {
defaultMessage: 'Give feedback about profiling',
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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 { EmbeddableStackTraces } from '@kbn/observability-shared-plugin/public';
import React from 'react';
import { TopNType } from '@kbn/profiling-utils';
import { EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { HOST_FIELD } from '../../../../../common/constants';
import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props';
import { useDatePickerContext } from '../../hooks/use_date_picker';
import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana';
import { ProfilingLinks } from './profiling_links';

export function Threads() {
const { services } = useKibanaContextForPlugin();
const { getDateRangeInTimestamp, dateRange, setDateRange } = useDatePickerContext();
const { from, to } = getDateRangeInTimestamp();
const { asset } = useAssetDetailsRenderPropsContext();
const stacktracesProfilingLinkLocator =
services.observabilityShared.locators.profiling.stacktracesLocator;

return (
<>
<ProfilingLinks
hostname={asset.name}
from={dateRange.from}
to={dateRange.to}
profilingLinkLocator={stacktracesProfilingLinkLocator}
profilingLinkLabel={i18n.translate('xpack.infra.flamegraph.profilingAppThreadsLink', {
defaultMessage: 'Go to Universal Profiling Threads',
})}
/>
<EuiSpacer />
<EmbeddableStackTraces
type={TopNType.Threads}
rangeFrom={from}
rangeTo={to}
kuery={`${HOST_FIELD}:"${asset.name}"`}
onClick={(category) => {
stacktracesProfilingLinkLocator.navigate({
type: TopNType.Traces,
rangeFrom: dateRange.from,
rangeTo: dateRange.to,
kuery: `(${HOST_FIELD}:"${asset.name}" ) AND process.thread.name:"${category}"`,
});
}}
onChartBrushEnd={(range) => {
setDateRange({ from: range.rangeFrom, to: range.rangeTo });
}}
/>
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@

import type { BaseFlameGraph } from '@kbn/profiling-utils';
import React from 'react';
import { ProfilingEmbeddable, ProfilingEmbeddableProps } from './profiling_embeddable';
import { ProfilingEmbeddable } from './profiling_embeddable';
import { EMBEDDABLE_FLAMEGRAPH } from '.';

type Props = Omit<ProfilingEmbeddableProps<BaseFlameGraph>, 'embeddableFactoryId'>;
interface Props {
data?: BaseFlameGraph;
isLoading: boolean;
height?: string;
}

export function EmbeddableFlamegraph(props: Props) {
return <ProfilingEmbeddable {...props} embeddableFactoryId={EMBEDDABLE_FLAMEGRAPH} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@

import type { TopNFunctions } from '@kbn/profiling-utils';
import React from 'react';
import { ProfilingEmbeddable, ProfilingEmbeddableProps } from './profiling_embeddable';
import { EMBEDDABLE_FUNCTIONS } from '.';
import { ProfilingEmbeddable } from './profiling_embeddable';

type Props = Omit<ProfilingEmbeddableProps<TopNFunctions>, 'embeddableFactoryId'> & {
interface Props {
data?: TopNFunctions;
isLoading: boolean;
rangeFrom: number;
rangeTo: number;
};
}

export function EmbeddableFunctions(props: Props) {
return <ProfilingEmbeddable {...props} embeddableFactoryId={EMBEDDABLE_FUNCTIONS} />;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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 React from 'react';
import { TopNType } from '@kbn/profiling-utils';
import { EMBEDDABLE_STACK_TRACES } from '.';
import { ProfilingEmbeddable } from './profiling_embeddable';

interface Props {
type: TopNType;
kuery: string;
rangeFrom: number;
rangeTo: number;
onClick: (category: string) => void;
onChartBrushEnd: (range: { rangeFrom: string; rangeTo: string }) => void;
}

export function EmbeddableStackTraces(props: Props) {
return <ProfilingEmbeddable {...props} embeddableFactoryId={EMBEDDABLE_STACK_TRACES} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export const EMBEDDABLE_FUNCTIONS = 'EMBEDDABLE_FUNCTIONS';
/** Profiling functions embeddable */
export { EmbeddableFunctions } from './embeddable_functions';

/** Profiling threads embeddable key */
export const EMBEDDABLE_STACK_TRACES = 'EMBEDDABLE_STACK_TRACES';
export { EmbeddableStackTraces } from './embeddable_stack_traces';

/** Profiling search bar embeddable key */
export const EMBEDDABLE_PROFILING_SEARCH_BAR = 'EMBEDDABLE_PROFILING_SEARCH_BAR';
/** Profiling search bar embeddable */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,19 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';
import React, { useEffect, useRef, useState } from 'react';
import { ObservabilitySharedStart } from '../../../plugin';

export interface ProfilingEmbeddableProps<T> {
data?: T;
embeddableFactoryId: string;
isLoading: boolean;
height?: string;
}

export function ProfilingEmbeddable<T>({
embeddableFactoryId,
data,
isLoading,
height,
...props
}: ProfilingEmbeddableProps<T>) {
}: T & { embeddableFactoryId: string; height?: string }) {
const { embeddable: embeddablePlugin } = useKibana<ObservabilitySharedStart>().services;
const [embeddable, setEmbeddable] = useState<any>();
const embeddableRoot: React.RefObject<HTMLDivElement> = useRef<HTMLDivElement>(null);

useEffect(() => {
async function createEmbeddable() {
const factory = embeddablePlugin?.getEmbeddableFactory(embeddableFactoryId);
const input = {
id: 'embeddable_profiling',
data,
isLoading,
};
const input = { ...props, id: 'embeddable_profiling' };
const embeddableObject = await factory?.create(input);
setEmbeddable(embeddableObject);
}
Expand All @@ -51,14 +38,10 @@ export function ProfilingEmbeddable<T>({

useEffect(() => {
if (embeddable) {
embeddable.updateInput({
data,
isLoading,
...props,
});
embeddable.updateInput(props);
embeddable.reload();
}
}, [data, embeddable, isLoading, props]);
}, [embeddable, props]);

return (
<div
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/observability_shared/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,11 @@ export {
EMBEDDABLE_FLAMEGRAPH,
EMBEDDABLE_FUNCTIONS,
EMBEDDABLE_PROFILING_SEARCH_BAR,
EMBEDDABLE_STACK_TRACES,
EmbeddableFlamegraph,
EmbeddableFunctions,
EmbeddableProfilingSearchBar,
EmbeddableStackTraces,
type EmbeddableProfilingSearchBarProps,
} from './components/profiling/embeddables';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,30 @@
import qs from 'query-string';
import type { SerializableRecord } from '@kbn/utility-types';
import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public';
import { TopNType } from '@kbn/profiling-utils';

export interface StacktracesLocatorParams extends SerializableRecord {
kuery?: string;
rangeFrom?: string;
rangeTo?: string;
type?: TopNType;
}

export type StacktracesLocator = LocatorPublic<StacktracesLocatorParams>;

export class StacktracesLocatorDefinition implements LocatorDefinition<StacktracesLocatorParams> {
public readonly id = 'stacktracesLocator';

public readonly getLocation = async ({ rangeFrom, rangeTo, kuery }: StacktracesLocatorParams) => {
public readonly getLocation = async ({
rangeFrom,
rangeTo,
kuery,
type = TopNType.Threads,
}: StacktracesLocatorParams) => {
const params = { rangeFrom, rangeTo, kuery };
return {
app: 'profiling',
path: `/stacktraces/threads?${qs.stringify(params)}`,
path: `/stacktraces/${type}?${qs.stringify(params)}`,
state: {},
};
};
Expand Down
51 changes: 27 additions & 24 deletions x-pack/plugins/profiling/common/topn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,17 +245,14 @@ export function getCategoryColor({
return colors[stringhash(category)];
}

export function groupSamplesByCategory({
samples,
totalCount,
metadata,
labels,
}: {
samples: TopNSample[];
totalCount: number;
metadata: Record<string, StackFrameMetadata[]>;
labels: Record<string, string>;
}): TopNSubchart[] {
export function groupSamplesByCategory(topNResponse: TopNResponse): { charts: TopNSubchart[] } {
const {
TotalCount: totalCount,
TopN: samples,
Metadata: metadata,
Labels: labels,
} = topNResponse;

const seriesByCategory = new Map<string, CountPerTime[]>();

for (let i = 0; i < samples.length; i++) {
Expand Down Expand Up @@ -289,17 +286,23 @@ export function groupSamplesByCategory({
rotations: Math.ceil(subcharts.length / 10),
});

return orderBy(subcharts, ['Percentage', 'Category'], ['desc', 'asc']).map((chart, index) => {
return {
...chart,
Color: getCategoryColor({ category: chart.Category, colors, subChartSize: subcharts.length }),
Index: index + 1,
Series: chart.Series.map((value) => {
return {
...value,
Category: chart.Category,
};
}),
};
});
return {
charts: orderBy(subcharts, ['Percentage', 'Category'], ['desc', 'asc']).map((chart, index) => {
return {
...chart,
Color: getCategoryColor({
category: chart.Category,
colors,
subChartSize: subcharts.length,
}),
Index: index + 1,
Series: chart.Series.map((value) => {
return {
...value,
Category: chart.Category,
};
}),
};
}),
};
}
Loading

0 comments on commit 50a6930

Please sign in to comment.