デバックモードで実行してブレイクポイントで止めるとAccess Violation エラーがでないため、特定できません。
ブレイクポイントで止めないと何回かに1回出てしまうというものです。
そのため確実に出す方法もわからず困っています。
Access Violationのエラーが出てしまう場所の特定をしたいのですが、エラーの情報から特定できますでしょうか?
例えばAccessViolation at address ------ Read of address ------
の-----の部分から特定したり、他になにか良い方法等ありますか?
よろしくお願いします。
とりあえず
・エラーで止まったところから履歴ウィンドウで遡る(表示>デバッグ>呼び出し履歴)
・履歴が得られないようなら、デバッグ用ライブラリを使ってみる
(プロジェクト>オプション>コンパイラで「デバッグ用DCUを使う」をチェックして再構築)
・アクセス違反は範囲外アクセスに起因していることが多いので「範囲チェック」を有効にして実行する
あたりを試してみてはどうでしょう。
まず、プロジェクトオプションのリンカにある、マップファイルの項目で「詳細」を選択します。
この状態でコンパイルすると、<プロジェクト名>.mapというファイルができます。
これはテキストファイルで、メモ帳など開くと、その先頭が
Start Length Name Class
0001:00401000 00247960H .text CODE
0002:00649000 00002174H .itext ICODE
0003:0064C000 00016C04H .data DATA
0004:00663000 000304A4H .bss BSS
0005:00000000 00000040H .tls TLS
のようになっているので、1行目のStartのアドレス、00401000の部分をメモします。
(ほとんどこの数値だと思いますが、違う場合もあるかもしれません)
で、エラーメッセージの"AccessViolation at address $AAAAAAAA"の
$AAAAAAAAからメモした値(つまり00401000)を引きます。
たとえば、メッセージのアドレスが$0051A084だったら
$0051A084 - $00401000 = $00119084 となります。
次に、マップファイルの
Detailed map of segments
0001:00000000 00006C55 C=CODE S=.text G=(none) M=System ACBP=A9
0001:00006C58 00000105 C=CODE S=.text G=(none) M=SysInit ACBP=A9
0001:00006D60 00000145 C=CODE S=.text G=(none) M=Types ACBP=A9
0001:00006EA8 0000113C C=CODE S=.text G=(none) M=Windows ACBP=A9
0001:00007FE4 00000315 C=CODE S=.text G=(none) M=ActiveX ACBP=A9
・
・
・
の部分から、この値(例では$00119084)が含まれるユニットを探します。
計算されたアドレスが$00006EA8から$00007FE4の間であれば、Windowsユニットと
いうことになります。このリストはアドレス順なので、すぐに分かるでしょう。
で、ユニット名が分かったら、そのユニット名で検索するとマップファイルの終わりの方に
Line numbers for <ユニット名>(<ユニット名>.pas) segment .text
52 0001:0024682C 53 0001:00246850 54 0001:0024685D 55 0001:0024686A
57 0001:0024687A 58 0001:0024689B 59 0001:002468A9 60 0001:002468C5
・
・
・
という部分が見つかります。
ここで、また計算されたアドレスを探します。
ドンピシャの箇所があれば、その前に書かれた数値がソースの行数です。
ピッタリ一致しなくても、次の行のアドレスより小さければその行となります。
例えば、計算されたアドレスが$00246858だったら、上のリストでは00246850と
0024685Dの間なので、"53 0001:00246850"の部分が該当し、53行目という事になります。
もし、出てきたユニット名が自分で作ったものではない(SystemやWindows等の)場合は
標準の関数や手続きを呼び出すときに、メモリ領域確保や初期化をしてない等が考えられます。
自作のユニットの場合は、該当の行辺りで原因を探せるでしょう。
回答ありがとうございます。
私のほうで1行目のStartを確認してみました。
Start
0001:00000000 00132DA8H .text CODE
となっていましたので、00000000がQuestさんの言っているアドレスですね。
実際に出ているエラーはAccessViolation at address 780149CEなのですが、
780149CE - 00000000 = 780149CEとなります。
ただ780149CEがDetailed map of segmentsに該当する箇所がありません。
まず、00000000がおかしいのでしょうか?
こういったこともありますか?
プロジェクトオプションのリンカにある、メモリーサイズのイメージベースは
いくつになっていますか?
ここの値が(ほぼ)コードのスタートアドレスになると思います。
ただこの場合は、アドレスの範囲が00000000〜00132DA8なのに
780149CEなんてアドレスを実行しているので、とんでもない場所を
アクセスしている事になります。
Createせずに呼び出そうとしているクラスのメンバーとか、動的にDLLを読み込んで
関数の割り当てに失敗したまま呼び出しているとか、心当たりはないでしょうか。
エラーが出るときに行っている処理から、ある程度ユニットが絞れませんか?
ブレークポイントで止めればエラーが出ないという事であれば、タイミングに
絡んだ問題かもしれません。
タイマーイベントとか、フォームなどのイベントが発生する順番が、想定した順番どおりに起きていないとか。
Questさん
イメージベースは$00400000になっています。
Createせずに呼び出していればその時点でAccess Violationになるので、それが原因ではない気がします。
確かにQuestさんいわれているとおり、タイミングで出る問題と予想できますので、確実に出る方法がわかるず、なかなかつかめない状況にあります。
エラーが出るタイミングとしてはCloseしたときに出ます。
Closeしたときにエラーが何個も出続けてしまうことから、Timerなどでエラーになっているのではないかと考えています。
ですが、ここに書き込みしてからまったく調子が良くなってしまい、エラーが出ません。
とにかくもう一度コードを確認し、色々な処理を行って、エラーが出るように見てみます。
イメージベースが$0040000なのに、
Start
0001:00000000 00132DA8H .text CODE
ってのは変ですねぇ。
うーん、すみません、これ以上突っ込むノウハウは持ってないです。^^;
Closeしたときにエラーになるって事は、2重にFreeしてしまうクラスやモジュールがありませんか?
再現できない不具合って、本当に厄介ですね。
やはりあれ以降エラーが出なくなったので、ソフトを変更しているうちに解消されたのかな(こういう考えはよくないですが)と思います。
とりあえずはこういった調べ方があるのか、ということがわかり、勉強になりました。
ありがとうございます。
ツイート | ![]() |