44

最適化を有効にしてコードをビルドした場合にのみ再現されるコードのバグに遭遇しました。テスト用のロジックを複製するコンソールアプリを作成しました(以下のコード)。最適化を有効にすると、この無効なロジックの実行後に「value」がnullになることがわかります。

if ((value == null || value == new string[0]) == false)

修正は簡単で、問題のあるコードの下にコメントアウトされています。しかし...私はアセンブラのバグに遭遇したかもしれないか、おそらく他の誰かが値が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);
                //}
            }
        }
    }
}

通常の出力は次のようになります。

Pre-check Value is: System.String[]
Post-check Value is: System.String[]
Key is: blah
Value is: System.String[]

バグのある出力は次のとおりです。

Pre-check Value is: System.String[]
Post-check Value is:
Key is: blah
Value is:   

My Envは、.NET3.5SP1を搭載したWindowsServer2003R2を実行しているVMです。VS2008チームシステムの使用。

ありがとう、

ブライアン

4

5 に答える 5

45

はい、あなたの式は JIT オプティマイザーを致命的に混乱させます。生成されたマシン コードは次のようになります。

                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()

バグはアドレス 41 で発生します。オプティマイザーは、value が常に null になると判断したため、null を直接 String.Concat() に渡します。

比較のために、これは JIT 最適化がオフになっているときに生成されるコードです。

                    Console.WriteLine("Post-check Value is: " + value);
00000056  mov         ecx,dword ptr ds:[03342090h] 
0000005c  mov         edx,dword ptr [ebp-8] 
0000005f  call        6D77B790 

コードは移動されましたが、アドレス 5c では null の代わりにローカル変数 (値) が使用されていることに注意してください。

このバグは、connect.microsoft.com で報告できます。回避策は簡単です:

  if (value != null)
  {
    Console.WriteLine("Post-check Value is: " + value);
    new PrintManager().Print("blah", value);
  }
于 2010-01-25T21:46:19.010 に答える
3

このバグは .NET 4 (ベータ 2) で修正されたようです。上で強調表示されているビット nobugz の最適化された x86 逆アセンブリを次に示します。

                    Console.WriteLine("Post-check Value is: " + value);
00000056  mov         ecx,dword ptr ds:[033C2090h] 
0000005c  mov         edx,dword ptr [ebp-8] 
0000005f  call        65D8FE10

プログラムは、最適化モードと最適化されていないモードの両方で期待される出力も表示します。

于 2010-01-28T14:50:18.917 に答える
2
value == new string[0]

上記は私には奇妙な声明のように見えます。2 つの文字列配列を equals ステートメントで比較しています。両方が同じ配列を指している場合にのみ true になりますが、これはほとんどありません。これは、最適化されたバージョンでこのコードの動作が異なる理由をまだ説明していません。

于 2010-01-25T21:42:49.297 に答える
1

私は x64 を使用していますが、最初は問題を再現できませんでした。次に、ターゲットを x86 に指定したところ、それが起こりました。x64に戻ると、消えました。これが何を意味するのか正確にはわかりませんが、今まで何度か行ったり来たりしました。

于 2010-01-25T21:20:23.877 に答える
0

確かにバグのように見えますが、このように演算子のオペランドを入れ替えると再現しますか?

if (false == (null == value || new string[0] == value))
于 2010-01-25T21:47:23.577 に答える