[教程]QProfile教程之四:生成基于 Mermaid 的调用序列示意图

QProfile 提供了两个函数,以供用户导出跟踪记录,进行进一步的分析。

  1. AsString : 生成一个 JSON 格式的跟踪记录及统计信息,用户可以通过将数据导入进行统计分析。
  2. 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;

滚动至顶部