【迷宮?謎解き?】デバッグに苦労した経験と学び3選
こんにちは、ムンペイです。
プログラミングを30年以上しています。プログラム高速化に関する経験をnoteに吐き出しています。サムライアプスという名前で中小企業のIT活用向けの情報発信もしています。
さて、デバッグもたくさんしてきましたが、特に苦労した経験事例を得た学びも交えて3つ挙げてみたいと思います。
1.マルチスレッド
高速化を目指すなら必須技術であるマルチスレッドは、デバッグしにくいパターンの代表格です。複数のことが同時進行しているので、問題が起こった場合に何が原因かわかりにくいためです。
例えば、データ更新を含む処理をマルチスレッドした場合、データ異常が発生することがあります。
データ更新の仕組みを細かく見ると、1)データをメモリからレジスタに読み込み、2)レジスタ上でデータ変更、3)変更後のデータをメモリに書き込み、の3段階で必ず進みます。ソースコードを見てもこのようには見えませんし、シングルスレッドの時は意識しなくてもよいのですが、マルチスレッドではこの3つの手順が分かれていることを認識していないとトラブルとなります。
例えば、「共通のメモリ」にあるデータを1増やすという処理を、AとBの2つのスレッドで同時並行して行うケースを考えてみます。「共通のメモリ」の初期値が1だとした場合、2つのスレッドがそれぞれ1増やすので、最終的には3になるのが期待となります。
このとき、スレッドAとBそれぞれに1)読み込み、2)更新、3)書き込み、の3手順があるので、スレッドAのどの手順のあとにスレッドBのどの手順が入り込むかはその時々の偶然の産物です。
図1の例では、2つのスレッドは同時に読み込みを行ってしまったので、2つとも「1から2に増やす処理」を行ってしまいました。そのまま書き込みが行われて、最終的な状態が3ではなく2になってしまいました。
このように、複数の処理が同時並行したために発生する異常が発生する状況を、レースコンディション(※1)と言います。
※1:レースコンディションについてはこちらに丁寧に書かれていましたのでご参照下さい。
レースコンディション対策としては「排他制御」をします。スレッドAがメモリにアクセスしている間は、スレッドBによるアクセスを禁止する、というものです。排他制御の詳細は割愛しますが、キーワードとしては、セマフォ(Semaphore)、ミューテックス(Mutex, Mutual Exclusion)、条件変数(Condition Variable)、アトミック変数(Atomic Variable)を挙げておきます。
また、レースコンディションによるデータ異常の多くは、計算結果の異常として観測されることが多いでしょう(※2)。マルチスレッドの問題だと思って見直していたら、実は本来の計算アルゴリズムに問題があったという可能性もあって、デバッグが迷走しかねません。ですので、マルチスレッドを予定しているプログラムでも、シングルスレッドで計算するモードを用意しておくとよいでしょう。シングルスレッドの結果を正として、マルチスレッドのデバッグができます。マルチスレッド実装してスレッド数を1に設定するのでも構いません。
※2:他にはクラッシュするケースもあるでしょう。
2.ファームウェア
ファームウェアとは、コンピュータが起動した直後に実行されるプログラムで、コンピュータが備えるメモリ、ディスク、画面、ネットワークなど、非常に基本的なものも含めた各種のデバイスが正しく動くように設定する仕事をします。
私の場合、組み込み機器向けのファームウェア開発でした。
「組み込み機器」とは家電や医療機器、産業機械など、コンピュータを組み込んだ各種製品です(定義は一定ではないようです)。組み込み機器向けのコンピュータはその製品に関わる処理だけをするようものであり、いわゆるPCのような汎用的な利用はされないのが特徴です。
ファームウェアは、非常に基本的なデバイスの設定を担うので、デバッグが大変です。実行中の状態を出力しようと思っても、まだモニターに信号を出力することができません。組み込み機器ではそもそもモニターもない場合が多いので、RS-232Cによるシリアル通信を備えていることがほとんどですが、シリアル通信も設定できていない段階で何か不具合がありデバッグしなければなりませんでした。
その時どうしたかと言いますと、基板上のLEDをチカチカさせて対応しました。電源が入っていることを示すLEDか何かだったと思いますが、確認したい変数の値をモールス信号のような考え方でパルスに代えて、LEDをチカチカ(Lチカ、※3)させました(図2)。
※3:ちなみに、Lチカは電子工作界隈のハローワールドと言われています(自分調べ)
もちろんコンピュータの速度でチカチカするLEDを人間が目視することはできませんので、オシロスコープでLチカを観測します。そしてその信号が、期待通りであるかを確認しました。
観測できないものは制御できない、という言葉を改めて噛み締めるとともに、どんな方法絵も
3.スマホアプリ
とあるお客様の製品である機器と組み合わせて使うアプリを、AndroidとiOS向けに開発しました。大きな会社の製品でしたので、それぞれ100万ダウンロードを大きく超える利用があり、非常に達成感もある仕事でした。
しかし、ユーザーの持っているスマホの機種は非常に様々で、発生する挙動の違いへの対応は大変でした。特にAndroidは機種数が膨大で、かなり絞り込んでも数十程度になっていました。また、アプリ内に無線LAN(WiFi)の制御機能があったのですが、機種ごとに挙動が違うようで、とても苦労しました。おそらく恐らく使われているチップの違いからくる、反応速度、待ち時間、検出性能、などなどが要因と思われますが、膨大な数をすべて調べるわけにもいかず対症療法を繰り返すことになりました。
もう10年くらい前なので、いまはそのような挙動の違いも標準化が進んで改善されているのではないかと思いますが、スマホの使い方やユーザーごとに違うのでスマホアプリの開発は大変です。
このようなパターンでは発生するケースを想定しきることができず事前の対処は難しいので、実行時データを集める仕組みを作りこむことが重要だと考えます。テレメトリーと言ったりします。「利用状況をメーカーに送っていいですか?」というあれです。GoogleやAppleの開発者サービスに含まれているはずです。前述の開発中も状況の把握や対処にとても役に立ちました。
広告や商売のためにトラッキングされているという印象が強くなりすぎて何かと嫌われてしまっていますが、製品開発者にとっては非常に重要な情報源となるので協力したいですね。
コンピュータ、そしてユーザーを想像する
3つのデバッグ経験を書きましたが、こうして改めてまとめてみますと、結局はコンピュータがいかに動いているか、どのようなことができるのか知っていることにより、デバッグ時の発想も柔軟になってくると感じました。
もう一つ、開発している自分の環境ではなく、利用されるときの環境を想像することも、不具合の発生の仕方を想像するためには重要ですね。スマホアプリなら100万人以上のことを想像する・・・。
でも、無事にデバッグを達成し、そして利用者からありがとうの声が聞かれたらこれほどうれしいことはないですね。この話はまたの機会に・・・。
これにて御免!
(見出し画像はChatGPTで生成しました)