プログラミングインタフェース

libcpc に追加された API

この節では、libcpc(3LIB) ライブラリに最近追加された API について説明します。以前のインタフェースについては、マニュアルページの libcpc を参照してください。

初期化インタフェース

アプリケーションが CPC 機能を使用できるように準備するには、cpc_open() 関数を呼び出してライブラリを初期化します。この関数は、ほかのインタフェースで使用される cpc_t * パラメータを返します。cpc_open() 関数の構文は、次のとおりです。

cpc_t*cpc_open(intver);

ver パラメータの値は、アプリケーションが使用中のインタフェースのバージョンを識別します。配下のカウンタがアクセス不可または使用不可の場合、cpc_open() 関数は失敗します。

ハードウェア照会インタフェース

uint_t cpc_npic(cpc_t *cpc);
uint_t cpc_caps(cpc_t *cpc);
void cpc_walk_events_all(cpc_t *cpc, void *arg,
          void (*action)(void *arg, const char *event));
void cpc_walk_events_pic(cpc_t *cpc, uint_t picno, void *arg, 
          void(*action)(void *arg, uint_t picno, const char *event));
void cpc_walk-attrs(cpc_t *cpc, void *arg,
          void (*action)(void *arg, const char *attr));

cpc_npic() 関数は、配下のプロセッサ上の物理カウンタ数を返します。

cpc_caps() 関数は、uint_t パラメータを返します。そのパラメータ値は、配下のプロセッサがサポートする機能に対して実行されたビット単位の論理和操作の結果です。この関数には 2 つの機能があります。CPC_CAP_OVERFLOW_INTERRUPT 機能により、カウンタのオーバーフロー時に、プロセッサは割り込みを発生できます。CPC_CAP_OVERFLOW_PRECISE 機能により、プロセッサはどのカウンタがオーバーフローの割り込みを発生したかを判別できます。

カーネルは、配下のプロセッサがサポートするイベントのリストを管理します。単一チップ上の異なる物理カウンタが同じイベントのリストを使用する必要はありません。cpc_walk_events_all() 関数は、物理カウンタに関係なく、プロセッサがサポートするイベントごとにaction() ルーチンを呼び出します。cpc_walk_events_pic() 関数は、特定の物理カウンタで、プロセッサがサポートするイベントごとにaction() ルーチンを呼び出します。どちらの関数も、 arg パラメータを非解釈で呼び出し元から各 action() 関数の呼び出しに渡します。

プラットフォームは、配下のプロセッサがサポートする属性のリストを管理します。これらの属性によって、性能カウンタのプロセッサ固有の拡張機能にアクセスできます。cpc_walk_attrs() 関数は、属性名ごとにアクションルーチンを呼び出します。

設定インタフェース

cpc_set_t *cpc_set_create(cpc_t *cpc);
int cpc_set_destroy(cpc_t *cpc, cpc_set_t *set);
int cpc_set_add_request(cpc_t *cpc, cpc_set_t *set, const char *event,
          uint64_t preset, uint_t flags, uint_t nattrs,
          const cpc_attr_t *attrs);
int cpc_set_request_preset(cpc_t *cpc, cpc_set_t *set, int index,
          uint64_t preset);

不透明なデータ型 cpc_set_t は要求のコレクションを表します。これらのコレクションはセットと呼ばれます。cpc_set_create() 関数は空のセットを作成します。cpc_set_destroy() 関数はセットを削除して、セットが使用していたメモリーをすべて解放します。セットを削除すると、そのセットが使用していたハードウェア資源が解放されます。

cpc_set_add_request() 関数は要求をセットに追加します。要求のパラメータは次のとおりです。

event

カウントのイベント名を指定する文字列。

preset

カウンタの初期値に使用される 64 ビットの符号なし整数。

flags

要求フラグのグループに適用される論理和操作の結果。

nattrs

attrs が指す配列内の属性の数。

attrs

cpc_attr_t 構造体の配列へのポインタ。

有効な要求フラグは次のとおりです。

CPC_COUNT_USER

このフラグを使用すると、CPU がユーザーモードで実行している間に発生するイベントをカウントできます。

CPC_COUNT_SYSTEM

このフラグを使用すると、CPU が特権モードで実行している間に発生するイベントをカウントできます。

CPC_OVF_NOTIFY_EMT

このフラグは、ハードウェアのカウンタオーバーフローの通知を要求します。

CPC インタフェースは、cpc_attr_t 構造体の配列として属性を渡します。

cpc_set_add_request() 関数が正常終了して戻った場合、この関数はインデックスを返します。インデックスは、cpc_set_add_request() 関数の呼び出しにより追加された要求が生成したデータを参照します。

cpc_set_request_preset() 関数は、事前に設定された要求の値を変更します。これによって、オーバーフローしたセットを新しい事前設定で再構築できます。

cpc_walk_requests() 関数は、ユーザーが提供した action() ルーチンを cpc_set_t 内の要求ごとに呼び出します。arg パラメータの値は、非解釈でユーザーのルーチンに渡されます。cpc_walk_requests() 関数を使用すると、アプリケーションはセット内の要求ごとに構成を出力できます。cpc_walk_requests() 関数の構文は、次のとおりです。

void cpc_walk_requests(cpc_t *cpc, cpc_set_t *set, void *arg,
void (*action)(void *arg, int index, const char *event,
uint64_t preset, uint_t flags, int nattrs,
            const cpc_attr_t *attrs));

バインド

この節のインタフェースは、セット内の要求を物理ハードウェアにバインドして、カウンタを開始位置に設定します。

int cpc_bind_curlwp(cpc_t *cpc, cpc_set_t *set, uint_t flags);
int cpc_bind_pctx(cpc_t *cpc, pctx_t *pctx, id_t id, cpc_set_t *set,
          uint_t flags);
int cpc_bind_cpu(cpc_t *cpc, processorid_t id, cpc_set_t *set, 
          uint_t flags);
int cpc_unbind(cpc_t *cpc, cpc_set_t *set);

cpc_bind_curlwp() 関数は、セットを呼び出し元の LWP にバインドします。セットのカウンタはこの LWP に仮想化され、呼び出し元の LWP の実行中に CPU で発生したイベントをカウントします。cpc_bind_curlwp() ルーチンで有効なフラグは CPC_BIND_LWP_INHERIT だけです。

cpc_bind_pctx() 関数は、セットを libpctx(3LIB) を使って得られたプロセス内の LWP にバインドします。この関数に有効なフラグはありません。

cpc_bind_cpu() 関数は、セットを id パラメータで指定されたプロセッサにバインドします。セットを CPU にバインドすると、システム上にある既存の性能カウンタのコンテキストが無効になります。この関数に有効なフラグはありません。

cpc_unbind() 関数は性能カウンタを停止して、バインドされたセットに関連付けられたハードウェアを解放します。セットが CPU にバインドされている場合、cpc_unbind() 関数は、CPU から LWP をバインド解除して、CPC 仮想デバイスを解放します。

抽出

この節で説明するインタフェースを使用すると、カウンタからアプリケーションにデータを返すことができます。カウンタデータは、cpc_buf_t という不透明なデータ構造体内にあります。このデータ構造体は、バインドされたセットが使用しているカウンタの状態についてのスナップショットを取得し、次の情報を含みます。

cpc_buf_t *cpc_buf_create(cpc_t *cpc, cpc_set_t *set);
int cpc_buf_destroy(cpc_t *cpc, cpc_buf_t *buf);
int cpc_set_sample(cpc_t *cpc, cpc_set_t *set, cpc_buf_t *buf);

cpc_buf_create() 関数は、 cpc_set_t で指定されたセットからデータを格納するバッファーを作成します。cpc_buf_destroy() 関数は、指定した cpc_buf_t に関連付けられたメモリーを解放します。cpc_buf_sample() 関数は、指定されたセットの代わりにカウントしているカウンタのスナップショットを取得します。cpc_buf_sample() 関数を呼び出す前に、指定されたセットはあらかじめバインドされ、バッファーが作成されている必要があります。

バッファーへの抽出では、そのセットに関連付けられた要求の事前設定を更新しません。cpc_buf_sample() 関数を使ってバッファーが抽出され、次にバインド解除してから再度バインドすると、cpc_set_add_request() 関数の元の呼び出し内の要求の事前設定からカウンタが開始します。

バッファー操作

次のルーチンにより、cpc_buf_t 構造体内のデータにアクセスできます。

int cpc_buf_get(cpc_t *cpc, cpc_buf_t *buf, int index, uint64_t *val);
int cpc_buf_set(cpc_t *cpc, cpc_buf_t *buf, int index, uint64_t *val);
hrtime_t cpc_buf_hrtime(cpc_t *cpc, cpc_buf_t *buf);
uint64_t cpc_buf_tick(cpc_t *cpc, cpc_buf_t *buf);
int cpc_buf_sub(cpc_t *cpc, cpc_buf_t *result, cpc_buf_t *left
      cpc_buf_t *right);
int cpc_buf_add(cpc_t *cpc, cpc_buf_t *result, cpc_buf_t *left,
      cpc_buf_t *right);
int cpc_buf_copy(cpc_t *cpc, cpc_buf_t *dest, cpc_buf_t *src);
void cpc_buf_zero(cpc_t *cpc, cpc_buf_t *buf);

cpc_buf_get() 関数は、index パラメータで特定したカウンタの値を取得します。index パラメータは、セットがバインドされる前に cpc_set_add_request() 関数で返された値です。cpc_buf_get() 関数は、val パラメータが示す位置のカウンタを格納します。

cpc_buf_set() 関数は、index パラメータで特定したカウンタの値を設定します。index パラメータは、セットがバインドされる前に cpc_set_add_request() 関数で返された値です。cpc_buf_set() 関数は、val パラメータが示す位置の値にカウンタの値を設定します。cpc_buf_get() 関数または cpc_buf_set() 関数のどちらも、対応する CPC 要求の事前設定を変更しません。

cpc_buf_hrtime() 関数は、いつハードウェアが抽出されたかを示す高精度な時刻表示を返します。cpc_buf_tick() 関数は、LWP の実行中に経過した CPU クロックサイクルの数を返します。

cpc_buf_sub() 関数は、leftright パラメータで指定されたカウンタとクロック刻み値の違いを計算します。cpc_buf_sub() 関数は、result に結果を格納します。cpc_buf_sub() 関数の呼び出しでは、cpc_buf_t 値がすべて同じ cpc_set_t 構造体から由来する必要があります。result インデックスは、バッファー内の要求インデックスごとの left - right の計算結果を含みます。また、結果のインデックスは tick の違いも含みます。cpc_buf_sub() 関数は、出力先バッファーについての高精度な時刻表示を、left または right バッファーの最新時刻に設定します。

cpc_buf_add() 関数は、leftright パラメータで指定されたカウンタとクロック刻み値の合計を計算します。cpc_buf_add() 関数は、result に結果を格納します。cpc_buf_add() 関数の呼び出しでは、cpc_buf_t 値がすべて同じ cpc_set_t 構造体から由来する必要があります。result インデックスは、バッファー内の要求インデックスごとの left + right の計算結果を含みます。また、結果のインデックスは tick の合計も含みます。cpc_buf_add() 関数は、の出力先バッファーについての高精度な時刻表示を、left または right バッファーの最新時刻に設定します。

cpc_buf_copy() 関数は、src と同じdest を作成します。

cpc_buf_zero() 関数は、buf 内のすべてを 0 に設定します。

起動インタフェース

この節では、CPC の起動インタフェースについて説明します。

int cpc_enable(cpc_t *cpc);
int cpc_disable(cpc_t *cpc);

この 2 つのインタフェースはそれぞれ、既存の LWP にバインドされたセットのカウンタを有効および無効にします。これらのインタフェースを使用すると、libpctx を使ってカウンタ設定を制御プロセスに任せながら、アプリケーションは対象のコードを指定できます。

エラー処理インタフェース

この節では、CPC のエラー処理インタフェースについて説明します。

typedef void (cpc_errhndlr_t)(const char *fn, int subcode, const char *fmt,
          va_list ap);
void cpc_seterrhndlr(cpc_t *cpc, cpc_errhndlr_t *errhndlr);

この 2 つのインタフェースによって、cpc_t ハンドルを渡すことができます。cpc_errhndlr_t ハンドルには、文字列のほかに整数のサブコードも指定します。整数の subcode は、 fn 引数が参照する関数によって発生した特定のエラーを示します。整数の subcode により、アプリケーションはエラー状況を簡単に認識できます。fmt 引数の文字列は、エラーサブコードについての国際化された説明を含み、出力に適しています。