Python: Como injetar código num processo em execução

Resposta curta: pyrasite

pip install pyrasite
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
pyrasite <pid> dump_stacks.py
pyrasite-shell <pid>
Utilizando pyrasite para injetar código num console Python
Utilizando pyrasite para injetar código num console Python

Contexto

Esses dias estava com um bug intermitente que só ocorria no servidor de desenvolvimento, então não estava conseguindo reproduzir localmente.

Tentando descobrir o problema, dei SSH para o servidor e vi que num processo estava justamente acontecendo o que eu queria corrigir.

Então comecei a tentar debugar o código que já estava em execução, sem ter que recomeçar o processo, pois se fizesse isso provavelmente o bug não iria ocorrer.

Foi assim que descobri o pyrasite e como debugar um processo Python que já está em execução.

Pyrasite

Com o pyrasite conseguimos injetar código em um processo Python e debugar em tempo de execução.

Fonte: http://pyrasite.com/

Instalação

pip install pyrasite

Habilitar o trace

No Linux, por uma questão de segurança, o trace é desabilitado por padrão. Então temos que habilitar para conseguir debugar com o pyrasite.

echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

Recomendo voltar o ptrace_scope para o valor padrão (1) após o debug.

Mais informações sobre o ptrace_scope na documentação do Kernel.

Utilização

Para utilizar o pyrasite chamamos com o ID do Processo (PID) e o script que queremos executar:

pyrasite <pid> my_script.py

A ferramenta já vem com alguns scripts prontos para injetar no processo, são eles:

Além desses, também temos reverse_python_shell.py, reverse_shell.py, start_callgraph.py, stop_callgraph.py, mas esses eu não consegui usar e não fui muito a fundo.

Para, por exemplo, utilizar o dump_stacks.py, rodamos:

pyrasite <pid> dump_stacks.py

Shell

Além de executar scripts, também podemos habilitar um shell Python para depurar o que está acontecendo na aplicação. Com o shell conseguimos rodar comandos, acessar variáveis e tudo mais que fazemos num console Python

pyrasite-shell <pid>

Troubleshooting

  1. Rodei o pyrasite e não apareceu nenhum resultado
    • O output do código injetado aparecerá no stdout do processo principal que está recebendo a injeção. Se o seu processo redireciona o stdout para um arquivo, a saída do código injetado também irá para o mesmo arquivo.