6.8 KiB
6.8 KiB
Debug And Profiler
文档类型:语法主线 是否可直接用于生成代码:仅部分 是否含已验证可执行示例:是 是否含已验证反例:是 遇到不确定时跳转到:08_control_flow.md、30_runtime_services_and_global_cache.md、12_pitfalls.md
手册位置:第 16 篇,共 32 篇。上一篇:15_ts_sql.md。下一篇:18_lexical_structure_and_compile_options.md。
这一篇收拢当前已经验证过的调试、计时、profiler 和调用栈相关入口。
这一篇解决什么问题
回答“goto、DebugReturn、DebugRunEnv、MTIC / MTOC、SetProfiler、__line__ 和 __stack_frame 在当前解释器里怎样写、会怎样表现”。
必须记住的规则
goto label_name;当前已验证可用,但目标位置当前以label label_name; statement这种内联形式最稳。- 当前正向验证只覆盖“跳到同一函数 / 同一脚本体后面的位置”,不要先把更复杂的跨层跳转边界写成事实。
DebugReturn value;在当前解释器里会直接结束整段脚本,后面的语句不会继续执行。DebugRunEnv(0)和DebugRunEnv(1)当前都可直接调用;它们面向调试客户端的副作用,在 CLI 里不直接可见。DebugRunEnvDo Func(...)当前已验证可直接写,并且会返回被调用函数的结果。MTIC会生成一个计时起点;MTOC和MTOC(tick)当前都会返回秒数。SetProfiler(7)配合GetProfilerInfo(1),当前可以在不弹窗的情况下拿到 profiler 信息。__line__当前会返回所在代码行号。__stack_frame当前会返回调用栈帧数组;最小ToSTN(...)观察结果里,每一项是(line, "function")这一类二元组。
已验证语法
goto
代码块身份:已验证可执行示例
program test;
begin
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);
end.
已验证运行结果:
- 依次输出
1、9 - 说明
goto found_label;当前可以跳到后面的label found_label; ... - 也说明当前最稳的目标写法是把
label和第一条目标语句放在同一行
跨函数跳转当前没有通过:
代码块身份:反例 / 不可照写
program test;
function Inner();
begin
goto out_label;
end;
begin
Inner();
label out_label; WriteLn(1);
end.
已验证结果:
- 上面这段会运行报错,核心信息是
Goto label can not found! - 当前正向验证只覆盖“同一函数 / 同一脚本体后面的位置”
- 不要把
goto泛化成能跨函数跳到外层label
目标 label 单独成行当前也没有通过:
代码块身份:反例 / 不可照写
program test;
begin
goto done_label;
label done_label;
WriteLn("after");
end.
已验证结果:
- 上面这种把
label单独放一行、下一行再写目标语句的最小例子,在当前解释器里会报Statement missing terminator - 因此当前手册只把
label name; statement这种内联形式写成已验证事实
DEBUGRETURN
代码块身份:已验证可执行示例
program test;
function Inner(bb);
begin
DebugReturn bb;
end;
begin
WriteLn("before");
a := Inner(3);
WriteLn("after");
end.
已验证运行结果:
- 只输出
before - 说明
DebugReturn bb;不只是结束Inner(...),而是直接让整段脚本提前返回 - 因此
Inner(3)后面的WriteLn("after")当前不会执行
DebugRunEnv 与 DebugRunEnvDo
DebugRunEnv(0) / DebugRunEnv(1):
代码块身份:已验证可执行示例
program test;
begin
a := 1;
DebugRunEnv(0);
DebugRunEnv(1);
WriteLn(1);
end.
已验证运行结果:
- 输出
1 - 说明这两个调用在当前 CLI 环境里至少能正常执行,不会中断后续语句
- 但它们把变量 / 系统参数送到调试窗口的效果,在 CLI 中不能直接观察到
DebugRunEnvDo Func(...):
代码块身份:已验证可执行示例
program test;
function Demo(x);
begin
y := x + 1;
return y;
end;
begin
r := DebugRunEnvDo Demo(2);
WriteLn(r);
end.
已验证运行结果:
- 输出
3 - 说明
DebugRunEnvDo Demo(2)当前可以直接写 - 也说明它当前会把被调用函数的结果继续返回给外层
MTIC 与 MTOC
代码块身份:已验证可执行示例
program test;
begin
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);
end.
已验证运行结果:
- 依次输出
1、1 - 说明
MTOC(t1)和无参MTOC当前都能返回可用的秒数结果
SetProfiler 与 GetProfilerInfo
代码块身份:已验证可执行示例
program test;
begin
SetProfiler(7);
a := 99;
b := IntToStr(a);
c := rand(10, 1);
info := GetProfilerInfo(1);
WriteLn(IfArray(info));
WriteLn(Length(info) > 0);
end.
已验证运行结果:
- 依次输出
1、1 - 说明
SetProfiler(7)当前可以开启 profiler 统计 - 说明
GetProfilerInfo(1)当前会直接返回 profiler 信息,而且结果是非空数组
__line__ 与 __stack_frame
__line__:
代码块身份:已验证可执行示例
program test;
begin
a := __line__;
WriteLn(a);
end.
已验证运行结果:
- 输出
3 - 说明
__line__当前直接返回所在代码行号
__stack_frame:
代码块身份:已验证可执行示例
program test;
function Inner();
begin
return __stack_frame;
end;
function Outer();
begin
return Inner();
end;
begin
s := Outer();
WriteLn(ToSTN(s));
end.
已验证运行结果:
代码块身份:已验证输出片段
array(
(11,"__main__"),
(8,"Outer"))
- 说明
__stack_frame当前返回的是调用栈帧数组 - 在这个最小例子里,可以直接看到调用位置行号和调用者函数名
跳转指引
- 回看主线流程控制:见 08_control_flow.md
- 运行时环境参数:见 11_runtime_context_and_with.md
- 回看语法主入口:见 index.md
- 看运行时服务和全局缓存:见 30_runtime_services_and_global_cache.md