123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- import {useCallback, useMemo} from 'react';
- import styled from '@emotion/styled';
- import CompactSelect from 'sentry/components/compactSelect';
- import {IconList} from 'sentry/icons';
- import {tn} from 'sentry/locale';
- import space from 'sentry/styles/space';
- import {SelectValue} from 'sentry/types';
- import {defined} from 'sentry/utils';
- import {FlamegraphState} from 'sentry/utils/profiling/flamegraph/flamegraphStateProvider/flamegraphContext';
- import {ProfileGroup} from 'sentry/utils/profiling/profile/importProfile';
- import {Profile} from 'sentry/utils/profiling/profile/profile';
- import {makeFormatter} from 'sentry/utils/profiling/units/units';
- interface ThreadSelectorProps {
- onThreadIdChange: (threadId: Profile['threadId']) => void;
- profileGroup: ProfileGroup;
- threadId: FlamegraphState['profiles']['threadId'];
- }
- function ThreadMenuSelector({
- threadId,
- onThreadIdChange,
- profileGroup,
- }: ThreadSelectorProps) {
- const options: SelectValue<number>[] = useMemo(() => {
- return [...profileGroup.profiles].sort(compareProfiles).map(profile => {
- return {
- label: profile.name
- ? `tid (${profile.threadId}): ${profile.name}`
- : `tid (${profile.threadId})`,
- value: profile.threadId,
- disabled: profile.samples.length === 0,
- details: (
- <ThreadLabelDetails
- duration={makeFormatter(profile.unit)(profile.duration)}
- // plus 1 because the last sample always has a weight of 0
- // and is not included in the raw weights
- samples={profile.rawWeights.length + 1}
- />
- ),
- };
- });
- }, [profileGroup]);
- const handleChange: (opt: SelectValue<number>) => void = useCallback(
- opt => {
- if (defined(opt)) {
- onThreadIdChange(opt.value);
- }
- },
- [onThreadIdChange]
- );
- return (
- <CompactSelect
- triggerProps={{
- icon: <IconList size="xs" />,
- size: 'xs',
- }}
- options={options}
- value={threadId}
- onChange={handleChange}
- isSearchable
- />
- );
- }
- interface ThreadLabelDetailsProps {
- duration: string;
- samples: number;
- }
- function ThreadLabelDetails(props: ThreadLabelDetailsProps) {
- return (
- <DetailsContainer>
- <div>{props.duration}</div>
- <div>{tn('%s sample', '%s samples', props.samples)}</div>
- </DetailsContainer>
- );
- }
- type ProfileLight = {
- duration: Profile['duration'];
- name: Profile['name'];
- threadId: Profile['threadId'];
- };
- function compareProfiles(a: ProfileLight, b: ProfileLight): number {
- if (!b.duration) {
- return -1;
- }
- if (!a.duration) {
- return 1;
- }
- if (a.name.startsWith('(tid') && b.name.startsWith('(tid')) {
- return -1;
- }
- if (a.name.startsWith('(tid')) {
- return -1;
- }
- if (b.name.startsWith('(tid')) {
- return -1;
- }
- if (a.name.includes('main')) {
- return -1;
- }
- if (b.name.includes('main')) {
- return 1;
- }
- return a.name > b.name ? -1 : 1;
- }
- const DetailsContainer = styled('div')`
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- gap: ${space(1)};
- `;
- export {ThreadMenuSelector};
|