例としてMaNGOSでGDBを使用する

あなたが、同時に多くの乗客がいる巨大な宇宙船の船長であり、整備士であると想像してください。 しかし、これは理想的な船ではなく、老朽化し​​た船です。 はい、多くは機能しますが、多くの修正が必要です。 問題は、どうやって? 船を最小のボルトに分解し、その配置方法、あるボタンまたは別のボタンを押したときにその内部で何が起こるか、本質、その魂を感じることを可能にするような楽器があります。 さらに、問題を修復する時点で時間を止めて、すべてが棚に置かれるようにすることが理想的です。 結局のところ、内部で何が起こっているのかを理解するのはそれほど簡単ではありません!

以前は、マンゴーなどのモンスターの中に入るのが怖かったので、研究所でコンピューターサイエンスのタスクを準備するときだけデバッガーを使用しました。 しかし、多くのコードがあっても、すべてがはるかに単純であり、心配することはありませんでした。 GDBを使用して1つのMMORPGの人気のあるエミュレーターの内部に入り、その作業を内部から見る方法を説明しようとします。 主なものは、理性の範囲内で、実験することを恐れないことです。 GDBが何であるかを知っている人は、ここで新しいことを見つけることはありません。 このアイデアは、パラディンとしての「最愛の」クラスの全員によるゲームによって促されました。 実際、ゲームのスクリーンショット:







何が見えますか? スペルの説明では、152〜172のダメージですが、実際には232です。追加のダメージはどこから来たのですか? GNUデバッガーはこれを理解するのに役立ちました。



サーバーアセンブリ



開始するには、cmakeを使用してサーバー自体を構築しましょう。 実際、すべてが標準です:

mkdir build cd build cmake .. make -j4 make install
      
      





DEBUG = 1変数を設定せずにデバッグの不可能性に焦点を合わせるために、意図的にサーバーをデバッグモードでビルドしませんでした。

アセンブリには最初の問題がありました。 Cmakeは正常にメイクファイルを準備しましたが、コンパイルにエラーが発生しました。つまり、ADAPTIVE Communication Environment(ACE)とTBBは収集されませんでした。 Autotoolsが事前に実行されているため、これらのライブラリは集まっていますが、これがgentushnikのやり方です。 すでに収集されてリポジトリにあるものを収集するのはなぜですか? その結果、変数ACE_USE_EXTERNAL = 1を追加しますTBB_USE_EXTERNAL = 1

OK、Cmakeコンソールですべてを取得しました。

 -- Found ACE library: /usr/lib/libACE.so -- Found ACE headers: /usr/include -- Found Intel TBB -- Using mysql-config: /usr/bin/mysql_config -- Found MySQL library: /usr/lib/libmysqlclient_r.so -- Found MySQL headers: /usr/include/mysql -- Found OpenSSL library: /usr/lib/libssl.so -- Found OpenSSL headers: /usr/include/openssl -- Found ZLIB: /usr/include
      
      





ライブラリを構築するのではなく、OSでライブラリを使用するようリンカーに指示しました。 キッチンでお茶を飲みながら、すべてが集まった。 デバッガーの下でサーバーを起動します。

 gdb ./mangos-world GNU gdb (GDB) 7.0.1-debian Copyright (C) 2009 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /home/gabriel/projects/cpp/mangos-server/bin/mangos-world...(no debugging symbols found)...done. (gdb) r
      
      





数秒後に、全世界のダウンロードとサーバーが起動したという碑文に関するスパムがコンソールに送信されました。

 WORLD: World initialized SERVER STARTUP TIME: 0 minutes 2 seconds [New Thread 0x7fffea326700 (LWP 8911)] [0 ms] SQL: UPDATE realmlist SET color = 0, population = 0, realmbuilds = '5875 6005 ' WHERE id = '1' [New Thread 0x7fffe9a25700 (LWP 8912)] engine: Max allowed socket connections 1024 [New Thread 0x7fffe9224700 (LWP 8913)] Network Thread Starting [New Thread 0x7fffe8a23700 (LWP 8914)] Network Thread Starting
      
      





rとは何ですか? これは実行、つまり起動です。 Ctrl + Cを押して、サーバーではなくデバッガーコンソールに戻り、最初のブレークポイントを設定します。

 (gdb) b Unit::DealDamage Can't find member of namespace, class, struct, or union named "Unit::DealDamage" Hint: try 'Unit::DealDamage<TAB> or 'Unit::DealDamage<ESC-?> (Note leading single quote.) Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (Unit::DealDamage) pending. (gdb) c Continuing.
      
      





b-停止する場所をデバッガーに指示するブレークポイント、 c-続行、続行

Unit :: DealDamage関数にブレークポイントを作成し、その中にある名前によって呪文の効果が適用されることを判断しましたが、これは純粋に仮定であり、真実であることが判明しました。 ちなみに、デバッガは関数の自動補完をサポートし、TABボタンが役に立ちました)今後の共有ライブラリのロードでブレークポイントを保留にするという警告に混乱しましたか? (yまたは[n])、英語の基本的な知識は、共有ライブラリの読み込みに応じてブレークポイントを作成することをデバッガが示唆していることを理解するのに役立ちました(翻訳は難しいですが、unixoidは十分に明確です)。 それから私はゲームに入り、パラディンを作成し、悪魔払いを教え、アンデッドを殺すためにテレポートします。







興味深いことに、ブレークポイントは機能しませんでした! これは、リリースでサーバーをまとめたためです。 -DDEBUG = 1を追加

私たちの世界では、型の不一致に関するゴミがコンソールにあるため、少し時間がかかりますが、最終的には準備が整いました。 再度、デバッガーを実行し、ブレークポイントを設定します。 サーバーに完全にロードされたことに注意してください( デバッグシンボルが見つからないというフレーズはありません)。 宣誓なし:

 (gdb) b Unit::DealDamage Breakpoint 1 at 0xb3d91a: file /home/gabriel/projects/cpp/mangos/src/game/Unit.cpp, line 479.
      
      





サーバー内部のピッキング



どうぞ

私たちは悪魔払いを押して...ゲームはハングしますが、コンソールには次のものがあります:

 Breakpoint 1, Unit::DealDamage (this=0x133c000, pVictim=0x7fffe80f6080, damage=599, cleanDamage=0x7fffeaa4b680, damagetype=SPELL_DIRECT_DAMAGE, damageSchoolMask=SPELL_SCHOOL_MASK_HOLY, spellProto=0x7fffefdb3010, durabilityLoss=true) at /home/gabriel/projects/cpp/mangos/src/game/Unit.cpp:479 479 if(pVictim != this)
      
      





説明では少なくなっていますが、ダメージはすでに増加しています(599)。 トレースを確認します。

 (gdb) bt #0 Unit::DealDamage (this=0x133c000, pVictim=0x7fffe80f6080, damage=599, cleanDamage=0x7fffeaa4b680, damagetype=SPELL_DIRECT_DAMAGE, damageSchoolMask=SPELL_SCHOOL_MASK_HOLY, spellProto=0x7fffefdb3010, durabilityLoss=true) at /home/gabriel/projects/cpp/mangos/src/game/Unit.cpp:479 #1 0x0000000000b410bd in Unit::DealSpellDamage (this=0x133c000, damageInfo=0x7fffeaa4b6f0, durabilityLoss=true) at /home/gabriel/projects/cpp/mangos/src/game/Unit.cpp:1331 #2 0x0000000000a21f66 in Spell::DoAllEffectOnTarget (this=0x7fffe7ee3d80, target=0x7fffe7f2bf80) at /home/gabriel/projects/cpp/mangos/src/game/Spell.cpp:1006 #3 0x0000000000a28912 in Spell::handle_immediate (this=0x7fffe7ee3d80) at /home/gabriel/projects/cpp/mangos/src/game/Spell.cpp:2804 #4 0x0000000000a286b2 in Spell::cast (this=0x7fffe7ee3d80, skipCheck=false) at /home/gabriel/projects/cpp/mangos/src/game/Spell.cpp:2769 #5 0x0000000000a29424 in Spell::update (this=0x7fffe7ee3d80, difftime=100) at /home/gabriel/projects/cpp/mangos/src/game/Spell.cpp:2982 #6 0x0000000000a34307 in SpellEvent::Execute (this=0x7fffe7f3bee8, e_time=5167, p_time=100) at /home/gabriel/projects/cpp/mangos/src/game/Spell.cpp:5992 #7 0x0000000000c77b8b in EventProcessor::Update (this=0x133c118, p_time=100) at /home/gabriel/projects/cpp/mangos/src/framework/Utilities/EventProcessor.cpp:34 #8 0x0000000000b3cd04 in Unit::Update (this=0x133c000, update_diff=100, p_time=100) at /home/gabriel/projects/cpp/mangos/src/game/Unit.cpp:300 #9 0x000000000096f66e in Player::Update (this=0x133c000, update_diff=100, p_time=100) at /home/gabriel/projects/cpp/mangos/src/game/Player.cpp:1124 #10 0x0000000000940bbb in WorldObject::UpdateHelper::Update (this=0x7fffeaa4bce0, time_diff=100) at /home/gabriel/projects/cpp/mangos/src/game/Object.h:404 #11 0x0000000000ae873c in Map::Update (this=0x2d14000, t_diff=@0x7fffe9a17e40) at /home/gabriel/projects/cpp/mangos/src/game/Map.cpp:446 #12 0x0000000000c5a8a6 in MapUpdateRequest::call (this=0x7fffe9a17e20) at /home/gabriel/projects/cpp/mangos/src/game/MapUpdater.cpp:61 #13 0x0000000000c76911 in DelayExecutor::svc (this=0x7fffeb8258d0) at /home/gabriel/projects/cpp/mangos/src/shared/DelayExecutor.cpp:57 #14 0x00007ffff7b77847 in ACE_Task_Base::svc_run(void*) () from /usr/lib/libACE-5.7.7.so #15 0x00007ffff7b78bc1 in ACE_Thread_Adapter::invoke() () from /usr/lib/libACE-5.7.7.so #16 0x00007ffff5e138ca in start_thread () from /lib/libpthread.so.0 #17 0x00007ffff5b7a86d in clone () from /lib/libc.so.6 #18 0x0000000000000000 in ?? ()
      
      





bt 、おそらく既に推測したように、これはバックトレースです。 関数呼び出しを少し調べて、Spell :: DoAllEffectOnTarget関数のdamageInfo変数の変化を監視する必要があることに気付きました。 この関数にブレークポイントを設定し、古いものと、アイテムおよびGOの余分なブレークを削除します。

 (gdb) b Spell::DoAllEffectOnTarget Breakpoint 2 at 0xa22e57: file /home/gabriel/projects/cpp/mangos/src/game/Spell.cpp, line 1257. Breakpoint 3 at 0xa22cef: file /home/gabriel/projects/cpp/mangos/src/game/Spell.cpp, line 1230. Breakpoint 4 at 0xa21668: file /home/gabriel/projects/cpp/mangos/src/game/Spell.cpp, line 860. warning: Multiple breakpoints were set. Use the "delete" command to delete unwanted breakpoints. (gdb) d 1 (gdb) d 2 (gdb) d 3
      
      





ゲームの新しいハング、関数内にあり、内部のエントリで行ごとに実行します:

 Breakpoint 4, Spell::DoAllEffectOnTarget (this=0x7fffe7ecfd80, target=0x7fffe7ef7f80) at /home/gabriel/projects/cpp/mangos/src/game/Spell.cpp:860 860 if (m_spellInfo->Id <= 0 || m_spellInfo->Id > MAX_SPELL_ID || m_spellInfo->Id == 32 || m_spellInfo->Id == 80) (gdb) list 855 m_UniqueItemInfo.push_back(target); 856 } 857 858 void Spell::DoAllEffectOnTarget(TargetInfo *target) 859 { 860 if (m_spellInfo->Id <= 0 || m_spellInfo->Id > MAX_SPELL_ID || m_spellInfo->Id == 32 || m_spellInfo->Id == 80) 861 return; 862 863 if (!target || target == (TargetInfo*)0x10 || target->processed) 864 return; (gdb) n 10 884 unitTarget = unit; ... 985 caster->CalculateSpellDamage(&damageInfo, m_damage, m_spellInfo, m_attackType); (gdb) print m_damage $4 = 535 (gdb) s Unit::CalculateSpellDamage (this=0x133c000, damageInfo=0x7fffeb24c6f0, damage=535, spellInfo=0x7fffefdb3010, attackType=BASE_ATTACK) at /home/gabriel/projects/cpp/mangos/src/game/Unit.cpp:1227 1227 SpellSchoolMask damageSchoolMask = GetSchoolMask(damageInfo->school); ... 1265 damage = SpellDamageBonusDone(pVictim, spellInfo, damage, SPELL_DIRECT_DAMAGE); (gdb) s Unit::SpellDamageBonusDone (this=0x133c000, pVictim=0x7fffe80f6080, spellProto=0x7fffefdb3010, pdamage=535, damagetype=SPELL_DIRECT_DAMAGE, stack=1) at /home/gabriel/projects/cpp/mangos/src/game/Unit.cpp:5345 5345 if(!spellProto || !pVictim || damagetype==DIRECT_DAMAGE ) ... 5425 DoneTotal = SpellBonusWithCoeffs(spellProto, DoneTotal, DoneAdvertisedBenefit, 0, damagetype, true); (gdb) s Unit::SpellBonusWithCoeffs (this=0x133c000, spellProto=0x7fffefdb3010, total=0, benefit=0, ap_benefit=0, damagetype=SPELL_DIRECT_DAMAGE, donePart=true) at /home/gabriel/projects/cpp/mangos/src/game/Unit.cpp:5302 5302 if (GetTypeId()==TYPEID_UNIT && !((Creature*)this)->IsPet()) (gdb) n 5305 else if (SpellBonusEntry const* bonus = sSpellMgr.GetSpellBonusData(spellProto->Id)) (gdb) n 5307 coeff = damagetype == DOT ? bonus->dot_damage : bonus->direct_damage; (gdb) n 5310 if (donePart && (bonus->ap_bonus || bonus->ap_dot_bonus)) (gdb) n 5312 float ap_bonus = damagetype == DOT ? bonus->ap_dot_bonus : bonus->ap_bonus; (gdb) n 5314 total += int32(ap_bonus * (GetTotalAttackPowerValue(IsSpellRequiresRangedAP(spellProto) ? RANGED_ATTACK : BASE_ATTACK) + ap_benefit)); (gdb) print ap_bonus $5 = 0.150000006 (gdb) n 5321 if (benefit) (gdb) n 5336 return total; (gdb) print total $6 = 68 (gdb) n 5337 }; (gdb) n Unit::SpellDamageBonusDone (this=0x133c000, pVictim=0x7fffe80f6080, spellProto=0x7fffefdb3010, pdamage=535, damagetype=SPELL_DIRECT_DAMAGE, stack=1) at /home/gabriel/projects/cpp/mangos/src/game/Unit.cpp:5427 5427 float tmpDamage = (int32(pdamage) + DoneTotal * int32(stack)) * DoneTotalMod; (gdb) n 5429 if(Player* modOwner = GetSpellModOwner()) (gdb) print tmpDamage $7 = 603 (gdb) c
      
      





あなたを退屈させないために、損傷の変化を行ごとに監視したため、コンソール出力の一部を見逃しました。 コマンドについて簡単に説明します。

n-次に、内部に入ることなくコードの次の行を実行します

s-ステップ、同じこと、関数の内部に入る

l-リスト、コードの一部を印刷

p-印刷、変数を印刷します。

その結果、スペルの説明にあるはずの基本ダメージが535であり、追加のダメージがUnit.cppファイルの5314行目で計算され、APの15%であることがわかりました。 なぜそう ゲームの仕組み。 合計で603が判明し、ゲーム内で飛び出しました。

APからのスクリーンショット:







それだけです。ライブプロジェクトでnixの下でデバッガを操作する基本を簡単かつ明確に説明したいと思います。



参照資料



GBDを使用したデバッグ-GDB 、Richard Stallman、Roland Pesh、Stan Shebsなどの詳細な説明。

MangosプロジェクトはC ++を学ぶのに最適な場所です



UPD:

メジェディから:

「今後の共有ライブラリのロード時にブレークポイントを保留にするという警告に混乱しましたか? (yまたは[n])、英語の基本的な知識は、共有ライブラリのロードに応じてブレークポイントを作成することをデバッガが提案することを理解するのに役立ちました”



このメッセージは、gdbがブレークポイントを設定する関数を見つけられなかったことを意味し、「ブレークポイントの設定を遅らせることができますか?」と尋ねます。 同意すると、新しい動的ライブラリ(共有ライブラリ)をロードするたびに、gdbはそこでこの関数を見つけようとし、成功するとブレークポイントを設定します。



All Articles