modifica: L'ho capito con l'aiuto dei commentatori. Per rispondere alla domanda posta nel mio titolo: No, non è il danneggiamento dello stack, il suo gdb riporta i valori sbagliati. Il programma si comporta effettivamente come previsto e ha il puntatore this
corretto. Il comportamento del buggy che mi ha spinto a postare questa domanda è probabilmente del tutto estraneo al problema che descrivo qui.Il costruttore ha chiamato con "questo" puntatore errato. È questa la corruzione dello stack?
Primo avvertimento. Credo che questo sia un problema di corruzione della memoria e normalmente non mi aspetto una risposta, ad eccezione di "controlla accuratamente il codice", ma ho visto questo comportamento saltar fuori ripetutamente e speravo che qualcuno di voi avesse una visione del problema e di come può trovare la sua fonte.
Attualmente sto implementando un'analisi statica a intervalli che tiene traccia del possibile intervallo di variabili in un programma C. Il costruttore di copia per la mia base di classe intervallo si presenta così:
itvt::itvt(const itvt& i)
: _i(i.type == INTBV ? new intbv_intervalt(i.i()) : NULL),
_f(i.type == FLOAT ? new float_intervalt(i.f()) : NULL),
type(i.type), other_bottom(i.other_bottom)
{ }
Ora, ho trovato un bug di corruzione della memoria e sono riuscito a risalire al seguente frammento di codice:
itvt itvt::get_split(bool le) const
{
itvt result(*this);
[...]
}
Utilizzando gdb, ho scoprire che la chiamata al costruttore non sembra per costruire l'oggetto "risultato":
Breakpoint 1, itvt::get_split (this=0x1016af560, le=false) at itv.cpp:517
517 itvt result(*this);
(gdb) n
519 if(is_singleton() || is_bot())
(gdb) print result
$3 = {
_i = {
_M_ptr = 0x7fff5fbfe100
},
_f = {
_M_ptr = 0x7fff5fbfed60
},
type = 1606410016,
other_bottom = 255
}
(gdb) print *this
$4 = {
_i = {
_M_ptr = 0x1020833a0
},
_f = {
_M_ptr = 0x0
},
type = itvt::INTBV,
other_bottom = false
}
Guardando più a fondo, trovo che all'interno del costruttore di copia, il puntatore "this" indica l'oggetto sbagliato:
Breakpoint 1, itvt::get_split (this=0x1016af560, le=false) at itv.cpp:517
517 itvt result(*this);
(gdb) print &result
$5 = (itvt *) 0x7fff5fbfdee0
(gdb) s
itvt::itvt (this=0x7fff5fbfdf80, [email protected]) at itv.cpp:500
500 type(i.type), other_bottom(i.other_bottom)
(gdb) print this
$6 = (itvt * const) 0x7fff5fbfdf80
Da "risultato" viene allocata sullo stack all'indirizzo 0x7fff5fbfdee0, mi si aspetterebbe il puntatore "this" all'interno del costruttore di copia per puntare allo stesso indirizzo. Invece punta a 0x7fff5fbfdf80.
Sembra che il costruttore di copie stia inizializzando qualcosa, ma non l'oggetto "risultato" nello stack su cui è chiamato. In realtà, posso accedere alla locazione di memoria che il costruttore inizializzato perfettamente:
Breakpoint 1, itvt::get_split (this=0x1016af560, le=false) at itv.cpp:517
517 itvt result(*this);
(gdb) s
itvt::itvt (this=0x7fff5fbfdf80, [email protected]) at itv.cpp:500
500 type(i.type), other_bottom(i.other_bottom)
(gdb) finish
Run till exit from #0 itvt::itvt (this=0x7fff5fbfdf80, [email protected]) at itv.cpp:500
itvt::get_split (this=0x1016af560, le=false) at itv.cpp:519
519 if(is_singleton() || is_bot())
(gdb) print *((const itvt*) (0x7fff5fbfdf80))
$7 = {
_i = {
_M_ptr = 0x1016b6d10
},
_f = {
_M_ptr = 0x0
},
type = itvt::INTBV,
other_bottom = false
}
La mia prima domanda: Può il fatto che i "questa" punti puntatore all'oggetto sbagliato essere spiegato come un comportamento normale? Sembra un qualche strano problema di corruzione della memoria, ma forse mi manca qualcosa.
Sto compilando con g ++ e "-O0 -ggdb" e ho fatto una nuova ricompilazione di tutto ciò che è successo. Ecco il mio g ++ versione:
[email protected] ai$ g++ --version
i686-apple-darwin11-llvm-g++-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
La mia seconda domanda: Se si tratta di corruzione della memoria, hai qualche consiglio su come posso rintracciare la fonte. Di solito seguo questi problemi alla loro causa principale usando gdb, ma non so dove cercare ora.
Questa non è la prima volta che incontro questo comportamento specifico. L'ho visto accadere quando esaminavo gli insetti di altre persone. In realtà non sono mai riuscito ad indirizzarlo direttamente, semplicemente ha smesso di funzionare o almeno è stato un problema visibile dopo qualche altro cambio di codice. Questo mi porta a credere che forse è solo un insolito artefatto di guardare allo stack usando gdb.
Sono grato per qualsiasi consiglio o intuizione che tu possa offrire.
edit: Ecco il frammento di rilevante della classe itvt:
class itvt
{
protected:
typedef std::auto_ptr<intbv_intervalt> iptrt;
typedef std::auto_ptr<float_intervalt> fptrt;
iptrt _i;
fptrt _f;
public:
typedef enum {INTBV, FLOAT, OTHER} itv_typet;
itv_typet type;
bool other_bottom;
//copy constr
itvt(const itvt& i);
inline intbv_intervalt& i() { return *_i; }
inline float_intervalt& f() { return *_f; }
inline const intbv_intervalt& i() const { return *_i; }
inline const float_intervalt& f() const { return *_f; }
itvt get_split(bool le) const;
[...]
};
Grazie per le informazioni, questo è interessante per me in un altro contesto. No, la classe ittv è completamente noiosa/non virtuale. Non vi è alcuna virtualità o eredità coinvolta in itvt o nei suoi membri. – leo
Solo una domanda: la tua versione di 'gdb' è aggiornata. Ho visto casi in cui l'uso di una vecchia versione di 'gdb' con una versione più recente del compilatore fa sì che' gdb' visualizzi le cose sbagliate. (A priori, questo dovrebbe essere un problema solo quando si cambiano le versioni principali: quando è uscito g ++ 4.0.0, ci è voluto un po 'di tempo per recuperare Gdb. Ma non si sa mai.) –
Un secondo punto è che quando si esegue 'per entrare in una funzione, alcuni debugger (non sono sicuro di gdb --- l'ho visto con VS, comunque) non vanno abbastanza nella funzione per lo stack frame locale da configurare. Ciò comporta che tutti i valori non sono corretti (se visualizzati dal debugger). –