Olá e seja bem-vindo.
Nesta aula, quero falar um pouco sobre a pilha de chamadas, o que ela é exatamente e como
você pode lê-la para ajudar a depurar seu programa.
Um rastreamento de pilha, que é basicamente o que é a pilha de chamadas, é gerado sempre que o aplicativo é bloqueado
devido a um erro fatal.
O motivo pelo qual o termo pilha é usado é porque ele se refere a uma estrutura de dados comum na programação.
A pilha é uma estrutura de dados conhecida como LIFO, last in, first out (último a entrar, primeiro a sair), e é assim que os dados são armazenados.
O último elemento de dados adicionado é o primeiro que você recupera.
Você pode pensar nisso como se estivesse comendo uma pilha de panquecas, a primeira panqueca está na parte inferior
e é adicionada.
A última panqueca está no topo.
Quando você vai comê-los, você come o último que foi adicionado.
Portanto, um rastreamento de pilha é muito útil quando o programa falha, pois contém informações que ajudam
a depurá-lo.
Portanto, um rastreamento de pilha mostra uma lista das chamadas de função que levaram ao erro.
E o que acontece é que, em um programa, quando você chama a função principal, esse é o ponto de entrada.
A função principal chamará outras funções.
Essas funções chamarão outras funções e todas as funções que forem implementadas ou chamadas
serão colocadas na pilha.
Assim, quando um programa trava, a pilha de chamadas informa qual foi a última função invocada
e a linha de código específica que causou o erro.
Portanto, o Callstack contém muitas informações úteis.
Ele incluirá o nome do arquivo e os números das linhas do código que causam a ocorrência da exceção
ou do erro fatal.
O topo da pilha contém a última chamada que causou o erro e pode haver chamadas aninhadas.
A parte inferior da pilha contém a primeira chamada que iniciou a cadeia de chamadas para chamar o erro.
Portanto, se você costuma usar C, isso estará na rotina principal, você invoca a principal, a principal invoca outra função,
que invoca outra função e assim por diante.
E a última linha é detalhada.
É muito, muito útil quando um aplicativo ou outro trava.
Assim, quando um programa em C falha, ele pode gerar uma falha de segmentação e isso não lhe dá muitas informações.
Isso significa apenas que algo ruim aconteceu.
Isso também gerará um arquivo de núcleo.
Você pode analisar o arquivo de núcleo que contém a pilha de chamadas, o que lhe dirá a linha exata
do código que causou o problema da falha.
Portanto, você precisa encontrar a chamada em seu aplicativo que está causando a falha.
E a pilha de chamadas é provavelmente a melhor maneira de fazer isso.
E nem sempre é necessário consultar esse arquivo principal.
Você também pode analisar a pilha de chamadas usando o GDB com um depurador.
E quero mostrar a você como isso se parece.
O programador também pode despejar o rastreamento da pilha a qualquer momento.
Portanto, se você detectar um erro ou reconhecer que pode ocorrer um erro fatal, poderá despejar a pilha de chamadas
para ver o que está acontecendo.
Se você não souber realmente quando o erro ocorrerá, ainda poderá despejar a pilha
de chamadas para ver os traços atuais que levaram a uma determinada chamada.
Ao usar o GDB, a pilha de chamadas terá a seguinte aparência.
Você pode acessar a pilha de chamadas por meio do menu quando estiver executando o depurador no GDB
e eu vou mostrar como fazer isso, mas é assim que ela se parece e o que você verá aqui é que ela tem um número,
um endereço e, em seguida, a função.
Essa pilha de chamadas específica aqui tem muitas chamadas diferentes, portanto, deve ter feito muitos aninhamentos diferentes
e muitas chamadas de funções diferentes.
Mas na parte inferior da pilha, na linha 22, está escrito GTK main.
Isso significa que o.
Isso significa que ele provavelmente executou a função principal.
E, nas linhas 21, parece que foi executado.
Talvez uma declaração diga que o loop principal foi executado.
E então parece que chamamos uma função chamada G main context iterate.
E dentro dessa função, sempre que havia outra chamada para uma função de despacho do contexto principal
do IAG, havia alguma coisa.
Portanto, havia uma função de iteração, depois uma função de despacho e, em seguida, os pontos de interrogação na linha
18 significam que não há informações de depuração.
Ele não sabe o que aconteceu lá.
E é por isso que, quando você cria seu programa, pode criá-lo para lançamento ou com informações
de depuração.
Quando você o constrói com informações de depuração, ele contém informações extras ao compilar para que, ao executar,
você possa examinar as pilhas de chamadas.
Quando você lançar o produto, crie algo para o lançamento.
O executável é muito menor e pode não conter essas informações.
Ele pode conter mais pontos de interrogação.
Mas isso também lhe dirá se você invocou alguma biblioteca, algum programa adicional, ele os chamará e lhe informará
a pilha de chamadas a partir daí.
Mas o que isso está mostrando, mais uma vez, são os diferentes eventos aninhados para a chamada na parte superior,
o que é importante, porque isso significará o que causou a falha.
Portanto, parece que, nessa pilha de chamadas específica, a atualização interna estava causando um problema.
E você pode ver que, da linha 22 à linha zero, há muitas invocações diferentes de códigos diferentes.
Mas o eventual travamento do programa na pilha de chamadas da linha zero.
Você também pode examinar as pilhas de chamadas enquanto o programa estiver em execução para ver as informações e poder
rastreá-las.
Portanto, se você colocar um ponto de interrupção dentro de uma função, você depura e executa o depurador nela.
O depurador vai parar nesse ponto de interrupção.
Você pode então abrir a pilha de chamadas e ela lhe dirá como você chegou a essa linha de código.
Essa função foi invocada antes dessa função.
E, mais uma vez, ele geralmente começa com a função principal.
Se você estiver familiarizado com a pilha, o conceito e a forma como ela está na memória, também
poderá alternar para quadros diferentes, mas essa é uma ferramenta muito útil para encontrar principalmente
falhas em C, e é muito fácil de ler.
Se você olhar apenas para a parte superior e não entender muito sobre a pilha de chamadas,
poderá olhar para a parte superior e dizer: "Ah, isso é uma fonte, é uma linha de código que causou a falha".
Aqui está um bom exemplo que encontrei na Web.
Excma.
Edu.
Isso ilustra exemplos de pilha de chamadas de função.
Portanto, se olharmos para um exemplo de código, como veremos, temos apenas um código simples.
Os cálculos não são realmente importantes.
Basicamente, estamos apenas chamando algumas funções.
Portanto, temos essa função mogrify e essa função truly half, e a principal vai chamar uma função mogrify e, em seguida,
vai chamar truly half e, depois, vai mogrify, de modo que podemos ilustrar a pilha de chamadas
e sua aparência nessas chamadas basicamente definindo um ponto de interrupção simples na principal e, em seguida,
observando a pilha de chamadas à medida que progredimos e fazemos chamadas.
Portanto, mais uma vez, vamos nos familiarizar com pontos de interrupção e informações desse tipo.
Mas eu só quero demonstrar a pilha de chamadas rapidamente e, na verdade, daremos um exemplo mais detalhado da pilha
de chamadas mais tarde.
Então, aqui eu defino um ponto de interrupção.
Posso prosseguir com a compilação e compilação, iniciar o depurador e atingir o ponto de interrupção.
E o que você notará agora é que pode ver todo o menu do depurador aqui, no lado direito, na parte inferior,
e verá a pilha de chamadas.
Vamos aumentar um pouco mais.
Mas logo quando ele para na linha 14, aqui ele tem apenas algumas instruções de atribuição e você verá que
a pilha de chamadas mostra main.
Se passarmos por cima disso e formos para a próxima linha, poderemos ver que ele vai para a linha 15
antes de chamar o Mogrify, certo?
Assim, você vê que a pilha de chamadas permanece com o Main.
Se entrarmos, você verá que a pilha de chamadas mostra o Mogrify.
Portanto, estamos de fato dentro dessa função.
Podemos passar por cima disso.
Portanto, ele executa a primeira linha e, em seguida, retorna temp.
Ele ainda permanece nessa função e, em seguida, retorna para onde faz a impressão F na linha 16, e você vê como a pilha de chamadas
está mudando.
Em seguida, vamos para a linha 18, chamamos a pilha de parada no main e entramos na função verdadeiramente intermediária.
E agora você vê a mudança na pilha de chamadas, onde ela mostra realmente que metade da função no topo é sempre
a função que foi chamada por último.
Novamente, passamos por cima e você vê que ainda estamos na metade.
Voltamos para Main, imprimimos algo na linha 19 e vamos para a linha 21.
Novamente, saltamos para a função modify, que, mais uma vez, você vê na pilha de chamadas. Saltamos, saltamos e, por
fim, imprimimos o resultado final na linha 23 e retornamos do programa.
Mas a pilha de chamadas nos mostra novamente a ordem das funções que estão sendo invocadas de uma função para
outra.
Portanto, se você tiver funções aninhadas, isso pode se tornar cada vez mais complicado.
A vantagem da pilha de chamadas é que, se você tiver uma linha de código que falha, por exemplo, na linha quatro, terá
uma pilha de chamadas que contém, no topo, o local onde a linha falhou.
Assim, você pode ver, percorrendo a pilha de chamadas, qual foi a última linha de código invocada,
e isso lhe dirá qual foi a linha de código que sofreu uma falha.
Mas você vai querer usar essa ferramenta quando seu programa travar.
Obrigado.