When an error occurs in my Delphi XE5 application, in my exception handler I am trying to get Jedi Code Library's JCLDebug.pas to show any parameters of method calls listed on the stack. I was hoping to get this information in a similar way to the way the Delphi IDE shows this information in its Call Stack debug window when you break during an exception, like this:
TestForm.DoSomething('A Test Parameter')
TestForm.TTestForm.DoSomethingClick($DF935B0)
VCL.Controls.TControl.Click
In my error handler, I get this a result similar to this with no parameter information:
[00F9701D]{Test.exe} TestForm.DoSomething$qqrx20System.UnicodeString (Line 138, "TestForm.pas")
[005E358B]{TestMonitor.exe} Vcl.Controls.TControl.Click$qqrv (Line 7340, "Vcl.Controls.pas")
I have initialised my error handling by calling during the units initialization section:
JclStackTrackingOptions:=JclStackTrackingOptions + [stStack, stRawMode];
JclStartExceptionTracking;
Then when the exception handler is invoked, I call the following to get the call stack strings:
JclLastExceptStackListToStrings(FErrors, True, False, False);
Is it possible to get the parameters the methods were called with using JCLDebug?
Related
In Jenkins pipeline build, sometimes I've seen null pointer or other exceptions like -
java.lang.NullPointerException: Cannot invoke method trim() on null object
Generally if we run Java program through IDE or command line, if an exception occurs we see at which line number the exception has occurred.
But with Jenkins build output console, it does not show the line number where the exception has occurred.
In this case, based on method name ie trim() from log, I check wherever trim() method is used. But as I've used it at multiple places in same method, it becomes difficult to identify exactly where error has occurred.
Another way is to add echo statements and re-run build and see where it gives this exception but this is time consuming.
Is there any better way/plugin using which I can identify at which line of pipeline code exception has occurred?
I don't really know if it's possible to show the exact line number, but you can wrap your code in try-catch statements and then show the exception info in the catch, like so:
try {
// line with trim()
catch (ex) {
println "Exception while trimming: $ex"
}
I have an VCL app containing an object TDownloadUrl (VCL.ExtActns) used to download applications, my question is how to handle any kind of exception that restrict to download [for example:- like download failed or invalid URL link or internet failure or url not reachable or internet not available] using TDownLoadURL?.
Thanks in advance
TDownloadURL only defines 2 error messages, which are both declared in the Vcl.Consts unit:
SUrlMonDllMissing, which is raised when the Win32 URLDownloadToFile() function cannot be accessed at runtime.
SErrorDownloadingURL, which is raised when URLDownloadToFile() fails for any reason. Unfortunately, there is no way to differentiate why URLDownloadToFile() fails (although the OnProgress event may provide information about what it was doing just before the failure occurred).
The error messages are resource strings and thus can be localized, so they could potentially be in any language, not just English. And they are raised using the general SysUtils.Exception class itself, not any derived types. However, you can use them for substring matching, at least:
uses
..., Vcl.ExtActns, Vcl.Consts, System.StrUtils;
try
DownloadURL1.Filename := ...;
DownloadURL1.URL := ...;
DownloadURL1.Execute;
except
on E: Exception do
begin
if StartsText(SUrlMonDllMissing, E.Message) then
...
else if StartsText(SErrorDownloadingURL, E.Message) then
...
else
...
end;
end;
If you need more detailed error information, you might try calling URLDownloadToFile() directly, as it returns an HRESULT value. However,
be careful by the following gotcha in the documentation:
URLDownloadToFile returns S_OK even if the file cannot be created and the download is canceled. If the szFileName parameter contains a file path, ensure that the destination directory exists before calling URLDownloadToFile. For best control over the download and its progress, an IBindStatusCallback interface is recommended.
If that does not solve your issue, then you should use a different HTTP client API/library to perform the download, such as the HTTP client in Indy, ICS, Synapse, WinInet/WinHTTP, libCURL, etc.
I've not used this component, but it likely generates different exception types based on the errors it encounters. If that's the case then the article here covers handling multiple exception types:
Delphi Exception handling problem with multiple Exception handling blocks
I have an exception happening during runtime which is non reproducible and therefore I can't debug it in the IDE/debugger. I want to know where in the code this happens so I surrounded the code with try/except statement, inserted a 'raise exception' statement for testing & displayed the stacktrace like this to see if it works:
on e: exception do begin
showmessage(e.StackTrace);
end;
However the message displayed was empty. Why was it empty? Is there another way to know where an exception happened? Using Delphi XE.
The Exception.StackTrace documentation indicates that the default implementation returns an empty string, and that in order to use the functionality you need to provide a GetStackInfoStringProc procedure. It also suggests some implementations (both free and commercial) from third-party providers that can be used instead of writing your own.
Here's a (brief) excerpt from that documentation - visit the documentation page for the related links:
By default, StackTrace is always an empty string. If you want StackTrace to contain an actual value, you must assign GetStackInfoStringProc a procedure that can generate a string from the stack information.
Using Exception.StackTrace
In order to use the StackTrace property to obtain the stack trace for exceptions, you have to use (or implement) a stack trace provider. There are a number of third-party solutions, both commercial and free.
Some of the stack trace providers are:
JEDI Code Library (the JclDebug and the JCLHookExcept unit).
EurekaLog
madExcept
For more information on how to use some of the above third-party stack trace providers, see the following blog posts:
Working with Delphi’s new Exception.StackTrace (Tobias Gurock, 2009).
CodeVerge - How to use Exception.StackTrace (2008).
However the message displayed was empty. Why was it empty?
Like Ken said, the StackTrace property is not implemented by default, per its documentation. You have to install a 3rd party exception logger with stack trace capabilities to hook up the StackTrace property.
Is there another way to know where an exception happened?
Without a viable stack trace, you would have to obtain the memory address of the code instruction where the exception actually occurred and then compare it to the list of addresses stored in the generated .map file for your compiled executable (if enabled in the Project Options, which is also necessary for stack tracing). With that, you can deduce which function the crashing code belongs to, but not necessarily the exact line of code.
If the exception is an EExternal-derived exception (like EAccessViolation), you can get the memory address from the EExternal.ExceptionRecord field (Windows only), or the EExternal.ExceptionAddress field (*Nix/OSX/iOS). Otherwise, use the System.ExceptAddr() function.
Another solution is to use synopse framework, take a look at TSQLLog class in the documentation.
You only have to generate map file (converting to .mab by the framework) and distribute it with your application.
Add this uses in your project file: Syncommons, momrmot and synlog.
uses
Vcl.Forms,
SynCommons,
mORMot,
SynLog,
F_calc in 'F_calc.pas' {Form1};
{$R *.res}
begin
TSQLLog.Family.Level := LOG_STACKTRACE; // LOG_VERBOSE
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
A log file will be generate.
You can read it with this sample program (in the framework):
Synopse\SQLite3\Samples\11 - Exception logging\logview.dpr
I'm using a TOpenPictureDialog to browse for images. In debug mode, when I'm browsing in this dialog for a picture and I happen to click (not double click) on a shortcut (.lnk), the debugger interrupts my program because it catches an exception, saying it's not a valid file format.
How to overcome this? I know it's only in debug time and don't have any issue in the final EXE, but it is getting very annoying, because I'd like to be able to go through these shortcuts.
You are out of luck here, this bug was reported in the QC 69533 and was fixed in the update 3 of Delphi 2009.
The code used to by the VCL to verify if a file is valid image, doesn't check for the shortcuts files (.lnk) so the VCL thinks which the file is a valid image and try to load the file and then raises a EInvalidGraphic exception.
The exception is only raised in the debugger because a code like this is used to check the validate the filename.
(Only showing part of the real code because is VCL code)
ValidPicture := FileExists(FullName) and ValidFile(FullName);
if ValidPicture then
try
// here try to load the file even if is a shortcut(.lnk)
except //this exception is caught by the debugger.
ValidPicture := False;
end;
Workarounds
1) You can add the EInvalidGraphic exception, to the exceptions list to ignore list.
2) you can write a detour (here you have a sample) and implement your own TOpenPictureDialog.DoSelectionChange method (validating the .lnk files), because is here where is made the validation of the files to load.
3) you can override the DoSelectionChange method of the TOpenPictureDialog using a interposer class, to validate the files to load.
TOpenPictureDialog= class (ExtDlgs.TOpenPictureDialog)
procedure DoSelectionChange; override;
end;
I am displaying a stack trace in DUnit using JCLDebug (Click Here for more info). See the stack trace below.
The stack trace shows all calls up to the function that calls the actual function that causes the error. Line 238 of ConvertScriptTestU calls another function called "GetDataLine" (which is missing from the stack trace) that then calls IntToStr (which raises EConvertError). Why does the stack trace not include GetDataLine ?
This stack trace is a lot less useful as I still have to work out which line in GetDataLine is causing the EConvertError.
I am using DUnit here but I have seen the same thing happen when using JclDebug outside DUnit.
[THE ANSWER]
Just add this line to your project file:
JCLdebug.JclStackTrackingOptions:=[stStack, stRawMode];
It turns on "Raw Mode". You might need to tweak some compiler options too (e.g. 'generate stack frames')
[/THE ANSWER]
TestConversion: EConvertError
at SysUtils:0 SysUtils.ConvertErrorFmt$00414A7E
'''' is not a valid integer value
StackTrace
[00414A79]{ConvertDataTests.exe} SysUtils.ConvertErrorFmt
[007E2699]{ConvertDataTests.exe} ConvertScriptTestU.ConvertDataTest.WriteDataToFile (Line 238, "ConvertDataTestU.pas")
[007E2198]{ConvertDataTests.exe} ConvertScriptTestU.ConvertDataTest.TestConversion (Line 164, "ConvertDataTestU.pas")
[0053C66E]{ConvertDataTests.exe} TestFramework.TTestCase.RunTest (Line 2380, "TestFrameWork.pas")
[00537DBA]{ConvertDataTests.exe} TestFramework.TTestResult.RunTestRun (Line 1199, "TestFrameWork.pas")
[00538078]{ConvertDataTests.exe} TestFramework.TTestResult.Run (Line 1275, "TestFrameWork.pas")
[0053963A]{ConvertDataTests.exe} TestFramework.TAbstractTest.RunWithFixture (Line 1723, "TestFrameWork.pas")
[0053C4DD]{ConvertDataTests.exe} TestFramework.TTestCase.RunWithFixture (Line 2363, "TestFrameWork.pas")
It could depend on how you compiled the application. For debugging purposes, you should set Stack Frames to On in compiler code generation options, and ensure all needed units are recompiled, and their symbols are available. Otherwise the compiler may choose not to generate them for some functions. Hand coded pure assembler functions may not have stack frames anyway, IIRC. Another issue could arise if the function is inlined in Delphi releases that supports that. Also, which kind of stack trace did you set up in JCL debug? IIRC it supports two methods, standard and "raw".
This sort of thing happens to stack traces a lot when errors get raised. Since there's no official API for doing stack traces, the trace generator has to pick its way backwards through the stack, one frame at a time. But if any of the intervening functions doesn't set up a stack frame, or if for some reason it does something that plays around with the stack in strange ways, then it can throw the tracer for a loop.
This will probably be a lot more reliable in 64-bit Delphi, because Windows mandates a certain style of stack frames so that it can unwind the stack properly if an exception gets raised, but for 32-bit it's something that we just have to live with.
Procedures or functions where the arguments can fit in the registers EAX, EDX and ECX, do not use a stack frame when using Delphi's default register calling convention.
If you use the stdcall or cdecl calling conventions, calling that routine will always use the stack.
If you set stack frame on {$W+} or {$STACKFRAMES ON} Delphi will always generate stack frames, even when they're not needed.
I recommend you set this compiler switch in a conditional define
{$IFDEF DEBUG}
{$W+}
{$ELSE}
{$W-}
{$ENDIF}
See: http://docwiki.embarcadero.com/RADStudio/XE/en/Stack_frames_(Delphi)