15

I am once again writing an external debugger visualizer, and am running into a wall. There appears to be a limit to the size of the string that the debugger visualizer can return.

The TStrings debugger visualizer that shipped with Delphi 2010 had a limit of 4K. In a response to a question posted on Embarcadero's newsgroups, Ewe Schuster replied that "You can increase the buffer a little bit, but AFAIR the actual limitation is in IOTAThread.Evaluate with a limit of about 12k chars."

My debugger visualizer is based on the code of the TStrings debugger visualizer, and I can see that the implementation of the TFrame's Evaluate method includes the following declaration of ResultStr, which is used to return the string returned from the IOTAThread.Evaluate call:

  ResultStr: array[0..4095] of Char;

I had hoped that increasing the size of this buffer would help, but no luck.

What can I do, if anything, to increase the size of the string that my external debugger visualizer can display?

Cary Jensen
  • 3,751
  • 3
  • 32
  • 55

1 Answers1

12

I had the same limited patience for that limit... So I made a debug visualizer that work around the limitation mostly by creating a MemoryStream in the debugged process to hold the string result of the Expression, then use something like

CurProcess.ReadProcessMemory(StrToInt(SrcMemoryAddr), DstMemStream.Size, DstMemStream.Memory^);

to copy it into a visualizer's MemoryStream. Then you can do whatever you want to display it (for instance formatting it for human reading if it's an XML string like a CLientDataSet.XMLData).

There are few tricks depending if the Expression is a const string, a var, or needs evaluation etc...

My FGStringVisualizer is not 100% satisfying, which is why I haven't published it yet on my blog, but as it does 99% of what I need, I didn't take the time to clean it a bit and publish it even "as-is". But if there is a need I can certainly do it with all needed disclaimers...

Update: It's the same idea that I used for my FGStringListVisualizer that I presented at the last DelphiLive. By the way, this one also might be worth putting on my blog as I made a few improvements since.

Francesca
  • 21,452
  • 4
  • 49
  • 90
  • 1
    Fantastic. Thank you very much. However, I do not completely understand the answer. I use CurProcess to get CurrentThread, and then call Evaluate on CurrentThread to get the value of the expression (which is a string in my case). How does the code you provided work into this equation. Is your ScrMemoryAddr variable the out ResultAddr parameter of Evaluate. and the DstMemStream your visualizer's TMemoryStream? – Cary Jensen Mar 01 '12 at 18:54
  • 1
    I call `Evaluate` to Create the MemoryStream in CurProcess: `if not Evaluate(DebugSvcs, CurProcess, CurThread, 'NativeInt(TMemoryStream.Create)', SrcMemStreamAddr)then`. Then I use `Evaluate` again to put the string into it: `if not Evaluate(DebugSvcs, CurProcess, CurThread, 'TMemoryStream(' + SrcMemStreamAddr + ').Write((' + FExpression + '), Length(' + FExpression + ')*SizeOf(Char))', CurSizeResult) then`... That's where it gets murky depending on the nature of Expression... – Francesca Mar 01 '12 at 19:07
  • 1
    Please excuse my ignorance, but I cannot find an Evaluate method that takes an IOTADebuggerServices, IOTAProcess, and IOTAThread as its first three parameters. Is this a custom method you wrote. If not, on which interface will I find this version of Evaluate? – Cary Jensen Mar 01 '12 at 19:37
  • 1
    You're right. It's based on Delphi sample (`TStringListViewerFrame.Evaluate`), but I moved the DebugSvcs, CurProcess and CurThread outside of it's scope to reuse them... – Francesca Mar 01 '12 at 19:47
  • @François, rather than use `Evaluate()` twice to create a `TMemoryStream` and `Write()` to it separately, have you tried calling `Evaluate()` just once and pass `FExpression` to the `TStringStream` constructor? `Evaluate()` has a lot of overhead to it, so you should minimize its use. – Remy Lebeau Mar 01 '12 at 23:24
  • @Remy. You don't necessarily have the `TStringStream` class in the debugged project, while, as soon as you include `Classes`, you have `TMemoryStream` available. And `TMemoryStream` does not have a 1 step constructor-writer like `TStringStream`... – Francesca Mar 09 '12 at 02:46
  • `TStringStream` is also in the `Classes` unit. And yes, I know `TMemoryStream` does not have a 1-step writing constructor, that is the point I was making. If you can construct a stream and write to it in a single call to `Evaluate()`, the better the performance. – Remy Lebeau Mar 09 '12 at 02:58
  • @Remy, If the project does not use `TStringStream`, it is simply not linked into the debugged project, while `TMemoryStream` is there due to the initialization clause. That's the point I was making. :) – Francesca Mar 09 '12 at 08:55
  • @François when I try "Evaluate('NativeInt(TMemoryStream.Create)'" the eval result is erDeferred and never finish! any idea? – Agustin Seifert Nov 04 '13 at 22:13
  • @François, do you have the source for your visualizer? Would love to play with it. – Johan Apr 10 '15 at 12:55