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

Technical Note TN2124
Mac OS X Debugging Magic

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





はじめに

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

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

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

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

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

このテクニカルノートでは、高度なデバッグテクニックを取り上げています。 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 における環境変数の設定

Figure 1, Setting environment variables in 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/tmp/do_xnc_log を作成すると、CFNotificationCenter によって、すべての通知に関する情報が /var/tmp/xnc_logs/ ディレクトリ内のファイルに記録されます。

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

$ touch /var/tmp/do_xnc_log

[... ここでいったんログアウトし、再度ログインします ...]

$ cat /var/tmp/xnc_logs/loginwindow
-------------------------------------------------
[0xa000a1ec] created notification center 0x40ccb0 0
[0xa000a1ec] register 0x40ccb0 _NSAppleEventManagerDidFailToDispatchNoti… 
[...]

先頭に戻る

呼び出し可能ルーチン

多くのシステムフレームワークには、デバッグ情報を 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…

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

先頭に戻る

デバッグ出力の確認

デバッグ出力を生成するプログラムは一般に、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」セクションで説明している方法を使用します。

先頭に戻る

CrashReporter

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

先頭に戻る

BSD

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

コアダンプ

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

システム全体でコアダンプを有効にするには、/etc/hostconfig で行 "COREDUMPS=-NO-" を "COREDUMPS=-YES-" に変更して再起動します。 または、「ターミナル」からプログラムを実行する場合は、あらかじめシェルでコアダンプのサイズ制限を簡単に引き上げることができます。リスト 9 にその一例を示します。

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

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

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

リスト 10: 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 を指定すれば、コアダンプをデバッグできます。リスト 11 に、この工程の一例を示します。

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

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 ?? ()

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

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

(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 ?? ()

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

先頭に戻る

メモリアロケータ

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

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

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

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

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

リスト 13: 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 が含まれています。保護メモリアロケータをデバッグ時に使用してメモリに関する一般的な問題(バッファオーバーランや解放後の使用など)を検出できます。 リスト 14 に、これを有効にする方法を例示します。

リスト 14: libgmalloc の有効化

$ gdb /Applications/TextEdit.app
GNU gdb 5.3-20030128 (Apple version gdb-330.1) [...]
(gdb) set env DYLD_INSERT_LIBRARIES /usr/lib/libgmalloc.dylib
(gdb) set env DYLD_FORCE_FLAT_NAMESPACE 1
(gdb) set env MALLOC_FILL_SPACE 1
(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-8
Reading symbols for shared libraries [...]

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

先頭に戻る

コマンドラインツール

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

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

ツール資料要約
gdbman ページコマンドラインデバッガ
fs_usageman ページファイルシステムトレースツール
sc_usageman ページシステムコールトレースツール
latencyman ページスケジューリングレイテンシデバッグツール
ktracekdumpman ページカーネルのトレース
heapman ページヒープダンプ
vmmapman ページアドレス空間ダンプ
malloc_historyman ページメモリ割り当て履歴
leaksman ページリーク検出
tcpdumpman ページTechnical 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_IMAGE_SUFFIX は、次のセクションで述べるように、システムのデバッグライブラリを有効にするのに使用できるため、最も便利です。

デバッグライブラリ

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」に設定します。 リスト 15 に、「ターミナル」からこれを実行する方法の一例を示します。

リスト 15: _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 でデバッグライブラリを有効化

Figure 2, Enabling debug libraries in Xcode

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

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

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

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

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

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

先頭に戻る

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

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

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

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

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

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

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

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

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

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

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

先頭に戻る

ウインドウサーバの回避

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

  • 「システム環境設定」の「アカウント」パネルで、「ログインオプション」をクリックします。

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

  • ログアウトします。

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

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

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

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

先頭に戻る

Core Services

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

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

Figure 3, Setting USERBREAK in Xcode

Code Fragment Manager(CFM)

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

先頭に戻る

Core Foundation

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

リスト 18: 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 からの出力が表示されない場合は、おそらくコンソールに送られています。 この出力を表示する方法については、「デバッグ出力の確認」を参照してください。

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

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

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

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

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

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

CFNotificationCenter

CFNotificationCenter ログを有効にするには、/var/tmp にファイルを作成します。 /var/tmp/do_xnc_log という 1 つのファイルでクライアント側ログを制御します。 このファイルを作成すると、CFNotificationCenter が各クライアントのアクティビティをファイル(/var/tmp/xnc_logs/<プログラム名>)に記録します。 この設定は、プログラムを再起動すると有効になります。

/var/tmp/do_xnc_log というもう 1 つのファイルでサーバ側ログを制御します。 このファイルを作成すると、CFNotificationCenter デーモン(distnoted)がアクティビティを /var/tmp/dnserver.log に記録します。 この変更は、distnoted を再起動すると有効になります。これを実行する最も安全な方法はシステムを再起動することです。

先頭に戻る

Component Manager

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

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

先頭に戻る

File Manager

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

リスト 19: 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

この他にも、PrintFileIDTreePrintEnumerationCache という 2 つのルーチンがありますが、これらは File Manager が維持している非 volfs ボリュームの互換性構造に関する情報を出力します(volfs については、テクニカル Q&A の QA1113「The "/.vol" directory and "volfs"」 を参照)。 どちらも vRefNum パラメータを取ります。 PrintFileIDTree は、ファイル ID からボリューム上のファイルパスへのマッピングを出力します。 PrintEnumerationCache は、ボリューム上のディレクトリ列挙キャッシュを出力します。 この 2 つのルーチンは主として、Mac OS X 上の VFS プラグインを開発している人々にとって興味あるものです。

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

先頭に戻る

Folder Manager

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

先頭に戻る

Gestalt

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

リスト 20: 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.plist の Command プロパティに「-d」を付加してシステムを再起動すると、Disk Arbitration のアクティビティに関する詳細な情報が /var/log/diskarbitrationd.log に記録されます。

先頭に戻る

Disc Recording

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

先頭に戻る

出力(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 を送信する必要があります。リスト 21 に、その方法を 1 つ示します。

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

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

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

リスト 22: 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");

先頭に戻る

ApplicationServices

アップルイベント

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

リスト 23: 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 を作成してデバッグ出力を有効にします。

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

リモートアップルイベントに関する問題がある場合は、アップルイベントサーバプロセスでログ記録を有効にすると便利なこともあります。 これを行うには、/etc/xinetd.d/eppc を編集して、「server_args」行をコメント解除し、パラメータ値を「--debug」に変更します。 その最終結果は、リスト 24 に示すようなファイルになります。

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

# EPPC はリモート AppleEvents サービスです
#
service eppc
{
        disable             = no
        socket_type         = stream
        wait                = no
        user                = eppc
        server              = /System/Library/Frameworks/\
ApplicationServices.framework/Frameworks/AE.framework/\
Versions/A/Support/AEServer
        server_args         = --debug
        groups              = yes
        flags               = REUSE
}

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

重要: AEServerxinetd の要求に応じて起動されるため、実際にアップルイベントをマシンに送信するまで、対応するログエントリは記録されません。

先頭に戻る

Process Manager

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

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

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

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

先頭に戻る

QuickDraw

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

リスト 26: 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) echo 0x1FE730 is "Vis rgn" from QDDebugPrintPortInfo\n
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

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

リスト 27: 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 オブジェクト出力ルーチンを示します。

リスト 28: 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) echo 0x489D10 is the kEventWindowUpdate event from last command\n
0x489D10 is the kEventWindowUpdate event from last command
(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<

リスト 29: 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) echo 0x0042EC00 is the QuickTime Player menu\n
0x0042EC00 is the QuickTime Player menu
(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
[…]

リスト 30: 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) echo 0x004350A0 is the "Untitled 1" window\n
0x004350A0 is the "Untitled 1" window
(gdb) set $window=0x004350A0
(gdb) echo 0x004A3C10 is the "Untitled 1 Properties" dialog\n
0x004A3C10 is the "Untitled 1 Properties" dialog
(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) echo 0x76E47A89 is the second window group\n
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)

リスト 31: 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) echo 0x4c24d0 is first scrollbar control\n
echo 0x4c24d0 is first scroll bar control
(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

先頭に戻る

HIToolbox リージョンの点滅

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

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

(gdb) call (void) DebugFlashWindowVisRgn(0x004351E0)
(gdb) call (void) DebugFlashWindowUpdateRgn(0x004351E0)

先頭に戻る

HIToolbox イベントデバッグ

Carbon イベントの登場で、ツールボックスにおけるイベントの流れが理解しにくいことがよくあります。 HIToolbox は、これを支援する 2 つのデバッグ機能を提供します。

EventDebug 環境変数

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

リスト 33: 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 ルーチンを呼び出します。 リスト 34 に、その一例を示します。この例では、kEventRawKeyDown イベントのトレースだけを有効にします。

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

(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 でもサポートされています。

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

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

先頭に戻る

Cocoa

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

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

$ gdb /Applications/TextEdit.app
GNU gdb 5.3-20030128 (Apple version gdb-330.1) […]
(gdb) fb *'-[NSDictionary hash]'
No symbol table is loaded.  Use the "file" command.
Breakpoint 1 at 0x0
(gdb) r
[…]
Breakpoint 1, 0x90a233d8 in -[NSDictionary hash] ()
(gdb) po $r3
<NSAttributeDictionary 0x159330>{
  NSParagraphStyle = Alignment 4, LineSpacing 0, \
ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, \
TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, \
LineHeightMultiple 0, LineBreakMode 2, Tabs (28L, 56L, 84L, \
112L, 140L, 168L, 196L, 224L, 252L, 280L, 308L, 336L), \
DefaultTabInterval 0;
  NSColor = NSCalibratedWhiteColorSpace 1 1;
  NSFont = "CGS LucidaGrande 13.00 pt. P [] (0x00158af0) \
fobj=0x00156f50, spc=4.11";
  _NSOriginalFontAttributeName = "CGS LucidaGrande 13.00 pt. \
P [] (0x00158af0) fobj=0x00156f50, spc=4.11";
}

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

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

  • Objective-C コンパイラは、2 つの暗黙のパラメータを各メソッドに追加します。 最初のパラメータは、メッセージの送信先オブジェクトへのポインタ(self)です。 PowerPC では、これはレジスタ r3 にあります。

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

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

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

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

リスト 36: 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) echo Set a breakpoint on the Objective-C method dispatcher.\n
Set a breakpoint on the Objective-C method dispatcher.
(gdb) b objc_msgSend
Breakpoint 1 at 0x908311f4
(gdb) echo Continue execution...\n
Continue execution...
(gdb) c
Continuing.

Breakpoint 1, 0x908311f4 in objc_msgSend ()
(gdb) echo Hit the breakpoint; dump the first 4 words of the object.\n
Hit the breakpoint; dump the first 4 words of the object.
(gdb) x/4x $r3
0x10cc10:       0xa0a04e18      0x00000001      0x00000000      0x00000000
(gdb) echo Print the selector.\n
Print the selector.
(gdb) x/s $r4
0x9083ed94 <_errDoesntRecognize+884>:    "init"
(gdb) echo Want to 'po' object; must disable the breakpoint first.\n
Want to 'po' object; must disable the breakpoint first.
(gdb) dis 1
(gdb) po $r3
<NSAutoreleasePool: 0x10cc10>
(gdb) echo Print the 'isa' pointer.\n
Print the 'isa' pointer.
(gdb) po 0xa0a04e18
NSAutoreleasePool

Foundation

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

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

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

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

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

リスト 37: 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 のスクリプティングサポートに関するログを有効にできます。 リスト 38 に、その一例を示します。表示されたログは、単純な AppleScript: version of application "TextEdit" を実行した結果です。

リスト 38: NSScriptingDebugLogLevel の有効化

$ defaults write com.apple.TextEdit NSScriptingDebugLogLevel YES
$ /Applications/TextEdit.app/Contents/MacOS/Te…
[…]
2004-09-07 15:57:21.449 TextEdit[4417] Command: NSCoreSuite.Get
        Direct Parameter: <NSPropertySpecifier: version>
        Receivers: <NSPropertySpecifier: version>
        Arguments: {}
2004-09-07 15:57:21.449 TextEdit[4417] Property Value: 1.3
2004-09-07 15:57:21.449 TextEdit[4417] Result: <NSAppleEventDescriptor:  …
[…]

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 は処理するすべてのイベントに関する情報を記録します。リスト 39 にその一例を示します。

リスト 39: NSTraceEvents の使用

$ defaults write com.apple.TextEdit NSTraceEvents YES
$ /Applications/TextEdit.app/Contents/MacOS/TextEdit
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 を有効にしたテキストエディット

Figure 4, TextEdit with NSShowAllViews enabled

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

図 5: NSShowAllDrawing による動作

Figure 5, NSShowAllDrawing in action

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

先頭に戻る

その他の AppKit デバッグ機能

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

リスト 40: NSDragManagerLogLevel の使用

$ defaults write com.apple.TextEdit NSDragManagerLogLevel 6
$ /Applications/TextEdit.app/Contents/MacOS/TextEdit
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 が最大有効値です。 リスト 41 に、これを設定する方法、および UIElementInspector(Sample Code Project 'UIElementInspector' を参照)を使用してテキストエディットを見る際に得られる出力の一例を示します。

リスト 41: NSAccessibilityDebugLogLevel の使用

$ defaults write com.apple.TextEdit NSAccessibilityDebugLogLevel 3
$ /Applications/TextEdit.app/Contents/MacOS/TextEdit
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
[…]

先頭に戻る

ドキュメントの改訂履歴

日付メモ
2004-12-02初版

掲載日: 2004-12-02