Apple Developer Connection
高度な検索
Member Login ログイン | ご入会 ADC連絡先

Technical Note TN2124
Mac OS Xにおけるデバッグの魔法

このテクニカルノートでは、Mac OS Xのさまざまな「隠れた」デバッグ機能、つまり環境変数、環境設定、GDBから呼び出し可能なルーチン、特殊ファイルなどについて説明します。Mac OS X向けの開発をしている場合は、このリストに目を通して、開発作業を楽にしてくれるものを見逃していないか確認してください。





はじめに

Mac OS Xには、個々のサブシステムの開発とデバッグを支援するために、エンジニアリングチームが追加したデバッグ機能がいくつか含まれています。これらの機能の多くは、リリース後のシステムにも残っており、コードのデバッグに利用できます。このテクニカルノートでは、広く役立つデバッグ機能をいくつか説明します。

別の場所で文書化されているデバッグ機能については、機能の簡単な概要と既存ドキュメントへのリンクを記載しています。

このテクニカルノートでは、デバッグ機能を網羅的には文書化しておらず、将来もそうする予定はありません。

警告:このテクニカルノートで説明するデバッグ機能はサポート対象外です。アップルは、Mac OS Xの進化に応じて各機能を変更または削除する権利を留保します。変更または削除は以前にもあり、今後もあることが予想されます。これらの機能はデバッグ専用です。このテクニカルノートで説明する機能の存在または動作に依存する製品を出荷してはなりません。

重要:このテクニカルノートは(Xcode 2.0をインストールした)Mac OS X 10.4をベースにしており、該当するシステムに関しては正確です。このテクニカルノートで説明する詳細の多くはリリースによって異なるため、新旧のシステムでは若干の差が生じる可能性があります。

注:このテクニカルノートの前バージョンは、(Xcode 1.5をインストールした)Mac OS X 10.3.5をベースにしていました。そのドキュメントを更新する過程で、そこで説明されていたデバッグ機能はすべて再テストしました。ほとんどの機能には大きな変更はありません。その場合には説明を変更していません。つまり、掲載した例のいくつかは実際には10.3.5から受け継いだものであり、10.4では若干異なる結果を示す可能性があります。しかし、例の全体的な要旨は現在も間違っていません。

特定のデバッグ機能が10.4で大きく変わっている場合は、10.4の動作を反映するように説明を書き換え、その中で10.3.xの動作にも言及しました。

このテクニカルノートでは、高度なデバッグテクニックを取り上げています。Mac OS Xの開発を始めたばかりであれば、以下の資料を参照してください。

  • GDBはMac OS Xの基本的なデバッガです。GDBの詳細については、「Debugging with GDB」を参照してください。

  • Xcodeはアップルの統合開発環境(IDE)です。Xcodeには、GDBのラッパとして実装された高度なグラフィカルデバッガが含まれています。Xcodeの詳細については、Apple Developer Reference LibraryのXcodeのセクションを参照してください。

先頭に戻る

基礎

このテクニカルノートでは、Mac OS Xの個々のデバッグ機能について後のセクションで詳細に説明します。これら機能の多くでは、機能の有効化と無効化、出力の表示に同じテクニックを使用します。このセクションでは、これらの共通テクニックについて説明します。

デバッグ機能の有効化

いくつかのデバッグ機能はデフォルトで有効になっています。しかし、ほとんどの機能は、次の方法のいずれかを使用して有効にする必要があります。

環境変数

多くの場合、特定の環境変数を設定することで、デバッグ機能を有効にできます。その最も簡単な方法は、「ターミナル」からアプリケーションを起動して、コマンドラインで環境変数を指定することです。リスト1にはshシェルで環境変数を設定する方法を示し、リスト2にはcshにおける同等の方法を示します。

リスト1:sh互換シェルにおける環境変数の設定

$ MallocStackLogging=1 /Applications/TextEdit.app/Contents/MacOS/TextEdit
malloc[1002]:recording stacks using standard recorder
[…]

リスト2:csh互換シェルにおける環境変数の設定

% setenv MallocStackLogging 1
% /Applications/TextEdit.app/Contents/MacOS/TextEdit
malloc[1004]: recording stacks using standard recorder
[…]

注:Mac OS X 10.3以降のデフォルトシェルは、sh互換シェルのbashです。Mac OS X 10.2以前のデフォルトシェルは、csh互換シェルのtcshでした。以降、このテクニカルノートの以下では、読者がbashを使用しているものとします。

重要:csh互換シェルを使用している場合は、デバッグを終了したら、(unsetenvコマンドを使用して)デバッグ環境変数をリセットするのを忘れないでください。そうしないと、これらの設定が以降のコマンドに悪影響を与えることがあります。

または、2つの「ターミナル」ウインドウを開き、一方のウインドウをデバッグ専用とし、他のすべてのコマンドには他方のウインドウを使用するという方法もあります。

さらに、リスト3に示すように、GDBで環境変数を設定することもできます。

リスト3:GDBにおける環境変数の設定

$ gdb /Applications/TextEdit.app
GNU gdb 5.3-20030128 (Apple version gdb-330.1) […]
(gdb) set env MallocStackLogging 1
(gdb) r
Starting program:/Applications/TextEdit.app/Contents/MacOS/TextEdit
malloc[1062]: recording stacks using standard recorder
[…]

アプリケーションのビルドとデバッグにXcodeを使用する場合は、実行可能ファイルインスペクタを使用して環境変数を設定できます。図1にその一例を示します。

図1:Xcodeにおける環境変数の設定

図1 Xcodeにおける環境変数の設定

最後に、Mac OS Xには、特定ユーザが起動するすべてのプロセスに環境変数を設定する仕組みがあります。詳細については、テクニカルQ&AのQA1067「Setting environment variables for user processes」 を参照してください。

先頭に戻る

環境設定

その他のデバッグ機能を有効にするには、defaultsコマンドラインツールを使用して環境設定を行います。リスト4にその方法を示します。このリストは、「テキストエディット」アプリケーション(バンドルIDはcom.apple.TextEdit)のNSTraceEvents環境設定をYESに設定します。

リスト4:defaultsを使用した環境設定

$ defaults write com.apple.TextEdit NSTraceEvents YES
$ /Applications/TextEdit.app/Contents/MacOS/TextEdit
2004-08-30 16:47:55.851 TextEdit[1135] timeout = 62998416724.149353 seco…
2004-08-30 16:47:55.894 TextEdit[1135] got apple event of class 61657674…
[…]

デバッグを完了したら、リスト5に示すように、再度defaultsを使用して環境設定を削除してください。

リスト5:defaultsを使用した環境設定の削除

$ defaults delete com.apple.TextEdit NSTraceEvents

defaultsの詳細については、manページを参照してください。

Cocoaアプリケーションでは、コマンドラインで一時的な環境設定を行えます。たとえば、リスト6に、永続的な環境設定を変更せずにリスト4と同等の結果を達成する方法を示します。

リスト6:一時的な環境設定

$ /Applications/TextEdit.app/Contents/MacOS/TextEdit -NSTraceEvents YES
2004-10-25 17:28:41.143 TextEdit[5774] timeout = 62993575878.857864 seco…
2004-10-25 17:28:41.179 TextEdit[5774] got apple event of class 61657674…
[…]

先頭に戻る

ファイル

いくつかのデバッグ機能は、ファイルシステム内に特定のファイルが存在することで有効になります。リスト7にその一例を示します。ファイル/var/log/do_dnserver_logを作成すると、CFNotificationCenterサーバ(distnoted)によって、すべての通知に関する情報がファイル/var/log/dnserver.logに記録されます。

リスト7:デバッグを有効にする特定ファイルの作成

$ sudo touch /var/log/do_dnserver_log

[… ここで再起動 …]

$ cat /var/log/dnserver.log
-------------------------------------------------
[…] create_client "KernelEventAgent" BFF7FA80 (1203)
[…] received_message_from "KernelEventAgent" register "DISCONNECT" […]
[…]

この例の詳細については、「CFNotificationCenter」を参照してください。

先頭に戻る

呼び出し可能ルーチン

多くのシステムフレームワークには、デバッグ情報をstderrに出力するルーチンが含まれています。これらのルーチンは、GDBから呼び出せるように特別に設計されています。リスト8にその一例を示します。この場合は、GDBComponentListルーチンを呼び出して、コンポーネントとそのインスタンスのリストを出力します(旧Mac OSのthing MacsBugコマンドと同等)。

リスト8:GDBからのデバッグルーチンの呼び出し

(gdb) call (void) GDBComponentList()
Cnt tRef# (address) Type/SubT/Manu Flags    EntryPnt File Parnt ThingName
  0 10008  0180d540 adec/.mp3/appl 10000000 00000000   -3 00000 (Not loade…
[…]
  2 1012c  005c3be0 clok/micr/appl 10000003 8b1586a8    1 00000 (Not loade…
        Inst:0x850003; Err=0; Storage:0x1fe6f0
        Inst:0x890004; Err=0; Storage:0x1fe6f8
  0 10065  0180fd08 clok/soun/appl 00000005 00000000   -3 00000 (Not loade…
[…]
There are 9 component manager files:
        0: refs 2, path [/System/Library/Components/VCH263Codec.component]…
        1: refs 94, path [/System/Library/QuickTime/QuickTimeComponents.co…
        2: refs 5, path [/System/Library/Components/IOQTComponents.compone…
        3: refs 2, path [/System/Library/QuickTime/QuickTimeVR.component],…
        4: refs 7, path [/System/Library/QuickTime/QuickTimeFirewireDV.com…
        5: refs 1, path [/System/Library/QuickTime/QuickTimeMPEG4.componen…
        6: refs 1, path [/System/Library/Components/PDFImporter.component]…
        7: refs 1, path [/System/Library/QuickTime/ApplePixletVideo.compon…
        8: refs 1, path [/System/Library/QuickTime/QuickTimeStreaming.comp…

ルーチンからの出力が表示されない場合は、次のセクションで述べるように、コンソールログを調べる必要があります。

重要:この手法を自分のコードに使用する場合は、staticとして宣言したルーチンに対して必ずしも有効でないことに注意してください。コンパイラのプロシージャ間最適化によって、staticルーチンが標準呼び出し規則から逸脱する場合があります。その場合、このルーチンをGDBから呼び出した場合の信頼性は保証されません。

現実には、これはインテル用のコードにのみ影響します。

先頭に戻る

デバッグ出力の確認

デバッグ出力を生成するプログラムは一般に、3つの異なるメカニズムのいずれかを使用して生成します。

  • stderrへの出力

  • システムログ

  • カーネルトレース機能

stderrへの出力が、最も一般的に使用されている出力メカニズムです。このトピックの重要性を考えて、これについては次のセクションで詳しく説明します。

他の2つのメカニズムはずっと単純です。システムログは、「コンソール」アプリケーション(/アプリケーション/ユーティリティ内)を使用して見ることができます。この詳細については、syslogのmanページ(syslogsyslog.conf、およびsyslogd)を参照してください。

カーネルトレース機能は、高度に特化した低レイテンシー、高可用性ログメカニズムです。多くの場合、カーネルトレース機能にログを記録するプログラムには、ログを表示する手段も含まれています(たとえば、ktraceを使用して生成されるトレースファイルを出力するには、kdumpを使用します)。

コンソール出力

多くのプログラム、そして実に多くのシステムフレームワークが、stderrにデバッグメッセージを出力します。出力先は最終的にプログラムによって制御されます。プログラムはstderrを任意の出力先にリダイレクトできます。しかし、多くの場合、プログラムはstderrをリダイレクトしないので、出力はプログラムが起動環境から継承したデフォルトの出力先に送られます。これは通常、次のいずれかになります。

  • 通常の方法で(たとえば、Finderでアプリケーションをダブルクリックすることで)GUIアプリケーションを起動すると、システムはそのstderrをコンソールデバイス(/dev/console)に接続します。その出力は、「コンソール」アプリケーション(/アプリケーション/ユーティリティ内)を使用して表示できます。また、出力はファイル/ライブラリ/Logs/Console/<ユーザ名>/console.logに記録されます。

  • 「ターミナル」からプログラム(GUIでも非GUIでも)を実行すると、stderrが「ターミナル」ウインドウに接続され、プログラムの出力はすべてこのウインドウに表示されます。これはまた、「ターミナル」ウインドウでGDBから実行するプログラムにも当てはまります。

  • Xcodeからプログラムを実行すると、Xcodeの「実行ログ」ウインドウまたは「デバッガコンソールログ」ウインドウでstderr出力を見ることができます(ウインドウを表示するには、「デバッグ」メニューから対応するコマンドを選択します)。

実行中のプログラムにアタッチしても(GDBのattachコマンドを使用)、プログラムのstderrが自動的にGDBウインドウに接続されることはありません。これをGDBから実行するには、テクニカルノートのTN2030「GDB for MacsBug Veterans」の「Seeing stdout and stderr After Attaching」セクションで説明している方法を使用します。

先頭に戻る

アーキテクチャの考慮事項

このテクニカルノートに掲載した事例は、PowerPCベースのMacintoshを使用したものです。しかし、インテルベースのマシンでも問題なく動作します。唯一の大きな違いはパラメータの受け渡しに関するものです。PowerPCアーキテクチャではレジスタを使用してパラメータを渡しますが、インテルアーキテクチャではスタックを使用してパラメータを渡します。

PowerPCでは、経験則として、最初のパラメータはGPR3 (General Purpose Register 3)に、2番目のパラメータはGPR4にというように渡されます。これらを確認するためのGDB構文は$r3$r4というようになります。戻り時には、関数の結果がGPR3 ($r3)に渡されます。

インテルに関しては、2つの経験則があります。

  • ルーチンの最初の命令で停止すると、最初のパラメータはスタックポインタから4バイト上の位置にあり、2番目のパラメータはスタックポインタから8バイト上の位置にある、という具合になります。GDB構文は*(int *)($esp+4)*(int *)($esp+8)というようになります。

  • (フレームの作成後)ルーチンの別の場所で停止すると、最初のパラメータはフレームポインタから8バイト上の位置にあり、2番目のパラメータはフレームポインタから12バイト上の位置にある、という具合になります。GDB構文は*(int *)($ebp+8)*(int *)($ebp+12)というようになります。

いずれの場合もインテルでは、関数の結果がレジスタEAX ($eax)に渡されます。

ルーチンがC++のメンバ関数の場合は、thisという黙示的な第1パラメータがあります。ルーチンがObjective-Cメソッドの場合は、2つの黙示的な第1パラメータがあります(この詳細については、「Cocoa」を参照)。

リスト9リスト10に、これらの経験則の実際を示します。

リスト9:PowerPCにおけるパラメータ

$ gdb /Applications/TextEdit.app
GNU gdb 6.1-20040303 (Apple version gdb-434) […]
(gdb) fb CFStringCreateWithFormat
Function "CFStringCreateWithFormat" not defined.
Breakpoint 1 (CFStringCreateWithFormat) pending.
(gdb) r
Starting program:/Applications/TextEdit.app/Contents/MacOS/TextEdit
Reading symbols for shared libraries […] done
Breakpoint 1 at 0x90741e80
Pending breakpoint 1 - "CFStringCreateWithFormat" resolved

Breakpoint 1, 0x90741e80 in CFStringCreateWithFormat ()
(gdb) # 最初のパラメータは、"alloc"
(gdb) p/a $r3
$1 = 0x0
(gdb) # 2番目のパラメータは、"formatOptions"
(gdb) p/a $r4
$2 = 0x0
(gdb) # 3番目のパラメータは、"format"
(gdb) p/a $r5
$3 = 0xa0742454 <kCFURLLocalhost+340>
(gdb) call (void) CFShow($r5)
%@%c
(gdb) finish
Run till exit from #0  0x90741e80 in CFStringCreateWithFormat ()
0x9073eaf8 in CFURLCreateWithFileSystemPathRelativeToBase ()
(gdb) # 関数の結果
(gdb) p/a $r3
$4 = 0x306a40
(gdb) call (void) CFShow($r3)
/Applications/TextEdit.app/Contents/MacOS/

リスト10:インテルにおけるパラメータ

$ gdb /Applications/TextEdit.app
GNU gdb 6.1-20040303 (Apple version gdb-425) […]
(gdb) fb CFStringCreateWithFormat
Function "CFStringCreateWithFormat" not defined.
Breakpoint 1 (CFStringCreateWithFormat) pending.
(gdb) r
Starting program:/Applications/TextEdit.app/Contents/MacOS/TextEdit
Reading symbols for shared libraries […] done
Breakpoint 1 at 0x9078f111
Pending breakpoint 1 - "CFStringCreateWithFormat" resolved

Breakpoint 1, 0x9078f111 in CFStringCreateWithFormat ()
(gdb) # 最初のパラメータは、"alloc"
(gdb) p/a *(int *)($ebp+8)
$1 = 0x0
(gdb) # 2番目のパラメータは、"formatOptions"
(gdb) p/a *(int *)($ebp+12)
$2 = 0x0
(gdb) # 3番目のパラメータは、"format"
(gdb) p/a *(int *)($ebp+16)
$3 = 0xa078bd88 <dyld_func_lookup_pointer+16100>
(gdb) call (void) CFShow($3)
%@%c
(gdb) finish
Run till exit from #0  0x9078f111 in CFStringCreateWithFormat ()
0x9078b6c1 in CFURLCreateWithFileSystemPathRelativeToBase ()
(gdb) # 関数の結果
(gdb) p/a $eax
$4 = 0x306ce0
(gdb) call (void) CFShow($eax)
/Applications/TextEdit.app/Contents/MacOS/
(gdb) # ここでブレークポイントをクリアして、
(gdb) # CFStringCreateWithFormat の最初の命令に設定しなおす。
(gdb) delete 1
(gdb) # 次のコマンドにおける"*"の構文に注目。
(gdb) # これは、ルーチンの最初の命令にブレークポイントを設定する。
(gdb) b *CFStringCreateWithFormat
Breakpoint 2 at 0x9078f10b
(gdb) c
Continuing.

Breakpoint 2, 0x9078f10b in CFStringCreateWithFormat ()
(gdb) # CFStringCreateWithFormat の最初の命令の位置で止まった。
(gdb) # スタックフレームはまだ作成されていない。
(gdb) # 各パラメータを、スタックポインタからの相対位置を指定して出力させる。
(gdb) p/a *(int *)($esp+4)
$5 = 0x0
(gdb) p/a *(int *)($esp+8)
$7 = 0x0
(gdb) p/a *(int *)($esp+12)
$8 = 0xa078bd88 <dyld_func_lookup_pointer+16100>
(gdb) call (void) CFShow($8)
%@%c

重要:これらは経験則にすぎません。ルーチンに非標準のパラメータまたは非標準の関数結果がある場合は、これらの経験則が適用されないため、詳細については資料を参照してください。

ここで標準パラメータとは、整数(単独のレジスタに置けるもの)、列挙、およびポインタ(配列のポインタや関数のポインタなどを含みます)です。非標準パラメータは浮動小数点数、ベクトル、構造体、およびレジスタより大きな整数です。

すべてのMac OS Xアーキテクチャにおける呼び出し規則の詳細については、『Mac OS X ABI Function Call Guide』を参照してください。

最後に、個々の命令に関する情報を探す場合は、Shark(Xcodeデベロッパツールに含まれている)のHelpメニューにPowerPCとインテル用の命令リファレンスがあります。

先頭に戻る

CrashReporter

CrashReporterはクラッシュするすべてのプログラムに関する情報を記録する貴重なデバッグ機能です。詳細については、テクニカルノートのTN2123「CrashReporter」を参照してください。CrashReporterは常時有効であり、必要なのは出力を見ることだけです。

先頭に戻る

BSD

BSDサブシステムはプロセス、メモリ、ファイル、およびネットワークインフラを実装するため、Mac OS X上のすべてのアプリケーションにとって不可欠です。BSDには、利用可能な優れたデバッグ機能がいくつか実装されています。

コアダンプ

コアダンプは、プリミティブなデバッグ機能であると過小評価されています。実際には、難しい問題をデバッグするとき、特に問題をローカルで再現できない場合は、コアダンプが非常に役立つことがあります。

システム全体でコアダンプを有効にするには、ファイル/etc/launchd.conf(ファイルがなければ作成します)にlimit core unlimitedという行を追加して再起動します。このファイルの詳細については、launchd.confのmanページを参照してください。

注:Mac OS X 10.4より前には、システム全体でコアダンプを有効にするのに、/etc/hostconfigで行「COREDUMPS=-NO-」を「COREDUMPS=-YES-」に変更して再起動していました。

または、「ターミナル」からプログラムを実行する場合は、あらかじめシェルでコアダンプのサイズ制限を簡単に引き上げることができます。リスト11にその一例を示します。

リスト11:無制限のコアダンプサイズ

$ ulimit -c unlimited
$ /Applications/TextEdit.app/Contents/MacOS/TextEdit
[…]

コアダンプ機能をテストするには、リスト12に示すように、killコマンドを使用してSIGABRTをプログラムに送信します。

リスト12:SIGABRTの送信によるコアダンプのテスト

$ ps | grep TextEdit
  374  p1  S+     0:00.58 /Applications/TextEdit.app/Contents/MacOS/TextEdit
  379 std  S+     0:00.01 grep TextEdit
$ kill -ABRT 374

この場合、アプリケーションは「Abort trap (core dumped)」というメッセージを出力して終了します。コアダンプは/coresにあります。otoolコマンドを使用すれば、どれが何のコアダンプかを知ることができます。最後に、GDBに-cを指定すれば、コアダンプをデバッグできます。リスト13に、このプロセスの一例を示します。

リスト13:コアダンプの使用

Abort trap (core dumped)
$ ls -lh /cores
total 296856
-r--------  1 quinn  admin       144M 29 Oct 10:23 core.374
$ otool -c /cores/core.374
/cores/core.374:
        Argument strings on the stack at:0xc0000000
        /Applications/TextEdit.app/Contents/MacOS/TextEdit

        /Applications/TextEdit.app/Contents/MacOS/TextEdit
        TERM_PROGRAM=Apple_Terminal
        TERM=xterm-color
        SHELL=/bin/bash
        TERM_PROGRAM_VERSION=100
        USER=quinn
        __CF_USER_TEXT_ENCODING=0x1F5:0:15
        PATH=/bin:/sbin:/usr/bin:/usr/sbin
        PWD=/Users/quinn
        SHLVL=1
        HOME=/Users/quinn
        LOGNAME=quinn
        SECURITYSESSIONID=20f550
        _=/Applications/TextEdit.app/Contents/MacOS/TextEdit
$ gdb -c /cores/core.374
GNU gdb 5.3-20030128 (Apple version gdb-292) (Sat Sep 20 03:22:27 GMT 2003)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.Type "show warranty" for details.
This GDB was configured as "powerpc-apple-darwin".
Core was generated by `/Applications/TextEdit.app/Contents/MacOS/TextEdit'.
#0  0x900075c8 in ?? ()
(gdb) bt
#0  0x900075c8 in ?? ()
#1  0x90007118 in ?? ()
#2  0x901960bc in ?? ()
#3  0x927d5ecc in ?? ()
#4  0x927dc640 in ?? ()
#5  0x927fe6d0 in ?? ()
#6  0x92dd2a80 in ?? ()
#7  0x92de93fc in ?? ()
#8  0x92dfd730 in ?? ()
#9  0x92eb9a1c in ?? ()
#10 0x00007d98 in ?? ()
#11 0x00007c0c in ?? ()

リスト13から分かるように、コアダンプにはデバッガシンボルが含まれていません。シンボルファイルが手元にあれば、add-symbol-fileコマンドを使用して、GDBにそのファイルを参照するよう指示できます。リスト14に、この「テキストエディット」バックトレースで使用されているシステムフレームワークについて、これを実行する方法を示します。

リスト14:シンボルの追加

(gdb) add-symbol-file /System/Library/Frameworks/AppKit.framework/AppKit
[…]
(gdb) add-symbol-file /System/Library/Frameworks/CoreFoundation.framework\
/CoreFoundation
[…]
(gdb) add-symbol-file /System/Library/Frameworks/System.framework/System
[…]
#12 0x00007c0c in ?? ()
(gdb) add-symbol-file /System/Library/Frameworks/Carbon.framework/\
Frameworks/HIToolbox.framework/HIToolbox
[…]
(gdb) bt
#0  0x900075c8 in mach_msg_trap ()
#1  0x90007118 in mach_msg ()
#2  0x90191930 in __CFRunLoopRun ()
#3  0x901960bc in CFRunLoopRunSpecific ()
#4  0x927d5ecc in RunCurrentEventLoopInMode ()
#5  0x927dc640 in ReceiveNextEventCommon ()
#6  0x927fe6d0 in BlockUntilNextEventMatchingListInMode ()
#7  0x92dd2a80 in _DPSNextEvent ()
#8  0x92de93fc in -[NSApplication nextEventMatchingMask:untilDate:inMode:…
#9  0x92dfd730 in -[NSApplication run] ()
#10 0x92eb9a1c in NSApplicationMain ()
#11 0x00007d98 in ?? ()
#12 0x00007c0c in ?? ()

重要:コアダンプは大きくなります。リスト13の例では、「テキストエディット」のコアダンプは144MBです。当然のことながら、コアダンプを有効にしている場合は、必ず/coresを定期的に整理して、起動ディスクがいっぱいにならないようにします。

先頭に戻る

メモリアロケータ

デフォルトのメモリアロケータには、環境変数を通じて有効にできるいくつかのデバッグ機能が含まれています。これらはmanページで完全に文書化されています。表1に、そのうちのいくつかの便利なデバッグ機能を示します。

表1:便利なメモリアロケータ環境変数

変数要約
MallocScribble割り当て解除されたメモリを0x55で満たす
MallocPreScribble(10.4以降)新たに割り当てられたメモリを0xAAで満たす
MallocGuardEdges大きな割り当ての前後に保護ページを追加する
MallocStackLoggingメモリデバッグツールを支援するために、各メモリブロックのスタックログを記録する
MallocStackLoggingNoCompactメモリデバッグツールを支援するために、すべてのオペレーションのスタックログを記録する

重要:これらの環境変数は、特別なメモリライブラリ(MallocDebugまたは保護メモリアロケータなど)を必要としません。実のところ、これらはデフォルトの非デバッグメモリアロケータによってサポートされているため、常時利用可能です。

デフォルトのメモリアロケータも、プログラミング上の一般的な問題を検出すると、メッセージのログを記録します。たとえば、同じメモリブロックを二度解放するか、割り当てていないメモリを解放すると、freeコマンドからリスト15に示すメッセージが出力される場合があります。角括弧内にある数字はプロセスIDです。

リスト15:freeが出力する一般的なメッセージ

*** malloc[4691]:Deallocation of a pointer not malloced:0x1000;  \
This could be a double free(), or free() called with the middle of \
an allocated block; Try setting environment variable MallocHelp to \
see tools to help debug

この種の問題をデバッグするには、GDB内でプログラムを実行し、malloc_printf(これらのメッセージを出力するのに使用するルーチン)にブレークポイントを置きます。ブレークポイントに達したら、GDBのbacktraceコマンドを使用して、直接の呼び出し元を判断することができます。

先頭に戻る

MallocDebugとObjectAlloc

Mac OS Xには、メモリ割り当てデバッグ用に2つのGUIアプリケーション、MallocDebugとObjectAllocが含まれています。これらのツールの詳細については、文書『Memory Performance』を参照してください。

先頭に戻る

保護メモリアロケータ

Mac OS Xには保護メモリアロケータのlibgmallocが含まれています。保護メモリアロケータをデバッグ時に使用してメモリに関する一般的な問題(バッファオーバーランや解放後の使用など)を検出できます。リスト16に、これを有効にする方法を例示します。

リスト16:libgmallocの有効化

$ gdb /Applications/TextEdit.app
GNU gdb 6.1-20040303 (Apple version gdb-434) […]
(gdb) set env DYLD_INSERT_LIBRARIES /usr/lib/libgmalloc.dylib
(gdb) r
Starting program:/Applications/TextEdit.app/Contents/MacOS/TextEdit
Allocations will be placed on word (4 byte) boundaries.
 - Small buffer overruns may not be noticed.
 - Applications using AltiVec instructions may fail.
GuardMalloc-11
Reading symbols for shared libraries […]

libgmallocの詳細については、manページを参照してください。.

注:Mac OS X 10.3.xでは、libgmallocによってフラットな名前空間を使用することを求められました(DYLD_FORCE_FLAT_NAMESPACE環境変数を設定)。その必要はもうありません。

Mac OS X 10.3.xにおけるlibgmallocの使い方については、当該システムでlibgmallocのmanページを参照してください。

先頭に戻る

標準C++ライブラリ

標準C++ライブラリはいくつかのデバッグ機能をサポートしています。

  • 標準C++ライブラリのデバッグモードを有効にするには、_GLIBCXX_DEBUGコンパイル時変数を設定します。詳細については、ドキュメント「standard C++ library」を参照してください。

  • GCC 4.0より前のバージョンでは、GLIBCPP_FORCE_NEW環境変数を1に設定することで、標準C++ライブラリのメモリキャッシュを無効にします。これによって、他のメモリデバッグツール(保護メモリアロケータなど)を使用してC++の割り当てをデバッグできます。詳細については、「standard C++ library documentation」を参照してください。

    GCC 4.0以降では、これがデフォルト動作です。

先頭に戻る

コマンドラインツール

Mac OS Xには、デバッグ用の優れたコマンドラインツールがいくつか含まれています。表2に筆者の愛用ツールを示します。

表2:重要なコマンドラインツール

ツール資料要約
gdbmanページ

Debugging with GDB
コマンドラインデバッガ
fs_usagemanページファイルシステムトレースツール
sc_usagemanページシステムコールトレースツール
latencymanページスケジューリングレイテンシデバッグツール
ktrace, kdumpmanページカーネルのトレース
heapmanページヒープダンプ
vmmapmanページアドレス空間ダンプ
malloc_historymanページメモリ割り当て履歴
leaksmanページリーク検出
tcpdumpmanページ

テクニカルQ&A QA1176「Getting a Packet Trace」
ネットワークパケットトレース
netstatmanページネットワーク統計情報。netstat -anですべてのネットワーク接続が表示
lsofmanページオープンファイルをリストアップ
PPCExplain説明付きのPPCニーモニック

先頭に戻る

ダイナミックリンカ(dyld)

Mac OS Xダイナミックリンカ(dyld)は、環境変数を使用して有効にできるいくつかのデバッグ機能をサポートしています。これらはmanページで完全に文書化されています。表3に、そのうちのいくつかの便利な変数を示します。

表3:ダイナミックリンカの環境変数

変数要約
DYLD_IMAGE_SUFFIXこの接尾辞が付いたライブラリを最初に検索する
DYLD_PRINT_LIBRARIESライブラリのロードを記録する
DYLD_PRINT_LIBRARIES_POST_LAUNCH同上、ただしmain実行後のみ
DYLD_PREBIND_DEBUG事前バインド診断を出力する
DYLD_PRINT_OPTS(10.4以降)起動時コマンドライン引数を出力する
DYLD_PRINT_ENV(10.4以降)起動時環境変数を出力する
DYLD_IGNORE_PREBINDING(10.4以降)性能テストの事前バインドを無効化する
DYLD_PRINT_APIS(10.4以降)dyld API呼び出し(dlopenなど)を記録する
DYLD_PRINT_BINDINGS(10.4以降)シンボルバインドを記録する
DYLD_PRINT_INITIALIZERS(10.4以降)イメージ初期化呼び出しを記録する
DYLD_PRINT_SEGMENTS(10.4以降)セグメントマッピングを記録する
DYLD_PRINT_STATISTICS(10.4以降)起動時の性能統計情報を出力する

これらのうち、DYLD_IMAGE_SUFFIXは、「デバッグライブラリ」で述べるように、システムのデバッグライブラリを有効にするのに使用できるため、最も便利です。

コードを動的にロードしようとしてロードに失敗した場合は、ダイナミックリンカルーチンNSLinkEditErrorを使用して、エラーの原因に関する情報を取得できます。この詳細については、ドキュメント「NSModule」を参照してください。CFBundleのような高レベルAPIを使用してコードをロードする場合でも、NSLinkEditErrorは正常に機能します。

また、dlopenを使ってコードをロードする場合は、dlerrorを使用して同様の情報を取得できます。

デバッグライブラリ

Mac OS Xの多くのフレームワークには、実運用版とデバッグ版の両方が含まれています。デバッグ版には、接尾辞「_debug」が付いています。たとえば、Core Foundationフレームワークの実運用版は/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundationで、そのデバッグ版は/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation_debugです。dyldでライブラリのデバッグ版をロードするには(デバッグ版がある場合)、DYLD_IMAGE_SUFFIX環境変数を「_debug」に設定します。リスト17に、「ターミナル」からこれを実行する方法の一例を示します。

リスト17:_debugライブラリの使用

$ DYLD_IMAGE_SUFFIX=_debug /Applications/TextEdit.app/Contents/MacOS/TextEdit
2004-08-30 18:32:06.051 TextEdit[1393] CFLog (0):Assertions enabled
[…]

「ターミナル」でうまくいかない場合は、Xcodeで実行ファイルインスペクタを使用して同じことを実行できます。図2では、「Use … suffix when loading frameworks」ポップアップが「debug」に設定されているのを確認することができます。

図2:Xcodeでデバッグライブラリを有効化

図2 XCodeでデバッグライブラリを有効化

デバッグライブラリの正確な動作はフレームワークによって異なります。ほとんどのデバッグライブラリには、次のものが含まれています。

  • 完全なデバッグシンボル。これが特に役立つのは、フレームワークのソースがDarwinに含まれている場合です。

  • 付加的なアサーション。これらはデベロッパ側のプログラミングエラーを正確に指摘するのに役立ちます。

  • 付加的なデバッグ機能。このドキュメントで後述するデバッグ機能のいくつかは、デバッグライブラリでのみ利用可能です。

日常的なデバッグ処理の一部として、デバッグライブラリを使用することを強くお勧めします。

注:特に明記しないかぎり、このテクニカルノートのデバッグ機能はデバッグライブラリの使用を必要としません。

先頭に戻る

1つのデバッグライブラリだけを有効化

状況によっては、1つのデバッグライブラリだけを有効にしたい場合もあります。たとえば、アップルイベントの問題をデバッグするために、AEフレームワークの「_debug」版を有効にしたいとします。しかし、DYLD_IMAGE_SUFFIXを「_debug」に設定した場合、すべてのデバッグライブラリを使用すると、アプリケーションの別の場所で問題が生じることが分かりました。今は、アップルイベント問題のデバッグに集中して取り組みたいと思っています。どうしますか?

幸い、多少乱暴ながらも、簡単な解答があります。デバッグ版を非デバッグ版にコピーするだけです。リスト18にその一例を示します。

リスト18:AEデバッグライブラリのみをアクティブにする

$ cd /System/Library/Frameworks/ApplicationServices.framework/\
Frameworks/AE.framework/Versions/A/
$ sudo cp -n AE AE_original
Password: ********
$ sudo cp AE_debug AE

コピーが完了したら、変更を有効にするために再起動する必要があります。

重要:このテクニックはデバッグに役立ちますが、弊害がいくつかあります。たとえば、デバッグライブラリは非デバッグライブラリと異なるアドレスに事前バインドされているため、事前バインドプログラムは事前バインドした状態では開始できず、起動が遅くなります。したがって、これは専用テストコンピュータにおいてのみ実行するか、それが現実的でない場合は、テスト用システムを起動したメインコンピュータ上で実行します。さらに、デバッグを完了したらすぐに、ライブラリを元に戻してください。

非デバッグライブラリに戻すには、リスト19に示すように、元のライブラリをコピーするだけです。

リスト19:AEデバッグライブラリを非アクティブにする

$ sudo cp AE_original AE
Password: ********

注:ライブラリ名を変更するのではなくコピーすることを勧める理由は、ライブラリの最新コピーを誤って上書きするのを難しくするためです。

先頭に戻る

デバッグライブラリのバージョニング

デバッグライブラリは、システムインストーラではなく、Xcodeインストーラによってインストールされます。システムソフトウェアをアップデートしても、デバッグライブラリは更新されません。そのため、デバッグライブラリが実運用ライブラリと同期していないことがよくあります(通常は、デバッグライブラリの方が古い)。これによって、いくつかの予期しない非互換性の問題が生じる可能性があります。

たとえば、Mac OS X 10.4をインストールし、次にXcode 2.0をインストールして、その後システムをMac OS X 10.4.3にアップデートした場合は、デバッグライブラリを有効にすると、アプリケーションを起動できなくなることがあります。その理由は、Mac OS X 10.4.3では、アップルがCoreServicesフレームワークに新しいルーチンを追加し、そのルーチンを使用するようにDesktopServicesPrivフレームワークを更新したためです。しかし、これらのフレームワークの一方(CoreServices)にはデバッグ版が存在しますが、もう一方(DesktopServicesPriv)には存在しません。そのため、デバッグライブラリを有効にすると、デバッグ版のCoreServices(Xcode 2.0によってインストールされ、バージョン10.4と同等)と実運用版のDesktopServicesPriv(バージョン10.4.3にアップデートされたソフトウェア)が使用されるため、この組み合わせはロードできません。

この問題はMac OS Xにはいつもつきまとっているため、アップルではこれを解決する方法を検討しています(r. 4379270)。当面の間、デバッグライブラリを使用する最も信頼できる方法は、デバッグ専用のパーティションをセットアップすることです。このパーティションで、まず対象となるメジャーOSリリースをクリーンインストールし、次にそのリリースに合ったデベロッパツールをインストールします。たとえば、Mac OS X 10.4をクリーンインストールし、次にXcode 2.0をインストールします。その後、このパーティションのソフトウェアアップデートを無効にします。これによって、実運用版およびデバッグ版のライブラリを、同期の取れた状態で使用を開始でき、同期を保ったままにできます。

先頭に戻る

プロファイルライブラリ

多くのライブラリは、「_profile」という接尾辞が付いたプロファイル版もサポートしています。プロファイルライブラリには、デバッグライブラリと同様の制限がたくさんあります。

先頭に戻る

ウインドウサーバの回避

状況によっては、ウインドウサーバなしで操作するほうが便利な場合もあります。たとえば、/etc/ttysに指定されているloginwindowのパラメータを変更する必要がある場合は、loginwindowが実行されていない状態で変更できるほうが適切です。Mac OS Xはそのための便利な方法を提供します。

  1. 「システム環境設定」の「アカウント」パネルで、「ログインオプション」をクリックします。テキストがグレー表示になっている場合は、ロックアイコンをクリックして、まずパネルのロックを解除する必要があります。

  2. 「ログイン時の表示」設定を「名前とパスワード」に変更します。

  3. ログアウトします。

  4. ログインウインドウで、ユーザ名として「>console」と入力します。loginwindowが終了し、「Login」プロンプトを表示する、ウインドウなしのTTYインターフェイスに直面することになります。

  5. いつも使用しているユーザ名とパスワードを使用してログインします。

このレベルでの作業を完了したら、(exitコマンドを使用して)シェルを終了し、標準のログインウインドウに戻ります。

重要:この環境はシングルユーザモードではありません。ほとんどのシステムデーモンはまだ実行されていて、システムのGUIコンポーネントだけが停止されます。

先頭に戻る

デーモン

ほとんどのシステムデーモンには、何らかのデバッグ機能が含まれています。多くの場合、デーモンのデバッグ機能はそのmanページに説明されています。このセクションでは、特に興味深い機能をいくつか説明します。

launchd

launchdはカーネルが最初に実行するプロセスであり(Mac OS X 10.4以降)、システムにおける他のプロセスをすべて起動します。launchdには便利なデバッグ機能がいくつかあります。各機能について以下のセクションで説明します。

launchctl

launchctlコマンドを使用すると、launchdの状態を変更できます。launchctlはいくつかのサブコマンドをサポートしています。デバッグに最も関連のあるものを表4にリストアップします。

表4:便利なlaunchctlコマンド群

コマンド要約
loglaunchd自体のログレベルを変更する。このコマンドの構文は少し注意が必要。最も便利なコマンドはlog level debugで、すべてのログ記録を有効にする。この出力を確認する方法については、「launchdログ記録」を参照。
stdoutlaunchdの標準出力を設定する。この値はlaunchdが実行するすべてのプロセスに継承される。
stderrlaunchdの標準エラーを設定する。この値はlaunchdが実行するすべてのプロセスに継承される。
limitlaunchdのリソース制限を設定する。この値はlaunchdが実行するすべてのプロセスに継承される。最も一般的な使い方はlimit core unlimitedで、コアダンプを有効にする。

注:stdoutstderrを制御する機能は思っているほど役に立ちません。ほとんどのプログラムは、stdoutstderrをオーバーライドするか(たとえば、GUIプログラムでは/dev/consoleを指す)、それらに何も書き込みません(たとえば、ほとんどのデーモンはsyslogを使用してログを記録する)。

一時的な変更を行うには、これらのコマンドの1つを引数として使用してlaunchctlを実行します。これはlaunchd自体、およびlaunchdがそれ以降に実行するすべてのプロセスに影響を及ぼします。

重要: launchdのグローバルインスタンスの状態に影響を及ぼすには、rootとして(通常はsudoを使用)launchctlを実行する必要があります。sudoを使用しないでlaunchctlを実行すると、変更はlaunchdを実行したユーザ単位のインスタンスに影響を及ぼします。

変更を永続的なものとするには、このコマンドを/etc/launchd.confに追加します。launchdはブートプロセスの初期にこれらの設定を利用するため、再起動すると、システムのすべてのプロセスに影響が及びます。

先頭に戻る

launchdログ記録

launchdの動作は、システムログに「launchd」という機能名の下に記録されます。launchdのすべてのログ出力をファイルに送るには、/etc/syslog.confに「launchd.* /var/log/launchd.log」という行を追加します。リスト20に、それを行う方法の1つを示します。

リスト20:ファイルへのlaunchd動作の記録

$ sudo cp /etc/syslog.conf /etc/syslog.conf-orig
Password: ********
$ ( cat /etc/syslog.conf-orig ; echo "launchd.* /var/log/launchd.log" ) | \
sudo cp /dev/stdin /etc/syslog.conf
$ sudo kill -HUP `cat /var/run/syslog.pid`

デフォルトでは、launchdはsyslog出力を大量には生成しないため、/var/log/launchd.logファイルは通常空のままです。リスト21に示すように、launchctlを使用してログの量を増やせます。

リスト21:launchdログ記録を増やす

$ sudo launchctl log level debug
Password: ********

先頭に戻る

特定ジョブのデバッグ

最後に、launchdでは、プロパティリストファイルのプロパティを変更することで特定のジョブをデバッグできます。デバッグに最も役立つプロパティを表5にリストアップします。

表5:デバッグに役立つプロパティ

プロパティ要約
Debuglaunchdによるジョブの処理中のログ記録を有効にする
StandardOutPathジョブの標準出力先を設定する
StandardErrorPathジョブの標準エラー出力先を設定する
EnvironmentVariablesジョブの環境変数を設定する
SoftResourceLimits, HardResourceLimitsジョブのリソース制限を設定する。1つのジョブのコアダンプを有効にするのに特に役立つ

先頭に戻る

lookupd

lookupdに関する問題(DNSリクエストに長い時間がかかるなど)を経験している場合は、以下の手順でlookupdのデバッグを有効にすることができます。

  1. ローカルマシンの/config/lookupd NetInfoディレクトリに、DebugおよびTraceプロパティを作成します。

  2. NetInfoデバッグ情報(netinfo.debug)がNetInfoログファイル(/var/log/netinfo.log)に送られるように、syslog設定を変更します。

  3. 前の手順の変更を認識させるために、SIGHUPsyslogdに送ります。

  4. 手順1の変更を認識させるために、SIGHUPlookupdに送ります。

リスト22にその一例を示します。上記の手順を完了したら、/var/log/netinfo.logの中でデバッグ出力を探せます。また、lookupd -statisticsを実行してlookupdから統計情報を取得できます。

リスト22:lookupdデバッグの有効化

$ sudo dscl . create /dsRecTypeStandard:Config/lookupd Debug YES
Password: ********
$ sudo dscl . create /dsRecTypeStandard:Config/lookupd Trace YES
$ sudo cp /etc/syslog.conf /etc/syslog.conf-orig
$ sed 's/netinfo.err/netinfo.debug/' /etc/syslog.conf-orig | \
sudo cp /dev/stdin /etc/syslog.conf
$ sudo kill -HUP `cat /var/run/syslog.pid`
$ sudo kill -HUP `cat /var/run/lookupd.pid`

リスト23に、これらの変更を取り消す方法を示します。

リスト23:lookupdデバッグの無効化

$ sudo dscl . delete /dsRecTypeStandard:Config/lookupd
$ sudo mv /etc/syslog.conf-orig /etc/syslog.conf
$ sudo kill -HUP `cat /var/run/syslog.pid`
$ sudo kill -HUP `cat /var/run/lookupd.pid`

lookupdの詳細については、manページを参照してください。

先頭に戻る

出力(CUPS)

Mac OS X 10.2以降では、コアプリンティングアーキテクチャとしてCUPS (Common UNIX Printing System)を使用しています。CUPSには、CUPS設定ファイル(/etc/cups/cupsd.conf)によって制御されるデバッグログ機能が組み込まれています。このファイル内では、LogLevelディレクティブがログレベルを制御し、ErrorLogディレクティブがログの保存先(デフォルトは/var/log/cups/error_log)を制御します。このファイルの詳細については、manページを参照してください。

重要:このファイルを変更したら、変更を有効にするために、CUPSデーモンにSIGHUPを送信する必要があります。リスト24にその方法を1つ示します。

リスト24:CUPSデーモンの再起動

sudo /System/Library/StartupItems/PrintingServices/PrintingServices restart

CUPSドライバまたはフィルタを開発している場合は、stderrに出力することで、このログにエントリを追加することができます。リスト25に示すように、各行をメッセージのログレベルで始める必要があります。

リスト25:CUPSへのログ記録

// デバッグメッセージ

fprintf(stderr, "DEBUG:page_width = %.0f\n", page_width);

// 警告メッセージ

fprintf(stderr, "WARNING:Printer not responding\n");

// エラーメッセージ

fprintf(stderr, "ERROR:Lost connection with printer\n");

先頭に戻る

Core Services

Core Servicesには、メッセージを出力してデバッガに入る多くのルーチン(DebuggerDebugStr、およびSysBreakなど)が含まれています。USERBREAK環境変数を1に設定すると、これらのルーチンによってSIGINTシグナルが現在のプロセスに送信され、その割り込みによってGDBでの作業に入れます。Xcodeには、この機能を有効にするGUIがあります(図3に示すように、実行可能ファイルのインスペクタの「Debugging」パネルにある「Break on Debugger() and DebugStr()」チェックボックス)。

図3:XcodeにおけるUSERBREAKの設定

図3 XCodeにおけるUSERBREAKの設定

Code Fragment Manager (CFM)

Mac OS XのCFM互換環境では、2つの便利な環境変数、CFMDebugBasicCFMDebugFullをサポートしており、それらを1に設定すれば、前者の場合には限定的なデバッグログ、後者の場合には冗長なデバッグログが得られます。この機能は、CFMクロージャ問題やフラグメント初期化ルーチンが失敗する問題を追跡するのに特に役立ちます。

先頭に戻る

Core Foundation

Core Foundation (CF)フレームワークのあらゆるバリエーションが、CFShowルーチンをサポートしています。このルーチンは任意のCFオブジェクトの記述をstderrに出力します。このルーチンはコードの中からも呼び出せますが、GDBから呼び出す場合は特に便利です。リスト26にその一例を示します。

リスト26:GDBからCFShowを呼び出す

$ gdb /Applications/TextEdit.app
GNU gdb 5.3-20030128 (Apple version gdb-330.1) […]
(gdb) fb CFRunLoopAddSource
No symbol table is loaded.Use the "file" command.
Breakpoint 1 at 0x0
(gdb) r
[…]
Breakpoint 1, 0x901b5764 in CFRunLoopAddSource ()
(gdb) call (void) CFShow($r3)
<CFRunLoop 0x116290 [0xa01900e0]>{
    locked = false,
    wait port = 0xf03,
    stopped = false,
    current mode = (none),
    common modes = <CFSet 0x1162c0 [0xa01900e0]>{
        count = 1,
        capacity = 4,
        values = (
            1 : <CFString 0xa0195b38 [0xa01900e0]>{
                contents = "kCFRunLoopDefaultMode"
            }
        )
    },
    common mode items = (null),
    modes = <CFSet 0x116310 [0xa01900e0]>{
        count = 1,
        capacity = 17,
        values = (
            20 : <CFRunLoopMode 0x1163a0 [0xa01900e0]>{
                name = kCFRunLoopDefaultMode,
                locked = false,
                port set = 0x1003,
                sources = (null),
                observers == (null),
                timers = (null)
            },
        )
    }
}

重要:CFShowからの出力が表示されない場合は、おそらくコンソールに送られています。この出力を表示する方法については、「デバッグ出力の確認」を参照してください。

注:リスト26に、CFShowからの出力を編集して読みやすくしました。

その他にも、CFGetRetainCountCFBundleGetMainBundle、およびCFRunLoopGetCurrentなど、GDBから呼び出すと役立つと考えられるCFルーチンがいくつかあります。

また、Core Foundationフレームワークには、広範なデバッグ支援を提供するデバッグ版もあります。たとえば、Core Foundationの非デバッグ版では、ルーチンに渡されるパラメータの妥当性はチェックされませんが、デバッグ版ではパラメータが全面的にチェックされます。これは、コードの多くのCore Foundation関連のバグを見つけ出すのに役立ちます。

Core Foundationのデバッグライブラリは、CFZombieLevelという環境変数をサポートしています。この変数は、一連のフラグビットを含む整数として解釈されます。表6に、現在定義されているビットを説明します。これらは、CFのメモリ管理に関係するさまざまな問題の追求に役立ちます。

表6:CFZombieLevel環境変数のビット定義

ビットアクション
0割り当て解除されたCFメモリを上書きする
1割り当て解除されたCFメモリを上書きする際に、オブジェクトヘッダ(CFRuntimeBase)を上書きしない
4CFオブジェクトを保持するのに使用するメモリを解放しない
7セットされていればビット8~15の値を使用して上書きし、セットされていなければ0xFCを使用する
8..15ビット7がセットされている場合は、これらのビットの値を使用して、割り当て解除されたメモリを上書きする
16割り当てられているCFメモリを上書きする
23セットされていれば、ビット24~31の値を使用して、割り当てられているメモリを上書きし、セットされていなければ0xCFを使用する
24..31ビット6がセットされている場合は、このビットの値を使用して、割り当て解除されたメモリを上書きする

警告: CFZombieLevelは現在、カスタムアロケータ(CFAllocatorCreateを使用して作成)と互換性がありません(r. 4158401)。この組み合わせを試みると、カスタムデアロケータでオブジェクトの割り当て最初に解除したときに、プログラムがクラッシュします。

CFNotificationCenter

CFNotificationCenterログを有効にするには、ファイル/var/log/do_dnserver_logを作成します。jこのファイルが存在し、rootに所有されている場合は、CFNotificationCenterデーモン(distnoted)がその活動を/var/log/dnserver.logに記録します。この変更は、distnotedを再起動すると有効になります。これを実行する最も安全な方法はシステムを再起動することです。

この例は「ファイル」にあります。

注:Mac OS X 10.3.xでは、対応するファイルは/var/tmp/do_dnserver_log/var/tmp/dnserver.logでした。

また、Mac OS X 10.3.xでは、クライアント側のログ記録をサポートしていました。ファイル/var/tmp/do_xnc_logを作成すると、CFNotificationCenterによって各クライアントの活動がそれぞれのファイル(/var/tmp/xnc_logs/<progname>)に記録されます。この設定は、プログラムを再起動すると有効になります。クライアント側ログ記録はMac OS X 10.4以降では利用できません。

先頭に戻る

Component Manager

Component ManagerはGDBComponentListというルーチンをエクスポートしています。このルーチンをGDBから呼び出して、登録されているコンポーネントとそのインスタンスに関するデータベースを出力できます。リスト8にその使用例を示します。

さらに、ComponentDebug環境変数を1に設定すると、Component Managerがさまざまなデバッグメッセージを出力します。これらは、コンポーネントに異常がある場合、特にロードしない場合に役立ちます。

最後に、コンポーネントがロードできない場合は、ダイナミックリンカ機能を使用して問題をデバッグできます。詳細については、「ダイナミックリンカ(dyld)」を参照してください。

先頭に戻る

File Manager

Core ServicesのFile Manager(一般にはCarbon File Manager、または単にFile Managerと呼ばれている)は、fs_usageコマンドラインツールと緊密に統合されています。Core Servicesデバッグライブラリを有効にすると、fs_usageはプリミティブなBSDファイルシステム呼び出しだけでなく、それらを誘発したFile Manager呼び出しも表示します。詳細については、fs_usagemanページを参照してください。

FilesASDDebug環境変数を1に設定すると、File ManagerはファイルシステムにおいてリソースフォークおよびMacintoshメタデータをネイティブサポートしていないAppleDoubleファイルの処理を記録します。

VNDebug環境変数を1に設定すると、File ManagerはDisk Arbitrationとの対話を記録します。

File Managerには、GDBから呼び出し可能な便利なルーチンがいくつかあります。最も役に立つPrintVolumeInfoはボリュームリストを出力します。このルーチンはパラメータを1つ取ります。値が1の場合はFile Manager APIを使用してボリュームが列挙され、0の場合は内部データ構造が調べられます。前者の結果は洗練された要約を示し、後者は詳細な情報を示します。リスト27にそれぞれの例を示します。

リスト27:PrintVolumeInfoの使用

(gdb) call (void) PrintVolumeInfo(1)
1:vol=-100      "X2"
2:vol=-101      "X1"
3:vol=-102      "Guy Smiley"
4:vol=-130      "UFS Victim"
5:vol=-105      "Network"
6:returned error -35
(gdb) call (void) PrintVolumeInfo(0)
Volume Information:
        "X2"    mountpoint:"/"
                vRef=-100  volID=-100 diskID=disk0s10
        "X1"    mountpoint:"/Volumes/X1"
                vRef=-101  volID=-101 diskID=disk0s9
        "Guy Smiley"    mountpoint:"/Volumes/Guy Smiley"
                vRef=-102  volID=-102 diskID=disk0s11
        "UFS Victim"    mountpoint:"/Volumes/UFS Victim"
                vRef=-130  volID=-130 diskID=disk3s2
        "Network"       mountpoint:"/Network"
                vRef=-105  volID=-105 diskID=/Network

その他にも、GDBから呼び出し可能な2つのルーチンがありますが、これらは主として、Mac OS X上のVFSプラグインを開発している人々にとって興味あるものです。これらのルーチンによって、ディレクトリ列挙キャッシュとファイルIDツリーを出力できますが、これらはどちらもFile Managerが非volfsボリュームに対して維持している互換性構造です(volfsについては、テクニカルQ&AのQA1113「The "/.vol" directory and "volfs"」を参照)。

PrintEnumerationCacheは、ボリューム上のディレクトリ列挙キャッシュを出力します。このルーチンはパラメータを1つ取りますが、それは対象となるボリュームのvRefNumです。

ファイルIDツリーはcoreservicesdによって一元管理されています。これを出力するには、まずcoreservicesdにGDBをアタッチし、(テクニカルノートTN2030「GDB for MacsBug Veterans」で述べているように)coreservicesdstderrをターミナルデバイスにリダイレクトして、「fsnode_all」という文字列パラメータを1つ指定してルーチンFileIDTreeStorageServerDumpを呼び出します。その結果は、非常に分かりにくい情報でいっぱいのCFStringになります。リスト28にその一例を示します。

リスト28:ファイルIDツリーの出力

$ sudo gdb
Password:********
GNU gdb 6.1-20040303 (Apple version gdb-425) […]
(gdb) call (int) close(2)
$1 = 0
(gdb) shell tty
/dev/ttyp2
(gdb) call (int) open("/dev/ttyp2", 2)
$2 = 2
(gdb) call (void *) FileIDTreeStorageServerDump("fsnode_all")
$3 = (void *) 0x316320
(gdb) call (void) CFShow($3)
Shared universes:
        501(3): mod seed = 2
        Shared segments ([domainID][segmentID](<unused entries>):<address>
                [0][0](32/480):0x200e00
                [1][0](496/16):0x300e00
                [2][0](504/8):0x504b00
        Entries scheduled for removal:
                69000000(108)@1: FSObjectVolumeEntry:vRefNum: -105
        0(5): mod seed = 2
        Shared segments ([domainID][segmentID](<unused entries>):<address>
                [0][0](32/480):0x400c00
                [1][0](496/16):0x500c00
                [2][0](504/8):0x300800
        Entries scheduled for removal:
                69000000(108)@1: FSObjectVolumeEntry:vRefNum: -105
checked-in processes:
        204(0/0x7000040):seed 2(0), idle
        211(0/0x6000040):seed 2(0), idle
        248(501/0x7000040):seed 2(0), idle
        114(501/0x6000040):seed 1(0), idle
        120(501/0x5000040):seed 2(0), idle
        183(0/0x5000040):seed 1(0), idle
No transactions in progress

注:Mac OS X 10.3.xでは、ファイルIDツリーは各プロセスによって個別に維持されていました。当該システムでは、PrintFileIDTreeを直接呼び出すことのみできます。このルーチンはvRefNumパラメータを1つだけ受け取り、ファイルIDから当該ボリュームのファイルパスへのプロセスのマッピングを出力します。

先頭に戻る

Folder Manager

Folder Managerは、FolderDebugという環境変数を1つだけサポートしています。この環境変数の値を1に設定すると、Folder Managerはいくつかの付加的なデバッグメッセージを出力するようになります。この機能はCore Servicesデバッグライブラリを必要とします。

先頭に戻る

Gestalt

Core ServicesはDebugDumpGestaltDebugGestalt、およびDebugGestaltStrという3つのルーチン をエクスポートしています。これらのルーチンをGDBから呼び出してGestaltレジストリを表示できます。リスト29にその使用例を示します。

リスト29:Gestaltデバッグルーチンの使用

(gdb) call (void) DebugDumpGestalt()
DebugDumpGestalt
'a/ux':0x2a65c6ac (proc)
'addr':0x00000007
[…]
(gdb) call (void) DebugGestalt(0x766d2020)
'vm  ':0x00000011
(gdb) call (void) DebugGestaltStr("vm  ")
'vm  ':0x00000011

先頭に戻る

スレッド化

Core Servicesスレッド化 API(MPスレッドおよびThread Manager)では、ThreadDebug環境変数をサポートしています。この環境変数は、いくつかのデバッグメッセージと内部整合性チェックを有効にします。この機能はCore Servicesデバッグライブラリを必要とします。

先頭に戻る

Webサービス

Webサービスでは、2つの便利な環境変数、WSDebugWSDebugVerboseをサポートしており、それらを1に設定すれば、前者の場合には限定的なデバッグログ、後者の場合には冗長なデバッグログが得られます。これらの変数は非デバッグライブラリでも有効ですが、デバッグライブラリの場合にはさらに多くのログが得られます。

先頭に戻る

DiskとDisc

Disk Arbitration

/etc/mach_init.d/diskarbitrationd.plistCommandプロパティに「-d」を付加してシステムを再起動すると、Disk Arbitrationのアクティビティに関する詳細な情報が/var/log/diskarbitrationd.logに記録されます。

先頭に戻る

Disc Recording

DRVerboseLogging環境変数を1に設定すると、Disc Recordingの情報がコンソールに記録されます。

重要:バグ(r. 4413303)のために、この機能はMac OS X 10.4.xでは正常に機能しません。

先頭に戻る

Disk Utility

DUDebugMenuEnabled環境設定を1に設定すると、Disk Utilityには、いくつかの役に立つコマンドを含んだ「Debug」メニューが表示されます。

先頭に戻る

ApplicationServices

アップルイベント

Apple Event Managerには、デバッグ用の広範なサポート機能が組み込まれています。このサポート機能について学ぶ最善の方法は、リスト30に示すように、GDBを使用してGDBPrintHelpDebuggingAppleEventsルーチンを呼び出すことです。

リスト30:Apple Event Managerデバッグ用ヘルプ

(gdb) call (void) GDBPrintHelpDebuggingAppleEvents()
The AppleEvent Manager has been completely rewritten for this
version of Mac OS X.  The internal structure of an AEDesc is
now a pointer to a sparse tree.If you're having problems
it could be because you're accessing the dataHandle of an
AEDesc directly.

Also of note is that AEGetDescData and AEGetDescDataSize only
work with value descriptors created by AECreateDesc - you cannot
get the data size of an AERecord or AEList, for example.

To print the contents of an AppleEvent from GDB, you can:
  (gdb) call (void) GDBPrintAEDesc(descPtr)

To view all currently installed AppleEvent coercion handlers:
  (gdb) call (void) GDBPrintAECoercionTables()

To view all contents install AppleEvent handlers:
  (gdb) call (void) GDBPrintAEHandlerTables()

Additionally, to log information about AppleEvent manager calls,
you can set environment variables that will produce debugging output
to the console:

  % setenv AEDebug         1            # general debug output
  % setenv AEDebugSends    1            # print sent events
  % setenv AEDebugReceives 1            # print received events and replies
  % setenv AEDebugVerbose  1            # print result information on (most) \
                                          calls (very verbose)
  % setenv AEDebugOSL      1            # print result information from OSL
  % setenv AEDebugFile     /tmp/logfile # send debug output to this file

注:上記のテキストで、「this version of Mac OS X」はMac OS X 10.2を指しています。

一部の環境変数、特にAEDebugAEDebugVerboseAEDebugOSL、およびAEDebugFileはデバッグライブラリでのみサポートされています。

デバッグライブラリを使用している場合は、/var/tmpに3つのファイルを作成して、アップルイベントのデバッグを制御することもできます。

  • /var/tmp/AEDebugファイルを作成するのは、AEDebugAEDebugSendsAEDebugReceivesAEDebugReceivesAEDebugOSLの各環境変数を設定するのと同等です。

  • /var/tmp/AEDebug.outファイルを作成するのは、AEDebugFileを「/var/tmp/AEDebug.out」に設定するのと同等です。

  • /var/tmp/AEDebugLogsファイルを作成すると、Apple Event Managerがその出力をすべて、/var/tmp/AELog-<プログラム名>というファイルに送信します。

重要:後者2つの項目は、デバッグの出力先にのみ影響を与えます。デバッグ出力を得るには、上記の環境変数のいずれかを使用するか、/var/tmp/AEDebugを作成してデバッグ出力を有効にします。

AE_PRINT_XML環境変数を1に設定すると(10.4以降)、GDBPrintAEDescが記述子をXMLとして出力します(可能な場合)。

リモートアップルイベント

リモートアップルイベントに関する問題がある場合は、アップルイベントサーバプロセスでログ記録を有効にすると便利なこともあります。それには、/System/Library/LaunchDaemons/eppc.plistを編集して、ProgramArguments配列に「--debug」エントリを追加します。その最終結果は、リスト31に示すようなファイルになります。

リスト31:リモートアップルイベントデバッグの有効化

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" \
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Disabled</key>
        <true/>
        <key>Label</key>
        <string>com.apple.AEServer</string>
        <key>ProgramArguments</key>
        <array>
                <string>/System/Library/Frameworks/ApplicationServices.\
framework/Frameworks/AE.framework/Versions/A/Support/AEServer</string>
                <string>--debug</string>
        </array>
        <key>inetdCompatibility</key>
        <dict>
                <key>Wait</key>
                <false/>
        </dict>
        <key>Sockets</key>
        <dict>
                <key>Listeners</key>
                <dict>
                        <key>SockServiceName</key>
                        <string>eppc</string>
                        <key>SockType</key>
                        <string>stream</string>
                        <key>Bonjour</key>
                       <true/>
                </dict>
        </dict>
</dict>
</plist>

この変更を有効にするには、リモートアップルイベントを停止して再開する必要があります。これは「システム環境設定」の「共有」パネルで行えます。ログ情報はシステムログに記録されます。

重要:AEServerlaunchdの要求に応じて起動されるため、実際にアップルイベントをマシンに送信するまで、対応するログエントリは記録されません。ログエントリが記録されない場合は、サーバマシンを再起動して、再度試してください。

注:Mac OS X 10.3.xでは、アップルイベントサーバはxinetdによって実行されていました。環境設定ファイルは/etc/xinetd.d/eppcでした。デバッグを有効にするには、「server_args」行のコメントアウトを解除し、パラメータ値を「--debug」に変更します。

先頭に戻る

Process Manager

プロセスをデバッグしたいけれども、GDBから開始したくない状況も考えられます。たとえば、GUIアプリケーションをデバッグするためにリモートコンピュータにsshでアクセスした場合、GDBから直接開始することはできません。アプリケーションが適切でないMachブートストラップ名前空間にあり、ペーストボードサーバのような重要なサービスに接続できないためです。通常、これは問題になりません。リモートユーザにアプリケーションを起動するよう依頼し、起動後にGDBのattachコマンドを使用して実行中のアプリケーションにアタッチするだけです。しかし、アプリケーションの起動プロシージャをデバッグしたい場合はどうでしょうか。結局は、「アタッチ競争」をすることになります。つまり、ユーザがアプリケーションを起動したところを狙って、急いでGDBとアタッチするという方法です。これではお話になりません。

この問題に対しては、Process Managerが良い解決策を提供します。INIT_Processes環境変数を1に設定すると、Process Managerがアプリケーションの起動を15秒間遅らせるので、GDBをアタッチする時間が得られます。しかも、適切なメッセージ(リスト32を参照)がシステムログに記録されるため、何が実行されているか分かります。

リスト32:一時停止されたプロセスによって生成されるシステムログメッセージ

Sep  7 14:18:37 guy-smiley QuickTime Player:Blocking on INIT_Processes \
for 15 seconds; attach to pid 4344 if you want.

先頭に戻る

Core Graphics

Quartz Debugには、便利なデバッグ機能がいくつかあります。詳細については、テクニカルQ&A QA1236「Debugging Graphics with QuartzDebug」を参照してください。

先頭に戻る

QuickDraw

QuickDrawでは、QuickDraw状態に関する情報を取得するためにGDBから呼び出せるルーチンがいくつかエクスポートされています。最初の3つのルーチン、QDDebugPrintPortInfoQDDebugPrintCGSInfo、およびQDDebugDumpRegionは情報をstderrに出力します。リスト33にその使用例を示します。

リスト33:QuickDraw出力ルーチン

(gdb) set $window = (void *) FrontWindow()
(gdb) set $port = (void *) GetWindowPort($window)
(gdb) call (int) QDDebugPrintPortInfo($port)
Dumping port 0x435670...
    PixMap:              0x1FE72C
        Base Address:    0xB0028000 [onscreen, buffered]
        RowBytes:        0xFFFF9400
        Bounds:          (0, 0, 106, 352)  (352w x 106h)
        Depth:           0020
    Port bounds:         (0, 0, 106, 352)  (352w x 106h)
    Port shape:          0x1FE798 (0, 0, 106, 352)  (352w x 106h) …
    Vis rgn:             0x1FE730 (0, 0, 106, 352)  (352w x 106h) …
    Clip rgn:            0x1FE738 (-32000, -32000, 32000, 32000)  …
    Fore Color:          0000 0000 0000
    Back Color:          FFFF FFFF FFFF
[…]
$21 = 0
(gdb) call (int) QDDebugPrintCGSInfo($port)
CGS info for port 0x435670
    CGSWindowID:           19798
    Shape:                 0x59E734 (99, 785, 205, 1137)  (352w x …
    Vis Region:            0x59E72C (0, 0, 0, 0)  (0w x 0h) [rect]
    Dirty Region:          0x59E730 (0, 0, 0, 0)  (0w x 0h) [rect]
$20 = 0
(gdb) # 0x1FE730 is "Vis rgn" from QDDebugPrintPortInfo
(gdb) set $rgn=0x1FE730
(gdb) call (int)QDDebugDumpRegion($rgn)
Size = 116  Bounds = (0, 0, 106, 352)  (352w x 106h)  NEW FORMAT
0: 2 350
1: 1 351
2: 0 352
104: 1 351
105: 2 350
106:

$21 = 0

残りのルーチンは、リージョン形状を点滅させることで、画面上のリージョンを視覚化するように設計されています。リスト34に、これらのルーチンを呼び出す方法を示します。残念ながらこのドキュメントでは結果を見ることができないので、自分で試してみる必要があります。簡易変数$portおよび$rgnは、リスト33のように設定するものとします。

リスト34:QuickDraw点滅ルーチン

(gdb) call (int) QDDebugFlashRegion($port, $rgn)
$23 = 0
(gdb) call (void) QDDebugFlashClipRgn($port)
(gdb) call (void) QDDebugFlashPortShape($port)
(gdb) call (void) QDDebugFlashVisRgn($port)
(gdb) call (int) QDDebugFlashCGSWindowShape($port)
$24 = 0
(gdb) call (int) QDDebugFlashCGSWindowOpaqueShape($port)
$25 = 0
(gdb) call (int) QDDebugFlashCGSVisRgn($port)
$26 = 0
(gdb) call (int) QDDebugFlashCGSDirtyRgn($port)
$27 = 0

先頭に戻る

Carbon (HIToolbox)

CarbonのHIToolboxには、デバッグに便利な多数の機能が含まれています。

  • HIToolboxライブラリには、エラーに遭遇したときにデバッグメッセージを出力するデバッグ版があります。

  • GDBから呼び出して、イベント、メニュー、ウインドウ、ダイアログ、コントロールなど、プロセスのHIToolboxオブジェクトについての情報を出力できる、HIToolboxルーチンが多数あります。

  • 画面上にリージョンを視覚化できるように、リージョンを点滅させるルーチンがあります。

  • ツールボックスを通過するイベントをトレースするための各種機能があります。

HIToolboxオブジェクト出力ルーチン

以下に、さまざまなHIToolboxオブジェクト出力ルーチンを示します。

リスト35:HIToolboxイベント出力

(gdb) call (int)GDBPrintEventQueue()

Printing event queue 0x7632536c...
RunLoop: 0x40c560
Count: 4 Header: 0x1805010 Head: 0x49faf0 Tail: 0x489d10

EventRef Event Kind           Time       P Cnt Desc
-------- -------------------- ---------- - --- --------------------
  49FAF0 kEventMouseDown       219335.28 H 001 x=879, y=61, button 1
  489530 kEventWindowActivate  219335.46 H 002 0x4350A0 "Untitled 1"
  43A4E0 kEventAppActiveWindo 218971.143 S 002
  489D10 kEventWindowUpdate   219335.473 L 002 0x4A3C10 "Untitled 1 Properties"
$2 = 0
(gdb) # 0x489D10は最後のコマンドからのkEventWindowUpdateイベント
(gdb) call (void) _DebugPrintEvent(0x489D10)
Displaying event 489D10...
   Class           wind
   Kind            1
   When                219335
   Priority        Low
   RetainCount     2
   Queued          Yes
   Info            kEventWindowUpdate, 0x4A3C10 "Untitled 1 Properties"
   Parameters
        param: ----
           type: wind
           size: 4
           data: 004A3C10
                  J<

リスト36:HIToolboxメニュー出力

(gdb) call (void) DebugPrintMenuList()
Index  MenuRef     ID   Title
-----  ----------  ------  -----
    1  0x0041F330  -21629  <Apple>
    2  0x0042EC00     128  QuickTime Player
    3  0x0043C4B0     129  File
    4  0x00445B70     130  Edit
[…]
<hierarchical menus>
           0x0042CF90     140  Open Recent
(gdb) # 0x0042EC00はQuickTime Playerメニュー
(gdb) set $menu=0x0042EC00
(gdb) call (void) DebugPrintMenu($menu)
MenuRef:          0x0042EC00
    Title               : QuickTime Player
    ID                  : 128
    Width               : 0
    Height              : 0
    Enabled             : true
    Attributes          : CondenseSeparators, ReceivedInit
    Modal level         : 0
    Refcount            : 3
    Element             : 0x004435B0
    Item Count          : 12
    Item Icon  Cmd Key   Mark   CmdID E V Text
    ---- ---- -------- -------- ----- - - ----
    0001 0000 0x00 ' ' 0x00 ' '       Y Y About QuickTime Player
    0002 0000 0x00 ' ' 0x00 ' '       N Y -
    0003 0000 0x00 ' ' 0x00 ' '  pref Y Y Preferences
[…]
HIObject
    Ref count           : 3
    Event Target        : 0x42f040
    Event Handler       : 0x436f10
(gdb) call (void) DebugPrintMenuItem($menu, 1)
Menu: 0x0042EC00 Item: 1 Info:
 Text:                  About QuickTime Player
 Mark:                  <none>
 Cmd Key:               <none>
 Icon:                  <none>
 Style Normal
 Command ID:          0 (0x00000000)
 Modifiers:            0x00
[…]

リスト37:HIToolboxウインドウおよびダイアログの出力

(gdb) call (void) DebugPrintWindowList()

Window      Class    WID  Vis Hil Level Title                 Group
----------  -------- ---- --- --- ----- --------------------- -----------…
0x004350A0  Document 4ED4  Y   Y      0 Untitled 1            0x76E47A89 …
0x004A3C10  Document 4EED  Y   N      0 Untitled 1 Properties 0x76E47A89 …
(gdb) # 0x004350A0は、「Untitled 1」(名称未設定1)ウインドウ
(gdb) set $window=0x004350A0
(gdb) # 0x004A3C10は「Untitled 1 Properties」(名称未設定1のプロパティ)ダイアログ
(gdb) set $dialogWindow=0x004A3C10
(gdb) call (void) DebugPrintWindow($window)
Window 0x004350A0
    Title               : Untitled 1
    Class               : Document
    Group               : 0x76E47A89 "com.apple.HIToolbox.windowgroups.document"
    Scope               : all
    Attributes          : Collapse Box, In WindowMenu
    Visible             : Yes
    Collapsed           : No
    Latent visibility   : <none>
    Highlighted         : Yes
    Structure region    : 1FE80C #0, #0, #106, #352 (#352w x #106h) [non-rect]
[…]
(gdb) call (void) DebugPrintAllWindowGroups()
Window group tree
-------------------------------------------------------------------------…
  1  level    0  group 0x76E0BFE9 "com.apple.hitoolbox.windowgroups.root"
  2  level    0    group 0x76E47A89 "com.apple.HIToolbox.windowgroups.doc…
(gdb) # 0x76E47A89 is the second window group
(gdb) call (void) DebugPrintWindowGroup(0x76E47A89)
WindowGroup 0x76E47A89 "com.apple.HIToolbox.windowgroups.document"
 Attributes:           <none>
 Refcount:             1
 Previous group:       <none>
 Next group:           <none>
 Parent group:         0x76E0BFE9 "com.apple.hitoolbox.windowgroups.root"
[…]
(gdb) set $dialog = (void *) GetDialogFromWindow($dialogWindow)
(gdb) call (void) GDBShowDialogInfo($dialog)
Dialog:                 0x76ED59A1
 Window:                0x004A3C10 "Untitled 1 Properties"
 TextHandle:            0x0059EC7C
 Default Item:          1
 Cancel Item:           0
 Keyboard Focus Item:   0
 RefCon:                0x06054AB5 (101010101)

リスト38:HIToolboxコントロール出力

(gdb) call (void) GDBShowControlHierarchy($window)
Dumping info for window 0x4A3C10
Window found.Dumping views...
Root 0x4ba260 , ID ''/0, (-32768,-32768,32767,32767), Embedder, Vis, Act,…
    Control 0x4c24d0 <appl/sbar> ( "" ), ID ''/0, (172,301,226,317), Vis,…
    Control 0x4c6080 <appl/sbar> ( "" ), ID ''/0, (75,301,142,317), Vis, …
    Control 0x4c49c0 <appl/push> ( "Delete" ), ID ''/0, (241,220,261,290)…
    Control 0x4c4790 <appl/push> ( "Edit?" ), ID ''/0, (241,135,261,205),…
    Control 0x4c17c0 <appl/push> ( "Add?" ), ID ''/0, (241,50,261,120), V…
    Control 0x4be1d0 <appl/popb> ( "" ), ID ''/0, (12,176,28,316), Vis, A…
    Control 0x4ba1f0 <appl/popb> ( "" ), ID ''/0, (12,24,28,164), Vis, Ac…
(gdb) # 0x4c24d0は最初のスクロールバーコントロール
(gdb) call (void) GDBShowControlInfo(0x4c24d0)
HIScrollBar
    Size                : Auto
    Live Tracking       : No
Control 0x004C24D0 ""
    Control Kind        : 'appl', 'sbar'
    Control ID          : '', 0
    Window              : 0x004A3C10 "Untitled 1 Properties"
    Parent              : 0x004BA260
    Minimum             : 0 (0x00000000)
    Maximum             : 0 (0x00000000)
    Value               : 0 (0x00000000)
[…]
HIObject
    Ref count           : 1
    Event Target        : 0x4c39a0
    Event Handler       : 0x4c3a10

最後に、HIObjectPrintDebugInfoを使用して、HIObject(ウインドウ、メニュー、コントロール、HIViewsなど)のデバッグ情報を出力できます。

先頭に戻る

HIToolboxリージョン点滅

リスト39に示すルーチンは、リージョンを点滅させることで画面上でリージョンを視覚化します。残念ながらこのドキュメントでは結果を見ることができないので、自分で試してみる必要があります。

リスト39:HIToolboxリージョン点滅ルーチン

(gdb) call (void) DebugFlashWindowVisRgn($window)
(gdb) call (void) DebugFlashWindowUpdateRgn($window)

先頭に戻る

HIToolboxイベントのデバッグ

Carbonイベントの登場で、ツールボックスにおけるイベントのフローが把握しにくいことがよくあります。HIToolboxは、これを支援する2つのデバッグ機能を備えています。

EventDebug環境変数

EventDebug環境変数を1に設定することで、HIToolboxに対してシステム内を流れるすべてのイベントを記録するように指示できます。リスト40にその一例を示します。

リスト40:EventDebug出力

$ EventDebug=1 /Applications/QuickTime\ Player.app/Contents/MacOS/\
QuickTime\ Player
Event Posted: Queue: 0x763059ae, Event: kEventAppleEvent, 221132.233, S …
SendEventToEventTarget entered
    Sending Event to 0x4128E0: hiob 2
        Called handler 0x927F3200. Event was handled
        Leaving target 0x4128E0 with result 0
SendEventToEventTarget entered
    Sending Event to 0x41E6F0: hiob 2
        SendEventToEventTarget entered
            Sending Event to 0x41EC30: hiob 2
                SendEventToEventTarget entered
                    Sending Event to 0x41EC30: hiob 2
                        Called handler 0x927F3200. Event was handled
                        Leaving target 0x41EC30 with result 0
                Called handler 0x927FBB50. Event was handled
                Leaving target 0x41EC30 with result 0
        Called handler 0x927F3200. Event was handled
        Leaving target 0x41E6F0 with result 0
[…]

先頭に戻る

イベントトレース

EventDebug環境変数は大量の出力を生成しますが、これが弊害をもたらすことがあります。具体的には、プログラムの処理速度が大幅に低下したり、探している情報が膨大な出力の中から見つけにくいなどです。

この問題に対する1つの解決策は、イベント単位のトレースを有効にすることです。これを実行するには、GDBからTraceEventByNameルーチンを呼び出します。リスト41にその一例を示します。この例では、kEventRawKeyDownイベントのトレースだけを有効にします。

リスト41:イベントトレース

(gdb) call (void) TraceEventByName("kEventRawKeyDown")
(gdb) c
Continuing.
Event Posted: Queue: 0x76309338, Event: kEventRawKeyDown, 221443.183, S
SendEventToEventTarget entered
    Sending Event to 0x415750: kEventRawKeyDown
        SendEventToEventTarget entered
            Sending Event to 0x413050: kEventRawKeyDown
                Called handler 0x928CD05C. Event was NOT handled
                Leaving target 0x413050 with result -9874
        SendEventToEventTarget entered
            Sending Event to 0x42DE30: kEventRawKeyDown
                Leaving target 0x42DE30 with result -9874
            Sending Event to 0x4351F0: kEventRawKeyDown
                Leaving target 0x4351F0 with result -9874
            Sending Event to 0x4126F0: kEventRawKeyDown
                Called handler 0x929597E0. Event was NOT handled
                Called handler 0x927F4F40. Event was NOT handled
                Leaving target 0x4126F0 with result -9874
        Leaving target 0x415750 with result -9874
Event Removed: Queue: 0x76309338, Event: kEventRawKeyDown, 221443.183, S
Event Pulled (C): kEventRawKeyDown, 221443.183, S

先頭に戻る

HIToolboxイベント統計情報

HIToolboxでイベント統計情報を出力できるようにする2つの環境変数があります。EventRateを1に設定すると、HIToolboxが毎秒、その秒の間に処理されたイベントの概要を出力します。HLTBRecordEventStatsを設定すると、HIToolboxはアプリケーションの終了時に、イベント処理に関する一連の統計情報を出力します。この機能にはデバッグライブラリが必要です。

先頭に戻る

その他のHIToolboxデバッグ機能

NSQuitAfterLaunch環境変数を1に設定すると、アプリケーションはイベントループに入るとすぐに終了します。これはアプリケーションの起動時間を測定したり、リークを探す場合に役立ちます。この環境変数は、AppKitでもサポートされています。

重要:Mac OS X 10.4以降では、これが正常に機能するように、プロファイル版のHIToolboxフレームワーク(HIToolbox_profile)を使用する必要があります。

HLTBPrintKeyMatchingStatus環境変数を1に設定すると、HIToolboxはメニュー項目のマッチングに関する詳細な情報を出力します。この機能はデバッグライブラリを必要とします。

TSMEventTracing環境変数を1に設定すると、HIToolboxはText Services Manager (TSM)イベント処理に関する詳細な情報を出力します。

先頭に戻る

Cocoa

すべてのCocoaオブジェクト(NSObjectから派生するすべて)は、オブジェクトを記述したNSStringを返すdescriptionメソッドをサポートしています。この記述にアクセスする最も便利な方法は、リスト42に示すように、GDBのprint-objectコマンド(略してpo)を使用することです。

リスト42:GDBのpoコマンドの使用

$ gdb /Applications/TextEdit.app
GNU gdb 6.1-20040303 (Apple version gdb-434) […]
(gdb) fb -[NSCFDictionary copyWithZone:]
Function "-[NSCFDictionary copyWithZone:]" not defined.
Breakpoint 1 (-[NSCFDictionary copyWithZone:])pending.
(gdb) r
[…]
Breakpoint 1 at 0x928ea1d4
Pending breakpoint 1 - "-[NSCFDictionary copyWithZone:]" resolved

Breakpoint 1, 0x928ea1d4 in -[NSCFDictionary copyWithZone:] ()
(gdb) po $r3
Reading symbols for shared libraries . done
<NSCFDictionary 0x32d6d0>{
copyright = ;
author = ;
OpenPanelFollowsMainWindow = 0;
UseTransitionalDocType = 0;
UseInlineCSS = 0;
[…]
}

注: print-objectは実際には、指定したオブジェクトのdebugDescriptionメソッドを呼び出します。NSObjectdescriptionメソッドを呼び出すことで、このメソッドを実装します。したがって、デフォルトでは、オブジェクトのデバッグ記述はオブジェクトの記述と同じです。しかし、これらを分離したい場合は、debugDescriptionをオーバーライドできます。多くのCocoaオブジェクトもそのようにしています。

Objective-C

OBJC_HELP環境変数を設定すると(10.4以降)、Objective-Cランタイムはサポートしているすべてのデバッグ環境変数のリストを出力します。その中でも特に便利なものをいくつか表7に示します。

表7:便利なObjective-Cランタイムデバッグ環境変数

変数要約
OBJC_PRINT_IMAGESランタイムがロードしたイメージを記録する
OBJC_PRINT_LOAD_METHODS+loadメソッドの実行を記録する
OBJC_DEBUG_FRAGILE_SUPERCLASSESクラスのインスタンス変数がそのスーパークラスのインスタンス変数と重複する場合に警告する

NSObjCMessageLoggingEnabled環境変数を「YES」に設定すると、Objective-CランタイムはディスパッチされたすべてのObjective-Cメッセージを/tmp/msgSends-<pid>というファイルに記録します。

Cocoaコードをアセンブリレベルでデバッグする際には、次のObjective-Cランタイムの特徴に留意してください。

  • Objective-Cコンパイラは、2つの暗黙のパラメータを各メソッドに追加します。最初のパラメータは、メッセージの送信先オブジェクトへのポインタ(self)です。PowerPCでは、これはレジスタr3にあります。インテルでは、メソッド実装の最初の命令で停止したとすると、これはesp + 4のメモリ位置にあります(インテルベースのコンピュータにおけるパラメータへのアクセスについては、「アーキテクチャの考慮事項」を参照)。

  • 第2の暗黙のパラメータはメソッドセレクタです。Objective-Cでは、これはSEL型です。GDBでは、これをC文字列として出力できます。PowerPCでは、このパラメータはレジスタr4にあります。インテルでは、このパラメータはesp + 8のメモリ位置にあります。

  • Objective-Cランタイムは、objc_msgSendというC関数を使用してメソッドをディスパッチします。

  • Objective-Cオブジェクトの先頭ワード(isaフィールド)は、オブジェクトクラスへのポインタです。

リスト43に、GDBからこの情報を使用する方法の一例を示します。

リスト43:Objective-Cランタイムの「秘密」

$ gdb /Applications/TextEdit.app
GNU gdb 5.3-20030128 (Apple version gdb-330.1) […]
(gdb) r
Starting program:/Applications/TextEdit.app/Contents/MacOS/TextEdit
[…]
^C
Program received signal SIGINT, Interrupt.
0x900074c8 in mach_msg_trap ()
(gdb) # Objective-Cのメソッドディスパッチャにブレークポイントを設定する
(gdb) b objc_msgSend
Breakpoint 1 at 0x908311f4
(gdb) # 実行を継続する...
(gdb) c
Continuing.

Breakpoint 1, 0x908311f4 in objc_msgSend ()
(gdb) # ブレークポイントに達した。オブジェクトの最初の4ワードをダンプする
(gdb) x/4x $r3
0x10cc10:0xa0a04e18      0x00000001      0x00000000      0x00000000
(gdb) # セレクタを出力する
(gdb) x/s $r4
0x9083ed94 <_errDoesntRecognize+884>:"init"
(gdb) # オブジェクトを'po'する。その前にブレークポイントを無効にする必要がある
(gdb) dis 1
(gdb) po $r3
<NSAutoreleasePool:0x10cc10>
(gdb) # 'isa'ポインタを出力する。
(gdb) po 0xa0a04e18
NSAutoreleasePool

シンボルなしでデバッグするときには、Objective-Cランタイムからデバッグに役立つ関数を使用できます。表8に示すルーチンは特に便利です。

表8:便利なObjective-Cランタイム関数

関数要約
id objc_getClass(const char *name);特定クラスのObjective-C Classオブジェクトを取得する
SEL sel_getUid(const char *str);特定メソッド名のObjective-C SELを取得する
Method class_getInstanceMethod(Class, SEL);特定クラスの特定メソッドのObjective-C IMPを取得する

IMPを取得したら、先頭ワードからメソッド名へのポインタ(すべてが正常に動作したかどうかを再確認するのに有用)を、第3ワードからメソッド実装へのポインタを取得できます。

「テキストエディット」はシンボルなしで提供されていますが、その「テキストエディット」の-[Controller applicationShouldTerminate:]メソッドのデバッグ例をリスト44に示します。

リスト44:Objective-Cランタイムを使用してシンボルなしでデバッグ

$ gdb /Applications/TextEdit.app
GNU gdb 6.1-20040303 (Apple version gdb-413) […]
(gdb) r
Starting program:/Applications/TextEdit.app/Contents/MacOS/TextEdit
[…]
^C
Program received signal SIGINT, Interrupt.
0x9000a778 in mach_msg_trap ()
(gdb) # -[Controller applicationShouldTerminate:] にブレークポイントを設定したいが
(gdb) # アプリケーションのシンボルはすべてストリップされている。したがって
(gdb) # 簡単にはできない。
(gdb) info func applicationShouldTerminate
All functions matching regular expression "applicationShouldTerminate":
(gdb) # ControllerクラスのClassオブジェクトを取得する
(gdb) call (void *)objc_getClass("Controller")
$1 = (void *) 0x1caa8
(gdb) # "applicationShouldTerminate:"メソッドに対応するSELオブジェクトを取得する
(gdb) call (void *)sel_getUid("applicationShouldTerminate:")
$2 = (void *) 0x909f36a0
(gdb) # 対象クラスに対応するIMPを取得する
(gdb) call (void *)class_getInstanceMethod($1, $2)
$3 = (void *) 0x325bd8
(gdb) # IMPをダンプする
(gdb) x/3x $3
0x325bd8:0x909f36a0      0x00018ea4      0x0000a7b0
(gdb) # すべてが順調であることを確認するために最初のワードを出力する
(gdb) x/s 0x909f36a0
0x909f36a0 <_errNewVars+220620>:"applicationShouldTerminate:"
(gdb) # 3番目のワードはコードへのポインタ
(gdb) x/8i 0x0000a7b0
0xa7b0:mflr    r0
0xa7b4:stmw    r23,-36(r1)
0xa7b8:lis     r4,2
0xa7bc:stw     r0,8(r1)
0xa7c0:mr      r3,r5
0xa7c4:li      r26,0
0xa7c8:stwu    r1,-112(r1)
0xa7cc:lwz     r4,-15200(r4)
(gdb) # コードにブレークポイントを設定する
(gdb) b *0x0000a7b0
Breakpoint 1 at 0xa7b0
(gdb) # 実行を再開し、アプリケーションを終了する
(gdb) c
Continuing.
Reading symbols for shared libraries ............ done
Reading symbols for shared libraries . done

Breakpoint 1, 0x0000a7b0 in ?? ()
(gdb) # ブレークポイントに達した。パラメータを出力する。まずは
(gdb) # すべてのメソッドに共通する暗黙の"self"および"SEL"パラメータから始め、
(gdb) # 続いてメソッド固有の"app"パラメータを出力する
(gdb) po $r3
<Controller:0x328b50>
(gdb) x/s $r4
0x909f36a0 <_errNewVars+220620>:"applicationShouldTerminate:"
(gdb) po $r5
<NSApplication:0x3198e0>

Objective-Cランタイム関数とデータ構造の詳細について、/usr/include/objc/のヘッダを参照してください。

警告:このテクニカルノートで説明している多くのことと同様に、Objective-Cランタイムの内部動作は非公開です。それらはこれまでにも変更されてきましたし、今後も変更されます。この情報をデバッグに利用するのは問題ありませんが、お客様に出すコードではこの情報に依存しないでください。

先頭に戻る

Foundation

Foundationには、環境変数を使用して有効にできる多数のデバッグ機能があります。表9に、中でも特に興味深いものをいくつか示します。これらの詳細ついては、NSDebug.hを参照してください。

表9:「NSDebug.h」の環境変数

名前デフォルトアクション
NSZombieEnabledNOYESに設定すると、割り当て解除されたオブジェクトが「ゾンビ化」されます。その結果、すでに解放されているオブジェクトにメッセージが送信されることで生じる問題をすばやくデバッグできます。詳細については後述します。
NSDeallocateZombiesNOYESに設定すると、「ゾンビ化」されたオブジェクトのメモリが実際に解放されます。
NSHangOnUncaughtExceptionNOYESに設定すると、キャッチできない例外が生じたときに、プロセスが終了せずにハングします。
NSEnableAutoreleasePoolYESNOに設定すると、プールの解放時に、自動解放プールによってプール内のオブジェクトが解放されません。
NSAutoreleaseFreedObjectCheckEnabledNOYESに設定すると、すでに解放されているオブジェクトを解放しようとした場合、自動解放プールによってメッセージが出力されます。
NSAutoreleaseHighWaterMark0数値Xに設定すると、Xより多くのオブジェクトがプールに蓄積されている場合、自動解放プールによってメッセージが出力されます。
NSAutoreleaseHighWaterResolution0数値Yに設定すると、しきい値(X)を超えてY個のオブジェクトがプールに蓄積されるたびにメッセージが記録されます。

重要:Foundationのデバッグ機能を有効または無効にするには、他のシステムコンポーネントの場合のように1または0ではなく、環境変数の値を「YES」または「NO」に設定してください。

Cocoaでプログラミングする際に最も一般的に見られるバグの形態は、オブジェクトの過剰解放によるものです。これは一般的にアプリケーションがクラッシュする原因となります。しかし、クラッシュが発生するのは最終参照カウントが解放されたとき(そして解放されたオブジェクトと通信しようとしたとき)で、通常はバグの実際の場所から遠いところで起こります。このような問題をデバッグするには、NSZombieEnabledを使用するのが最善策です。これを使用すれば、解放されたオブジェクトを対象に何かすると、例外が生じるようになります。リスト45に、このような場合に表示されるメッセージの一例を示します。

リスト45:NSZombieの効果

$ NSZombieEnabled=YES DragNDropOutlineView
2004-04-30 14:56:28.238 DragNDropOutlineView[803] *** *** Selector \
'_propagateDirtyRectsToOpaqueAncestors' sent to dealloced instance \
0x530540 of class AnimatingOutlineView.

このような問題をさらにデバッグするには、GDBを使用して、-[_NSZombie methodSignatureForSelector:]にブレークポイントを設定します。

NSScriptingDebugLogLevel環境設定を使用すれば、Foundationのスクリプティングサポートに関するログを有効にできます。リスト46にその一例を示します。表示されたログは、単純なAppleScript version of application "TextEdit"を実行した結果です。

リスト46:NSScriptingDebugLogLevelの有効化

$ /Applications/TextEdit.app/Contents/MacOS/TextEdit -NSScriptingDebugLogLevel 1
[…] Suite NSCoreSuite, apple event code 0x3f3f3f3f
[…] Suite NSTextSuite, apple event code 0x3f3f3f3f
[…] Suite TextEdit, apple event code 0x74786474
[…] Command: NSCoreSuite.Get
        Direct Parameter: <NSPropertySpecifier: version>
        Receivers: <NSPropertySpecifier: version>
        Arguments: {}
[…] Property Value: 1.4
[…] Result: <NSAppleEventDescriptor: 'utxt'($0031002E0034$)

NSPrintDynamicClassLoads環境変数を「YES」に設定すると、Foundationはクラスまたはカテゴリを動的に(つまり、バンドルから)ロードするたびにメッセージを記録します。

NSExceptionLoggingEnabled環境変数を「YES」に設定すると、Foundationはすべての例外アクティビティ(NSException)をstderrに記録します。

NSUnbufferedIO環境変数を「YES」に設定すると、Foundationはstdoutに非バッファリング入出力を使用します(stderrはデフォルトで非バッファリングです)。

NSDOLoggingEnabled環境変数を「YES」に設定すると、Foundationは分散オブジェクト(NSConnectionNSInvocationNSDistantObjectNSConcretePortCoder)のログを有効にします。

先頭に戻る

AppKit

NSQuitAfterLaunch環境変数を1に設定すると、アプリケーションはイベントループに入るとすぐに終了します。これはアプリケーションの起動時間を測定したり、リークを探す場合に役立ちます。この環境変数は、Carbonでもサポートされています。

AppKitイベント

NSTraceEvents環境設定をYESに設定すると、AppKitは処理するすべてのイベントに関する情報を記録します。リスト47にその一例を示します。

リスト47:NSTraceEventsの使用

$ /Applications/TextEdit.app/Contents/MacOS/TextEdit -NSTraceEvents YES
2004-09-07 16:21:23.334 TextEdit[4520] timeout = 62997727116.666718 seco…
2004-09-07 16:21:23.341 TextEdit[4520] got apple event of class 61657674…
2004-09-07 16:21:23.454 TextEdit[4520] still in loop, timeout = 62997727…
2004-09-07 16:21:23.455 TextEdit[4520] timeout = 62997727116.546562 seco…
2004-09-07 16:21:27.793 TextEdit[4520] Received event:Kitdefined at:0.…
2004-09-07 16:21:27.804 TextEdit[4520]     In Application:NSEvent:type…
2004-09-07 16:21:27.804 TextEdit[4520] timeout = 62997727112.196404 seco…
2004-09-07 16:21:27.805 TextEdit[4520] Received event:LMouseDown at:37…
2004-09-07 16:21:27.805 TextEdit[4520]     In Application:NSEvent:type…
2004-09-07 16:21:27.805 TextEdit[4520]     In Window:NSEvent:type=LMou…
2004-09-07 16:21:27.809 TextEdit[4520]     In Application:NSEvent:type…
[…]

先頭に戻る

AppKitビュー

NSShowAllViews環境設定をYESに設定すると、AppKitはウインドウの各ビューの周りに縁取りを描画します。図4に、NSShowAllViewsを有効にした「テキストエディット」ウインドウの標準的な外観を示します。

図4:NSShowAllViewsを有効にしたテキストエディット

図4 NSShowAllViewsを有効にしたテキストエディット

NSShowAllDrawing環境設定をYESに設定すると、Quartzデバッグとまったく同様に、AppKitは描画するすべての矩形を点滅させます。図5にその結果の一例を示します。

図5:実行中のNSShowAllDrawing

図5 実行中のNSShowAllDrawing

点滅の継続時間を制御するには、NSShowAllDrawingを設定します。設定した値は、点滅継続時間をミリ秒単位で示す数値と解釈されます。デフォルトは100 msです。点滅の色を制御するには、NSShowAllDrawingColorを3つの浮動小数点数が含まれる文字列(スペース区切りで、赤、緑、青の色成分を示す)に設定します。各数字は0.0~1.0の値で、その成分の輝度を示します。たとえば、「0.0 1.0 1.0」という文字列は純粋なシアンになります。また、NSShowAllDrawingColorを「CYCLE」に設定すると、AppKitは純粋な赤、緑、青を循環表示します。

先頭に戻る

その他のAppKitデバッグ機能

NSDragManagerLogLevel環境設定は、ドラッグ&ドロップ操作時にAppKitが実行するログの頻度を制御する数字です。値が大きくなるほど、ログの頻度が高くなります。現在のところ、値6が最大有効値です。リスト48に、これを設定する方法、および「テキストエディット」で単純なテキスト選択をドラッグする際に得られる出力の一例を示します。

リスト48:NSDragManagerLogLevelの使用

$ /Applications/TextEdit.app/Contents/MacOS/TextEdit -NSDragManagerLogLevel 6
2004-09-07 16:26:42.031 TextEdit[4523] mouseDown location:{29, 389}, ba…
2004-09-07 16:26:42.033 TextEdit[4523] offset of image lower left relati…
2004-09-07 16:26:42.033 TextEdit[4523] type NeXT Rich Text Format v1.0 p…
2004-09-07 16:26:42.034 TextEdit[4523] type NSStringPboardType:data <CF…
2004-09-07 16:26:42.034 TextEdit[4523] type NeXT plain ascii pasteboard …
2004-09-07 16:26:42.036 TextEdit[4523] type CorePasteboardFlavorType 0x7…
2004-09-07 16:26:42.036 TextEdit[4523] type CorePasteboardFlavorType 0x7…
2004-09-07 16:26:42.049 TextEdit[4523] type CorePasteboardFlavorType 0x5…
2004-09-07 16:26:42.050 TextEdit[4523] type CorePasteboardFlavorType 0x7…
[…]

NSAccessibilityDebugLogLevel環境設定は、アクセシビリティ操作時にAppKitが実行するログ記録の頻度を制御する数字です。値が大きくなるほど、ログの頻度が高くなります。現在のところ、値3が最大有効値です。リスト49に、これを設定する方法、およびUIElementInspector(サンプルコードプロジェクト「UIElementInspector」を参照)を使用してテキストエディットを見る際に得られる出力の一例を示します。

リスト49:NSAccessibilityDebugLogLevelの使用

$ /Applications/TextEdit.app/Contents/MacOS/TextEdit -NSAccessibilityDebugLogLevel 3
2004-09-07 16:36:34.990 TextEdit[4526] creating id<=element table
2004-09-07 16:36:34.990 TextEdit[4526] creating id=>element
2004-09-07 16:36:35.001 TextEdit[4526] Element<=>UniqueId Tables
--- id<=element --- size 1
42 <- (0x1576e0): <NSTextView: 0x1576e0>
    Frame = {{0.00, 0.00}, {460.00, 395.00}}, Bounds = {{0.00, 0.00}, …
    Horizontally resizable: NO, Vertically resizable: YES
    MinSize = {460.00, 395.00}, MaxSize = {340282346638528859811704183…
2004-09-07 16:36:35.003 TextEdit[4526] Element<=>UniqueId Tables
[…]

先頭に戻る

ドキュメント改訂履歴

日付メモ
2006-07-25
2006-04-11CFZombieLevelに関する警告を追加。リスト20のバグを修正。
2006-03-31PowerPCおよびインテルベースのコンピュータ用のMac OS X 10.4.xに関する一般的な更新。標準C++ライブラリ、launchd、lookupd、Disk Utility、Core Graphics、Objective-Cランタイムをカバーした新しいセクションを追加。
2004-12-02Mac OS Xデバッグのヒントとこつに関する修正。

掲載日: 2006-07-25




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.