In an delphi 2007 application, we have used madExcept to handle exeptions for us. We use the standard dialog, letting the user send us a report with mail if he want to.
We want to add a little to this functionality. If the user choose not to send a full report, we still want to log the exception to a file, but not a full report. We want the exception class and message, and the source file and line number.
The first two is easy, but I can't find a straight forward way of finding the latter.
I have the full exception report in the form of an IMEException interface. As far as I can see, the call stack is available only through the BugReportSections property, which gives me the stack in plain text.
In madStackTrace.pas, there is a StackTrace( )-function that should give me what I want. It takes a lot of parameters, and the documentation is a little short on information..
Question:
Does anybody know the right parameters to feed into StackTrace( ), so that it only return the top item of the stack from the thread where the exception was thrown?
madStackTrace.pas has another function, StackAddrToStr that just takes a single address and returns the formatted text for it. If you're in the except block for the crashing thread you can pass the global ExceptAddr variable to it. If you're in one of the madExcept callbacks use `IMEException.ExceptAddr' instead.
Related
Right now I'm struggling to solve a bug that caused by the dataset mode at Delphi (using ADODataset),
details as below for the add button mechanism :
IDMain: =self.DBTextIDUser.Caption+'-'+self.DBEditWorkingDate.Text;
datamodule1.ADODataSetWorkingDetails.Append;
with datamodule1.ADODataSetWorkingDetails do
begin
dbgridworkinghours.Fields[0].AsString := IDMain;
dbgridworkinghours.Fields[7].AsString := self.DBTextIDUser.Caption;
dbgridworkinghours.Fields[8].AsString := self.DBTextName.Caption;
dbgridworkinghours.Fields[9].AsString := self.DBEditWorkingDate.Text;
dbgridworkinghours.Fields[11].AsString := self.DBTextPeriod.caption;
dbgridworkinghours.Fields[10].AsString := self.DBTextToday.Caption;
end;
I already set the adodataset to append mode at the save button :
datamodule1.ADODataSetWorkingDetails.post;
when I click the save button, an error appears that:
The adodataset not in edit/insert mode
I already used this mechanism at the other form and it works
note: I already tried setting the adodataset mode to insert, but still faced the same error
What #kobik said.
Your problem is most likely being caused by something you haven't told us in your q.
I think the important thing is for you to find out how to debug this sort of thing
yourself, so that even if you don't understand the cause, you can at least isolate
it and provide better information when you ask for help here. So I'm going to
outline how to do that.
In your Project Options, check the box "Use Debug DCUs"
Set up two event handlers, for your ADODataSetWorkingDetails's
AfterPost and AfterScroll events, put a some "do nothing" code in both of them
(to stop the IDE removing them). Put a debugger breakpoint on the first line
inside the AfterScroll handler, but not (yet) the AfterScroll one.
Compile and run your program.
You should find that somewhere after you call Append but before you click your
Save button, the debugger stops on your AfterPost breakpoint.
When it does,
go to View | Debug windows | Call stack. This will show you a list of
program lines, the one at the top being the one closest to where the breakpoint
tripped. This will likely be deep inside the VCL's run-time code (which is why
I said to check "Use Debug DCUs". Scroll down the list towards the bottom, and
eventually you should come to a line which is the cause of why Post was called.
If it isn't obvious to you why the AfterPost event was called, put a breakpoint
on your Append line and run the program again. When this breakpoint trips,
put another breakpoint inside your AfterScroll event, resume the program
by pressing F9 and see if the AfterScroll breakpoint is hit. If
it is, again view the Call stack and that should show you why it was called -
if it isn't obvious, then add the contents of tthe Call stack window to your q.
If the cause is obvious, then change your code to avoid it.
The reason I've gone on about the AfterScroll event is that what isn't obvious
is that when your code causes a dataset to scroll, any pending change (because
the dtaset is in dsInsert or dsEdit state will cause the change to be posted
and you will then got the error you've quoted if you try to call Post on the
dataset again. Calling Append initially sets a dataset into dsInsert state, btw.
See if you can at least identify what is causing your dataset to post before
it is supposed to, and let us know in a comment to your q or this answer.
Btw, I strongly recommend that you get out of the habit of using the with construct in your code. Although it may save you a bit of typing, in the long term it will likely make bugs far more likely to happen and far harder to find.
Update TDataSet and its descendants have a State property which is of type TDataSetState (see
DB.Pas). Normally, for browsing data and navigating around the dataset, the
dataset is in dsBrowse state. If you call Edit or Append (or Insert), the dataset
is temporarily put in dsEdit or dsInsert state, respectively. Various routines in DB.Pas
check the dataset state before certain operations are performed and raise an exception if the
DataSet in not in the correct state for the operation to go ahead. It is very, very likely
that it is one of these checks that is giving you the exception.
My original hunch was that your error was occurring because something was happening which
was causing Post to be called, because if Post succeeds, it puts the dataset back into
dsBrowse state, so when clicking your Save button calls Post, the dataset is already
in dsBrowse state. You can, of course, put a breakpoint in TDataSet.Post in DB.Pas
check which state the dataset is actually in when it is called.
There are two other main possibilities for the cause of your exception, namely that
either TDataSet.Cancel or the general Abort method is being called. To investigate
these, put breakpoints on the first lines inside TDataSet.Cancel (in DB.Pas) and
Abort (in SysUtils.Pas). If either of these breakpoints trips between you calling
Append and Post, then you can use the Call Stack view to try and figure
out why execution has reached there.
I am getting the following error on iOS 9:
-[UIApplication_handleNonLaunchSpecificActions:
forScene:
withTransitionContext:
completion:] unhandled action ->
<FBSSceneSnapshotAction: 0x150b2aef0>
{
handler = remote;
info = <BSSettings: 0x15333f650>
{
(1) = 5;
};
}
Has anyone else come across this error or it's implications? What is wrong?
There is nothing wrong with your code. This is a logging message internal to Apple, and you should file a radar about it.
There are two hints that show that this is probably Apple's code:
The underscore leading the method name _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion is a convention indicating that the method is private/internal to the class that it's declared in. (See this comment.)
It's reasonable to guess that the two letter prefix in FBSSceneSnapshotAction is shorthand for FrontBoard, which according to Rene Ritchie in "iOS 9 wish-list: Guest Mode" is part of the whole family of software related to launching apps:
With iOS 8, Apple refactored its system manager, SpringBoard, into several smaller, more focused components. In addition to BackBoard, which was already spun off to handle background tasks, they added Frontboard for foreground tasks. They also added PreBoard to handle the Lock screen under secure, encrypted conditions. [...]
I have no idea what the BS prefix in BSSettings is for, but an analysis of this log message would indicate that it's not anything you did, and you should file a radar with steps to reproduce the logging message.
If you want to try and grab a stack trace, you can implement the category linked to here. Some would argue that overriding private API is a bad idea, but in this case a temporary injection to grab a stack trace can't be too harmful.
EDIT:
But, we still want to know what this action is. So I put a breakpoint on -[UIApplication _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion] and started printing out register values and found a class called FBSceneImpl which had a whole bunch of information about my application:
We are able to find out which private method is called next (stored in the program counter, register 15.)
I tried finding the un-handled FBSceneSnapshotAction referenced in the log, but no dice. Then, I subclassed UIApplication, and overrode _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion. Now I was able to get at the action directly, but still, we don't know what it is.
Then, I looked at the FBSceneSnapshotAction again. Turns out it has a superclass called BSAction.
Then I wrote a tool similar to RuntimeBrowser and looked up all of the subclasses of BSAction. It turns out that there's quite a list of them:
The two method names we have (one from the log and one from the program counter on the devices) indicate that these actions are used under the hood for passing actions around the system.
Some actions are probably sent up to the app delegate's callbacks, while others are handled internally.
What's happening here is that there is an action that wasn't handled correctly and the system is noting it. We weren't supposed to see it, apparently.
Is it possible to see the list of watches with the realtime value when I am not at a breakpoint?
I have defined an integer Mycounter in the public section of a TDatamodule.
I add it to the Watch list.
I can see its value being updated in the watch list only as I am debugging line by line or I am at a rbeakpoint.
Is there a way to keep seeing its value in the watch list even when the control goes back to the VCL thread (=when I press F9)?
As David said, a watch is not possible but you could get close by adding a data breakpoint to your variable and have it dump it's value to the Event Log each time it changes.
Steps
Get the address of the variable you like to track.
Add a databreakpoint
Open the Event Log debugging window
Get the address of a variable
I have used the variable I in my example. As per your example, this should be #Mycounter
Adding the Data Breakpoint
Use the address of the variable
Uncheck the Break checkbox
Evaluate the expression PInteger($45622C)^
Log the result
Viewing the results
There is no way to do this from the IDE. Watch evaluation requires all the threads in the process to be suspended so that the debugger can read the memory and perform the evaluation.
If you want to see values being refreshed without the debugger breaking, you would most likely have to add code to your application to instrument the values of interest. For example, add code to your application output debug messages (e.g. OutputDebugString(), CodeSite etc.) whenever the value changed. You would of course need to view the information in a separate viewer.
I know how to create a .map file to track down access violation errors when the error message includes an actual address.
But what if the error message says
Access violation at address 00000000. Read of address 00000000.
Where do I start looking for the cause of this problem... ?
The accepted answer does not tell the entire story.
Yes, whenever you see zeros, a NULL pointer is involved. That is because NULL is by definition zero. So calling zero NULL may not be saying much.
What is interesting about the message you get is the fact that NULL is mentioned twice. In fact, the message you report looks a little bit like the messages Windows-brand operating systems show the user.
The message says the address NULL tried to read NULL. So what does that mean? Specifically, how does an address read itself?
We typically think of the instructions at an address reading and writing from memory at certain addresses. Knowing that allows us to parse the error message. The message is trying to articulate that the instruction at address NULL tried to read NULL.
Of course, there is no instruction at address NULL, that is why we think of NULL as special in our code. But every instruction can be thought of as commencing with the attempt to read itself. If the CPUs EIP register is at address NULL, then the CPU will attempt to read the opcode for an instruction from address 0x00000000 (NULL). This attempt to read NULL will fail, and generate the message you have received.
In the debugger, notice that EIP equals 0x00000000 when you receive this message. This confirms the description I have given you.
The question then becomes, "why does my program attempt to execute the NULL address." There are three possibilities which spring to mind:
You have attempt to make a function call via a function pointer which you have declared, assigned to NULL, never initialized otherwise, and are dereferencing.
Similarly, you may be calling an "abstract" C++ method which has a NULL entry in the object's vtable. These are created in your code with the syntax virtual function_name()=0.
In your code, a stack buffer has been overflowed while writing zeros. The zeros have been written beyond the end of the stack buffer, over the preserved return address. When the function later executes its ret instruction, the value 0x00000000 (NULL) is loaded from the overwritten memory spot. This type of error, stack overflow, is the eponym of our forum.
Since you mention that you are calling a third-party library, I will point out that it may be a situation of the library expecting you to provide a non-NULL function pointer as input to some API. These are sometimes known as "call back" functions.
You will have to use the debugger to narrow down the cause of your problem further, but the above possiblities should help you solve the riddle.
An access violation at anywhere near adress '00000000' indicates a null pointer access. You're using something before it's ever been created, most likely, or after it's been FreeAndNil()'d.
A lot of times this is caused by accessing a component in the wrong place during form creation, or by having your main form try and access something in a datamodule that hasn't been created yet.
MadExcept makes it pretty easy to track these things down, and is free for non-commercial use. (Actually, a commercial use license is pretty inexpensive as well, and well worth the money.)
You start looking near that code that you know ran, and you stop looking when you reach the code you know didn't run.
What you're looking for is probably some place where your program calls a function through a function pointer, but that pointer is null.
It's also possible you have stack corruption. You might have overwritten a function's return address with zero, and the exception occurs at the end of the function. Check for possible buffer overflows, and if you are calling any DLL functions, make sure you used the right calling convention and parameter count.
This isn't an ordinary case of using a null pointer, like an unassigned object reference or PChar. In those cases, you'll have a non-zero "at address x" value. Since the instruction occurred at address zero, you know the CPU's instruction pointer was not pointing at any valid instruction. That's why the debugger can't show you which line of code caused the problem — there is no line of code. You need to find it by finding the code that lead up to the place where the CPU jumped to the invalid address.
The call stack might still be intact, which should at least get you pretty close to your goal. If you have stack corruption, though, you might not be able to trust the call stack.
If you get 'Access violation at address 00000000.', you are calling a function pointer that hasn't been assigned - possibly an event handler or a callback function.
for example
type
TTest = class(TForm);
protected
procedure DoCustomEvent;
public
property OnCustomEvent : TNotifyEvent read FOnCustomEvent write FOnCustomEvent;
end;
procedure TTest.DoCustomEvent;
begin
FOnCustomEvent(Self);
end;
Instead of
procedure TTest.DoCustomEvent;
begin
if Assigned(FOnCustomEvent) then // need to check event handler is assigned!
FOnCustomEvent(Self);
end;
If the error is in a third party component, and you can track the offending code down, use an empty event handler to prevent the AV.
When I've stumbled upon this problem I usually start looking at the places where I FreeAndNil() or just xxx := NIL; variables and the code after that.
When nothing else has helped I've added a Log() function to output messages from various suspect places during execution, and then later looked at that log to trace where in the code the access violation comes.
There are ofcourse many more elegant solutions available for tracing these violations, but if you do not have them at your disposal the old-fashioned trial & error method works fine.
It's probably because you are directly or indirectly through a library call accessing a NULL pointer. In this particular case, it looks like you've jumped to a NULL address, which is a b bit hairier.
In my experience, the easiest way to track these down are to run it with a debugger, and dump a stack trace.
Alternatively, you can do it "by hand" and add lots of logging until you can track down exactly which function (and possibly LOC) this violation occurred in.
Take a look at Stack Tracer, which might help you improve your debugging.
Use MadExcept. Or JclDebug.
I will second madExcept and similar tools, like Eurekalog, but I think you can come a good way with FastMM also. With full debugmode enabled, it should give you some clues of whats wrong.
Anyway, even though Delphi uses FastMM as default, it's worth getting the full FastMM for it's additional control over logging.
Here is a real quick temporary fix, at least until you reboot again but it will get rid of a persistent access. I had installed a program that works fine but for some reason, there is a point that did not install correctly in the right file. So when it cannot access the file, it pops up the access denied but instead of just one, it keeps trying to start it up so even searching for the location to stop it permanently, it will continue to pop up more and more and more every 3 seconds. To stop that from happening at least temporarily, do the following...
Ctl+Alt+Del
Open your Task Manager
Note down the name of the program that's requesting access (you may see it in your application's tab)
Click on your Processes tab
Scroll through until you find the Process matching the program name and click on it
Click End Process
That will prevent the window from persistently popping up, at least until you reboot. I know that does not solve the problem but like anything, there is a process of elimination and this step here will at least make it a little less annoying.
I have one TClientDataSet with several Fields and I'm getting this exception, I'm sure that I forgot to set one field value, but the question is, which Field?
Is there some way to find out quickly which field is raising this exception?
EDIT
I solved the problem, I was connecting the TClientDataSet to the wrong provider, which had a required field on the server but didn't have this field on the client.
But it still doesn't invalidate my question, I lost a lot of time to figure it out, and if I had some way to know which server side required field was raising this error it would be very quick to realise what was my mistake.
EDIT 2
Related QCs:
#5557 - Solved as "As Designed".
#54380 - Open
When the debugger notifies you of the exception, look at the call stack. Navigate to the routine that is iterating over the list of fields, and inspect the loop-control variable at that point to find out how far through the field list your program got before raising an exception.
If the loop-control variable isn't accurate, then set a breakpoint in that routine and re-run your program. Step through until you get the exception again.
Another option is to look at all the fields and see for yourself which one lacks a value.