Showing posts with label windows. Show all posts
Showing posts with label windows. Show all posts

2017-08-19

LLVMがWindowsのデバッグ情報フォーマットのPDBをサポート

LLVM Project Blog: LLVM on Windows now supports PDB Debug Info

この数年、clangをWindowsでソフトウェア開発するための世界級のツールチェインにするために尽力してきた。このことについては、すでに何度も書いてきたことだ。LLVMは完全なABI互換を実現した(ただしバグ互換ではない)。互換性を実現するのが難しい分野にデバッグ情報があるが、この2年間で、LLVMは飛躍的な発展をとげた。とりあえず結論を先に書くとこうだ。WindowsでClangを使うと、PDBデバッグ情報が出せる。

背景:CodeView VS PDB

CodeViewは1980年台の中頃にMicrosoftによって考案されたデバッグ情報フォーマットだ。様々な理由で、他のデバッガーはDWARFという独立したフォーマットを開発し、これは標準化されて、多くのコンパイラーとプログラミング言語でサポートされている。CodeViewは、DWARFと同じく、ソースコード行とコードアドレスのマッピングと、プログラムが使う型とシンボルのレコード集である。デバッガーはこの情報を使って、関数名でブレイクポイントを設定したり、変数の値を表示したりする。ただし、CodeViewはあまりよくドキュメント化されていない。最新の公式なドキュメントは少なくとも20年前のものだ。レコードの中にはドキュメント化されているものと同じフォーマットのものもあるが、まだドキュメント化されていない後々の追加のレコードもある。

ここで重要なのが、CodeViewは単にレコード集だということだ。もしユーザーが、「Fooの値を表示してくれ」といったときどうなるだろうか。デバッガーはFooについてのレコードを検索する。そして物事は更に複雑になる。どの最適化が有効にされていたのか? コンパイラーのバージョンは?(これはコンパイラーのバージョンによって一部のABIの非互換があったり、極度に最適化されたコードからバックトレースを再現する際のヒントとして使ったり、スタックが破壊されているかどうかの確認に重要だ)。プログラムには大量のシンボルがある。どうやって遅いO(n)にならずにシンボルを検索すればいいのか? どうやってコードを僅かに変更した時にインクリメンタルリンクを実現してデバッグ情報の再生成を回避すればいいのだ? 文字列の重複を省いて空間を節約するにはどうすれば? ここでPDBが登場する。

PDB(Program Database)は、その名前通り、データベースだ。これにはCodeViewが含まれているが、CodeViewレコードを様々な方法でインデックス化するための様々なものが含まれている。これによって型やシンボルを名前やアドレスで検索するのを高速化している。発想としては入力ファイルに対する「テーブル」と同等であり、ユーザーはその存在を気にすることはないものの、Windowsにおけるデバッグを快適にするのに貢献している。しかし問題がある。CodeViewはある程度はドキュメント化されているのに対し、PDBはまったくドキュメント化されていない。しかもその構造は複雑だ。

お手上げだ(ホントか?)

数年前、LLVMは開発の方向性として、CodeViewとPDBを出力する望みを一切捨て去り、以下の2つに注力することにした。

  1. clang-clはWindowsでDWARFデバッグ情報を出力する
  2. LLDBをWindowsに移植してWindows ABIに対応させる。これはVisual StudioやWinDbgをDWARFに対応させるより遥かに簡単だ(そもそもそんなことが可能であればの話だが。Visual StudioとWinDbgの拡張機能を使って実装可能なのだろうか)

実際、私はすでにブログ記事でこのことを2年前に書いている。作業の結果、LLDBをWindowsに移植して簡単なデバッグをさせることはできるようになった。

残念ながら、PDBのサポートは必須であることが明らかになった。LLVMの目標はWindowsエコシステムに囚われた開発者にとってできるだけ抵抗が少なくなるようにすることである。Windows Performance AnalyzerやvTuneのようなツールはとても強力でエンジニアの慣れ親しんだものである。企業はPDBファイルを保存、収集してクラッシュダンプを解析するインフラに投資している。PDBによるデバッグはとても高速だ。というのも、インデックスはファイルフォーマットで実現されていて、デバッガーが起動時にシンボルをインデックス化しなくてもいいためだ。それに、WinDbgのようなツールはすでにデバッグ用途に便利で、率直に言って、多くの(おそらくはほとんどの)Windows開発者がVisual Studioを手放すためには、彼らの死体の手からもぎ取る必要があるだろう。

私がとりあえずMicrosoftに協力を求めてみればええんちゃうと提案したときには皆から冷たいまなざしを受けたものだ。しかし、最終的に我々はMicrosoftに協力を求めた、そしてMSは協力した。協力はMicrosoft Githubにコード片を投下するという形で得られた。後はこのコードを解析するだけだ。Microsoftが公開できたコード片はPDBのコードの一部(我々は推測と解析をしなければならないし、そもそもコードは半分ぐらいかけているのでコンパイルすら通らないわけだが)だけであるが、実装をするだけの情報は得られた。

このコードを1年半解析し、試し、更に解析し、更に試しなどした結果、lld(LLVMリンカー)はついに機能するPDBを出力することができるようになった。基本的なことであるコード行や名前でのブレイクポイントの設定や、変数の表示や、シンボルや型の検索は、すべて動くようになった(ただし、もちろん、バグ互換はない)

PDBの詳細を調べたい人のために我々はツールも開発中だ。llvm-pdbutilと呼ばれるツールで、Microsoftのcvdumpユーティリティとよく似たものだ。このツールはPDBの内部情報をダンプして、PDBとyamlの相互変換、2つのPDBのdiffなどを実現している。llvmpdbutilの簡単なドキュメントはここにある。PDBファイルフォーマットの詳細の解説はここにある。この2年間に我々が解析したすべてが書いてある(まだ途中だ。私はドキュメントとPDBの実装の両方に時間を割かなければならないのだ)

バグをもってこい!

さて、ここで読者の協力が必要となる。我々はPDBで簡単なデバッグ状況をテストしたが、まだデバッグ情報の品質としてはアルファ段階だと考えている。是非試して、問題をバグトラッカーで知らせてほしい。始めるためには、まず最新のWindows用Clangのスナップショットをダウンロードしよう。この機能をテストする簡単な2つの方法は以下だ。

  1. clang-clにlldを自動的に実行させる

    clang-cl -fuse-ld=lld -Z7 -MTd hello.cpp

  2. clang-clとlldを別々に実行する。

    clang-cl -c -Z7 -MTd -o hello.obj hello.cpp

    lld-link -debug hello.obj

バグレポートがあふれかえるのをお待ちしております!

Microsoftが我々に協力してgithubレポジトリにコードをアップロードしてくれたことに心から感謝している。Microsoftの協力なしには実現できなかったことだ。

ところで、将来期待される歓喜すべきある事柄について読者の想像に委ねたい。ここに書いたPDBサポートはWindows特有のAPIやdllやライブラリに一切依存していない。100%移植性がある。ところで、君はクロスコンパイルに興味があるかね?

Zach Turner(LLVMのWindowsチームとして)

なかなか興味深い。

ところで、Microsoftが公開したMicrosoft/microsoft-pdbはMSVCのPDBを処理しているコードの一部だ。PDBフォーマットの詳細を開示するよう要請したところ、コード片が開示され、しかも、Microsoft自ら、「ソースコードは究極のドキュメントである」"Source code is the ultimate documentation :-)"、書いているところを見ると、果たしてMicrosoft社内にPDBの詳細なドキュメントはあるのか疑問だ。

2016-03-30

WindowsにLinuxサブシステムを載せてUbuntuのユーザースペースが動く未来が来る?

​Microsoft and Canonical partner to bring Ubuntu to Windows 10 | ZDNet

詳しいことは明日発表されるということで、未だに公式の確証はないのだが、MicrosoftとCanonicalが提携してUbuntuをWindowsに持ってくるという話が上がっている。

現在予測されている内容では、これは単にWindows上で動く仮想環境でUbuntu GNU/Linuxを動かすという話ではなく、WindowsにLinux互換サブシステムを実装して、その上でUbuntuのGNU/Linux上で動くユーザースペースをそのまま持ち込むのではないかという憶測が出ている。つまり、aptでパッケージを管理してbashが使えてその他諸々のUbuntuのユーザースペースのCLIツールが全部使えるということだ。

これは、技術的に可能であろうが、Windowsに十分に互換性のあるLinux互換サブシステムを実装するのは不毛なほど金と労力がかかるだろう。

この憶測が正しいとして、どこまで実装するのかも気になる。CLIツールが動く程度だろうか。X.org互換か、あるいはX.orgが動くまで実装するのだろうか。

Linux互換サブシステムを実装する上で一番だるいのはなんだろうかと考えたところ、システムコールではioctlが一番だるいのではないかと思った。/procと/devもサポートしないと大抵のGNU/Linuxユーザースペースのプログラムは動かないので、やはりダルいだろう。

そしてもうひとつ気になるのが、ターミナルエミューレーターだ。まさかcmd.exeではあるまいな。せっかくのGNU/LinuxユーザースペースCLIプログラムが全部動いたとしても、端末がcmd.exeならば全てが台無しだ。

ということを考えると、やはりGNU/Linuxユーザースペースのターミナルエミューレーターも使いたくなり、ということはやはりX.orgが必要になる。

もうひとつの可能性として、Linuxカーネルを使うという手がある。ただ、これはGPLを考えるとありえないように思う。

何にせよ、そこまでしてGNU/Linuxのユーザースペースのツールを使うぐらいなら、最初からGNU/Linuxをネイティブにインストールしたほうがよい。

2016-01-09

Linus Torvalds、Microsoftが「ジャンプしてみろよ」と言えばIntelとAMDはジャンプする

LKML: Linus Torvalds: Re: [PATCH 0/3] TLB flush multiple pages per IPI v5

IntelのCPUのTLBの挙動に、頻出するパターンにおける最適化らしきものが施されていることが観測できることに対して議論した後で、

前にも言ったように、Transmetaで働いていた時期、俺はNT以前のWindowsがどういう世界だったかということを垣間見た。GDI protection traversalはGDIがカーネル側に入るたびにTLBを全部フラッシュするらしく、また当時の一部のグラフィックベンチマーク(これはまだハードウェア支援されたVGAグラフィックが一般的ではなかった時代のことだ)は、5千から1万命令以内にTLBを全部ふっとばす実装になっていた。そのため、IntelとAMDはTLB fillを高速にするために多大な労力を割くだけの理由があった。なぜならば、GDIベンチマークは当時重要だったからだ。当時のグラフィックベンチマークというのは、基本的な2Dウインドウ処理やフォント描画のベンチマークのことだ。

RISCベンダーは全く気にしなかった。奴らと来たら完全にクソなハードウェアで、ソフトウェアパートナー(大方はデータベース)に、ソフトウェアを変更して、large-pageを使うようにしたり、TLBミスを回避すべく努力させた。奴らのコンパイラーはロードを早期に行い、ストアを遅延させた。というのも、メモリサブシステムは完全にオモチャだったからだ。TLBミスはパイプライン全体をぶっ壊すなどしていた。本当にクソなハードウェアで、まだ期待している奴もいる。残念なことだ。

Windowsの業界では、そんなことは望みようがなかった。Microsoftが、「おう、ジャンプしてみろよ」と言ったならば、IntelとAMDはどちらも「どれだけ高く飛べばいいのでしょうか?」と言ったものだ。結果として、x86はどのRISCよりも柔軟だった。なぜならば、IntelとAMDはどんなクソなソフトウェアでも実行しなければならなかったからだ。ソフトウェア開発者に最適化をさせる代わりに。

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-11-01

MicrosoftがPDBフォーマットの構造体定義のソースコードを公開

Microsoft/microsoft-pdb

MicrosoftがPDBファイルを扱うのに使っているMSVCのソースコードの一部をMITライセンスで公開している。ソースコード、cvinfo.hの中身の大半は、構造体定義のようだ。

このレポジトリは、MicrosoftにおけるPDB(プログラムデータベース) シンボルファイルフォーマットの情報を載せている。

PDBフォーマットはこれまで公式のドキュメント化されておらず、他のコンパイラーやツールセット(例えばClang/LLVM)がWindows上で動作したり、Visual Studioデバッガーとのやり取りを困難にしていた。MicrosoftはオープンソースコンパイラーがWindowsプラットフォームで動作するのを支援する。

このレポジトリの中身の大半は、VC++コンパイラーツールセットにおける実際のソースコードである。ソースコードは究極のドキュメントである(^_^;)。役に立てば幸いだ。もし、他の情報が必要であれば、Issuesを投げて知らせてもらいたい。

シンボルファイルフォーマットが今まで公式に非公開だったことに驚いている。

2015-04-19

The Old New Thing: なぜ環境変数にはTMPとTEMPがあるのか。どちらが正しいのか

Why are there both TMP and TEMP environment variables, and which one is right? - The Old New Thing - Site Home - MSDN Blogs

久しぶりにRaymond Chenの記事を翻訳する。

環境変数を覗いてみると、一時ファイルを置いておく場所を指定する目的の変数が2つあることに気がつくだろう。TMPとTEMPだ。なぜ2つもあるのか。もし値が異なる場合、どちらが正しいのか。

話は1973年にさかのぼる。この当時の一般的なマイクロコンピューター用のオペレーティングシステムはCP/Mだった。CP/Mオペレーティングシステムには環境変数がなかった。環境変数の話を始めるにしては不思議な時代だが、実は大事な話なのだ。環境変数が存在しなかったのだから、TMPやTEMPなどあるはずがない。当時、プログラムに一時ファイルを置いておく場所を指定したければ、プログラムごとに独自の方法で行う必要があった。たとえば、実行ファイルの特定の1バイトをパッチして一時オブジェクトを置いておくドライブレターを変更するなど。

(筆者の記憶する限り、当時のほとんどのCP/Mプログラムはパッチによって設定していた。少なくとも、筆者はそのように設定していた。筆者はWordStarマニュアルに、どのバイトをパッチすればどのように動作するのかという記述があったことを覚えている。また、プリンター用に独自の動作を設定したい場合などに、利用者が独自のサブルーチンを書くことができる数十バイトのパッチスペースが設けられていた。筆者は、「プリンターは次の文字を受け取る用意があるか?」関数を書くのに使った。これでバックグラウンドでの印刷がスムーズになった。)

1981年に話は飛ぶ。8086プロセッサーとMS-DOSオペレーティングシステムがやってきた。8086プロセッサーの設計と、MS-DOSおペーレーティングシステムの設計は、CP/Mの影響を受けている。実際、8080プロセッサー用に書かれたCP/Mプログラムは、機械的に8086プロセッサー用のMS-DOSプログラムに変換できることが設計目標だった。もちろん、変換器は自己改変コードとか、命令の途中にジャンプするとか、コードをデータとして扱うなどの変なトリックを使わないことを前提にしているが、健全なコードならば、変換器はプログラムを変換できたのだ。

(8080プロセッサー用に書かれたコードを8086プロセッサー向けに機械変換できるという目標は、8086命令セットに存在するいくつかの不思議な制約を説明できる。たとえば、8080のHとLレジスターは、8086のBHとBLレジスターにマップされている。8080では、計算されたアドレスにアクセスできるレジスターはHLだけであった。これにより、なぜ8086の4つの基本的なレジスター、AX, BX, CX, DXのうち、メモリにアクセスできるのはBXだけなのかが説明できる)

MS-DOSがCP/Mとの互換性以上に追加した機能として、環境変数がある。既存のCP/Mのプログラムは環境変数を使っていなかったので、初期のMS-DOSプログラムも使うことはなかった。なぜならば、最初のMS-DOS用のプログラムというのは、すべてCP/Mから移植されていたからだ。もちろん、TEMPやTMPといった環境変数を設定することはできるが、誰も注意を払うものなどいない。

やがて、プログラムは最初からMS-DOS用に書かれるようになり、環境変数は設定データを格納するために使えることに皆気がついた。混沌とした市場から、2つの環境変数が、一時オブジェクトを格納する場所を記述するのに使われるようになった。TEMPとTMPである。

MS-DOS 2.0はあるプログラムの出力をパイプして、別のプログラムの入力とする機能を追加した。MS-DOSはシングルタスクのオペレーティングシステムのため、この機能は、最初のプログラムの出力を一時ファイルにリダイレクトして、実行が終了するまで走らせ、しかる後に第二のプログラムを実行しつつ、入力は一時ファイルからリダイレクトするという方法でシミュレートされた。さて唐突に、MS-DOSは一時オブジェクトを作成する場所を必要とするようになった。何らかの理由で、MS-DOSの作者はTEMP環境変数を一時ファイルが作成される場所を指定する方法として使うことにした。

ところで、COMMAND.comがTEMPを使うことにしたという決定は、他のプログラムがTEMPとTMPのどちらを使うかという選択には影響しない。作者の気分次第で選ばれるものだ。多くのプログラムは両方を確認するという折衷的な戦略を取った。そして、どちらの環境変数を先に確認するかというのも、やはり作者の気分次第で選ばれた。たとえば、昔のDISKCOPYとEDITプログラムは、TMPの前にTEMPを先に確認した。

Windowsも似たような道をたどったが、何らかの理由で、GetTempFileName関数の作者は、TEMPより先にTMPを見にいった。

結果として、特定のプログラムの一時ファイルに使われるディレクトリは、プログラムの種類次第となった。WindowsプログラムはGetTempFileName関数を使って一時ファイルを作成することが多く、その場合、TMPを優先する結果となった。

環境変数の設定ダイアログをみると、まだ2つの変数、TMPとTEMPが生き残っていて、読者の興味をひきつけている。ギーク版のアディダスVSプーマのようなものだ。

2014-10-06

The Old New Thing: 実行ファイルのベースアドレスが0x00400000である理由

古参MS社員のRaymond Chenが、なぜWindowsのデフォルトのベースアドレスは0x00400000に配置されているのか。最初の4MBは何だという質問に答えている。

Why is 0x00400000 the default base address for an executable? - The Old New Thing - Site Home - MSDN Blogs

DLLのデフォルトのベースアドレスは0x10000000だが、なぜEXEのデフォルトのベースアドレスは0x00400000なのか。なぜこの値なのか。4メガバイトに何の意味があるのか。

これには、x86のディレクトリエントリひとつでマップできるアドレス空間であることと、1987年に決められた設計のためだ。

EXEのベースアドレスの制約としては、64KBの倍数であることのみだ。しかし、ベースアドレスを選ぶ理由には他にもある。

ベースアドレスを選ぶ目的は、モジュールが再配置される可能性を最小限に抑えることである。これはつまり、すでにそのアドレス空間にあるものと衝突させない(衝突にすると再配置しなければならない)ことと、後からそのアドレス空間に配置されるものと衝突しないということだ(後から来たものが再配置される)。実行可能ファイルにとって、後から配置されるものとの衝突を回避することとは、DLLによって占められる領域を回避することだ。OSはDLLを高アドレスに配置するし、OSのものではないDLLのデフォルトのベースアドレスは0x10000000なので、実行可能ファイルのベースアドレスは、0x10000000よりも低い場所であるべきで、低いほど、DLLと衝突するまでの領域が多くあることになる。しかし、どこまで低くできるのだろうか。

まず、すでにそこに配置されているものとの衝突を避けなけれあbならない。Windows NTは低アドレスにそれほどものを置かない。すでに配置してあるものは、nullポインターアクセスを補足するための、ゼロにマップしてあるPAGE_NOACCESSページだ。故に、Windows NTでは、実行可能ファイルのベースアドレスを0x00010000にすることができ、当時の多くのアプリケーションは実際にそうしていた。

しかし、Windows 95では、すでに多くのものが配置されていた。Windows 95のバーチャルマシンマネージャーは、CPUの不具合に対処するため、常に最初の64KBの物理メモリと最初の64KBのバーチャルメモリに配置されていた(Windows 95は、大量のCPUバグや、ファームウェアのバグに対処しなければならなかったのだ)。さらに、最初の1メガバイトのバーチャルアドレス空間は、バーチャルマシンの論理アドレス空間にまっぷされていた。(実際は、1メガバイトよりすこし多かった)。このマッピングは、x86プロセッサーのvirtual-8086モードに必要なものであった。

Windows 95は、その前身のWindows 3.1のように、Windowsを特別なバーチャルマシン上で動かしていて(System VMとして知られている)、互換性のために、あらゆるものを16bitコード上から通すようにしていた。偽物が正しく鳴くようにするためだ。そのため、CPUは(MS-DOSベースのアプリケーションではなく)Windowsアプリケーションを実行しているものの、バーチャルマシンのマッピングをいじしつづけているため、MS-DOS互換レイヤーのために毎回ページリマッピング(と、それに伴い高くつくTLBフラッシュ)を行う必要はなかった。

さて、最初の1メガバイトのアドレス空間はまな板の上から取り除かれているとして、他の3メガバイトはどうしたんだ。

さて、ここで記事の最初のヒントに戻る。

コンテキストスイッチを速くするため、Windows 3.1のバーチャルマシンマネージャーは、VMごとのコンテキストを4MBに切り上げていた。これにより、メモリーのコンテキストスイッチは、ページディレクトリのたったひとつの32bit値を書き変えるだけですんだ。(細かく言うと、インスタンスデータページもあるが、それは数十個程度のビットを反転させるだけだ)。この切り上げにより、3メガバイトのアドレス空間を失った。しかし、4ギガバイトものアドレス空間があるからには、十分の一パーセント以下の損失は、圧倒的なパフォーマンスの向上に比べれば、十分支払うべき価値のあるものだ(特に、この当時のアプリケーションで限界に挑むものなどなかった。当時のコンピューターは全体で2MBしかRAMがなかったのだから)

このメモリーマップは32-bit Windowsアプリケーションのための分離されたアドレス空間のための一部の変更がありながらも、Windows 95に引き継がれた。故に、実行可能ファイルがロードできる最も低いアドレスは4MBで、つまり0x00400000だ。

ギークトリビア:Win32アプリケーションがMS-DOS互換領域にアクセスするのを防ぐため、フラットデータセレクターは、実際には4MB境界で止まる下方に拡張するセレクターだ(同様に、16bit Windowsアプリケーションにおけるnullポインターは、nullセレクターが無効なため、アクセス違反を引き起こす。割り込みベクターテーブルにはアクセスしない)

リンカーは実行可能ファイルのデフォルトのベースアドレスとして0x0400000を選択するので、結果のバイナリはWindows NTとWindows 95の両方で再配置なしでロードできる。Windows 95対応はもはや誰も気にしないので、原理的には、リンカーの開発部署は、今ならば別のデフォルトベースアドレスを選べるはずである。しかし、グラフをちょっと綺麗にする以外の実質的な利点はない。それに、もし変えたとしたら、「なんである実行可能ファイルのベースアドレスには0x0400000のと、0x00010000のがあるんだ?」という質問が来るだけである。

またもや歴史の話。それにしても、今回のRaymond Chenの記事には、やたらとリンクがある。リンクされているページは、だいぶ昔のものが多いが、面白い。

2014-09-11

Old New Thing: ブルースクリーンを書いた人間は、まあ、僕さ

Old New Thingの著者で、古参MS社員のRaymond Chenが、Windows 95のブルースクリーンを書いた時のことをブログに書いている。

I wrote the original blue screen of death, sort of - The Old New Thing - Site Home - MSDN Blogs

この前、Windows 95の話を持ちだした。ここで書いたように、Ctrl + Alt + Delダイアログは、Windows 3.1で導入され、Windows 95では、すでになくなった。Windows 95では、Ctrl + Alt + Delを押すと、以下のようなダイアログボックスが表示される。

Close Program × 
Explorer
Contoso Deluxe Composer [not responding]
Fabrikam Chart 2.0
LitWare Chess Challenger
Systray
WARNING: Pressing CTRL+ALT+DEL again will restart your computer. You will lose unsaved information in all programs that are running.
End Task
Shut Down
Cancel

(Systrayについては以前学んだよね)

Windows 3.1では、深刻なエラーではクラッシュしてブラックスクリーンになったが、Windows 95では、青で表示するようにした。僕はそのコードを書いたうちの一人だ。少なくとも、最後に書き換えた人間だな。

私はブルースクリーンメッセージを表示するコードを担当していた。カーネルモードビデオドライバーにテキストモードに切り替えるよう指示し、画面を青背景にして、白文字を表示して、ユーザーがキー入力をするのを待ち、画面を元に戻し、ユーザーの入力を、メッセージを表示するよう要請したコンポーネントに伝える[1]。

デバイスドライバーがクラッシュした場合、Windows 95はカーネルモードコンポーネント内の深刻な問題に、最善を尽くして対処しようとする。死の青画面(blue screen of death)というべきなのか、不便な青画面(blue screen of lameness)というべきなのか。ブルースクリーンを見たことがない幸運なもののために、以下がその例だ。



 Windows 


An exception 0D has occurred at 0028:80014812. This was called from 0028:80014C34. It may be possible to continue normally.

* Press any key to attempt to continue.
* Press CTRL+ALT+DEL to restart your computer. You will
  lose any unsaved information in all applications.

「通常通り動作を実行できるかもしれませんよ」という楽観的な文面に注目されたい。みんな忘れているが、Windows 95は、ブルースクリーンエラーを表示した後、なるべくそのエラーを無視して実行し続けようとすることだ。つまり、まあ、スキャナーのドライバーはクラッシュしたので、スキャナーは動かなくなったかもしれないけれど、それ以外のシステムは無事だというわけだ。

(これを今日おこなったらどうなるだろうか。「カーネルパニックを無視するには何かのキーを押してください」)

技術的に何が行われているかというと、バーチャルマシンマネージャーが現在行われているイベントを破棄して、イベントディスパッチャーに戻るのだ。これは、カーネル内における、例外をウインドウプロシージャで飲み込んで、メッセージループに戻るようなものだ。継続できるイベントがなければ、現在のアプリケーションが終了される。

時に、問題はシステム全体に及ぶこともあり、現在のイベントを破棄したり、アプリケーションを終了しても、問題を解決しないことがある。次に起こるのは、次のイベントやアプリケーションが、同じ問題に引っかかり、数ミリ秒後に、またブルースクリーンメッセージを目にするということだ。何回か似たようなメッセージを目にした後、おそらくは諦めて、Ctrl + Alt + Delを押すだろう。

さて、当初のメッセージは、上のようであった。しかし、メッセージには問題上がる。デバイスドライバーがカーネルに読み込まれるアドレスというのは予測可能ではないので、生のアドレスが表示されていても、大して役に立たない。もし誰かが、「うちんとこの重役がこんなクラッシュメッセージ出したんだけど、何があったか分かるかい?」と言われたとしても、得られるのは無意味な数値だけだ。

その誰かというのは、僕でもあったわけだ。

この問題に対処しやすくするために、僕はメッセージを書き開けて、ドライバーの名前と、セクション番号と、セクションからのオフセットを追加した。



 Windows 


An exception 0D has occurred at 0028:80014812 in VxD CONTOSO(03) + 00000152. This was called from 0028:80014C34 in VxD CONTOSO(03) + 00000574. It may be possible to continue normally.

* Press any key to attempt to continue.
* Press CTRL+ALT+DEL to restart your computer. You will
  lose any unsaved information in all applications.

これで、クラッシュしたドライバーの名前が分かる。何もわからなかったとしても、問題がどこにあるかのヒントは得られるわけだ。ドライバーのMAPファイルにアクセスした誰かさんは、アドレスを探して、クラッシュした箇所を特定できる。最高ではないにせよ、何もないよりマシだ。僕がこの変更をするまでは、何もなかったのだ。

そういうわけで、僕は不便なブルースクリーンを、僕の仕事を多少マシにするために書き換えたと言える。

余話:その後、誰か(僕だったかどうかは覚えていないので、誰か同僚としておこう)が、クラッシュアドレスを調べるためのコードを追加した。もし、箇所がカーネルのヒープマネージャー出会った場合、メッセージは多少変更される。



 Windows 


A 32-bit device driver has corrupted critical system memory, resulting in an exception 0D at 0028:80001812 in VxD VMM(01) + 00001812. This was called from 0028:80014C34 in VxD CONTOSO(03) + 00000575.

* Press any key to attempt to continue.
* Press CTRL+ALT+DEL to restart your computer. You will
  lose any unsaved information in all applications.

この場合、「通常通り動作を実行できるかもしれませんよ」という文面が消える。なぜなら、この場合、そういうことはまずないからだ。

余話:よくやったな。Slashdot君。訂正をしたつもりかもしれないが、その訂正も間違っているよ。まあ、間違いには気がついたみたいだけどね。

[1]: このコードはカーネルで動くので、キーボードレイアウト情報にはアクセスできない。読者がChinese-Bopomofoキーボードレイアウトを使っているかどうかなんてわかりっこない。その場合、"OK"とタイプするには、C, L, 3だ。特に訳には立たないけどね。カーネルにはIMEなんてないんだから。そういうわけで、入力は、EnterとかESCみたいな、言語からは独立したキーに割り当てられていた。

だいぶ久しぶりにOld New Thingを訳した気がする。以前はだいぶ苦労していた翻訳も、今はそれほど苦労しないのは、英語力が上がったからであろうか。それとも単に慣れたのか。

2014-08-05

何でWindowsは俺のワイヤレスキーボードをトースターだと認識するんだ?

device manager - Why does Windows think that my wireless keyboard is a toaster? - Super User

俺は彼女の父親から古いPCを相続したんだが、プリンターを設定していると、ちょっとおどろきのものが現れた。

画像

さて、二つの疑問がある。

  1. なんでWindowsは俺のワイヤレスキーボードをトースターだと認識するんだ?
  2. なんでWindowsはデバイスメニューにトースターのアイコンを持っているんだ?

なぜか、質問者のワイヤレスキーボードに、トースターのアイコンが表示されている。Windowsはなぜそんなアイコンを用意しているのだろうか。

答えは。

理由1

Microsoftはトースター用ドライバーというサンプルを作っている。このサンプルでは、<DeviceIconFile>Toaster.ico</DeviceIconFile>という行があり、おそらく、お前のキーボードの製造業者は、このサンプルをそのまま使ったのだろう。

理由2

キーボードの裏に、食パンを差し込む口は見当たらないか?

どうやら、Microsoftがサンプル用に作ったトースター用のドライバーのデバイス情報のXMLファイルをそのまま使ったずぼらなキーボードメーカーがいるらしい。サンプルのアイコンがトースターなのは、Microsoftのユーモア精神なのか、そのままコピペして使うずぼらなハードウェア製造業者を防ぐためなのか。それでもコピペはされる。

それにしても、USBトースターはあまり実用になりそうにない。一般的なトースターの消費電力は1000Wはある。一方、USBは3.0でもせいぜい100W程度の電力しか供給できない。USB3.0ポートを制御用に1ポート、電力供給用に9ポート必要とするトースターだろうか。電源ユニットも相当によい物を使う必要がありそうだ。

2014-04-01

MSVC++コンパイラーの中に入っているWebブラウザー

You Got Your Web Browser in my Compiler! | Random ASCII

筆者は最近、MicrosoftのVC++コンパイラーがmshtml.dll、すなわちInternet Explorerを読み込むことを発見した。/analyzeオプション(静的コード解析を指示)を使うとコンパイラーがこの挙動を示す。筆者はコンパイラーアーキテクチャの専門家ではないが、インターネットエクスプローラーを読み込むコンパイラーというのはいかにも奇妙だ。

これは単なる気持ちの問題ではないのだ。筆者の環境で静的解析を並列コンパイルすると、コンピューターが数分間も反応が悪くなる。どうやらmshtmlウインドウが、その原因らしいのだ。

いつも通り、筆者はxperf/ETWを使って原因を探った。

本当に問題なのか?

筆者は問題を探すために何日もの時間を浪費したりはしない。問題が私を見つけたのだ。この場合、筆者は新しいコードベースに/analyzeを使おうとしていた。

(/analyzeの利用方法について学びたい場合は、Two Years (and Thousands of Bugs) of Static Analysisを参照されたし)

筆者は並列コンパイルと、並列プロジェクトビルドを有効にしている。どちらの並列ビルドオプションも、デフォルトでプロセッサーの数だけの並列数設定になっているため、プロセッサーの数 * プロセッサーの数だけの数のコンパイラーが実行されることになり、私の環境では、144個の並列コンパイルが走ることになる。筆者は高速なSSDと32GBのRAMを有しているため、多少は遅くなるものの、メルトダウンを引き起こすほどではないと考えている。実際、通常のコンパイルでは、ちょっと遅いかな程度のものだ。ところが、/analyzeでコンパイルすると、筆者のコンピューターは30分ほど反応がなくなるのだ! マウスクリックが処理されるのに、10秒もかかり、タスクマネージャーがウインドウをアップデートするのもまれにしか行われなくなる。これは筆者が並列/analyzeビルドを行うたびに発生する。筆者はこの問題をVS2010で発見していたが、VS2013を使うまで、この問題を真剣に調査することはなかった。

ハング解析

筆者のコンピューターでは、ETWトレーシングを24時間週7日走らせている。データーは300MBのリングバッファに記録されていて、Win+Aを押した時に、いつでもトレースが記録されるようにしてある。これは、ゲーム中にトレースの記録を取りやすいようにするためだが、デスクトップがろっくされたが、トレースだけは取りたい場合にも、都合がいい。Microsoftのwpruiにも、Ctrl+Win+Cというグローバルショートカットで、似たようなものがある。トレースを記録した後、筆者はなんとかビルドをキャンセルさせて、デスクトップのコントロールを取り戻した。そして、トレースを解析して、何が起こっているのかを確かめた。

(wpruiでトレースを記録する方法については、Xperf Basics: Recording a Trace (the easy way)を参照されたし)

「なぜ俺のコンピューターは何重もの並列コンパイル中に反応が悪くなるのか」という質問は、答えにくい種類の質問だ。あまりにも範囲が広すぎる。数十個のコンパイラーがCPU時間を奪い合っているというのは、どのスレッドもCPU時間に飢えている状態になるわけだ。検証には、もっと狭い質問が必要だ。

WPAには、UIハングの検証に役立つグラフがある。Windowsは各アプリケーションがメッセージを確認していない時間を把握していて、もし、アプリケーションがメッセージを確認していない時間が「長すぎる」場合、ETWイベントが発行される。適切なところ(Microsoft-Windows-Win32k)からデータを記録していれば、これらのイベントがトレースに入り、UIディレイグラフがSystem Activity areaに表示される。通常、このグラフは空であるが、筆者の反応しないシステムのトレースは、棒グラフの海となり、何十ものMsgCheckディレイイベントが、システムのあらゆるプロセスから発せされていた。これがその一部だ。

[訳注:画像]

興味深いことに、3/4ほどのハングしたプログラムは、cl.exeのインスタンスだ。Windowsはコンパイラーがメッセージポンプを実行していないと報告している。この事実は、ただちに、「なぜコンパイラーにメッセージポンプがあるのか!」という疑問を生じさせる。

この疑問は、ひとまず無視しよう。その背景事情を考えるのだ。

次のステップは、ハングしたアプリケーションをひとつ選び、なぜメッセージを処理できていないのかという原因を探ることだ。筆者はdwm.exe(Desktop Window Manager)を選択した。なぜならば、これは高優先度で実行されているからだ。このプロセスはシステムがオーバーロードした時もスムーズに動くべきであるので、何か悪いことが起こっているのは明らかである。

UIディレイイベントには、メッセージポンプに失敗しているスレッドのプロセスIDとスレッドID、メッセージが処理されていない時間といった情報が含まれている。これにより、ようやくまともな疑問を構築することができる。

なぜ、プロセスdwm.exe(4336)のスレッド19,308は、トレース開始から84847.551566180秒時点で、2.668秒もメッセージを処理できていないのか?

このしっかりとした疑問があれば、この疑問に答えることができる。

このまともな疑問の存在は重要である。なぜならば、適当にwait analysisをしても、多くのスレッドが何秒も実行されずに待っていることが判明するだけだからだ。wait analysisをするときは、アイドルスレッドは問題ではないという認識を持つことが重要である。コンピューター上のほとんどのスレッドは、たいていの時間はアイドルであるべきなのだ。問題なのは、反応していないスレッドだ。メッセージポンプを行うべきなのに行っていないスレッドだ。だからこそ、UIディレイイベントはとても重要なのだ。反応していないスレッドを抽出してくれる。

(wait chainをたどる方法については、Xperf Wait Analysis–Finding Idle Timeを参照されたし)

反応の遅いdwmスレッドは、ExAcquireResourceExclusiveLiteを、何度も待っていた。時として、何百ミリ秒も待っている。ここで解説されている、ReadyingProcessなどのカラムにより、誰がdwmスレッドを準備可能な状態にするのがが判明する。誰がロックをリリースして、dwmスレッドを実行させるのかが判明する。筆者はこのチェインを手動でしばらく追っていたが、数百回ものコンテキストスイッチの後には、いい加減に飽きた。

そこで、筆者はwpaexporterを使って、関心のある範囲のコンテキストスイッチをすべてエクスポートした。そして、結果をフィルターしてNewThreadStackの中のExAcquireResourceExclusiveLiteのある行だけ得て、Excelでコンテキストスイッチの時間差を計算した。これはlock chainのアタリをつけるためと、誰がロックを長い間握っているか調べるための、手っ取り早くて小汚い方法である。これらのロック所有は、通常はとてもすばやいものである。大方は、1ミリ秒以下である。しかし、大多数が、長い間ロックを握っていた。大方は、16ミリ秒か32ミリ秒だ。観測によれば、15ミリ秒以上もロック所有しているスレッドは全体の3%だというのに、この遅いロック所有は、全体の75%の時間を使っている。

コンテキストスイッチのデータをより詳細にみると、どうやら、遅いロック所有の原因は、スレッドがロックを所有しているのに、CPU時間が割り当てられていないからであるように思われた。そのため、ロックは、OSの次のスケジューリング期間になってCPU時間を割り当てられるまで、未使用のままだ。もしくは、ひょっとしたら、スレッドがロックを所有した後に、タイムスライスを失っているのか。どちらとも言えない。とにかく、これは優先順位の逆転のように思われる。もしくは、単にビジーなシステムで毎秒数千回もロックしたら必ず起こる現象なのだろうか。

訳注:優先順位の高いスレッドがロックの解放を待っているが、そのロックを開放するのは、優先順位の低いスレッドであり、優先順位の低いスレッドにはCPU時間がなかなか割り当てられずに、ロックの解放に無駄に時間のかかる現象。

ExAcquireResourceExclusiveLiteの呼び出しはすべて、SetWindowsPos, CreateWindowExなどの関数から生じているようだ。明らかに、奪い合っているリソースは、ウインドウシステムの重要な部分であるようだ。

lock chainの筆頭に存在するスレッドは、VC++コンパイラーの一部である。CreateWindowExWから呼ばれている。CreateWindowExWとは、コンパイラーにとっては奇妙だ。それから、CreateWindowExWはmshtmlから呼ばれているが、これはさらに解せない。実際、65%のウインドウシステムのロックへのトラフィックは、VC++コンパイラー由来で、ほとんどがmshtml.dllからだ。

つまり、/analyzeを実行すると、コンパイラーは多数のCPU時間をコード解析に費やすと同時に、ウインドウも開く(実際には二つひらく。COM目的にもうひとつ)というわけだ。コンパイラーを多数走らせると、多数のウインドウが開かれて、GPUに負荷がかかる。狂気お墨付き。

コンパイラーのインスタンスは、Visual StudioとかOutlookといった通常のウインドウ付きプロセスより、すこし低い優先順位で実行される。そして、この低優先度と高CPU時間要求と、ウインドウシステムのロックの多用は、問題の根本的理由のようだ。優先順位の逆転を引き起こすのだ。

技術的に、筆者はmshtmlと、ウインドウを開くといったことが、ハングの原因であるかどうかはわからない。筆者が分かるのは、ハングは/analyzeでコンパイルした時のみ起こること、/analyzeコンパイルはmshtml.dllをロードすること、mshtml.dllがwait chainに現れることである。相関と因果はちがうが、だいぶ怪しい

xkcd: Correlation

男「いままで、相関関係があると、因果関係があると思っていたんだ」

男「統計学の授業を取ったら、そうは思わなくなった」

女「授業は役に立った様子ね」
男「うーん、たぶんね」

もし、Microsoftがmshtml.dllをコンパイラーから取り除いたのならば、筆者は喜んで再びテストをして再報告する所存である。

でも、なんで?

ソースコードにアクセスできない以上、筆者には何が起こっているのか明らかではない。だが、これだけは分かる。コンパイラーを/analyzeで実行すると、コンパイラーはmspft120.dllをロードする。/analyzeのDLLだ。そして、mspft120はmsxml6.dllをロードして、XML設定ファイルを読み込む。そして、msxml6はurlmon.dllをロードして、ストリームを開く、そして最後に、urlmonはmshtml.dllをロードする。そして、mshtml.dllはウインドウを作成する。なぜならば、mshtml.dllはそうなっているからだ。

読み込まれてるXMLファイルは、

  • res://C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\BIN\mspft120.dll/213
  • res://mspft120.dll/300

この一連の処理は、ある場合においては当然のことであることは確かだ。ただしこの場合においてではない。思うに、だれもmshtml.dllがロードされていることに気がついていないのではないか。もしくは、問題が発覚するほど大規模な並列コンパイルを誰も実行したことがないのか。

訳注:原文ではMSVCがmshtml.dllを読み込むことを検証する方法が書かれているが省略

対処法

Microsoftが、自社のコンパイラーがWebブラウザーを読み込む問題を修正するまでは、大規模な並列ビルドを行う際にこの問題は避けられないようだ。筆者の知る唯一の解決法は、並列数を下げることだ。最大の並列プロジェクトビルドを小さな値に設定することで、ハングの度合いを、まだマシなレベルに抑えられる。

思うに、CPUコアが少なければ、問題を抑えられるのではないだろうか。自動的に過剰な設定が減るわけだから。しかし、筆者は多大な並列性がほしいのだ。やれやれ。

Microsoftにグローバルなコンパイラースケジューラーがあれば、このCPU多使用は回避できたであろうに。

追記

筆者に対して、なぜに段階の並列プロジェクトビルドをやめることで問題解決にはならないのかという質問が寄せられた。その理由は、もちろん、コンパイラーがWebブラウザーを開くなどということは、依然として馬鹿げているからだ。もうひとつの理由は、12並列並行プロジェクトビルドは、12スレッドCPUのデフォルトだからだ。デフォルトの挙動はもっとマシであるべきだ。

対処法が理想ではない最後の理由は、理想のビルド速度を得るためには、2並列並行プロジェクトビルド以上が必要な場合があるからだ。筆者が先週、並列コンパイルのブログ記事で示したように、並列コンパイルはデフォルトで無効になっている。たとえ有効にしても、多くのプロジェクトは、かなりの時間を直列ビルドに使っている。そのため、並列コンパイルと並列プロジェクトビルドを組み合せることが適切なのだ。

特に、我々はほとんどのプロジェクトでプリコンパイルドヘッダーを使っている。VC++はプロジェクトのビルドに際して、プリコンパイルドヘッダーをまずビルドする。プリコンパイルドヘッダーが用意できるまで何もコンパイルしない。そのため、プロジェクトビルドこそが、すべてのプロジェクトのフルビルドを開始時から並列できる方法なのだ。

VC++/msbuildが、144個ものコンパイルを並列実行するのを止めるために、グローバルスケジューラーを持っていないのは残念である。グローバルなコンパイラースケジューラーこそが、プロジェクト外部、内部で、過剰にならずに完全な並列化を達成するための方法であるのに。

それから、並列プロジェクトビルドはグローバル設定であるので、設定値を下げると、いくつかのソリューションが余計に遅くビルドされる。

ドワンゴ広告

この記事は自宅とドワンゴ勤務中の両方で書かれた。MSVCが本物のC++コンパイラーかどうかは議論の分かれるところである。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2013-10-28

Windows 1.01をブラウザー上で動作させる

jsmachines.net

最近、ブラウザー上で動作するエミュレータ―が盛んだ。このデモは、なんとWindows 1.01をブラウザー上で実装したエミュレータ―上で動かしているという。

もちろん、少し前に、ブラウザー上のエミュレータ―でGNU/Linux/Waylandを動かしているデモがあったわけだが。

本の虫: JavaScriptのよるOpenRISCエミュレーター上でGNU/Linux/XorWaylandを動かすデモ

歴史検証の点から、大昔のWindowsがどんな感じだったのかを手軽に体験できるのは素晴らしいことだ。

2013-10-11

ビル・ゲイツ曰く、「俺は飛行機の中でFAT書いたこともあるんだぞ、アホンダラ」

I wrote FAT on an airplane, for heaven's sake - The Old New Thing - Site Home - MSDN Blogs

16-bit Windows用にコードを書く時、パフォーマンスの最適化として割く時間には、どの関数をどのセグメントに置くかということがある。

16-bit Windowsにおけるコードは、コードセグメントの中から実行される、セグメントのサイズは64KBである。コードセグメントがひとつディスクからロードされるときは、そのセグメント全体がロードされるし、セグメントが破棄される時には、そのセグメント全体が破棄される。つまり、関数がどのセグメントに配置されているかということは、アプリケーションのパフォーマンスに多大な影響を及ぼすのだ。

例えば、同時に呼ばれる関数群を同じセグメントに配置しておくのは都合がいい。そうすれば、関数群は一個のものとして読み込まれ、I/O時間を節約できる。考えなしに関連性のない関数を同じセグメントに配置したならば、セグメント上の関数を呼び出すと、他の関数まですべてロードされてしまい、使わない関数までロードするので、I/Oを浪費する。

たとえ関数が同時に呼ばれるとしても、その頻度には目を配っておかなければならない。頻繁に呼ばれる関数と、頻繁に呼ばれない関数(ただし最初の関数の後に呼ばれる)がある場合、頻度の低い関数は、虎の威を借りてメモリにロードされたままになる。これにより、メモリ使用量が上がり、容量を開けるために、頻繁に呼ばれる関数が破棄されることもある。

セグメントを細かく大量に作ると、メモリ使用量を細かく管理できるが、その分オーバーヘッドも多いし、I/Oにも負担がかかる。なぜならば、セグメントをロードするという事は、ディスクへのラウンドトリップだからだ。そのため、メモリー使用量とI/Oのコストのバランスを取らなければならない。

関数群をセグメントに配置する最適化を、セグメントチューニングと呼ぶ。

Windows 3.0の開発中、定期的な会議でビル・ゲイツに進捗を報告することになっていた。ある会議の議題はパフォーマンスで、ビルは、「てめーらセグメントチューニングに時間かけすぎだろ。セグメントチューンなんて12歳のガキにだってできらぁ。もっと本物の最適化をやれ。こんな馬鹿馬鹿しいセグメントチューニングじゃなくてよ。おれは飛行機の中でFAT書いたこともあるんだぞ、アホンダラ」と文句をつけた。

(やれやれ、こういうことを書くことになるとは。これは議事録ではなく、やや脚色されている)

この、「俺は飛行機の中でFAT書いたこともあるんだぞ」というのは、ビルが本物のプログラミングをしていないと感じた連中に文句をつける時のセリフらしい。だが、今回ばかりは、開発マネージャーはうんざりしたので、こう言った。

「あらそうなの、ビルさん。では、Windowsのソースコードが入ったマシンをご用意いたしますので、あなたの魔法のプログラミングとやらを発揮して最適化を手伝ってくださるかしら?」

これにより、ビルを黙らせた。

久々にOld New Thing。

コメント欄では、「悲しいかな、もしビルがFATの開発にもっと時間をかけていれば、もう少しはマシなものになったものを」とか、「飛行機の中で書いたから何だっていうんだ。なんにも変わらんだろ」などと言われている。特に、この時代のプログラミングというのは、当初の記述は紙の上で行われることも多かったので、なおさら変わらない。

ところで、この開発マネージャーというのは原文では女性という事になっているのだが、日本語ではセリフの言い回し以外に自然な方法でその情報を表現できないのは難しい。

2013-04-24

マイクロソフトがClang開発者に求人中?

Clangの開発者メーリングリストに、Microsoftから依頼を受けたリクルーターが現れて求人広告を投下している。

The cfe-dev April 2013 Archive by date
[cfe-dev] Microsoft Job Opportunity

なんでも、MicrosoftがClang開発者を雇いたがっているらしい。要件はC++開発経験3年以上、Clang開発者、Clangの機能実装経験とClang AST経験。

雇用期間は6ヶ月で延長の可能性もありとなっている。

一体何をするつもりなのだろうか。

単にC++コンパイラー開発者が欲しいというわけではないだろう。そもそも、Microsoft WindowsにはろくなC++コンパイラーがない。おそらくClangを使って何かをやるに違いない。

ただし、雇用期間が6ヶ月というのも気になる。何か短期の実験的プロジェクトか、あるいはClangの社内教育などだろうか。

すでに、Visual StudioのIntelliSenseに使われているC++パーサーは、EDGのフロントエンドを使っていることがVisual C++ Teamブログで明らかにされている。

Rebuilding Intellisense - Visual C++ Team Blog - Site Home - MSDN Blogs

もはや、C++コンパイラーを自社開発することで技術的優位に立てる時代ではないのだ。とすれば、MSがClangを使うのはおかしくもない。というより、MSVCは悲惨すぎて、とてもではないがC++と呼べるシロモノではない。いっそのことClangにでも変えてしまったほうがいいのではないか。下位互換性はぶっ壊すが、まあ痛みなくして得るものなしだ。不自由ソフトウェアのつらいところよ。

まあ、主に不自由ソフトウェアと制限コンピューターを開発している会社のことはどうでもいいか。

2013-04-16

NTFSのAlternate Data Streamとrarアーカイブによるマルウェア隠し

Why questionable downloads use rar archives : Len Boyette

NTFSには、Alternate Data Streamという機能がある。これは、ファイルに複数のストリームを関連付ける機能で、主となるストリーム以外は、基本的にあまり表に出ない。Windowsはファイルのメタデータを保存するのにこの機能を使っている。例えば、インターネット上からダウンロードしたファイルに対して警告を発するのも、特別なAlternate Data Streamを付加しているからで、ファイルのプロパティからこれ以上警告しないという設定するのは、このストリームを削除するという事である。

Alternate Data Streamは、ファイル名の後ろにコロンをつけることで使える。例えば、"example.txt"という名前のファイルがある場合、"example.txt:secret"などというように。

Alternate Data Streamは、なかなか表に出ない。GUIのエクスプローラーでは直接は表示されない。コマンドdir /rで表示される位だ。しかも、Alternate Data Streamから実行されるプロセスは、タスクマネージャーにも現れない。これは、例えば表のストリームはファイルのAlternate Data Streamを実行するようなプログラムにしておけば、単に軽くファイルを検証しただけではマルウェアであることが分かりにくいということだ。

世の中の多くのアンチマルウェアソフトウェアは、Alternate Data Streamを調べない。

もちろん、普通のプログラムが扱うのは主のストリームであり、Alternate Data Streamはほとんど考慮されない。多くのアーカイバーソフトウェアも、Alternate Data Streamは無視する。従って、ネットワーク越しにファイルを送る場合、Alternate Data Streamは送られない。ただし、Alternate Data Streamにも対応している有名なアーカイバーがひとつだけある。rarだ。

WinRarは、Alternate Data Streamまで含めてアーカイブ、展開する機能を持っている。rarを使えば、Alternate Data Streamを含めてファイルをネットワーク越しに渡せるのだ。

市場の60%のアンチマルウェアソフトウェアは、このrarファイルや、Alternate Data Streamを使ったマルウェアを正しく検出できない。

元記事では、7zipなどのWindows用に設計された、よりより圧縮率、より良い暗号化方式、Unicodeサポート、並列処理などのモダンなアーカイブ形式があるにも関わらず、いまだにrarも使われているのは、ひょっとしたらこのためかと推測している。Windows用のマルウェアをAlternate Data Streamに隠して運ぶためではないのか。

2013-02-25

skype4COMを利用してASLRを回避

Bypassing Windows ASLR using “skype4COM” protocol handler | GreyHatHacker.NET

不自由なソフトウェアのSkypeをインストールするともれなく入ってくるプロトコルハンドラーの実装、skype4com.dllは、IEにロードされる。しかし、skype4com.dllはASLRが有効になっていないので、ロードされるアドレスが容易に推測でき、rop chainに利用できてしまう。もちろん、そのrop chainを仕掛けるための脆弱性は別に必要になるが、ASLRさえあれば深刻な問題にならなかった脆弱性が、Skypeがインストールされている環境で攻撃に利用できてしまうのは痛い。皆が常に最新のパッチを適用しているとも限らないことを考えれば、IEに読み込まれるdllを書くときは、注意したほうがよい。

しかし、もはやSkypeはMicrosoft傘下なので、これはあまりにもお粗末すぎる。しかし、なぜ有効になっていないのだろう。まさかデバッグ時に無効にしてそのまんまということは・・・まさかな。

まあ、不自由なOSのことはしったこっちゃないが。

2012-12-19

ピンボールがWindows Vistaから取り除かれた理由

不自由なOSであるWindows XPまでは、不自由なソフトウェアのピンボールゲームが付属していた。

このゲームは、不自由なWindows Vistaでは、取り除かれた。何故かという理由を、Microsoftの古参社員であるRaymond Chenが明かしている。

Windows 95の時代からあるピンボールゲームが付属していたのは、Windows XPが最後のバージョンである。なぜなくなったのかということについては、法的な理由であるという推測があるようだ。

それは違う。

僕がWindows XPでやった仕事のひとつは、何百万行ものコードを32bitから64bitに移植して、Windows XP 64-bitエディションを出荷できるようにすることだった。64bit版のピンボールには、ボールが幽霊のように、オブジェクトをすり抜けてしまうという、厄介なバグがあった。特に、ゲームを開始して、ボールがランチャーにセットされると、そのままゆっくり画面下に落ちていくのだった。

すぐにゲームオーバーになる。

僕ともう一人が、プログラムをデバッグしていたが、そもそもこのコードは、何年も前に他所の会社で書かれたものであり、Microsoftにこのコードについて理解している人間がおらず(未だによくわかっていない)、しかも、コードのほとんどはコメントが削られているので、我々は衝突判定がなぜ動かないのか分からなかった。いや、そもそも、衝突判定がどこで行われているかすら分からなかったのだ

まだ我々は何百万行ものコードを移植せねばならず、この程度のコードに、何日も無駄に時間をかけて、どこぞの浮動小数点数の丸め誤差が衝突判定を失敗させているかなどと解析することなどできやしなかったのだ。我々は 、製品からピンボールを取り除く判断をした。

僕もこの決定は悲しかったんだよ。このゲームではよく遊んだし。Windows XPで僕が誇れる仕事の成果でもあったわけだし

Why was Pinball removed from Windows Vista? - The Old New Thing - Site Home - MSDN Blogs

ちなみに、あのピンボールゲームは、もともと、Full Tilt! Pinballという、Cinematronics, LLCによって開発され、Maxisによって発売された不自由なゲームが、簡易版としてWindowsに同梱されていたのだ。

1996年にリリースされた不自由なWindows用のソフトウェアである。時代を考えると、おそらく、ソースコードは悲惨なほど読みづらいはずだ。

最初のリンク、

What one Windows XP feature am I most proud of? - The Old New Thing - Site Home - MSDN Blogs

は、Raymond ChenのWindows XPにおける仕事として、ピンボールのFPSを120に制限し、CPUを100%食わないようにしたというもの。このピンボールゲームは古い設計で、可能な限り延々と画面を描画更新し続けるようになっていた。

次のリンク、

When somebody gives you a gift of code, it's more often than not a burden in disguise - The Old New Thing - Site Home - MSDN Blogs

は、なぜMicrosoftはサードパーティのプログラムを付属しないのかという説明。一度付属させたソフトウェアは、相当に長い期間サポートし続けなければならない。Windowsのバージョンの違いや、UIデザインポリシーの変更や、アーキテクチャの変更に対して、常に移植され続けなければならない。特許侵害の訴訟を受ける可能性があるのもMicrosoftだ。

ましてや、自然の風景の写真を壁紙として使ってみたところ、卑猥なサブリミナルが含まれているなどと報告したベータテスターもいるのだ(ベータテスターの精神状態が気になる)。

Windows brings out the Rorschach test in everyone - The Old New Thing - Site Home - MSDN Blogs

2012-06-09

Wine 1.5.6の興味深い変更

WineHQ - Wine Announcement

実は、今のところ、特に実行したい不自由なWindowsで動く不自由なソフトウェアがないのでWineはインストールしていないのだが、Wineの開発状況だけは、たまに観察している。Wine 1.5.6は、かなり興味深い。

The Wine development release 1.5.6 is now available.

What's new in this release (see below for details):
  - Automatic installation of the Mono add-on package.
  - Control panel applet for joysticks.
  - Device bitmap rendering now done through the DIB engine.
  - Support for video rendering through DirectX (VMR-9).
  - First steps towards a D3D shader compiler.
  - Build fixes for DragonFly BSD.
  - Various bug fixes.

まず、デフォルトでmonoも同時にインストールされるようになったことがある。.net frameworkはWindowsで最初からサポートされているライブラリである。ゆえに、実用的なWindowsの互換レイヤーを目指すならば、マネージドコードも実行出来なくてはならない。VMR-9のサポートも興味深い。

一番目をひくのは、D3Dシェーダーコンパイラーをサポートするべく取り組んでいるという事だろう。

WindowsでDirectXのシェーダーがどのように実行されるかというと、まず、HLSLかシェーダーアセンブリでシェーダーを記述し、マイクロソフトの、いわゆるd3d9x_##.dll(##はバージョン番号)でバイトコードにコンパイルする。アセンブリでの記述は最近のDirectXでは、確かサポートされていなかったはずだし、バイトコードは、昔はバイトコードに変換したものをプログラムに同梱していたが、今はプログラムの実行時に動的にコンパイルするようになっていたはずだが、それはこの際問題ではない。GPUのドライバーはこのバイトコードからGPU依存のコードに変換して実行する。もちろん、GPU内部でもひと仕事あるが、それはソフトウェアの外の話だ。

Wineでは、バイトコードを受け取り、GLSLに変換してあとはOpenGLの実装に任せる。Wineでは、d3d9x_##.dllの実装にも取り組んでいる。それには、シェーダーのコンパイラーを実装しなければならない。d3d9x_##.dllを完全に実装できれば、マイクロソフトの再配布ランタイムライブラリをインストールしなくてもすむのだ。

2012-06-03

Windows 8を直す方法

A1E1: My Fix for Windows 8 Freezes and General Weirdness

にもかかわらず、HDDがたまに、一分間ほど100%ビジーな状態だが、読み書きが0%ってことがあった。

[中略]

まあともかく、内蔵HDDを取り替えようと外部HDDを用意してたんだが、多数のファイルがあるディレクトリを外部HDDにコピーしようとしたとき、実際の書き込みが遅れるという、おなじ挙動をした。つまり、Windowsが実際の書き込みの前に、何かしていて、それがディスクを回転させているわけだが、実際には何もしてないわけだ。不思議に思い、この問題を再現しようと、色々な実験をしてみたところ、でかいコピーとかダウンロードとかをしてると、再現できることがわかった。ダウンロード先が別のドライブだとしても、やはり再現できる。

そこで俺は、一見めちゃくちゃな行動を起こすことにした。詳細なシステムプロパティから、ページファイルを無効化したのだ。リブートしてみたところ、今までの問題が一切解決した。Windows 8のフリーズに関するフォーラムとかを眺めてみると、どうも、フリーズする状況は、ブラウザのキャッシュを参照しているとか、でかいダウンロード中だとか、ファイルコピーだとかのようだ。

つまりこういうことだ。今のシステムはメモリが大量にあるため(俺のマシンの場合、6GB)、ページファイルなんて必要ないのだ。数日ほどマシンの状態を観察してみても、俺の環境では、1.5GBぐらいしか消費しない。6GB使いきるなんてありえない。これを考えてみると、もはや4GB以上のメモリを持つ環境では、ページファイルの存在は、システムの動作を遅らせるだけではないか。なぜならば、メモリはどのHDDよりも早いからだ(ある種の特殊なSSDはともかくとして)。

つまり、こういうことだろう。Windowsでは普通にメモリを確保すると、メモリのためにディスク上のページファイルの領域が割り当てられる。この処理には、ページファイルにアクセスしなければならない。つまり、メモリの確保にはディスクアクセスが必要になる。しかし、メモリページファイルがあるディスクへのアクセスが集中すると、メモリ領域の確保や解放処理などのためのディスクアクセスも、当然影響を受ける。他のディスクへの読み書きには、メモリを必要とするが、そのメモリの確保に戸惑うため、実際の処理が進まない。

もし、この予想の通りだとしたら、なんだか非常に馬鹿げた実装だ。

2012-04-08

なぜCtrlキーを押しながらタスクマネージャの新しいタスクを選択するとコマンドプロンプトが開くのか

Why does holding the Ctrl key when selecting New Task from Task Manager open a command prompt? - The Old New Thing - Site Home - MSDN Blogs

Adam SはなぜCtrlキーを押しながらタスクマネージャの新しいタスクを選択するとコマンドプロンプトが開くのか質問した。

隠し機能さ。

Windows XPはビジュアルスタイルを導入した。ビジュアルスタイルをデバッグする際に問題になることは、もしビジュアルスタイルエンジンが暴走した場合、まともに見えなくなるということだ。ビジュアルスタイルの開発者がしばしば直面した問題に、実行ダイアログが動かなくなるというものがあった。実行ダイアログがなければ、問題を検証するためにデバッガーを起動することすらできなくなる。

解決方法として、Ctrlキーを押しながらタスクマネージャの新しいタスクを選択すると、実行ダイアログをすっ飛ばして、コマンドプロンプトを起動できるようにした。コマンドプロンプトから、デバッガーをインストールしてデバッグができる。この手法は、Windows XPのコンソールウインドウはテーマが適用されないという仕様上、都合よく動く。ビジュアルシステムが壊れたとしても、コンソールウインドウは動くのだ。

その後、ビジュアルスタイルのバグは潰されていき、隠し機能は必要なくなった。しかし、どういうわけか、取り除かれずに残ったのだ。

ちなみに、なぜ仮想環境を使わないのかという疑問に対しては、そもそもWindows XPの開発当時、仮想環境はまだ発展途上だったからだ。

2012-03-20

なぜDOSのパスはバックスラッシュなのか

Why is the DOS path character "\"? - Larry Osterman's WebLog - Site Home - MSDN Blogs

基本的にDOSのパスはUNIX由来なのだが、スラッシュはすでにスイッチとして使われていたので、仕方なくバックスラッシュにしたらしい。

2012-01-07

oldnewthing: なんでHeapFreeがERROR_POSSIBLE_DEADLOCKで失敗するのか?

Why did HeapFree fail with ERROR_POSSIBLE_DEADLOCK? - The Old New Thing - Site Home - MSDN Blogs

ある顧客がassertに引っかかると報告してきた。何でも、HeapFree関数が、全く問題ないヒープブロックに対する操作に失敗し、GetLastError関数の結果は、ERROR_POSSIBLE_DEADLOCKだという。一体何がどうなっているのだ。

私の同僚が持ち前のエスパー能力を駆使して訊ねた。「ひょっとしてプロセスは終了中かい?」

「ええ、まあ。何で分かったんだい?」

プロセスはどのように終了するかという話を思い出して欲しい。まず最初に起こることは、プロセスの他のスレッドをすべて、強制的に終了させるのである。これにより、スレッドによって所有されていた同期リソースがロックされたままになる可能性がある。この場合、同期リソースとはヒープである。

ある関数がHeapFreeを呼んだ際、ヒープのコードはヒープをロックしようとするが、他のスレッドによって所有されているので、ロックできないことを検知する。そして、その他のスレッドはすでに終了しているのだ(他のスレッドはHeapFreeの操作の最中に強制終了される可能性がある)。ヒープのコードはこの状況を検知し、デッドロックを回避して、ERROR_POSSIBLE_DEADLOCKを返す。

これと同様に、プロセスの終了中に、メモリーを安全に確保することはできない。つまり、メモリーは確保も解放もできないのだ。前回学んだように、プロセスが終了中であるならば、クリーンアップなどすべきではないのだ。プロセスのアドレス空間が解放されるときに、メモリーはすべて解放される。手動で解放する必要はない。時間の無駄である。

Windowsにおいて、プロセスの終了中にリソースを解放する必要はない。この単純なルールを理解出来ないWindowsプログラマーは実に多い。そういうプログラマーの書いたコードは、プログラムの終了時にまぬけにも例外エラーを出したりする。