259 lines
6.6 KiB
Markdown
259 lines
6.6 KiB
Markdown
# TSL 调试与性能分析器
|
||
|
||
文档类型:语法主线
|
||
是否可直接用于生成代码:仅部分
|
||
是否含可直接照写示例:是
|
||
是否含不可照写反例:是
|
||
遇到不确定时:先按本页候选页继续判断;[07_control_flow.md](07_control_flow.md)、[10_runtime_context_and_with.md](10_runtime_context_and_with.md)、[11_pitfalls.md](11_pitfalls.md);仍不命中时回到语法路由中心 [index.md](index.md);如果问题已经超出语法层,回到 TSL 总入口 [../index.md](../index.md)
|
||
|
||
这一篇收拢本页明确的调试、计时、性能分析器和调用栈相关入口。
|
||
|
||
## 本篇职责
|
||
|
||
回答“`goto`、`debugReturn`、`debugRunEnv`、`mtic` / `mtoc`、`setProfiler`、`__line__` 和 `__stack_frame` 怎样写、会怎样表现”。
|
||
|
||
## 智能体调试/性能分析器判断流程
|
||
|
||
1. 先判断任务需要跳转、提前返回、运行环境调试、计时还是性能分析器。
|
||
2. 普通控制流优先回到 [07_control_flow.md](07_control_flow.md),本页只处理调试补充工具。
|
||
3. `debugReturn` 会结束整段脚本,不能当成普通函数返回。
|
||
4. 计时和性能分析器只照文档最小调用写,不要补未写入文档参数。
|
||
5. 没有对应代码块时不要发明调试/性能分析器写法。
|
||
|
||
## 核心规则
|
||
|
||
- `goto label_name;` 属于文档明确写法,但目标位置以 `label label_name; statement` 这种内联形式作为默认生成形态。
|
||
- 本页正向边界只覆盖“跳到同一函数 / 同一脚本体后面的位置”,不要先把更复杂的跨层跳转边界写成事实。
|
||
- `debugReturn value;` 会直接结束整段脚本,后面的语句不会继续执行。
|
||
- `debugRunEnv(0)` 和 `debugRunEnv(1)` 可直接调用;它们面向调试客户端的副作用,不作为本页输出事实。
|
||
- `debugRunEnvDo Func(...)` 可直接写,并且会返回被调用函数的结果。
|
||
- `mtic` 会生成一个计时起点;`mtoc` 和 `mtoc(tick)` 都会返回秒数。
|
||
- `setProfiler(...)` 和 `getProfilerInfo(...)` 的参数规格见 [../reference/catalog/system.md](../reference/catalog/system.md);本页只保留性能分析器行为示例。
|
||
- `setProfiler(7)` 配合 `getProfilerInfo(1)`,可以在不弹窗的情况下拿到性能分析器信息。
|
||
- `__line__` 会返回所在代码行号。
|
||
- `__stack_frame` 会返回调用栈帧数组;最小 `toStn(...)` 观察结果里,每一项是 `(line, "function")` 这一类二元组。
|
||
|
||
## 可直接照写示例
|
||
|
||
### `goto`
|
||
|
||
代码块身份:可直接照写示例
|
||
|
||
```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`
|
||
|
||
代码块身份:可直接照写示例
|
||
|
||
```tsl
|
||
writeLn("before");
|
||
a := Inner(3);
|
||
writeLn("after");
|
||
|
||
function Inner(bb);
|
||
begin
|
||
debugReturn bb;
|
||
end;
|
||
```
|
||
|
||
结果说明:
|
||
|
||
- 只输出 `before`
|
||
- 说明 `debugReturn bb;` 不只是结束 `Inner(...)`,而是直接让整段脚本提前返回
|
||
- 因此 `Inner(3)` 后面的 `writeLn("after")` 不会执行
|
||
|
||
### `debugRunEnv` 与 `debugRunEnvDo`
|
||
|
||
`debugRunEnv(0)` / `debugRunEnv(1)`:
|
||
|
||
代码块身份:可直接照写示例
|
||
|
||
```tsl
|
||
a := 1;
|
||
debugRunEnv(0);
|
||
debugRunEnv(1);
|
||
writeLn(1);
|
||
```
|
||
|
||
结果说明:
|
||
|
||
- 输出 `1`
|
||
- 说明这两个调用能正常执行,不会中断后续语句
|
||
- 但它们把变量 / 系统参数送到调试窗口的效果,不作为本页输出事实
|
||
|
||
`debugRunEnvDo Func(...)`:
|
||
|
||
代码块身份:可直接照写示例
|
||
|
||
```tsl
|
||
r := debugRunEnvDo Demo(2);
|
||
writeLn(r);
|
||
|
||
function Demo(x);
|
||
begin
|
||
y := x + 1;
|
||
return y;
|
||
end;
|
||
```
|
||
|
||
结果说明:
|
||
|
||
- 输出 `3`
|
||
- 说明 `debugRunEnvDo Demo(2)` 可以直接写
|
||
- 也说明它会把被调用函数的结果继续返回给外层
|
||
|
||
### `mtic` 与 `mtoc`
|
||
|
||
代码块身份:可直接照写示例
|
||
|
||
```tsl
|
||
t1 := mtic;
|
||
s := 0;
|
||
for i := 0 to 9999 do
|
||
s := s + i;
|
||
te1 := mtoc(t1);
|
||
t2 := mtic;
|
||
for j := 0 to 9999 do
|
||
s := s + j;
|
||
te2 := mtoc;
|
||
writeLn(te1 >= 0);
|
||
writeLn(te2 >= 0);
|
||
```
|
||
|
||
结果说明:
|
||
|
||
- 依次输出 `1`、`1`
|
||
- 说明 `mtoc(t1)` 和无参 `mtoc` 都能返回可用的秒数结果
|
||
|
||
### `setProfiler` 与 `getProfilerInfo`
|
||
|
||
代码块身份:可直接照写示例
|
||
|
||
```tsl
|
||
setProfiler(7);
|
||
a := 99;
|
||
b := intToStr(a);
|
||
c := rand(10, 1);
|
||
info := getProfilerInfo(1);
|
||
writeLn(ifArray(info));
|
||
writeLn(length(info) > 0);
|
||
```
|
||
|
||
结果说明:
|
||
|
||
- 依次输出 `1`、`1`
|
||
- 说明 `setProfiler(7)` 可以开启性能分析器统计
|
||
- 说明 `getProfilerInfo(1)` 会直接返回性能分析器信息,而且结果是非空数组
|
||
|
||
### `__line__` 与 `__stack_frame`
|
||
|
||
`__line__`:
|
||
|
||
代码块身份:可直接照写示例
|
||
|
||
```tsl
|
||
a := __line__;
|
||
writeLn(a);
|
||
```
|
||
|
||
结果说明:
|
||
|
||
- 输出 `3`
|
||
- 说明 `__line__` 直接返回所在代码行号
|
||
|
||
`__stack_frame`:
|
||
|
||
代码块身份:可直接照写示例
|
||
|
||
```tsl
|
||
s := Outer();
|
||
writeLn(toStn(s));
|
||
|
||
function Inner();
|
||
begin
|
||
return __stack_frame;
|
||
end;
|
||
function Outer();
|
||
begin
|
||
return Inner();
|
||
end;
|
||
```
|
||
|
||
结果说明:
|
||
|
||
代码块身份:输出片段
|
||
|
||
```text
|
||
array(
|
||
(11,"__main__"),
|
||
(8,"Outer"))
|
||
```
|
||
|
||
- 说明 `__stack_frame` 返回的是调用栈帧数组
|
||
- 在这个最小例子里,可以直接看到调用位置行号和调用者函数名
|
||
|
||
## 禁止项
|
||
|
||
- 不要把 `debugReturn` 当成普通函数 `return` 使用。
|
||
- 不要假设 `goto` 可以跨函数、跨脚本体或跳到单独成行的 `label`。
|
||
- 不要给计时或性能分析器调用补未写入文档参数。
|
||
- 不要把调试客户端副作用写成普通输出事实。
|