Depuração por dentro¶
Instável
A depuração funciona no uso descrito no guia da extensão, mas ainda pode falhar. Esta página descreve como ela é feita.
São duas peças e um soquete:
editor ──DAP──► adaptador (no núcleo) ──spawn──► omp-server
▲ │ carrega
└───────── soquete ─────────── plugin
O adaptador (crates/debugger/adapter) traduz o DAP do editor para os
comandos que o plugin (crates/debugger/plugin) executa dentro da máquina
virtual do Pawn. O que passa entre os dois é o crates/debugger/protocol — um
enum de comandos e um de eventos, serializados em linhas JSON.
Como o plugin encontra a sessão¶
A sessão sobe o servidor do jogo com quatro variáveis de ambiente, e o plugin as lê ao carregar:
| Variável | Para quê |
|---|---|
PAWNPRO_DBG_ENDPOINT |
O endereço do soquete do núcleo |
PAWNPRO_DBG_SESSION |
Com que id ele se apresenta (PAWNPRO/1 plugin <id>) |
PAWNPRO_DBG_AMXDBG |
O .amx em depuração, de onde ele lê o bloco de debug |
PAWNPRO_DBG_LOCALE |
O idioma das mensagens de erro de runtime |
Sem essas variáveis o plugin não faz nada: carregado num servidor iniciado à mão, ele fica quieto em vez de tentar conectar em algum lugar.
Só a VM do programa depurado¶
O servidor carrega várias máquinas virtuais — o gamemode e cada filterscript —, e o plugin recebe todas. Os endereços de código começam em zero em cada uma: um breakpoint aplicado à VM errada pararia em código de outro script, mostrado com as linhas e as variáveis do programa depurado.
O plugin reconhece a VM certa pelo conteúdo, comparando com o .amx que a
sessão indicou: o cabeçalho, a tabela de publics e a tabela de nomes, que o
servidor não altera ao carregar. Ficam de fora o código, que é relocado na
carga, e a tabela de natives, que recebe endereços quando os plugins registram
funções.
É isso que faz um filterscript deixar de disparar os breakpoints do gamemode.
Breakpoints com o fonte já editado¶
O breakpoint que o editor manda é uma linha do arquivo como está agora; o
que roda é o .amx compilado antes. Editar durante a sessão desalinha os dois,
e a versão anterior parava no lugar errado.
A sessão guarda o texto de cada arquivo no momento da compilação e compara com o texto atual por diff de linhas:
Uma linha que não existia na compilação não tem endereço, e o breakpoint fica não verificado em vez de parar em outro lugar — o editor a mostra apagada, e o console explica que é preciso reiniciar para recompilar.
Reiniciar e recompilar¶
Quem decide recompilar é o adaptador, no restart: é por onde todo reinício
passa, venha do botão, da tecla ou da paleta. Compilar, porém, é da extensão —
o compilador e as flags são configuração do projeto.
restart ──► fonte mais novo que o .amx, ou .amx sem bloco de debug?
├─ não → sobe o servidor de novo
└─ sim → evento `pawnproRebuild` ──► a extensão compila
└─► restart de novo
Se a compilação falhar, a extensão não reenvia o restart: é o que impede o
servidor de subir com o binário velho.
Parar¶
terminate derruba o servidor e mantém a sessão viva; disconnect encerra a
sessão. São coisas diferentes no DAP, e tratá-las como sinônimo é o que fazia a
barra de progresso do editor ficar presa esperando um fim que já tinha
acontecido.
Inspeção¶
- Arrays são endereçados como o compilador os gera: o adaptador descreve o
caminho até o elemento (
grid[1][2]é um caminho de dois índices), e o plugin dereferencia o que for referência (iREFERENCE,iREFARRAY) e soma os deslocamentos de indireção. Sem isso, um array de várias dimensões mostrava o valor de outra posição da memória. - Editar uma variável é um comando com id; o plugin confirma com um evento correlacionado, e só então o editor mostra o valor novo. Um "escrevi" otimista esconderia a escrita que não aconteceu.
- Data breakpoint vale para global, local e elemento de array. O de uma local expira quando a função dona retorna — a célula passa a ser de outra coisa.
- Erros de runtime (divisão por zero, índice fora do limite, colisão entre
pilha e heap, underflow de heap, acesso inválido) pausam com a mensagem no
idioma configurado, traduzida no
protocol.
Enquanto está pausado¶
A pausa acontece dentro da VM: nenhum callback, timer ou comando do Pawn executa até continuar. A rede do servidor segue de pé — ele continua respondendo à consulta de status —, e o que chegou durante a pausa é processado de uma vez ao continuar, inclusive os timers atrasados.
Por isso a depuração é para servidor local de desenvolvimento, não para um servidor com jogadores.