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

È stato utile?

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))
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top