ロック lint サブコマンドの中には、assert などのサブコマンドと対応しているものがあります。以下の理由によって、ソースコードへの注釈の挿入がサブコマンドよりも望ましい場合があります。
より細かい指定が可能
メンテナンスが容易
問題のコードに対するコメントとしての機能を持つ
ソースコードに注釈を直接挿入する方法については、「ソースコードへの注釈の挿入」を参照してください。
analyze [-hv]
読み込まれたファイルに含まれる、データ競合やデッドロックに通じる可能性のあるロックの不整合を解析します。このサブコマンドは大量の出力を生成する可能性があるため、ファイルにリダイレクトするほうがよいかもしれません。このサブコマンドは、保存された各ステートに対して、一度だけ実行できます (「save」を参照)。
-h (history) は、解析の各フェーズごとの詳細情報を生成します。追加エラーは生成しません。
-v (verbose) は、解析中に追加メッセージを生成します。
ロックの保持なしに書き込み可能変数の読み取りが行われています
ロックの保持なしに変数へ書き込んでいます
条件変数の待機中にロックの保持に整合性がありません
以下の状況では、analyze サブコマンドの出力は特に膨大な量となることがあります。
以前にコードの解析が行われたことがない。
読み取り専用変数の識別に、assert read only サブコマンドが使用されなかった。
書き込み可能変数の保護に関するアサーションが作成されていない。
出力メッセージは、実際には問題でない状況を反映することもあるため、最初は -v オプションなしにコードの解析を行ったほうが、実際の問題を表している可能性の高いメッセージを表示できます。
解析中に遭遇する各問題箇所は、1 行または数行に渡って報告され、その先頭行はアスタリスクから始まります。可能な箇所では、ロック lint は問題箇所にたどり着くまでの呼び出しの完全なトレースバックを提供します。解析は以下のフェーズを経て進行します。
ロックに関して変数の副作用を持った関数をチェックします。
disallow シーケンスによって、ロック副作用を持った関数を解析対象から外すよう指定されている場合、ロック lint は正しくない結果を生成します。そうした disallow シーケンスが見つかると、報告が行われ、解析は行われません。
保持するロックの順序に関する情報を準備します。
ロック lint は、アサートされているロックの順序に関する情報を処理します。ロック lint がアサートされているロックの順序の中に循環部分を検出した場合、その循環部分はエラーとして報告されます。
ターゲットのない関数ポインタをチェックします。
ロック lint は、関数ポインタの割り当て状況をつねに推測できるわけではありません。このフェーズでは、ロック lint は、1 つのターゲット (ソースから推測されるか、func.ptr ターゲットが宣言されている) も存在しないと思われる関数ポインタを報告します。
無視される変数へのアクセスを削除します。
パフォーマンスを改善するため、ロック lint は、この時点で無視されている変数に対する参照を削除します (これは vars サブコマンドの出力に影響します)。
解析の対象となる関数を準備します。
このフェーズでは、ロック lint は、各関数がロックについてどんな副作用を持っているかを確認します (副作用とは、関数から戻る前に復旧しないロック状態の変化を指します)。以下の状況では、エラーが生じます。
副作用がロック lint の予想と一致しない場合。
副作用が、関数が通過するパスに応じて異なる場合。
そうした副作用を持った関数が再帰的である場合。
ロック lint は、assert side effect サブコマンドを使って副作用が追加されている箇所を除き、関数がロックに副作用を持たないと予想します。
allow/disallow に対する呼び出しシーケンスの確認を準備します。
この時点で、ロック lint はすでに指示されているさまざまな allow/disallow サブコマンドを処理します。エラーや警告は報告されません。
関数ポインタターゲットのロック副作用をチェックします。
関数ポインタを介した呼び出しは、複数の関数をターゲットにしていることがあります。特定の関数ポインタのターゲットである関数はすべて、ロックに同じ副作用を持たなければなりません (該当する場合) 。副作用が異なる複数のターゲットを関数ポインタが持つ場合、解析は行われません。
状態変数のロックの使用法の整合性をチェックします。
この時点で、ロック lint は、特定の状態変数での待機が同じ相互排他ロックを使用しているかをチェックします。また、その状態変数を保護している特定のロックをアサートする場合、ロック lint は、状態変数での待機中、必ずそのロックを使用するかをユーザに確認します。
各関数に制御が渡されたときに、整合性が保持されるロックを確認します。
このフェーズでは、ロック lint は、関数へのエントリに際して保持されるべきロックのアサーションの破綻を報告します (「assert」 サブコマンドを参照)。すでに保持されている相互排他ロックをロックしたり、保持されていないロックを解放するなどのエラーもまた報告されます。匿名のロック (foo::lock など) を2 回以上ロックしても、declare one コマンドを使用してそうでない状況を指示していない限り、エラーと見なされません (匿名データについての詳細は、 「ロックの逆転」 を参照してください)。
各変数がアクセスされたときに、整合性が保持されるロックを確認します。
このフェーズでは、ロック lint は、変数がアクセスされるときに保持されるべきロックのアサーションの破綻を報告します (「assert」サブコマンドを参照)。また、書き込み禁止の変数に対する書き込みも報告されます。
時には、特定の関数がまったく呼び出されなかったというメッセージを受け取る場合もあります。ルート関数でない一連の関数が交互に呼び出し合うと、こうした状況は起こります。ただ 1 つの関数もその外部から呼び出されないと、ロック lint は、それらの関数はまったく呼び出されなかったと報告します。その後の解析において、declare root サブコマンドを使うことで、こうした状況を修復できます。
また、disallow サブコマンドを使って、関数にたどりつくまでのすべてのシーケンスを禁じる場合にも、関数がまったく呼び出されないというメッセージが報告されます。
いったん解析を実行した後では、vars および order サブコマンドの出力において、さらに多くの潜在的な問題を見つけ出すことが可能です。
assert side effect |
mutex |
acquired in |
func ... |
assert side effect |
rwlock [read] |
acquired in |
func ... |
assert side effect |
lock |
released in |
func ... |
assert side effect |
rwlock |
upgraded in |
func ... |
assert side effect |
rwlock |
downgraded in |
func ... |
assert mutex|rwlock |
protects |
|
var ... |
assert mutex |
protects |
|
func ... |
assert rwlock |
protects |
[reads in] |
func ... |
assert order |
|
|
lock lock ... |
assert read only |
|
|
var ... |
assert rwlock |
covers |
|
lock ... |
これらのサブコマンドは、ロック lint に対して、チェックの対象となるアプリケーションにおいてロックおよび変数がどのようにアクセスおよび変更されることをプログラマが期待しているかを指示します。解析中には、そうしたアサーションの違反が報告されます。
1 つの変数について 2 回以上のアサートが行われている場合、最後の assert のみが有効となります。
副作用 (side effect) とは、ロックの状態が関数によって変化し、関数が制御を返す前に、その変化が復帰しないことを指します。関数にロックの副作用が含まれ、その副作用についてのアサーションが作成されていない場合、あるいは、実際の副作用がアサーションの内容と異なる場合、解析中に警告が発行されます。その後は、予期せぬ副作用はまったく起こらないものとして、解析は続行されます。
副作用には逆転と呼ばれる種類もあります。詳細については、 「ロックの逆転」、および「locks」または「funcs」サブコマンドを参照してください。
関数によってもたらされる副作用が呼び出しごとに異なる場合も (たとえば、条件付きの副作用など)、警告が発せられます。キーワード acquired in、released in、upgraded in、downgraded in は、関数についてアサートされているロック副作用の種類を明らかにします。これらのキーワードは、スレッドライブラリインタフェース、DDI および DKI カーネルファンクションを介して利用が可能な副作用に対応しています (「mutex(3T)」、「rwlock(3T)」、「mutex(9F)」、「rwlock(9F)」を 参照)。
rwlocks に対する副作用のアサーションは、オプション引数 read を取ります。read が付いている場合、その副作用は、その関数がそのロックに対して読み取りレベルのアクセスを獲得することになります。read がない場合は、書き込みレベルのアクセスを獲得することになります。
相互排他ロックが変数を保護しているとアサートした場合、相互排他ロックを保持せずにその変数がアクセスされた時点で必ずエラーとなります。読み取り書き込みロックが変数を保護しているとアサートした場合、読み取りアクセスに対するロックを保持せずにその変数を読み取ろうした時点、あるいは書き込みアクセスに対するロックを保持せずにその変数に書き込もうとした時点で必ずエラーとなります。どちらのロックが変数を保護しているかに関するその後のアサーションは、それ以前のアサーションを上書きします。つまり、解析中は、変数保護について、最後にアサートされたロックのみが常に使用されるということです。
相互排他ロックが関数を保護しているとアサートした場合、ロックを保持せずにその関数が呼び出された時点で必ずエラーとなります。ルート関数の場合、このアサーションが真である状態でルート関数が呼び出されたものとして、解析は実行されます。
読み取り書き込みロックが関数を保護しているとアサートした場合、書き込みアクセスに対するロックを保持せずにその関数が呼び出された時点で必ずエラーとなります。読み取り書き込みロックが関数の読み取りを保護しているとアサートした場合、読み取りアクセスに対するロックを保持せずにその関数が呼び出された時点で必ずエラーとなります。ルート関数の場合、このアサーションが真である状態でルート関数が呼び出されたものとして、解析は実行されます。
単独の assert ... protects サブコマンドに対する違反が多すぎて、出力が大量になることを防ぐため、指定されたアサーションの違反は最大 20 まで提示されます。この制限は、assert order サブコマンドには適用されません。
ロックがどのような順番で獲得されるべきかを、ロック lint に知らせます。見慣れたロックの順序を守っていれば、プログラムにデッドロックは起こらないと、ロック lint は想定します。このサブコマンドを使って、ロック lint にあらかじめ定めた順序を理解させ、その結果、解析中にその順序の違反が見つかると出力されます。
特定の変数に対して、アプリケーションは絶対に書き込みを行ってはいけないというアサーションを作成します。ロック lint は、そうした変数に対する書き込みを報告します。変数が書き込み禁止ではない限り、ロックが保持されていない状態で、変数の読み取りが行われると、エラーが発行されます。これは、ロック lint が、ほかのスレッドが同時にその変数へ書き込みを行う心配があると想定しているためです。
ロック lint に、階層的なロック関係が存在することを知らせます。特定の状況におけるパフォーマンスを改善するため、読み取り書き込みロックは、ほかのロック (相互排他あるいは読み取り書き込み) と組み合わせて使用されることがあります。
カバーと呼ばれる読み取り書き込みロックは、カバーされる側であるほかのロックが保持されている間、必ず保持されていなければなりません。これは、読み込みアクセスに対するカバーを保持していない間に、カバーされる側のロックを保持することは規則に反する (こうした表記規則の下では) ということです。
書き込みアクセスに対するカバーを保持している間は、カバーされる側のロックを保持せずに、カバーされる側のロックの 1 つによって保護された変数にアクセスすることが可能です。ほかのスレッドがカバーされる側のロックを保持することは不可能であるため (そのスレッドはカバーも保持していなければならないため)、こうした操作は可能となります。カバー上に集中した競合が存在しない限り、カバーされる側のロックを保持しないことによって節約される時間の分だけ、パフォーマンスは向上します。
assert rwlock covers を利用することで、カバーされる側のロックではなく、書き込みアクセスに対するカバーを保持しながらスレッドが変数にアクセスする場合に、ロック lint によるエラーメッセージの表示を防止できます。また、カバーが保持されていないときは、カバーされる側のロックは絶対に保持されていないというルールのチェックを可能にします。
declare |
mutex |
mutex . . . |
|
declare |
rwlocks |
rwlock ... |
|
declare |
func_ptr |
targets |
func ... |
declare |
nonreturning |
func ... |
|
declare |
one |
tag ... |
|
declare |
readable |
var ... |
|
declare |
root |
func ... |
|
これらのサブコマンドは、ロック lint に対して、提示されているソースからは推測できない事柄を伝えます。
これらのサブコマンドは (この後の declare root とともに)、主に、サポートするハーネスなしで、ライブラリを解析する際に使用されます。サブコマンド declare mutex と declare rwlocks は、特定された名前の相互排他ロックおよび読み取り側/書き込み側ロックを生成します。これらの名前は、その後の assert サブコマンドで利用されます。
指定された関数を、指定された関数ポインタを介した呼び出しが可能な関数のリストに追加します。
ロック lint は、初期設定および割り当て構成に注目し、独力で関数ポインタのターゲットについての大量な情報を収集します。たとえば、以下のコードに対して、
struct foo { int (*fp)(); } foo1 = { bar };
ロック lint は、以下のコマンドに相当する動作を実行します。
% lock_lint declare foo::fp targets bar
ロック lint は (上記の例に対して)、以下の動作は行いません。
% lock_lint declare foo1.fp targets bar
しかし、関数ポインタの割り当てに対しては、いずれの動作も行います。「ロックの逆転」を参照してください。
ロック lint に対して、指定された関数は戻らないことを伝えます。ロック lint は、この関数が呼び出された後、ロックの状態についてのエラーを報告しません。
そのタグが指定されている各構造体の名前未定のインスタンスが 1 つだけ存在することを、ロック lint に伝えます。これによって、その構造体のロックが解放されることなく複数回獲得された場合に、ロック lint はエラーを報告します。こうした情報がない場合は、構造体の 2 つの異なるインスタンスが含まれる可能性があるため、ロック lint は、匿名のロック (例えば foo::lock) の複数獲得について不具合を報告することはありません (「ロックの逆転」 を参照)。
ロック lint に対して、ロックを保持しなくても、指定された変数が安全に読み込まれることを伝えます。さらに、こうした非保護状態での読み込みに対して、通常は表示されるエラーも抑制されます。
指定された関数をルート関数として解析するよう、ロック lint に指示します。デフォルトでは、関数がほかの関数から呼び出される場合、ロック lint はその関数を呼び出しシーケンスのルートとして解析しようとはしません。
ルート関数とは、解析の開始ポイントです。読み込まれたファイル内から呼び出されることのない関数は本来ルートです。たとえば、直接呼び出されることはないけれど、スレッドの初期開始ポイントである関数 (thread_create 呼び出しのターゲット関数など) もこれに含まれます。しかし、読み込まれたファイルの内側から呼び出される関数が、読み込まれたファイルの外側から呼び出されることもあり、こうした場合は、このサブコマンドを使って、ロック lint にこの関数を解析の開始ポイントとして使用するよう指示する必要があります。
disallow func ...
指定された呼び出しシーケンスを解析の対象から外すようにロック lint に指示します。たとえば、f() が g() を呼び出し、さらに h() を呼び出している呼び出しシーケンスをロック lint が解析しないようにするには、以下のサブコマンドを使用します。
% lock_lint disallow f g h
関数ポインタは、実際には起こらない多くの呼び出しシーケンスをプログラムがたどっているかのように見せることがあります。実態のないロック問題、特にデッドロックなどはそうしたシーケンスに現れます (「reallow」の解説も参照してください)。disallow は、ロック lint がそうしたシーケンスをたどることを防ぎます。
disallows
disallow サブコマンドによって除外の対象とされた呼び出しシーケンスをリストします。
ロック lint には、終了のためのサブコマンドは存在しません。ロック lint を終了するには、使用しているシェルの終了コマンドを使用してください。
files
load サブコマンドによって読み込まれたソースコードファイルの .ll バージョンをリストします。
funcptrs [-botu] func_ptr ... funcptrs [-blotuz]
読み込まれたファイルの中で使用されている関数ポインタについての情報をリストプします。各関数ポインタに対して、1 行分の情報が生成されます。
表 A-6 funcptrs の オプション
オプション |
定義 |
|
---|---|---|
-b |
(bound) 関数ターゲットがバインドされている関数ポインタのみをリストします。つまり、バインドされたターゲットの存在しない関数ポインタの表示は抑制されます。 |
|
-l |
(long) -ot に相当します。 |
|
-o |
(other) 各関数ポインタに対する以下の情報を提示します。 |
|
Calls=# |
読み込まれたファイル中で、関数の呼び出しにこの関数ポインタが使用されている箇所の数を示します。 |
|
=nonreturning |
この関数ポインタを介した呼び出しが戻らないことを示します (ターゲットとされた関数が一切戻りま せん)。 |
|
-t |
(target) リストされている各関数ポインタのターゲットとして現在バインドされている関数をリストします。以下のように表示されます。 targets={ func ... } |
|
-u |
(unbound) 関数ターゲットがバインドされていない関数ポインタのみをリストします。バインドされたターゲットが存在する関数ポインタの表示は抑制されます。 |
|
-z |
(zero) 呼び出しが存在しない関数ポインタをリストします。このオプションが省略されると、それを介して呼び出しが行われる関数ポインタについての情報のみが提示されます。 |
funcptrs には複数のオプションを組み合わせることができます。
この例では、指定された関数ポインタに関する情報をリストします。デフォルトでは、この形式のサブコマンドは、-ot オプションが指定された場合と同様に、関数ポインタに関するあらゆる詳細情報を提供します。
funcptrs [-botu] func_ptr ...
この例では、それを介して呼び出しが行われるすべての関数ポインタに関する情報をリストします。-z オプションを使用した場合、それを介して呼び出しが行われない関数ポインタもリストします。
funcptrs [-blotuz]
funcs[-adehou] |
|
|
func ... |
funcs[-adehilou] |
[directly] |
|
|
funcs[-adehlou] |
[directly] |
called by |
func ... |
funcs[-adehlou] |
[directly] |
calling |
func ... |
funcs[-adehlou] |
[directly] |
reading |
var .... |
funcs[-adehlou] |
[directly] |
writing |
var ... |
funcs[-adehlou] |
[directly] |
accessing |
var ... |
funcs[-adehlou] |
[directly] |
affecting |
lock ... |
funcs[-adehlou] |
[directly] |
inverting |
lock ... |
funcs は、読み込まれたファイル中で定義され、呼び出される関数についての情報をリストします。各関数について 1 行分の情報が表示されます。
表 A-7 funcs のオプション
オプション |
定義 |
|
---|---|---|
-a |
(asserts) assert サブコマンドの設定内容に従って、各関数のエントリに対してどのロックが保持されることになっているかに関する情報を提示します。アサーションは以下のように表示されます。 asserts={ lock ... } read_asserts={ lock ... } エントリに対して整合性が保持されていないロック名の前には、アスタリクが付けられます (解析後)。 |
|
-e |
(effects) 各関数がロックに持つ副作用についての情報を提示します (たとえば、"acquires mutex lock foo" など)。副作用は以下のように表示されます。 side_effects={ effect [, effect] ... } 解析に先だってこのオプションを使用すると、 assert side effect サブコマンドによってアサートされた副作用も提示されます。解析後には、解析中に発見された副作用についての情報も提示されます。 |
|
-d |
(defined) 読み込まれたファイル中で定義されている関数のみを提示します。未定義の関数の表示は抑制されます。 |
|
-h |
(held) (解析後) 関数が呼び出された際に、どのロックが整合性を保持されていたかについての情報を提示します。エントリの読み取り (または書き込み) に対してロックが整合性を保持されている場合、以下のように表示されます。 held={ lock ... }+{ lock ... } read_held={ lock ... }+{ lock ... } 各セットの最初の行は、関数呼び出し時に整合性を保持されるロックのリストで、2 行目は整合性を保持されるないロック (関数が呼び出し時、保持されていることはあっても、つねに保持されるわけではないロック) のリストです。 |
|
-i |
(ignored) 無視される関数をリストします。 |
|
-l |
(long) -aeoh に相当します。 |
|
-o |
(other) 各関数に関する以下の情報を提示します (該当する場合)。 |
|
=ignored |
ロック lint は、ignore サブコマンドを使って関数を無視するよう指示されています。 |
|
=nonreturning |
この関数を介した呼び出しは戻ってきません (ターゲットとなる関数のいずれも戻りません)。 |
|
=rooted |
declare root サブコマンドによって、関数がルート化されています。 |
|
=root |
関数がもともとルートです (ほかの関数から呼び出されることがありません)。 |
|
=recursive |
関数が自分自身を呼び出します。 |
|
=unanalyzed |
解析中に、関数が呼び出されませんでした (そのため、解析が行われませんでした)。foo が bar を呼び出し、bar が foo を呼び出し、foo または bar を呼び出す関数はほかになく、いずれもルート化されていない (「=rooted」を参照) 場合で、=root とは異なります。つまり、foo および bar はルートではなく、ほかのいかなるルート関数からもたどり着くことができないため、これらの関数は解析されることはありません。 |
|
calls=# |
読み込まれたファイルの内容そのままに、ソースコード中、この関数が呼び出されている箇所の数を示します。実際には解析されない呼び出しもあります。たとえば、disallow サブコマンドによって、実際には呼び出しが行われないこともあります。 |
|
-u |
(undefined) 読み込まれたファイル中に定義されていない関数のみを提示します。 |
個々の関数に関する情報をリストします。デフォルトでは、この形式のサブコマンドは、-aeho が指定されている場合と同様に、関数についての詳細情報をすべて提供します。
無視されないすべての関数についての情報をリストします。-i が使用されると、無視される関数もリストされます。
指定された関数の呼び出しの結果として呼び出される可能性のある関数のみをリストします。directly が指定されると、指定された関数によって呼び出される関数のみがリストされます。directly が指定されない場合は、呼び出された関数がさらに呼び出している関数もリストされます。
自身が呼び出されると、その結果として、指定された 1 つまたは複数の関数を呼び出す可能性のある関数のみをリストします。この後の「directly について」の項も参照してください。
自身が呼び出されると、その結果として、指定された 1 つまたは複数の関数が読み込まれる可能性のある関数のみをリストします。この後の「directly について」の項も参照してください。
自身が呼び出されると、その結果として、指定された 1 つまたは複数の変数が書き込まれる可能性のある関数のみをリストします。この後の「directly について」の項も参照してください。
自身が呼び出されると、その結果として、指定された 1 つまたは複数の変数がアクセスされる (読み取られるまたは書き込まれる) 可能性のある関数のみをリストします。この後の「directly について」の項も参照してください。
自身が呼び出されると、その結果として、指定された 1 つまたは複数のロックに影響 (獲得、解放、昇格、または降格) を与える可能性のある関数のみをリストします。この後の「directly について」の項も参照してください。
指定された 1 つまたは複数のロックを反転させる関数のみをリストします (「ロックの逆転」を参照)。directly が指定されると、自身が 1 つまたは複数のロックを反転させている (実際には解放する) 関数のみがリストされます。directly が指定されない場合は、すでに保持されたロックとともに呼び出され、その後、そのロックを反転させるほかの関数を呼び出す関数もリストされます。
たとえば、以下のコードでは、f3() はロック m を直接反転し、f2() はそれを間接的に反転しています。
f1() { pthread_mutex_unlock(&m); f2(); pthread_mutex_lock(&m); } f2() { f3(); } f3() { pthread_mutex_unlock(&m); pthread_mutex_lock(&m); }
特にことわり書きがない限り、キーワード directly を許可する形式は、それ自身が記述に一致する関数のみをリストします。directly が指定されない場合は、指定された関数を呼び出すすべての関数が表示され、さらにそれらの関数を呼び出す関数もリストされます。
help [keyword]
キーワードが指定されない場合、help はサブコマンドのセットを表示します。
キーワードが指定された場合は、指定されたキーワードに関連した情報を提供します。キーワードには、ロック lint サブコマンドの最初の単語を指定できます。また、その他のキーワードもいくつか用意されています。
condvars locking example makefile ifdef names inversions overview limitations shell
環境変数 PAGER が設定されている場合、そのプログラムが help のページャーとして使用されます。設定されていない場合は、more が使用されます。
ignore func|var ... [ in func ... ]
ロック lint に、特定の関数および変数を解析の対象から外す (無視する) よう指示します。除外は、in func ... 句に指定された関数に限定されます。この句が省略された場合は、すべての関数が除外対象となります。
以下のコマンドで、どの関数および変数が無視されるかを示します。
% lock_lint funcs -io | grep =ignored % lock_lint vars -io | grep =ignored
load file ...
指定された .ll ファイルを読み込みます。拡張子は省略可能ですが、拡張子を指定する場合は、必ず .ll でなければなりません。相対パスおよび絶対パスの使用が可能です。シェルとの対話において、以下はすべて規則に合っています (シェルの機能によります)。
% lock_lint load *.ll % lock_lint load ../foo/abcdef{1,2} % lock_lint load `find . -name ¥*.ll -print`
locks [-co] lock ... locks [-col] locks [-col] [directly] affected by func ... locks [-col] [directly] inverted by func ...
読み込まれたファイルのロックに関する情報をリストします。ロック操作ルーチン中で実際に使用される変数のみが提示されますが、宣言されているだけで、実際には操作されることのないロックは提示されません。
表 A-8 locks のオプション
オプション |
定義 |
---|---|
-c |
(cover) ロックの階層関係についての情報を提示します。こうした関係は、assert rwlock covers サブコマンドを使って記述されます (ロックがこうした関係で配置されている場合、カバーされる側のロックが保持される際には、少なくとも読み取りアクセスに対するカバーする側のロックが保持されなければなりません。一方、書き込みアクセスに対してはカバーする側のロックを保持している間は、カバーされる側のロックは保持する必要はありません)。ロックがほかのロックをカバーする場合、ロックは次のように提示されます。 covered={ lock ... } ロックがほかのロックによってカバーされている場合、ロックは次のように表示されます。 cover=lock |
-l |
(long) -co に相当します。 |
-o |
(other) ロックの型を type として示します。ロックの型には、mutex (相互排他型)、rwlock (読み込み/書き込み型)、ambiguous (あいまい型) (ある場所では mutex 型、別の場所では rwlock 型として使用される) があります。 |
個々のロックについての情報をリストします。デフォルトでは、この形式のサブコマンドは、-co が指定されているものとして、ロックの詳細情報をすべて表示します。
すべてのロックについての情報をリストします。
指定された関数を呼び出した結果として、影響 (獲得、解放、昇格、降格) を受ける可能性のあるロックのみをリストします。キーワード directly が指定された場合は、スレッドライブラリルーチンを使用し、直接ロックに影響 (獲得、解放、昇格、降格) を与える関数のみが表示されます。キーワード directly が指定されない場合は、ロックに影響を与える関数を呼び出すすべての関数、さらに、そうした関数を呼び出すすべての関数といった具合に、関連するすべての関数が表示されます。
指定された関数の 1 つを呼び出すことで、逆転させられる可能性のあるロックのみを表示します(「ロックの逆転」 を参照) 。
キーワード directly が指定された場合は、指定された関数 (スレッドライブラリルーチンを使って、実際にロックの獲得および解放を行う関数) によって直接逆転させられるロックのみが表示されます。キーワード directly が指定されない場合は、指定された関数のどれか 1 つによって保持され、そこから呼び出される関数によって逆転させられる (以下同様の) ロックも表示されます。たとえば、以下のコードでは、f1 は m1 を直接逆転し、さらに、m2 を間接的に逆転しています。
f1() { pthread_mutex_unlock(&m1); f2(); pthread_mutex_lock(&m1); } f2() { f3(); } f3() { pthread_mutex_unlock(&m2); pthread_mutex_lock(&m2); }
members struct_tag
指定されたタグを持つ struct のメンバーを 1 行につき 1 つずつリストします。タグが割り当てられていない struct の場合、ファイル名@行番号という形式が使用され (例:x.c@29)、ファイル名と行番号は struct 宣言におけるソース位置となります。
members は、ほかのロック lint サブコマンドに対する入力として利用する場合に特に便利です。たとえば、ロックが struct の全メンバーを保護していることをアサートしようと試みる場合、以下のコマンドで十分です。
% lock_lint assert foo::lock protects `lock_lint members foo`
members サブコマンドは、mutex_t、rwlock_t、krwlock_t、kmutex_t 型として定義された struct のいずれのフィールドもリストすることはありません。
order [lock [lock]] order summary
order サブコマンドは、解析対象となるコードがロックを獲得する順序についての情報をリストします。analyze サブコマンドの後でのみ実行が可能です。
ロックの組み合わせについての詳細情報を提示します。たとえば、以下のコマンドは、
% lock_lint order foo bar
ロック foo を保持している間に、ロック bar を獲得しようという試みがなされたかどうかを示します。出力は以下のようになります。
:foo :bar seen (first never write-held), valid
まず、この出力は、そうした試みが実際に行われたかどうか (seen または unseen) を伝えます。もし、そうした試みが行われた場合でも、ロックのどちらかまたはその両方が書き込み用に保持されていない場合は、上記の例のように、その旨を表すメッセージが括弧付きで表示されます。この例では、bar を獲得している間、foo はまったく書き込み用に保持されませんでした。
ロックの順序に関するアサーションが設定されている場合、その出力は、そのアサーションに対して指定された順序が valid (正しい) か invalid (正しくない) かを示します。foo および bar の順序についてのアサーションが存在しない場合、あるいは、いずれの順序もアサートされている場合 (一方のロックが獲得されている間、もう一方のロックも保持されているすべての箇所を、ユーザーが確認したい場合など)、出力は valid も invalid も示しません。
ロックが獲得される順序をより簡潔な形式で示します。たとえば、このサブコマンドの結果は次のようになります。
:f :e :d :g :a :f :c :g :a
この例では、ロック c に関してロック e および d の順序を決定するだけの情報がないため、2 通りの順序が考えられます。
一方では、循環が提示され、もう一方では、提示されないこともあります。たとえば、
:a :b :c :b
と表示されても、
:a :b :c :a
と表示されないこともあります (これらのロックのどれか 1 つを獲得しようとしている間、ほかのロックは絶対に保持されない場合など)。この解析からはデッドロック情報も報告されます。
pointer calls
読み込まれたファイル中の関数ポインタを介した呼び出しをリストします。各呼び出しは、以下の形式で表示されます。
function [location of call] calls through funcptr func_ptr
foo.c:func1 [foo.c,84] calls through funcptr bar::read
foo.c の関数 func1、呼び出し位置としては foo.c の 84 行目で、関数の呼び出しに関数ポインタ bar::read (bar 型の struct に対するポインタのメンバー read) が使用されているという意味になります。
reallow func ...
disallow サブコマンドに対する例外を作成します。たとえば、f() が g() を呼び出し、さらに h() を呼び出すシーケンスをロック lint の解析の対象から外し、ただし、d() によって呼び出される e() によって f() が呼び出される場合のみは解析の対象とするには、以下のコマンドを使用します。
% lock_lint disallow f g h % lock_lint reallow d e f g h
ある関数が、特定の関数から呼び出される状況のみを解析の対象としたい場合は、次のように指定します。
% lock_lint disallow f % lock_lint reallow e f
reallow サブコマンドは、シーケンスの最後が同じになる場合のみ、disallow サブコマンドの効果を抑制します。たとえば、以下のコマンドを実行した後も、シーケンス d e f g h では依然、除外状態は続行します。
% lock_lint disallow e f g h % lock_lint reallow d e f g
reallows
reallow サブコマンドによって指定された、解析対象からの除外を解除された呼び出しシーケンスをリストします。
refresh
保存状態スタックをポップし、ロック lint を保存状態スタックの一番上の状態に戻します。そして、その状態に関連した概要 (存在する場合) を出力し、すぐにその状態を再保存します。refresh は、restore サブコマンドの後で save サブコマンドを実行するのと同じです。
restore
保存状態スタックをポップし、ロック lint を保存状態スタックの一番上の状態に戻し、その状態に関連した概要を出力します (存在する場合)。
保存状態スタックは LIFO (Last-In-First-Out) スタックです。保存された状態がスタックから復元 (ポップ) されると、その状態は保存状態スタックからは除かれます。その状態を繰り返し保存、復元する必要がある場合は、復元後、即座にその状態を再保存するか、refresh サブコマンドを利用してください。
save description
スタックに、ツールの現在の状態を保存します。ユーザーが指定する description (概要) が、その状態に付加されます。保存状態スタックは LIFO (Last-In-First-Out) スタックで、最後に保存された状態が最初に復元されます。
このサブコマンドは、通常、analyze サブコマンドの実行前に、ツールの状態を保存するために使用されます。analyze サブコマンドは、ある状態に対して、つねに 1 回しか実行できません。save サブコマンドの使用例を以下に示します。
%: lock_lint load *.ll %: lock_lint save Before Analysis %: lock_lint analyze <output from analyze> %: lock_lint vars -h | grep ¥* <apparent members of struct foo are not consistently protected> %: lock_lint refresh Before Analysis %: lock_lint assert lock1 protects `lock_lint members foo` %: lock_lint analyze <output now contains info about where the assertion is violated>
saves
save コマンドによって保存スタックに保存された状態の概要をリストします。概要はスタックの上部から下部にかけて順番に示されます。つまり、一番最後に保存された状態 (復元されていない) の概要が最初に表示され、一番古い状態 (復元されていない) の概要が最後に表示されるということです。
start [cmd]
ロック lint のセッションを開始します。ほかのロック lint サブコマンドを利用する前に、かならずロック lint セッションを開始しておく必要があります。デフォルトでは、start はロック lint のコンテキストを確立し、そのコンテキスト内で $SHELL によって指定されたユーザー用のサブシェルを起動します。シェルへエクスポートされるロック lint コンテキストは、ロック lint セッションの維持に使用されるファイルの一時ディレクトリへのパスが含まれた環境変数 LL_CONTEXT のみです。
cmd の部分には、コマンドとそのパスおよびオプションを指定できます。デフォルトでは、cmd が未指定の場合、$SHELL の値が使用されます。
ロック lint セッションを終了するには、使用するシェルの exit コマンドを使用してください。
start サブコマンドで開始するためのロック lint 環境の設定方法の詳細については、「ロック lint の制限事項」 を参照してください。
start サブコマンドの使用例を以下に示します。
% lock_lint start
ロック lint のコンテキストが確立され、LL_CONTEXT が設定されます。その後、$SHELL によって識別されるプログラムが実行されます (通常は、デフォルトのシェル)。こうして、ロック lint サブコマンドの入力が可能となります。シェルの終了時点で、ロック lint コンテキストも削除されます。
% lock_lint start foo
ロック lint のコンテキストが確立され、LL_CONTEXT が設定されます。その後、コマンド /bin/csh -c foo が実行されます。 この結果、ロック lint コマンドを含む C シェルのコマンドファイル foo が実行されます。/bin/csh による foo のコマンドの実行が終了した時点で、ロック lint コンテキストも削除されます。
ロック lint を起動するシェルスクリプトを使用する場合は、スクリプトの最初の行に #! を挿入することで、そのスクリプトを処理するインタプリタの名前を定義してください。たとえば、C シェルを指定する場合、スクリプトの先頭行は次のようになります。
#! /bin/csh
以下の場合、ユーザーは Korn シェルでロック lint を起動します。
% lock_lint start /bin/ksh
ロック lint のコンテキストが確立され、LL_CONTEXT が設定された後、コマンド /bin/ksh が実行されます。この結果、ユーザーはシェルと対話できるようになります。Korn シェルを終了した時点で、ロック lint コンテキストも削除されます。
sym name ...
読み込まれたファイル内で、指定された名前が参照可能なさまざまなアイテムの完全指定名をリストします。たとえば、foo という名前は、そのコンテキストによっては、変数 x.c:func1/foo と関数 y.c:foo の両方を参照することがあります。
unassert vars var ...
指定された変数を保護するロックについてのアサーションの設定をすべて元に戻します。関数を保護しているロックについてのアサーションを削除する手段は用意されていません。
vars [-aho] var ... vars [-ahilo] vars [-ahlo] protected by lock vars [-ahlo] [directly] read by func ... vars [-ahlo] [directly] written by func ... vars [-ahlo] [directly] accessed by func ...
読み込まれたファイルの変数についての情報をリストします。ただし、実際に使用されている変数のみが表示され、プログラム中で宣言はされていても、アクセスされることのない変数は表示されません。
表 A-9 vars のオプション
オプション |
定義 |
|
---|---|---|
-a |
(assert) assert mutex|rwlock protects サブコマンドの指定に従って、各変数がどのロックによって保護されることになっているかの情報を提示します。情報は以下のように表示されます。 assert=lock アサーションに違反する場合は、解析の後、行の先頭にアスタリスク(*) が付けられます (例:*assert=<lock>)。 |
|
-h |
(held) 変数がアクセスされていた間、どのロックが整合性を保持されていたかについての情報を提示します。この情報は、analyze サブコマンドの実行後に提示されます。変数が決してアクセスされない場合、この情報は提示されません。情報は次のように表示されます。 held={ <lock> ... } ロックが整合性を保持されておらず、変数に書き込みが行われた場合、行の先頭にアスタリスク(*) が付けられます (例:*held={ })。funcs とは異なり、vars サブコマンドは、ロックが実際には保持されていなくても、ほかのロックによってカバーされていれば、変数を保護しているものとしてロックをリストします (「assert」 の「assert rwlock covers」を参照してください)。 |
|
-i |
(ignored) 無視されている変数もリストします。 |
|
-l |
(long) -aho に相当します。 |
|
-o |
(other) 各変数について以下の情報を提示します (該当する場合)。 |
|
=cond_var |
この変数は状態変数として使用されています。 |
|
=ignored |
ignore サブコマンドを介して、ロック lint がその変数を無視するように明示的に指示されています。 |
|
=read-only |
(assert read only によって)ロック lint にはその変数が書き込み禁止であることが伝えられ、書き込みが行われた場合には警告を発します。書き込みが行われた場合は、解析後、その後ろにアスタリスク (*) が付けられます (例: =read-only*)。 |
|
=readable |
declare readable サブコマンドによって、その変数はロックを保持しなくても安全に読み取れることがロック lint に伝えられています。 |
|
=unwritten |
その変数が書き込み禁止と宣言されていない間も、書き込みが行われませんでした。この情報は解析後に現れます。 |
個々の変数についての情報をリストします。デフォルトでは、この形式のサブコマンドは、-aho が指定された場合と同様に、変数についての詳細情報をすべて提供します。
無視されていないすべての変数についての情報をリストします。-i が指定された場合、無視されている変数の情報も提示されます。
指定されたロックによって保護される変数のみをリストします。このサブコマンドは、analyze サブコマンドの実行後にのみ実行されます。
指定された関数の呼び出しの結果として読み取りが行われる変数のみをリストします。 以下の「directly について」を参照してください。
指定された関数の呼び出しの結果として書き込みが行われる変数のみをリストします。以下の「directly について」を参照してください。
指定された関数の呼び出しの結果としてアクセス (読み取りまたは書き込み) が行われる変数のみをリストします。
関数のリストによってアクセスされる変数をリストする形式では、directly が指定されると、指定された関数によって直接アクセスされる変数のみがリストされます。directly を指定しない場合、これらの関数によってアクセスされる変数、それらが呼び出す関数、またそれらの関数が呼び出す関数...、などがリストされます。