リリースバージョン:デバッグはまだ始まったばかりです...

何言ってるの?



プログラムはようやく機能し、顧客は自信を持ってテストサイトでボタンを押します。すべてが通常どおり動作します...しかし、喜んで急いでください:リリースバージョンのリリース後に問題が発生する可能性があります。 ソフトウェアは不可解なバグで溢れ出し始め、顧客はイギリス国旗であなたを引き裂く準備ができています...



これはS-shnikovだけの問題だと思いますか? あなたは間違っています!



実用例



通常、デバッグバージョンをテストサイトに配置することに慣れています(もちろん、これはすべての人に当てはまるわけではありませんが、信じられますが、これは非常に頻繁に起こります)。 これにより時間を節約でき、より多くのデバッグ情報があり、リモートデバッグに巻き込まれる可能性があります...



リリースをリリースすることの負担は、あなたにはほとんど関心がないように思われますが、.NETは少し改善されたC ++ であり、特定の問題があります。 しかし、すべてが最初に見えるほどクラウドレスではありません。



例:

static void Main( string [] args) { System.Threading.Timer t = new System.Threading.Timer(state => { Console .WriteLine( "Timer!" ); GC.Collect(); }, null , 0, 1000); Console .ReadKey(); } * This source code was highlighted with Source Code Highlighter .



  1. static void Main( string [] args) { System.Threading.Timer t = new System.Threading.Timer(state => { Console .WriteLine( "Timer!" ); GC.Collect(); }, null , 0, 1000); Console .ReadKey(); } * This source code was highlighted with Source Code Highlighter .



  2. static void Main( string [] args) { System.Threading.Timer t = new System.Threading.Timer(state => { Console .WriteLine( "Timer!" ); GC.Collect(); }, null , 0, 1000); Console .ReadKey(); } * This source code was highlighted with Source Code Highlighter .



  3. static void Main( string [] args) { System.Threading.Timer t = new System.Threading.Timer(state => { Console .WriteLine( "Timer!" ); GC.Collect(); }, null , 0, 1000); Console .ReadKey(); } * This source code was highlighted with Source Code Highlighter .



  4. static void Main( string [] args) { System.Threading.Timer t = new System.Threading.Timer(state => { Console .WriteLine( "Timer!" ); GC.Collect(); }, null , 0, 1000); Console .ReadKey(); } * This source code was highlighted with Source Code Highlighter .



  5. static void Main( string [] args) { System.Threading.Timer t = new System.Threading.Timer(state => { Console .WriteLine( "Timer!" ); GC.Collect(); }, null , 0, 1000); Console .ReadKey(); } * This source code was highlighted with Source Code Highlighter .



  6. static void Main( string [] args) { System.Threading.Timer t = new System.Threading.Timer(state => { Console .WriteLine( "Timer!" ); GC.Collect(); }, null , 0, 1000); Console .ReadKey(); } * This source code was highlighted with Source Code Highlighter .



  7. static void Main( string [] args) { System.Threading.Timer t = new System.Threading.Timer(state => { Console .WriteLine( "Timer!" ); GC.Collect(); }, null , 0, 1000); Console .ReadKey(); } * This source code was highlighted with Source Code Highlighter .



  8. static void Main( string [] args) { System.Threading.Timer t = new System.Threading.Timer(state => { Console .WriteLine( "Timer!" ); GC.Collect(); }, null , 0, 1000); Console .ReadKey(); } * This source code was highlighted with Source Code Highlighter .



  9. static void Main( string [] args) { System.Threading.Timer t = new System.Threading.Timer(state => { Console .WriteLine( "Timer!" ); GC.Collect(); }, null , 0, 1000); Console .ReadKey(); } * This source code was highlighted with Source Code Highlighter .



static void Main( string [] args) { System.Threading.Timer t = new System.Threading.Timer(state => { Console .WriteLine( "Timer!" ); GC.Collect(); }, null , 0, 1000); Console .ReadKey(); } * This source code was highlighted with Source Code Highlighter .







著者の意図によると、最も単純なコードは、切望されている「タイマー!」を1秒に1回コンソールに書き込むことです。 デバッグ構成で収集されたプロジェクトのデバッグでは、疑わしいものは何も明らかにされず、リリースが準備されています...プログラムは動作しません



デブリーフィング



はい、はい、この「バグ」は最適化の直接的な結果です(そしてもちろん、プログラマーの小さな過失です)。 怠慢は、指定されたSystem.Threading.Timerが実際にIDisposableを継承するという事実にあり、最良の場合、それに対してDispose()メソッドを呼び出す必要があります。 さて、これを行わないようにしましょう(実際、これは無関係です)。



リリースおよびデバッグバージョンで生成されたILコードのリストを調査し、これを確認します。

デバッグ:

.method private hidebysig static void Main(string[] args) cil managed

{

.entrypoint

.maxstack 5

.locals init (

[0] class [mscorlib]System.Threading.Timer t)


L_0000: nop

L_0001: ldsfld class

[mscorlib]System.Threading.TimerCallback ConsoleApplication11.Program::CS$<>9__CachedAnonymousMethodDelegate1

....











およびリリース:

.method private hidebysig static void Main(string[] args) cil managed

{

.entrypoint

.maxstack 8

L_0000: ldsfld class [mscorlib]System.Threading.TimerCallback ConsoleApplication11.Program::CS$<>9__CachedAnonymousMethodDelegate1

....











違いを強調しました



まだ推測していない人のために


デバッグバージョンでは、コンパイラは使用されていない(宣言を除く)Timerリンクをスタックに残し、GCはリンクが(メソッドが終了するまで)生きていることを確認します。 タイマーが実行されています。



このバージョンのリリースでは、リンクはどこにも存在せず、最初の(保証されない)メソッドGC.Collect()はタイマーを破壊します。



あとがき



上記の例のように、実生活で明示的な(またはそうでない)オプティマイザーのバグに出くわすことは決してないことを心から願っています。 このようなバグの検索(特に顧客側)は非常に複雑です。「欠陥」アセンブリとそのさらなる分析を検索するために、すべてのコンポーネントのデバッグバージョンをアセンブルし、リリースで徐々にパーツをコンパイルすることをお勧めします。 デバッグ情報(通常は通常.pdbファイル)の配布も役立ちます。



良い金曜日を過ごして、ログイン8-)



All Articles