Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions doc/GdbServer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# GDB-Server Debug Endpoint

The GDB-server lets a debugger — or an AI agent — attach to a **running**
`mangosd` process and drive it directly: read process memory, inspect live game
state, and run server/GM commands, all over a single network endpoint. It is
built on the GDB Remote Serial Protocol and integrated into the mangosd world
loop and ACE networking.

It has two layers:

1. **Native layer** — a real GDB **Remote Serial Protocol (RSP)** stub. Any
debugger that speaks RSP can attach: GDB, LLDB, IDA Pro (its built-in
"Remote GDB debugger" backend), radare2/rizin, Binary Ninja.
2. **Semantic layer** — mangos-specific introspection/control delivered over
GDB's standard `qRcmd` ("monitor") packet as `mangos <verb>` commands. The
same verbs are also exposed on a plain-text TCP bridge for AI agents and for
debuggers that do not speak RSP (WinDbg, CDB, x64dbg).

## Enabling

In `mangosd.conf` (see the CONSOLE / REMOTE ACCESS section):

```
GdbServer.Enable = 1
GdbServer.IP = 127.0.0.1 # never expose publicly
GdbServer.Port = 2345 # RSP port
GdbServer.MonitorPort = 2346 # plain-text bridge (0 to disable)
GdbServer.AllowMemWrite = 0 # allow RSP 'M' writes (dangerous)
```

> **Security:** anyone who can reach these ports gets full process-memory read
> and full server-command access. Keep the bind address on `127.0.0.1`. The
> subsystem is disabled by default. Memory writes (`M`) and are off by default.

At boot the server runs a self-test and logs `[gdb-monitor-selftest] PASS`,
then (when enabled) `GdbServer Thread started (RSP on 127.0.0.1:2345 ...)`.

## Attaching

### GDB / LLDB / IDA (RSP)

```
# GDB
gdb -ex "target remote 127.0.0.1:2345"

# LLDB
lldb -o "gdb-remote 127.0.0.1:2345"

# IDA Pro: Debugger -> Attach -> Remote GDB debugger,
# host 127.0.0.1, port 2345
```

Once attached, the semantic layer is reached with `monitor`:

```
(gdb) monitor mangos help
(gdb) monitor mangos status
(gdb) monitor mangos players
(gdb) monitor mangos config WorldServerPort
(gdb) monitor mangos cmd .server info
(gdb) x/16xb 0x<addr> # read live process memory
```

Pressing **Ctrl-C** in gdb interrupts the target: the world tick pauses and the
process enters a cooperative stop loop until you `continue`. The server still
honours shutdown signals while paused, so it can always be stopped.

### AI agents / WinDbg / CDB / x64dbg (plain-text bridge)

Non-RSP tools attach to the `mangosd` process natively (by PID) for low-level
work, and use the plain-text monitor bridge for the semantic layer:

```
# any raw TCP client / AI agent
$ nc 127.0.0.1 2346
mangos help
mangos tick
mangos players
mangos cmd .save all
```

Each line is a `mangos <verb>` command; the text reply comes straight back.

## Monitor verbs

| Verb | Description |
|------|-------------|
| `mangos help` | List verbs |
| `mangos status` | World summary: motd, uptime, sessions, world tick |
| `mangos players` (`ps`) | List online sessions/players |
| `mangos tick` | World loop counter + uptime |
| `mangos session <accId>` | Session detail for an account id |
| `mangos config <key>` | Read a `mangosd.conf` value |
| `mangos cmd <command>` | Run any server/GM command (e.g. `.server info`, `.account onlinelist`) |
| `mangos break ...` | Arm/list/clear game-level breakpoints (see below) |
| `mangos dump` | Backtrace of the world thread |

The `cmd` verb bridges to the existing `ChatCommand` system at `SEC_CONSOLE`
level, so the entire console/GM command surface is drivable over the debug
channel.

## Game-level breakpoints

Beyond the native protocol, the server can pause itself at semantically
meaningful points and hand control to the attached debugger. When a breakpoint
fires (and a debugger is attached), the world thread stops **inline at the call
site**, captures the live registers, and waits — so `bt` in gdb shows the real
call stack and `monitor mangos ...` reads quiescent game state. `continue`
resumes the tick.

A breakpoint is an **event** plus an optional numeric **filter** (`0`/omitted =
"any"). Arm, list, and clear them over the monitor surface:

```
(gdb) monitor mangos break events # list all event names
(gdb) monitor mangos break spellcast 133 # pause on cast of spell 133
(gdb) monitor mangos break opcode 0x12E # pause on a given opcode
(gdb) monitor mangos break death 0 # pause on any unit death
(gdb) monitor mangos break worldtick # stop every tick (single-step the world)
(gdb) monitor mangos break list
(gdb) monitor mangos break del spellcast 133
(gdb) monitor mangos break clear
```

`mangos break events` prints the authoritative, up-to-date list. Events span
every major subsystem (all wired to real call sites):

| Group | Events (filter) |
|-------|-----------------|
| Core gameplay | `opcode` (opcode), `login`/`logout` (account), `mapenter`/`mapleave` (map), `spellcast`/`spellprepare` (spell), `death` (entry), `damage` (victim entry), `levelup` (level), `loot` (type), `questaccept`/`questcomplete`/`questreward` (quest), `chat`, `itemuse`, `gossip` (id), `creaturecreate` (entry), `gobjectuse` (entry), `gmcmd`, `worldtick` |
| Netcode / auth | `netaccept`, `netclose`, `authsession`, `packetsend` (opcode), `packetrecv` (opcode) |
| Database (shared layer) | `dbquery`, `dbexecute`, `dbasyncquery` |
| Warden anti-cheat | `wardencheck`, `wardenviolation` |
| Scripting | `scriptai` (entry), `eluna`, `sd3` |
| Creature AI | `aicombat`/`aicombatend` (entry), `aiupdate` (entry), `aispawn` (entry) |
| Maps / instances | `mapcreate` (map), `gridload` (map), `instancecreate` (map), `instancereset` (map) |
| Economy / social | `mailsend`, `mailrecv`, `auctionadd`, `auctionbuy`, `trade`, `groupjoin` |
| BG / pet / item / pvp / move | `bgstart`/`bgend` (type), `petsummon` (entry), `itemequip`/`itemdestroy` (entry), `pvpkill`, `movementinform` |

Note: `opcode` with a filter already covers *every* client packet, and
`dbquery`/`dbexecute` cover *every* SQL statement, so those single events span
their whole subsystem without one breakpoint per id.

Breakpoints only fire while a debugger is attached, so an armed-but-unattended
breakpoint never stalls the server; stops never nest. The hot-path guard is a
single relaxed atomic load, so unarmed call sites are effectively free. The
database hooks live in the shared library and reach the engine through a thin
registered bridge (`shared/Debug/DebugBreakHook`). New game sites are added
with a one-line `GDB_BREAK(<event>, <detail>)`; new shared-layer sites with
`GDB_BREAK_SHARED(<event>, <detail>)`.

## Capabilities and limits

**Implemented:**
- RSP transport over TCP; `qSupported`, `?`, `g`/`G`, `m`/`M` (guarded
process-memory read/write), `H`, `c`/`s`/`D`/`k`, `vCont`, `qRcmd` monitor.
- The `mangos` semantic verbs including the `cmd` bridge; the plain-text bridge.
- Cooperative world-tick stop on Ctrl-C.
- **Live register capture** (`g`): at a stop the world thread's real registers
are captured (Linux `getcontext`, Windows `RtlCaptureContext` on x86_64), so
gdb can `bt` through the actual call stack via the `m` memory packets.
- **Game-level breakpoints** across ~55 event families spanning every major
subsystem — core gameplay, netcode/auth, database, warden, scripting,
creature AI, maps/instances, economy/social, and battleground/pet/item/pvp —
each with an optional numeric filter. `mangos break events` lists them.
- `mangos dump` backtrace.

**Limits:**
- `g`/`G` cover the x86_64 integer register file; FPU/SSE are reported as zero,
and non-x86_64 builds report zeroed registers (memory + monitor still work).
- Native instruction breakpoints (`Z`/`z`) and hardware single-step are not
implemented — by design. Driving `int3` patching or self-set debug registers
in a live, multi-threaded server is unsafe; the game-level breakpoints above
are the supported, cooperative equivalent.
- One debugger connection at a time.
- State seen during a stop is *world-tick-consistent*, not globally race-free:
the network, database and map-update threads keep running.

**Possible future work:** symbol-resolved backtraces on Windows (via the
existing crash-report tooling) and additional `GDB_BREAK_*` call sites.
23 changes: 3 additions & 20 deletions src/game/BattleGround/BattleGround.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "Formulas.h"
#include "GridNotifiersImpl.h"
#include "Chat.h"
#include "Debug/GdbServer/GdbBreakpoints.h"
#ifdef ENABLE_ELUNA
#include "LuaEngine.h"
#endif /* ENABLE_ELUNA */
Expand Down Expand Up @@ -489,6 +490,8 @@ void BattleGround::Update(uint32 diff)
StartingEventOpenDoors();

SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH], CHAT_MSG_BG_SYSTEM_NEUTRAL);
// GDB-server game breakpoint
GDB_BREAK(BgStart, GetTypeID());
SetStatus(STATUS_IN_PROGRESS);
SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]);

Expand Down Expand Up @@ -703,17 +706,6 @@ void BattleGround::CastSpellOnTeam(uint32 SpellID, Team teamId)
}













/**
* @brief Blocks the movement of the player.
*
Expand Down Expand Up @@ -1115,15 +1107,6 @@ void BattleGround::UpdatePlayerScore(Player* Source, uint32 type, uint32 value)
}











/**
* @brief Sends a message to all players in the battleground.
*
Expand Down
3 changes: 3 additions & 0 deletions src/game/BattleGround/BattleGroundReward.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@


#include "BattleGround.h"
#include "Debug/GdbServer/GdbBreakpoints.h"
#include "Object.h"
#include "Player.h"
#include "BattleGroundMgr.h"
Expand Down Expand Up @@ -174,6 +175,8 @@ void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player*
*/
void BattleGround::EndBattleGround(Team winner)
{
// GDB-server game breakpoint
GDB_BREAK(BgEnd, GetTypeID());
#ifdef ENABLE_ELUNA
if (Eluna* e = GetBgMap()->GetEluna())
{
Expand Down
6 changes: 6 additions & 0 deletions src/game/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ source_group("Warden\\Modules" FILES ${SRC_GRP_WARDEN_MODULES})
file(GLOB SRC_GRP_WORLD_HANDLERS WorldHandlers/*.cpp WorldHandlers/*.h)
source_group("World\\Handlers" FILES ${SRC_GRP_WORLD_HANDLERS})

#GDB debug server group
file(GLOB SRC_GRP_DEBUG_GDB Debug/GdbServer/*.cpp Debug/GdbServer/*.h)
source_group("Debug\\GdbServer" FILES ${SRC_GRP_DEBUG_GDB})

# Build the Eluna library if enabled
if(SCRIPT_LIB_ELUNA)
file(GLOB SRC_GRP_ELUNA
Expand Down Expand Up @@ -238,6 +242,7 @@ add_library(game STATIC
${SRC_GRP_WARDEN}
${SRC_GRP_WARDEN_MODULES}
${SRC_GRP_WORLD_HANDLERS}
${SRC_GRP_DEBUG_GDB}
$<$<BOOL:${SCRIPT_LIB_ELUNA}>:${SRC_GRP_ELUNA}>
$<$<BOOL:${PLAYERBOTS}>:${SRC_GRP_BOTS}>
)
Expand All @@ -260,6 +265,7 @@ target_include_directories(game
Warden
Warden/Modules
WorldHandlers
Debug/GdbServer
$<$<BOOL:${SCRIPT_LIB_ELUNA}>:
${CMAKE_SOURCE_DIR}/src/modules/Eluna
${CMAKE_SOURCE_DIR}/src/modules/Eluna/hooks
Expand Down
Loading
Loading