Bug solo che si verificano quando l'ottimizzazione della compilazione abilitato
-
22-09-2019 - |
Domanda
mi sono imbattuto in un bug nel codice che viene riprodotto solo quando il codice è costruito con le ottimizzazioni attivate. Ho fatto una console app che replica la logica per il test (codice qui sotto). Vedrai che quando è attivata l'ottimizzazione 'valore' diventa nulla dopo l'esecuzione di questa logica non valida:
if ((value == null || value == new string[0]) == false)
La soluzione è semplice ed è commentata sotto il codice incriminato. Ma ... io sono più preoccupato del fatto che io possa aver incontrato un bug nel assembler o forse qualcun altro ha una spiegazione del motivo per cui valore viene impostato su null.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace memory_testing
{
class Program
{
sta tic void Main(string[] args)
{
while(true)
{
Console.Write("Press any key to start...");
Console.ReadKey();
Console.WriteLine();
PrintManagerUser c = new PrintManagerUser();
c.MyProperty = new string[1];
}
}
}
public class PrintManager
{
public void Print(string key, object value)
{
Console.WriteLine("Key is: " + key);
Console.WriteLine("Value is: " + value);
}
}
public class PrintManagerUser
{
public string[] MyProperty
{
get { return new string[100]; }
set
{
Console.WriteLine("Pre-check Value is: " + value);
if ((value == null || value == new string[0]) == false)
{
Console.WriteLine("Post-check Value is: " + value);
new PrintManager().Print("blah", value);
}
//if (value != null && value.Length > 0)
//{
// new PrintManager().Print("blah", value);
//}
}
}
}
}
L'output normale dovrebbe essere:
Pre-check Value is: System.String[]
Post-check Value is: System.String[]
Key is: blah
Value is: System.String[]
L'uscita buggy è:
Pre-check Value is: System.String[]
Post-check Value is:
Key is: blah
Value is:
Il mio Env è una VM in esecuzione Windows Server 2003 R2 con .NET 3.5 SP1. Utilizzando VS2008 Team System.
Grazie,
Brian
Soluzione
Sì, la tua espressione confonde fatalmente l'ottimizzatore JIT. Il codice macchina generato è simile al seguente:
if ((value == null || value == new string[0]) == false)
00000027 test esi,esi ; value == null?
00000029 je 00000075
0000002b xor edx,edx ; new string[0]
0000002d mov ecx,6D913BD2h
00000032 call FFD20BC8
00000037 cmp eax,esi ; (value == new string[0]) == false?
00000039 je 00000075
{
Console.WriteLine("Post-check Value is: " + value);
0000003b mov ecx,dword ptr ds:[03532090h] ; "Post-check value is: "
00000041 xor edx,edx ; BUGBUG not null!
00000043 call 6D70B7E8 ; String.Concat()
00000048 mov esi,eax ;
0000004a call 6D72BE08 ; get Console.Out
0000004f mov ecx,eax
00000051 mov edx,esi
00000053 mov eax,dword ptr [ecx]
00000055 call dword ptr [eax+000000D8h] ; Console.WriteLine()
L'errore si verifica all'indirizzo 41, l'ottimizzatore ha concluso che il valore sarà sempre nullo quindi passa direttamente un null a String.Concat ().
Per fare un confronto, questo è il codice che viene generato quando l'ottimizzazione JIT è disattivato:
Console.WriteLine("Post-check Value is: " + value);
00000056 mov ecx,dword ptr ds:[03342090h]
0000005c mov edx,dword ptr [ebp-8]
0000005f call 6D77B790
Il codice ottenuto mossa, ma notano che all'indirizzo 5c esso utilizza ora la variabile locale (valore) invece di null.
È possibile segnalare questo bug a connect.microsoft.com. La soluzione è semplice:
if (value != null)
{
Console.WriteLine("Post-check Value is: " + value);
new PrintManager().Print("blah", value);
}
Altri suggerimenti
Questo bug sembra essere stato risolto in .NET 4 (beta 2). Ecco lo smontaggio x86 ottimizzato per la nobugz bit evidenziato sopra:
Console.WriteLine("Post-check Value is: " + value);
00000056 mov ecx,dword ptr ds:[033C2090h]
0000005c mov edx,dword ptr [ebp-8]
0000005f call 65D8FE10
Il programma visualizza anche i risultati attesi in entrambe le modalità ottimizzate e non ottimizzate.
value == new string[0]
L'aspetto di cui sopra, come una dichiarazione strano per me. Si confrontano due array di stringhe con una uguale dichiarazione. Ciò si tradurrà solo in vera se entrambi punto allo stesso array, che è abbastanza improbabile. Questo non spiega ancora perché questo codice si comporta in modo diverso in una versione ottimizzata.
Sono su x64 e non sono riuscito a riprodurre il problema in un primo momento. Poi ho specificato la destinazione come x86, ed è successo a me. Torna a x64, ed è andato via. Non sei sicuro di ciò che questo significa, esattamente, ma sono andato avanti e indietro un paio di volte.
appare certamente come un insetto, non si riproduce quando si scambia gli operandi dell'operatore come questo?
if (false == (null == value || new string[0] == value))