こんにちは､ ようこそ｡
この講義では､ コールスタックについて少しお話したいと思います｡ コールスタックとは一体何なのか､ そしてそれをどのように読めばプログラムのデバッグにさらに役立てることができるのか｡
スタックトレースとは､ 本来はコールスタックのことで､ 致命的なエラーでアプリがクラッシュするたびに生成されます｡
スタックという言葉が使われるのは､ プログラミングでよく使われるデータ構造を指しているからです｡
スタックとは､ LIFO､ Last in, First outと呼ばれるデータ構造です｡
最後に追加されたデータエレメントを最初に取り出すと､ そのデータエレメントが戻ってくる｡
パンケーキを重ねるように食べると､ 最初のパンケーキが一番下にくるので､ それを追加するイメージです｡
最後のパンケーキは一番上にあります｡
食べようとすると､ 最後に追加されたものを食べてしまうのです｡
そして､ スタックトレースにはデバッグに役立つ情報が含まれているため､ プログラムがクラッシュしたときに非常に役に立ちます｡
つまり､ スタックトレースには､ エラーに至った関数呼び出しのリストが表示されるわけです｡
そして､ プログラムの中でmain関数を呼び出すと､ そこがエントリーポイントになるのですが､ その時に何が起こるのでしょうか？
メイン関数は他の関数を呼び出すことになります｡
これらの関数は他の関数を呼び出し､ 実装されたり呼び出されたりした関数はすべてスタックに置かれることになります｡
プログラムがクラッシュしたとき､ コールスタックを見れば､ 最後に呼び出された関数と､
エラーの原因となった特定のコード行がわかるのです｡
だから､ コールスタックには､ いい情報がたくさん詰まっているんです｡
例外や致命的なエラーが発生したコードのファイル名と行番号が記載されます｡
スタックの最上部には､ エラーの原因となった最後の呼び出しが含まれており､ 呼び出しが入れ子になっている可能性があります｡
スタックの一番下には､ エラーを呼び出すための呼び出しの連鎖を開始した最初の呼び出しが含まれています｡
つまり､ C言語では通常､ メインルーチンでmainを呼び出し､ mainが別の関数を呼び出し､ その関数がまた別の関数を呼び出すといった具合に､
mainを呼び出します｡
そして､ 最後の一行が細かい｡
app for appがクラッシュした時にとても､ 助かります｡
そして､ Cプログラムがクラッシュしたとき､ セグメンテーションフォールトを生成することがあり､ それは多くの情報を教えてくれません｡
ただ､ 何か悪いことが起こったということです｡
それによって､ コアファイルも生成されます｡
コールスタックを含むコアファイルを解析することで､ クラッシュの原因となった正確なコード行を知ることができます｡
ですから､ クラッシュの原因となっているアプリケーションの呼び出しを見つける必要があります｡
そして､ コールスタックは､ その最たるものでしょう｡
そして､ そのコアファイルを常に見る必要はない｡
また､ デバッガでGDBを使用することで､ コールスタックを解析することができます｡
そして､ これがどのようなものなのかをお見せしたいと思います｡
また､ プログラマーはいつでもスタックトレースをダンプすることができます｡
そのため､ エラーをキャッチした場合､ あるいは致命的なエラーが起こるかもしれないと認識した場合､ コールスタックをダンプして何が起こっているのかを確認することができます｡
実際にいつエラーが起こるかわからない場合でも､ コールスタックをダンプして､
特定の呼び出しに至る現在のトレースを確認することができます｡
GDBを使用する場合､ コールスタックは次のようになります｡
GDBでデバッガーを実行しているときにメニューからコールスタックにアクセスできます｡
これからその方法を紹介しますが､ このような感じで､ 数字､ アドレス､ そして関数が書かれています｡
この特定のコールスタックには､ さまざまな呼び出しがあるので､ さまざまなネストとさまざまな関数呼び出しを行っていたのでしょう｡
しかし､ スタックの一番下の22行目には､ GTK mainと書かれています｡
を意味します｡
つまり､ メイン関数を実行したのだろうということです｡
そして､ 21行目で､ 実行されたようです｡
メインループの実行という文言があるのかもしれませんね｡
そして､ G main context iterateという関数を呼び出したようです｡
そして､ その関数の中で､ IAGのメインコンテキストディスパッチ関数が呼び出されるたびに､
何度かあったのです｡
つまり､ iterate関数があって､ 次にdispatch関数があって､ 18行目のクエスチョンマークはデバッグ情報がないってことですね｡
そこで何が起こったのかがわからない｡
そして､ プログラムをビルドするときに､ リリース用にビルドするか､ デバッグ用の情報を入れてビルドするかのどちらかになるのはそのためです｡
デバッグ情報を入れてビルドすると､ コンパイル時に余計な情報が入ってしまうので､ 実行時にコールスタックを見ることができるようになります｡
リリースするときは､ リリースするためのものを作る｡
実行ファイルはもっと小さいので､ この情報が含まれていないかもしれません｡
クエスチョンマークが多く含まれる場合があります｡
しかし､ ライブラリや追加プログラムを呼び出した場合は､ それらを呼び出して､ そこからコールスタックを教えてくれます｡
しかし､ これが示しているのは､ 最上部の呼び出しに対するさまざまな入れ子のイベントが重要であるということです｡
つまり､ この特定のコールスタックでは､ インナーリフレッシュが問題を引き起こしていたようです｡
そして､ 22行目から0行目まで､ さまざまなコードが呼び出されていることがお分かりいただけると思います｡
しかし､ 最終的に0行目のコールスタックでプログラムがクラッシュしてしまうのです｡
また､ プログラムの実行中にコールスタックを見ることで､ 情報を確認することができますので､ 追跡することができます｡
だから､ 関数の中にブレークポイントを置けば､ デバッグする､ その上でデバッガを走らせる｡
デバッガは､ そのブレークポイントで停止することになります｡
コールスタックを表示させれば､ そのコード行に至った経緯を知ることができます｡
この関数は､ この関数の前に呼び出されたものである｡
そしてまた､ 通常はメイン機能から始まることになります｡
また､ スタックや概念､ メモリ内の様子を熟知していれば､ 別のフレームに切り替えて見ることもできますが､
これはC言語で主にクラッシュを見つけるのにとても便利なツールで､ とても見やすいです｡
上だけを見て､ コールスタックについてあまり理解していなくても､ 上だけを見て､
ああ､ これはソースだ､ クラッシュを引き起こしたコード行だ､ と言うことができる｡
ウェブで見つけた素敵な例をご紹介します｡
エクスマです｡
エデュ｡
これは､ 関数呼び出しスタックの例を示しています｡
そこで､ コードで例を見てみると......簡単なコードしかありません｡
計算はどうでもいいんです｡
基本的にはいくつかの関数を呼び出すだけです｡
mainはmogrify関数を呼び出し､
次にtrue half関数を呼び出し､
さらにmogrify関数を呼び出します｡
だからまた､ ブレークポイントやそういう情報に詳しくなるんです｡
ただ､ コールスタックのデモを手短に行いたいだけで､ 実際にはコールスタックの詳細な例を後で説明します｡
そこで､ ここではブレークポイントを設定しました｡
このままビルドしてコンパイルし､ デバッガーを起動すればブレークポイントを押すことができます｡
そして､ 右側の下部にあるデバッガーメニューがすべて表示され､ コールスタックが表示されることにお気づきでしょうか｡
これをもう少し大きくしてみましょう｡
しかし､ 14行目で停止した時点で､ ここにはいくつかの代入文があるだけで､ コールスタックにはmainが表示されているのがわかると思います｡
それを踏み越えて次の行に行くと､ Mogrifyを呼び出す前に15行目まで行っていることがわかりますね？
そうすると､ コールスタックがMainに残っていることがわかります｡
踏み込んでみると､ 今度はコールスタックにMogrifyが表示されていることがわかります｡
つまり､ 実際にこの関数の中に入っているわけです｡
それを踏み越えることができる｡
つまり､ 1行目を実行し､ tempを返すということですね｡
まだその関数にとどまっていて､ 16行目のprint Fを実行したところで戻ってくるのですが､ コールスタックがどのように変化しているかがわかります｡
その後､ 18行目に行き､ mainでスタックスチールを呼び出し､ 真に半分の関数に足を踏み入れます｡
そして今､ コールスタックが変化し､ 一番上にある機能の半分が常に最後に呼び出された機能であることを如実に示しているのがわかります｡
もう一度､ 踏み込んでみると､ 私たちはまだ本当に半分の位置にいることがわかります｡
Mainに戻り､ 19行目に何かをプリントアウトし､ 21行目に行く｡
再びmodify関数に飛びますが､ これもコールスタック上に表示されます｡
しかし､ コールスタックは再び､ ある関数から次の関数へと呼び出される順序を示す｡
そのため､ 関数を入れ子にしてしまうと､ どんどん複雑になってしまうことがあります｡
コールスタックの利点は､ 例えば4行目でクラッシュしたコードがあった場合､ その行がクラッシュした場所を一番上に含むコールスタックを持つことができることです｡
コールスタックを調べれば､ 最後に呼び出されたのはどのコード行か､ それによってどのコード行がクラッシュしたかがわかるのです｡
しかし､ プログラムがクラッシュしたときに､ このツールを使いたくなるはずです｡
ありがとうございます｡