PTRACE(2)

名称
ptrace - プロセスのトレースとデバッグ

ライブラリ
libc

書式
<sys/types.h> <sys/ptrace.h> int ptrace(int request, pid_t pid, caddr_t addr, int data)

解説
ptrace() システムコールはトレースとデバッグの機能を提供します。 これによって 1 つのプロセス (トレースするプロセス) が他のプロセス (トレースされるプロセス) を制御できます。 トレースするプロセスは、最初にトレースされるプロセスにアタッチ しなければなりません。 それから、そのプロセスの実行を制御するために ptrace() システムコールを発行します。 プロセスのメモリおよびレジスタ状態へのアクセスも同様です。 トレースするセッションの期間中、トレースされるプロセスは その親プロセス ID が (その結果の動作として) トレースするプロセスに “親を変更” されます。 トレースするプロセスが同時に 1 つ以上のプロセスをアタッチすることは、 許可されます。 トレースするプロセスがその作業を完了したときには、そのトレースされる プロセスをデタッチしなければなりません。 トレースするプロセスが最初にアタッチされている全てのプロセスをデタッチ せずに exit した場合には、それらのプロセスは kill されます。

ほとんどの場合、トレースされるプロセスは正常に実行されます。 ただし、トレースされるプロセスはシグナル ((sigaction) 2 を参照) を受信すると停止します。 トレースするプロセスは、 wait(2) または SIGCHLD シグナルによってこれを検知し、停止されたプロセスの状態を調査して、 それを終了させるか、または適切な形で実行を継続させます。 そのシグナルは、トレースされるプロセスの動作の結果として生成されたか、 kill(2) システムコールの使用による、通常のプロセスのシグナルかもしれません。 もしくは、アタッチ、システムコール、 またはトレースするプロセスによるステップの結果、 トレースする仕組みによって生成されたものかもしれません。 トレースしているプロセスは、そのシグナルを使用してプロセスの動作を観測する ために (SIGTRAP の様に) そのシグナルを横取りするか、もし適切であればそのシグナルを そのプロセスに転送するかを、選択できます。 ptrace() システムコールは、これらすべてを制御するメカニズムです。

request 引数は、どの操作を実行するかを指定します。 残りの引数の意味は操作によって異なります。 後述する 1 つの特殊なケースを除いて、 ptrace() 呼び出しはすべてトレースするプロセスによって行われ、 pid 引数はトレースされるプロセスのプロセス ID を指定します。 request 引数は次のものにできます。

PT_TRACE_ME
    この要求は、トレースされるプロセスが使用する唯一の要求です。 この要求は、プロセスがその親によってトレースされることを宣言します。 他の引数はすべて無視されます (親プロセスが子プロセスをトレースしない場合は、 かなり混乱した結果になります。 トレースされるプロセスが停止すると、このプロセスは、 ptrace() によってしか実行を継続できません)。 プロセスがこの要求を使用し、 execve(2) またはそれに組み込まれているルーチン (たとえば、 execv(3)) を呼び出した場合、 そのプロセスは新しいイメージの最初の命令を実行する前に停止します。 また、実行される実行可能モジュールの setuid または setgid ビットは無視されます。

PT_READ_I, PT_READ_D
    これらの要求は、トレースされるプロセスのアドレス空間から 1 つの int データを読み取ります。 従来、 ptrace() は命令とデータについて区別されたアドレス空間のある マシンを許容していました。 これが 2 つの要求がある理由です。 概念的には、 PT_READ_I が命令空間から読み取り、 PT_READ_D がデータ空間から読み取ります。 現在の FreeBSD システムでは、これらの 2 つの要求は完全に同一です。 addr 引数が、読取りが行われる (トレースされるプロセスの仮想アドレス空間内の) アドレスを指定します。 このアドレスはどのような境界調整制約も満たす必要はありません。 読み取られた値は ptrace() からの戻り値として返されます。

PT_WRITE_I, PT_WRITE_D
    これらの要求は PT_READ_I および PT_READ_D と同様ですが、読み取るのではなく書き込むところが異なります。 data 引数で書き込まれる値を指定します。

PT_IO
    この要求によって、トレースされるプロセスのアドレス空間内にある 任意量のデータの読取りと書込みが有効となります。 addr 引数は、以下のように定義される struct ptrace_io_desc へのポインタを指定します。

        struct ptrace_io_desc {
        	int	piod_op;	/* I/O 操作 */
        	void	*piod_offs;	/* 子オフセット */
        	void	*piod_addr;	/* 親オフセット */
        	size_t	piod_len;	/* 要求の長さ */
        };
        /*
         * piod_op で指定される操作
         */
        #define PIOD_READ_D	1	/* データ空間からの読取り */
        #define PIOD_WRITE_D	2	/* データ空間への書込み */
        #define PIOD_READ_I	3	/* 命令空間からの読取り */
        #define PIOD_WRITE_I	4	/* 命令空間への書込み */

    data 引数は無視されます。 実際に読み書きされたバイト数は、上記の piod_len に入れられ返されます。

PT_CONTINUE
    トレースされるプロセスは実行を継続します。 addr 引数は、実行が再開される場所 (プログラムカウンタの新しい値)、 または実行が停止されたところで再開されることを示す (caddr_t)1 を指定します。 data 引数には、トレースされるプロセスが実行を再開するときに受信するシグナル番号、 またはシグナルを送信しない場合には 0 を指定します。

PT_STEP
    トレースされるプロセスは 1 命令ずつステップ実行されます。 addr 引数には (caddr_t)1 を渡す必要があります。 data には実行の再開のために、トレースされたプロセスへ配信されるべきシグナルの数を 与えます。 または、送られるべきシグナルが無い場合には 0 を与えます。

PT_KILL
    トレースされるプロセスは、あたかも SIGKILL を配信シグナルとして PT_CONTINUE が使用されたかのように、終了します。

PT_ATTACH
    この要求は、他の無関係なプロセスの制御を取得し、 そのトレースを開始します。 トレースされるプロセスからの協力は必要としません。 このケースでは、 pid にトレースされるプロセスのプロセス ID を指定し、 他の 2 つの引数は無視されます。 この要求では、ターゲットプロセスがトレースする プロセスと同じ実 UID を持つこと、 それが setuid または setgid された実行可能モジュールでないことが 要求されます (トレースするプロセスが root として実行されている場合、 これらの制約は適用されません)。 トレースするプロセスは、新たにトレースされるプロセスを停止させ、 あたかも最初からずっとトレースしていたかのように制御できます。

PT_DETACH
    この要求は PT_CONTINUE と類似していますが、実行を継続する別の場所を 指定できないこと、および要求が成功した後、 トレースされていたプロセスはもはやトレースされず、 通常どおり実行を継続することが異なります。

PT_GETREGS
    この要求は、トレースされるプロセスのマシンレジスタを、 addr が指す “ struct reg ” <(> machine/reg.h 内に定義されています) 内に読み取ります。

PT_SETREGS
    この要求は PT_GETREGS の逆です。 addr が指す “ struct reg ” <(> machine/reg.h 内に定義されています) からトレースされるプロセスのマシンレジスタをロードします。

PT_GETFPREGS
    この要求はトレースされるプロセスの浮動小数点レジスタを addr が指す “ struct fpreg ” <(> machine/reg.h に定義されています) に読み取ります。

PT_SETFPREGS
    この要求は PT_GETFPREGS の反対です。 addr が指す “ struct fpreg ” <(> machine/reg.h 内に定義されています) からトレースされるプロセスの浮動小数点レジスタをロードします。

PT_GETDBREGS
    この要求はトレースされるプロセスのデバッグレジスタを addr が指す “ struct dbreg ” <(> machine/reg.h 内に定義されています) に読み取ります。

PT_SETDBREGS
    この要求は PT_GETDBREGS の反対です。 addr の指す “ struct dbreg ” <(> machine/reg.h 内に定義されています) からトレースされるプロセスのデバッグレジスタにロードします。

PT_LWPINFO
    この要求は、トレースされるプロセスが停止する原因となった (軽量プロセスとしても知られる) カーネルスレッドについての情報を 獲得するために使用されます。 addr 引数は以下のように定義される struct ptrace_lwpinfo へのポインタを指定します:

        struct ptrace_lwpinfo {
        	lwpid_t	pl_lwpid;	/* LWP 識別子 */
        	int	pl_event;	/* 受け取ったイベント */
        };

    data 引数は、呼び出し側に知られている構造体の大きさに設定されます。 これにより、古いプログラムに影響を与えること無しに、 この構造体を大きくすることができます。

さらにマシン固有の要求が存在することがあります。

戻り値
いくつかの要求で ptrace() はエラー以外の場合にも -1 を返します。 曖昧さをなくすためには、呼び出しの前に errno を 0 に設定し、後でチェックします。

エラー
ptrace() システムコールは次の場合に処理に失敗します。

[ESRCH]
    •   指定されたプロセス ID を持つプロセスが存在しません。

[EINVAL]
    •   プロセスが自分自身に対して PT_ATTACH を使おうとしました。
    •   request 引数が正しい要求の 1 つではありませんでした。
    •   PT_CONTINUE へのシグナル番号 (data) が 0 でないか、または正しいシグナル番号ではありませんでした。
    •   PT_GETREGS, PT_SETREGS, PT_GETFPREGS, PT_SETFPREGS, PT_GETDBREGS または PT_SETDBREGS が、有効なレジスタセットを設定せずに使用されました (これが真になるのは、通常、システムプロセスについてのみです)。

[EBUSY]
    •   PT_ATTACH が既にトレース中のプロセスについて使用されました。
    •   要求をしているプロセス以外のプロセスによって、トレースされるプロセスを 操作しようとする要求が試みられました。
    •   要求 (PT_ATTACH 以外の) が停止されていないプロセスを指定しました。

[EPERM]
    •   要求 (PT_ATTACH 以外の) が、まったくトレースされていないプロセスを操作しようとしました。
    •   前述の PT_ATTACH で説明した条件を満たさないプロセスについて PT_ATTACH を使おうとしました。

関連項目
execve(2), sigaction(2), wait(2), execv(3), i386_clr_watch(3), i386_set_watch(3)

歴史
ptrace() 関数は AT&T UNIX で登場しました。