playbook/docs/tsl/syntax/15_debug_and_profiler.md

6.6 KiB
Raw Blame History

TSL 调试与性能分析器

文档类型:语法主线 是否可直接用于生成代码:仅部分 是否含可直接照写示例:是 是否含不可照写反例:是 遇到不确定时:先按本页候选页继续判断;07_control_flow.md10_runtime_context_and_with.md11_pitfalls.md;仍不命中时回到语法路由中心 index.md;如果问题已经超出语法层,回到 TSL 总入口 ../index.md

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

本篇职责

回答“gotodebugReturndebugRunEnvmtic / mtocsetProfiler__line____stack_frame 怎样写、会怎样表现”。

智能体调试/性能分析器判断流程

  1. 先判断任务需要跳转、提前返回、运行环境调试、计时还是性能分析器。
  2. 普通控制流优先回到 07_control_flow.md,本页只处理调试补充工具。
  3. debugReturn 会结束整段脚本,不能当成普通函数返回。
  4. 计时和性能分析器只照文档最小调用写,不要补未写入文档参数。
  5. 没有对应代码块时不要发明调试/性能分析器写法。

核心规则

  • goto label_name; 属于文档明确写法,但目标位置以 label label_name; statement 这种内联形式作为默认生成形态。
  • 本页正向边界只覆盖“跳到同一函数 / 同一脚本体后面的位置”,不要先把更复杂的跨层跳转边界写成事实。
  • debugReturn value; 会直接结束整段脚本,后面的语句不会继续执行。
  • debugRunEnv(0)debugRunEnv(1) 可直接调用;它们面向调试客户端的副作用,不作为本页输出事实。
  • debugRunEnvDo Func(...) 可直接写,并且会返回被调用函数的结果。
  • mtic 会生成一个计时起点;mtocmtoc(tick) 都会返回秒数。
  • setProfiler(...)getProfilerInfo(...) 的参数规格见 ../reference/catalog/system.md;本页只保留性能分析器行为示例。
  • setProfiler(7) 配合 getProfilerInfo(1),可以在不弹窗的情况下拿到性能分析器信息。
  • __line__ 会返回所在代码行号。
  • __stack_frame 会返回调用栈帧数组;最小 toStn(...) 观察结果里,每一项是 (line, "function") 这一类二元组。

可直接照写示例

goto

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

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);

结果说明:

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

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

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

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

function Inner();
begin
    goto out_label;
end;

结果说明:

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

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

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

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

结果说明:

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

debugReturn

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

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

function Inner(bb);
begin
    debugReturn bb;
end;

结果说明:

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

debugRunEnvdebugRunEnvDo

debugRunEnv(0) / debugRunEnv(1)

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

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

结果说明:

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

debugRunEnvDo Func(...)

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

r := debugRunEnvDo Demo(2);
writeLn(r);

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

结果说明:

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

mticmtoc

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

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);

结果说明:

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

setProfilergetProfilerInfo

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

setProfiler(7);
a := 99;
b := intToStr(a);
c := rand(10, 1);
info := getProfilerInfo(1);
writeLn(ifArray(info));
writeLn(length(info) > 0);

结果说明:

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

__line____stack_frame

__line__

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

a := __line__;
writeLn(a);

结果说明:

  • 输出 3
  • 说明 __line__ 直接返回所在代码行号

__stack_frame

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

s := Outer();
writeLn(toStn(s));

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

结果说明:

代码块身份:输出片段

array(
(11,"__main__"),
(8,"Outer"))
  • 说明 __stack_frame 返回的是调用栈帧数组
  • 在这个最小例子里,可以直接看到调用位置行号和调用者函数名

禁止项

  • 不要把 debugReturn 当成普通函数 return 使用。
  • 不要假设 goto 可以跨函数、跨脚本体或跳到单独成行的 label
  • 不要给计时或性能分析器调用补未写入文档参数。
  • 不要把调试客户端副作用写成普通输出事实。