QProfile 提供了两个函数,以供用户导出跟踪记录,进行进一步的分析。
- AsString : 生成一个 JSON 格式的跟踪记录及统计信息,用户可以通过将数据导入进行统计分析。
- AsDiagrams : 生成一个基于 mermaid 格式的调用流程图,然后我们可以直接根据该图,直观的查看函数间的调用关系。
在我们的示例程序中,我们创建了一个本地的网页,然后调用浏览器显示 AsDiagrams 的结果,在实践中,我们实际上可能只需要 AsDiagrams 的部分,通过自己的网页,将这部分内容显示出来就可以了。
我们来看示例程序:
- 在示例的源码目录下,我们将 mermaid 的 Javascript 库副本放到了 mermaid.esm.min 目录下。
- 在页面上,我们放置了一个 TIdHttpServer 组件,设置 DefaultPort 为 8090。
- 在我们界面的 ShowDiagrams 按钮的响应中,我们设置这个 IdHttpServer1.Active 为 true,并用默认浏览器打开 http://localhost:8090 这个地址。
- 在 IdHttpServer1.OnGetCommand 事件的响应中,生成一个临时的首页,并将关于 mermaid 的 JavaScript 调用进行处理。
- 在浏览器中,我们可以看到生成的调用序列示意图
首先,我们看效果:
我们可以看到:
- TForm1.Button1Click 调用了 TForm1.DoProfile,同时异步触发了 TForm1.Button1Click.ForceQueue 的调用
- TForm1.Button5Click 调用了 TForm1.DoProfile,同时异常触发了 TForm1.Button5Click.ForceQueue 的调用
- TForm1.Button1Click.ForceQueue 和 TForm1.Button5Click.ForceQueue 都是独立被调用的(直接从线程下来的)
- TForm1.Button3Click 是直接被单元调用
接下来,进入代码时间:
- 使用默认浏览器访问 http://localhost:8090
procedure TForm1.Button6Click(Sender: TObject);
begin
IdHTTPServer1.Active := true;
ShellExecute(Handle, 'open',
PChar('http://localhost:' + IntToStr(IdHTTPServer1.DefaultPort)), nil, nil,
SW_SHOWNORMAL);
end;
- 处理 HTTP GET 请求
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
function ExtractJsFileName(const S:String):String;
var
AList:TArray<String>;
begin
AList:=S.Split(['/','\']);
Result:=AList[High(AList)];
end;
procedure LoadJs;
var
AFileName: String;
begin
if ARequestInfo.Document.StartsWith
('/mermaid.esm.min/chunks/mermaid.esm.min/') then
AFileName := '../../mermaid.esm.min/chunks/' +
ExtractJsFileName(ARequestInfo.Document)
else
AFileName := '../../mermaid.esm.min/' +
ExtractJsFileName(ARequestInfo.Document);
if FileExists(AFileName) then
begin
AResponseInfo.ContentType := 'application/javascript;charset=utf-8';
AResponseInfo.ServeFile(AContext,AFileName);
end
else
AResponseInfo.ResponseNo := 404;
end;
begin
if ARequestInfo.Document.StartsWith('/mermaid.esm.min') then
LoadJs
else if ARequestInfo.Document = '/' then
begin
AResponseInfo.ContentText := '<html>' + SLineBreak + //
'<body>' + SLineBreak + //
'<pre class="mermaid">' + SLineBreak + //
TQProfile.AsDiagrams + SLineBreak + //
'</pre>' + SLineBreak + //
'<script type="module">' + SLineBreak + //
'import mermaid from "/mermaid.esm.min/mermaid.esm.min.mjs"' +
SLineBreak + //
'mermaid.initialize({ startOnLoad: true });' + SLineBreak + //
'</script>' + SLineBreak + //
'</body>' + SLineBreak + //
'</html>'; //
AResponseInfo.ContentType := 'text/html;charset=utf-8';
end
else
AResponseInfo.ResponseNo := 404;
end;