From 9882984b825a8715df462f89b9c6ca5658b12ad9 Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Sun, 30 Aug 2026 18:11:01 -0300
Subject: [PATCH 01/14] ci: migrate CodeQL to advanced setup and add CODEOWNERS
(#16)
The CodeQL default setup only analysed a pull request when it touched
files relevant to the configured languages, so a docs-only PR produced
no analysis while master still carried one per language, leaving the
code_scanning branch rule unable to diff the two sides.
Also add the CODEOWNERS file this repository was missing, so the branch
ruleset can require code owner review like the sibling repositories do.
---
.github/CODEOWNERS | 1 +
.github/workflows/codeql.yml | 55 ++++++++++++++++++++++++++++++++++++
2 files changed, 56 insertions(+)
create mode 100644 .github/CODEOWNERS
create mode 100644 .github/workflows/codeql.yml
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..d2d6bc1
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @NullSablex
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000..f7ff48c
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,55 @@
+# CodeQL advanced setup.
+#
+# Replaces the repository's CodeQL *default* setup, which only analyses a pull
+# request when it touches files relevant to the configured languages. A PR that
+# changes only docs or dependency manifests produced no analysis at all, while
+# `master` still carried one per language — so the `code_scanning` branch rule
+# could not diff the two sides and reported "configurations not found".
+#
+# Running here, with no path filter, guarantees both configurations exist on
+# every pull request. The categories below must keep matching the ones recorded
+# on `master` (`/language:actions`, `/language:rust`) for that diff to work.
+name: CodeQL
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+ schedule:
+ # Weekly, to catch newly published queries against unchanged code.
+ - cron: '27 4 * * 1'
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+jobs:
+ analyze:
+ name: Analyze (${{ matrix.language }})
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ security-events: write # upload the SARIF results
+ actions: read
+ strategy:
+ fail-fast: false
+ matrix:
+ # Keep in sync with the languages the previous default setup covered.
+ language: [ actions, rust ]
+
+ steps:
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
+ with:
+ languages: ${{ matrix.language }}
+ # Neither language needs a compiled build for CodeQL to extract it.
+ build-mode: none
+ queries: security-extended
+
+ - name: Perform CodeQL analysis
+ uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
+ with:
+ category: "/language:${{ matrix.language }}"
From c299a3bca947c4a1a0dbf9e1ba45b547c8440b6e Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Mon, 31 Aug 2026 03:36:57 -0300
Subject: [PATCH 02/14] chore: re-pin rust-samp SDK to 51ba519 (v3.4.0)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Traz o lookup_function do bloco de debug (base para a pilha de chamadas),
Amx::exec_public_scope e o hardening de buffer/stack no FFI. Eventos do SDK
(#[event]) são x86-only e viram no-op no check aarch64.
---
Cargo.lock | 229 ++++++++++++++++++++++++++++++---
crates/dap-adapter/Cargo.toml | 2 +-
crates/debug-plugin/Cargo.toml | 2 +-
3 files changed, 213 insertions(+), 20 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 6c06076..500558f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,12 +2,24 @@
# It is not intended for manual editing.
version = 4
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
[[package]]
name = "bitflags"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
[[package]]
name = "dap-adapter"
version = "0.1.0"
@@ -49,6 +61,25 @@ dependencies = [
"log",
]
+[[package]]
+name = "generic-array"
+version = "0.14.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "iced-x86"
+version = "1.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b"
+dependencies = [
+ "lazy_static",
+]
+
[[package]]
name = "interprocess"
version = "2.4.3"
@@ -59,7 +90,7 @@ dependencies = [
"libc",
"recvmsg",
"widestring",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -68,6 +99,12 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
[[package]]
name = "libc"
version = "0.2.186"
@@ -80,12 +117,31 @@ version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
+[[package]]
+name = "mach2"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "memchr"
version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
+[[package]]
+name = "mmap-fixed-fixed"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0681853891801e4763dc252e843672faf32bcfee27a0aa3b19733902af450acc"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
[[package]]
name = "num-conv"
version = "0.2.2"
@@ -101,6 +157,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
[[package]]
name = "pawnpro-dbg-protocol"
version = "0.1.0"
@@ -140,13 +202,42 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175"
+[[package]]
+name = "region"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7"
+dependencies = [
+ "bitflags 1.3.2",
+ "libc",
+ "mach2",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "retour"
+version = "0.4.0-alpha.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ead4bc8e12d553ff70769c5f5c21f5f4f0e73c0018068a6bb5a3d7d3b9e57ec7"
+dependencies = [
+ "cfg-if",
+ "generic-array",
+ "iced-x86",
+ "libc",
+ "mmap-fixed-fixed",
+ "once_cell",
+ "region",
+ "slice-pool2",
+]
+
[[package]]
name = "rust-samp"
-version = "3.2.0"
-source = "git+https://github.com/NullSablex/rust-samp?rev=e5b5fc1#e5b5fc10cd41351e684c28b0a7cc62071812f3c7"
+version = "3.3.0"
+source = "git+https://github.com/NullSablex/rust-samp?rev=51ba519#51ba519157e04258ef49515958491b9ba734e2db"
dependencies = [
"fern",
"log",
+ "retour",
"rust-samp-codegen",
"rust-samp-sdk",
"time",
@@ -154,20 +245,20 @@ dependencies = [
[[package]]
name = "rust-samp-codegen"
-version = "1.3.0"
-source = "git+https://github.com/NullSablex/rust-samp?rev=e5b5fc1#e5b5fc10cd41351e684c28b0a7cc62071812f3c7"
+version = "1.4.0"
+source = "git+https://github.com/NullSablex/rust-samp?rev=51ba519#51ba519157e04258ef49515958491b9ba734e2db"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.118",
+ "syn",
]
[[package]]
name = "rust-samp-sdk"
-version = "3.2.1"
-source = "git+https://github.com/NullSablex/rust-samp?rev=e5b5fc1#e5b5fc10cd41351e684c28b0a7cc62071812f3c7"
+version = "3.3.0"
+source = "git+https://github.com/NullSablex/rust-samp?rev=51ba519#51ba519157e04258ef49515958491b9ba734e2db"
dependencies = [
- "bitflags",
+ "bitflags 2.13.0",
]
[[package]]
@@ -197,7 +288,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
- "syn 3.0.0",
+ "syn",
]
[[package]]
@@ -214,15 +305,10 @@ dependencies = [
]
[[package]]
-name = "syn"
-version = "2.0.118"
+name = "slice-pool2"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
+checksum = "7a3d689654af89bdfeba29a914ab6ac0236d382eb3b764f7454dde052f2821f8"
[[package]]
name = "syn"
@@ -267,24 +353,67 @@ dependencies = [
"time-core",
]
+[[package]]
+name = "typenum"
+version = "1.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
+
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
[[package]]
name = "widestring"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
[[package]]
name = "windows-sys"
version = "0.61.2"
@@ -294,6 +423,70 @@ dependencies = [
"windows-link",
]
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
[[package]]
name = "zmij"
version = "1.0.21"
diff --git a/crates/dap-adapter/Cargo.toml b/crates/dap-adapter/Cargo.toml
index 4e5b1fb..77616c0 100644
--- a/crates/dap-adapter/Cargo.toml
+++ b/crates/dap-adapter/Cargo.toml
@@ -11,7 +11,7 @@ name = "dap-adapter"
path = "src/main.rs"
[dependencies]
-rust-samp-sdk = { git = "https://github.com/NullSablex/rust-samp", rev = "e5b5fc1", default-features = false, features = ["debug"] }
+rust-samp-sdk = { git = "https://github.com/NullSablex/rust-samp", rev = "51ba519", default-features = false, features = ["debug"] }
pawnpro-dbg-protocol = { path = "../protocol" }
interprocess = "2"
serde = { version = "1", features = ["derive"] }
diff --git a/crates/debug-plugin/Cargo.toml b/crates/debug-plugin/Cargo.toml
index b293b9b..620ea73 100644
--- a/crates/debug-plugin/Cargo.toml
+++ b/crates/debug-plugin/Cargo.toml
@@ -12,7 +12,7 @@ crate-type = ["cdylib"]
[dependencies]
pawnpro-dbg-protocol = { path = "../protocol" }
interprocess = "2"
-samp = { package = "rust-samp", git = "https://github.com/NullSablex/rust-samp", rev = "e5b5fc1", features = ["debug"] }
+samp = { package = "rust-samp", git = "https://github.com/NullSablex/rust-samp", rev = "51ba519", features = ["debug"] }
[package.metadata.samp]
uid = "0x0d9107bbd31c8d1b"
From fb86df3f0953ac5291a71935fdf15397dccd348c Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Mon, 31 Aug 2026 03:37:45 -0300
Subject: [PATCH 03/14] feat(debugger): call stack multi-frame
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Caminha a cadeia de frames do AMX (FRM anterior + endereço de retorno,
parando no público de entrada) e expõe N frames ao editor. Cada frame traz
nome da função (lookup_function), linha e variáveis em escopo — reaproveitando
inspect::collect por frame.
- protocolo: Event::Paused passa a carregar frames: Vec; Command::
SetVariable ganha o índice do frame.
- plugin: novo módulo stack (walker puro e testável) + build_frames no on_pause;
PAUSE_CTX guarda o contexto de todos os frames para editar no frame certo.
- adaptador: stackTrace com N frames + source; scopes/variables/evaluate/
setVariable operam por frame (frameId/variablesReference).
Cobertura: 6 testes do walker + 2 do plumbing do adaptador. aarch64 ok.
---
README.md | 2 +-
crates/dap-adapter/src/plugin_client.rs | 61 +++++------
crates/dap-adapter/src/session.rs | 128 +++++++++++++++++-----
crates/debug-plugin/src/bridge.rs | 10 +-
crates/debug-plugin/src/hook.rs | 73 +++++++++----
crates/debug-plugin/src/lib.rs | 1 +
crates/debug-plugin/src/stack.rs | 138 ++++++++++++++++++++++++
crates/protocol/src/lib.rs | 54 +++++++---
8 files changed, 369 insertions(+), 98 deletions(-)
create mode 100644 crates/debug-plugin/src/stack.rs
diff --git a/README.md b/README.md
index 0e94f95..878c5cf 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ desenvolvimento.
| Editar variável | ✅ | Durante a pausa (`setVariable`). |
| Pausar em erro de runtime | ✅ | Divisão por zero e índice fora do limite; pausa na linha, antes do abort. SA-MP e open.mp. |
| Mensagens localizadas | ✅ | pt-BR, en, es, ro, ru (segue o idioma do editor). |
-| Call stack multi-frame | ⬜ | Hoje mostra um frame; caminhar a pilha está planejado. |
+| Call stack multi-frame | ✅ | Caminha a cadeia de frames (FRM→retorno); nome da função, linha e variáveis por frame. |
| Data breakpoints | ⬜ | Pausar quando uma variável muda — em avaliação. |
| Mais erros de runtime | ⬜ | STACKERR / MEMACCESS / HEAPLOW — em avaliação. |
diff --git a/crates/dap-adapter/src/plugin_client.rs b/crates/dap-adapter/src/plugin_client.rs
index e6b3cd9..ce59677 100644
--- a/crates/dap-adapter/src/plugin_client.rs
+++ b/crates/dap-adapter/src/plugin_client.rs
@@ -150,17 +150,14 @@ impl PluginClient {
match wire::from_line::(&line) {
Ok(Event::Paused {
reason,
- line,
- vars,
+ frames,
description,
}) => {
- store_vars(&vars);
- store_line(line);
+ store_frames(&frames);
let mut body = json!({
"reason": reason,
"threadId": 1,
"allThreadsStopped": true,
- "line": line,
});
// Runtime error: `description`/`text` show the cause in the
// editor's call-stack header (reason "exception").
@@ -204,41 +201,39 @@ impl PluginClient {
}
}
-/// Últimas variáveis recebidas num `Paused` — servidas ao `variables` do DAP.
-/// Global porque chegam pela thread leitora e são consultadas no loop principal.
-static LAST_VARS: Mutex> = Mutex::new(Vec::new());
+/// Pilha de chamadas da última pausa — servida ao `stackTrace`/`scopes`/
+/// `variables` do DAP. Global porque chega pela thread leitora e é consultada no
+/// loop principal. Índice 0 = topo (onde a VM parou).
+static LAST_FRAMES: Mutex> = Mutex::new(Vec::new());
-fn store_vars(vars: &[wire::Var]) {
- if let Ok(mut g) = LAST_VARS.lock() {
- *g = vars.to_vec();
+fn store_frames(frames: &[wire::Frame]) {
+ if let Ok(mut g) = LAST_FRAMES.lock() {
+ *g = frames.to_vec();
}
}
-/// Variáveis da última pausa (para o handler `variables` do DAP).
-pub fn last_vars() -> Vec {
- LAST_VARS.lock().map(|g| g.clone()).unwrap_or_default()
+/// Frames da última pausa (para o `stackTrace` do DAP).
+pub fn last_frames() -> Vec {
+ LAST_FRAMES.lock().map(|g| g.clone()).unwrap_or_default()
}
-/// Atualiza no cache o valor de uma variável editada via `setVariable`, para que
-/// o painel/watch reflitam o novo valor sem reler a VM (o plugin já a escreveu).
-pub fn update_var(name: &str, value: &str) {
- if let Ok(mut g) = LAST_VARS.lock()
- && let Some(v) = g.iter_mut().find(|v| v.name == name)
- {
- v.value = value.to_string();
- }
+/// Variáveis em escopo no frame dado (0 = topo) — para `variables`/`evaluate`.
+pub fn frame_vars(frame: usize) -> Vec {
+ LAST_FRAMES
+ .lock()
+ .ok()
+ .and_then(|g| g.get(frame).map(|f| f.vars.clone()))
+ .unwrap_or_default()
}
-/// Linha-fonte da última pausa (para o `stackTrace` do DAP).
-static LAST_LINE: Mutex