# TSL 调试与性能分析器

这一篇收拢本页明确的调试、计时、性能分析器和调用栈相关入口。

## 本篇职责

<!-- section-id: syntax-14-001 -->

回答“`goto`、`debugReturn`、`debugRunEnv`、`mtic` / `mtoc`、`setProfiler`、内存伪变量、`__line__` 和 `__stack_frame` 怎样写、会怎样表现”。

## 核心规则

<!-- section-id: syntax-14-002 -->

本页示例只说明调试与分析结构中的调用位置和源码外形。相关 API 的精确签名、参数、返回行为、平台 scope、目标环境或解释器可用性必须使用 `tsl-api-reference` skill 重新核对，不能由本页示例反推。

- `goto label_name;` 属于文档明确写法，但目标位置以 `label label_name; statement` 这种内联形式作为默认生成形态。
- 本页正向边界只覆盖“跳到同一函数 / 同一脚本体后面的位置”，不要先把更复杂的跨层跳转边界写成事实。
- `debugReturn value;` 会直接结束整段脚本，后面的语句不会继续执行。
- `debugRunEnv(0)` 和 `debugRunEnv(1)` 可直接调用；它们面向调试客户端的副作用，不作为本页输出事实。
- `debugRunEnvDo Func(...)` 可直接写，并且会返回被调用函数的结果。
- `mtic` 会生成一个计时起点；`mtoc` 和 `mtoc(tick)` 都会返回秒数。
- `setProfiler(...)` 和 `getProfilerInfo(...)` 的参数规格使用 `tsl-api-reference` skill 按名查询；本页只保留性能分析器行为示例。
- `setProfiler(7)` 配合 `getProfilerInfo(1)`，可以在不弹窗的情况下拿到性能分析器信息。
- `__line__` 会返回所在代码行号。
- `__stack_frame` 会返回调用栈帧数组；最小 `toStn(...)` 观察结果里，每一项是 `(line, "function")` 这一类二元组。
- `_myMem_` 表示应用当前已使用内存，`_maxMem_` 表示应用允许使用的最大内存；两者是只读数值伪变量。单位和 `_maxMem_` 的具体取值由宿主环境决定。

## 可直接照写示例

<!-- section-id: syntax-14-003 -->

### `goto`

<!-- section-id: syntax-14-004 -->

<!-- tags: 跳转, 跳出多重循环, 标签跳转 -->

代码块身份：可直接照写示例

```tsl
data := array((1, 2), (3, 4));
target := 3;
found := 0;
for i := 0 to length(data) - 1 do
begin
    for j := 0 to length(data[i]) - 1 do
    begin
        if data[i][j] = target then
            goto found_label;
    end
end
writeLn(0);
goto done_label;
label found_label; found := 1;
writeLn(found);
label done_label; writeLn(9);
```

结果说明：

- 依次输出 `1`、`9`
- 说明 `goto found_label;` 可以跳到后面的 `label found_label; ...`
- 也说明默认目标写法是把 `label` 和第一条目标语句放在同一行

跨函数跳转不作为可写事实：

代码块身份：反例 / 不可照写

```text
Inner();
label out_label; writeLn(1);

function Inner();
begin
    goto out_label;
end;
```

结果说明：

- 上面这段会运行报错，核心信息是 `Goto label can not found!`
- 本页正向边界只覆盖“同一函数 / 同一脚本体后面的位置”
- 不要把 `goto` 泛化成能跨函数跳到外层 `label`

目标 `label` 单独成行不作为可写事实：

代码块身份：反例 / 不可照写

```text
goto done_label;
label done_label;
writeLn("after");
```

结果说明：

- 上面这种把 `label` 单独放一行、下一行再写目标语句的最小例子，会报 `Statement missing terminator`
- 因此本页只把 `label name; statement` 这种内联形式写成文档事实

### `debugReturn`

<!-- section-id: syntax-14-005 -->

<!-- tags: 中途看返回值, 调试返回, 提前打断 -->

代码块身份：可直接照写示例

```tsl
writeLn("before");
a := Inner(3);
writeLn("after");

function Inner(value);
begin
    debugReturn value;
end;
```

结果说明：

- 只输出 `before`
- 说明 `debugReturn value;` 不只是结束 `Inner(...)`，而是直接让整段脚本提前返回
- 因此 `Inner(3)` 后面的 `writeLn("after")` 不会执行

### `debugRunEnv` 与 `debugRunEnvDo`

<!-- section-id: syntax-14-006 -->

<!-- tags: 调试环境, 运行环境切换, 调试开关 -->

`debugRunEnv(0)` / `debugRunEnv(1)`：

代码块身份：可直接照写示例

```tsl
a := 1;
debugRunEnv(0);
debugRunEnv(1);
writeLn(1);
```

结果说明：

- 输出 `1`
- 说明这两个调用能正常执行，不会中断后续语句
- 但它们把变量 / 系统参数送到调试窗口的效果，不作为本页输出事实

`debugRunEnvDo Func(...)`：

代码块身份：可直接照写示例

```tsl
result := debugRunEnvDo Demo(2);
writeLn(result);

function Demo(x);
begin
    y := x + 1;
    return y;
end;
```

结果说明：

- 输出 `3`
- 说明 `debugRunEnvDo Demo(2)` 可以直接写
- 也说明它会把被调用函数的结果继续返回给外层

### `mtic` 与 `mtoc`

<!-- section-id: syntax-14-007 -->

<!-- tags: 计时, 测耗时, 跑了多久, 秒表, 执行时间 -->

代码块身份：可直接照写示例

```tsl
tick1 := mtic;
sum := 0;
for i := 0 to 9999 do
    sum := sum + i;
elapsed1 := mtoc(tick1);
tick2 := mtic;
for j := 0 to 9999 do
    sum := sum + j;
elapsed2 := mtoc;
writeLn(elapsed1 >= 0);
writeLn(elapsed2 >= 0);
```

结果说明：

- 依次输出 `1`、`1`
- 说明 `mtoc(t1)` 和无参 `mtoc` 都能返回可用的秒数结果

### `setProfiler` 与 `getProfilerInfo`

<!-- section-id: syntax-14-008 -->

<!-- tags: 性能分析, 找瓶颈, profiler, 哪段最慢 -->

代码块身份：可直接照写示例

```tsl
setProfiler(7);
value := 99;
str := intToStr(value);
random := rand(10, 1);
info := getProfilerInfo(1);
writeLn(ifArray(info));
writeLn(length(info) > 0);
```

结果说明：

- 依次输出 `1`、`1`
- 说明 `setProfiler(7)` 可以开启性能分析器统计
- 说明 `getProfilerInfo(1)` 会直接返回性能分析器信息，而且结果是非空数组

### 内存伪变量 `_myMem_` / `_maxMem_`

<!-- section-id: syntax-14-011 -->

<!-- tags: 已用内存, 内存上限, 最大内存, 内存伪变量, _myMem_, _maxMem_ -->

代码块身份：可直接照写示例

```tsl
writeLn(ifNumber(_myMem_));
writeLn(ifNumber(_maxMem_));
```

代码块身份：输出片段

```text
1
1
```

说明：

- `_myMem_` 是应用已使用内存的数值
- `_maxMem_` 是宿主允许应用使用的最大内存数值；某些环境可能返回 `0` 表示未给出可比较的上限
- 不要假设 `_maxMem_ >= _myMem_`，也不要在没有宿主文档时写死单位

### `__line__` 与 `__stack_frame`

<!-- section-id: syntax-14-009 -->

<!-- tags: 当前行号, 调用栈, 堆栈信息, 报错在哪一行 -->

`__line__`：

代码块身份：可直接照写示例

```tsl
line_number := __line__;
writeLn(line_number);
```

结果说明：

- 输出 `1`
- 说明 `__line__` 直接返回它所在的代码行号（这里 `__line__` 在脚本第 1 行）

`__stack_frame`：

代码块身份：可直接照写示例

```tsl
stack := Outer();
writeLn(toStn(stack));

function Inner();
begin
    return __stack_frame;
end;
function Outer();
begin
    return Inner();
end;
```

结果说明：

代码块身份：输出片段

```text
array(
(1,"__main__"),
(10,"Outer"))
```

- 说明 `__stack_frame` 返回的是调用栈帧数组
- 在这个最小例子里，可以直接看到调用位置行号和调用者函数名：`__main__` 在第 1 行调用 `Outer()`，`Outer` 在第 10 行调用 `Inner()`

## 禁止项

<!-- section-id: syntax-14-010 -->

- 不要把 `debugReturn` 当成普通函数 `return` 使用。
- 不要假设 `goto` 可以跨函数、跨脚本体或跳到单独成行的 `label`。
- 不要给计时或性能分析器调用补未写入文档参数。
- 不要把调试客户端副作用写成普通输出事实。
- 不要假定 `_maxMem_` 总是非零、总是大于 `_myMem_`，或擅自指定内存单位。
