[C++/Mac] bug in std::ostringstream, of mijn fout?

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • MisterData
  • Registratie: September 2001
  • Laatst online: 29-08 20:29
Nadat een programma dat ik onlangs heb geport van Windows naar Mac begon te crashen in Debug-builds, maar het prima deed in Release-builds, ben ik daar eens ingedoken en heb ik het probleem weten te reduceren tot een testcase. Onderstaand programma geeft in Release-modus de (wat mij betreft) correcte output "12345[number=12345]", maar in Debug-modus wordt dat "12345[number=]", oftewel, lijkt het alsof de int helemaal niet aan de ostringstream wordt toegevoegd:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <sstream>
#include <iomanip>
using namespace std;

int main (int argc, char * const argv[]) {
    std::ostringstream wos;
    int x = 12345;
    wos << (const int&)x;
    wos.flush();
    std::string test = wos.str();
    
    std::cout << x;
    std::cout << "[number=" << test << "]" ;
    
    return 0;
}


(Bovenstaande programma gewoon in een nieuw XCode commandline project gooien, ik zit onder Snow Leopard en compileer dus voor 10.6 en x86_64, maar dat lijkt niets uit te maken).

Ik heb het niet gecheckt, maar ik kan me herinneren dat dit soort code onder Windows ook gewoon werkte, en volgens mij doe ik niet zoveel verkeerd. Het toevoegen van extra std::setbase(10) of std::dec manipulators helpt allemaal niet. Wie ziet wat ik over het hoofd zie?

[ Voor 8% gewijzigd door MisterData op 19-10-2009 17:04 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Er lijkt me niets mis met die code. Heb je er al eens gewoon doorheen getraced?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

komt het niet door die flush()?

Acties:
  • 0 Henk 'm!

  • MisterData
  • Registratie: September 2001
  • Laatst online: 29-08 20:29
Nope, die had ik er juist bijgezet om te kijken of het hielp. Het gekke is dat het met strings gewoon wel goed gaat (wos << "string"), maar met ints en doubles (of het nou literals zijn of const&'s ernaartoe) staat er gewoon niets:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <sstream>
#include <iomanip>

using namespace std;

int main (int argc, char * const argv[]) {
    std::ostringstream wos;
    int x = 12345;
    double y = 9876.0;
    std::cout << "good? " << wos.good() << std::endl;
    wos << "string" << 1234.5 << " " << y << " " << y;
    std::cout << "good? " << wos.good() << std::endl;
    std::string test = wos.str();
    
    
    std::cout << x;
    std::cout << "[number=" << test << "]" ;
    
    return 0;
}


Verder geen exceptions of wat dan ook... Het gekke is nu dat er in Debug-modus eerst "good? 1" en dan "good? 0" staat, maar dat in Release-modus alles goed werkt en er dus ook twee keer "good? 1" uitkomt. Op de een of andere manier kan de ostringstream in Release-modus wél ints naar strings converteren, en in Debug-modus ineens niet...

Edit: ander pikant detail, het gaat in beide builds goed als ik compileer met GCC4.0 in plaats van met de system-default GCC 4.2 (met LLVM GCC 4.2 gaat het ook mis).

[ Voor 7% gewijzigd door MisterData op 19-10-2009 18:07 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Die zou niet de buffer weer leeg moeten maken. flush() roept pubsync() aan op de basic_stringbuf, die weer sync() aanroept, en die returnt by default 0 zonder verder iets te doen.

Je zou evt. kunnen checken of 'wos' errors heeft na de insert of na de flush.

.edit: laat

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Nu online
Zal wel een compilerbug zijn. Compileer eens met -fdump-tree-ssa en post de uitvoer van de ssa-pass eens ergens? (Niet hier, is veel code.)
MisterData schreef op maandag 19 oktober 2009 @ 18:05:
Edit: ander pikant detail, het gaat in beide builds goed als ik compileer met GCC4.0 in plaats van met de system-default GCC 4.2 (met LLVM GCC 4.2 gaat het ook mis).
't Moet dan haast wel een C++ front-end bug zijn...

[ Voor 53% gewijzigd door Soultaker op 19-10-2009 18:14 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Wellicht is het een locale issue? Aangezien die voor getallen gebruikt wordt, maar niet voor strings. Doe eens
C++:
1
wos.imbue(std::cout.getloc());

vantevoren

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Het lijkt me interessant om een stukje disassembly te zijn. Er staan 4 calls naar operator<<, maar 1 daarvan lijkt niet te gebeuren? En welke overloads zijn het precies?

[ Voor 11% gewijzigd door MSalters op 19-10-2009 20:24 ]

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • MisterData
  • Registratie: September 2001
  • Laatst online: 29-08 20:29
De assembly die door GCC 4.2 (Debug build) wordt gegenereerd (en dus niet goed werkt): hier. De assembly gegenereerd door GCC 4.0 in Debug mode (werkt): hier. Voor het gemak heb ik 'm even laten compileren naar i386, maar het probleem treedt net zo hard op als ik 'm als x86_64 compileer. Het gebruikte stukje code:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <sstream>
#include <iomanip>

using namespace std;

int main (int argc, char * const argv[]) {
    std::ostringstream wos;
    int x = 12345;
    std::cout << "good? " << wos.good() << std::endl;
    wos << x;
    std::cout << "good? " << wos.good() << std::endl;
    std::string test = wos.str();
    
    
    std::cout << x;
    std::cout << "[number=" << test << "]" ;
    
    return 0;
}



De 'truc' die .oisyn aandraagt, lijkt overigens geen effect te hebben...

Hier hebben ze het over hetzelfde probleem. Ik gok een bugje in GCC 4.2!

[ Voor 8% gewijzigd door MisterData op 20-10-2009 08:54 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Een bugje? :X

Er zit trouwens 0 verschil in de assembly, maar je hebt alleen maar main() gepost en ik had sowieso al het vermoeden dat daar het probleem niet zat. 't Is waarschijnlijk een library iets.

[ Voor 114% gewijzigd door .oisyn op 20-10-2009 11:02 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Lijkt me ook.

De "endl" calls zijn makkelijk terug te vinden; na Llabel8 en Llabel18. De daarbij gebruikte operator<< voor "good? " is __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc.

Deze zien we verder nog terugkomen bij Llabel24 en Llabel28; dat moet dus voor "[number=" en "]" zijn. Zit daartussen nog een unieke call? Ja, LLabel26 bevat een call naar _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKSbIS4_S5_T1_E

(zou handig zijn om de demangled symbols te zien; assembly kan ik nog lezen...).

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Nu online
Om mangled names te decoderen kun je c++filt gebruiken, ziehier het resultaat: http://pastebin.com/m39b87bcf

Maar zoals ik hier al suggereerde kun je meestal beter naar de treelang code kijken; dat is het interne formaat wat GCC gebruikt tot vlak vóór het genereren van code, wat genoeg is als het probleem niet in de code generator zit, anders zou het probleem niet ook met LLVM optreden. Die treelang code bevat meer informatie en is (voor mensen ;)) makkelijker te lezen dan de assembly output (bovendien kun je dan zien in welk stadium een fout geïntroduceert wordt, wat vooral handig is bij bugs die optreden mét optimalisaties).

Anyway, in dit concrete geval wordt operator<<(ostream &,int) wél aangeroepen, maar deze functie wordt níet geinlined, wat suggereert dat de fout in de C++ library zit (libstdc++). Onder Linux en Windows heb ik geen aparte versies van die library voor debug en release builds, dus kan zo'n fout niet optreden. Hoe zit dat op de Mac?

Als er een aparte debug-library is, dan is die waarschijnlijk miscompiled. Welke versie van GCC gaat 't precies om? Er zijn vijf releases van GCC 4.2 geweest, de laatste is 4.2.4, en van eerdere versies is het bekend dat ze regressies bevatten.

Acties:
  • 0 Henk 'm!

  • MisterData
  • Registratie: September 2001
  • Laatst online: 29-08 20:29
Soultaker schreef op dinsdag 20 oktober 2009 @ 20:18:
Om mangled names te decoderen kun je c++filt gebruiken, ziehier het resultaat: http://pastebin.com/m39b87bcf
Altijd handig om te weten, dat soort trucjes! :)
Als er een aparte debug-library is, dan is die waarschijnlijk miscompiled. Welke versie van GCC gaat 't precies om? Er zijn vijf releases van GCC 4.2 geweest, de laatste is 4.2.4, en van eerdere versies is het bekend dat ze regressies bevatten.
Zo te zien wordt 4.2.1 gebruikt (i686-apple-darwin10-gcc-4.2.1). Ik zal binnenkort eens uitzoeken wat XCode precies doet met de libraries in Debug/Release. Hoe dan ook, ik kan nu debuggen in de oudere versie van GCC en de Release-versie doet het prima onder 4.2.1. Ik was alleen bang dat er ergens geheugencorruptie plaatsvond en dat dat door mijn code kwam, maar dat is dus zo te zien niet zo. Ik kreeg namelijk later in een ander stukje code een melding dat free() werd aangeroepen op een stuk geheugen dat niet door malloc() was gealloceerd, en dat was ergens in de stringstream-code. Had daar ook een melding in de GCC bugtracker voor gevonden, maar kan de link zo snel niet vinden.

Acties:
  • 0 Henk 'm!

  • MisterData
  • Registratie: September 2001
  • Laatst online: 29-08 20:29
Misschien dat jullie er iets mee kunnen, dit is de GCC commandline in debug:

code:
1
/Developer/usr/bin/gcc-4.2 -x c++ -arch i386 -fmessage-length=0 -pipe -Wno-trigraphs -fpascal-strings -fasm-blocks -O0 -Wreturn-type -Wunused-variable -D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1 -isysroot /Developer/SDKs/MacOSX10.6.sdk -mfix-and-continue -fvisibility-inlines-hidden -mmacosx-version-min=10.6 -gdwarf-2 -iquote /Users/tommy/Desktop/bugtest/build/bugtest.build/Debug/bugtest.build/bugtest-generated-files.hmap -I/Users/tommy/Desktop/bugtest/build/bugtest.build/Debug/bugtest.build/bugtest-own-target-headers.hmap -I/Users/tommy/Desktop/bugtest/build/bugtest.build/Debug/bugtest.build/bugtest-all-target-headers.hmap -iquote /Users/tommy/Desktop/bugtest/build/bugtest.build/Debug/bugtest.build/bugtest-project-headers.hmap -F/Users/tommy/Desktop/bugtest/build/Debug -I/Users/tommy/Desktop/bugtest/build/Debug/include -I/Users/tommy/Desktop/bugtest/build/bugtest.build/Debug/bugtest.build/DerivedSources/i386 -I/Users/tommy/Desktop/bugtest/build/bugtest.build/Debug/bugtest.build/DerivedSources -c /Users/tommy/Desktop/bugtest/main.cpp -o /Users/tommy/Desktop/bugtest/build/bugtest.build/Debug/bugtest.build/Objects-normal/i386/main.o


En deze is in Release:

code:
1
/Developer/usr/bin/gcc-4.2 -x c++ -arch i386 -fmessage-length=0 -pipe -Wno-trigraphs -fpascal-strings -fasm-blocks -Os -mdynamic-no-pic -Wreturn-type -Wunused-variable -isysroot /Developer/SDKs/MacOSX10.6.sdk -fvisibility=hidden -fvisibility-inlines-hidden -mmacosx-version-min=10.6 -gdwarf-2 -iquote /Users/tommy/Desktop/bugtest/build/bugtest.build/Release/bugtest.build/bugtest-generated-files.hmap -I/Users/tommy/Desktop/bugtest/build/bugtest.build/Release/bugtest.build/bugtest-own-target-headers.hmap -I/Users/tommy/Desktop/bugtest/build/bugtest.build/Release/bugtest.build/bugtest-all-target-headers.hmap -iquote /Users/tommy/Desktop/bugtest/build/bugtest.build/Release/bugtest.build/bugtest-project-headers.hmap -F/Users/tommy/Desktop/bugtest/build/Release -I/Users/tommy/Desktop/bugtest/build/Release/include -I/Users/tommy/Desktop/bugtest/build/bugtest.build/Release/bugtest.build/DerivedSources/i386 -I/Users/tommy/Desktop/bugtest/build/bugtest.build/Release/bugtest.build/DerivedSources -c /Users/tommy/Desktop/bugtest/main.cpp -o /Users/tommy/Desktop/bugtest/build/bugtest.build/Release/bugtest.build/Objects-normal/i386/main.o

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Nu online
Kun je die command lines ook eens uitvoeren met --print-sysroot erachter? Ik gok dat er een aparte sysroot is voor een debug of release build. Alternatief kun je ook eens ldd runnen op de twee executables om te zien welke libraries gebruikt worden.

In ieder geval helpt dit niet concreet om je probleem op te lossen. Als 't probleem in de C++ library zit is upgraden naar een hogere versie waarschijnlijk de makkelijkste oplossing. :)
Pagina: 1