提高Delphi2010自己的调试可视化工具的速度

提高Delphi2010自己的调试可视化工具的速度,delphi,debugging,toolsapi,Delphi,Debugging,Toolsapi,我为TDataSet编写了Delphi调试可视化工具来显示当前行的值,源代码+屏幕截图:。工作很好,但速度很慢。我做了一些优化(如何获得字段名),但仍然只有20个字段需要10秒才能显示出来——非常糟糕 主要问题似乎是IoAthread90太慢。下面显示的主要代码使用的评估,这个过程花费了大部分时间,与**大约80%的时间一致。FExpression是代码中TDataset的名称 procedure TDataSetViewerFrame.mFillData; var iCount: Integ

我为TDataSet编写了Delphi调试可视化工具来显示当前行的值,源代码+屏幕截图:。工作很好,但速度很慢。我做了一些优化(如何获得字段名),但仍然只有20个字段需要10秒才能显示出来——非常糟糕

主要问题似乎是IoAthread90太慢。下面显示的主要代码使用的评估,这个过程花费了大部分时间,与**大约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;
程序TDataSetViewerFrame.mFillData;
变量
i计数:整数;
I:整数;
//sw:TStopwatch;
s:字符串;
开始
//sw:=TStopwatch.StartNew;
iCount:=strotintdef(Evaluate(FExpression+'.Fields.Count'),0);
对于I:=0到I计数-1 do
开始
s:=s+格式(“%s.Fields[%d]),字段名+“”,“+”,[FExpression,I]);
//添加(求值(格式('%s.Fields[%d].FieldName',[feexpression,I]));
添加(求值(格式('%s.Fields[%d].Value',[FExpression,I]))//**
结束;
如果是“”,那么
删除(s,长度)-4,5;
s:=评估(s);
s:=副本,2,长度-2;
FFields.CommaText:=s;
{sw.停止;
s:=sw.经过的时间;
Application.MessageBox(Pchar,'';}
结束;

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

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


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

我还没有机会使用调试可视化工具,所以我不知道这是否有效,但您是否尝试过使用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; 

由于Delphi是一个与调试的exe不同的进程,因此无法直接使用exe的内存指针,因此需要对所有内容使用“.Evaluate”

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

  • 将特殊的调试转储函数添加到可执行文件中,可执行文件在一次调用中执行所有值检索
  • 将特殊dll插入exe中,执行与1相同的操作(更多黑客攻击等)
  • 我得到了选项1的工作,2也应该是可能的,但有点复杂和“丑陋”,因为黑客战术。。。 使用以下代码(只需添加到dpr),您可以使用:

    Result := 'Dump=' + Evaluate('TObjectDumper.SpecialDump(' + FExpression + ')');
    
    选项1的演示代码,为您的TDataset更改它(可能使所有值成为CSV字符串?)

    单元1;
    接口
    类型
    TObjectDumper=class
    公众的
    类函数SpecialDump(aObj:TObject):字符串;
    结束;
    实施
    类函数TObjectDumper.SpecialDump(aObj:TObject):字符串;
    开始
    结果:='';
    如果aObj为零,则
    结果:='特殊转储:'+aObj.Classname;
    结束;
    初始化
    //伪调用,只是为了确保它是由编译器使用的c.q.链接的
    TObjectDumper.SpecialDump(无);
    结束。
    

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

    我不熟悉这些数据组件,所以我不知道如何显著提高性能。但是,您的副本可以替换为删除,这可能会更快。也许s“”可以替换为长度(s)=0。我不知道在这方面是否有任何性能提升。但是,总的来说,我认为使用=和进行字符串比较比智能SametText(s1,s2)和SameStr(s1,s2)例程慢。@Andreas-这些微优化不可能在10秒钟内完成太多工作delay@Barry凯莉:我知道。这就是为什么我写了“我不知道如何显著提高性能”。
    Length(s)0
    实际上较慢,因为Length()是一个内联函数调用,但仍然比
    s''慢,因为编译器将后者转换为
    指针nil
    。好的,作为快速测试,我构建了字段值列表(没有转义)现在的总时间约为7s(无转义),但将“s.Fields[%d].Value”替换为“%s.Fields[%d].AsString”我知道在计算器中可以执行单个过程,但我可以声明变量或执行序列语句吗?类似于var l:TStringList;ds.GetFieldsNames(l);结果:=l.CommaText;l、 免费的;随机想法-我们可以为任何类定义一个特殊的“debugDump”函数,并且只使用一个文本替换可视化程序在任何对象上调用它吗?这将在许多(如果不是大多数)情况下消除编写可视化程序的需要。好主意,但存在一个问题:Evaluate('@'+FExpression)返回0,Evaluate(Format('Addr(%s),[FExpression])返回与Addr(dataset)不同的地址或者在IDE->AV的监视列表中@dataset,例如,从可视化工具返回$23F288,同时从IDE返回@ds1=$AC46AC。代码:iAddr:=stroint(Evaluate(格式('Addr(%s)][FExpression]);Application.MessageBox(Pchar(IntToStr(i)),“”);//调试DS:=TDataSet(iAddr);所指向的实际TDataSet对象的地址与获取TDataSet指针本身地址的地址不同。我
    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.