* -------------------------------------------------------------------------
* 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, { useCallback, useEffect, useMemo, useState } from 'react';
import { observer } from 'mobx-react';
import { useTranslation } from 'react-i18next';
import { Checkbox, Layout } from '@insight/lib/components';
import { ResizeTable } from '@insight/lib/resize';
import { formatDecimal, safeJSONParse } from '@insight/lib/utils';
import type { ColumnsType } from 'antd/es/table';
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
import Filter from './Filter';
import { CODE_SEARCH_WINDOW_HEIGHT } from './CodeTextSearch';
import CodeTable, { UNSELECTED } from './CodeAttrTable';
import { getInstrColumns } from './InstructionTable';
import { ASCENDC_INNER_CODE, NOT_APPLICABLE, InstructionVersion, FieldType } from './defs';
import type { Ilinetable, InstrsColumnType, JsonInstructionType, Limit } from './defs';
import { queryDynamicInstr } from '../RequestUtils';
import { GetPageConfigWhithPageData, HeaderFixedContainer, LeftRightContainer, syncScroller } from '../Common';
import { updateSession } from '../../connection/handler';
import { defaultCacheUnit, InstructionSelectSource } from '../../entity/session';
import type { Session } from '../../entity/session';
import './HotMethod.css';
const MAX_INSTRUCTION = 1000000;
const PAGE_LIMIT = 500000;
const ROW_HEIGHT = 32;
export interface ConditionType {
core: string;
source: string;
onlyRelated?: boolean;
};
export const isRelated = (instr: InstrsColumnType, range: string[][] = []): boolean => {
return Boolean(range?.find(item => Number(item[0]) <= Number(instr.Address) && Number(item[1]) >= Number(instr.Address)));
};
interface IRelatedLineParams {
instr: InstrsColumnType;
instrVersion: InstructionVersion;
condition: ConditionType;
loggedCodeLines: Ilinetable[];
}
function getRelatedLine({ instr, instrVersion, condition, loggedCodeLines }: IRelatedLineParams): number {
let line = UNSELECTED;
if (instrVersion === InstructionVersion.ASCENDC_INNER_CODE) {
const infoList = String(instr[ASCENDC_INNER_CODE]).split(':');
const file = infoList[0];
const fileLine = Number(infoList[1]);
if (file === condition.source && !isNaN(fileLine)) {
line = fileLine;
}
}
if (line <= 0) {
const data = loggedCodeLines.find((codeline: Ilinetable) => isRelated(instr, codeline['Address Range']));
line = data?.Line ?? UNSELECTED;
}
return line;
}
export const recoverDefaultInstructionSource = (): void => {
updateSession({
instructionSelectSource: InstructionSelectSource.DEFAULT,
cacheUnit: defaultCacheUnit,
});
};
const Index = observer(({ session }: { session: Session }) => {
const { t } = useTranslation('source');
const [condition, setCondition] = useState<ConditionType>({ core: '', source: '', onlyRelated: false });
const [selectedLine, setSelectedLine] = useState<number>(UNSELECTED);
const [selectedCodeLine, setSelectedCodeLine] = useState<Ilinetable | null>(null);
const [jumpListener, setJumpListener] = useState<number>(0);
const [codeTableHeight, setCodeTableHeight] = useState<number>(1000);
const [instructionTableHeight, setInstructionTableHeight] = useState<number>(1000);
const [instrLimit, setInstrLimit] = useState({ maxSize: MAX_INSTRUCTION, overlimit: false, current: 0 });
const [relatedInstrsLength, setRelatedInstrsLength] = useState<number>();
const handleFilterChange = (newConditions: ConditionType): void => {
setCondition({ ...condition, ...newConditions });
};
function resizeHeight(): void {
const height = document.getElementById('CodeTable')?.clientHeight ?? 1000;
if (height === 0) {
return;
}
setCodeTableHeight(height);
setInstructionTableHeight(session.openFind ? height + CODE_SEARCH_WINDOW_HEIGHT : height);
}
function activeJump(): void {
setTimeout(() => {
setJumpListener(pre => (pre + 1) % 100);
});
}
function cancelSelected(): void {
setSelectedLine(UNSELECTED);
};
useEffect(() => {
syncScroller([document.getElementById('CodeTable'),
document.querySelector('#CodeAttrTable .ant-table-body'),
document.querySelector('#CodeLine .ant-table-body'),
]);
const resizeFunc = (): void => {
resizeHeight();
};
window.addEventListener('resize', resizeFunc);
return (): void => {
window.removeEventListener('resize', resizeFunc);
};
}, []);
useEffect(() => {
if (session.instructionSelectSource === InstructionSelectSource.CACHE) {
cancelSelected();
activeJump();
}
}, [session.instructionSelectSource, session.cacheUnit]);
return <div id="hotMethod" style={{ height: '100%', width: '100%' }} className={'th35'}>
<Layout>
<HeaderFixedContainer
headerStyle={{ padding: '10px' }}
header={
<>
<Filter session={session} handleFilterChange={handleFilterChange}/>
<LeftRightContainer right={
<>
<div className="hit-label">
<span>
{
session.instructionSelectSource === InstructionSelectSource.CACHE
? <>{t('Cacheline Id')}:<span>{session.cacheUnit.cachelineId},</span></>
: <>
{t('Line')} :
<span>
{selectedLine >= 0 ? selectedLine : ''},
</span>
</>
}
{t('RelatedInstructionsCount')} :
<span>
{relatedInstrsLength}
</span>
</span>
<Checkbox
style={{ float: 'right' }}
checked={condition.onlyRelated}
onChange={(e: CheckboxChangeEvent): void => {
handleFilterChange({ ...condition, onlyRelated: e.target.checked });
}}
> {t('OnlyRelatedInstructions')}</Checkbox>
</div>
{instrLimit.overlimit
? (<div style={{ color: 'red', padding: '0 10px 0 20px' }}>
{t('ExceedInstructions', { max: instrLimit.maxSize })}
</div>)
: <></>}
</>
}/>
</>
}
body={
<LeftRightContainer
flex
left={<CodeTable
session={session}
tableHeight={codeTableHeight}
activeJump={activeJump}
condition={condition}
resizeHeight={resizeHeight}
selectedLine={selectedLine}
setSelectedLine={setSelectedLine}
setSelectedCodeLine={setSelectedCodeLine}
/>}
right={
<HeaderFixedContainer
id={'Instructions'}
style={{ paddingLeft: '8px' }}
body={<InstructionTable
session={session}
tableHeight={instructionTableHeight}
condition={condition}
selectedLine={selectedLine}
setSelectedLine={setSelectedLine}
selectedCodeLine={selectedCodeLine}
jumpListener={jumpListener}
setInstrLimit={setInstrLimit}
setRelatedInstrsLength={setRelatedInstrsLength}
/>}
/>
}
/>
}
/>
</Layout>
</div>;
});
async function getInstrs(coreName: string): Promise<{ instructions: InstrsColumnType[]; fields: Record<string, FieldType>; count: number; GPRStatusLengthMax: number }> {
let GPRStatusLengthMax = 0;
if (coreName === '') {
return { instructions: [], fields: {}, count: 0, GPRStatusLengthMax };
}
const res = await queryDynamicInstr({ coreName });
const fields = res?.['Instructions Dtype']?.Instructions ?? {};
let records = res?.Instructions ?? [];
const count = records.length;
records = records.slice(0, MAX_INSTRUCTION);
const percentageFields = Object.keys(fields).filter(fieldName => fields[fieldName] === FieldType.PERCENTAGE);
const list: InstrsColumnType[] = records.map((item: JsonInstructionType, index: number) => {
const formatData: Record<string, any> = {};
percentageFields.forEach(fieldName => {
formatData[fieldName] = formatDecimal(item[fieldName] as number);
});
const GPRStatus = safeJSONParse(item['GPR Status'], []);
GPRStatusLengthMax = Math.max(GPRStatusLengthMax, GPRStatus.length);
return {
...item,
...formatData,
index: index + 1,
maxCycles: 0,
'GPR Status': GPRStatus,
};
});
let maxCycles = 1;
list.forEach(item => {
if (!isNaN(Number(item.Cycles))) {
maxCycles = Math.max(maxCycles, Number(item.Cycles));
}
});
list.forEach(item => {
item.maxCycles = maxCycles;
});
return { instructions: list, fields, count, GPRStatusLengthMax };
};
function isRelatedInstruction({ instr, session, selectedCodeLine }: {
instr: InstrsColumnType;
session: Session;
selectedCodeLine: Ilinetable | null;
}): boolean {
if (session.instructionSelectSource === InstructionSelectSource.CACHE) {
return isRelated(instr, session.cacheUnit.addressRange);
}
if (selectedCodeLine !== null && selectedCodeLine !== undefined) {
return isRelated(instr, selectedCodeLine['Address Range']);
}
return false;
}
interface IProps {
session: Session;
condition: ConditionType;
tableHeight: number;
selectedLine: number;
selectedCodeLine: Ilinetable | null;
jumpListener: number;
setInstrLimit: (val: (pre: Limit) => Limit) => void ;
setSelectedLine: (val: number) => void;
setRelatedInstrsLength: (val: number) => void;
}
const InstructionTable = observer(({
session, condition, tableHeight, selectedCodeLine, setInstrLimit, selectedLine, jumpListener, setSelectedLine, setRelatedInstrsLength,
}: IProps) => {
const { t } = useTranslation('source');
const [allInstrs, setAllInstrs] = useState<InstrsColumnType[]>([]);
const [GPRStatusWidth, setGPRStatusWidth] = useState(0);
const isRelatedInstr = useCallback((instr: InstrsColumnType): boolean => isRelatedInstruction({ instr, session, selectedCodeLine })
, [selectedCodeLine, session.instructionSelectSource, session.cacheUnit.addressRange]);
const getRelatedInstrs = useCallback((): InstrsColumnType[] => allInstrs.filter((record: InstrsColumnType) => isRelatedInstr(record))
, [allInstrs, isRelatedInstr]);
const relatedInstrs = useMemo(() => getRelatedInstrs(), [getRelatedInstrs]);
const showInstrs = useMemo(() => condition.onlyRelated ? relatedInstrs : allInstrs, [allInstrs, relatedInstrs, condition.onlyRelated]);
const [dynamicInstrFields, setDynamicInstrFields] = useState<Record<string, FieldType>>({});
const instrColumns = useMemo(() => getInstrColumns(dynamicInstrFields, t, showInstrs, GPRStatusWidth), [dynamicInstrFields, t, showInstrs]);
async function updateInstrs(): Promise<void> {
getInstrs(condition.core).then(({ instructions: newInstrlist, fields, count, GPRStatusLengthMax }) => {
setGPRStatusWidth(++GPRStatusLengthMax * 16);
setDynamicInstrFields(fields);
setAllInstrs(newInstrlist);
setInstrLimit(pre => ({ ...pre, overlimit: count > MAX_INSTRUCTION, current: count }));
});
}
const handleInstrsClick = (instr: InstrsColumnType): void => {
setSelectedLine(getRelatedLine({ instr, instrVersion: session.instrVersion, condition, loggedCodeLines: session.loggedCodeLines }));
recoverDefaultInstructionSource();
};
useEffect(() => {
updateInstrs();
}, [condition.core]);
useEffect(() => {
setRelatedInstrsLength(relatedInstrs.length);
}, [relatedInstrs.length]);
return <InstructionTableComp
tableHeight={tableHeight}
columns={instrColumns}
condition={condition}
dataSource={showInstrs}
isRelatedInstr={isRelatedInstr}
handleInstrsClick={handleInstrsClick}
selectedLine={selectedLine}
jumpListener={jumpListener}
isShowPage={showInstrs.length > PAGE_LIMIT}
instructionSelectSource={session.instructionSelectSource}
/>;
});
interface IinstrProp {
condition: ConditionType;
columns: ColumnsType<InstrsColumnType>;
dataSource: InstrsColumnType[];
isRelatedInstr: (instr: InstrsColumnType) => boolean;
handleInstrsClick: (instr: InstrsColumnType) => void;
tableHeight: number;
selectedLine: number;
jumpListener: number;
isShowPage?: boolean;
instructionSelectSource: InstructionSelectSource;
}
const srcollToView = ({
condition,
selectedLine,
showDataSource,
isRelatedInstr,
instructionSelectSource = InstructionSelectSource.DEFAULT,
}:
{
condition: ConditionType;
selectedLine: number;
showDataSource: InstrsColumnType[];
isRelatedInstr: (instr: InstrsColumnType) => boolean;
instructionSelectSource: InstructionSelectSource;
},
): void => {
if (condition.onlyRelated === true || (selectedLine < 0 && instructionSelectSource === InstructionSelectSource.DEFAULT)) {
return;
}
const index = showDataSource.findIndex(isRelatedInstr);
if (index < 0) {
return;
}
const top = index * ROW_HEIGHT;
const parentNode = document.querySelector('#Instructions .ant-table-body') as HTMLElement;
parentNode.scrollTo({ top });
};
function InstructionTableComp({
columns,
dataSource,
isRelatedInstr,
handleInstrsClick,
tableHeight,
selectedLine,
condition,
jumpListener,
isShowPage,
instructionSelectSource,
}: IinstrProp): JSX.Element {
return isShowPage
? <InstructionTablePage
tableHeight={tableHeight}
columns={columns}
condition={condition}
dataSource={dataSource}
isRelatedInstr={isRelatedInstr}
handleInstrsClick={handleInstrsClick}
selectedLine={selectedLine}
jumpListener={jumpListener}
instructionSelectSource={instructionSelectSource}
/>
: <InstructionTableNopage
tableHeight={tableHeight}
columns={columns}
condition={condition}
dataSource={dataSource}
isRelatedInstr={isRelatedInstr}
handleInstrsClick={handleInstrsClick}
selectedLine={selectedLine}
jumpListener={jumpListener}
instructionSelectSource={instructionSelectSource}
/>;
};
const getShowData = (dataSource: InstrsColumnType[], filters: Record<string, any[]>, sorter: Record<string, any>): InstrsColumnType[] => {
let newDataSource = [...dataSource];
newDataSource = filterData(newDataSource, filters);
newDataSource = sortData(newDataSource, sorter);
return newDataSource;
};
const isMatchfFilter = (filterValue: any, value: any): boolean => {
if (!isNaN(Number(value))) {
return filterValue === NOT_APPLICABLE ? value < 0 : filterValue === value;
}
return filterValue === value;
};
const filterData = (dataSource: InstrsColumnType[], filters: Record<string, any[]>): InstrsColumnType[] => {
const fields = Object.keys(filters).filter(field => filters[field] !== null);
if (fields.length === 0) {
return dataSource;
}
return dataSource.filter(row => {
let fit = true;
for (let i = 0; i < fields.length; i++) {
const field = fields[i];
const value = (row as any)[field];
fit = fit && filters[field].some((filterValue: string | number) => {
return isMatchfFilter(filterValue, value);
});
if (!fit) {
break;
}
}
return fit;
});
};
const sortData = (dataSource: InstrsColumnType[], sorter: Record<string, any>): InstrsColumnType[] => {
if (sorter?.order === undefined || sorter?.order === null || dataSource?.length === 0) {
return dataSource;
}
const sign = sorter.order === 'ascend' ? 1 : -1;
const field: keyof InstrsColumnType = sorter.field;
return [...dataSource].sort((a, b) => {
const aType = isNaN(Number(a[field])) ? typeof a[field] : 'number';
const bType = isNaN(Number(b[field])) ? typeof b[field] : 'number';
const isAllNegativeNumber = aType === 'number' && bType === 'number' && Number(a[field]) < 0 && Number(b[field]) < 0;
if (isAllNegativeNumber) {
return -1;
}
if (aType === 'number' || bType === 'number') {
return sign * ((aType === 'number' ? Number(a[field]) : -1) - (bType === 'number' ? Number(b[field]) : -1));
}
return sign * String(a[field]).localeCompare(String(b[field]));
});
};
function InstructionTableNopage({
columns,
dataSource,
isRelatedInstr,
handleInstrsClick,
tableHeight,
selectedLine,
jumpListener,
condition,
instructionSelectSource,
}: IinstrProp): JSX.Element {
const [showDataSource, setShowDataSource] = useState(dataSource);
const [filters, setFilters] = useState({});
const [sorter, setSorter] = useState({});
const doSrcollToView = useCallback((): void => srcollToView({ condition, selectedLine, showDataSource, isRelatedInstr, instructionSelectSource })
, [condition, selectedLine, showDataSource, isRelatedInstr, instructionSelectSource]);
useEffect(() => {
setShowDataSource(getShowData(dataSource, filters, sorter));
}, [dataSource, filters, sorter]);
useEffect(() => {
doSrcollToView();
}, [jumpListener]);
return <ResizeTable
size="small"
minThWidth={50}
columns={columns}
dataSource={showDataSource}
rowClassName={(record: InstrsColumnType): string => (isRelatedInstr(record) ? 'selected' : '')}
onRow={(record: InstrsColumnType): { onClick: () => void } => ({
onClick: (): void => {
handleInstrsClick(record);
},
})}
pagination={false}
scroll={{ y: tableHeight, rowHeight: ROW_HEIGHT, scrollToFirstRowOnChange: false }}
virtual={true}
onChange={(pagination, newFilters, newSorter, extra): void => {
switch (extra.action) {
case 'filter':
setFilters(newFilters);
break;
case 'sort':
setSorter(newSorter);
break;
default:
break;
}
}}
/>;
}
function InstructionTablePage({
columns,
dataSource,
isRelatedInstr,
handleInstrsClick,
tableHeight,
selectedLine,
jumpListener,
condition,
instructionSelectSource,
}: IinstrProp): JSX.Element {
const [showDataSource, setShowDataSource] = useState<InstrsColumnType[]>([]);
const [filters, setFilters] = useState({});
const [sorter, setSorter] = useState({});
const [page, setPage] = useState({ current: 1, pageSize: PAGE_LIMIT, total: dataSource.length });
const [pageData, setPageData] = useState(showDataSource.slice((page.current - 1) * page.pageSize, page.current * page.pageSize));
const doSrcollToView = useCallback((): void => srcollToView({ condition, selectedLine, showDataSource: pageData, isRelatedInstr, instructionSelectSource })
, [condition, selectedLine, pageData, isRelatedInstr, instructionSelectSource]);
useEffect(() => {
setShowDataSource(getShowData(dataSource, filters, sorter));
}, [dataSource, filters, sorter]);
useEffect(() => {
setPage({ ...page, total: showDataSource.length });
}, [showDataSource]);
useEffect(() => {
const index = showDataSource.findIndex(isRelatedInstr);
const onPage = Number(page.pageSize) > 0 ? Math.ceil((index + 1) / page.pageSize) : 1;
if (index > 0 && onPage !== page.current) {
setPage({ ...page, current: onPage, total: showDataSource.length });
}
}, [showDataSource, jumpListener]);
useEffect(() => {
const curPageData = showDataSource.slice((page.current - 1) * page.pageSize, page.current * page.pageSize);
setPageData(curPageData);
}, [showDataSource, page.pageSize, page.current]);
useEffect(() => {
doSrcollToView();
}, [jumpListener, pageData, page.current]);
return <ResizeTable
size="small"
minThWidth={50}
columns={columns}
dataSource={pageData}
rowClassName={(record: InstrsColumnType): string => (isRelatedInstr(record) ? 'selected' : '')}
onRow={(record: InstrsColumnType): { onClick: () => void } => ({
onClick: (): void => {
handleInstrsClick(record);
},
})}
pagination={GetPageConfigWhithPageData(page, setPage, [PAGE_LIMIT])}
scroll={{ y: tableHeight - 50, rowHeight: ROW_HEIGHT }}
virtual={true}
onChange={(pagination, newFilters, newSorter, extra): void => {
switch (extra.action) {
case 'filter':
setFilters(newFilters);
break;
case 'sort':
setSorter(newSorter);
break;
default:
break;
}
}}
/>;
}
export default Index;