2016-07-18 9 views
0

私たちは毎回知っているすべてのMVCアプリケーションを持っています(1日数回から2〜3日に1回)サイト全体のハングに悩まされています。それ自身でも再起動しません)。容疑者の数を1ページに減らすことができました(このページがぶら下がっているリクエストの先頭に表示されるハングに苦しむたびに)。開発マシンで問題を再現できないため、どのページが原因であるかを知ることは役に立たない。実際には、本番マシンで問題を再現することさえできません(ほとんどの場合、障害のあるページを開くだけで何かが壊れません)。ハンギングアクションをデバッグする方法

私たちが知っていることは、ユーザーがこのページ(コントローラの操作)に行くとサイトがハングアップすることがあり、リクエストが行われている途中でIISは、ユーザーがどのように障害ページにアクセスしたかを知っており、クエリ文字列paramsを使用しない単純なGETを処理しています。

ハングアップのコードがどこにあるのかを知りたいのですが、アプリ/ワーカースレッド/ IIS/Windows Serverからこのような情報を取得する方法はわかりません。例外はWindowsのログに保存されず、アプリケーションロガーに組み込まれていないため、何らかの例外が発生しています(おそらくハングアップと関係している)。

特定の時間にIISワーカースレッドが何をしているのか(対応するファイルとコード行を取得するなど)を知る方法は何ですか?


P.S.私は別の質問でサイト全体のハングアップの正確な症状を説明しましたが、これらはこの質問とは関係ありません。さらに、質問が広すぎて一般的な回答しか得られなかった。 P.S.2問題のあるページは、IISのWorker Processes \ Requestsを調べると見つかりました。

+0

デバッガを接続するか、ログを追加します。 – CodeCaster

+0

ステップ1 - 完全なメモリダンプを取得します。タスクエクスプローラまたはデバッグツールで実行できます。ステップ2 - [Debug Diagnostic Tool](https://www.microsoft.com/en-us/download/details.aspx?id=49924)を使用してメモリダンプを解析するか、Windbgを使用してください最初のオプション)。スレッドの完全なリストを取得し、スレッドごとにスタックを呼び出すことは比較的簡単です。Webアプリケーションが応答しなくなったと思うときは、メモリダンプを作成する必要があります。 – Igor

+0

サイドノート - 'Debug Diagnostic Tool'アプリは非常にうまく作られており、ダウンロードして試してみます。それは完全なGUIを持っているので、コマンドを学ぶ必要はありません。結果を分析するために少しは努力しますが、このようなことを考え出すことは、あなたがそれに近づく方法にかかわらず、通常は簡単な作業ではありません。 – Igor

答えて

3

これまで私は同じ問題を抱えていましたが、本当にすぐに私がMono.Cecilを使って一連のDLLをDLLに挿入する機能を手に入れました。以下のコードは、DLLの署名されていないバージョンにトレースを挿入し、開始時にロギング行を注入してコードに "インデントされたトレース"を作成するために使用できる新しいDLLを出力しますそれぞれのコールの入力と存在を時間を計ることができるようにします。サードパーティのツールがたくさんありますが、これは簡単です(最高でうまく動作します)、開発者を完全に制御し、ボーナスとして無料です。

また、トレースクラスを持つDLL(以下のIISStackTraceProvider)と、データのログに使用できる静的呼び出し "TraceStep"を作成する必要があります。ワーカープロセスとスレッド間でそのデータを再構築するには、 "HttpContext.Itemsプロパティ"を使用してBeginRequest/EndRequestにGUIDとストップウォッチを関連付けます。あなたのコールがハングアップしているので...「開始」しても「終了」しない、またはタイムアウトで終了するコールをトレースし、残りの部分を投げ捨てて速く保つことができます。

パフォーマンスに影響を与えずに、どのような要求を記録したいのか、どんな要求を捨て去るのかに注意しながら、本番環境のWebファームで1時間あたり100万件の呼び出しに対してこれをテストしました。また、私は書き込み時間が非常に速く、また無料であるため、Redisを使ってログをキャプチャしました。問題がトラップされると、Redisデータを読み取るだけです。

class TraceInjection 
{ 
    private ELogLevel logLevel; 
    public enum ELogLevel 
    { 
     eLow, 
     eMid, 
     eHigh 
    } 

    public TraceInjection(ELogLevel logLevel) 
    { 
     this.logLevel = logLevel; 
    } 

    public bool InjectTracingLine(string assemblyPath, string outputDirectory) 
    { 
     CustomAttribute customAttr; 
     AssemblyDefinition asmDef; 

     // New assembly path 
     string fileName = Path.GetFileName(assemblyPath); 

     string newPath = outputDirectory + "\\" + fileName; 

     // Check if Output directory already exists, if not, create one 
     if (!Directory.Exists(outputDirectory)) 
     { 
      Directory.CreateDirectory(outputDirectory); 
     } 

     ModuleDefinition modDefCopy = null; 
     TypeDefinition typDefCopy = null; 

     try 
     { 
      var resolver = new DefaultAssemblyResolver(); 
      resolver.AddSearchDirectory(System.IO.Path.GetDirectoryName(assemblyPath)); 

      var parameters = new ReaderParameters 
      { 
       AssemblyResolver = resolver, 
      }; 

      // Load assembly 
      asmDef = AssemblyDefinition.ReadAssembly(assemblyPath, parameters); 

      String functionsFound = ""; 

      foreach (var modDef in asmDef.Modules) 
      { 
       modDefCopy = modDef; 
       foreach (var typDef in modDef.Types) 
       { 
        typDefCopy = typDef;       
        foreach (MethodDefinition metDef in typDef.Methods) 
        { 
         try 
         { 
          // Skipping things I personally don't want traced... 
          if (metDef.IsConstructor || 
           metDef.IsAbstract || 
           metDef.IsCompilerControlled || 
           metDef.IsGetter || 
           metDef.IsSetter 
           ) continue; 

          functionsFound += String.Format("{0}\r\n", metDef.Name.Trim()); 


          // Get ILProcessor 
          ILProcessor ilProcessor = metDef.Body.GetILProcessor(); 

          /*** Begin Method ******/ 
          // Load fully qualified method name as string 
          Instruction i1 = ilProcessor.Create(
           OpCodes.Ldstr, 
           String.Format(">,{0},{1}", metDef.Name.Replace(",", ""), asmDef.Name.Name) 
          ); 
          ilProcessor.InsertBefore(metDef.Body.Instructions[0], i1); 

          // Call the method which would write tracing info 
          Instruction i2 = ilProcessor.Create(
           OpCodes.Call, 
           metDef.Module.Import(
            typeof(IISStackTraceProvider).GetMethod("TraceStep", new[] { typeof(string) }) 
           ) 
          ); 
          ilProcessor.InsertAfter(i1, i2); 

         }catch(Exception ex) 
         { 
          // ... 
         } 
        } 
       } 
      } 

      Console.Write(functionsFound); 
      Console.ReadKey(); 

      // Save modified assembly 
      asmDef.Write(newPath, new WriterParameters() { WriteSymbols = true }); 
     } 
     catch (Exception ex) 
     { 
      modDefCopy = null; 
      typDefCopy = null; 

      // Nothing to be done, just let the caller handle exception 
      // or do logging and so on 
      throw; 
     } 

     return true; 
    } 

    public bool TryGetCustomAttribute(MethodDefinition type, string attributeType, out CustomAttribute result) 
    { 
     result = null; 
     if (!type.HasCustomAttributes) 
      return false; 

     foreach (CustomAttribute attribute in type.CustomAttributes) 
     { 
      if (attribute.Constructor.DeclaringType.FullName != attributeType) 
       continue; 

      result = attribute; 
      return true; 
     } 

     return false; 
    } 
} 
+0

これは本当に良い答えです。ログをテキストファイルに書き込んで、リクエストが完了したときにそれを検出し、そこにすべてを書き込むだけで、単純化しました(私たちはあまりにも多くのリクエストを持っていませんでした)。これにより、私たちは問題を特定することができました。 Razorがハングする原因となった、邪魔にならないカスタムのバリデーターでした。 – jahu

関連する問題