Desenvolvimento do Diablo IV na Blizzard e depuração de despejos de memória do Linux no Visual Studio

O blog da Microsoft recentemente apresentou um artigo escrito por Bill Randolph, engenheiro de software sênior da Blizzard que trabalha no Diablo IV. Este artigo cobre algumas das especificidades do trabalho no Diablo IV e, em particular, descreve como usar o Visual Studio para depurar código baseado em Linux. Hoje chamamos sua atenção para uma tradução deste material.





Introdução



Quando trabalhamos no Diablo IV, escrevemos todo o código no Windows e então compilamos para diferentes plataformas. Isso também se aplica aos nossos servidores que executam Linux. (O código inclui diretivas de compilação condicional e, quando necessário, contém fragmentos escritos especificamente para uma plataforma específica.) Nosso trabalho é organizado dessa forma por vários motivos. Para começar, as principais habilidades profissionais de nossa equipe são o Windows. Até mesmo nossos programadores de servidor estão mais familiarizados com o desenvolvimento do Windows. Agradecemos a capacidade de usar as mesmas ferramentas e a mesma base de conhecimento por todos os programadores de nossa equipe.



Outro motivo mais importante pelo qual fazemos desenvolvimento no Windows é a capacidade de aproveitar as vantagens do conjunto de ferramentas altamente funcional e confiável que o Visual Studio nos oferece. E mesmo se estivéssemos desenvolvendo algo no Linux, então posso dizer que não há nada no mundo Linux que possa ser comparado ao Visual Studio.



É verdade, por causa disso, enfrentamos algumas dificuldades que surgem quando o servidor trava e precisamos depurar um despejo de memória. Temos a capacidade de efetuar login remotamente em uma máquina virtual (ou, mais precisamente, em um contêiner) que falhou, podemos executar o gdb para descobrir as razões do que aconteceu. Mas essa abordagem tem muitas desvantagens. Por exemplo - não implantamos binários junto com o código-fonte - como resultado, ao trabalhar com uma máquina virtual ou com um contêiner, o código-fonte não está disponível na sessão gdb.



Outra complicação está no próprio gdb. O fato é que se não usarmos essa ferramenta constantemente, de forma regular, ela não pode ser dominada em um nível que nos convém. Simplificando, nossos desenvolvedores estariam muito mais dispostos a usar ferramentas familiares para depurar código. Como apenas 2-3 de nossos desenvolvedores conhecem muito bem o gdb, quando algo dá errado, são eles que procuram o problema. E isso não pode ser chamado de distribuição ideal da carga de trabalho para programadores.



Sempre quisemos encontrar uma maneira de depurar o código Linux que fosse intuitiva. É por isso que estamos tão animados em poder usar o novo recurso do Visual Studio, que nos permite resolver exatamente esse problema em um ambiente familiar! E não seria exagero dizer que graças a isso nosso sonho se tornou realidade.





Sobre nosso processo de depuração de código



A depuração do código Linux no Visual Studio só é possível se o Windows Subsystem for Linux (WSL) estiver instalado no sistema ou se a conexão com o Linux estiver configurada no Connection Manager . Todos os nossos desenvolvedores de back-end instalaram WSL usando a distribuição na qual implantamos nosso projeto. Executamos um script que escrevi que instala todas as ferramentas de desenvolvimento e bibliotecas de suporte necessárias para construir nosso servidor em WSL.



(Vou me desviar de nosso tópico principal por um momento. Gostaria de enfatizar que chegamos à conclusão de que WSL é o melhor ambiente existente que permite aos desenvolvedores testar mudanças em compilações Linux. Este esquema de trabalho parece extremamente conveniente: mudar para WSL, usando o comando cd



para ir para um diretório de código compartilhado e construir o projeto diretamente a partir dele. Esta é uma solução muito melhor do que usar uma máquina virtual ou mesmo um contêiner. Se você construir projetos usando CMake, também poderá usar o suporte integrado do Visual Studio para WSL .)



Vou contar um pouco sobre nossas assembleias. Desenvolvemos o código em Windows e temos uma versão Windows de nosso servidor projetada para funcionar neste SO. Isso é útil para nós ao trabalharmos nas capacidades usuais do projeto. Mas estamos implantando nosso código do lado do servidor no Linux, que requer compilações no Linux. Os assemblies do Linux são criados em um build farm. Ele usa um sistema de construção que é executado em um computador Linux. Com sua ajuda, nosso projeto de servidor e o contêiner correspondente são montados, que são posteriormente implantados. Os binários do Linux são implantados apenas em contêineres. Normalmente, os desenvolvedores não têm acesso a esses contêineres.



Quando um dos servidores em nossa infraestrutura falha, somos notificados por um script especial, após o qual os arquivos de despejo são gravados em uma pasta de rede compartilhada. Para depurar esses arquivos, seja no Linux ou Visual Studio, você precisa de um programa em funcionamento. Ao depurar, é útil usar exatamente as mesmas bibliotecas compartilhadas usadas no contêiner implementado. Usamos um script diferente para obter esses arquivos. Primeiro, copiamos o dump para a máquina local e, em seguida, executamos o script e passamos informações sobre esse dump. O script baixa o contêiner Docker que foi construído para a versão testada do código, extrai os arquivos executáveis ​​de nosso servidor, bem como certas bibliotecas de tempo de execução comuns. Tudo isso é necessário para o gdb. (Isso, ao trabalhar com gdb, evita os problemas de compatibilidade que podem surgir sese a versão WSL do sistema não for exatamente igual à versão do Linux implantado.) O script, configurando uma sessão de depuração, grava dados em ~/.gdbinit



, indicando que as bibliotecas compartilhadas são bibliotecas do sistema.



Em seguida, vamos para o Visual Studio, onde a diversão começa. Estamos baixando uma solução de construção para a versão Windows de nossos servidores. Em seguida, abrimos uma nova caixa de diálogo de depuração usando o comando Debug -> Other Debug Targets -> Debug Linux Core Dump with Native Only



. Marcamos a caixa Debug on WSL



e inserimos os caminhos para os arquivos de despejo e os binários do servidor (destinados a WSL!). Depois disso, basta apertar o botão Debug



e observar o que está acontecendo.





Iniciando a depuração no Visual Studio



Visual Studio inicia automaticamente o gdb no WSL. Depois que o sistema estiver trabalhando com o disco por algum tempo, a pilha de chamadas do programa que falhou é exibida e o ponteiro de instrução é definido para a linha de código correspondente. Este é realmente um admirável mundo novo!



A seguir, tratamos da identificação da própria falha. Temos um manipulador de falhas que intercepta o evento apropriado para executar alguns procedimentos de serviço. Portanto, as informações sobre a própria falha estão localizadas, em um servidor de thread único, mais profundamente na pilha de chamadas. Mas alguns de nossos servidores são multithread. E a falha pode acontecer em qualquer um de seus segmentos. O manipulador de falhas registra informações sobre o código do arquivo com defeito e sobre o número da linha. Portanto, examinar esses dados nos dá a primeira pista. Estamos procurando o lugar na pilha de chamadas que corresponde à execução deste código.



Antigamente, ou seja, algumas semanas atrás, teríamos usado gdb para rastrear todas as threads e, em seguida, varrer a lista resultante para encontrar a thread cuja pilha de chamadas provavelmente travou. Por exemplo, se o encadeamento estava em um estado inativo, provavelmente ele não travou. Precisamos de uma pilha que tenha mais do que alguns quadros e informações de que estamos lidando com um thread em espera. Em seguida, precisamos examinar o código para entender qual é o problema. Se for algo simples, você pode ver diretamente no código. Se enfrentarmos um problema mais complicado, teremos que recorrer aos recursos do gdb para investigar o estado do processo.



Mas o Visual Studio nos oferece recursos muito mais poderosos do que tínhamos antes. Em ambientes multithread, você pode abrir uma janela em uma sessão de depuração Threads



e clicar nos threads para ver suas pilhas. No entanto, isso é muito semelhante à abordagem usada no gdb. Portanto, se você precisa estudar, digamos, 50 tópicos, isso pode se tornar uma tarefa bastante demorada e entediante. Felizmente, o Visual Studio possui uma ferramenta que torna essa tarefa muito mais fácil. Esta é a janela Pilhas paralelas .



Eu admito, a maioria de nós não sabia sobre o Parallel Stacks até que Erica Sweet e sua equipe nos contaram sobre isso. Se durante a sessão de depuração execute o comando Debug -> Windows -> Parallel Stacks



- uma nova janela será aberta, exibindo informações sobre a pilha de chamadas de cada thread no processo sob investigação. Isso é algo como uma visão panorâmica de todo o espaço do processo. Qualquer frame de pilha de qualquer thread pode ser clicado duas vezes. Depois disso, o Visual Studio pulará para esse quadro na janela do código-fonte e na janela da pilha de chamadas. Isso nos ajuda muito a economizar tempo.



Depois de ver o código nas proximidades do local do acidente, podemos examinar as variáveis ​​usando o mouse, o QuickWatch ou qualquer uma das muitas ferramentas do Visual Studio. Claro, em compilações de lançamento, muitas variáveis ​​são otimizadas, mas ao mesmo tempo, muitas não são! Nós, usando a interface do Visual Studio, podemos localizar o problema muito mais rápido do que antes de usar o gdb.







Resultado



Nossa equipe está muito feliz em poder depurar dumps do Linux no Visual Studio, o ambiente em que estamos desenvolvendo. Para nós, esta é uma grande melhoria, pois permite que muito mais desenvolvedores do que nunca diagnostiquem problemas na natureza. Isso nos permite tirar proveito das poderosas ferramentas de depuração do Visual Studio. Após a conclusão da preparação preliminar do ambiente de trabalho, você pode entrar em uma sessão de depuração do Visual Studio em literalmente questão de minutos. Esse recurso aumenta muito a velocidade de localização de problemas e a eficiência do nosso trabalho. Obrigado a Erica e sua equipe pela ajuda.



O que você acha mais útil no Visual Studio?






All Articles