Range Check Error and Delphi 7.0 - delphi

After spending a week checking and fixing my program for memory leaks through FastMM4, I finally test ran my program on a different PC. Now, I am getting "Range Check Error." I have spent hours researching online about this, but none of them seem to give me what I am looking for. My program was complied with the Runtime Error option Range Check. So, I know that's why I am getting the error, but I needed to know exactly why the error is raised.
The program was compiled on XP with Delphi 7.0. The testing PC is a Windows 7. As soon as it starts up, my program begins to communicate through serial port and then followed by "Range Check Error" message boxes. When I stop the serial communication, there are no "Range Check Error" boxes. What does this mean and how do I go about resolving it? I am looking for simple strategy. I know I could spend days checking line by line.
"Range Check Error" caused by improper assignment of values or accessing inaccessible index of an array. Am I correct?

Your understanding of range check errors is correct. They arise when you access an array outside it's bounds. For example:
type
TFixedArray = array [0..41] of Integer;
var
a: TFixedArray;
begin
a[42] := 1+2;//!! this is a range check error !!
end;
Or for a dynamic array:
var
a: array of Integer;
begin
SetLength(a, 666);
a[665] := 12;//this is fine
a[666] := 42;//!! this is a range check error !!
end;
I've illustrated this with assignment, but reading an array with an index out of bounds will also produce a range error.
The range error should report an address at which it occurs which you can then translate into a code location with your map file. Even better would be if you were using madExcept or some such tool.
UPDATE
Prompted by Ken, the documentation states what is affected by the range checking option as follows:
In the {$R+} state, all array and
string-indexing expressions are
verified as being within the defined
bounds, and all assignments to scalar
and subrange variables are checked to
be within range.

Having read other information about the concept of "range check error", I believe that the reason for causing the "range check error" in this scenerio is that:
the variable assigning to access the serial port the program reads is an 16-bytes(or smaller) type, and the serial port the program reads exceeds the limitation of the variable.
Notice that [When I stop the serial communication, there are no "Range Check Error" boxes.], this should make all things clear.

Related

Delphi TWriter.WriteListBegin

I have a component which is writing into a blob different information by using the TWriter class. The problem is that some blobs have been saved incorrect into blob(or under another data sequence), and I need to correct somehow those errors. The problem arrives when I'm expectin an WriteListBegin or WriteListEnd and I get an EReadError "Invalid property value". I'm thinking of reading the stream byte by byte, and to know where these separators are located. How can I know that I'm encountering a WriteListBegin or WriteListEnd?
LE: The issue can not be solved as easily the comments suggest. I don't know the vendor, so I can not ask for details. Concerning of what is behind the TWriter mechanism, this is the following assembly routine, which I don't understand what bytes writes as a
start-of-list marker to the writer object's associated stream
procedure TWriter.Write(const Buf; Count: Longint); assembler;
Probably I will start to write my own custom TReader in order to fix the bogus streams.
If I unraveled your question well, then I understand you have corrupt data which, obviously, does not let itself get parsed correctly. Specifically, the list begin and end markings are missing or, are in the wrong order or at the wrong place.
I can think of four solutions to fix that:
See to get the data uncorrupted (ask supplier).
Fix the data manually (if within reasonable size).
Write an own custom parser to fix the markings automatically, and use that in advance.
Using TWriter, for every line; remember current Position, check current line, rewrite, substitute or ignore that line in case of corruption and return to old position if necessary.
In case of several data chunks being corrupted in the same manner, maybe a partial manual investigation (2) may lead to custom parser (3) in no time.
I solved the problem by reading data type until reader.EndOfList separator for most of the blobs.
Thank all, especially for the -1's

SortCompareFunction with always same result creates EAccessViolation

What is the exact reason for the SortCompareObjects function getting an EAccessViolation if it always returns the same result (as opposed to a changing result e.g. with CompareText)?
function SortCompareObjects(Item1, Item2: Pointer): Integer;
begin
Result := 1; // EAccessViolation
// Result := CompareText(...); // No EAccessViolation
end;
MyObjectList: System.Contnrs.TObjectList;
MyObjectList := System.Contnrs.TObjectList.Create;
for i := 0 to x do
MyObjectList.Add(AObject);
MyObjectList.Sort(#SortCompareObjects); // EAccesViolation
A comparison sort algorithm accesses elements in an array under the assumption that the sort function has certain properties. Specifically,
If f(x,y)<0 then f(y,x)>0
If f(x,y)=0 then f(y,x)=0
If f(x,y)<0 and f(y,z)<0 then f(x,z)<0
f(x,x)=0
The sort algorithm guarantees that it will sort the array if your function obeys the rules. Otherwise, if you don't obey the rules, all bets are off. Anything could happen. Don't be surprised if you encounter runtime errors. The most commonly seen, in my experience, is stack overflow, but access violation is plausible too.
Under the assumption that the sort algorithm is sequential ...
That's a very wrong assumption, one you don't need to make. First of all, unless you're on a trial version of Delphi, you can see the source code; It's QucikSort, not anything else. The second problem is, what's a "sequential" sort algorithm? I've never heard of one!
To answer your question directly, here's a snip of code from the QuickSort algorithm used by Delphi. SCompare is the function you supplied, the one that always reutrns 1
while SCompare(SortList^[J], P) > 0 do
Dec(J);
Since 1 is always grater then zero, that loop would never stop. It only stops when SortList^[j] generates an access violation, and that's bound to happen sooner or later.

Delphi 64-bit: finding incorrect casts?

I'm working on adapting a large Delphi code base to 64-bits. In many cases there are lines where pointers are casted to/from 32-bit values similar to this:
var
p1,p2 : pointer;
begin
inc(Integer(p1),10);
p2 := Pointer(Integer(p1) + 42);
Where I can find these casts I have replaced them with NativeInt-casts instead to make them correct in 64-bit mode.
However I'm not sure I have found them all. Sometimes the casts are more subtle so just text-searching for the string "integer(" is not sufficient either.
Since the "integer(" casts will fail in 64-bit if the pointer value is above the range of integer type I have an idea: what if I could force the memory manager to allocate memory above 4gb (so the pointer values are using more than 32-bits)? Then I would get runtime errors and can more easily find the casts that are wrong. Is this possible? Or can anyone recommend some other technique?
There's no magic trick to finding these casts beyond the sort of text search that you are using. It would be really nice if the compiler warned of such a cast. I find it very disappointing that it doesn't.
When you do find such a problem, don't change to NativeInt. Change the pointers to be typed pointers, and use pointer arithmetic.
var
p1, p2: PByte;
....
inc(p1, 10);
p2 := p2;
inc(p2, 42);
Then your code will be safe forever.
There are still some situations where you need to cast to integers. For example when passing addresses to SendMessage. But cast these to either WPARAM or LPARAM as appropriate.
Your idea of forcing runtime errors is sound and, thankfully for you, not original! You should use the full version of FastMM and define AlwaysAllocateTopDown. This forces the calls that FastMM makes to VirtualAlloc to pass the MEM_TOP_DOWN flag. This will flush out most of your erroneous casts as runtime pointer truncation errors.
However, that will only force top down allocation for memory allocated by your memory manager. Other modules in your process will use the default policy of bottom up. You can set a machine wide setting to change that default policy. Set HKLM\System\CurrentControlSet\Control\Session Manager\Memory Management\AllocationPreference to REG_DWORD with value 0x100000 and reboot.
Note that this might cause your machine to have stability problems. Many applications cannot cope with this. In particular there are very few anti-virus products that can cope with this setting. MSE is the one that I found works with machine wide top down allocation. What's more the 64 bit debugger does not run under top down allocation! So you have to do this kind of testing without the debugger. My QC report is still open and this problem has not been addressed, even in XE3.

Switch off Delphi range checking for a small portion of code only

How can one switch off range checking for a part of a file. Switching off is easy, but how do I revert to the project setting later on? The pseudo-code below should explain it:
Unit1;
//here's range checking on or off as per the project setting
code here...
{$R-}
//range checking is off here because the code causes range check errors
code here...
//now I want to revert to the project setting. How do I do that?
code here...
end.
See: IFOPT directive.
{$IFOPT R+}
{$DEFINE RANGEON}
{$R-}
{$ELSE}
{$UNDEF RANGEON}
{$ENDIF}
//range checking is off here because the code causes range check errors
//code here...
{$IFDEF RANGEON}
{$R+}
{$UNDEF RANGEON}
{$ENDIF}
Wrap your code in $R directives:
{$R-} // disable range checking
// do non-range-checked operations here
{$R+} // turn range checking back on
Note that the directive applies at the statement level. You cannot wrap just part of an expression with that.
Why would you want to turn it off for release builds? – dan-gph
I have seen too many Delphi programmers writing quite large programs without ever activating Range, Overflow and Assertion checking. Of course, you can do that if you want, but your code will be more buggy.
I hope to convince more programmers to enable these 3 checking right now, to make the program more reliable.
However, note that there is a price to pay for that: your program will be slower. I show below some actual time comparison of code running with and without range checking.
the point is you can turn it off locally where you need to with {$R-}.But you can leave it on globally in the project settings –
dan-gph
Personally, next to Debug and Release, I have a 3rd option called PreRelease. This is actually a Debug version with one single setting different: the "Optimizations" is on. It is fast enough while it still does the checking (range, overflow, assertions, etc).
I release this kind of version to a limited number of customers. If all seems good after one week, I replace it with the true Release version, where the checkings are off.
Overflow checking
This will check certain integer arithmetic operations (+, -, *, Abs, Sqr, Succ, Pred, Inc, and Dec) for overflow. For example, after a + (addition) operation the compiler will insert additional binary code that verifies that the result of the operation is within the supported range.
An "integer overflow" occurs when an operation on an integer variable produces a result that is outside the range of that variable. For example, if an integer variable is declared as a 16-bit signed integer, its value can range from -32768 to 32767. If an operation on this variable produces a result greater than 32767 or less than -32768, an integer overflow has occurred.
When an integer overflow occurs, the result of the operation is undefined and can lead to undefined behavior in the program:
• Wrap-around
The result might result in a wrapped-around value. This means that the number 32768 will be actually stored as 1 since it is 1 unit higher than the highest value we can store (32767).
• Truncation
The result may be truncated or otherwise modified to fit within the range of the integer type. For example, the number 32768 will be actually stored as 32767 since that is the highest value we can store.
Undefined program behavior is one of the worst kind of errors, because it is not an error easy to reproduce. Therefore, it is difficult to track and repair.
There is a small price to pay if you activate this: the speed of the program will decrease slightly.
IO checking
Checks the result of an I/O operation. If an I/O operation fails, an exception is raised. If this switch is off, we must check for I/O errors manually.
There is a minor price to pay if you activate this: the speed of the program will decrease, but insignificantly because the few microseconds introduced by this check is nothing compared with the millisecond-range time required by the I/O operation itself (the hard drives are slow).
Range Checking
The Delphi Geek calls this “The most important Delphi setting” and I totally agree. It checks if all array and string indexing expressions are within the defined bounds. It also checks that all assignments to scalar and subrange variables are within range.
Here is an example of code that would ruin our life if Range Checking would not be available:
Type
Pasword= array [1..10] of byte; // we define an array of 10 elements
…
x:= Pasword[20]; // Range Checking will prevent the program from accessing element 20 (ERangecheckError exception is raised). Security breach avoided. Error log automatically sent to the programmer. Bruce Willis saves everyone.
Enabling Runtime Error Checking
To activate the Runtime Error Checking, go to Project Options and check these three boxes:
Enabling the Runtime Error Checking in ‘Project Options’
Assertions
A good programmer MUST use assertions in its code to increase the quality and stability of the program. Seriously man! You really need to use them.
Assertions are used to check for conditions that should always be true at a certain point in the program, and to raise an exception if the condition is not met. The Assert procedure, which is defined in the SysUtils unit, is typically used to perform assertions.
You can think of assertions as poor man’s unit testing. I strongly advise you to look deeper into assertions. They are very useful and do not require as much work as unit testing.
Typical example:
SysUtils.Assert(Input <> Nil, ‘The input should not be nil!’);
But for the program to check our assertions, we need to active this feature in the Project Settings -> Compiler Options, otherwise they will simply be ignored, like they are not there in our code. Make sure that you understand the implications of what I just said! For example, we screw up badly if we call routines that have side effects in the Assert. In the example below, during Debugging when the assertions are on, the Test() function will be executed and 'This was executed' will appear in the Memo. However, during Release more, that text will not appear in the memo because now Assert is simply ignored. Congratulations we just made the program to behave differently in debug/release mode ☹.
function TMainForm.Test: Boolean;
begin
Result:= FALSE;
mmo.Lines.Add('This was executed');
end;
procedure TMainForm.Start;
VAR x: Integer;
begin
x:= 0;
if x= 0
then Assert(Test(), 'nope');
end;
Here are a few examples of how it can be used:
1 To check if an input parameter is within the 0..100 range:
procedure DoSomething(value: Integer);
begin
Assert((value >= 0) and (value <= 100), 'Value out of range');
…
end;
2 To check if a pointer is not nil before using it:
Var p: Pointer;
Begin
p := GetPointer;
Assert(Assigned(p), 'Pointer is nil');
…
End;
3 To check if a variable has a certain value before proceeding:
var i: Integer;
begin
i := GetValue;
Assert(i = 42, 'Incorrect response to “What is the answer to life”!');
…
end;
Assertions can also be disabled by defining the NDEBUG symbol in the project options or by using the {$D-} compiler directives.
We can also use the Assert as a more elegant way of handling errors and exceptions in some cases, as it can be more readable and it also includes a custom message, that would help the developer understand what went wrong.
Personally, I use it a lot at the top of my routines to check if the parameters are valid.
Activating this feature will (naturally) make your program slower because… well, there is one extra line of code to execute.
Nothing comes for free
Everything nice comes with a price (fortunately a small price in our case): enabling Runtime error checking and Assertions slows down our program and makes it somewhat larger.
Computers today have lots of RAM so the slight increase in size is irrelevant, so, let’s put that aside. But let’s look at the speed, because that is not something we can easily ignore:
Type Disabled Enabled
Range checking 73ms 120ms
Overflow checking 580ms 680ms
I/O checking Not tested Not tested
As we can see the program's speed is strongly impacted by these runtime checking. If we have a program where speed is critical, we better activate “Runtime error checking” during debugging only. What I do, I also leave it active in the first release and wait a few weeks. If no bugs are reported, then I release an update in which “Runtime error checking” is off.
Personally, I leave the “IO checking” always active. The performance hit because of this check is microscopic.
Big warning:
If you have an existing project that was not so nicely written, and you activate any of the Runtime Error checking below, your program may will crash more often than usual.
No, the Runtime Error checking routines did not break your program. It was always broken – you just didn’t know. The Runtime Checking routines are now finding all those places where the code is fishy and shitty and smelly. The sole purpose of Runtime Checking is to find bugs in your program.

Address Error in Assembly (ColdFire MCF5307)

Taking my first course in assembly language, I am frustrated with cryptic error messages during debugging... I acknowledge that the following information will not be enough to find the cause of the problem (given my limited understanding of the assembly language, ColdFire(MCF5307, M68K family)), but I will gladly take any advice.
...
jsr out_string
Address Error (format 0x04 vector 0x03 fault status 0x1 status reg 0x2700)
I found a similar question on http://forums.freescale.com/freescale/board/message?board.id=CFCOMM&thread.id=271, regarding on ADDRESS ERROR in general.
The answer to the question states that the address error is because the code is "incorrectly" trying to execute on a non-aligned boundary (or accessing non-aligned memory).
So my questions will be:
What does it mean to "incorrectly" trying to execute a non-aligned boundary/memory? If there is an example, it would help a lot
What is non-aligned boundary/memory?
How would you approach fixing this problem, assuming you have little debugging technique(eg. using breakpoints and trace)
First of all, it is possible that isn't the instruction causing the error. Be sure to see if the previous or next instruction could have caused it. However, assuming that exception handlers and debuggers have improved:
An alignment exception is what occurs when, say 32 bit (4 byte) data is retrieved from an address which is not a multiple of 4 bytes. For example, variable x is 32 bits at address 2, then
const1: dc.w someconstant
x: dc.l someotherconstant
Then the instruction
mov.l x, %r0
would cause a data alignment fault on a 68000 (and 68010, IIRC). The 68020 eliminated this restriction and performs the unaligned access, but at the cost of decreased performance. I'm not aware of the jsr (jump to subroutine) instruction requiring alignment, but it's not unreasonable and it's easy to arrange—Before each function, insert the assembly language's macro for alignment:
.align long
func: ...
It has been a long time since I've used a 68K family processor, but I can give you some hints.
Trying to execute on an unaligned boundary means executing code at an odd address. If out_string were at an address with the low bit set for example.
The same holds true for a data access to memory of 2 or 4 byte data. I'm not sure if the Coldfire supports byte access to odd memory addresses, but the other 68K family members did.
The address error occurs on the instruction that causes the error in all cases.
Find out what instruction is there. If the pc matches (or is close) then it is an unaligned execution. If it is a memory access, e.g. move.w d0,(a0), then check to see what address is being read/written, in this case the one pointed at by a0.
I just wanted to add that this is very good stuff to figure out. I program high end medical imaging devices in my day job, but occasionally I need to get down to this level. I have found and fixed more than one COTS OS problem by being able to track down just this sort of problem.

Resources