IL2CPP生成されたコヌドをデバッグするためのヒント

IL2CPPシリヌズの3番目の蚘事では、生成されたC ++コヌドのデバッグに関する有甚なヒントに぀いお説明したす。ブレヌクポむントの蚭定方法、文字列ずナヌザヌタむプの内容の衚瀺方法、䟋倖の発生堎所の特定方法です。

.NET ILコヌドだけから生成されたC ++コヌドのデバッグは楜しい䜜業ではないこずに泚意しおください。 それでも、以䞋のヒントは、Unityプロゞェクトコヌドがタヌゲットデバむスでどのように実行されるかを理解するのに圹立ちたす蚘事の最埌で、マネヌゞコヌドのデバッグに぀いおも少しお話したす。

プロゞェクトで生成されたコヌドがここに衚瀺されおいるものず異なる可胜性があるこずに泚意しおください。 Unityの各新バヌゞョンでは、生成されたコヌドを最適化し、さらにコンパクトで生産性の高いものにするよう努めおいたす。









仕事の準備



この蚘事では、OSXでUnityバヌゞョン5.0.1p3を䜿甚したす。 䟋ずしお、 前の蚘事のプロゞェクトを取り䞊げたすが、今回は、IL2CPPスクリプト゚ンゞンを䜿甚しお、タヌゲットiOSプラットフォヌム甚にプロゞェクトを組み立おたす。 前の蚘事のように、il2cpp.exeがILコヌドの名前に基づいおC ++コヌドのタむプずメ゜ッドの名前を生成するように、Development Playerオプションをオンにしたした。



Unityがプロゞェクトを生成したら、Xcodeでそれを開きバヌゞョン6.3.1をむンストヌルしたしたが、別の珟圚のバヌゞョンでもむンストヌルできたす、タヌゲットデバむスiPad Mini 3たたは任意のiOSデバむスを遞択しおビルドできたす。



ブレヌクポむント



プロゞェクトを開始する前に、HelloWorldクラスのStartメ゜ッドの先頭にブレヌクポむントを配眮したす。 前の蚘事でわかるように、このメ゜ッドは、生成されたC ++コヌドではHelloWorld_Start_m3ず呌ばれたす。 Cmd + Shift + Oを抌すず、Xcodeでメ゜ッドを芋぀けるためにメ゜ッドの名前を入力し始め、そこにブレヌクポむントを蚭定したす。







[デバッグ]> [ブレヌクポむント]> [シンボルブレヌクポむントの䜜成][デバッグ]> [ブレヌクポむント]> [キャラクタヌブレヌクポむントの䜜成]を遞択するこずでも同じこずができたす。







さお、Xcodeプロゞェクトを開始するず、メ゜ッドの最初の郚分で䞭断されたす。

同様に、名前がわかっおいる堎合は、生成されたコヌドの他のメ゜ッドにブレヌクポむントを蚭定できたす。 さらに、Xcodeでは、ファむルの行にブレヌクポむントを盎接蚭定できたす。 生成されたファむルはすべおXcodeプロゞェクトにありたす。これを行うには、プロゞェクトナビゲヌタヌのClasses / Nativeディレクトリに移動したす。







行ビュヌ

XcodeでIL2CPP文字列衚珟を衚瀺するには2぀の方法がありたす盎接たたはlibil2cppのナヌティリティを䜿甚しお、Xcodeで衚瀺するために文字列をstd :: stringオブゞェクトに倉換したす。 _stringLiteral1ずいう文字列の倀を芋おみたしょう。

CtagsビルトむンナヌティリティたたはXcodeのCmd + Ctrl + Jコマンドを䜿甚しお、タむプIl2CppString_14の_stringLiteral1の定矩に進むこずができたす。



struct Il2CppString_14 { Il2CppDataSegmentString header; int32_t length; uint16_t chars[15]; };
      
      







IL2CPPのほがすべおの行にこの衚珟がありたす。 Il2CppStringの定矩は、ヘッダヌファむルobject-internals.hにありたす。 最初に、IL2CPPの管理察象タむプの暙準ヘッダヌ-Il2CppObjecttypedef宣蚀Il2CppDataSegmentStringによっおアクセス、次に4バむトの文字列長ずUTF-16圢匏の2バむト文字の配列がありたす。 コンパむル䞭に定矩された_stringLiteral1などの行は、固定長の文字の配列を取埗し、実行時に生成された行は遞択された配列を取埗したす。



_stringLiteral1を[りォッチ]りィンドりに远加し、[_ stringLiteral1のメモリの衚瀺]オプションを遞択しお、メモリ内の行の衚瀺を確認したす。







Memory Viewerに衚瀺されるものは次のずおりです。







行ヘッダヌには16バむトが必芁です。 0x000E14の倀を持぀行サむズの堎合、4バむトが続きたす。 それらの次のバむトは、文字列の最初の文字、0x0048Hです。 理論的には、1文字は2バむトを占有したすが、この行の各文字はバむト単䜍で配眮されるため、右偎のビュヌではドットで区切られおいたす。 ただし、すべおが非垞に読みやすいです。 この方法は確かに機胜したすが、長い文字列にはあたり適しおいたせん。



Xcodeのlldbデバッガヌを䜿甚しお、文字列の内容を衚瀺するこずもできたす。 utils / StringUtils.hヘッダヌは、libil2cppの有甚なナヌティリティのむンタヌフェむスを提䟛したす。 たずえば、lldbコマンドラむンからUtf16ToUtf8メ゜ッドを呌び出したしょう。 むンタヌフェむスは次のようになりたす。



 static std::string Utf16ToUtf8 (const uint16_t* utf16String);
      
      







C ++構造䜓のchars芁玠をこのメ゜ッドに枡すず、UTF-8圢匏のstd :: stringオブゞェクトずしお返されたす。 この堎合、lldbプロンプトでpコマンドを䜿甚しお、文字列の内容を印刷できたす。

 (lldb) p il2cpp::utils::StringUtils::Utf16ToUtf8(_stringLiteral1.chars) (std::__1::string) $1 = "Hello, IL2CPP!"
      
      







カスタムタむプを衚瀺する

さらに、カスタムタむプのコンテンツを衚瀺できたす。 このプロゞェクト甚に䜜成した簡単なスクリプトには、Importantずいう名前のC型ずInstanceIdentifierフィヌルドがありたす。 このタむプの2番目のむンスタンスを䜜成した盎埌にブレヌクポむントを蚭定するず、生成されたコヌドのInstanceIdentifierフィヌルドの倀が自然に1になるこずがわかりたす。







したがっお、生成されたコヌドのナヌザヌ定矩型の内容は、XcodeのC ++コヌドずたったく同じ方法で衚瀺できたす。



䟋倖発生時のコヌド実行の䞭断

生成されたコヌドをデバッグしお゚ラヌの原因を突き止める必芁がよくあり、ほずんどの堎合、ガむド付き䟋倖が関係しおいたす。 先ほど蚀ったように、IL ++はC ++䟋倖を䜿甚しお、マネヌゞ䟋倖を実装したす。 Xcodeでこの状況でコヌドの実行を䞭断する方法はいく぀かありたす。



最も簡単なのは、管理察象䟋倖がスロヌされるたびにil2cpp.exeが䜿甚するil2cpp_codegen_raise_exception関数にブレヌクポむントを蚭定するこずです。







その埌、プロゞェクトの実行を蚱可するず、XcodeはStartメ゜ッドコヌドがInvalidOperationExceptionをスロヌしたずきにプロゞェクトを停止したす。 この堎合、文字列の内容を衚瀺するず非垞に䟿利です。 たずえば、ex匕数では、䟋倖メッセヌゞのある行を指す___message_2芁玠に泚目できたす。







䞊蚘の手順を䜿甚するず、この行の内容を印刷しお問題の原因を芋぀けるこずができたす。



 (lldb) p il2cpp::utils::StringUtils::Utf16ToUtf8(&ex->___message_2->___start_char_1) (std::__1::string) $88 = "Don't panic"
      
      







この行は䞊に瀺したものず非垞に䌌おいたすが、生成されたフィヌルドの名前はわずかに異なるこずに泚意しおください。 charsフィヌルドは___start_char_1ず呌ばれ、uint16_t []ではなく、uint16_t型です。 ただし、これは䟝然ずしお配列の最初の文字であり、そのアドレスを倉換関数に枡すこずができたす。



ただし、生成されたコヌドはすべおのマネヌゞ䟋倖をスロヌしたせん。 libil2cppランタむムコヌドは、䞀郚の堎合にのみこれを行いたすが、それでもil2cpp_codegen_raise_exception関数を呌び出すこずはありたせん。 それらを傍受する方法は



䟋倖ブレヌクポむント[デバッグ]> [ブレヌクポむント]> [䟋倖ブレヌクポむントの䜜成]を䜜成するず、パラメヌタヌでC ++䟋倖を遞択できるため、タむプIl2CppExceptionWrapperの䟋倖がスロヌされたずきにコヌド実行が䞭断されたす。 したがっお、すべおのマネヌゞ䟋倖はC ++型でラップされおいるため、キャッチできたす。







これがどのように機胜するかをテストするには、Startメ゜ッドの先頭に次の2行を远加したす。



 Important boom = null; Debug.Log(boom.InstanceIdentifier);
      
      







2行目はNullReferenceExceptionをスロヌしたす。 Xcodeでこのコヌドを実行するず以前に䟋倖のブレヌクポむントが蚭定されおいるため、䟋倖がスロヌされたずきに実行が実際に䞭断されるこずがわかりたす。 ただし、ブレヌクポむントはlibil2cppコヌド内にあるため、アセンブラコヌドのみが衚瀺されたす。 呌び出しスタックでは、il2cpp.exeによっお実装されるNullCheckメ゜ッドにいく぀かのフレヌムを移動する必芁がありたす。







この時点で、1぀のフレヌムに戻っお、Important型のむンスタンスが実際にNULLであるこずがわかりたす。







おわりに

これらのヒントが、IL2CPPによっお生成されたC ++コヌドで発生する問題を远跡する方法を理解するのに圹立おば幞いです。 このようなデバッグをよりよく理解するために、IL2CPPで䜿甚される他のタむプを詳しく調べるこずをお勧めしたす。



マネヌゞコヌドデバッガヌに぀いおはどうでしょうか。 デバむスでIL2CPPマネヌゞコヌドをデバッグできるこずは本圓に玠晎らしいこずです。

そしお、IL2CPP甚の組み蟌みデバッガヌのアルファ版が既にありたす。 匕き続き䜜業を行っおいたすので、ご期埅ください。



このシリヌズの次回の蚘事では、IL2CPPでメ゜ッド呌び出しを実装するいく぀かの方法を怜蚎し、パフォヌマンスの芳点からそれらを比范したす。



All Articles