9

我为 TDataSet 编写了 Delphi 调试可视化工具来显示当前行的值,源 + 屏幕截图:http ://delphi.netcode.cz/text/tdataset-debug-visualizer.aspx 。工作很好,但很慢。我做了一些优化(如何获取字段名),但仍然只有 20 个字段需要 10 秒才能显示 - 非常糟糕。

主要问题似乎是慢 IOTAThread90.Evaluate 使用的主要代码如下所示,这个过程花费了大部分时间,与 ** 大约 80% 的时间一致。FExpression 是代码中 TDataset 的名称。

procedure TDataSetViewerFrame.mFillData;
var
 iCount: Integer;
 I: Integer;
 //  sw: TStopwatch;
 s: string;
 begin
 //  sw := TStopwatch.StartNew;
   iCount := StrToIntDef(Evaluate(FExpression+'.Fields.Count'), 0);
   for I := 0 to iCount - 1 do
   begin
     s:= s + Format('%s.Fields[%d].FieldName+'',''+', [FExpression, I]);
  //  FFields.Add(Evaluate(Format('%s.Fields[%d].FieldName', [FExpression, I])));
     FValues.Add(Evaluate(Format('%s.Fields[%d].Value', [FExpression, I]))); //**
   end;
 if s<> '' then
   Delete(s, length(s)-4, 5);
 s := Evaluate(s);
 s:= Copy(s, 2, Length(s) -2);
 FFields.CommaText := s;
{  sw.Stop;
 s := sw.Elapsed;
 Application.MessageBox(Pchar(s), '');}
end;

现在我不知道如何提高性能。

4

3 回答 3

9

该 Evaluate 需要做大量的工作。编译器需要编译它,将符号解析为内存地址,而评估属性可能会导致函数被调用,这需要调试器将参数复制到被调试者中,设置堆栈帧,调用要调用的函数,收集结果 - 这涉及暂停和恢复被调试者。

我只能建议尝试在Evaluate通话中加入更多工作。我不是 100% 确定调试器和评估器(它是编译器的一部分)之间的交互如何适用于这些可视化工具,但尽可能多地处理工作可能会有所帮助。Evaluate在循环之后调用之前尝试构建一个更复杂的表达式。您可能需要使用一些转义或定界约定来解压缩结果。例如,想象一下构建字段值列表并将它们作为逗号分隔的字符串返回的表达式是什么样的 - 但您需要在值本身中转义逗号。

于 2010-03-31T20:43:38.953 回答
5

因为 Delphi 与您调试的 exe 是不同的进程,所以您不能直接使用 exe 的内存指针,因此您需要对所有内容使用“.Evaluate”。

您可以使用 2 种不同的方法:

  1. 将特殊的调试转储功能添加到可执行文件中,一次调用即可检索所有值
  2. 将特殊的 dll 注入到 exe 中,其作用与 1 相同(更多黑客攻击等)

我得到了选项 1 的工作,2 也应该是可能的,但由于黑客策略有点复杂和“丑陋”......使用下面的代码(只需添加到 dpr)你可以使用:

Result := 'Dump=' + Evaluate('TObjectDumper.SpecialDump(' + FExpression + ')');

选项 1 的演示代码,为您的 TDataset 更改它(也许制作所有值的 CSV 字符串?):

unit Unit1;

interface

type
  TObjectDumper = class
  public
    class function SpecialDump(aObj: TObject): string;
  end;

implementation

class function TObjectDumper.SpecialDump(aObj: TObject): string;
begin
  Result := ''; 
  if aObj <> nil then 
    Result := 'Special dump: ' + aObj.Classname;
end;

initialization
  //dummy call, just to ensure it is linked c.q. used by compiler
  TObjectDumper.SpecialDump(nil);

end.

编辑:如果有人感兴趣:我也有选项 2 工作(bpl 注入)

于 2010-08-17T09:53:20.250 回答
0

我还没有机会使用调试可视化工具,所以我不知道这是否可行,但是您是否尝试过使用 Evaluate() 转换FExpression为其实际内存地址?如果可以做到这一点,则将该内存地址类型转换为TDataSet指针并正常使用其属性,而无需进行额外的 Evaluate() 调用。例如:

procedure TDataSetViewerFrame.mFillData; 
var 
  DS: TDataSet;
  I: Integer; 
  //  sw: TStopwatch; 
begin 
  //  sw := TStopwatch.StartNew; 
  DS := TDataSet(StrToInt(Evaluate(FExpression)); // this line may need tweaking
  for I := 0 to DS.Fields.Count - 1 do 
  begin 
    with DS.Fields[I] do begin
      FFields.Add(FieldName);
      FValues.Add(VarToStr(Value));
    end;
  end; 
  {
  sw.Stop; 
  s := sw.Elapsed; 
  Application.MessageBox(Pchar(s), '');
  } 
end; 
于 2010-04-01T20:10:10.643 回答