diff --git a/ide/src/base-ui/table/lit-table.ts b/ide/src/base-ui/table/lit-table.ts index 4b46b866e3365c162e7e75dc60142b14bedb67b1..68cf01f07390196b6870848c8df7735f0fdd117d 100644 --- a/ide/src/base-ui/table/lit-table.ts +++ b/ide/src/base-ui/table/lit-table.ts @@ -23,6 +23,7 @@ import { JSONToCSV } from '../utils/CSVFormater.js'; import { NodeType } from '../../js-heap/model/DatabaseStruct.js'; import { ConstructorType } from '../../js-heap/model/UiStruct.js'; import { LitIcon } from '../icon/LitIcon.js'; +import { JsCpuProfilerStatisticsStruct } from '../../trace/bean/JsStruct.js'; const iconWidth = 20; const iconPadding = 5; @element('lit-table') @@ -809,13 +810,13 @@ export class LitTable extends HTMLElement { this.dispatchEvent( new CustomEvent('button-click', { detail: { - key: key + key: key, }, - composed: true + composed: true, }) ); - event.stopPropagation() - }) + event.stopPropagation(); + }); } h.style.justifyContent = a.getAttribute('align'); this.gridTemplateColumns.push(a.getAttribute('width') || '1fr'); @@ -1317,7 +1318,13 @@ export class LitTable extends HTMLElement { td.style.whiteSpace = 'nowrap'; let text = this.formatName(dataIndex, rowData.data[dataIndex]); if (text.indexOf('<') === -1) { - td.title = text; + if (dataIndex === 'selfTimeStr' && rowData.data.chartFrameChildren) { + td.title = rowData.data.selfTime + 'ns'; + } else if (dataIndex === 'totalTimeStr' && rowData.data.chartFrameChildren) { + td.title = rowData.data.totalTime + 'ns'; + } else { + td.title = text; + } } td.dataIndex = dataIndex; td.style.justifyContent = column.getAttribute('align') || 'flex-start'; @@ -1548,7 +1555,11 @@ export class LitTable extends HTMLElement { td.style.justifyContent = column.getAttribute('align') || 'flex-start'; let text = this.formatName(dataIndex, rowData.data[dataIndex]); if (text.indexOf('<') === -1) { - td.title = text; + if (dataIndex === 'totalTimeStr' && rowData.data.chartFrameChildren) { + td.title = rowData.data.totalTime + 'ns'; + } else { + td.title = text; + } } // 如果表格中有模板的情况,将模板中的数据放进td中,没有模板,直接将文本放进td // 但是对于Current Selection tab页来说,表格前两列是时间,第三列是input标签,第四列是button标签 @@ -1813,7 +1824,15 @@ export class LitTable extends HTMLElement { (child as HTMLElement).title = text; } else { (child as HTMLElement).innerHTML = text; - (child as HTMLElement).title = text; + if (dataIndex === 'selfTimeStr' && rowObject.data.chartFrameChildren) { + (child as HTMLElement).title = rowObject.data.selfTime + 'ns'; + } else if (dataIndex === 'totalTimeStr' && rowObject.data.chartFrameChildren) { + (child as HTMLElement).title = rowObject.data.totalTime + 'ns'; + } else if (dataIndex === 'timeStr' && rowObject.data instanceof JsCpuProfilerStatisticsStruct) { + (child as HTMLElement).title = rowObject.data.time + 'ns'; + } else { + (child as HTMLElement).title = text; + } } } }); diff --git a/ide/src/doc/md/quickstart_smaps.md b/ide/src/doc/md/quickstart_smaps.md deleted file mode 100644 index b0117a5db3a10284932e3a1d1439b24bcd2fb844..0000000000000000000000000000000000000000 --- a/ide/src/doc/md/quickstart_smaps.md +++ /dev/null @@ -1,62 +0,0 @@ -# 进程 smaps 的抓取和展示说明 - -smaps 展示了一个进程的内存消耗。 - -## smaps 的抓取 - -### smaps 抓取配置参数 - -![GitHub Logo](../../figures/smaps/smapssetting.jpg) -配置项说明: - -- Start VM Tracker Record:配置项的总开关。 -- Process:smaps的抓取只能选择单进程抓取。 - -再点击 Record setting,在 output file path 输入文件名 hiprofiler_data_smaps.htrace,拖动滚动条设置 buffer size 大小是 64M,抓取时长是 50s。 -![GitHub Logo](../../figures/smaps/smapsrecord.jpg) -点击 Trace command,就会根据上面的配置生成抓取命令,点击 Record 抓取,抓取过程中会显示抓取时长。 -![GitHub Logo](../../figures/smaps/smapsexcuting.jpg) - -### smaps 展示说明 - -抓取结束后 smaps 的 trace 会自动加载展示。 -![GitHub Logo](../../figures/smaps/smapssummary.jpg) - -界面布局介绍:smaps 整体界面布局分为 3 个部分: - -- 红色区域:泳道图。 -- 绿色区域:详细信息。 - -### smaps 泳道图展示 - -smaps 泳道图展示当前时刻该进程的内存消耗。 -![GitHub Logo](../../figures/smaps/smapschart.jpg) - -### smaps 泳道图的框选功能 - -可以对泳道图进行框选,框选后在最下方的弹出层中会展示框选数据的统计表格,总共有两个个 tab 页。 -VM Tracker Statistics 的 Tab 页如图: -![GitHub Logo](../../figures/smaps/smapsstatistics.jpg) - -- Type: 将抓取到的信息根据Type归类,分四类,Data,Text,Const,Other。 -- % of Res: 每行的Resident Size 占总Ressident Size的比例。 -- #Reg:统计的类型个数。 -- Path:虚拟内存块路径,类型中有多个则显示multiple。 -- Resident Size: smaps节点中Rss(Shared_Clean+Shared_Dirty+Private_Clean+Private_Dirty)。 -- Dirty Size:smaps节点中Shared_Dirty + Private_Dirty。 -- Swapped: smaps节点中Swap + SwapPss。 -- Virtual Size:smaps节点中Size。 -- Pss: smaps节点中Pss。 -- Res. %:Resident Size / Virtual Size 比值。 - VM Tracker Record List 的 Tab 页如图: - ![GitHub Logo](../../figures/smaps/smapslist.jpg) -- Type: 将抓取到的信息根据Type归类,Data,Text,Const,Other。 -- Address Range: 每段虚拟内存段的开始和结束位置。 -- Dirty Size:smaps节点中Shared_Dirty + Private_Dirty。 -- Swapper: smaps节点中Swap + SwapPss。 -- Resident Size:smaps节点中Rss(Shared_Clean+Shared_Dirty+Private_Clean+Private_Dirty)。 -- Virtual Size:smaps节点中Size。 -- Pss:smaps节点中Pss。 -- Reside: Rss / Size 比值。 -- Protection: 内存块的权限(读写执行执行)。 -- Path: 内存段路径。 diff --git a/ide/src/doc/quickstart_smaps.html b/ide/src/doc/quickstart_smaps.html deleted file mode 100644 index 644dda7fe55c21c92286e4538e9a3865eff60b2d..0000000000000000000000000000000000000000 --- a/ide/src/doc/quickstart_smaps.html +++ /dev/null @@ -1,1010 +0,0 @@ - - - - - quickstart_smaps - - - - - - -
-

进程smaps的抓取和展示说明

- -

smaps展示了一个进程的内存消耗。

-

smaps的抓取

- -

smaps抓取配置参数

- -

- GitHub Logo
- 配置项说明: -

- -

- 再点击Record setting,在output file path输入文件名hiprofiler_data_smaps.htrace,拖动滚动条设置buffer - size大小是64M,抓取时长是50s。
- GitHub Logo
- 点击Trace command,就会根据上面的配置生成抓取命令,点击Record抓取,抓取过程中会显示抓取时长。
- GitHub Logo -

-

smaps展示说明

- -

- 抓取结束后smaps的trace会自动加载展示。
- GitHub Logo -

-

界面布局介绍:smaps整体界面布局分为3个部分:

- -

smaps泳道图展示

- -

- smaps泳道图展示当前时刻该进程的内存消耗。
- GitHub Logo -

-

smaps泳道图的框选功能

- -

- 可以对泳道图进行框选,框选后在最下方的弹出层中会展示框选数据的统计表格,总共有两个个tab页。
- VM Tracker Statistics的Tab页如图:
- GitHub Logo -

- -

- VM Tracker Record List的Tab页如图:
- GitHub Logo -

- -
- - - - diff --git a/ide/src/figures/smaps/smapschart.jpg b/ide/src/figures/smaps/smapschart.jpg deleted file mode 100644 index 032d543261f85121367fa3f7e260e688b018d815..0000000000000000000000000000000000000000 Binary files a/ide/src/figures/smaps/smapschart.jpg and /dev/null differ diff --git a/ide/src/figures/smaps/smapsexcuting.jpg b/ide/src/figures/smaps/smapsexcuting.jpg deleted file mode 100644 index 53f86e1472c53801d7d3637ffadae6d493233921..0000000000000000000000000000000000000000 Binary files a/ide/src/figures/smaps/smapsexcuting.jpg and /dev/null differ diff --git a/ide/src/figures/smaps/smapslist.jpg b/ide/src/figures/smaps/smapslist.jpg deleted file mode 100644 index 598f6a1289980ead2857757d8d7ff513d5a022d9..0000000000000000000000000000000000000000 Binary files a/ide/src/figures/smaps/smapslist.jpg and /dev/null differ diff --git a/ide/src/figures/smaps/smapsrecord.jpg b/ide/src/figures/smaps/smapsrecord.jpg deleted file mode 100644 index 9380722264bf06e22a1b1231e97eca0f06a0db1f..0000000000000000000000000000000000000000 Binary files a/ide/src/figures/smaps/smapsrecord.jpg and /dev/null differ diff --git a/ide/src/figures/smaps/smapssetting.jpg b/ide/src/figures/smaps/smapssetting.jpg deleted file mode 100644 index c79f7f9d047c0ca2e4d4b90ab15a58f81e279f81..0000000000000000000000000000000000000000 Binary files a/ide/src/figures/smaps/smapssetting.jpg and /dev/null differ diff --git a/ide/src/figures/smaps/smapsstatistics.jpg b/ide/src/figures/smaps/smapsstatistics.jpg deleted file mode 100644 index 4af1ef5ad619eeb8a0ed024286c868421118c428..0000000000000000000000000000000000000000 Binary files a/ide/src/figures/smaps/smapsstatistics.jpg and /dev/null differ diff --git a/ide/src/figures/smaps/smapssummary.jpg b/ide/src/figures/smaps/smapssummary.jpg deleted file mode 100644 index df74018bc3bdfd6bec26d540415e2125e929530a..0000000000000000000000000000000000000000 Binary files a/ide/src/figures/smaps/smapssummary.jpg and /dev/null differ diff --git a/ide/src/trace/component/SpHelp.ts b/ide/src/trace/component/SpHelp.ts index 2db6c0f34b77db36fad0f94df9c73a750dbaf840..c87c806a568ec748a92cae53df3f8bad5b2b7ad4 100644 --- a/ide/src/trace/component/SpHelp.ts +++ b/ide/src/trace/component/SpHelp.ts @@ -186,19 +186,6 @@ export class SpHelp extends BaseElement { `/application/doc/quickstart_bio.html?${that.dark} width="100%" height="100%">`; }, }, - { - title: '进程smaps抓取和展示说明', - icon: '', - clickHandler: function (item: MenuItem) { - SpStatisticsHttpUtil.addOrdinaryVisitAction({ - event: 'smaps', - action: 'help_doc', - }); - that.appContent!.innerHTML = - ' { + if (ev.key === '0' && ev.target.value.length === 1 && ev.target.value === '0') { + ev.preventDefault(); + } + }); this.processId = this.shadowRoot?.getElementById('pid') as LitAllocationSelect; let process = this.processId.shadowRoot?.querySelector('.multipleSelect') as HTMLDivElement; let sp = document.querySelector('sp-application') as SpApplication; @@ -190,6 +198,16 @@ export class SpAllocations extends BaseElement { let stepValue = [0, 1, 10, 30, 60, 300, 600, 1800, 3600]; this.statisticsSlider = this.shadowRoot?.querySelector('#interval-slider') as LitSlider; + this.unwindEL.addEventListener('keydown', (ev: any) => { + if (ev.key === '0' && ev.target.value.length === 1 && ev.target.value === '0') { + ev.preventDefault(); + } + }); + this.shareMemory.addEventListener('keydown', (ev: any) => { + if (ev.key === '0' && ev.target.value.length === 1 && ev.target.value === '0') { + ev.preventDefault(); + } + }); this.recordStatisticsResult = this.shadowRoot?.querySelector( '.record-statistics-result' ) as HTMLDivElement; @@ -204,6 +222,11 @@ export class SpAllocations extends BaseElement { }; let parentElement = this.statisticsSlider!.parentNode as Element; this.intervalResultInput = this.shadowRoot?.querySelector('.interval-result') as HTMLInputElement; + this.intervalResultInput!.addEventListener('keydown', (ev: any) => { + if (ev.key === '0' && ev.target.value.length === 1 && ev.target.value === '0') { + ev.preventDefault(); + } + }); this.intervalResultInput.value = '10'; this.statisticsSlider.addEventListener('input', (evt) => { this.statisticsSlider!.sliderStyle = { @@ -253,8 +276,9 @@ export class SpAllocations extends BaseElement { defaultSize = stepIndex * stepSize; break; } else if (inputValue < currentValue && stepIndex !== 0) { - defaultSize = (inputValue - stepValue[stepIndex - 1]) / - (currentValue - stepValue[stepIndex - 1]) * stepSize + (stepSize * (stepIndex - 1)); + defaultSize = + ((inputValue - stepValue[stepIndex - 1]) / (currentValue - stepValue[stepIndex - 1])) * stepSize + + stepSize * (stepIndex - 1); break; } } @@ -571,13 +595,13 @@ export class SpAllocations extends BaseElement {
Max unwind level Max Unwind Level Rang is 0 - 512, default 10 - +
Shared Memory Size (One page equals 4 KB) Shared Memory Size Range is 0 - 131072 page, default 16384 page
- + Page
@@ -585,7 +609,7 @@ export class SpAllocations extends BaseElement { Filter Memory Size Filter size Range is 0 - 65535 byte, default 0 byte
- + Byte
@@ -613,7 +637,7 @@ export class SpAllocations extends BaseElement {
- + S
diff --git a/ide/src/trace/component/setting/SpHilogRecord.ts b/ide/src/trace/component/setting/SpHilogRecord.ts index 4a4de44955fe59aef4ac30ba1668e579470c19b7..fbb07bd80a70e4b800db1dfcc0712f44c8ffee12 100644 --- a/ide/src/trace/component/setting/SpHilogRecord.ts +++ b/ide/src/trace/component/setting/SpHilogRecord.ts @@ -84,7 +84,7 @@ export class SpHilogRecord extends BaseElement { } getHiLogLevel(): string[] { - return ['ALL-Level', 'Debug', 'Info', 'Warn', 'Error']; + return ['ALL-Level', 'Debug', 'Info', 'Warn', 'Error', 'Fatal']; } initHtml(): string { diff --git a/ide/src/trace/component/setting/SpProbesConfig.ts b/ide/src/trace/component/setting/SpProbesConfig.ts index 30daee5873f63479a5d2a4f0715b1b8cfb697baa..83e799a541c7e5394a4f5a0840227cb0cf197ce1 100644 --- a/ide/src/trace/component/setting/SpProbesConfig.ts +++ b/ide/src/trace/component/setting/SpProbesConfig.ts @@ -35,6 +35,8 @@ export class SpProbesConfig extends BaseElement { private ftraceBufferSizeResult: HTMLDivElement | null | undefined; private ftraceSlider: LitSlider | null | undefined; + private ftraceBuffSizeResultInput: HTMLInputElement | null | undefined; + set startSamp(allocationStart: boolean) { if (allocationStart) { this.setAttribute('startSamp', ''); @@ -110,6 +112,12 @@ export class SpProbesConfig extends BaseElement { } initElements(): void { + this.ftraceBuffSizeResultInput = this.shadowRoot?.querySelector('.ftrace-buff-size-result') as HTMLInputElement; + this.ftraceBuffSizeResultInput!.addEventListener('keydown', (ev: any) => { + if (ev.key === '0' && ev.target.value.length === 1 && ev.target.value === '0') { + ev.preventDefault(); + } + }); this.traceConfigList = [ { value: 'Scheduling details', @@ -374,45 +382,50 @@ export class SpProbesConfig extends BaseElement { private unDisable() { this.startSamp = true; - let checkDesBoxDis=this.shadowRoot?.querySelectorAll('check-des-box') - let litCheckBoxDis=this.shadowRoot?.querySelectorAll('lit-check-box') - - let defaultSelected:any=[] - defaultSelected=defaultSelected.concat(this.traceConfigList,this.memoryConfigList,this.abilityConfigList,this.hitraceConfigList) - - this.shadowRoot?.querySelector("[value='Hitrace categories']")?.setAttribute('checked','true') - this.ftraceSlider!.removeAttribute('disabled') - - checkDesBoxDis?.forEach((item:any)=>{ - item.removeAttribute('disabled') - }) - - litCheckBoxDis?.forEach((item:any)=>{ - item.removeAttribute('disabled') - }) - - defaultSelected.filter((item:any)=>{ - if(item.isSelect) this.shadowRoot?.querySelector(`[value='${item.value}']`)?.setAttribute('checked','true') - }) + let checkDesBoxDis = this.shadowRoot?.querySelectorAll('check-des-box'); + let litCheckBoxDis = this.shadowRoot?.querySelectorAll('lit-check-box'); + + let defaultSelected: any = []; + defaultSelected = defaultSelected.concat( + this.traceConfigList, + this.memoryConfigList, + this.abilityConfigList, + this.hitraceConfigList + ); + + this.shadowRoot?.querySelector("[value='Hitrace categories']")?.setAttribute('checked', 'true'); + this.ftraceSlider!.removeAttribute('disabled'); + + checkDesBoxDis?.forEach((item: any) => { + item.removeAttribute('disabled'); + }); + + litCheckBoxDis?.forEach((item: any) => { + item.removeAttribute('disabled'); + }); + + defaultSelected.filter((item: any) => { + if (item.isSelect) + this.shadowRoot?.querySelector(`[value='${item.value}']`)?.setAttribute('checked', 'true'); + }); } private disable() { this.startSamp = false; - let checkDesBoxDis=this.shadowRoot?.querySelectorAll('check-des-box') - let litCheckBoxDis=this.shadowRoot?.querySelectorAll('lit-check-box') + let checkDesBoxDis = this.shadowRoot?.querySelectorAll('check-des-box'); + let litCheckBoxDis = this.shadowRoot?.querySelectorAll('lit-check-box'); - this.ftraceSlider!.setAttribute('disabled','') - - checkDesBoxDis?.forEach((item:any)=>{ - item.setAttribute('disabled','') - item.checked=false - }) + this.ftraceSlider!.setAttribute('disabled', ''); - litCheckBoxDis?.forEach((item:any)=>{ - item.setAttribute('disabled','') - item.checked=false - }) + checkDesBoxDis?.forEach((item: any) => { + item.setAttribute('disabled', ''); + item.checked = false; + }); + litCheckBoxDis?.forEach((item: any) => { + item.setAttribute('disabled', ''); + item.checked = false; + }); } initHtml(): string { @@ -573,8 +586,8 @@ export class SpProbesConfig extends BaseElement {
-
- +
+ KB
diff --git a/ide/src/trace/component/setting/SpRecordSetting.ts b/ide/src/trace/component/setting/SpRecordSetting.ts index 757f4a3d4138dce1331713a508f4e53cb66121f4..0421f3630a956b3e6643d736d90767027556ecd7 100644 --- a/ide/src/trace/component/setting/SpRecordSetting.ts +++ b/ide/src/trace/component/setting/SpRecordSetting.ts @@ -132,7 +132,7 @@ export class SpRecordSetting extends BaseElement { this.bufferNumber = this.shadowRoot?.querySelector('.buffer-size') as HTMLElement; this.durationNumber = this.shadowRoot?.querySelector('.max-duration') as HTMLElement; let inputs = this.shadowRoot?.querySelectorAll('input'); - inputs!.forEach(item => { + inputs!.forEach((item) => { item.addEventListener('keydown', (ev) => { // @ts-ignore if (ev.key === '0' && ev.target.value.length === 1 && ev.target.value === '0') { @@ -168,7 +168,7 @@ export class SpRecordSetting extends BaseElement {
- + MB
`; @@ -555,7 +555,7 @@ export class SpRecordSetting extends BaseElement {
- + MB
diff --git a/ide/src/trace/component/setting/bean/ProfilerServiceTypes.ts b/ide/src/trace/component/setting/bean/ProfilerServiceTypes.ts index fb936195950e8cccc3d87536e3f444cd15d7c137..21d8430d21a39ad7a67621da83fdcdef8e85087f 100644 --- a/ide/src/trace/component/setting/bean/ProfilerServiceTypes.ts +++ b/ide/src/trace/component/setting/bean/ProfilerServiceTypes.ts @@ -836,6 +836,9 @@ export function levelFromJSON(object: any): Level { case 4: case 'Warn': return Level.LOG_WARN; + case 5: + case 'Fatal': + return Level.LOG_FATAL; case -1: case 'UNRECOGNIZED': default: @@ -849,6 +852,7 @@ export enum Level { LOG_INFO = 'INFO', LOG_DEBUG = 'DEBUG', LOG_WARN = 'WARN', + LOG_FATAL = 'FATAL', UNRECOGNIZED = -1, } diff --git a/ide/src/trace/component/setting/utils/PluginConvertUtils.ts b/ide/src/trace/component/setting/utils/PluginConvertUtils.ts index 4d97e5d194ad84d153b430b6bb61b3af367effe1..b3b9ff0fe865c3e284cde678dda8acae90f8fd6b 100644 --- a/ide/src/trace/component/setting/utils/PluginConvertUtils.ts +++ b/ide/src/trace/component/setting/utils/PluginConvertUtils.ts @@ -241,4 +241,4 @@ export class PluginConvertUtils { } } -const LevelConfigEnumList = ['LEVEL_UNSPECIFIED', 'DEBUG', 'INFO', 'WARN', 'ERROR'] +const LevelConfigEnumList = ['LEVEL_UNSPECIFIED', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'] diff --git a/ide/src/trace/component/trace/sheet/ark-ts/TabPaneJsCpuStatistics.ts b/ide/src/trace/component/trace/sheet/ark-ts/TabPaneJsCpuStatistics.ts index cc47674bb26f59ececf693d8de07e007bc3bb214..69c37acb31968d7f143724f53af3850911e0a2f1 100644 --- a/ide/src/trace/component/trace/sheet/ark-ts/TabPaneJsCpuStatistics.ts +++ b/ide/src/trace/component/trace/sheet/ark-ts/TabPaneJsCpuStatistics.ts @@ -33,8 +33,16 @@ export class TabPaneJsCpuStatistics extends BaseElement { private sortKey = 'timeStr'; private sortType = 2; private statisticsPie: LitChartPie | null | undefined; + private currentSelection: SelectionParam | undefined; set data(data: SelectionParam | Array) { + if (data instanceof SelectionParam) { + if (data === this.currentSelection) { + return; + } + this.currentSelection = data; + } + this.init(); this.clearData(); this.progress!.loading = true; diff --git a/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogSummary.ts b/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogSummary.ts index be4531768fbd5674a544156994deb6242c6c5b84..3da7ee6597a1043b315f00fe8ca566528c94f512 100644 --- a/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogSummary.ts +++ b/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogSummary.ts @@ -100,6 +100,9 @@ export class TabPaneHiLogSummary extends BaseElement { .tree-row-tr:hover { background-color: #DEEDFF; } + .tree-row-tr:nth-last-child(1):hover { + background-color: white; + } .head-label, .head-count { white-space: nowrap; overflow: hidden; @@ -276,6 +279,12 @@ export class TabPaneHiLogSummary extends BaseElement { tableTreeEl.style.height = `${this.parentElement!.clientHeight - 40}px`; } this.createRowNodeTableEL(this.logTreeNodes, tableTreeEl, tableCountEl, ''); + let emptyTr = document.createElement('tr'); + emptyTr.className = 'tree-row-tr'; + tableTreeEl?.appendChild(emptyTr); + let emptyCountTr = document.createElement('tr'); + emptyCountTr.className = 'tree-row-tr'; + tableCountEl?.appendChild(emptyCountTr); tableFragmentEl.appendChild(tableTreeEl); tableFragmentEl.appendChild(tableCountEl); this.logSummaryTable!.appendChild(tableFragmentEl); diff --git a/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts b/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts index 9d235f3c9cce0f1a04b5ba142d88ce63730c9826..a408a39552692fa97a2da1d9cf0e51c170f1396c 100644 --- a/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts +++ b/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts @@ -112,7 +112,7 @@ export class TabPaneHiLogs extends BaseElement { connectedCallback(): void { super.connectedCallback(); - this.tagFilterInput?.addEventListener('keydown', this.tagFilterKeyEvent); + this.tagFilterInput?.addEventListener('keyup', this.tagFilterKeyEvent); new ResizeObserver((): void => { this.parentElement!.style.overflow = 'hidden'; // @ts-ignore @@ -124,7 +124,7 @@ export class TabPaneHiLogs extends BaseElement { disconnectedCallback(): void { super.disconnectedCallback(); - this.tagFilterInput?.removeEventListener('keydown', this.tagFilterKeyEvent); + this.tagFilterInput?.removeEventListener('keyup', this.tagFilterKeyEvent); } initHtml(): string { @@ -179,7 +179,6 @@ export class TabPaneHiLogs extends BaseElement { let rowCount = frontTotalRowSize.toString().split('.'); height += trEl.clientHeight - (Number(rowCount[1]) / 100 * trEl.clientHeight); } - firstRowHeight = trEl.clientHeight; } let allTdEl = trEl.querySelectorAll('.td'); allTdEl[0].style.color = '#3D88C7'; diff --git a/ide/src/trace/database/SqlLite.ts b/ide/src/trace/database/SqlLite.ts index 6ee980f1d694d5cd4ac2304989923ce6d14aeb43..6e16d6da601851694f7cc82a063901769e57edbb 100644 --- a/ide/src/trace/database/SqlLite.ts +++ b/ide/src/trace/database/SqlLite.ts @@ -2755,7 +2755,7 @@ export const querySceneSearchFunc = (search: string, processList: Array) select c.cookie,c.id,c.name as funName,c.ts - r.start_ts as startTime,c.dur,c.depth,t.tid,t.name as threadName ,p.pid ,'func' as type from callstack c left join thread t on c.callid = t.id left join process p on t.ipid = p.id left join trace_range r - where c.name like '%${search}%' and startTime > 0 and p.pid in (${processList.join(',')}); + where c.name like '%${search}%' ESCAPE '\\' and startTime > 0 and p.pid in (${processList.join(',')}); `, { $search: search } ); diff --git a/ide/src/trace/database/TraceWorker.ts b/ide/src/trace/database/TraceWorker.ts index 7eea2a9958cfe5277f458c1482895dfbc2f397b8..b58ca480e2ee59848c5207d7605cd32202d74315 100644 --- a/ide/src/trace/database/TraceWorker.ts +++ b/ide/src/trace/database/TraceWorker.ts @@ -283,7 +283,11 @@ self.onmessage = async (e: MessageEvent) => { const dataSlice = uint8Array.subarray(wrSize, wrSize + sliceLen); Module.HEAPU8.set(dataSlice, reqBufferAddr); wrSize += sliceLen; - r2 = Module._TraceStreamerParseDataEx(sliceLen); + if (wrSize >= uint8Array.length) { + r2 = Module._TraceStreamerParseDataEx(sliceLen, 1); + } else { + r2 = Module._TraceStreamerParseDataEx(sliceLen, 0); + } if (r2 == -1) { break; } diff --git a/trace_streamer/README.md b/trace_streamer/README.md index b975f0b55554f6f7baf7905fd6184259ae051508..06194db91b1b6620f73651af900b69a03c713941 100644 --- a/trace_streamer/README.md +++ b/trace_streamer/README.md @@ -60,7 +60,7 @@ EMSCRIPTEN_KEEPALIVE uint8_t* TraceStreamer_Set_ThirdParty_DataDealer(SendDataCa * @ dataLen: 需要解析的数据源长度 * return: 成功返回0,失败返回-1 */ -EMSCRIPTEN_KEEPALIVE int TraceStreamerParseDataEx(int dataLen) +EMSCRIPTEN_KEEPALIVE int TraceStreamerParseDataEx(int dataLen, bool isFinish) /* TraceStreamer停止解析数据,由JS调用 * diff --git a/trace_streamer/build/protoc_w.py b/trace_streamer/build/protoc_w.py index 2b8102566b5b5003ce4236ae1044fc7a0cc7bccc..3dd7b13a4a1de30832f345213cb58bad9f5ace55 100755 --- a/trace_streamer/build/protoc_w.py +++ b/trace_streamer/build/protoc_w.py @@ -52,5 +52,4 @@ if not sys.argv[4].startswith("--plugin"): cmd=[PROTOC, OPT_PLUGIN_PROTOREADER, f"{PLUGINOUT}:{sys.argv[5]}", *PARAMS_ALL.split()] print("执行参数:--------------- ", cmd, " --------------------------") subprocess.run(cmd) - # subprocess.run(cmd, env={"LD_LIBRARY_PATH": f"{LIBCXX_X64_OUT}:{SUBSYS_X64_OUT}"}) subprocess.run([PROTOC, *PARAMS_ALL.split()]) diff --git a/trace_streamer/doc/app_startup.md b/trace_streamer/doc/app_startup.md index e7031ab2c8e3e0d90429203e908bbb15435fab2c..1056b351f21ba28deeb5426c0581487973fb75c7 100644 --- a/trace_streamer/doc/app_startup.md +++ b/trace_streamer/doc/app_startup.md @@ -1,23 +1,26 @@ # 应用启动数据说明 -TraceStreamer支持解析应用启动数据,数据包含应用拉起的6个阶段及相关的调用栈信息,分别为Proess Creating(创建应用进程),Application -Launching(加载应用),UI Ability Launching(加载UI Ability), UI Ability OnForeground(应用进入前台),First Frame-App Phase(首帧渲染提交-应用),First Frame-Render Phase(首帧渲染提交-Render Service)。 +TraceStreamer支持解析应用启动数据,数据包含应用拉起的8个阶段及相关的调用栈信息,分别为ProcessTouchEvent(手指点击),StartUIAbilityBySCB(处理创建进程信息&创建窗口), LoadAbility(拉起进程),ApplicationLaunching(加载应用),UIAbilityLaunching(加载UIAbility), UIAbilityOnForeground(应用进入前台),First Frame-App Phase(首帧渲染提交-应用),First Frame-Render Phase(首帧渲染提交-Render Service)。 应用启动每个阶段的开始和结束时间如图: -![1689415472707](image/app_startup/1689415472707.png) +![1689415472707](image/app_startup/app_start_up.png) 前四个阶段对应的标志事件字符串: -Proess Creating:在callstack表中查找包含"H:int OHOS::AAFwk::MissionListManager::StartAbilityLocked("的字符,并且向上的调用栈包含"H:virtual int OHOS::AAFwk::AbilityManagerService::StartAbility("字符的数据,即为启动第一阶段。该数据中第一个'##'之后的字符串即为该应用的名称。 +ProcessTouchEvent:在callstack表中查找包含"H:client dispatch touchId:"的字符,即为启动第1阶段。注:**一次点击存在多个,取后面trace前的最后一个** -Application Launching:在callstack表中查找包含"H:virtual void OHOS::AppExecFwk::AppMgrServiceInner::AttachApplication(const pid_t, const sptrOHOS::AppExecFwk::IAppScheduler &)##"字符的数据,即为启动第二阶段。该数据中第一个'##'之后的字符串即为该应用的名称。 +StartUIAbilityBySCB:在callstack表中查找包含"H:OHOS::ErrCode OHOS::AAFwk::AbilityManagerClient::StartUIAbilityBySCB"的字符,即为启动第2阶段。 -UI Ability Launching:在callstack表中查找包含"H:void OHOS::AppExecFwk::MainThread::HandleLaunchAbility(const std::shared_ptr `` &)##"字符的数据,即为启动的第三阶段,在这个阶段会上报包名和拉起该应用的ipid。如数据中缺少此阶段,可视为应用未启动。 +LoadAbility:在callstack表中查找包含"H:virtual void OHOS::AppExecFwk::AppMgrServiceInner::LoadAbility"的字符,即为启动第3阶段。 -UI Ability OnForeground:在callstack表中查找包含"H:void OHOS::AppExecFwk::AbilityThread::HandleAbilityTransaction(const OHOS::AppExecFwk::Want &, const OHOS::AppExecFwk::LifeCycleStateInfo &, sptrOHOS::AAFwk::SessionInfo)##"字符的数据,即为启动的第四阶段,该阶段会上报ipid,同一个应用第三和第四阶段上报的ipid相同。 +Application Launching:在callstack表中查找包含"H:virtual void OHOS::AppExecFwk::AppMgrServiceInner::AttachApplication(const pid_t, const sptrOHOS::AppExecFwk::IAppScheduler &)##"字符的数据,即为启动第4阶段。该数据中第一个'##'之后的字符串即为该应用的名称。在此阶段关联前3阶段数据。 + +UI Ability Launching:在callstack表中查找包含"H:void OHOS::AppExecFwk::MainThread::HandleLaunchAbility(const std::shared_ptr `` &)##"字符的数据,即为启动的第5阶段,在这个阶段会上报包名和拉起该应用的ipid。如数据中缺少此阶段,可视为应用未启动。 + +UI Ability OnForeground:在callstack表中查找包含"H:void OHOS::AbilityRuntime::FAAbilityThread::HandleAbilityTransaction(const OHOS::AbilityRuntime::Want &, const OHOS::AbilityRuntime::LifeCycleStateInfo &, sptr)##"字符的数据,即为启动的第6阶段,该阶段会上报ipid,同一个应用UIAbilityLaunching(加载UIAbility)阶段和当前阶段上报的ipid相同。 后两个阶段的计算方式如下: -First Frame-App Phase:使用第三阶段得到的ipid关联到frame_slice表中,取到该ipid对应的应用帧的首帧数据,即为该应用第五阶段的数据。 +First Frame-App Phase:使用UIAbilityLaunching(加载UIAbility)阶段得到的ipid关联到frame_slice表中,取到该ipid对应的应用帧的首帧数据,即为该应用第7阶段的数据。 -First Frame-Render Phase:通过应用帧首帧数据,可以在frame_slice表中通过dst字段关联到渲染帧首帧数据,即为该应用的第六阶段的数据。 +First Frame-Render Phase:通过应用帧首帧数据,可以在frame_slice表中通过dst字段关联到渲染帧首帧数据,即为该应用的第8阶段的数据。 diff --git a/trace_streamer/doc/image/app_startup/1689415472707.png b/trace_streamer/doc/image/app_startup/1689415472707.png deleted file mode 100644 index 91c4453e38cfb444363287b6447f5d94ee230aa5..0000000000000000000000000000000000000000 Binary files a/trace_streamer/doc/image/app_startup/1689415472707.png and /dev/null differ diff --git a/trace_streamer/doc/image/app_startup/app_start_up.png b/trace_streamer/doc/image/app_startup/app_start_up.png new file mode 100644 index 0000000000000000000000000000000000000000..89d147910cebe526e6afac75e9aafa35e526a70e Binary files /dev/null and b/trace_streamer/doc/image/app_startup/app_start_up.png differ diff --git a/trace_streamer/pare_third_party.sh b/trace_streamer/pare_third_party.sh index 88335f09e4be4eb59ec7fbbcbeee141b54268314..70972257b31d39e6cc06c78b5a0e74ed6f555a9d 100755 --- a/trace_streamer/pare_third_party.sh +++ b/trace_streamer/pare_third_party.sh @@ -86,20 +86,48 @@ if [ ! -f "perf_include/musl/elf.h" ];then mv elf.h perf_include/musl/elf.h fi +if [ ! -d "perf_include/hiviewdfx/hilog" ];then + rm -rf hiviewdfx_hilog perf_include/hiviewdfx/hilog + mkdir -p perf_include/hiviewdfx/hilog + git clone --depth=1 https://gitee.com/openharmony/hiviewdfx_hilog.git + mv hiviewdfx_hilog/interfaces/native/innerkits/include/ perf_include/hiviewdfx/hilog + rm -rf hiviewdfx_hilog +fi + +if [ ! -d "perf_include/hiviewdfx/faultloggerd" ];then + rm -rf hiviewdfx_faultloggerd perf_include/hiviewdfx/faultloggerd + mkdir -p perf_include/hiviewdfx/faultloggerd/interfaces/innerkits + git clone --depth=1 git@gitee.com:openharmony/hiviewdfx_faultloggerd.git + mv hiviewdfx_faultloggerd/common/ perf_include/hiviewdfx/faultloggerd + mv hiviewdfx_faultloggerd/interfaces/common/ perf_include/hiviewdfx/faultloggerd/interfaces + mv hiviewdfx_faultloggerd/interfaces/nonlinux/ perf_include/hiviewdfx/faultloggerd/interfaces + mv hiviewdfx_faultloggerd/interfaces/innerkits/unwinder/ perf_include/hiviewdfx/faultloggerd/interfaces/innerkits + find perf_include/hiviewdfx/faultloggerd -type f -name "*.gn" -delete + $cp ../prebuilts/patch_hiperf/hiviewdfx_BUILD.gn ../third_party/perf_include/hiviewdfx/BUILD.gn + rm -rf hiviewdfx_faultloggerd + rm -rf perf_include/hiviewdfx/common/build + rm -rf perf_include/hiviewdfx/common/cutil + rm perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_regs_x86_64.cpp + $sed -i '/HiLogPrint/s/^/\/\/ /' perf_include/hiviewdfx/faultloggerd/common/dfxlog/dfx_log.cpp + $sed -i '/TRAP_BRANCH/s/^/\/\/ /' perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_signal.cpp + $sed -i '/TRAP_HWBKPT/s/^/\/\/ /' perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_signal.cpp + $sed -i '/is_ohos/s/is_ohos/true/g' perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_mmap.cpp + $sed -i '/is_ohos/s/is_ohos/true/g' perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/include/dfx_regs.h + $sed -i '/#include /a #include "debug_logger.h"' perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/include/unwinder.h + $sed -i '/VerifyFilePath/s/const std::vector/std::vector\/g' perf_include/hiviewdfx/faultloggerd/common/dfxutil/dfx_util.h + $sed -i '/VerifyFilePath/s/const std::vector/std::vector\/g' perf_include/hiviewdfx/faultloggerd/common/dfxutil/dfx_util.cpp + $sed -i '/getpid() == gettid()/s/getpid() == gettid()/false/g' perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/unwinder.cpp + $sed -i '/!realpath(path, realPath)/s/!realpath(path, realPath)/false/g' perf_include/hiviewdfx/faultloggerd/common/dfxutil/dfx_util.cpp + $sed -i '/#include "dfx_util.h"/a #include "utilities.h"' perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_mmap.cpp +fi if [ ! -f "hiperf/BUILD.gn" ];then rm -rf hiperf developtools_hiperf git clone --depth=1 git@gitee.com:openharmony/developtools_hiperf.git if [ -d "developtools_hiperf" ];then mv developtools_hiperf hiperf - $patch -p1 -d ./hiperf < ../prebuilts/patch_hiperf/1_pengjingtong.diff - $patch -p1 -d ./hiperf < ../prebuilts/patch_hiperf/2_perf_dump_report_0913.patch $cp ../prebuilts/patch_hiperf/BUILD.gn ../third_party/hiperf/BUILD.gn $cp ../prebuilts/patch_hiperf/file_ex.h hiperf/include/nonlinux/linux $cp ../prebuilts/patch_hiperf/unique_fd.h hiperf/include/nonlinux/linux - #include <../musl/include/elf.h> - # 替换为 - #include - $sed -i "s/..\/musl\/include\/elf.h/elf.h/g" hiperf/include/elf_parser.h $sed -i "/FRIEND_TEST/s/^\(.*\)$/\/\/\1/g" hiperf/include/virtual_thread.h $sed -i "s/HIPERF_DEBUG/ALWAYSTRUE/g" hiperf/include/virtual_thread.h $sed -i "/#include \"report_json_file.h\"/s/^\(.*\)$/\/\/\1/g" hiperf/include/report.h @@ -107,16 +135,23 @@ if [ ! -f "hiperf/BUILD.gn" ];then $sed -i "/#include /s/^\(.*\)$/\/\/\1/g" hiperf/include/utilities.h $sed -i "/FRIEND_TEST/s/^\(.*\)$/\/\/\1/g" hiperf/include/virtual_thread.h $sed -i "/FRIEND_TEST/s/^\(.*\)$/\/\/\1/g" hiperf/include/callstack.h + $sed -i '/unwinder.h/s/^/\/\/ /' hiperf/include/callstack.h $sed -i "/FRIEND_TEST/s/^\(.*\)$/\/\/\1/g" hiperf/include/symbols_file.h $sed -i "/FRIEND_TEST/s/^\(.*\)$/\/\/\1/g" hiperf/include/virtual_runtime.h - # elf_parser.h $sed -i "/FRIEND_TEST/s/^\(.*\)$/\/\/\1/g" hiperf/include/report.h - # virtual_thread.h - # HIPERF_DEBUG 替换为 ALWAYSTRUE + $sed -i "s/HIPERF_DEBUG/ALWAYSTRUE/g" hiperf/include/virtual_thread.h $sed -i "/using __s8 = char;/a #define unw_word_t uint64_t" hiperf/include/nonlinux/linux/types.h $sed -i '/^void Report::PrepareConsole(/,/^}/ s/^.*$/\/\/&/; /^void Report::PrepareConsole(/,/return;/ s/^[[:blank:]]*/ /' hiperf/src/report.cpp $sed -i '/namespace HiPerf {/avoid Report::PrepareConsole(){ return;}' hiperf/src/report.cpp + $sed -i '/HITRACE_METER_NAME/s/^/\/\/ /' hiperf/src/callstack.cpp + $sed -i '/hitrace_meter.h/s/^/\/\/ /' hiperf/src/callstack.cpp + $sed -i '/dlfcn.h/s/^/\/\/ /' hiperf/src/callstack.cpp + $sed -i '/dfx_ark.h/s/^/\/\/ /' hiperf/src/callstack.cpp + $sed -i '/dfx_regs.h/s/^/\/\/ /' hiperf/src/callstack.cpp + $sed -i '/return DoUnwind2/s/^/\/\/ /' hiperf/src/callstack.cpp + $sed -i '/#if defined(is_ohos) && is_ohos/s/defined(is_ohos) && is_ohos/true/g' hiperf/src/virtual_runtime.cpp + $sed -i '/#if defined(is_ohos) && is_ohos/s/defined(is_ohos) && is_ohos/true/g' hiperf/include/virtual_runtime.h fi fi diff --git a/trace_streamer/prebuilts/patch_hiperf/1_pengjingtong.diff b/trace_streamer/prebuilts/patch_hiperf/1_pengjingtong.diff deleted file mode 100755 index 4b2878f6d33428f67fb7fde481b31dee35c758be..0000000000000000000000000000000000000000 --- a/trace_streamer/prebuilts/patch_hiperf/1_pengjingtong.diff +++ /dev/null @@ -1,879 +0,0 @@ -diff --git a/BUILD.gn b/BUILD.gn -index 7f6d1a5..f6a020e 100644 ---- a/BUILD.gn -+++ b/BUILD.gn -@@ -140,6 +140,7 @@ sources_platform_linux = [ - "./src/subcommand_stat.cpp", - "./src/subcommand_record.cpp", - "./src/subcommand_list.cpp", -+ "./src/unique_stack_table.cpp", - ] - - common_deps = [ -diff --git a/include/perf_event_record.h b/include/perf_event_record.h -index ef53eee..5630375 100644 ---- a/include/perf_event_record.h -+++ b/include/perf_event_record.h -@@ -32,6 +32,7 @@ - #include "mem_map_item.h" - #include "perf_record_format.h" - #include "utilities.h" -+#include "unique_stack_table.h" - - namespace OHOS { - namespace Developtools { -@@ -237,7 +238,8 @@ public: - // used for data_.ips replace (ReplaceWithCallStack) - std::vector ips_; - std::vector callFrames_; -- -+ StackId StackId_; -+ bool removeStack_; - // referenced input(p) in PerfRecordSample, require caller keep input(p) together - PerfRecordSample(uint8_t *p, const perf_event_attr &attr); - bool GetBinary(std::vector &buf) const override; -diff --git a/include/perf_file_format.h b/include/perf_file_format.h -index 4d76db8..96b269d 100644 ---- a/include/perf_file_format.h -+++ b/include/perf_file_format.h -@@ -59,8 +59,8 @@ enum class FEATURE { - HIPERF_WORKLOAD_CMD, - HIPERF_RECORD_TIME, - HIPERF_CPU_OFF, -- HIPERF_LAST_FEATURE = HIPERF_CPU_OFF, -- -+ HIPERF_FILES_UNISTACK_TABLE, -+ HIPERF_LAST_FEATURE = HIPERF_FILES_UNISTACK_TABLE, - FEATURE_MAX_BITS = 256, - }; - -@@ -216,6 +216,17 @@ private: - const size_t MAX_SYMBOLS_NUMBER = 10000; - }; - -+class PerfFileSectionUniStackTable : public PerfFileSection { -+public: -+ PerfFileSectionUniStackTable(FEATURE id, -+ const ProcessStackMap *table) -+ : PerfFileSection(id), processStackTable_(table) {} -+private: -+ const ProcessStackMap *processStackTable_; -+ size_t GetSize(); -+ bool GetBinary(char *buf, size_t size); -+}; -+ - // NRCPUS: A structure defining the number of CPUs. - class PerfFileSectionNrCpus : public PerfFileSection { - uint32_t nrCpusAvailable_; /* CPUs not yet onlined */ -diff --git a/include/perf_file_writer.h b/include/perf_file_writer.h -index 1bb32b9..66a19c6 100644 ---- a/include/perf_file_writer.h -+++ b/include/perf_file_writer.h -@@ -52,6 +52,7 @@ public: - bool AddU64Feature(FEATURE feature, uint64_t v); - bool AddBoolFeature(FEATURE feature); - bool AddSymbolsFeature(const std::vector> &); -+ bool AddUniStackTableFeature(const ProcessStackMap *table); - // close file - bool Close(); - -diff --git a/include/subcommand_record.h b/include/subcommand_record.h -index 835950c..126f217 100644 ---- a/include/subcommand_record.h -+++ b/include/subcommand_record.h -@@ -166,6 +166,8 @@ public: - " pause: pause sampling\n" - " resume: resume sampling\n" - " stop: stop sampling\n" -+ " --dedup_stack\n" -+ " Remove duplicated stacks in perf record, conflicts with -a, only restrain using with -p\n" - ) - // clang-format on - { -@@ -241,6 +243,7 @@ private: - std::string controlCmd_ = {}; - bool isFifoServer_ = false; - bool isFifoClient_ = false; -+ bool dedupStack_ = false; - bool ProcessControl(); - bool CreateFifoServer(); - bool SendFifoAndWaitReply(const std::string &cmd, const std::chrono::milliseconds &timeOut); -@@ -281,7 +284,7 @@ private: - #endif - - bool CollectionSymbol(std::unique_ptr record); -- -+ void CollectSymbol(PerfRecordSample *sample); - bool SetPerfLimit(const std::string& file, int value, std::function const& cmd, - const std::string& param); - bool SetPerfCpuMaxPercent(); -@@ -299,8 +302,8 @@ private: - - VirtualRuntime virtualRuntime_; - #if USE_COLLECT_SYMBOLIC -- std::unordered_set kernelSymbolsHits_; -- std::unordered_map> userSymbolsHits_; -+ kSymbolsHits kernelSymbolsHits_; -+ uSymbolsHits userSymbolsHits_; - void SymbolicHits(); - #endif - -diff --git a/include/unique_stack_table.h b/include/unique_stack_table.h -new file mode 100644 -index 0000000..babae1d ---- /dev/null -+++ b/include/unique_stack_table.h -@@ -0,0 +1,135 @@ -+/* -+ * Copyright (c) 2021-2022 Huawei Device Co., Ltd. -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ */ -+#ifndef HIEPRF_UNIQUE_STACK_TABLE_H -+#define HIEPRF_UNIQUE_STACK_TABLE_H -+#include "utilities.h" -+#include -+#include -+#include -+namespace OHOS { -+namespace Developtools { -+namespace HiPerf { -+ -+#define ADDR_BIT_LENGTH 40 -+#define IDX_BIT_LENGTH 20 -+#define KERNEL_FLAG_BIT_LENGTH 4 -+#define COLLISION_GROWTH_RATE 3 -+#define EXPAND_FACTOR 2 -+#define NR_BIT_LENGTH 44 -+// 1000 4kpage 4M RAM -+constexpr uint32_t INITIAL_TABLE_SIZE = 4 * 1024 * 1024; -+constexpr uint32_t MAX_TABLE_SIZE = 16 * 1024 * 1024; -+constexpr uint64_t IP_IN_KERNEL = 1ull << 63; -+constexpr uint64_t HEAD_NODE_INDEX = 0; -+// FFFFFF0000000000 -+constexpr uint64_t KERNEL_PREFIX = 0xFFFFFFull << 40; -+ -+// align -+#pragma pack(push, 4) -+ -+union Node { -+ uint64_t value; -+ struct { -+ uint64_t ip : ADDR_BIT_LENGTH; -+ uint64_t prevIdx : IDX_BIT_LENGTH; -+ uint64_t inKernel : KERNEL_FLAG_BIT_LENGTH; -+ } section; -+}; -+ -+union StackId { -+ uint64_t value; -+ struct { -+ uint64_t id : IDX_BIT_LENGTH; -+ uint64_t nr : NR_BIT_LENGTH; -+ } section; -+}; -+ -+#pragma pack(pop) -+static_assert(sizeof(Node) == 8, "Node size must be 8 byte"); -+ -+class UniqueStackTable { -+public: -+ bool Init(uint32_t size); -+ UniqueStackTable(pid_t pid) : pid(pid) -+ { -+ Init(INITIAL_TABLE_SIZE); -+ } -+ -+ UniqueStackTable() : pid(getpid()) -+ { -+ Init(INITIAL_TABLE_SIZE); -+ } -+ -+ UniqueStackTable(pid_t pid, uint32_t size) : pid(pid) -+ { -+ Init(size); -+ } -+ // static instance can not be free. -+ ~UniqueStackTable() { -+ if (table != nullptr) { -+ free(table); -+ } -+ table = nullptr; -+ } -+ -+ bool PutIpInSlot(uint64_t thisIp, uint64_t &prevIdx); -+ bool PutIpsInTable(StackId *stackId, u64 *ips, u64 nr); -+ bool GetIpsByStackId(StackId stackId, u64 *ips); -+ size_t GetWriteSize(); -+ uint32_t GetPid() -+ { -+ return pid; -+ } -+ -+ uint32_t GetTabelSize() -+ { -+ return tableSize; -+ } -+ -+ uint32_t GetUntouchableNode() -+ { -+ return untouchableNode; -+ } -+ -+ std::vector& GetUsedIndexes() -+ { -+ return usedIndexes_; -+ } -+ -+ Node* GetHeadNode() -+ { -+ return table; -+ } -+private: -+ u32 pid; -+ uint32_t tableSize; -+ uint32_t maxSize = MAX_TABLE_SIZE; -+ uint32_t numNodes; -+ Node *table = nullptr; -+ uint32_t untouchableNode = 0; -+ uint8_t maxCollide = 22; -+ double loadFactor = 0.66; -+ std::vector usedIndexes_; -+ uint64_t slots; -+ uint64_t hashMultiplier; -+ bool EnsureCapacity(); -+ bool Resize(); -+}; -+ -+using ProcessStackMap = std::map>; -+} // namespace HiPerf -+} // namespace Developtools -+} // namespace OHOS -+#endif // HIEPRF_UNIQUE_STACK_TABLE_H -\ No newline at end of file -diff --git a/include/virtual_runtime.h b/include/virtual_runtime.h -index c42c9e3..f8f62dc 100644 ---- a/include/virtual_runtime.h -+++ b/include/virtual_runtime.h -@@ -21,7 +21,6 @@ - #include "perf_event_record.h" - #include "symbols_file.h" - #include "virtual_thread.h" -- - namespace OHOS { - namespace Developtools { - namespace HiPerf { -@@ -37,7 +36,8 @@ DSO) - Then find the corresponding symbol in the corresponding elf symbol file according to the offset - recorded in the corresponding mmap. - */ -- -+using kSymbolsHits = std::unordered_set; -+using uSymbolsHits = std::unordered_map>; - class VirtualRuntime { - public: - VirtualRuntime(bool onDevice = true); -@@ -47,8 +47,10 @@ public: - // case 1. some mmap will be create when it read mmaps for each new process (from record sample) - - using RecordCallBack = std::function)>; -- void SetRecordMode(RecordCallBack recordCallBack); -+ using CollectSymbolCallBack = std::function; - -+ void SetRecordMode(RecordCallBack recordCallBack); -+ void SetCollectSymbolCallBack(CollectSymbolCallBack collectSymboolCallBack); - // this both used in report and record follow - // it process the record, and rebuild the trhread maps - // It internally determines whether to go to the Record process (which will generate virtual -@@ -76,6 +78,11 @@ public: - return symbolsFiles_; - } - -+ const ProcessStackMap* GetUniStackTable() -+ { -+ return &processStackMap_; -+ } -+ - void SetCallStackExpend(size_t mergeLevel = 0) - { - callstackMergeLevel_ = mergeLevel; -@@ -87,6 +94,11 @@ public: - disableUnwind_ = disableUnwind; - } - -+ void SetDedupStack() -+ { -+ dedupStack_ = true; -+ } -+ - const Symbol GetSymbol(uint64_t ip, pid_t pid, pid_t tid, - const perf_callchain_context &context = PERF_CONTEXT_MAX); - -@@ -101,7 +113,8 @@ public: - void UpdateFromPerfData(const std::vector &); - void UnwindFromRecord(PerfRecordSample &recordSample); - std::string ReadThreadName(pid_t tid, bool isThread); -- -+ void CollectDedupSymbol(kSymbolsHits &kernelSymbolsHits, -+ uSymbolsHits &userSymbolsHits); - // debug time - #ifdef HIPERF_DEBUG_TIME - std::chrono::microseconds updateSymbolsTimes_ = std::chrono::microseconds::zero(); -@@ -120,13 +133,16 @@ public: - - private: - bool disableUnwind_ = true; -+ bool dedupStack_ = false; - size_t callstackMergeLevel_ = 1; - CallStack callstack_; - // pid map with user space thread - std::map userSpaceThreadMap_; - // not pid , just memmap - std::vector kernelSpaceMemMaps_; -+ ProcessStackMap processStackMap_; - RecordCallBack recordCallBack_; -+ CollectSymbolCallBack collectSymbolCallBack_; - std::vector> symbolsFiles_; - enum SymbolCacheLimit : std::size_t { - KERNEL_SYMBOL_CACHE_LIMIT = 4000, -@@ -144,7 +160,7 @@ private: - void UpdateFromRecord(PerfRecordMmap &recordMmap); - void UpdateFromRecord(PerfRecordMmap2 &recordMmap2); - void UpdateFromRecord(PerfRecordComm &recordComm); -- -+ void DedupFromRecord(PerfRecordSample *recordSample); - // threads - VirtualThread &UpdateThread(pid_t pid, pid_t tid, const std::string name = ""); - VirtualThread &CreateThread(pid_t pid, pid_t tid); -diff --git a/interfaces/innerkits/native/include/hiperf_client.h b/interfaces/innerkits/native/include/hiperf_client.h -index 84ad586..e7f2d25 100644 ---- a/interfaces/innerkits/native/include/hiperf_client.h -+++ b/interfaces/innerkits/native/include/hiperf_client.h -@@ -69,6 +69,7 @@ static const std::string ArgAppPackage = "--app"; - static const std::string ArgClockId = "--clockid"; - static const std::string ArgVecBranchSampleTypes = "-j"; - static const std::string ArgMmapPages = "-m"; -+static const std::string ArgDedupStack = "--dedup_stack"; - - static const int DEFAULT_DURATION_TIME = 10; - static const int DEFAULT_FREQUENCY_TIME = 100; -diff --git a/src/perf_event_record.cpp b/src/perf_event_record.cpp -index fa8883d..43f3edf 100644 ---- a/src/perf_event_record.cpp -+++ b/src/perf_event_record.cpp -@@ -305,7 +305,7 @@ bool PerfRecordSample::GetBinary(std::vector &buf) const - PushToBinary2(sampleType_ & PERF_SAMPLE_CPU, p, data_.cpu, data_.res); - PushToBinary(sampleType_ & PERF_SAMPLE_PERIOD, p, data_.period); - PushToBinary(sampleType_ & PERF_SAMPLE_CALLCHAIN, p, data_.nr); -- if (data_.nr > 0) { -+ if (data_.nr > 0 && !removeStack_) { - std::copy(data_.ips, data_.ips + data_.nr, reinterpret_cast(p)); - p += data_.nr * sizeof(u64); - } -@@ -330,7 +330,7 @@ bool PerfRecordSample::GetBinary(std::vector &buf) const - p += data_.stack_size * sizeof(u8); - PushToBinary(true, p, data_.dyn_size); - } -- -+ PushToBinary(removeStack_, p, StackId_.value); - return true; - } - -diff --git a/src/perf_file_format.cpp b/src/perf_file_format.cpp -index 967a38e..e57dab5 100644 ---- a/src/perf_file_format.cpp -+++ b/src/perf_file_format.cpp -@@ -221,6 +221,17 @@ size_t PerfFileSectionSymbolsFiles::GetSize() - return size; - } - -+size_t PerfFileSectionUniStackTable::GetSize() -+{ -+ size_t size = 0; -+ // section header info size -+ size += sizeof(uint32_t); // how many tables/process -+ for (auto it = processStackTable_->begin(); it != processStackTable_->end(); ++it) { -+ size += it->second->GetWriteSize(); -+ } -+ return size; -+} -+ - PerfFileSectionSymbolsFiles::PerfFileSectionSymbolsFiles(FEATURE id, const char *buf, size_t size) - : PerfFileSection(id) - { -@@ -301,6 +312,28 @@ bool PerfFileSectionSymbolsFiles::GetBinary(char *buf, size_t size) - return true; - } - -+bool PerfFileSectionUniStackTable::GetBinary(char *buf, size_t size) -+{ -+ HLOG_ASSERT(size >= GetSize()); -+ Init(buf, size); -+ Write(uint32_t(processStackTable_->size())); -+ for (auto it = processStackTable_->begin(); it != processStackTable_->end(); ++it) { -+ const auto &table = it->second; -+ Write(table->GetPid()); -+ Write(table->GetTabelSize()); -+ const auto &idxs = table->GetUsedIndexes(); -+ Write(uint32_t(idxs.size())); -+ Node *head = table->GetHeadNode(); -+ Node *node = nullptr; -+ for (const auto idx : idxs) { -+ node = head + idx; -+ Write(idx); -+ Write(node->value); -+ } -+ } -+ return true; -+} -+ - PerfFileSectionNrCpus::PerfFileSectionNrCpus(FEATURE id, const char *buf, size_t size) - : PerfFileSection(id) - { -diff --git a/src/perf_file_writer.cpp b/src/perf_file_writer.cpp -index addd72b..f6a3d77 100644 ---- a/src/perf_file_writer.cpp -+++ b/src/perf_file_writer.cpp -@@ -451,6 +451,15 @@ bool PerfFileWriter::AddU64Feature(FEATURE feature, uint64_t v) - return true; - } - -+bool PerfFileWriter::AddUniStackTableFeature(const ProcessStackMap *table) -+{ -+ const FEATURE feature = FEATURE::HIPERF_FILES_UNISTACK_TABLE; -+ featureSections_.emplace_back( -+ std::make_unique(feature, table)); -+ header_.features[(int)feature / BITS_IN_BYTE] |= 1 << ((int)feature % BITS_IN_BYTE); -+ return true; -+} -+ - bool PerfFileWriter::AddSymbolsFeature( - const std::vector> &symbolsFiles) - { -diff --git a/src/subcommand_record.cpp b/src/subcommand_record.cpp -index 75bd95e..ec429d7 100644 ---- a/src/subcommand_record.cpp -+++ b/src/subcommand_record.cpp -@@ -266,7 +266,13 @@ bool SubCommandRecord::GetOptions(std::vector &args) - if (!Option::GetOptionValue(args, "--control", controlCmd_)) { - return false; - } -- -+ if (!Option::GetOptionValue(args, "--dedup_stack", dedupStack_)) { -+ return false; -+ } -+ if (targetSystemWide_ && dedupStack_) { -+ printf("-a option is conflict with --dedup_stack.\n"); -+ return false; -+ } - if (!Option::GetOptionTrackedCommand(args, trackedCommand_)) { - return false; - } -@@ -805,6 +811,12 @@ bool SubCommandRecord::PrepareVirtualRuntime() - // prepare from kernel and ko - virtualRuntime_.UpdateKernelSpaceMaps(); - virtualRuntime_.UpdateKernelModulesSpaceMaps(); -+ if (dedupStack_) { -+ virtualRuntime_.SetDedupStack(); -+ auto collectSymbol = -+ std::bind(&SubCommandRecord::CollectSymbol, this, std::placeholders::_1); -+ virtualRuntime_.SetCollectSymbolCallBack(collectSymbol); -+ } - return true; - } - -@@ -1472,37 +1484,42 @@ bool SubCommandRecord::CollectionSymbol(std::unique_ptr record) - if (record->GetType() == PERF_RECORD_SAMPLE) { - PerfRecordSample *sample = static_cast(record.get()); - #if USE_COLLECT_SYMBOLIC -- perf_callchain_context context = record->inKernel() ? PERF_CONTEXT_KERNEL -- : PERF_CONTEXT_USER; -- // if no nr use ip -- if (sample->data_.nr == 0) { -- if (context == PERF_CONTEXT_KERNEL) { -- kernelSymbolsHits_.insert(sample->data_.ip); -- } else { -- userSymbolsHits_[sample->data_.pid].insert(sample->data_.ip); -- } -+ CollectSymbol(sample); -+#else -+ virtualRuntime_.SymbolicRecord(*sample); -+#endif -+ } -+ return true; -+} -+ -+void SubCommandRecord::CollectSymbol(PerfRecordSample *sample) -+{ -+ perf_callchain_context context = sample->inKernel() ? PERF_CONTEXT_KERNEL -+ : PERF_CONTEXT_USER; -+ // if no nr use ip -+ if (sample->data_.nr == 0) { -+ if (context == PERF_CONTEXT_KERNEL) { -+ kernelSymbolsHits_.insert(sample->data_.ip); - } else { -- for (u64 i = 0; i < sample->data_.nr; i++) { -- if (sample->data_.ips[i] >= PERF_CONTEXT_MAX) { -- if (sample->data_.ips[i] == PERF_CONTEXT_KERNEL) { -- context = PERF_CONTEXT_KERNEL; -- } else { -- context = PERF_CONTEXT_USER; -- } -+ userSymbolsHits_[sample->data_.pid].insert(sample->data_.ip); -+ } -+ } else { -+ for (u64 i = 0; i < sample->data_.nr; i++) { -+ if (sample->data_.ips[i] >= PERF_CONTEXT_MAX) { -+ if (sample->data_.ips[i] == PERF_CONTEXT_KERNEL) { -+ context = PERF_CONTEXT_KERNEL; -+ } else { -+ context = PERF_CONTEXT_USER; -+ } -+ } else { -+ if (context == PERF_CONTEXT_KERNEL) { -+ kernelSymbolsHits_.insert(sample->data_.ips[i]); - } else { -- if (context == PERF_CONTEXT_KERNEL) { -- kernelSymbolsHits_.insert(sample->data_.ips[i]); -- } else { -- userSymbolsHits_[sample->data_.pid].insert(sample->data_.ips[i]); -- } -+ userSymbolsHits_[sample->data_.pid].insert(sample->data_.ips[i]); - } - } - } --#else -- virtualRuntime_.SymbolicRecord(*sample); --#endif - } -- return true; - } - - // finish writing data file, then close file -@@ -1519,8 +1536,12 @@ bool SubCommandRecord::FinishWriteRecordFile() - virtualRuntime_.UpdateKernelModulesSymbols(); - #endif - HLOGD("Load user symbols"); -- fileWriter_->ReadDataSection( -- std::bind(&SubCommandRecord::CollectionSymbol, this, std::placeholders::_1)); -+ if (dedupStack_) { -+ virtualRuntime_.CollectDedupSymbol(kernelSymbolsHits_, userSymbolsHits_); -+ } else { -+ fileWriter_->ReadDataSection( -+ std::bind(&SubCommandRecord::CollectionSymbol, this, std::placeholders::_1)); -+ } - #if USE_COLLECT_SYMBOLIC - SymbolicHits(); - #endif -@@ -1536,7 +1557,10 @@ bool SubCommandRecord::FinishWriteRecordFile() - #endif - } - #endif -- -+ if (dedupStack_ && -+ !fileWriter_->AddUniStackTableFeature(virtualRuntime_.GetUniStackTable())) { -+ return false; -+ } - if (!fileWriter_->Close()) { - HLOGE("Fail to close record file %s", outputFilename_.c_str()); - return false; -diff --git a/src/unique_stack_table.cpp b/src/unique_stack_table.cpp -new file mode 100644 -index 0000000..1ea5a7a ---- /dev/null -+++ b/src/unique_stack_table.cpp -@@ -0,0 +1,183 @@ -+/* -+ * Copyright (c) 2021-2022 Huawei Device Co., Ltd. -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ */ -+#include "unique_stack_table.h" -+namespace OHOS { -+namespace Developtools { -+namespace HiPerf { -+ -+bool UniqueStackTable::Init(uint32_t size) -+{ -+ if (size < INITIAL_TABLE_SIZE || size > MAX_TABLE_SIZE) { -+ return false; -+ } -+ tableSize = size; -+ numNodes = ((tableSize / sizeof(Node)) >> 1) << 1; // make it even. -+ slots = numNodes - untouchableNode - 1; -+ hashMultiplier = ((numNodes - untouchableNode) / (maxCollide * 2 + 1)); -+ table = (Node*)malloc(tableSize); -+ return table != nullptr; -+} -+ -+bool UniqueStackTable::Resize() -+{ -+ Node *oldTable = table; -+ uint32_t oldSize = tableSize; -+ uint32_t oldNumNodes = numNodes; -+ tableSize <<= EXPAND_FACTOR; -+ Node *newTable = (Node*)malloc(tableSize); -+ table = newTable; -+ maxCollide += COLLISION_GROWTH_RATE; -+ // ignore unused space, because can't ruin the hash distance -+ if (memmove(newTable, oldTable, oldSize) != 0) { -+ HLOGD("resize failed"); -+ return false; -+ } -+ untouchableNode = oldNumNodes; -+ slots = numNodes - untouchableNode - 1; -+ hashMultiplier = ((numNodes - untouchableNode) / (maxCollide * 2 + 1)); -+ if (oldTable != nullptr) { -+ free(oldTable); -+ oldTable = nullptr; -+ } -+ return true; -+} -+ -+bool UniqueStackTable::EnsureCapacity() -+{ -+ bool slotSufficient = loadFactor * (double)numNodes > (double)usedIndexes_.size(); -+ if (slotSufficient) { -+ return true; -+ } else { -+ if (tableSize == maxSize) { -+ return false; -+ } else { -+ return Resize(); -+ } -+ } -+} -+ -+bool UniqueStackTable::PutIpInSlot(uint64_t thisIp, uint64_t &prevIdx) -+{ -+ Node *node = nullptr; -+ // hash -+ uint64_t curIpIdx = untouchableNode + (((prevIdx << 4) ^ (thisIp >> 2)) % slots); -+ uint8_t collisions = maxCollide; -+ while (collisions--) { -+ node = table + curIpIdx; -+ // slot empty, put in -+ if (node->value == 0 && node->section.prevIdx == 0) { -+ node->section.ip = thisIp; -+ node->section.prevIdx = prevIdx; -+ node->section.inKernel = !!(thisIp & IP_IN_KERNEL); -+ usedIndexes_.emplace_back(uint32_t(curIpIdx)); -+ prevIdx = curIpIdx; -+ return true; -+ } -+ -+ // slot not empty -+ u64 nodeIp = node->section.inKernel ? -+ (node->section.ip | KERNEL_PREFIX) : node->section.ip; -+ // same node have same ip and same parent -+ if (nodeIp == thisIp && node->section.prevIdx == prevIdx) { -+ prevIdx = curIpIdx; -+ return true; -+ } -+ -+ // slot not empty, not same node, collide -+ curIpIdx += collisions * hashMultiplier + 1; -+ if (curIpIdx >= numNodes) { -+ curIpIdx -= numNodes - untouchableNode; -+ } -+ } -+ if (collisions < 0) { -+ return false; -+ } -+ return true; -+} -+ -+bool UniqueStackTable::PutIpsInTable(StackId *stackId, u64 *ips, u64 nr) -+{ -+ if (!EnsureCapacity()) { -+ return false; -+ } -+ u64 thisIp; -+ uint64_t prevIdx {HEAD_NODE_INDEX}; -+ // reduce head node place -+ uint8_t collisions= 0; -+ u64 left = 0; -+ uint8_t notUsedIpNum = 0; -+ for (; left < nr; ++left) { -+ thisIp = ips[left]; -+ if(!PutIpInSlot(thisIp, prevIdx)) { -+ return false; -+ } -+ } -+ stackId->section.id = prevIdx; -+ stackId->section.nr = nr; -+ return true; -+} -+ -+size_t UniqueStackTable::GetWriteSize() -+{ -+ size_t size = 0; -+ size += sizeof(pid); -+ size += sizeof(tableSize); -+ uint32_t usedNodes = usedIndexes_.size(); -+ size += sizeof(usedNodes); -+ size += usedNodes * sizeof(uint32_t); // key index -+ size += usedNodes * sizeof(uint64_t); // node value -+ return size; -+} -+ -+/** -+ * u64 ips[stackId.section.nr]; -+ * stackId contains, tailIdx and nr of this ips chain. -+*/ -+bool UniqueStackTable::GetIpsByStackId(StackId stackId, u64 *ips) -+{ -+ uint64_t nr = stackId.section.nr; -+ uint64_t tailIdx = stackId.section.id; -+ uint32_t foundFrames = 0; -+ uint64_t prevIdx; -+ // correct tailIdx -+ if (tailIdx < numNodes) { -+ Node *node = table + tailIdx; -+ prevIdx = node->section.prevIdx; -+ // nr for guarantee no endless loop. -+ while (foundFrames < nr) { -+ if (prevIdx == HEAD_NODE_INDEX) { -+ HLOGD("earlier reach head, error"); -+ return false; -+ } -+ if (node->value == 0) { -+ HLOGD("empty slot, error."); -+ return false; -+ } -+ ips[foundFrames++] = -+ node->section.inKernel ? (node->section.ip | KERNEL_PREFIX) : node->section.ip; -+ // jump to prev -+ node = table + prevIdx; -+ prevIdx = node->section.prevIdx; -+ } -+ } else { -+ return false; -+ } -+ return true; -+} -+ -+ -+} // namespace HiPerf -+} // namespace Developtools -+} // namespace OHOS -\ No newline at end of file -diff --git a/src/virtual_runtime.cpp b/src/virtual_runtime.cpp -index e14b056..29d87f0 100644 ---- a/src/virtual_runtime.cpp -+++ b/src/virtual_runtime.cpp -@@ -268,6 +268,63 @@ void VirtualRuntime::UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offs - } - } - -+void VirtualRuntime::DedupFromRecord(PerfRecordSample *recordSample) -+{ -+ u32 pid = recordSample->data_.pid; -+ u64 nr = recordSample->data_.nr; -+ u64 *ips = recordSample->data_.ips; -+ StackId stackId; -+ stackId.value = 0; -+ auto entry = processStackMap_.find(pid); -+ std::shared_ptr table = nullptr; -+ if (entry != processStackMap_.end()) { -+ table = entry->second; -+ } else { -+ table = std::make_shared(pid); -+ processStackMap_[pid] = table; -+ } -+ if (!table->PutIpsInTable(&stackId, ips, nr) || -+ stackId.value == 0) { -+ collectSymbolCallBack_(recordSample); -+ return; -+ } -+ recordSample->StackId_.value = stackId.value; -+ recordSample->header.size -= (sizeof(u64) * nr + sizeof(stackId)); -+ recordSample->data_.nr = 0; -+ recordSample->data_.ips = nullptr; -+ recordSample->removeStack_ = true; -+} -+ -+void VirtualRuntime::CollectDedupSymbol(kSymbolsHits &kernelSymbolsHits, -+ uSymbolsHits &userSymbolsHits) -+{ -+ Node *node = nullptr; -+ Node *head = nullptr; -+ u32 pid; -+ for (const auto &tableEntry : processStackMap_) { -+ const auto &table = tableEntry.second; -+ pid = table->GetPid(); -+ head = table->GetHeadNode(); -+ const auto &idxes = table->GetUsedIndexes(); -+ for (const auto idx : idxes) { -+ node = head + idx; -+ if (node->value != 0) { -+ if (node->section.inKernel) { -+ uint64_t ip = node->section.ip | KERNEL_PREFIX; -+ if (ip == PERF_CONTEXT_KERNEL || ip == PERF_CONTEXT_USER) { -+ continue; -+ } -+ kernelSymbolsHits.insert(ip); -+ } else { -+ userSymbolsHits[pid].insert(node->section.ip); -+ } -+ } else { -+ HLOGD("node value error 0x%x", idx); -+ } -+ } -+ } -+} -+ - void VirtualRuntime::UpdateFromRecord(PerfEventRecord &record) - { - #ifdef HIPERF_DEBUG_TIME -@@ -383,7 +440,10 @@ void VirtualRuntime::UnwindFromRecord(PerfRecordSample &recordSample) - #ifdef HIPERF_DEBUG_TIME - unwindFromRecordTimes_ += duration_cast(steady_clock::now() - startTime); - #endif -- -+ // we will not do this in non record mode. -+ if (dedupStack_ && recordCallBack_) { -+ DedupFromRecord(&recordSample); -+ } - // we will not do this in record mode - if (recordCallBack_ == nullptr) { - // find the symbols , reabuild frame info -@@ -459,6 +519,11 @@ void VirtualRuntime::SetRecordMode(RecordCallBack recordCallBack) - recordCallBack_ = recordCallBack; - } - -+void VirtualRuntime::SetCollectSymbolCallBack(CollectSymbolCallBack collectSymbolCallBack) -+{ -+ collectSymbolCallBack_ = collectSymbolCallBack; -+} -+ - void VirtualRuntime::UpdateSymbols(std::string fileName) - { - HLOGD("try to find symbols for file: %s", fileName.c_str()); -@@ -586,7 +651,7 @@ const Symbol VirtualRuntime::GetUserSymbol(uint64_t ip, const VirtualThread &thr - bool VirtualRuntime::GetSymbolCache(uint64_t ip, Symbol &symbol, - const perf_callchain_context &context) - { -- if (context != PERF_CONTEXT_USER and kernelSymbolCache_.count(ip)) { -+ if (context != PERF_CONTEXT_USER and kernelSymbolCache_.count(symbol.fileVaddr_)) { - if (kernelSymbolCache_.find(symbol.fileVaddr_) == kernelSymbolCache_.end()) { - return false; - } diff --git a/trace_streamer/prebuilts/patch_hiperf/2_perf_dump_report_0913.patch b/trace_streamer/prebuilts/patch_hiperf/2_perf_dump_report_0913.patch deleted file mode 100755 index 7cc16f80f942b9b419f7930c04d7d5ae7b2402da..0000000000000000000000000000000000000000 --- a/trace_streamer/prebuilts/patch_hiperf/2_perf_dump_report_0913.patch +++ /dev/null @@ -1,814 +0,0 @@ -diff --git a/BUILD.gn b/BUILD.gn -index f6a020e..682ed10 100644 ---- a/BUILD.gn -+++ b/BUILD.gn -@@ -123,6 +123,7 @@ sources_platform_common = [ - "./src/report_json_file.cpp", - "./src/register.cpp", - "./src/callstack.cpp", -+ "./src/unique_stack_table.cpp", - ] - - if (hiperf_debug) { -@@ -140,7 +141,6 @@ sources_platform_linux = [ - "./src/subcommand_stat.cpp", - "./src/subcommand_record.cpp", - "./src/subcommand_list.cpp", -- "./src/unique_stack_table.cpp", - ] - - common_deps = [ -diff --git a/include/perf_event_record.h b/include/perf_event_record.h -index 5630375..1eab0c1 100644 ---- a/include/perf_event_record.h -+++ b/include/perf_event_record.h -@@ -238,14 +238,15 @@ public: - // used for data_.ips replace (ReplaceWithCallStack) - std::vector ips_; - std::vector callFrames_; -- StackId StackId_; -- bool removeStack_; -+ StackId StackId_ {0}; -+ bool removeStack_ {false}; -+ inline static bool dumpRemoveStack_ {false}; - // referenced input(p) in PerfRecordSample, require caller keep input(p) together - PerfRecordSample(uint8_t *p, const perf_event_attr &attr); - bool GetBinary(std::vector &buf) const override; - void DumpData(int indent = 0) const override; - void DumpLog(const std::string &prefix) const override; -- -+ void RecoverCallStack(); - // originalSize is use for expand callstack - void ReplaceWithCallStack(size_t originalSize = 0); - pid_t GetPid() const override; -diff --git a/include/perf_file_format.h b/include/perf_file_format.h -index 96b269d..8ca1df9 100644 ---- a/include/perf_file_format.h -+++ b/include/perf_file_format.h -@@ -111,6 +111,7 @@ static const std::vector extFeatureNames = { - "hiperf_workloader_cmd", - "hiperf_record_time", - "hiperf_cpu_off", -+ "hiperf_stack_table", - }; - static const std::vector featureNames = { - "unknown_feature", "tracing_data", "build_id", "hostname", "osrelease", -@@ -218,9 +219,11 @@ private: - - class PerfFileSectionUniStackTable : public PerfFileSection { - public: -+ std::vector uniStackTableInfos_; - PerfFileSectionUniStackTable(FEATURE id, - const ProcessStackMap *table) - : PerfFileSection(id), processStackTable_(table) {} -+ PerfFileSectionUniStackTable(FEATURE id, const char *buf, size_t size); - private: - const ProcessStackMap *processStackTable_; - size_t GetSize(); -diff --git a/include/subcommand_dump.h b/include/subcommand_dump.h -index 5a0cbd0..d9d135d 100644 ---- a/include/subcommand_dump.h -+++ b/include/subcommand_dump.h -@@ -110,6 +110,7 @@ private: - void DumpDataPortion(int indent = 0); - void DumpCallChain(int indent, std::unique_ptr &sample); - void DumpFeaturePortion(int indent = 0); -+ void DumpUniqueStackTableNode(int indent, const PerfFileSectionUniStackTable &uniStackTable); - void ExprotUserData(std::unique_ptr &record); - void ExprotUserStack(const PerfRecordSample &recordSample); - void PrintHeaderInfo(const int &indent); -diff --git a/include/subcommand_report.h b/include/subcommand_report.h -index bf97bc6..2596783 100644 ---- a/include/subcommand_report.h -+++ b/include/subcommand_report.h -@@ -125,6 +125,7 @@ private: - void LoadEventDesc(); - void ProcessSymbolsData(); - void LoadPerfDataCompleted(); -+ void ProcessUniStackTableData(); - - bool OutputReport(); - bool OutputStd(); -diff --git a/include/unique_stack_table.h b/include/unique_stack_table.h -index babae1d..57a71cf 100644 ---- a/include/unique_stack_table.h -+++ b/include/unique_stack_table.h -@@ -48,6 +48,18 @@ union Node { - } section; - }; - -+struct UniStackNode { -+ uint32_t index; -+ Node node; -+}; -+ -+struct UniStackTableInfo { -+ uint32_t pid; -+ uint32_t tableSize; -+ uint32_t numNodes; -+ std::vector nodes; -+}; -+ - union StackId { - uint64_t value; - struct { -@@ -67,10 +79,6 @@ public: - Init(INITIAL_TABLE_SIZE); - } - -- UniqueStackTable() : pid(getpid()) -- { -- Init(INITIAL_TABLE_SIZE); -- } - - UniqueStackTable(pid_t pid, uint32_t size) : pid(pid) - { -@@ -84,10 +92,12 @@ public: - table = nullptr; - } - -- bool PutIpInSlot(uint64_t thisIp, uint64_t &prevIdx); -- bool PutIpsInTable(StackId *stackId, u64 *ips, u64 nr); -- bool GetIpsByStackId(StackId stackId, u64 *ips); -+ uint64_t PutIpInSlot(uint64_t thisIp, uint64_t prevIdx); -+ uint64_t PutIpsInTable(StackId *stackId, u64 *ips, u64 nr); -+ bool GetIpsByStackId(StackId stackId, std::vector& ips); -+ bool ImportNode(uint32_t index, Node node); - size_t GetWriteSize(); -+ Node* GetFrame(uint64_t stackId); - uint32_t GetPid() - { - return pid; -diff --git a/include/virtual_runtime.h b/include/virtual_runtime.h -index f8f62dc..23ee260 100644 ---- a/include/virtual_runtime.h -+++ b/include/virtual_runtime.h -@@ -99,6 +99,8 @@ public: - dedupStack_ = true; - } - -+ void ImportUniqueStackNodes(const std::vector&); -+ - const Symbol GetSymbol(uint64_t ip, pid_t pid, pid_t tid, - const perf_callchain_context &context = PERF_CONTEXT_MAX); - -@@ -141,6 +143,8 @@ private: - // not pid , just memmap - std::vector kernelSpaceMemMaps_; - ProcessStackMap processStackMap_; -+ // just dump -+ std::map dumpStackMap_; - RecordCallBack recordCallBack_; - CollectSymbolCallBack collectSymbolCallBack_; - std::vector> symbolsFiles_; -@@ -178,7 +182,7 @@ private: - #endif - void SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip, - perf_callchain_context context); -- -+ bool RecoverCallStack(PerfRecordSample &recordSample); - std::vector symbolsPaths_; - - FRIEND_TEST(VirtualRuntimeTest, SetRecordMode); -diff --git a/src/perf_event_record.cpp b/src/perf_event_record.cpp -index 5904afe..d491e98 100644 ---- a/src/perf_event_record.cpp -+++ b/src/perf_event_record.cpp -@@ -167,6 +167,11 @@ void PerfRecordSample::DumpLog(const std::string &prefix) const - data_.reg_nr, data_.dyn_size, data_.time); - } - -+void PerfRecordSample::RecoverCallStack() -+{ -+ data_.ips = ips_.data(); -+} -+ - void PerfRecordSample::ReplaceWithCallStack(size_t originalSize) - { - // first we check if we have some user unwind stack need to merge ? -@@ -285,6 +290,9 @@ PerfRecordSample::PerfRecordSample(uint8_t *p, const perf_event_attr &attr) - p += data_.stack_size; - PopFromBinary(true, p, data_.dyn_size); - } -+ if (data_.nr == 0 && dumpRemoveStack_) { -+ PopFromBinary(true, p, StackId_.value); -+ } - } - - bool PerfRecordSample::GetBinary(std::vector &buf) const -@@ -364,10 +372,14 @@ void PerfRecordSample::DumpData(int indent) const - if (sampleType_ & PERF_SAMPLE_PERIOD) { - PrintIndent(indent, "period %" PRIu64 "\n", static_cast(data_.period)); - } -+ if (StackId_.section.id > 0 ) { -+ PrintIndent(indent, "stackid %" PRIu64 "\n", static_cast(StackId_.section.id)); -+ } - if (sampleType_ & PERF_SAMPLE_CALLCHAIN) { - bool userContext = false; -- PrintIndent(indent, "callchain nr=%lld\n", data_.nr); -- for (uint64_t i = 0; i < data_.nr; ++i) { -+ u64 nr = data_.nr ? data_.nr : ips_.size(); -+ PrintIndent(indent, "callchain nr=%lld\n", nr); -+ for (uint64_t i = 0; i < nr; ++i) { - std::string_view supplement = ""; - if ((sampleType_ & PERF_SAMPLE_STACK_USER) == 0 || data_.ips[i] != PERF_CONTEXT_USER) { - PrintIndent(indent + 1, "0x%llx%s\n", data_.ips[i], supplement.data()); -diff --git a/src/perf_file_format.cpp b/src/perf_file_format.cpp -index e57dab5..f5e359d 100644 ---- a/src/perf_file_format.cpp -+++ b/src/perf_file_format.cpp -@@ -312,6 +312,33 @@ bool PerfFileSectionSymbolsFiles::GetBinary(char *buf, size_t size) - return true; - } - -+PerfFileSectionUniStackTable::PerfFileSectionUniStackTable(FEATURE id, const char *buf, size_t size) -+ : PerfFileSection(id) -+{ -+ uint32_t processTableCount; -+ Init(buf, size); -+ if (!Read(processTableCount)) { -+ HLOGV("processTableCount read failed\n"); -+ return; -+ } else { -+ HLOGV("processTableCount %" PRIu32 "\n", processTableCount); -+ } -+ for (uint32_t i = 0; i < processTableCount; ++i) { -+ UniStackTableInfo& stackTable = uniStackTableInfos_.emplace_back(); -+ Read(stackTable.pid); -+ HLOGV("pid %" PRIu32 " ", stackTable.pid); -+ Read(stackTable.tableSize); -+ HLOGV("tableSize %" PRIu32 " ", stackTable.tableSize); -+ Read(stackTable.numNodes); -+ HLOGV("numNodes %" PRIu32 " ", stackTable.numNodes); -+ for (size_t i = 0; i < stackTable.numNodes; i++) { -+ UniStackNode& node = stackTable.nodes.emplace_back(); -+ Read(node.index); -+ Read(node.node.value); -+ } -+ } -+} -+ - bool PerfFileSectionUniStackTable::GetBinary(char *buf, size_t size) - { - HLOG_ASSERT(size >= GetSize()); -diff --git a/src/perf_file_reader.cpp b/src/perf_file_reader.cpp -index a4673cc..21879e4 100644 ---- a/src/perf_file_reader.cpp -+++ b/src/perf_file_reader.cpp -@@ -432,6 +432,10 @@ bool PerfFileReader::ReadFeatureSection() - } else if (feature == FEATURE::EVENT_DESC) { - perfFileSections_.emplace_back( - std::make_unique(feature, (char *)&buf[0], buf.size())); -+ } else if (feature == FEATURE::HIPERF_FILES_UNISTACK_TABLE) { -+ perfFileSections_.emplace_back( -+ std::make_unique(feature, (char *)&buf[0], buf.size())); -+ PerfRecordSample::dumpRemoveStack_ = true; - } else { - HLOGW("still not imp how to process with feature %d", feature); - } -diff --git a/src/subcommand_dump.cpp b/src/subcommand_dump.cpp -index aecc5d2..59a8536 100644 ---- a/src/subcommand_dump.cpp -+++ b/src/subcommand_dump.cpp -@@ -269,6 +269,11 @@ void SubCommandDump::DumpPrintFileHeader(int indent) - const PerfFileSectionSymbolsFiles *sectionSymbolsFiles = - static_cast(featureSection.get()); - vr_.UpdateFromPerfData(sectionSymbolsFiles->symbolFileStructs_); -+ } else if (featureSection.get()->featureId_ == FEATURE::HIPERF_FILES_UNISTACK_TABLE) { -+ const PerfFileSectionUniStackTable *sectionUniStackTable = -+ static_cast(featureSection.get()); -+ vr_.ImportUniqueStackNodes(sectionUniStackTable->uniStackTableInfos_); -+ vr_.SetDedupStack(); - } - } - } -@@ -538,12 +543,39 @@ void SubCommandDump::DumpFeaturePortion(int indent) - PrintIndent(LEVEL2, "get SymbolFiles failed\n"); - } - continue; -+ } else if (featureSection.get()->featureId_ == FEATURE::HIPERF_FILES_UNISTACK_TABLE) { -+ const PerfFileSectionUniStackTable *sectioniStackTable = -+ static_cast(const_cast(featureSection.get())); -+ if (sectioniStackTable != nullptr) { -+ DumpUniqueStackTableNode(LEVEL1, *sectioniStackTable); -+ } else { -+ PrintIndent(LEVEL2, "get StackTable failed\n"); -+ } -+ continue; - } else { - PrintIndent(LEVEL2, "not support dump this feature(%d).\n", featureSection.get()->featureId_); - } - } - } - -+void SubCommandDump:: DumpUniqueStackTableNode(int indent, const PerfFileSectionUniStackTable &uniStackTable) -+{ -+ int tableid = 0; -+ PrintIndent(LEVEL1, "TableNums: %zu\n\n", uniStackTable.uniStackTableInfos_.size()); -+ for (const auto& uniStackTableInfo : uniStackTable.uniStackTableInfos_) { -+ PrintIndent(LEVEL2, "tableid: %d\n", tableid); -+ PrintIndent(LEVEL2, "pid: %" PRIu32 "\n", uniStackTableInfo.pid); -+ PrintIndent(LEVEL2, "tableSize: %" PRIu32 "\n", uniStackTableInfo.tableSize); -+ PrintIndent(LEVEL2, "numNodes: %" PRIu32 "\n", uniStackTableInfo.numNodes); -+ PrintIndent(LEVEL2, "%-7s %-7s %-8s\n", "no", "index" , "node"); -+ for (size_t i = 0; i < uniStackTableInfo.nodes.size(); i++) { -+ UniStackNode node = uniStackTableInfo.nodes[i]; -+ PrintIndent(LEVEL2, "%-7zu %-7" PRIu32 " 0x%-8" PRIx64 "\n", i, node.index, node.node.value); -+ } -+ tableid++; -+ } -+} -+ - bool SubCommandDump::RegisterSubCommandDump() - { - return SubCommand::RegisterSubCommand("dump", std::make_unique()); -diff --git a/src/subcommand_report.cpp b/src/subcommand_report.cpp -index 7f52196..d942c94 100644 ---- a/src/subcommand_report.cpp -+++ b/src/subcommand_report.cpp -@@ -304,6 +304,17 @@ void SubCommandReport::ProcessSymbolsData() - } - } - -+void SubCommandReport::ProcessUniStackTableData() -+{ -+ auto featureSection = recordFileReader_->GetFeatureSection(FEATURE::HIPERF_FILES_UNISTACK_TABLE); -+ if (featureSection != nullptr) { -+ PerfFileSectionUniStackTable *sectioniStackTable = -+ static_cast(const_cast(featureSection)); -+ GetReport().virtualRuntime_.ImportUniqueStackNodes(sectioniStackTable->uniStackTableInfos_); -+ GetReport().virtualRuntime_.SetDedupStack(); -+ } -+} -+ - void SubCommandReport::UpdateReportInfo() - { - // get some meta info for protobuf -@@ -463,7 +474,7 @@ bool SubCommandReport::LoadPerfData() - - ProcessFeaturesData(); - ProcessSymbolsData(); -- -+ ProcessUniStackTableData(); - HLOGD("process record"); - recordFileReader_->ReadDataSection( - std::bind(&SubCommandReport::RecordCallBack, this, std::placeholders::_1)); -diff --git a/src/unique_stack_table.cpp b/src/unique_stack_table.cpp -index 1ea5a7a..f285452 100644 ---- a/src/unique_stack_table.cpp -+++ b/src/unique_stack_table.cpp -@@ -27,6 +27,10 @@ bool UniqueStackTable::Init(uint32_t size) - slots = numNodes - untouchableNode - 1; - hashMultiplier = ((numNodes - untouchableNode) / (maxCollide * 2 + 1)); - table = (Node*)malloc(tableSize); -+ if (memset_s(table, tableSize, 0, tableSize) != EOK) { -+ HLOGE("memset_s() failed"); -+ return false; -+ } - return table != nullptr; - } - -@@ -68,65 +72,63 @@ bool UniqueStackTable::EnsureCapacity() - } - } - --bool UniqueStackTable::PutIpInSlot(uint64_t thisIp, uint64_t &prevIdx) -+uint64_t UniqueStackTable::PutIpInSlot(uint64_t thisIp, uint64_t prevIdx) - { -- Node *node = nullptr; - // hash -- uint64_t curIpIdx = untouchableNode + (((prevIdx << 4) ^ (thisIp >> 2)) % slots); -+ uint64_t curIpIdx = untouchableNode + (((prevIdx << 4) ^ (thisIp >> 2)) % slots) +1; - uint8_t collisions = maxCollide; -+ Node node; -+ node.section.ip = thisIp; -+ node.section.prevIdx = prevIdx; - while (collisions--) { -- node = table + curIpIdx; -- // slot empty, put in -- if (node->value == 0 && node->section.prevIdx == 0) { -- node->section.ip = thisIp; -- node->section.prevIdx = prevIdx; -- node->section.inKernel = !!(thisIp & IP_IN_KERNEL); -+ Node* tableNode = (Node*)table + curIpIdx; -+ // empty case -+ if (tableNode->value == 0) { -+ node.section.inKernel = !!(thisIp & IP_IN_KERNEL); -+ tableNode->value = node.value; - usedIndexes_.emplace_back(uint32_t(curIpIdx)); -- prevIdx = curIpIdx; -- return true; -+ return curIpIdx; - } - -- // slot not empty -- u64 nodeIp = node->section.inKernel ? -- (node->section.ip | KERNEL_PREFIX) : node->section.ip; -- // same node have same ip and same parent -- if (nodeIp == thisIp && node->section.prevIdx == prevIdx) { -- prevIdx = curIpIdx; -- return true; -+ // already inerted -+ if (tableNode->value == node.value) { -+ return curIpIdx; - } - - // slot not empty, not same node, collide - curIpIdx += collisions * hashMultiplier + 1; -- if (curIpIdx >= numNodes) { -+ if (curIpIdx > numNodes) { - curIpIdx -= numNodes - untouchableNode; - } - } -- if (collisions < 0) { -- return false; -- } -- return true; -+ return 0; - } - --bool UniqueStackTable::PutIpsInTable(StackId *stackId, u64 *ips, u64 nr) -+uint64_t UniqueStackTable::PutIpsInTable(StackId *stackId, u64 *ips, u64 nr) - { - if (!EnsureCapacity()) { -- return false; -+ return 0; - } -- u64 thisIp; -- uint64_t prevIdx {HEAD_NODE_INDEX}; -- // reduce head node place -- uint8_t collisions= 0; -- u64 left = 0; -- uint8_t notUsedIpNum = 0; -- for (; left < nr; ++left) { -- thisIp = ips[left]; -- if(!PutIpInSlot(thisIp, prevIdx)) { -- return false; -+ int64_t reverseIndex = static_cast(nr); -+ uint64_t prev = 0; -+ while (--reverseIndex >= 0) { -+ uint64_t pc = ips[reverseIndex]; -+ if (pc == 0) { -+ continue; -+ } -+ prev = PutIpInSlot(pc, prev); -+ if (prev == 0) { -+ //printf("Insert fail \n"); -+ break; - } - } -- stackId->section.id = prevIdx; -- stackId->section.nr = nr; -- return true; -+ stackId->section.id = prev; -+ if (prev && reverseIndex < 0) { -+ stackId->section.nr = nr; -+ } else { -+ stackId->value = 0; -+ } -+ return prev; - } - - size_t UniqueStackTable::GetWriteSize() -@@ -141,39 +143,49 @@ size_t UniqueStackTable::GetWriteSize() - return size; - } - -+Node* UniqueStackTable::GetFrame(uint64_t stackId) -+{ -+ if (stackId >= numNodes) -+ { -+ return nullptr; -+ } -+ -+ return (Node *)&table[stackId]; -+} -+ - /** - * u64 ips[stackId.section.nr]; - * stackId contains, tailIdx and nr of this ips chain. - */ --bool UniqueStackTable::GetIpsByStackId(StackId stackId, u64 *ips) -+bool UniqueStackTable::GetIpsByStackId(StackId stackId, std::vector& ips) - { - uint64_t nr = stackId.section.nr; - uint64_t tailIdx = stackId.section.id; -- uint32_t foundFrames = 0; -- uint64_t prevIdx; -+ uint64_t prevIdx = 0; - // correct tailIdx -- if (tailIdx < numNodes) { -- Node *node = table + tailIdx; -- prevIdx = node->section.prevIdx; -- // nr for guarantee no endless loop. -- while (foundFrames < nr) { -- if (prevIdx == HEAD_NODE_INDEX) { -- HLOGD("earlier reach head, error"); -- return false; -- } -- if (node->value == 0) { -- HLOGD("empty slot, error."); -- return false; -- } -- ips[foundFrames++] = -- node->section.inKernel ? (node->section.ip | KERNEL_PREFIX) : node->section.ip; -- // jump to prev -- node = table + prevIdx; -- prevIdx = node->section.prevIdx; -+ Node *node = GetFrame(tailIdx); -+ if (node == nullptr) { -+ // should never happend -+ printf("Failed to find frame\n"); -+ return false; -+ } -+ while (node != nullptr && nr--) { -+ ips.push_back( -+ node->section.inKernel ? (node->section.ip | KERNEL_PREFIX) : node->section.ip); -+ if (node->section.prevIdx == HEAD_NODE_INDEX) { -+ break; - } -- } else { -+ node = GetFrame(node->section.prevIdx); -+ } -+ return true; -+} -+ -+bool UniqueStackTable::ImportNode(uint32_t index, Node node) -+{ -+ if (index >= tableSize) { - return false; - } -+ table[index].value = node.value; - return true; - } - -diff --git a/src/virtual_runtime.cpp b/src/virtual_runtime.cpp -index 29d87f0..09551d6 100644 ---- a/src/virtual_runtime.cpp -+++ b/src/virtual_runtime.cpp -@@ -289,7 +289,7 @@ void VirtualRuntime::DedupFromRecord(PerfRecordSample *recordSample) - return; - } - recordSample->StackId_.value = stackId.value; -- recordSample->header.size -= (sizeof(u64) * nr + sizeof(stackId)); -+ recordSample->header.size -= (sizeof(u64) * nr - sizeof(stackId)); - recordSample->data_.nr = 0; - recordSample->data_.ips = nullptr; - recordSample->removeStack_ = true; -@@ -380,6 +380,19 @@ void VirtualRuntime::SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t - recordSample.callFrames_.back().ToSymbolString().c_str()); - } - -+bool VirtualRuntime::RecoverCallStack(PerfRecordSample &recordSample) -+{ -+ auto StackTable = dumpStackMap_.find(recordSample.data_.pid); -+ if (StackTable == dumpStackMap_.end()) { -+ HLOGV("not found %" PRIu32 " pid", recordSample.data_.pid); -+ return false; -+ } -+ recordSample.ips_.clear(); -+ StackTable->second.GetIpsByStackId(recordSample.StackId_, recordSample.ips_); -+ recordSample.RecoverCallStack(); -+ return true; -+} -+ - void VirtualRuntime::SymbolicRecord(PerfRecordSample &recordSample) - { - #ifdef HIPERF_DEBUG_TIME -@@ -388,10 +401,11 @@ void VirtualRuntime::SymbolicRecord(PerfRecordSample &recordSample) - // Symbolic the Call Stack - recordSample.callFrames_.clear(); - perf_callchain_context context = PERF_CONTEXT_MAX; -- if (recordSample.data_.nr == 0) { -+ if (recordSample.data_.nr == 0 && !dedupStack_) { - SymbolicCallFrame(recordSample, recordSample.data_.ip, PERF_CONTEXT_MAX); - } -- for (u64 i = 0; i < recordSample.data_.nr; i++) { -+ u64 nr = recordSample.data_.nr ? recordSample.data_.nr : recordSample.ips_.size(); -+ for (u64 i = 0; i < nr; i++) { - uint64_t ip = recordSample.data_.ips[i]; - if (ip >= PERF_CONTEXT_MAX) { - std::string contextName = UpdatePerfContext(ip, context); -@@ -446,6 +460,9 @@ void VirtualRuntime::UnwindFromRecord(PerfRecordSample &recordSample) - } - // we will not do this in record mode - if (recordCallBack_ == nullptr) { -+ if (dedupStack_ && recordSample.StackId_.section.id > 0 && recordSample.data_.nr == 0) { -+ RecoverCallStack(recordSample); -+ } - // find the symbols , reabuild frame info - SymbolicRecord(recordSample); - } -@@ -741,6 +758,18 @@ void VirtualRuntime::UpdateFromPerfData(const std::vector &sym - } - } - -+void VirtualRuntime::ImportUniqueStackNodes(const std::vector& uniStackTableInfos) -+{ -+ for (const UniStackTableInfo& item : uniStackTableInfos) { -+ auto [stackTable , ok] = dumpStackMap_.emplace(std::piecewise_construct, -+ std::forward_as_tuple(item.pid), -+ std::forward_as_tuple(item.tableSize)); -+ for (const UniStackNode& node : item.nodes) { -+ stackTable->second.ImportNode(node.index, node.node); -+ } -+ } -+} -+ - /* - ARM functions - The table below lists the symbols exported by the vDSO. -diff --git a/test/unittest/common/native/subcommand_dump_test.cpp b/test/unittest/common/native/subcommand_dump_test.cpp -index 4f42746..3b4a8b7 100644 ---- a/test/unittest/common/native/subcommand_dump_test.cpp -+++ b/test/unittest/common/native/subcommand_dump_test.cpp -@@ -203,6 +203,59 @@ HWTEST_F(SubCommandDumpTest, DumpElfProtoConflict, TestSize.Level1) - TestDumpCommand("--elf elffile --proto ptotofile ", false); - } - #endif -+ -+HWTEST_F(SubCommandDumpTest, DumpCompressDwarf, TestSize.Level1) -+{ -+ StdoutRecord stdoutRecord; -+ stdoutRecord.Start(); -+ std::string cmdString = "dump -i /data/test/resource/testdata/dwarf.compress.data"; -+ EXPECT_EQ(Command::DispatchCommand(cmdString), true); -+ std::string stringOut = stdoutRecord.Stop(); -+ -+ EXPECT_EQ(stringOut.find("hiperf_stack_table") != std::string::npos, true); -+ EXPECT_EQ(stringOut.find("stackid") != std::string::npos, true); -+ EXPECT_EQ(stringOut.find("TableNums") != std::string::npos, true); -+} -+ -+HWTEST_F(SubCommandDumpTest, DumpCompressFp, TestSize.Level1) -+{ -+ StdoutRecord stdoutRecord; -+ stdoutRecord.Start(); -+ std::string cmdString = "dump -i /data/test/resource/testdata/fp.compress.data"; -+ EXPECT_EQ(Command::DispatchCommand(cmdString), true); -+ std::string stringOut = stdoutRecord.Stop(); -+ -+ EXPECT_EQ(stringOut.find("hiperf_stack_table") != std::string::npos, true); -+ EXPECT_EQ(stringOut.find("stackid") != std::string::npos, true); -+ EXPECT_EQ(stringOut.find("TableNums") != std::string::npos, true); -+} -+ -+HWTEST_F(SubCommandDumpTest, DumpUncompressDwarf, TestSize.Level1) -+{ -+ StdoutRecord stdoutRecord; -+ stdoutRecord.Start(); -+ std::string cmdString = "dump -i /data/test/resource/testdata/dwarf.uncompress.data"; -+ EXPECT_EQ(Command::DispatchCommand(cmdString), true); -+ std::string stringOut = stdoutRecord.Stop(); -+ -+ EXPECT_EQ(stringOut.find("hiperf_stack_table") != std::string::npos, false); -+ EXPECT_EQ(stringOut.find("stackid") != std::string::npos, false); -+ EXPECT_EQ(stringOut.find("TableNums") != std::string::npos, false); -+ -+} -+ -+HWTEST_F(SubCommandDumpTest, DumpUncompressFp, TestSize.Level1) -+{ -+ StdoutRecord stdoutRecord; -+ stdoutRecord.Start(); -+ std::string cmdString = "dump -i /data/test/resource/testdata/fp.uncompress.data"; -+ EXPECT_EQ(Command::DispatchCommand(cmdString), true); -+ std::string stringOut = stdoutRecord.Stop(); -+ -+ EXPECT_EQ(stringOut.find("hiperf_stack_table") != std::string::npos, false); -+ EXPECT_EQ(stringOut.find("stackid") != std::string::npos, false); -+ EXPECT_EQ(stringOut.find("TableNums") != std::string::npos, false); -+} - } // namespace HiPerf - } // namespace Developtools - } // namespace OHOS -diff --git a/test/unittest/common/native/subcommand_report_test.cpp b/test/unittest/common/native/subcommand_report_test.cpp -index 46f8b22..caf8fad 100644 ---- a/test/unittest/common/native/subcommand_report_test.cpp -+++ b/test/unittest/common/native/subcommand_report_test.cpp -@@ -1112,6 +1112,94 @@ HWTEST_F(SubCommandReportTest, TestVerifyDisplayOption, TestSize.Level1) - args = {"report -i " + RESOURCE_PATH + "report_test.data --pids "}; - EXPECT_EQ(mSubCommandReport.VerifyDisplayOption(), true); - } -+ -+/** -+ * @tc.name: TestDwarfCompress -+ * @tc.desc: -+ * @tc.type: FUNC -+ */ -+HWTEST_F(SubCommandReportTest, TestDwarfCompress, TestSize.Level1) -+{ -+ StdoutRecord stdoutRecord; -+ stdoutRecord.Start(); -+ EXPECT_EQ( -+ Command::DispatchCommand("report -s -i " + RESOURCE_PATH + "dwarf.compress.data"), -+ true); -+ std::string stringOut = stdoutRecord.Stop(); -+ if (HasFailure()) { -+ printf("output:\n%s", stringOut.c_str()); -+ } -+ const std::string expectStr = "--dedup_stack"; -+ EXPECT_EQ(FindExpectStr(stringOut, expectStr), true); -+ const std::string expectPercentageStr = "|- 45.81% __schedule"; -+ EXPECT_EQ(FindExpectStr(stringOut, expectPercentageStr), true); -+} -+ -+/** -+ * @tc.name: TestFpCompress -+ * @tc.desc: -+ * @tc.type: FUNC -+ */ -+HWTEST_F(SubCommandReportTest, TestFpCompress, TestSize.Level1) -+{ -+ StdoutRecord stdoutRecord; -+ stdoutRecord.Start(); -+ EXPECT_EQ( -+ Command::DispatchCommand("report -s -i " + RESOURCE_PATH + "fp.compress.data"), -+ true); -+ std::string stringOut = stdoutRecord.Stop(); -+ if (HasFailure()) { -+ printf("output:\n%s", stringOut.c_str()); -+ } -+ const std::string expectStr = "--dedup_stack"; -+ EXPECT_EQ(FindExpectStr(stringOut, expectStr), true); -+ const std::string expectPercentageStr = "|- 91.88% __schedule"; -+ EXPECT_EQ(FindExpectStr(stringOut, expectPercentageStr), true); -+} -+ -+/** -+ * @tc.name: TestDwarfUnCompress -+ * @tc.desc: -+ * @tc.type: FUNC -+ */ -+HWTEST_F(SubCommandReportTest, TestDwarfUnCompress, TestSize.Level1) -+{ -+ StdoutRecord stdoutRecord; -+ stdoutRecord.Start(); -+ EXPECT_EQ( -+ Command::DispatchCommand("report -s -i " + RESOURCE_PATH + "dwarf.uncompress.data"), -+ true); -+ std::string stringOut = stdoutRecord.Stop(); -+ if (HasFailure()) { -+ printf("output:\n%s", stringOut.c_str()); -+ } -+ const std::string expectStr = "--dedup_stack"; -+ EXPECT_EQ(FindExpectStr(stringOut, expectStr), false); -+ const std::string expectPercentageStr = "|- 7.63% __schedule"; -+ EXPECT_EQ(FindExpectStr(stringOut, expectPercentageStr), true); -+} -+ -+/** -+ * @tc.name: TestFpUnCompress -+ * @tc.desc: -+ * @tc.type: FUNC -+ */ -+HWTEST_F(SubCommandReportTest, TestFpUnCompress, TestSize.Level1) -+{ -+ StdoutRecord stdoutRecord; -+ stdoutRecord.Start(); -+ EXPECT_EQ( -+ Command::DispatchCommand("report -s -i " + RESOURCE_PATH + "fp.uncompress.data"), -+ true); -+ std::string stringOut = stdoutRecord.Stop(); -+ if (HasFailure()) { -+ printf("output:\n%s", stringOut.c_str()); -+ } -+ const std::string expectStr = "--dedup_stack"; -+ EXPECT_EQ(FindExpectStr(stringOut, expectStr), false); -+ const std::string expectPercentageStr = "|- 53.27% __kmalloc_reserve"; -+ EXPECT_EQ(FindExpectStr(stringOut, expectPercentageStr), true); -+} - } // namespace HiPerf - } // namespace Developtools - } // namespace OHOS -diff --git a/test/unittest/resource/ohos_test.xml b/test/unittest/resource/ohos_test.xml -index 2b2a583..e3354ca 100644 ---- a/test/unittest/resource/ohos_test.xml -+++ b/test/unittest/resource/ohos_test.xml -@@ -91,6 +91,10 @@ -