diff --git a/ide/src/base-ui/headline/lit-headline.ts b/ide/src/base-ui/headline/lit-headline.ts
index 0beed234ab19c6ab5b4d8752a3ecdd5469aafa6b..b0c7d4a83d8dfaa930cb3ac49abea7fb6243fa9a 100644
--- a/ide/src/base-ui/headline/lit-headline.ts
+++ b/ide/src/base-ui/headline/lit-headline.ts
@@ -41,6 +41,7 @@ export class LitHeadLine extends BaseElement {
set titleTxt(value: string) {
this.titleContent = value;
this._titleContext!.innerHTML = value;
+ this._titleContext?.setAttribute('title', value || '');
}
get titleTxt(): string {
return this.titleContent || '';
diff --git a/ide/src/doc/md/quickstart_hiperf.md b/ide/src/doc/md/quickstart_hiperf.md
index 742c82c5ef7801a8f0805d4a3e6996515bf78a72..8e0c2f8be437ee098074310987f19b1b2b95c8e9 100644
--- a/ide/src/doc/md/quickstart_hiperf.md
+++ b/ide/src/doc/md/quickstart_hiperf.md
@@ -13,6 +13,7 @@ Hiperf 工具是对系统性能数据进行采样记录,并将采样数据保
- Start Hiperf Sampling:配置项的总开关。
- Process:离线模式下配置的是整个系统的进程。
- Frequency:配置抓取的频率。
+- Off CPU:配置抓取进程调度切换事件。
- Call Stack:配置抓取的堆栈类型。
- Advance Options:更多的抓取配置项。
再点击 Record setting,在 output file path 输入文件名 hiprofiler_data_perf.htrace,拖动滚动条设置 buffer size 大小是 64MB,抓取时长是 50s。
diff --git a/ide/src/doc/md/quickstart_xpower.md b/ide/src/doc/md/quickstart_xpower.md
index 9ed37e17fc2e7c47af866d18bd51c0c483cbe20a..09d9997db7005f088c2e7fe969ae78deb0b02b8e 100644
--- a/ide/src/doc/md/quickstart_xpower.md
+++ b/ide/src/doc/md/quickstart_xpower.md
@@ -65,7 +65,7 @@ Xpower用于查看系统整机和应用功耗数据,当前支持:电源信
- Value:值
- Duration:持续时间
-框选system下的任意时间泳道图可以弹出Xpower Counters和Xpowe Component Top两个tab页
+框选system下的任意时间泳道图可以弹出Xpower Counters和Xpower Component Top两个tab页
Xpower Counters的tab页
@@ -84,7 +84,7 @@ Xpower Counters的tab页
- Min value:最小值
- Max value:最大值
-Xpowe Component Top的tab页(此tab页为框选Battery.RealCurrent泳道图出现)
+Xpower Component Top的tab页(此tab页为框选Battery.RealCurrent泳道图出现)

@@ -213,7 +213,7 @@ WIFIPacket、WIFIPacket和DisPlay泳道图的点选功能:点击WIFIPacket、W
- StartTime(Relative) :相对开始时间
- StartTime(Absolute) :绝对开始时间
-- receiver:接收数据大小
+- receive:接收数据大小
- send:发送数据大小
特别地:点击display的泳道图会出现屏幕刷新率的持续时间
@@ -261,7 +261,7 @@ Thread Count和Gpu Freq Count框选功能:框选Gpu Freq Count、Thread Count
- Min value:最小值
- Max value:最大值
-Statistics泳道图的框选功能:框选Statistics泳道图任意处会弹出Xpowe Statistics的tab页
+Statistics泳道图的框选功能:框选Statistics泳道图任意处会弹出Xpower Statistics的tab页

@@ -279,7 +279,7 @@ WIFIPackets、WIFIBytes和Display泳道图的框选功能:框选WIFIPackets、
- Min:最小值(display为刷新率的最小持续时间,WIFIBytes为收发的数据包个数活数据最小值)
- Avg:平均值(display为刷新率的平均持续时间,WIFIBytes为收发的数据包个数活数据平均数)
-Thread energy泳道图的框选功能:框选Thread energy泳道图会弹出Xpowe Thread energy的tab页
+Thread energy泳道图的框选功能:框选Thread energy泳道图会弹出Xpower Thread energy的tab页

@@ -289,7 +289,7 @@ Thread energy泳道图的框选功能:框选Thread energy泳道图会弹出Xpo
- Max Energy:最大能耗
- Min Energy:最小能耗
-Thread Load泳道图的框选功能:框选Thread Load泳道图会弹出Xpowe Thread Load的tab页
+Thread Load泳道图的框选功能:框选Thread Load泳道图会弹出Xpower Thread Load的tab页

@@ -299,7 +299,7 @@ Thread Load泳道图的框选功能:框选Thread Load泳道图会弹出Xpowe T
- Max Load:最大占比
- Min Load:最小占比
-Gpu Freq Count泳道图的框选功能:框选Thread energy泳道图会弹出Xpowe Gpu Frequency 的tab页
+Gpu Freq Count泳道图的框选功能:框选Thread energy泳道图会弹出Xpower Gpu Frequency 的tab页

diff --git a/ide/src/doc/quickstart_extensions.html b/ide/src/doc/quickstart_extensions.html
index 9be16c4ff4f1f29954cc269adacd29610074a7ef..2a1a5b85f64400e66dced7123e1bf0a899cfba48 100644
--- a/ide/src/doc/quickstart_extensions.html
+++ b/ide/src/doc/quickstart_extensions.html
@@ -811,7 +811,7 @@
在hi-smart-perf-host-extend目录下,找到stop.bat文件,右键选择以管理员身份运行,即可关闭扩展服务。
- 备注:当前扩展服务版本为1.0.8,如果本地存在以其他方式安装的非正式版本,请手动关闭扩展服务,并重新按照该指导安装
+ 备注:当前扩展服务版本为1.1.0,如果本地存在以其他方式安装的非正式版本,请手动关闭扩展服务,并重新按照该指导安装
diff --git a/ide/src/doc/quickstart_hiperf.html b/ide/src/doc/quickstart_hiperf.html
index f73ecd41bf0a912f1a5d61c299fc4d3bc46b36b5..e829a997833d35977d33e7272ee9e3f5912c0b8d 100644
--- a/ide/src/doc/quickstart_hiperf.html
+++ b/ide/src/doc/quickstart_hiperf.html
@@ -811,6 +811,12 @@ Process:离线模式下配置的是整个系统的。
Frequency:配置抓取的频率。
+
+
+
+
+Off CPU:配置抓取进程调度切换事件。
diff --git a/ide/src/doc/quickstart_limit.html b/ide/src/doc/quickstart_limit.html
new file mode 100644
index 0000000000000000000000000000000000000000..9120d82589fa3bc6d8345b43fbc0126e85b9dcf2
--- /dev/null
+++ b/ide/src/doc/quickstart_limit.html
@@ -0,0 +1,894 @@
+
+
+
+
+
+ quickstart_limit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+仅支持linux内核,鸿蒙内核不支持抓取。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ide/src/doc/quickstart_native_memory.html b/ide/src/doc/quickstart_native_memory.html
index 48948d3832d2321a8a6bed0f1bc6ae4ba6fd7810..cd0c98a138ae908f97f3f6006962a087f9445a1a 100644
--- a/ide/src/doc/quickstart_native_memory.html
+++ b/ide/src/doc/quickstart_native_memory.html
@@ -1088,9 +1088,9 @@
调用栈默认会显示火焰图,新增搜索框表达式输入。表达式作用范围为 nativehook
的统计与非统计模式。其中处理的为 Responsible Library 与 Responsible Caller,其中 Responsible
Library 和Responsible Caller 表示从下往上非 libc++ musl 的第一条调用栈的 lib 跟
- symbol,如下图所示,由于最后一条 [ operator new(unsigned long) ] libc++.so
- 为 libc++.so 的函数,故跳过,所以该条调用栈的 Responsible Library
- 为 libhilog.so,Responsible Caller 为
+ symbol,如下图所示,由于最后一条 [ operator new(unsigned long) ] libc++.so
+ 为 libc++.so 的函数,故跳过,所以该条调用栈的 Responsible Library
+ 为 libhilog.so,Responsible Caller 为
OHOS::HiviewDFX::GetDomainLevel(unsigned int) 。
只显示 Responsible Library 路径/data/user 且 symbol 为任意值的数据
- @(mali.so *,libGLES_mali.so *) |
- 只显示 Responsible Library 包含 mali.so 或者 libGLES_mali.so
+ | @(mali.so *,libGLES_mali.so *) |
+ 只显示 Responsible Library 包含 mali.so 或者 libGLES_mali.so
的数据
|
diff --git a/ide/src/doc/quickstart_xpower.html b/ide/src/doc/quickstart_xpower.html
index 02a78a4377d5c51f774c50d740a778b6309620a6..09f2f97ea85e0a3927af8f697afbf94fab7f4fe2 100644
--- a/ide/src/doc/quickstart_xpower.html
+++ b/ide/src/doc/quickstart_xpower.html
@@ -914,7 +914,7 @@ ThermalReport.ThermalLevel: 温度等级。
Value:值
Duration:持续时间
- 框选system下的RealCurrent泳道图可以弹出Xpower Counters和Xpowe Component Top两个tab页
+ 框选system下的RealCurrent泳道图可以弹出Xpower Counters和Xpower Component Top两个tab页
Xpower Counters的tab页

- Statistics泳道图的框选功能:框选Statistics泳道图任意处会弹出Xpowe Statistics的tab页
+ Statistics泳道图的框选功能:框选Statistics泳道图任意处会弹出Xpower Statistics的tab页

- Name:器件名
@@ -1194,7 +1194,7 @@ gpu:gpuTop应用的能耗
- Min:最小值(display的tab页为刷新率的最小持续时间,WIFIPackets、WIFIBytes的tab页为收发的数据包最小个数或数据最小字节)
- Avg:平均值(display的tab页为刷新率的平均持续时间,WIFIPackets、WIFIBytes的tab页为收发的数据包平均个数或数据平均字节)
- Thread energy泳道图的框选功能:框选Thread energy泳道图会弹出Xpowe Thread energy的tab页
+ Thread energy泳道图的框选功能:框选Thread energy泳道图会弹出Xpower Thread energy的tab页

- ThreadName:线程名
@@ -1203,7 +1203,7 @@ gpu:gpuTop应用的能耗
- Max Energy:最大能耗
- Min Energy:最小能耗
- Thread Load泳道图的框选功能:框选Thread Load泳道图会弹出Xpowe Thread Load的tab页
+ Thread Load泳道图的框选功能:框选Thread Load泳道图会弹出Xpower Thread Load的tab页

- ThreadName:线程名
@@ -1212,7 +1212,7 @@ gpu:gpuTop应用的能耗
- Max Load:最大占比
- Min Load:最小占比
- Gpu Freq Count泳道图的框选功能:框选Thread energy泳道图会弹出Xpowe Gpu Frequency 的tab页
+ Gpu Freq Count泳道图的框选功能:框选Thread energy泳道图会弹出Xpower Gpu Frequency 的tab页

- Frequency:频率
diff --git a/ide/src/figures/Limit/htrace.jpg b/ide/src/figures/Limit/htrace.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..3f623683a8caeb90ec352dfc8a48214c542f6d74
Binary files /dev/null and b/ide/src/figures/Limit/htrace.jpg differ
diff --git a/ide/src/trace/SpApplication.ts b/ide/src/trace/SpApplication.ts
index 9bdcf438abdec7e1fd42b2582e7fabc927edfc0e..6ee9782965e548aeb39a200b161fa0a9d3a563ff 100644
--- a/ide/src/trace/SpApplication.ts
+++ b/ide/src/trace/SpApplication.ts
@@ -17,7 +17,7 @@ import { BaseElement, element } from '../base-ui/BaseElement';
import '../base-ui/menu/LitMainMenu';
import '../base-ui/icon/LitIcon';
import '../base-ui/loading/LitLoading';
-import '../base-ui/like/LitLike';
+import '../base-ui/like/LitLike';
import { SpMetrics } from './component/SpMetrics';
import { SpHelp } from './component/SpHelp';
import './component/SpHelp';
@@ -85,11 +85,14 @@ import './component/SpThirdParty';
import { cancelCurrentTraceRowHighlight } from './component/SpSystemTrace.init';
import './component/SpBubblesAI';
import './component/SpAiAnalysisPage';
+import './component/SpSnapShotView';
import { WebSocketManager } from '../webSocket/WebSocketManager';
import { SpAiAnalysisPage } from './component/SpAiAnalysisPage';
import './component/SpAdvertisement';
import { ShadowRootInput } from './component/trace/base/ShadowRootInput';
import { SpBubblesAI } from './component/SpBubblesAI';
+import { SpSnapShotView } from './component/SpSnapShotView';
+import { SnapShotStruct } from './database/ui-worker/ProcedureWorkerSnaps';
@element('sp-application')
export class SpApplication extends BaseElement {
@@ -178,6 +181,8 @@ export class SpApplication extends BaseElement {
private currentDataTime: string[] = [];
static traceType: String = '';
private isZipFile: boolean = false;
+ isClear: boolean = false;
+ static spSnapShotView: SpSnapShotView | undefined | null;
static get observedAttributes(): Array {
return ['server', 'sqlite', 'wasm', 'dark', 'vs', 'query-sql', 'subsection'];
@@ -321,6 +326,7 @@ export class SpApplication extends BaseElement {
this.contentCenterOption = this.shadowRoot?.querySelector('.content-center-option');
this.spAiAnalysisPage = this.shadowRoot!.querySelector('#sp-ai-analysis') as SpAiAnalysisPage;
let xiaoLubanEl: HTMLElement | null = this.shadowRoot!.querySelector('#sp-bubbles');
+ SpApplication.spSnapShotView = this.shadowRoot!.querySelector('#sp-snapshot-view') as SpSnapShotView;
this.initElementsAttr();
this.initEvents();
this.initRecordEvents();
@@ -334,8 +340,24 @@ export class SpApplication extends BaseElement {
this.initElementsEnd();
this.dragXiaolubanEvents(xiaoLubanEl!);
this.connectWebSocket();
+ SpApplication.spSnapShotView!.addEventListener('mousemove', () => {
+ this.clearSnapShot();
+ })
+ SpApplication.spSnapShotView!.addEventListener('mouseout', () => {
+ this.clearSnapShot();
+ setTimeout(() => {
+ SnapShotStruct.isClear = false;
+ }, 0);
+ })
}
+
+ private clearSnapShot():void {
+ SnapShotStruct.hoverSnapShotStruct = undefined;
+ SnapShotStruct.isClear = true;
+ this.spSystemTrace?.refreshCanvas(true);
+ }
+
private dragXiaolubanEvents(xiaoLubanEl: HTMLElement): void {
document.querySelector('body')!.addEventListener('dragover', function (event) {
event.preventDefault();
@@ -686,7 +708,7 @@ export class SpApplication extends BaseElement {
private judgeZip(typeHeader: Blob): void {
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(typeHeader);
- fileReader.onload = (event):void => {
+ fileReader.onload = (event): void => {
const uint8Array = new Uint8Array(event.target!.result as ArrayBuffer);
this.isZipFile = isZipFile(uint8Array) || isZlibFile(uint8Array);
if (this.isZipFile) {
@@ -1997,7 +2019,15 @@ export class SpApplication extends BaseElement {
this.filterRowConfigClickHandle();
});
this.cutTraceFile!.addEventListener('click', (ev) => {
- this.croppingFile(this.progressEL!, this.litSearch!);
+ this.validateFileCacheLost();
+ if (this.isClear) {
+ let search = document.querySelector('body > sp-application')!.shadowRoot!.querySelector('#lit-search');
+ let progressEL = document.querySelector("body > sp-application")!.shadowRoot!.querySelector("div > div.search-vessel > lit-progress-bar");
+ progressEL!.loading = false;
+ search!.setPercent('import the trace file again...', -3);
+ } else {
+ this.croppingFile(this.progressEL!, this.litSearch!);
+ }
});
let aiAnalysis = this.shadowRoot
@@ -2024,7 +2054,6 @@ export class SpApplication extends BaseElement {
private aiPageResize(): void {
const resizableDiv = this.spAiAnalysisPage!;
let isResizing = false;
-
resizableDiv.addEventListener('mousemove', (e) => {
if (Math.abs(e.clientX - resizableDiv.getBoundingClientRect().left) < 5) {
resizableDiv.style.cursor = 'e-resize';
@@ -2035,12 +2064,25 @@ export class SpApplication extends BaseElement {
resizableDiv.addEventListener('mousedown', function (e) {
isResizing = true;
+ let iframe = document.querySelector('body > sp-application')?.shadowRoot!.querySelector('#sp-help')?.shadowRoot?.querySelector('#myIframe');
+ // @ts-ignore
+ let iframeWindow = iframe?.contentWindow;
if (e.clientX - resizableDiv.getBoundingClientRect().left < 5) {
document.addEventListener('mousemove', changeAiWidth);
+ iframeWindow?.addEventListener('mousemove', iframeChangeAiWidth);
}
document.addEventListener('mouseup', mouseUp);
+ iframeWindow?.addEventListener('mouseup', mouseUp);
});
+ function iframeChangeAiWidth(e: unknown): void {
+ let iframe = document.querySelector('body > sp-application')?.shadowRoot!.querySelector('#sp-help')?.shadowRoot?.querySelector('#myIframe');
+ // @ts-ignore
+ let iframeWindow = iframe?.contentWindow;
+ resizableDiv.style.cursor = 'e-resize';
+ // @ts-ignore
+ resizableDiv.style.width = iframeWindow.innerWidth - e.clientX + 'px';
+ }
function changeAiWidth(e: unknown): void {
resizableDiv.style.cursor = 'e-resize';
@@ -2052,6 +2094,12 @@ export class SpApplication extends BaseElement {
isResizing = false;
document.removeEventListener('mousemove', changeAiWidth);
document.removeEventListener('mouseup', mouseUp);
+
+ let iframe = document.querySelector('body > sp-application')?.shadowRoot!.querySelector('#sp-help')?.shadowRoot?.querySelector('#myIframe');
+ // @ts-ignore
+ let iframeWindow = iframe?.contentWindow;
+ iframeWindow?.removeEventListener('mousemove', iframeChangeAiWidth);
+ iframeWindow?.removeEventListener('mouseup', mouseUp);
}
}
@@ -2254,8 +2302,10 @@ export class SpApplication extends BaseElement {
}
});
});
- this.cutTraceFile!.style.display = 'none';
+ this.isClear = true;
this.mainMenu!.menus = this.mainMenu!.menus;
+ } else {
+ this.isClear = false;
}
});
}
@@ -2611,4 +2661,10 @@ export class SpApplication extends BaseElement {
this.mainMenu!.menus = this.mainMenu!.menus;
this.filterConfig!.style.visibility = disable ? 'hidden' : 'visible';
}
+
+ static displaySnapShot(selectSnapShotStruct: SnapShotStruct | undefined) {
+ this.spSnapShotView!.style.display = 'block';
+ this.spSnapShotView!.style.visibility = 'visible';
+ this.spSnapShotView?.init(selectSnapShotStruct!)
+ }
}
diff --git a/ide/src/trace/SpApplicationPublicFunc.ts b/ide/src/trace/SpApplicationPublicFunc.ts
index be41c7d5fbb9bd2bb417d880678732f6a00d4067..6c366a72c11745e3017f60c5cc80eed972da158c 100644
--- a/ide/src/trace/SpApplicationPublicFunc.ts
+++ b/ide/src/trace/SpApplicationPublicFunc.ts
@@ -342,6 +342,18 @@ export const applicationHtml: string = `
box-sizing:border-box;
visibility:hidden;
}
+ #sp-snapshot-view {
+ top:75px;
+ right:0px;
+ position:absolute;
+ z-index:2000;
+ width:25%;
+ height:90%;
+ box-shadow:3px 0px 14px #000;
+ background-color:#fff;
+ box-sizing:border-box;
+ visibility:hidden;
+ }
@@ -418,7 +430,8 @@ export const applicationHtml: string = `
-
+
+
`;
diff --git a/ide/src/trace/bean/FrameChartStruct.ts b/ide/src/trace/bean/FrameChartStruct.ts
index c4883a6a20880fadf53acbda6d788ce674b9ed6a..de1eeb94f33fb1119a9175451d17092ea664ce23 100644
--- a/ide/src/trace/bean/FrameChartStruct.ts
+++ b/ide/src/trace/bean/FrameChartStruct.ts
@@ -68,6 +68,7 @@ export class ChartStruct extends BaseStruct {
children: Array = [];
percent: number = 0; // 0 - 1 该node所占整体的百分比
addr: string = '';
+ isReverseFilter:boolean = false;
isSearch: boolean = false;
isChartSelect: boolean = false; // 是否为点选的调用链
isChartSelectParent: boolean = false; // 用来显示灰色
diff --git a/ide/src/trace/component/SpAdvertisement.ts b/ide/src/trace/component/SpAdvertisement.ts
index 9fcf8e6e63b07fb9c284ec48141918532380e1e4..51f303492e81126d240793fa1a91e5a74cf1bad7 100644
--- a/ide/src/trace/component/SpAdvertisement.ts
+++ b/ide/src/trace/component/SpAdvertisement.ts
@@ -34,10 +34,6 @@ export class SpAdvertisement extends BaseElement {
// 公告内容
this.noticeEl = document.querySelector('body > sp-application')?.shadowRoot?.
querySelector('#sp-advertisement')?.shadowRoot?.querySelector('.text');
- this.getMessage();
- setInterval(() => {
- this.getMessage();
- }, 300000);
this.closeEL?.addEventListener('click', () => {
this.advertisementEL!.style!.display = 'none';
localStorage.setItem('isdisplay', 'false');
@@ -54,11 +50,9 @@ export class SpAdvertisement extends BaseElement {
this.message = resp.data.data;
localStorage.setItem('message', this.message);
let parts = this.message.split(';');
- let registrationLinkInfo = (parts[2].match(/报名链接:([^\s]+)/) || [])[1] || '';
- let onlineMeetingLinkInfo = (parts[3].match(/线上会议链接:([^\s]+)/) || [])[1] || '';
- let registrationLink = `报名链接`;
- let onlineMeetingLink = `线上会议链接`;
- let finalString = `${parts[0]}
${parts[1]}
${registrationLink} ${onlineMeetingLink}
${parts[4]}`;
+ let registrationLinkInfo = (parts[2].match(/版本特性链接:([^\s]+)/) || [])[1] || '';
+ let registrationLink = `版本特性链接`;
+ let finalString = `${parts[0]}
${parts[1]}
${registrationLink}`;
this.noticeEl!.innerHTML = `${finalString}
`;
if (publish) {
if (resp.data.data !== publish) {
@@ -81,6 +75,7 @@ export class SpAdvertisement extends BaseElement {
});
}
+
initHtml(): string {
return SpAdvertisementHtml;
}
diff --git a/ide/src/trace/component/SpAiAnalysisPage.ts b/ide/src/trace/component/SpAiAnalysisPage.ts
index c816f9a17fb84a5e60c9a2f723aac01ebc6b8885..e23bb357438ff2a0e32f2a0fe658d97d276ecf2a 100644
--- a/ide/src/trace/component/SpAiAnalysisPage.ts
+++ b/ide/src/trace/component/SpAiAnalysisPage.ts
@@ -258,6 +258,8 @@ export class SpAiAnalysisPage extends BaseElement {
if (e.key.toLocaleLowerCase() === 'control' || e.keyCode === 17) {
this.isCtrlDown = false;
};
+ e.preventDefault();
+ e.stopPropagation();
});
// 下载诊断报告按钮监听
@@ -831,7 +833,7 @@ export class SpAiAnalysisPage extends BaseElement {
// 获取提示语
getStatusesPrompt(): unknown {
let guideSrc = `https://${window.location.host.split(':')[0]}:${window.location.port
- }/application/?action=help_27`;
+ }${window.location.pathname}?action=help_27`;
return {
unconnected: {
prompt: `未连接,请启动本地扩展程序再试![指导]`
diff --git a/ide/src/trace/component/SpHelp.ts b/ide/src/trace/component/SpHelp.ts
index 8d4fadb6390ec05ffc31dd5c8be19f6a847feef5..d3568e5b627703aa8cb61b6eecb20d47dfa1d33c 100644
--- a/ide/src/trace/component/SpHelp.ts
+++ b/ide/src/trace/component/SpHelp.ts
@@ -38,8 +38,8 @@ export class SpHelp extends BaseElement {
this.removeAttribute('dark');
}
this.helpFile!.innerHTML =
- '