EN JA
PTRACE(2)
PTRACE(2) FreeBSD System Calls Manual PTRACE(2)

名称

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

ライブラリ

Standard C Library (libc, -lc)

書式

#include < sys/types.h>
#include < 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 または対応するスレッド ID を指定します。 request 引数は、次のものを指定できます:

PT_TRACE_ME
この要求は、トレースされるプロセスが使用する唯一の要求です。この要求は、プロセスがその親によってトレースされることを宣言します。他の引数は、すべて無視されます (親プロセスが子プロセスをトレースしない場合は、かなり混乱した結果になります。トレースされるプロセスが停止すると、このプロセスは、 ptrace() によってしか実行を継続できません)。プロセスがこの要求を使用し、 execve(2) またはそれに組み込まれているルーチン (たとえば、 execv(3)) を呼び出した場合、そのプロセスは、新しいイメージの最初の命令を実行する前に停止します。また、実行される実行可能モジュールの setuid または setgid ビットは、無視されます。子プロセスが vfork(2) システムコールまたは RFMEM が指定された rfork(2) 呼び出しによって作成されるなら、デバックイベントは、 execve(2) が実行された後だけ、親プロセスに報告されます。
PT_READ_I, PT_READ_D
これらの要求は、トレースされるプロセスのアドレス空間から単一の int のデータを読み込むます。伝統的に、 ptrace() は、なぜ、2 つの要求があるかの理由ですが、命令とデータのための区別されたアドレス空間があるマシンに対して許可されていました。概念的に、 PT_READ_I は、命令空間から読み込み、 PT_READ_D は、データ空間から読み込みます。現在の FreeBSD の実装では、これらの 2 つの要求は、完全に同一です。 addr 引数は、読み込みが行われることになっている (トレースされたプロセスの仮想アドレス空間の) アドレスを指定します。このアドレスは、あらゆる整列制約を満たす必要はありません。読み込む値は、 ptrace() から返された値として返されます。
PT_WRITE_I, PT_WRITE_D
これらの要求は、それらが読み込みではなく書き込むことを除いて、 PT_READ_IPT_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 命令ごとに 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; 
 int pl_event; 
 int pl_flags; 
 sigset_t pl_sigmask; 
 sigset_t pl_siglist; 
 siginfo_t pl_siginfo; 
 char pl_tdname[MAXCOMLEN + 1]; 
 int pl_child_pid; 
};

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

struct ptrace_lwpinfo のフィールドには、次の意味があります:

pl_lwpid
スレッドの LWP ID
pl_event
停止を引き起こしたイベント。現在定義されたイベントは、次の通りです:
PL_EVENT_NONE
与える理由なし
PL_EVENT_SIGNAL
スレッドは、保留中 (pending) のシグナルのために停止しました
pl_flags
観測される停止に関する追加の詳細を指定するフラグ。現在定義されたフラグは、次の通りです:
PL_FLAG_SCE
スレッドは、カーネルに入れられた直後に、システムコールエントリのために停止しました。デバッガは、現在のプロセスの ABI にしたがってメモリとレジスタに格納される syscall 引数を調べて、必要なら、それらを変更します。
PL_FLAG_SCX
スレッドは、syscall がユーザモードに戻る直前に停止されました。デバッガは、ABI 定義のレジスタおよびメモリのシステムコールリターン値を調べるかもしれません。
PL_FLAG_EXEC
PL_FLAG_SCX が設定されるとき、プログラムが execve( 2) ファミリからシステムコールの実行が成功することによって変更された debuggee プロセスによって実行されていることを知らせるために、このフラグを、さらに指定します。
PL_FLAG_SI
struct ptrace_lwpinfopl_siginfo メンバが有効な情報を含むことを示します。
PL_FLAG_FORKED
プロセスが、新しい子プロセスを作成した fork( 2) への呼び出しから戻っていることを示します。新しいロセスのプロセス識別子は、 struct ptrace_lwpinfopl_child_pid メンバで利用可能です。
PL_FLAG_CHILD
フラグは、有効にされた PT_FOLLOW_FORK によって自動的にアタッチされる、新しい子プロセスから報告される最初のイベントのために設定されます。
pl_sigmask
LWP の現在のシグナルマスク
pl_siglist
LWP のための現在保留中のセットのシグナル。プロセスに配信されるシグナルは、スレッドが配信のために選択されるまで、 LWP siglist に現れないことに注意してください。
pl_siginfo
保留中のシグナルに伴う siginfo。 PL_FLAG_SIpl_flags に設定されているとき、 PL_EVENT_SIGNAL 停止のためだけに有効です。
pl_tdname
スレッドの名前。
pl_child_pid
新しい子プロセスのプロセス識別子。 PL_FLAG_FORKEDpl_flags に設定されているとき、 PL_EVENT_SIGNAL 停止のためだけに有効です。
PT_GETNUMLWPS
この要求は、トレースされたプロセスに関連しているカーネルスレッドの数を返します。
PT_GETLWPLIST
現在のスレッドのリストを取得するために、この要求を使用することができます。タイプ lwpid_t の配列へのポインタは、 data によって指定された配列サイズがある addr で渡されるべきです。 ptrace() からの返り値は、書き込まれた配列エントリの数です。
PT_SETSTEP
この要求は、指定されたプロセスの単一ステップ動作をオンにします。
PT_CLEARSTEP
この要求は、指定されたプロセスの単一ステップ動作をオフにします。
PT_SUSPEND
この要求は、指定されたスレッドをサスペンドします。
PT_RESUME
この要求は、指定されたスレッドを再開します。
PT_TO_SCE
この要求は、各システムコールの入口で指定されたプロセスをトレースします。
PT_TO_SCX
この要求は、各システムコールの出口で指定されたプロセスをトレースします。
PT_SYSCALL
この要求は、各システムコールの入口と出口で指定されたプロセスをトレースします。
PT_FOLLOW_FORK
この要求は、トレースされたプロセスの新しい子プロセスのトレースを制御します。 data が 0 以外であるなら、新しい子プロセスは、トレースを有効にして、それらの最初の命令を実行する前に、停止します。 data が 0 であるなら、新しい子プロセスは、有効にされたトレースなしで実行されます。デフォルトで、トレースは、新しい子プロセスのために有効にされません。子プロセスは、この特性を引き継ぎません。トーレスされたプロセスは、新しいプロセスを作成するシステムコールからの出口で PL_FLAG_FORKED フラグを設定します。
PT_VM_TIMESTAMP
この要求は、 ptrace() から返り値としてトレースされたプロセスのメモリマップの世代 (generation) 番号またはタイムスタンプを返します。これは、VM マップが、この要求が行われた最後の時から変更されたかどうか決定するためにトレースするプロセスのための安価な方法を提供しています。
PT_VM_ENTRY
この要求は、トレースされたプロセスの VM マップのエントリを繰り返すために使用されます。 addr 引数は、次の通り定義される struct ptrace_vm_entry へのポインタを指定します:

struct ptrace_vm_entry { 
 int  pve_entry; 
 int  pve_timestamp; 
 u_long  pve_start; 
 u_long  pve_end; 
 u_long  pve_offset; 
 u_int  pve_prot; 
 u_int  pve_pathlen; 
 long  pve_fileid; 
 uint32_t pve_fsid; 
 char  *pve_path; 
};

最初のエントリは、 pve_entry を 0 に設定することによって返されます。それに続くエントリは、前の要求によって返された値から変更されないままの pve_entry が返されます。エントリで繰り返えされている間に、VM マップへの変更を検出するために pve_timestamp フィールドを使用することができます。そして、トレースしているプロセスは、再開などのような適切なアクションを取ることができます。エントリの pve_pathlen を 0 以外に設定することによって、支援されたオブジェクトのパス名は、 vnode によって支援される提供されたエントリである。 pve_path によって指されたバッファに返されます。 pve_pathlen フィールドは、(終端のヌル文字を含んで) パス名の実際の長さに更新されます。 pve_offset フィールドは、範囲が始まる支援されたオブジェクト内のオフセットです。範囲は、 pve_start の VM 空間に位置して pve_end まで広がっています (包括的)。

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 が、有効なレジスタセットを設定せずに使用されました (これが真になるのは、通常、システムプロセスについてのみです)。
  • PT_VM_ENTRY は、 pve_entry のための無効の値が与えられました。これは、また、プロセスの VM マップへの変更によって引き起こされるかもしれません。
  • PT_LWPINFO に提供される ( data の) サイズは、0 以下か、またはカーネルに既知の ptrace_lwpinfo 構造体より大きくなります。
[ EBUSY]
  • PT_ATTACH が既にトレース中のプロセスについて使用されました。
  • 要求をしているプロセス以外のプロセスによって、トレースされるプロセスを操作しようとする要求が試みられました。
  • 要求 ( PT_ATTACH 以外の) が停止されていないプロセスを指定しました。
[ EPERM]
  • 要求 ( PT_ATTACH 以外の) が、まったくトレースされていないプロセスを操作しようとしました。
  • 前述の PT_ATTACH で説明した条件を満たさないプロセスについて PT_ATTACH を使おうとしました。
[ ENOENT]
  • PT_VM_ENTRY は、以前に返された、メモリマップの最後のエントリです。それ以上のエントリが存在していません。
[ ENAMETOOLONG]
  • PT_VM_ENTRY は、バッファが十分に大きくないので、支援されたオブジェクトのパス名を返すことができません。 pve_pathlen は、戻り時に必要であった最小のバッファサイズを保持しています。

歴史

ptrace() 関数は、 Version 7 AT&T UNIX で登場しました。
July 22, 2013 FreeBSD