WPR (Windows Performance Recorder)

CLI 版 wpr.exe は Windows 10 で OS 標準のツールとなった。
GUI 版 wprui.exe は Windows SDK をインストールする必要がある。SDK の機能の中でインストールするのは Windows Performance Toolkit だけでよい。

CLI 版の使い方

  • 管理者権限のコマンドプロンプトを起動する
  • 組み込みのプロファイル(トレースで収集したいものを表すプリセット)の一覧を表示する
wpr -profiles

プロファイル名を指定してトレースを開始する

wpr -start <profile> [-start <profile> ...]

Ex. CPU のみトレースする場合

wpr -start CPU

Ex. CPU と Heap をトレースする場合

wpr -start CPU -start Heap

WPR の現在の実行状況を表示する

wpr -status

トレースを終了する

wpr -stop <save filename>

ロギングモード

何も指定しなければ既定ではメモリモード。メモリ内の循環バッファーに記録される。
ファイルモードにするには -filemode を指定する。ファイルモードの場合、ファイルサイズはディスクの空き領域を使い果たすまで大きくなるので注意。

wpr -start CPU -filemode

OS Boot Trace

autologger (boot trace) は、システムの起動時に自動的に開始されるトレースセッション。Windows では、レジストリ設定を使用してブートプロセスの初期段階でトレースセッションを開始する方法が提供されている。

wpr -addboot <profile> [-addboot <profile> ...] -filemode -recordtempto <temp folder path>

Ex. First leve triage と CPU をトレースする

wpr -addboot GeneralProfile -addboot CPU -filemode -recordtempto C:\temp
wpr -stopboot <save filename>

プロファイル情報の詳細を確認

メモリモードの場合

wpr -profiledetails <profile>

ファイルモードの場合

wpr -profiledetails <profile> -filemode

WPA (Windows Performance Analyzer)

WPR で収集したデータは WPA を使って分析できる。WPA についてはまた別の記事で書く予定。

2022-11-12-1919.png

参考

タスクマネージャーの見方 (Memory 編)

こちらのマイクロソフトの記事がとても分かりやすかった。

MS:タスク マネージャーの見方(Memory)

物理メモリ

  • ハードウェア予約済み (Hardware Reserved)
    BIOS または他の周辺機器のドライバーで使用するために予約されたメモリ
  • 使用中 (In Use)
    プロセス、ドライバーまたは OS によって使用されているメモリ
  • 変更済み (Modified)
    他の用途で使用する前にディスクへの書き込みが必要な内容が含まれているメモリ
  • スタンバイ (Standby)
    キャッシュ データやアクティブに使用されていないコードが含まれているメモリ
  • 空き (Free)
    現在、使用されておらず、プロセス、ドライバーまたは OS にメモリが必要になったとき最初に使用されるメモリ領域

タスクマネージャーには「ハードウェア予約済み」と「使用中」の使用量は、そのまま表示されていますが、他の状態はまとめられて表示されています。「利用可能」は、[スタンバイ + 空き] であり、「キャッシュ済み」は、[変更済み + スタンバイ] になります。

System Commit Limit

  • システムコミット制限は「物理メモリ量 + 現在のページファイルサイズ」の合計値。タスクマネージャーの「コミット済み」のスラッシュの右側で確認できる。

2022-11-05-1401.png

  • より詳細な情報は Process Explorer の [View] - [System Information] で確認できる

2022-11-05-1410.png

Paged Pool, Non-paged Pool

  • カーネルモードヒープ (システムメモリプール) には、ページプールと、非ページプールがある
  • ページプールはページイン、ページアウトが可能。非ページプールは常に物理メモリに存在することが保証されている
  • タスクマネージャーのページプールと非ページプールには、空き容量と割り当て済み領域の両方が含まれる
  • ページプールの最大サイズは、Windows8.1 以降の 64-bit システムの場合 15.5TB か、システムコミット制限のどちらか小さい方
  • 非ページプールの最大サイズは、Windows8.1 以降の 64-bit システムの場合、物理メモリ量か 16TB のどちらか小さい方

物理メモリ量の制限

  • Windows 10 Pro x64 の物理メモリの制限:2TB
  • Windows 11 Pro x64 の物理メモリの制限:2TB

参考

Window Handle の作成で例外発生!

"Error Creating Window Handle" 例外に遭遇した場合、特定のアプリでの現象なのか、システム全体での現象なのかによって話が変わってくる。
以下では、特定のアプリでの現象という想定で話を進める。

まずは、タスクマネージャーの「詳細」画面で、「ハンドル数」「ユーザーオブジェクト」「GDIオブジェクト」の列を追加し、該当プロセスの値を確認する。

2022-11-04-2333.png

Object Categories

システムには 3 つのカテゴリのオブジェクトが用意されている。
1. User Object:ウィンドウ管理をサポート
2. GDI (Graphics Device Interface) Object:グラフィックスをサポート
3. Kernel Object:メモリ管理、プロセス実行、およびプロセス間通信 (IPC) をサポート

プロセスのハンドルテーブルは Kernel Object のみに使用され、User Object や GDI Object には使用されない

User Object 数が上限に達していないか

  • User Object はほとんどが デスクトップヒープ から割り当てられる
  • 既定ではプロセス毎に 10,000 個の上限がある
  • この上限は以下のレジストリで変更できるが、上限に達した理由を調査する必要がある
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\USERProcessHandleQuota

2022-11-04-2305.png

GDI Object 数が上限に達していないか

  • GDI Object はデスクトップヒープからではなく セッションプール から割り当てられる
  • 既定ではプロセスごとに 10,000 個の上限がある
  • この上限は以下のレジストリで変更できるが、上限に達した理由を調査する必要がある
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota

2022-11-04-2318.png

ハンドルの上限に達した?

ハンドルテーブルエントリ

  • プロセスのハンドルテーブルエントリが枯渇する可能性は極めて低い
  • Windows が 32-bit, 64-bit 版どちらであっても、エントリ数は 約1,600万個 もあり基本的には無制限と考えられる
  • 特定の時点で 1万個を超えるハンドルを開いているプロセスは設計がよくないか、ハンドルリークが発生しているかのいずれか

ハンドルテーブルを格納するために必要なメモリ量

  • ハンドルテーブルを格納するのに必要なメモリは ページプール から割り当てられる
  • 最大数のハンドル(約1,600万個)を格納するのに必要なメモリ量は、64-bit Windows の場合 256MB

2022-11-05-0925.png

2022-11-05-0926.png

オブジェクトの種類ごとのハンドル数

Sysinternals の handle ユーティリティを使うと、開かれているオブジェクトの種類ごとのハンドル数とその合計値を確認できる。

handle -s [-p <process name | pid>]

2022-11-27-2338.png

参考

NTFS 代替ストリーム

代替ストリーム

インターネットからダウンロードしたファイルや、メールの添付ファイルを NTFS 上に保存すると、ファイルに 代替ストリーム (Alternate Data Stream: ADS) が追加されます。ファイルのプロパティを開くと以下のようなセキュリティメッセージが表示されます。

2022-10-31-0940.png


ファイルに ADS が付いているかどうかは dir /r で確認できます。 :Zone.Identifier:$DATA というのがそれです。

2022-10-31-1001.png

また、more < ファイル名:Zone.Identifier で ADS の内容を確認できます。
notepad ファイル名:Zone.Identifier とかでもよいです。

2022-10-31-1004.png

zip ファイルを展開する場合の注意点

zip ファイルに ADS が付いている場合、Windows 標準の「展開」で解凍すると展開されたファイルにも ADS が付与されます。圧縮・解凍ソフト(Lhaplus や 7zip など) で解凍すると、展開したファイルには ADS が付与されないケースがあります。Lhaplus では付与されませんでした。

参考サイト

Windows Kernel Debug のやり方

ローカルシステムのデバッグ

LiveKd を利用すると、ローカルシステムのカーネルメモリのスナップショットをメモリダンプとして作成し、そのダンプファイルにデバッガ―をアタッチしてデバッグができる。

livekd -k "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe"
  • LiveKd は Sysinternals のツール。Microsft Store から Sysinternals をインストールできる。
  • OS をデバッグモードに切り替えて再起動する必要がない。ちなみに、デバッグモードに切り替えるのは msconfig などでできる。
  • デバッグ対象のコンピュータで進行中の挙動はアタッチしているスナップショットには反映されないので注意。デバッガ―を終了する際に再実行するか聞かれるので「y」を入力するとスナップショットがリフレッシュされる。
  • windbgWindows SDK をダウンロードし、Debugging Tools for Windows のみインストールすればよい。


カーネルデバッグ中の様子

2022-10-30-1617.png

LiveKd - windbg kernel debug

参考サイト

COMってなんだ

COM (Common Object Model) について調べたり実験したりして分かったことを書きます。
想像で書いている箇所もあり、間違いも多くあると思いますのでご注意ください。
調べれば調べるほどどこまでも奥深く沼だなーという気分になりました。

以下は、x64 PC 前提で書きます。

COM ってなんだ

  • インターフェースと実装の分離
  • COM はインターフェイスベースの開発を前提に作られている環境
  • COM はプログラミング言語に依存しないフレームワーク
  • インターフェース定義に専用言語 (IDL) を使う。真の COM プログラマは IDL から作業を開始する。
    • 専用言語でかかれたインターフェイスは、実装に使われるプログラミング言語を想定しない
    • 異なるプログラミング言語で作ったプログラムが相互に連携できる
    • 連携の部分は OS の COM がサポートしてくれる
    • IDL では COM クラスを coclass キーワードで定義する
    • coclass に設定された GUID は COM クラスを識別する。クライアントはこの GUID を利用して COM クラスの実装を起動する
  • COM ではそれぞれの GUID に固有の名前を付けている
    • IID (Interface-ID) - インターフェース属性の GUID
    • CLSID - coclass の GUID
    • LIBID - library の GUID
  • IUnknown インターフェイスの QueryInterface というメソッドで、COM オブジェクトがある特定のインターフェイスを実装しているかどうかを調べることができる
  • インターフェイス絶対に変更できないので、 バージョンアップするにはインターフェイスを新しく定義することになる

COM 開発の流れ

IDL でインターフェース・COMクラスを定義する

  1. IDL ファイル *.idlインターフェイス、COM クラスを定義(どのクラスがどのインターフェイスを持つか定義)する。
  2. ILD を開発環境に取り込むには、Visual Studio や Platform SDK に付属する IDL コンパイラ MIDL.exe で IDL ファイルをビルドする。COM サーバ、COM クラインアントを C++ 以外の言語で開発する場合は、IDL ファイルで library 属性を設定し、IDL コンパイラでビルドした時にタイプライブラリファイルが出力されるようにする。タイプライブラリは IDL をバイナリトークンにしたもの。

COM サーバ、COM クライアントを C++ で実装する場合

  1. IDL コンパイラで生成された *.h, *.c ファイルを使ってインターフェイスを実装する。
  2. ビルドして COM DLL を作る。

COM サーバ、COM クライアントを C++ 以外の言語で実装する場合

COM サーバの実装

  1. 参照にタイプライブラリを追加する。
  2. インターフェイスを実装する。
  3. ビルドして COM DLL を作る。

COM クライアントの実装

  1. VB の場合は CreateObject() の引数に ProgID を指定する。COM の GUID は長くて使いにくいので COM 利用側は プログラム識別子 (ProgID) という別名を利用する。
  2. ProgID がレジストリなどを利用して CLSID に変換され、CLSID を利用して DLL がロードされ、COM オブジェクトが生成される。
  3. COM オブジェクトを利用してクライアント側を実装する。

COM コンポートをレジストリに登録する

COMクライアントからCOMコンポーネント(COMサーバー)を利用するには事前にレジストリに登録が必要です。レジストリへの登録となるので管理者権限での実行が必要です。

COM がアンマネージコードで作成されている場合
[32bit COM の場合]

C:\Windows\SysWOW64\regsvr32 <COM コンポーネント>

[64bit COM の場合]

C:\Windows\System32\regsvr32 <COM コンポーネント>

regsvr32.exe を実行すると COM コンポーネントがロードされ、DllRegisterServer() 関数が呼び出される。通常、この関数の中にレジストリへの登録処理が書かれている。登録を解除するには regsvr32 /u <COM コンポーネント> を実行する。この場合は、DllUnregisterServer() 関数が呼ばれるのでこの中にレジストリの登録を解除する処理が書かれている。

COM がマネージコードで作成されている場合

[32bit COM の場合]

C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm <COM コンポーネント>

[64bit COM の場合]

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm <COM コンポーネント>

.Net から COMコンポーネントを利用する

  • 参照に *.tlb (Type Library) を追加します。事前にレジストリに COM コンポーネントが登録されていないとエラーになります(実験結果からこう理解していますが怪しいです・・・)
  • 参照を追加するとメタデータを含む 相互運用機能アセンブリ(Interop Assembly) が自動生成されます。Tlbimp.exe ツールが生成している?

    COM オブジェクトのメンバーへの参照は相互運用アセンブリにルーティングされてから、実際の COM オブジェクトに転送されます。 COM オブジェクトからの応答は相互運用アセンブリにルーティングされてから、.NET Framework アプリケーションに転送されます。
    COM相互運用

COM サーバー

  • COM サーバーには インプロセスサーバーアウトオブプロセスサーバー の 2種類がある。
  • インプロセスサーバーは DLL として実装され、クライアントと同じプロセス空間で実行される。そのためクライアントと bitness は同じである必要がある。クライアントは、COM インターフェースへの直接呼び出しを使ってインプロセスサーバーと通信する。
  • アウトオブプロセスサーバーは EXE として実装される。ローカルコンピューターまたはリモートコンピューター上に存在できる。別プロセスとの通信なので bitness は異なっていても問題ない。

64-bit OS で 32-bit COM を使う方法

リモートプロシージャコール (RPC) は問題なく 32-bit と 64-bit の境界を越えることができるため、COM コンポーネントをアウトオブプロセスで利用している場合は、64-bit Windows に移行するときに問題を引き起こすことはない。

COM コンポーネントをインプロセスからアウトオブプロセスに移行する方法

方法1:プロジェクトタイプをインプロセスからアウトオブプロセスに変換する

  • 関連するソースコードが手元にある必要がある
  • Visual Studio のプロジェクトの設定でサーバーのタイプを DLL から EXE に変更してビルドする
  • この方法の利点は 64-bit クライアント側は全く変更する必要がないこと

方法2: COM+ を利用する

方法3: dllhost をサロゲートホストとして使う

  • dllhost は 32-bit クライアントと 64-bit DLL の間の呼び出しを中継するラッパーとして機能する
  • レジストリ設定が必要

方法4: アプトオブプロセスの COM Wrapper を DLL に追加する

参考サイト

よさそうな本 (中古だけど)

MSIアンインストールの詳細ログ出力コマンド

詳細ログを出力してアンインストールする

msiexec /x <ProductCode> /l*vx! log.txt

インストールオプション

/i <Product.msi> - インストール
/x <Product.msi | ProductCode> - アンインストール

ログオプション

/l[*vx!] <LogFile>:詳細出力
    v - 詳細出力
    x - 詳細デバッグ情報
    ! - 各行をログにフラッシュ
    * - v オプションと x オプションを除くすべての情報をログに記録します