Improve speed of own debug visualizer for Delphi 2010

I wrote Delphi debug visualizer for TDataSet to display values of current row, source + screenshot: http://delphi.netcode.cz/text/tdataset-debug-visualizer.aspx . Working good, but very slow. I did some optimalization (how to get fieldnames) but still for only 20 fields takes 10 seconds to show - very bad.

Main problem seems to be slow IOTAThread90.Evaluate used by main code shown below, this procedure cost most of time, line with ** about 80% time. FExpression is name of TDataset in code.

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;

Now I have no idea how to improve performance.

Answers


That Evaluate needs to do a surprising amount of work. The compiler needs to compile it, resolving symbols to memory addresses, while evaluating properties may cause functions to be called, which needs the debugger to copy the arguments across into the debugee, set up a stack frame, invoke the function to be called, collect the results - and this involves pausing and resuming the debugee.

I can only suggest trying to pack more work into the Evaluate call. I'm not 100% sure how the interaction between the debugger and the evaluator (which is part of the compiler) works for these visualizers, but batching up as much work as possible may help. Try building up a more complicated expression before calling Evaluate after the loop. You may need to use some escaping or delimiting convention to unpack the results. For example, imagine what an expression that built the list of field values and returned them as a comma separated string would look like - but you would need to escape commas in the values themselves.


Because Delphi is a different process than your debugged exe, you cannot direct use the memory pointers of your exe, so you need to use ".Evaluate" for everything.

You can use 2 different approaches:

  1. Add special debug dump function into executable, which does all value retrieving in one call
  2. Inject special dll into exe with does the same as 1 (more hacking etc)

I got option 1 working, 2 should also be possible but a little bit more complicated and "ugly" because of hacking tactics... With code below (just add to dpr) you can use:

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

Demo code of option 1, change it for your TDataset (maybe make CSV string of all values?):

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.

Edit: in case someone is interested: I got option 2 working too (bpl injection)


I have not had a chance to play with the debug visualizers yet, so I do not know if this work, but have you tried using Evaluate() to convert FExpression into its actual memory address? If you can do that, then type-cast that memory address to a TDataSet pointer and use its properties normally without going through additional Evaluate() calls. For example:

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; 

Need Your Help

Phaser: how to group polygons in layers?

javascript phaser-framework

I want to have two polygons, one stationary and one that moves and changes its shape. When they overlap, I want the stationary polygon to stay in top.

How do I reset MathJax settings in IPython?

ipython ipython-notebook sympy mathjax jupyter

I wanted to change the formatting of my equations in the IPython notebook (Jupyter) and right clicked on the equation to bring up the MathJax menu. I selected SVG as the rendering type. Now, I'm