एक पल के लिए कल्पना कीजिए कि आप एक विशाल अंतरिक्ष यान के कप्तान और मैकेनिक हैं, जिस पर एक ही समय में कई यात्री हैं। लेकिन यह एक आदर्श जहाज नहीं है, बल्कि एक जीर्ण जहाज है। हां, बहुत काम करता है, लेकिन बहुत कुछ तय होना बाकी है। सवाल है, कैसे? ऐसा कोई उपकरण होगा जो हमें हमारे जहाज को सबसे छोटे बोल्ट को अलग करने की अनुमति देगा, यह समझने के लिए कि यह कैसे व्यवस्थित किया जाता है, इसके अंदर क्या होता है जब एक बटन या किसी अन्य को दबाया जाता है, सार को महसूस करने के लिए, इसकी आत्मा। इसके अलावा, यह अभी भी समस्या की मरम्मत के समय को रोकने में सक्षम होने के लिए आदर्श होगा, ताकि सब कुछ अलमारियों पर डाल दिया जाए। आखिरकार, यह समझना इतना आसान नहीं है कि अंदर क्या हो रहा है!
इससे पहले, मैं इस तरह के एक राक्षस के रूप में आम के अंदर जाने से डरता था, और मैंने संस्थान में कंप्यूटर विज्ञान पर एक असाइनमेंट तैयार करते समय केवल डिबगर का उपयोग किया था। लेकिन यह पता चला कि सब कुछ बहुत सरल है और चिंता करने की कोई बात नहीं है, भले ही बहुत अधिक कोड हो। मैं यह वर्णन करने का प्रयास करूँगा कि कैसे जीडीबी का उपयोग एक MMORPG के लोकप्रिय एमुलेटर के अंदर करने के लिए किया जाए और अंदर से इसके काम को देखें। मुख्य बात कारण के भीतर प्रयोग करने से डरना नहीं है। जो लोग जानते हैं कि जीडीबी को यहां अपने लिए कुछ नया नहीं मिलेगा।
इस विचार को खेल के द्वारा "प्यारे" वर्ग के सभी लोगों द्वारा एक पैलाडिन के रूप में प्रेरित किया गया था। दरअसल, खेल से एक स्क्रीनशॉट:

हम क्या देखते हैं? मंत्र के वर्णन में, 152-172 की क्षति, लेकिन व्यवहार में 232. अतिरिक्त क्षति कहां से आई? GNU डिबगर ने मुझे यह समझने में मदद की।
सर्वर असेंबली
आरंभ करने के लिए, आइए cmake का उपयोग करके सर्वर का निर्माण करें। वास्तव में, सब कुछ मानक है:
mkdir build cd build cmake .. make -j4 make install
मैंने जानबूझकर डिबग मोड में सर्वर का निर्माण नहीं किया, ताकि डेबग = 1 चर को सेट किए बिना डिबगिंग की असंभवता पर ध्यान केंद्रित किया जा सके।
विधानसभा को पहली समस्याएं मिलीं। Cmake ने सफलतापूर्वक मेकफाइल्स तैयार किया, लेकिन त्रुटियों को संकलन में मिला, अर्थात्, ADAPTIVE संचार पर्यावरण (ACE) और TBB एकत्र नहीं किए गए। ऑटोटूलस के चलने से पहले, ये पुस्तकालय एकत्रित हो चुके हैं, लेकिन यह जेंटुशनिक का तरीका है। जो पहले से ही एकत्र है और भंडार में है, उसे क्यों इकट्ठा करें? परिणामस्वरूप, हम चर ACE_USE_EXTERNAL = 1 TBB_USE_EXTERNAL = 1 जोड़ते हैं
ठीक है, 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
मैंने लिंकर को ओएस के साथ पुस्तकालयों का उपयोग करने के बजाय उन्हें बनाने के लिए कहा था। चाय के लिए रसोई में जाते समय सब कुछ इकट्ठा हो गया। हम डिबगर के तहत सर्वर शुरू करते हैं:
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
आर क्या है? यह चलाया जाता है, अर्थात लॉन्च। डीबगर कंसोल पर लौटने के लिए 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 - ब्रेकपॉइंट, डीबगर को यह बताना कि कहां रुकना है, जारी रखना है, जारी रखें
मैंने यूनिट पर एक ब्रेकपॉइंट बनाया: डीलडैमेज फ़ंक्शन, नाम से यह देखते हुए कि इसमें मंत्र का प्रभाव लागू होता है, लेकिन यह विशुद्ध रूप से एक धारणा है, और यह सच हो गया। वैसे, डिबगर ऑटो-फ़ंक्शंस को पूरा करने का समर्थन करता है, TAB बटन ने मदद की) मैं भविष्य की साझा लाइब्रेरी लोड पर लंबित ब्रेकपॉइंट को चेतावनी से भ्रमित कर रहा था? (y या [n]), अंग्रेजी के बुनियादी ज्ञान ने यह समझने में मदद की कि डिबगर साझा लाइब्रेरी के लोडिंग के आधार पर एक ब्रेकप्वाइंट बनाने का सुझाव देता है (यह अनुवाद करना मुश्किल है, लेकिन अर्थ यूनिक्स के लिए स्पष्ट है)। फिर मैं खेल में प्रवेश करता हूं, एक पैलाडिन बनाता हूं, मरे को मारने के लिए भूत भगाने और टेलीपोर्ट सिखाता हूं।

यह दिलचस्प है, ब्रेकप्वाइंट काम नहीं करता है! यह इसलिए है क्योंकि मैंने रिलीज में सर्वर को एक साथ रखा था। Add -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
बीटी , जैसा कि आप शायद पहले से ही अनुमान लगा चुके हैं, यह बैकट्रेस है। फ़ंक्शन का अध्ययन करने के बाद थोड़ा कॉल होता है, मैंने महसूस किया कि स्पेल में नुकसानइन्फो चर में बदलाव की निगरानी करना आवश्यक है :: DoAllEffectOnTarget फ़ंक्शन। हम इस फ़ंक्शन पर एक ब्रेकपॉइंट सेट करते हैं, और पुराने और साथ ही आइटम और 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 - चरण, एक ही बात, बस फ़ंक्शन के अंदर जाएं
एल - सूची, कोड का एक टुकड़ा प्रिंट करें
पी - प्रिंट, एक चर प्रिंट करें।
नतीजतन, मुझे पता चला कि आधार क्षति 535 थी, क्योंकि यह वर्तनी के विवरण में होना चाहिए, और अतिरिक्त नुकसान की गणना Unit.cpp फ़ाइल की लाइन 5314 में की जाती है और यह एपी का 15% है। ऐसा क्यों? खेल के यांत्रिकी। कुल मिलाकर, यह 603 निकला, जिसने फिर खेल में उड़ान भरी।
एपी से स्क्रीनशॉट:

यह सब, मुझे आशा है कि मैंने संक्षेप में और स्पष्ट रूप से एक लाइव प्रोजेक्ट पर निक्स के तहत डिबगर के साथ काम करने की मूल बातें वर्णित की हैं।
संदर्भ
GBD के साथ डिबगिंग - GDB, रिचर्ड स्टैलमैन, रोलैंड पेश, स्टेन शेब्स और अन्य का विस्तृत विवरण।
सी + + सीखने के लिए
मैंगोस प्रोजेक्ट एक बेहतरीन जगह है
युपीडी:
mejedi से :
"मैं भविष्य के साझा पुस्तकालय लोड पर लंबित ब्रेक पॉइंट को चेतावनी से भ्रमित कर रहा था? (y या [n]), अंग्रेजी के बुनियादी ज्ञान ने यह समझने में मदद की कि डिबगर साझा पुस्तकालय के लोडिंग के आधार पर एक ब्रेकप्वाइंट बनाने का सुझाव देता है "
इस संदेश का मतलब है कि जीडीबी में वह फ़ंक्शन नहीं मिला, जिसमें वे एक ब्रेकपॉइंट सेट करना चाहते थे, और पूछता है: ब्रेकपॉइंट सेट करने में देरी हो सकती है? यदि आप सहमत हैं, तो प्रत्येक नए गतिशील पुस्तकालय (साझा पुस्तकालय) को लोड करते समय, जीडीबी इस फ़ंक्शन को वहां खोजने की कोशिश करेगा, और यदि सफल हो, तो एक ब्रेकपॉइंट सेट करें।