Se você entender muito bem a pilha, entenderá como a memória funciona no programa e se entenderá como a memória funciona no programa, entenderá como o armazenamento de funções no programa e se entenderá como o armazenamento de funções no programa entenderá como funciona a função recursiva e se você entende como a função recursiva funciona, entenderá como o compilador funciona e, se entender como o compilador funciona, sua mente funcionará como compilador e você depurará qualquer programa com muita facilidade.
Deixe-me explicar como a pilha funciona:
Primeiro você precisa saber como as funções armazenam na pilha:
Os valores de alocação de memória dinâmica de armazenamento de heap. Valores automáticos de alocação e exclusão de armazenamento de pilha.

Vamos entender com o exemplo:
def hello(x):
if x==1:
return "op"
else:
u=1
e=12
s=hello(x-1)
e+=1
print(s)
print(x)
u+=1
return e
hello(4)
Agora entenda partes deste programa:

Agora vamos ver o que é pilha e o que são partes da pilha:

Alocação da pilha:
Lembre-se de uma coisa: se alguma função obtiver "retorno", não importa que tenha carregado todas as suas variáveis locais ou qualquer coisa que ela retorne imediatamente da pilha, será o quadro da pilha. Significa que quando qualquer função recursiva obtém a condição base e colocamos retorno após a condição base, para que a condição base não espere para carregar variáveis locais que estão localizadas na parte “else” do programa, ela retornará imediatamente o quadro atual da pilha e agora se um quadro retornar próximo quadro está no registro de ativação. Veja isso na prática:

Desalocação do bloco:
Portanto, agora, sempre que uma função encontrada retorna a instrução, ela exclui o quadro atual da pilha.
enquanto retornar do valor da pilha retornará na ordem inversa da ordem em que foram alocados na pilha.

Essas são descrições muito curtas e, se você quiser saber mais sobre pilha e recursão dupla, leia dois posts deste blog:
Mais sobre a pilha passo a passo
Mais sobre recursão dupla passo a passo com pilha