Found Nasty Bug in GCC 4.1.2 on CentOS 5

gcc.png

I was working on my latest price feeder today and got word that I wasn't sending enough information to the receiving system for it to make proper use of the data. Basically, I needed to get more data about the instruments from this system and then feed it back to the system with each price for each instrument. Problem was, that really made the way I was storing data for each instrument a royal pain and I needed to go from a simple STL data structure to a class that will hold all the data for each instrument and then put these instances into a data structure for efficient access.

What I did was something a lot like this test code:

  1. /*
  2.  * This trys to reproduce the bug I was seeing in the initialization
  3.  * of the CKString while in a CKVector in a std::map.
  4.  */
  5. #include <iostream>
  6. #include <map>
  7. #include "CKVector.h"
  8. #include "CKString.h"
  9.  
  10. typedef struct tuple_t {
  11. CKString one;
  12. CKString two;
  13. CKString three;
  14.  
  15. // constructors
  16. tuple_t() :
  17. one(),
  18. two(),
  19. three()
  20. {
  21. }
  22.  
  23. tuple_t( const CKString & aOne,
  24. const CKString & aTwo,
  25. const CKString & aThree ) :
  26. one(aOne),
  27. two(aTwo),
  28. three(aThree)
  29. {
  30. }
  31.  
  32. tuple_t( const tuple_t & anOther ) :
  33. one(),
  34. two(),
  35. three()
  36. {
  37. *this = anOther;
  38. }
  39.  
  40. virtual ~tuple_t()
  41. {
  42. }
  43.  
  44. tuple_t & operator=( const tuple_t & anOther )
  45. {
  46. one = anOther.one;
  47. two = anOther.two;
  48. three = anOther.three;
  49.  
  50. return *this;
  51. }
  52.  
  53. bool operator==( const tuple_t & anOther )
  54. {
  55. bool equal = true;
  56. if ((one != anOther.one) ||
  57. (two != anOther.two) ||
  58. (three != anOther.three)) {
  59. equal = false;
  60. }
  61. return equal;
  62. }
  63.  
  64. bool operator!=( const tuple_t & anOther )
  65. {
  66. return !operator==(anOther);
  67. }
  68.  
  69. CKString toString() const
  70. {
  71. CKString retval("[one=");
  72. retval.append(one).append(", two=").append(two).
  73. append(", three=").append(three).append("]");
  74. return retval;
  75. }
  76. } tuple;
  77.  
  78. typedef CKVector<tuple> TList;
  79. typedef std::map<CKString, TList> TListMap;
  80.  
  81. int main(int argc, char *argv[]) {
  82. // make the map - no entries
  83. TListMap myMap;
  84. // this is the first key we'll be using
  85. CKString myKey("key");
  86. // create the first map entry
  87. TList & list = myMap[myKey];
  88. // create a tuple to place on the list
  89. tuple t("a", "b", "c");
  90. std::cout << "tuple = " << t.toString() << std::endl;
  91. // put the tuple on the list
  92. std::cout << "list has " << list.size()
  93. << " elems with capacity of " << list.capacity()
  94. << std::endl;
  95. if (!list.contains(t)) {
  96. std::cout << "tuple not in list, adding..." << std::endl;
  97. list.addToEnd(t);
  98. }
  99. // print out the map
  100. std::cout << "here's the map now: ";
  101. TListMap::iterator mi;
  102. for (mi = myMap.begin(); mi != myMap.end(); ++mi) {
  103. std::cout << mi->first << " = [";
  104. for (int i = 0; i < mi->second.size(); ++i) {
  105. if (i != 0) {
  106. std::cout << ", ";
  107. }
  108. std::cout << mi->second[i].toString();
  109. }
  110. std::cout << "]" << std::endl;
  111. }
  112. }

Lines 10-76 simply creates the object that is going to hold the instrument data - in this case, three pieces of data. Line 78 creates a type that is a simple vector of these tuples. Line 79 creates an STL map where the key is a string and the value is a vector of these tuples. Seems like a reasonable data structure, given that I'm basing this all on CKit.

In the main method, I'm basically creating a map of these string-list pairs, then creating a tuple and seeing if the new key isn't there - it's not, and then adding the tuple to the end of the list. This all works fine on Mac OS X 10.5.2 and GCC 4.0.1 (Xcode 3.0). But on RHEL 5 (CentOS 5) with GCC 4.1.2 we get an entirely different result.

At line 97 we get a very spectacular core dump. The gdp session is basically showing me that the initial value of one in the existing allocated storage in the vector for the first element is not being properly initialized. This initialization is done properly on OS X, but on RHEL5 it's not. When I change the typedefs to say something like this:

  1. typedef CKVector<tuple> TList;
  2. typedef std::map<CKString, TList*> TListMap;

thus making it necessary for me to new a TList and place that into the TListMap, and then delete it when I'm done, then things work just fine and the initialization is properly done.

I'm not that big of a stickler for compiler bugs - they happen, but so rarely that it's not worth getting all bent out of shape over one that's so easily worked-around. But I'll admit, it's a little frustrating to think that it's your fault for several hours only to find out that it's not, that at least a part of me wants to send in a bug report, but I probably won't. Just glad I got it figured out.