コード内に指定できる 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