22581b2f创建于 2025年12月16日历史提交
/*
 * -------------------------------------------------------------------------
 * This file is part of the MindStudio project.
 * Copyright (c) 2025 Huawei Technologies Co.,Ltd.
 *
 * MindStudio is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 */
import React, { useEffect, useState, useRef } from 'react';
import * as echarts from 'echarts';
import { safeStr } from '@insight/lib/utils';
import { MIChart } from '@insight/lib/components';
import type { ChartsHandle } from '@insight/lib';
import { type EChartsOption } from 'echarts';
import { Session } from '../entity/session';
import { observer } from 'mobx-react';
import { getFuncNewData } from './dataHandler';
import { chartResize } from '../utils/utils';
import { useTheme, type Theme } from '@emotion/react';
const colorTypes: string[] = ['#8fd3e8', '#d95850', '#eb8146', '#ffb248', '#f2d643', '#ebdba4', '#fcce10', '#b5c334', '#1bca93'];
const transData = (data: any): any => {
    return data.map((item: any, index: number) => ({
        name: item.func,
        value: [item.depth, item.startTimestamp, item.endTimestamp, item.func],
        itemStyle: {
            color: colorTypes[index % 9],
        },
    }));
};
const getToolbox = (): echarts.ToolboxComponentOption => {
    return {
        show: true,
        feature: {
            dataZoom: {
                yAxisIndex: false,
                filterMode: 'weakFilter',
                icon: {
                    back: 'none',
                },
            },
            restore: {
                show: true,
            },
            dataView: {
                show: false,
            },
        },
        right: 80,
        top: 20,
    };
};
const getRenderItem = (session: Session, theme: Theme, api: any): any => {
    const level = api.value(0);
    const start = api.coord([Math.max(api.value(1), session.minTime), level]);
    const end = api.coord([Math.min(api.value(2), session.maxTime), level]);
    const height = ((api.size([0, 1]) || [0, 20]) as number[])[1];
    const width = end[0] - start[0];
    const customRes = {
        type: 'rect',
        transition: ['shape'],
        shape: {
            x: start[0],
            y: start[1],
            width,
            height: height - 2,
        },
        emphasis: { style: { stroke: '#000' } },
        textContent: {
            type: 'text',
            style: {
                text: api.value(3),
                fill: '#000',
                overflow: 'truncate',
                width: width - 4,
                fontSize: 11,
            },
        },
        textConfig: {
            position: 'inside',
            inside: true,
            local: true,
        },
        style: {
            fill: api.visual('color'),
            stroke: '',
            lineWidth: 0,
        },
    };
    if (session.searchFunc.includes(api.value(3))) {
        customRes.style.stroke = theme.mode === 'dark' ? '#fff' : '#000';
        customRes.style.lineWidth = 3;
    }
    return customRes;
};
const getSeries = (session: Session, theme: Theme): any => {
    return [
        {
            type: 'custom',
            renderItem: (params: any, api: any): any => getRenderItem(session, theme, api),
            encode: {
                x: [0, 1, 2],
                y: 0,
            },
            data: transData(session.funcData.traces),
            clip: true,
        },
    ];
};
const getTooltip = (session: Session): echarts.TooltipComponentOption => {
    return {
        trigger: 'item',
        formatter: function (params: any): string {
            const info = session.funcData.traces[params.dataIndex];
            if (!info) {
                return '';
            }
            return safeStr(info.func);
        },
        textStyle: {
            fontSize: 12,
        },
    };
};
const getOptions = (session: Session, theme: Theme): EChartsOption => {
    return {
        color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],
        gradientColor: ['#f6efa6', '#d88273', '#bf444c'],
        xAxis: {
            axisTick: {
                show: false,
            },
            min: session.minTime,
            max: session.maxTime,
            axisLine: {
                show: false,
            },
            axisLabel: {
                formatter: function (value: number): string {
                    return `${(value / 1000000000).toFixed(3)}s`;
                },
            },
            splitLine: {
                show: false,
            },
        },
        yAxis: {
            show: false,
            inverse: true,
            axisPointer: {
                show: false,
                snap: true,
            },
            max: session.maxDepth + 1,
        },
        toolbox: getToolbox(),
        axisPointer: {
            show: true,
        },
        tooltip: getTooltip(session),
        series: getSeries(session, theme),
        grid: {
            left: 80,
            right: 60,
        },
    };
};

const MemoryFunctionCall = observer(({ session, setFuncIns }: {
    session: Session;
    setFuncIns: (value: echarts.ECharts | null) => void;
}): React.ReactElement => {
    const chartRef = useRef<ChartsHandle>(null);
    const [loading, setLoading] = useState(false);
    const [chartOptions, setChartOptions] = useState<EChartsOption>({});
    const { funcData, deviceId, eventType, threadId, maxTime, minTime, searchFunc, threadFlag } = session;
    const theme: Theme = useTheme();
    useEffect(() => {
        if (deviceId === '' || threadFlag) return;
        setLoading(true);
        getFuncNewData(session);
        setLoading(false);
    }, [deviceId, eventType, threadId]);
    useEffect(() => {
        setChartOptions(getOptions(session, theme));
        if (chartRef.current !== null && chartRef.current !== undefined) {
            setFuncIns(chartRef.current.getInstance());
        }
        chartResize(chartRef?.current?.getInstance());
    }, [deviceId, eventType, JSON.stringify(funcData.traces), maxTime, minTime]);
    useEffect(() => {
        setChartOptions(getOptions(session, theme));
    }, [JSON.stringify(searchFunc), theme.mode]);
    useEffect(() => {
        chartRef.current?.getInstance()?.dispatchAction({
            type: 'takeGlobalCursor',
            key: 'dataZoomSelect',
            dataZoomSelectActive: true,
        });
    }, [chartOptions, theme.mode]);
    return (
        <MIChart
            ref={chartRef}
            height="500px"
            width="calc(100vw - 120px)"
            loading={loading}
            options={chartOptions}
        />
    );
});
export default MemoryFunctionCall;