Trying to catch use of uninitialized variables

Trying to catch use of uninitialized variables

To return myself to a former era where I could regularly trap the use of uninitialized variables (it was in Salford...), I decided to try out the equivalent in Intel (/RTCu as per the docs).I switched it on for all files in my DLL, then ran, just to see if we had any problems. The application ran just fine without trapping anything. Being distrustful, I forced the use of an uninitialized variable and reran. But it didn't catch this. So I'm now wondering exactly how this /RTCu option is supposed to work.

My entire compilation line is:
ifort /nologo /c /iface:cvf /Z7 /Tfplacemen.for /define:INTEL9 /Qsave /Foplacemen.obj /fpconstant /real_size:64 /4Yb /RTCu /Zi /4R8

Is another option I'm using negating /RTCu?

Adrian

41 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

If you're seeing nothing at all, then it may be that the compiler isn't noticing your uninitialized use. To get anything more than a "user breakpoint" error, you have to build with /dbglibs and run under the VS.NET debugger. That will change in the future.

Steve - Intel Developer Support

I am running in the the debugger. I'm not sure about /dbglibs, I'll check into that.

Another problem: I decided to swicth on /check:all as well. The debugger brought up a window informing me that I was accessing element 0 of an array. I click OK, but nothing happens. So then I click the break button (||). It breaks, but not in my source, but in some nodebug runtime library where all I see is assembler. In CVF, clicking OK after encountering the error brought me directly to the line in the source code where the error lay. What is the problem?

BTW: I haven'tupgraded my version from that on theCD yet, maybe I need to do that?

Adrian

The break on array bounds error is something we have also seen and are working on. Sometimes it works and sometimes it doesn't.

Yes, please do update to the current version, 9.0.025, but this won't make much difference for uninitialized variable checking, I think. (Actually, it may make it worse in that the name of the reported variable will be wrong.) But the newer version fixes many other issues.

Steve - Intel Developer Support

I managed to upgrade to the latest version, but yes as you say, there is still the problem.

So here's my question: I am responsiblefor signaling the time for, and making the change in our organization to switch from DVF6.1 to Intel. I think both of these issues are essential for debugging (especially the ability to take the user to the problem line in the source). Maybe v9.x is not yet mature enough? Should we beswitching tov8.0 instead?

Adrian

9.0 is much better than 8.0. My guess is that there's an undiscovered bug in your code. If you'd like us to investigate, please submit an example to Intel premier Support.

Steve - Intel Developer Support

Yes, I'm sure there are undiscovered bugs in our code, I'm trying to find them!

You say you have seen this behavior already so I guess there's no need for me to send a specific case. How long for the fix?

Adrian

Ferrad,

I have had good success in finding uninitialized variables and subscript errors using Visual Studio .NET 2003, and it's debugger. What the debugger will not catch gracefuly is when your application passes in a reference to an invalid address when calling a .DLL or other system function that does not have debug information.

When this occures look at the call stack. If the call stack appears to contain routines from your code (above stuff you don't recognise) then open the closest routine you recognize. If this is of little help then open up the Dissassembly window. You may also have to click on the yellow right arrow to get the focus in the assembly window to the line that caused the problem.

You do not have to under stand much about assembly to find statement in your Fortran program thatprecipitated the error.

The strategy to use is to"Set Next Statement" to the proper position to return from the current routine. Then single step out of the current routine. Then once properly in the context of the next level up check the call stack again. If you seeFortran routines thenclose the Disassemblywindow and click on the Yellow right arrow to find the Fortran statement.
More than likely you will have to step out of the next higher routine and keep doing so until the call stack looks like Fortran code.

Note, as you step out do not use the run until return button. As this assumes the the faulty routine that caused the break had completed it's job. Each time as you pop up a routine level then look for return code in the disassembly window and do the "Set Next Statement" thing. Repeat until you see Fortran routines in the Call Stack or until you flub-up and crash the call stack (then start over).

There are several ways assembly code performs it'sreturns. You have to be careful to place the Next Statement at the correct position in order to return properly. Examples follow:
Code:

 00475455 8B E5            mov         esp,ebp ! <- Set Next Statement here
00475457 5D               pop         ebp  
00475458 8B E3            mov         esp,ebx 
0047545A 5B               pop         ebx  
0047545B C3               ret              

Code:

0047533F C9               leave     ! <- Set Next Statemen here
00475340 C3               ret              

Or you may simply see a "ret" alone. i.e. if you see
mov esp,ebp
you must position there and single step past the ret to the caller.
failing that if you see "leave" then place the next statement there and single step to the caller. Failing that, place the next statement on the "ret" and then single step out to the caller.

Wait, not done yet. At times when you get back to the caller, the calling code may cleanup the stack of the arguments. If upon returnyou see an "add esp, nn" then you must step through that instruction before attempting to return from the next level.

Good luck bug hunting

Jim Dempsey

Thanks, Jim. While not pretending to follow everything you wrote, I did follow some of it, but as my call stack is probably 50 deep, and these are mostly system dll's, it is very difficult to set the correct return positions. I miss one, and it goes back right to the top again.

In the meantime, I have sent a "Show-Stopper" bug report into premier.intel.com, with an example - hopefully they'll sort it out soon. I believe it is pretty much a requirement of any debugger that it stops at the line of offending code when it encounters an exception, I'm amazed that v9.0 was released at all with this problem.

It does stop when there is an exception. It does not stop when there is no exception.

Steve - Intel Developer Support

Yes it stops, but not in the correct place!

Adrian

I get the attached window when it hits the exception. Any idea why I don't get line numbers? All files are compiled with full debug.
Adrian

Although your call stack is of little use you do have an indication the the error involves a subscript error with subscript 2on ACID_CONCENTRATION. If there are only a few places where ACID_CONCENTRATION is referenced then you can place some diagnostic code that conditionaly compiles infront of the references. This can do a quick range check and bugcheck when appropriate.

Jim Dempsey

Yes, this is what I've being doing for 3 days now...

Unfortunately in most cases there are many references to the variable listed inthe exception window. I've being putting write statements before every occurrence, to find out which one is causing the problem. Also I can only find one at a time - I fix this one, then move onto the next. I've found 6 bits of bad code in 3 days, not a very efficient way to debug.

In my opinion, this is exactly the type of problem the Intel debugger is supposed to identify and stop (which it does), *and* point to the actual line where the problem occurs. CVF had no problem with this - it also used to give line numbers in the exception window.

Steve, please tell me this is being fixed as a matter of urgency...

Adrian

It is being actively investigated. The problem seems to have appeared sometime after the initial 9.0 release.

Steve - Intel Developer Support

My experience is using VS .NET 2003 and it's debugger on WinXP and running consol apps. The array subscripting is caught and the source line shown by the debugger. What you may be experiencing is something a little different. And when you fix it in one place in your program the same problem may be throught the program. In which case you don't rely so much on the debugger to catch the errors.

The particular thing about uninitialized variables and is the exception is thrown when the variable is dereferenced. This will not catch uninitialized variables from being passed in to a stack of subroutines only to fail somewhere down deep.

Subscript errors can also occur late if you call a routine with the base of an array plus additional args that subscript the array. These typicaly will not show up as subscript errors but will show up as (usualy) data corruption errors.

Here is a nifyt little programming trick to help find your problem

1) enable the fortran preprocessor
2) locate the declaration of the array with indexing errors. e.g. YOUR_ARRAY
3) change the name of the array. e.g. was_YOUR_ARRAY
4) create subscript check functions for the indexes
function subchk1(i)
integer :: subchk1, i
if((i .lt. 1) .or. (i .gt. iMax1Whatever)) call bugcheck()
subchk1 = i
end function subchk1

function subchk1(i)
integer :: subchk1, i
if((i .lt. 1) .or. (i .gt. iMax2Whatever)) call bugcheck()
subchk2 = i
end function subchk2
function subchk3(i)
integer :: subchk1, i
if((i .lt. 1) .or. (i .gt. iMax3Whatever)) call bugcheck()
subchk3 = i
end function subchk35) define a macro for referencing your array. e.g.
#define YOUR_ARRAY(i,j,k) was_YOUR_ARRAY(subchk1(i),subchk2(j),subchk3(k))

Place the macro deffinition into a common header file.

With this technique you have no source code changes to catch the bug.
Other than the changes in a common header file.

Jim Dempsey

There is a real problem that, in some circumstances, when an array bounds violation occurs that the breakpoint is in the middle of some system code and there is no way to determine where in the user code the problem occurred. I have seen this myself - but some examples show it and some do not. We do consider this a serious problem and are investigating.

Steve - Intel Developer Support

Yes, that's exactly what I did yesterday! Seems like we old Fortran diehards have learnt all the same tricks...

The only problem with this approach is that it only works when the array isread (eg. if(subchk1(i)) then...). When it s written to, you have to change all these to the new variable name. But that wasn't such a problem, and it enabled me to catch a number of array over/underuns in one routine.

I await the fix with aniticipation...

Adrian

The replacement macro works on the left side the the = too.

Code:

module aModule
    real(8) :: array(100)

contains
function TheArray(index)
    real(8) :: TheArray
    integer :: index
    TheArray = array(index)
end function TheArray

function iIndex(i)
    integer :: iIndex, i
    iIndex = i+1
end function iIndex
end module aModule

program FuncTest
    use aModule

    implicit none
    ! Variables
    integer :: i

    ! Body of FuncTest
    do i=1,10
        array(iIndex(i)) = real(i)
    end do
    do i=1,10
        write(*,*) TheArray(i)
    end do
end program FuncTest

You should see 0., 1., 2., ...

Recode the indexing functions to suit your array. You could make it generic whereby the test function also test shape, rank, etc.. as well as allocation.

The macro method is quite useful becase you can enable and disable it with one compiler variable.

Jim Dempsey

Adrian, I tried the example you sent to Premier Support and it works for me (debugger context is fine.) I have another example where it is not. We are still investigating.

Steve - Intel Developer Support

Here is a better example

Code:

module aModule
    real(8) :: was_array(10)

contains
function iChk(i)
    integer :: iChk, i
    iChk = i
    if((i .lt. lbound(was_array,dim=1)) .or. (i .gt. ubound(was_array,dim=1))) then
        write(*,*) "gotcha - i=", i
#ifdef _DEBUG
        iChk = lbound(was_array,dim=1)
#else
        stop
#endif
    endif
end function iChk
end module aModule
! in common header file
#define array(i) was_array(iChk(i))
program FuncTest
    use aModule

    implicit none
    ! Variables
    integer :: i

    ! Body of FuncTest
    do i=1,10
        array(i) = real(i)
    end do
    do i=1,10
        write(*,*) array(i)
    end do
    array(0) = 0.
    array(11) = 11.
    array(-1) = -1.
end program FuncTest

Jim Dempsey

Can't get this to compile, it's upset with the # syntax. I think it needs to be !DEC$ something, but I can't get the syntax right for the array(i) #define.

Adrian

Enable FPP under Preprocessor

When you get the array bounds error, bring the console window to the front - does it identify the source file and line?

Steve - Intel Developer Support

Nope, it fails before then telling me a variable '' is used without being defined (see attached screen capture). Not sure which variable it's talking about.
Adrian

That's a different problem. The name of the variable in the message will almost always be wrong. It worked in the initial 9.0 release but not now. It's being fixed.

I was asking about the array bounds problem where you could not see where in the source the error occurred.

Steve - Intel Developer Support

No, it doesn't stop at all if I have all checking on except "Check Uninitialized Variables"

compiler options are:

/nologo /Zi /Od /fpp /fpe:0 /module:"$(INTDIR)/" /object:"$(INTDIR)/" /traceback /check:bounds /check:format /check:output_conversion /check:arg_temp_created /libs:static /dbglibs /c

Adrian

Are you sure there's an error?

Steve - Intel Developer Support

Hello,

I have been using the Intel compiler to trap various unititalized variables lately, and I have found a few tricks that may help.

1. First ensure that the Installation of IVF is good. If you have previous versions that are not entirely unistalled, please unistall everything (IVF related), and do reboot/regclean a few times.Then install the latest version. This may normaly not be required, but sometimes an old dll can do much harm.

2. Ensure that you enable the "Generate traceback information" switch in all projects.

3. Ensure that the floating point exception allow level is "Underflow generates zero, abort on IEEE exceptions".

4. Ensure that the all run time exceptions are enabled.

5. You may experiment by turning on and off the switch /Qtrapuv, read documentation for details.

6. You may experiment by turning on and off the switch /Qfpstkchk, read documentation for details.

7. In addition you may add to the command line "/check:all /warn:all /debug:full".

8. Before debugging, ensure to do a full clean and the a full rebuild of allinvolved projects.

9. When debugging starts, after pressing F5, please go to the Exceptions window and enable the exceptions you want to trap (se trap.jpg).

10. Sometimes you notice a cmd window with information about file and line number of the exception, but the debugger stops in some C++ code. Go to this Fortran file and set a conditional breakpoint to trap the condition yourself. In most cases this works very well.

We have trapped many bugs using IVF, that have been around a long time, in particular unitialized variables. In certain cases standard approaches does not catch an unitialized variable, and we have found the combining the option /check:unitialized with actively enabling the "Floating Point Underflow" trap in the Exception window (trap.jpg) may trap these variables.

I hope this helps.

Lars Petter Endresen

Message Edited by lpe@scandpower.no on 11-11-2005 04:41 PM

Hello again,

The option /check:unintializedmay set double precision variables to a very low number (correct me if Iam wrong) like 1.0E-307, and if these are used a "Floating Point Underflow" exception can be cathed if you have enabled this exception in the Exception window. This also works for arrays.

The /Qtrapuv option may sometimes be beneficial and sometimes not.

Best regards,

Lars Petter Endresen

/check:uninitialied has no effect on values of variables. It does not rely on the data in variables. /Qtrapuv is not terribly useful right now.

Steve - Intel Developer Support

Hello,

Why dounitialized variables havestrange values like 1.0E-307 in the debugger?

Lars Petter

Small integers in the high order 32 bits, when read as double precision floating point, are interpreted as subnormal numbers. You could do the same with "legal" code, using equivalence or transfer().

The Floating Point Processor (core) can be set to generate traps on certain errors. e.g. trap on divide by 0, underflow, overflow and some others. The bit patterns in the encoded numbers in addition to containing numbers have a few special case values (or value ranges). One of these values (two if you count the difference in sign bit) is given a value of Not A Number, (there is also + and - infinity, and then subnormal numbers).

When you set the compiler to trap on uninitialized variables one of the techniques employed by the compiler is to initialize the variables to Not A Number (NaN). Your code can overwrite a NaN without trapping but if one of these are read (and the FPU has been setup to trap on NaN) then a trap will occure and you receive a break into the debugger.

A second way to trap uninitialized numbers (an Intel person might correct me) is for the compiler to generate an address for the variable which is an invalid address. On first reference to the variable if the reference is a read then the invalid address trap is converted into a trap for reference of variable before initialization. Should the first access be a write then the address for the variable is modified to a valid address. One way to do this trick is to use self modifying code (which is frowned upon). A second way is to make all variables references i.e. the variable is not the address of the data, instead the variable contains the address of the data (similar to a dummy argument). Now after first reference the address of the data inside the variable can be updated without worry about using self modifying code.

Jim Dempsey

Ferrad,

The screenshot error doesn't make sense. It would appear that lbound or ubound is failing. Which shouldn't be the case. This could be a compiler problem (another thread in this forum is talking about this subject).

For your purposes you know the proper lbound and most likely the ubound of your array that is failing. (the demo case used 1:10). Your code may be using an integer parameter for the size of the array (and from prior discussions 1 is the lbound of the array).

Replace the lbound(...) and ubound(...) with the correct bounds for your array.

Jim Dempsey

If you believe you have found a compiler bug, please submut a test case to Intel Premier Support. That's the way to get the issue investigated.

Steve - Intel Developer Support

Yes, I have copied exactly the above bit of code, which contains the errors:
array(0) = 0.
array(11) = 11.
array(-1) = -1.
I have all the exception checking on as per the subsequent posting. But it does not stop in the debugger on these lines.
Adrian

Ferrad,

You must interpret the code postings and adapt the code to your particular needs.

Snip of code from my prior post:

if((i .lt. lbound(was_array,dim=1)) .or. (i .gt. ubound(was_array,dim=1))) then
write(*,*) "gotcha - i=", i
#ifdef _DEBUG
iChk = lbound(was_array,dim=1)
#else
stop
#endif
endif

Interpretation:

When compiled in the Debug configuration the program will not Break and the program will not stop (the stop statement is not compiled in Debug).

Instead of breakingthe program forces the array index to 1. The intention here is (when in Debug) for you to place a Break Point at the location:

iChk = lbound(was_array,dim=1)

Or because of your prior lbound problem at your edited line:

iChk = 1

The purpose of the _DEBUG conditional compile section setting the index to 1 is that this permits you, when entering your break condition, to write down the caller information .AND. then continue running with the invalid index set to 1. Note, it is true that your program is continuing using an incorrect index. However, it is also true that should you decide to continue with the invalid index that you may also encounter a subsiqent index error, thus catching multiple bugs in one debug session. Now then, if after examining the caller of the first instance of the incorrect error, you determine it would be of no benifit to continue then simply stop the program.

Jim Dempsey

Ah, got you, my apologies. This works just fine.
Adrian

Good, it seems like you are getting the hang of using fpp to help catch bugs. Remember to make the debugging macros selectible depending on compile variables. Your release code need not have the bugchecks (unless you want to include the bugchecks during a beta release). Let the preprocessor add and remove code sections depending on your requirements.

Did you resolve the lbound/ubound problem? These are intrinsic functions to the compiler and should work. (unless maybe your are compiling .F files vs .F90)

Jim

Yes, it works well, thanks for the help. I had theubound problem on Friday, but when I recompiled this morning, I didn't have it. Who knows, probably some weird stuff I was experimenting with on Friday, but now it's OK.

BTW putting the #define line in seems to invalidate my

!DEC$ FIXEDFORMLINESIZE: 132

directive as now it's complaining about code after column 72. Are /fpp and !DEC$ incompatible?

Adrian

FPP and !DEC$ are not incompatible. FPP is sensitive to the fortran line length. If you set the fixed line length with !DEC$ then FPP might not know what the fixed length is. FPP must know what the line length is in order to construct continuation lines properly.

Try setting the line length via project option switches instead (as well). Or add the FPP option /e. The FPP options can be found using "fpp /help" on the command line. I think you may find using the ifort option for fixed line length more appropriate.

Jim Dempsey

Leave a Comment

Please sign in to add a comment. Not a member? Join today