📜 ⬆️ ⬇️

Using GDB on the example of MaNGOS

Imagine for a moment that you are the captain and mechanic of a huge spacecraft, on which there are many passengers at the same time. But this is not an ideal ship, but a dilapidated vessel. Yes, a lot of work, but much remains to be fixed. The question arises, how? That would be such a tool that allowed us to disassemble our ship to the smallest bolt, to understand how it works, what happens inside it when a button is pressed, to feel the essence, its soul. Moreover, it would be ideal to still have the opportunity to stop time at the time of repairing the problem, in order to sort everything out. After all, it is not so easy to understand, but what happens inside!
Previously, I was afraid to climb inside such a monster as mangos, and used the debugger only when preparing a computer science assignment at the institute. But it turned out that everything is much simpler and there is nothing terrible, even though there is a lot of code there. I will try to describe how using GDB to get inside the popular one MMORPG emulator and look at its work from the inside. The main thing is not to be afraid to experiment, within reason. Those who know what GDB is will not find anything new for themselves here. This idea was pushed through me by the game of the “beloved” class by everyone like a paladin. Actually, a screenshot from the game:



What do we see? In the description of the spell 152-172 damage, and in practice 232. Where did the extra damage come from? The GNU debugger helped me understand this.
')

Server build


To begin with, we will assemble the server itself with the help of cmake. Actually, everything is standard:
mkdir build cd build cmake .. make -j4 make install 

I didn’t specifically compile the server in debug mode to focus attention on the impossibility of debugging without setting the variable DEBUG = 1
In the assembly got out the first problem. Cmake successfully prepared the makefiles, but errors came up during the compilation, namely, the ADAPTIVE Communication Environment (ACE) and TBB were not collected. Having banished Autotools beforehand, these libraries have gathered, but this is the way of the gantushnik. Why collect what is already collected and lies in the repository? As a result, we add variables ACE_USE_EXTERNAL = 1 TBB_USE_EXTERNAL = 1
Well, in the cmake console, everything grabbed:
 -- 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 

I instructed the linker to use libraries with the OS, and not to build them. While I went to the kitchen for tea, everything gathered. We start the server under the debugger:
 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 

A few seconds passed, spamming to the console about downloading the whole world and the inscription that the server started:
 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 

What is r ? This is a run, that is, that is. Click Ctrl + C to go back to the debugger console, not the server and set the first breakpoint:
 (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 - breakpoint, indicating to the debugger where to stop, with - continue, continue
I made a breakpoint on the Unit :: DealDamage function, judging by the name, it is in it that the effects of spells are applied, but this is purely an assumption, and it turned out to be true. The debugger by the way supports autocompletion of functions, the TAB button helped) I was confused by the warning Make breakpoint pending on future shared library load? (y or [n]), basic knowledge of English helped to understand that the debugger suggests making a breakpoint depending on the load of the common library (difficult to translate, but the Unix sense is already clear). Then I enter the game, create a paladin, teach exorcism and teleport to kill the undead.



Here is an interesting breakpoint not working! This is because I put the server in release. Add -DDEBUG = 1
Our world was going a little longer, with garbage in the console about the type mismatch, but in the end it was going to. Again, run the debugger and set a breakpoint. Note that now it has completely loaded the server (there is no no debugging symbols found phrase). And there is no abuse:
 (gdb) b Unit::DealDamage Breakpoint 1 at 0xb3d91a: file /home/gabriel/projects/cpp/mangos/src/game/Unit.cpp, line 479. 

Inside server picking


Go ahead.
We press exorcism and ... the game hung, and in the console we have:
 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) 

Damage is already increased, 599, although the description is less. We look tracing:
 (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 , as you probably already guessed, this is a backtrace. Having learned a little about calls to functions, I realized that it was necessary to monitor the change in the damageInfo variable in the Spell :: DoAllEffectOnTarget function. We set a breakpoint on this function, and delete the old one, as well as the extra breaks for the items and 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 

The new hang of the game, we are inside the f-tion, run it line by line with the entry inside:
 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 

In order not to bore you, I missed part of the console output as I watched the damage change line by line. I will describe only briefly commands:
n - next, execute the following code line without entering inside
s - step, the same thing, just go inside the function
l - list, print a piece of code
p - print, print the variable.
As a result, I learned that the base damage was 535, as it should be in the spell description, and the additional damage is calculated in the 5314 line of the Unit.cpp file and is 15% of the AP. Why is that? The mechanics of the game. In total, it turned out 603, which then flew out in the game.
Screenshot with AP:



That's all, I hope that I briefly and clearly described the basics of working with a debugger under Nix on a live project.

Links


Debugging with GBD - a detailed description of GDB, Richard Stallman, Roland Pesch, Stan Shebs and others.
Mangos project is a great platform for learning c ++

UPD:
by mejedi :
“I was embarrassed by the warning Make breakpoint pending on future shared library load? (y or [n]), basic knowledge of English helped us to understand that the debugger suggests making a breakpoint depending on the load of the shared library. ”

This message means that gdb did not find the function in which they wanted to set breakpoint, and asks: can it delay the installation of breakpoint? If you agree, then when loading each new dynamic library (shared library) gdb will try to find this function there, and if successful, it sets breakpoint.

Source: https://habr.com/ru/post/138111/


All Articles