コード内に指定できる discover API と環境変数がいくつか用意されています。
Oracle Solaris Studio 12.4 には、プログラムから呼び出してメモリーリークやメモリー割り当ての情報を受け取ることができる 6 つの新しい discover 関数が実装されています。これらの関数は、stderr に情報を出力します。discover は、デフォルトでプログラム出力の最後に、プログラム内のメモリーリークを含む最終的なメモリーレポートを出力します。これらの API を使用するには、アプリケーションのソースファイルに discover 用のヘッダーファイルをインクルードする必要があります (#include <discoverAPI.h>)。
関数と報告される内容は次のとおりです。
すべてのメモリー割り当てを報告します
以前に報告されていないすべてのメモリー割り当てを報告します。
今までに報告されたすべてのメモリー割り当てをマークします。
すべてのメモリーリークを報告します。
以前に報告されていないすべてのメモリーリークを報告します。
今までに報告されたすべてのメモリーリークをマークします
このセクションでは、discover API の使用方法について説明します。
discover は、コード内に指定した関数ごとに、メモリーが割り当てられた場所のスタックを報告します。メモリーリークとは、プログラム内で到達不可能な割り当てメモリーのことです。
次の例は、これらの API の使用方法を示しています。
$ cat -n tdata.C
1 #include <discoverAPI.h>
2
3 void foo()
4 {
5 int *j = new int;
6 }
7
8 int main()
9 {
10 foo();
11 discover_report_all_leaks();
12
13 foo();
14 discover_report_unreported_leaks();
15
16 return 0;
17 }
$ CC -g tdata.C
$ discover -w - a.out
$ a.out
次の例は、予想される出力を示しています。
******** discover_report_all_leaks() Report ********
1 allocation at 1 location left on the heap with a total size of 4 bytes
LEAK 1: 1 allocation with total size of 4 bytes
void*operator new(unsigned) + 0x36
void foo() + 0x5e <tdata.C:5>
2:
3: void foo()
4: {
5:=> int *j = new int;
6: }
7:
8: int main()
main()+0x1a <tdata.C:10>
9: {
10:=> foo();
11: discover_report_all_leaks();
12:
13: foo(); _start() +
**********************************************************
******** discover_report_unreported_leaks() Report ********
1 allocation at 1 location left on the heap with a total size of 4 bytes
LEAK 1: 1 allocation with total size of 4 bytes
void*operator new(unsigned) + 0x36
void foo() + 0x5e <tdata.C:5>
2:
3: void foo()
4: {
5:=> int *j = new int;
6: }
7:
8:int main()
main() + 0x24 <tdata.C:13>
10: foo();
11: discover_report_all_leaks();
12:
13:=> foo();
14: discover_report_unreported_leaks();
15:
16:return 0;
_start() + 0x71
**********************************************************
***************** Discover Memory Report *****************
2 allocations at 2 locations left on the heap with a total size of 8 bytes
LEAK 1: 1 allocation with total size of 4 bytes
void*operator new(unsigned) + 0x36
void foo() + 0x5e <tdata.C:5>
2:
3: void foo()
4: {
5:=> int *j = new int;
6: }
7:
8: int main()
main() + 0x1a <tdata.C:10>
7:
8: int main()
9: { 10:=> foo();
11: discover_report_all_leaks();
12:
13: foo(); _start() + 0x71
LEAK 2: 1 allocation with total size of 4 bytes
void*operator new(unsigned) + 0x36
void foo() + 0x5e <tdata.C:5>
2:
3: void foo()
4: {
5:=> int *j = new int;
6: }
7:
8: int main()
main() + 0x24 <tdata.C:13>
10: foo();
11: discover_report_all_leaks();
12:
13:=> foo();
14: discover_report_unreported_leaks();
15:
16: return 0;
_start() + 0x71
DISCOVER SUMMARY:
unique errors : 0 (0 total)
unique warnings : 0 (0 total)
終了することがない長時間実行プログラムまたはサーバーがある場合は、コード内に呼び出しを配置しなくても、dbx を使用していつでもこれらの discover 関数を呼び出すことができます。プログラムは、少なくとも –l オプションを使用して discover の軽量モードで実行されている必要があります。dbx は実行中のプログラムに接続できます。次の例は、長時間実行プログラム内のリークを検出する方法を示しています。
使用例 1 長時間実行プログラム内の 2 つのリークの検出この例で、a.out ファイルは 2 つのプロセスを持つ長時間実行プログラムであり、各プロセスにリークが 1 つずつあります。各プロセスにはプロセス ID が割り当てられています。
次の rl スクリプトには、このプログラムに対して報告されていないメモリーリークを報告するように要求するコマンドが含まれています。
#!/bin/sh dbx - $1 > /dev/null 2> &1 << END call discover_report_unreported_leaks() exit END
プログラムとスクリプトが用意できたら、discover を使用してプログラムを実行できます。
% discover -l -w - a.out % a.out 8252: Parent allocation 64 8253: Child allocation 32
別個の端末ウィンドウで、親プロセスに対してスクリプトを実行できます。
% rl 8252
プログラムは親プロセスに関して次の情報を報告します。
******** discover_report_unreported_leaks() Report ********
1 allocation at 1 location left on the heap with a total size of 64 bytes
LEAK 1: 1 allocation with total size of 64 bytes
main() + 0x1e <xx.c:17>
14:
15: if (child > 0) {
16:
17:=> void *p = malloc(64);
18: printf("%jd: Parent allocation 64\n", (intmax_t)getpid());
19: p = 0;
20: for (int j=0; j < 1000; j++) sleep(1);
_start() + 0x66
**********************************************************
子プロセスに対して再度プロセスを実行します。
% rl 8253
プログラムは子プロセスに関して次の情報を報告します。
******** discover_report_unreported_leaks() Report ********
1 allocation at 1 location left on the heap with a total size of 32 bytes
LEAK 1: 1 allocation with total size of 32 bytes
main() + 0x80 <xx.c:24>
21: }
22:
23: else {
24:=> void *p = malloc(32);
25: printf("%jd: Child allocation 32\n", (intmax_t)getpid());
26: p = 0;
27: for (int j=0; j < 1000; j++) sleep(1);
_start() + 0x66
**********************************************************
スクリプトを繰り返し使用して、新しいリークを検出できます。
計測対象バイナリの実行時の動作を変更するには、SUNW_DISCOVER_OPTIONS 環境変数に、コマンド行オプション –a、–A、–b–e、–E、–f、–F、–H、–l、–L、–m、–P、–S、および –w のリストを設定します。たとえば、レポートされるエラー数を 50 に変更し、レポート内のスタックの深さを 3 に制限する場合、環境変数を次のように設定します。
–e 50 –s 3
デフォルトでは、discover で計測機構を組み込んだバイナリが実行中にフォークした場合、discover は親および子プロセスからメモリーアクセスエラーのデータを収集し続けます。つまり、デフォルトの動作は both です。たとえば、discover でフォークを追跡し、子プロセスからメモリーアクセスデータを収集する場合は、SUNW_DISCOVER_FOLLOW_FORK_MODE 環境変数を次のように設定します。
–F child