Common Reasons for Bugs in Release Version Not Present in Debug Mode

Common reasons for bugs in release version not present in debug mode

Many times, in debug mode in C++ all variables are null initialized, whereas the same does not happen in release mode unless explicitly stated.

Check for any debug macros and uninitialized variables

Does your program uses threading, then optimization can also cause some issues in release mode.

Also check for all exceptions, for example not directly related to release mode but sometime we just ignore some critical exceptions, like mem access violation in VC++, but the same can be a issue at least in other OS like Linux, Solaris. Ideally your program should not catch such critical exceptions like accessing a NULL pointer.

Bugs in Release not in Debug

One common glitch when switching is the behavior of uninitialized variables. Do you ever set all of the uicGrids[][] to some initial value?

Frequently, Debug will automagically initialize these to 0 for you.. Release leaves them with whatever ram is leftover from previous usage.

bug on RELEASE but not on DEBUG

The %s format specifier expects a NULL terminated char*. You are passing in a std::string instance. If it works in DEBUG, that's just pure luck.

You should use:

std::string s("foo");
sprintf(buf, "%s", s.c_str());

This will extract a char* and ensure that the buffer is NULL terminated.

It is possible that in the runtime library std::string has different implementations for DEBUG and RELEASE. Try compiling using both settings, but adding debug symbols to the RELEASE build and then step through the code. Look at the memory location where s is stored. Is there any difference?

A bug that is not present in debug mode, but just when i run the project

Thanks for the MCVE. Without it, it would have been impossible to find out what was wrong, because the problems are in parts of the code you did not show.

The problem starts with the fact that the user base class Utente extends GregorianCalendar. A user is not a calendar. Maybe a user has or uses one or more calendars. If your class is about OOP principles, you may want to study the differences between generalisation (inheritance) vs. composition vs. aggregation vs. association.

The rather technical problem with Utente extending GregorianCalendar is that its parent class Calendar already implements Comparable<Calendar>. So if you want to override compareTo, you cannot just override compareTo(Object) like you tried (but commented out), or even compareTo(Studente) in order to achieve sorting in a TreeSet, but you need to override compareTo(Calendar). The other two variants will never be called.

Background: Due to type erasure, You cannot implement the same interface twice for different generic types, i.e. something like Studente extends Utente implements Comparable<Studente> will yield a compile error "java.lang.Comparable cannot be inherited with different arguments: <scuola.Studente> and <java.util.Calendar>".

The consequence of the above is that, the way your code looks now on GitHub, your TreeSet<Studente> will be sorted as a set of Calendar objects. But Utente does not initialise the parent calendar instance by calling super(...) in its own constructors, i.e. the calendar will be uninitialised, because only its default constructor with no arguments will be called. But that one is inherited by Object and not overridden by any calendar or user class, i.e. Calendar.compareTo, when noticing that no time is set for itself, will simply always use System.currentTimeMillis(). The results are hard to predict and seem non-deterministic, even though of course they are perfectly deterministic, but dependent on when compareTo is called for which Studente object, and which ones are on the left and right hand sides of the comparison.

Therefore, because Calendar.compareTo(Calendar) is inadequate for class Studente, the TreeSet will have inconsistent sorting and e.g. A < B and B < C does not reliably mean A < C. It could even be that you get A < B one time and B < A at the same time. Your TreeSet, if you simply print its size or print its toString() value to the console, will show the correct size and the correct set of Studente elements. But when iterating over it like you do in the Classe.toString() method, you will see strange results due to the bogus ordering, because it upsets the set iterator. By the way, I would recommend a toString method printing only a simple string without line breaks. If you want to print something with multiple lines, you should do so in a utility method.

So you have several choices:

  1. Implement compareTo(Calendar) in Studente (maybe also in Utente, if you need it there). But then you need to make sure that it also does something meaningful for other Calendar instances passed in or at least throws meaningful exceptions if something other than a Student is passed in. But like I said, a student or user is not a calendar, so this feels awfully wrong.

  2. Remove extends GregorianCalendar from Utente and let Studente implements Comparable<Studente> instead. Then implement a compareTo(Studente) method which is consistent with equals(Object). Because your equals method compares first by ID (matricola), then by last name (cognome) and first name (nome), you should do the same in compareTo and not use some other criterion like mediaVoti() there. If you want to sort by average votes (whatever that means in your context), you can still do so later using Collections.sort(List, Comparator) or so, using a custom comparator.

  3. Do not implement Comparable at all, use a normal, unsorted set and sort students on demand using Collections.sort(List, Comparator).

I am showing you no. 2, so you can learn something about Comparable and compareTo:

public class Utente /*extends GregorianCalendar*/ {
// ...
}
public class Studente extends Utente implements Comparable<Studente> {
// ...
/**
* Confronta per matricola, cognome, nome
*/
@Override
public int compareTo(Studente altroStudente) {
if (this.equals(altroStudente))
return 0;
int result = Integer.compare(matricola, altroStudente.matricola);
if (result != 0)
return result;
result = cognome.compareTo(altroStudente.cognome);
if (result != 0)
return result;
return nome.compareTo(altroStudente.nome);
}
// ...
}

This solution also is not perfect, because it will throw exceptions if first or last names are null. But I guess for your simple solution this is acceptable.

There are many other smaller and bigger problems in your application, but I know you are all just starting, and I think all in all you have achieved a lot together as a group. Just some small things:

  • Classe.equals(Object) contains a bug: if (altro == this) return false; should be if (altro == this) return true;.

  • PercorsoDidattico.equals(Object) also contains a problem: When comparing strings, you should use equals, never ==, i.e. instead of an.indirizzo == this.indirizzo you ought to use an.indirizzo.equals(this.indirizzo).

I could write more, but the answer is long already.

After fixing the two equals methods and implementing the Comparable<Studente> interface correctly, your program prints:

...
input: Matricola: 0, Rossi, Marco Classe 4^B

Iscrizione effettuata: Rossi Marco B 4

Classe 3^A
1: Matricola: 0, Bianchi, Paola
2: Matricola: 0, Rossi, Mario
3: Matricola: 0, Verdi, Luigi

Classe 4^A
1: Matricola: 0, Gialli, Dario
2: Matricola: 0, Neri, Carlo
3: Matricola: 0, Rosa, Rosa
4: Matricola: 0, Rossetto, Giulia

Classe 4^B
1: Matricola: 0, Rossi, Marco
2: Matricola: 0, Viola, Viola

As you can see, the new compareTo(Studente) method leads to each class being sorted by student last name, then first name, because matricola is still unused in your program.


Update: By the way, the explanation for why it worked correctly in the debugger or with an additional Thread.sleep() is probably related to Calendar.compareTo using different timestamps, which accidentally led to at least the iterator finding all elements in the tree-set. Depending on your code, that would still have failed in other situations, you were just lucky there.

My code works in Debug mode, but not in Release mode

Debug modes often initialize heap data allocations. The program might be dependent on this behavior. Look for variables and buffers that are not getting initialized.

Bug in Release only

You could have uninitialized variables which are automatically initialized by the compiler in debug mode, and not in release mode.

Additionally you could be facing alignment issue and minor memory overruns which you are protected from in debug mode due to having no optimizations but when optimizations are enabled and your code is aligned differently this could cause issues and undefined behavior.

Try making sure that all your variables are explicitly initialized, and not assume that:

int i;
is the same as int i = 0;

How do i know when to use Debug mode or Release mode in flutter?

The apps in debug mode has a bigger size, because load libraries to allow the hot reload and hot restart
In release mode hot reload and hard reload isn't available for the same reason, and recommend use release mode for presentations and depending for some tests



Related Topics



Leave a reply



Submit