この記事は、遠い2014年の記事の続きです。
前の記事で議論さ
れたことを思い出させて
ください 。
どの問題を解決しますか
Visual Studio 2015 ではC ++でソフトウェアを作成します。 もちろん、このプロジェクトには、カスタムデータ型があります。 このような型の例として、
MbSolidクラスを引用できます。 このクラスは
C3D数学コアの一部であり、剛体の抽象化です。 ボディは、面、いくつかの面による面などによって記述されます。 すなわち クラスの構造はかなり複雑であり、独自のアルゴリズムをデバッグする過程で、現時点でどのボディが判明しているかを視覚的に把握したいと思います。
最後の記事の写真。 カスタムクラスの例として、ラインセグメントクラスが使用されます。
この問題を解決するために、VisualStudio用の拡張機能が作成されました。 興味深いことは何もありません。リンクは最後の記事にあります。 しかし、問題がありました-デバッグされた(他の)プロセスのアドレス空間からデータを取得するVisualStudio拡張機能の方法は?
配列などの単純なデータ型の場合、すべてはそれほど複雑ではなく、Microsoftは
std :: vectorの例を用意しました。 そして、私たちの場合、
MbSolidに
は印象的な継承階層と多数のデータフィールドがあります。
カスタムタイプの変更
この問題に対する侵襲的な解決策は、以前の記事で提案されました。 マーカーフィールドが各カスタムクラスに追加されます(デバッグ中にレンダリングする)。 また、各非定数クラスメソッドでは、クラスデータを
共有メモリにシリアル化するコードが追加され
ます 。 マーカーフィールドは、クラスデータを保存した
共有メモリにアドレスを格納します。 デバッグ時に、関心のある変数の内容を表示すると、VisualStudio拡張機能はマーカーフィールドを検出し、
共有メモリからデータを逆シリアル化し、受信データを何らかの方法で視覚化します。
明らかな理由により、このソリューションは実際には適用されません。 特に、この方法でデバッグしたいクラスのソースコードにアクセスできない場合。 当時はもっと良いものを思い付くことができなかったため、このトピックは数年間行き詰っていました。
ユーザープロセスのサーバー
そして最近、別のストリームのユーザープロセスに存在し、VisualStudio拡張機能からの要求に応答する単純なサーバーを作成するというアイデアが生まれました。 サーバーの場合、
Microsoft C ++ REST SDKプロジェクトが基礎として採用されました。 このプロジェクトにより、HTTPサーバーをすばやく作成できます。HTTPサーバーは、GET要求を受信し、json形式でユーザークラスインスタンスの説明を返します。
MbSolidクラス(ソリッド)のインスタンスの視覚的表現に興味があることを思い出させてください。
サーバーへのリクエストでは、デバッグされたプロセスのアドレス空間の変数のアドレスが送信されます。 なぜなら サーバーは同じプロセス内にあるため、要求されたアドレスのデータに問題なくアクセスできます。 クラスインスタンスのアドレスを受け取ったサーバーは、このポインターを
MbSolid *型に
キャストします。 次に、サーバーはこのボディの近似をポリゴンメッシュの形式で作成します。 jsonの三角形の計算された頂点とインデックスをシリアル化し、応答を送信します。 VisualStudioの側では、拡張機能が応答を受け取り、データをデシリアライズし、結果のポリゴンメッシュをVisualStudioウィンドウに描画します。
その結果、VisualStudioの拡張機能はユーザーデータの構造を知る必要さえありません;正しいGETリクエストを送信し、応答をデシリアライズし、VisualStudioウィンドウに三角形を描くことができさえすればよいのです。 サーバーを拡張できます。 この方法で、ポリゴンメッシュまたはラインセグメントのセットとして表すことができるカスタムクラスをデバッグでき、VisualStudio拡張機能はそれらを視覚化できます。
さらに、この方法では、ブラウザからサーバーにリクエストを送信し、WebGLを使用してプロセスデータを視覚化することもできます。
簡単なデモを作成しました。 アプリケーションを起動します。 ブラウザでサンプルページを開き、ページ上の変数のアドレスを入力し、サーバーにリクエストを送信して、レスポンスを描画します。 なぜこれが必要なのかわかりませんが、クールなこと
サーバーを復活させます
すべてが順調です。 しかし、1つの問題があります。 ブレークポイントがトリガーされると、スタジオはユーザープロセスのすべてのスレッドを停止します。 その結果、サーバーも停止し、リクエストに応答できなくなります。 この問題を回避するには、次の松葉杖を使用します。ブレークポイントがトリガーされた現在のスレッドが凍結され、ユーザープロセスが開始されます。 この時点で、サーバーが有効になり、拡張機能は、目的の変数のアドレスを含むリクエストを送信します。 応答を受信した後、ユーザープロセスは再び一時停止され、ブレークポイントがトリガーされる現在のスレッドがデバッガーの現在のスレッドとして設定されます。 ユーザーにとっては、何も起こらず、プログラムがブレークポイントで停止したように見えます。
VisualStudio拡張コードでは、この松葉杖は次のようになります。 ブレークポイントがトリガーされました。 ユーザーは、目的の変数のデータを要求します。 この時点で、現在のスレッドをフリーズし、デバッガーを開始します。
if (dte.Debugger.CurrentMode != EnvDTE.dbgDebugMode.dbgBreakMode) return; currentThread = dte.Debugger.CurrentThread; currentThread.Freeze(); dte.Debugger.Go(false);
サーバーにリクエストを送信します。 答えが得られます。 プロセスを停止し、スレッドを解凍します。
if (dte.Debugger.CurrentMode == EnvDTE.dbgDebugMode.dbgBreakMode) return; dte.Debugger.Break(); if (currentThread != null) { currentThread.Thaw(); dte.Debugger.CurrentThread = currentThread; }
それだけです! サーバーからデータを受け取り、VisualStudioウィンドウにレンダリングしました。 プログラムの実行は、最初のブレークポイントで行われます。
結論として、私はこれまでのところ、このアプローチが生み出す副作用を理解していないことに注意したいと思います。 この記事のコメントであなたの考えを共有していただければ幸いです。