quinta-feira, 22 de janeiro de 2015

Private Symbol Server + NuGet Symbol Packages

Num determinado momento durante o desenvolvimento do projeto no qual estou alocado no momento, eu quis aproveitar para depurar um pacote do NuGet que instalei no meu projeto, pacote este ao qual fui eu mesmo quem o criou. Eu quis aproveitar o cenário que já estava instaurado no momento da depuração do projeto para tentar encontrar um possível bug no componente do pacote. Porém isto não foi possível pois o pacote distribuído e referenciado continha apenas o assembly (.dll), e para a realização da depuração faz-se necessário a existência do arquivo de símbolos (.pdb).

Apesar da possibilidade de atender a este problema com uma solução em particular (e.g. copiando o .pdb manualmente), eu aproveitei para tentar encontrar uma solução madura para casos como este. O primeiro pensamento foi em convencionar a distribuição dos arquivos .pdb's dentro dos respectivos pacotes do NuGet, mas isso não me pareceu razoável como uma solução definitiva, pois assim os arquivos de simbolos também seriam disponibilizados no ambiente de produção, o que pra muitos é considerado inseguro, pois em casos específicos algumas informações valiosas sobre a construção da aplicação poderiam ser exploradas indevidamente.

Assim, depois de fazer uma pesquisa sobre o assunto, concluí que a alternativa seria configurar um Symbol Server Privado para ficar disponível no ambiente de desenvolvimento. Este Server fica então responsável pelo armazenamento dos arquivos de símbolos, e o depurador fica configurado para consultá-lo sempre que precisar depurar um assembly.

Apesar da solução ser simples, a minha pesquisa não o foi. Por que? Porque, de um lado, eu encontrei materiais explicando como configurar um Symbol Server para ser consumido pelo Visual Studio (WinDbg); do outro, encontrei como gerar Symbol Packages do NuGet para ser publicados no SymbolSource.org, mas não encontrei como utilizar um Symbol Package do NuGet num Symbol Server privado. Minha dúvida era: o depurador faria o download do arquivo .symbol.nupkg e saberia extrair o .pdb?

Uma vez que não consegui encontrar em uma única página na internet a resposta centralizada, onde eu pudesse concluir a dinâmica da coisa, decidi escrever este post. Então, vamos começar pelo princípio.

Conceito Básico de um Symbol/Source Server

Antes de tudo, apesar do nome bonito, um Symbol Server basicamente não passa de um diretório onde um depurador pode procurar pelos arquivos .pdb. Basicamente nestes arquivos ficam armazenadas as informações necessárias para a depuração, fazendo um mapeamento entre o binário do assembly e as linhas no script no arquivo fonte. 

Quando depuramos a nossa aplicação, o depurador realiza uma busca a um arquivo .pdb, que possua o mesmo nome do assembly (e.g. se o assembly é xpto.dll, então ele procura por xpto.pdb). Esta busca é realizada basicamente na seguinte ordem:
  • no diretório do assembly;
  • no diretório do windows;
  • no diretório %windir%\symbols\dll
  • no diretório %windir%\dll
  • no cache do Visual Studio (configurado em Options > Debugging > Symbols)
  • no Symbols Locations configurados no Visual Studio (configurado em Options > Debugging > Symbols)


Isso significa que se eu criar um diretório c:\temp\symbols, colocar ali o arquivo 'assembly.pdb' e configurar este diretório como um Symbol Location no Visual Studio, quando eu depurar 'assembly.dll' referenciada na minha aplicação o Visual Studio vai encontrar o pdb/symbols.

Neste momento os símbolos estariam carregados, mas isso ainda não quer dizer que conseguríamos depurar o nosso assembly. Assim seria porque o que o depurador encontraria no .pdb é apenas a referencia de onde está localizado o arquivo fonte utilizado na compilação. E aqui está algo que é um grande problema nesses arquivos: as referências a estes arquivos estão armazenados usando um caminho absoluto (e.g. C:\meuprojeto\minhaclasse.cs). Isso leva a dois problemas: O primeiro é que, se nós compilássemos o assembly num Build Server, ao depurar na máquina de desenvolvimento o depurador não iria encontrar o arquivo fonte (obviamente partindo do principio de que nas duas máquinas o projeto está configurado em diretórios de caminhos absolutos diferente). O segundo problema acarretado é que se nós versionássemos e compilássemos um assembly, por exemplo, 3 vezes, neste caso os três arquivos .pdb gerados apontariam para o mesmo arquivo fonte físico, o que me impossibilitaria a depuração dos pacotes de versões antigas, pois eu sempre teria apenas a última versão do código em disco.

Assim, se o arquivo fonte existir na máquina de desenvolvimento, OK; caso contrário, o Visual Studio vai pedir pra você indicar a localização do aquivo.


Como montar um Symbol/Source Server Básico

Um Symbol Server de verdade precisa ter o controle das versões dos .pdb's de um assembly, e atuar também como um Source Server, contendo o código fonte correto para a versão do assembly. Uma vez que o código fonte é carregado junto com o .pdb, é necessário ajustar a referência absoluta que estes aquivos possuem ao fonte. Este processo é chamado de  Source Indexing. Mais à frente eu explico como este processo pode e será realizado; permita-me por hora ater apenas à teoria.

Depois da indexação do fonte, nós precisaríamos organizar o nosso Symbol Server (que por hora é um diretório numa pasta, lembra)? por versão. Lembra-se do processo de busca que o depurador faz para encontrar o .pdb? Então... tem algo que eu não disse: em cada um dos diretórios é possível organizar da seguite forma: .\{assembly.pdb}\{GuidDoAssembly}\assembly.pdb. Assim o depurador saberá como encontrar a versão correta: pelo Guid do assembly.

A boa notícia é que não precisamos fazer isso "na mão". A Microsoft disponibiliza um conjuto de ferramentas (console) chamada Debugging Tools for Windows (WinDbg). Nele tem várias coisas, mas não é necessário instalar tudo, como é possível entender pela imagem abaixo.


Neste setup são instaladas ferramentas para indexação do fonte, montagem e consulta à um symbol store (server). Adicione o caminho da instalação do path do windows. Depois, para montar um Symbol Store de exemplo, use o seguinte comando no console:

symstore add /f Vvs.Nfe.TecnoSpeed.pdb /s "C:\PackageServer\symbols\" /t "Meu Produto"

Como resultado o .pdb (sem os fontes) será adicionado ao nosso symbol server, como pode ser visto conforme a imagem abaixo:


Assim, adicione o path na lista de Symbol Locations do Visual Studio e o depurador encontrará o .pdb correto para o assembly. Assim temos um Symbol server básico, porém ele ainda não funciona como Source Server, e eu não vou montá-lo na mão (o que é possível usando o script Ssindex.cmd, porém seria impraticável), mas vou fazer tudo isso à vera ao gerar o pacote do NuGet.

Usando Symbol Package do NuGet

O NuGet cria Symbol Packages! Para mais informações quanto à isso, procure no Google. O que não me contaram é como utilizar o raio do arquivo '.symbol.nupkg' num private Symbol Server. O processo manual seria:

  1. descompactar o pacote;
  2. indexar o fonte no pdb;
  3. adicionar ao store;

Então, o que o pessoal do SymbolSource.org fez foi criar um Serviço Web MVC4 chamado SymbolSourcer Server Basic (disponível no nuget.org) que linka o push do NuGet com o processo acima, e disponibiliza o Symbol Source por http. Assim, o que eu fiz foi:


  1. Crieu uma aplicação MVC 4 em branco;
  2. Instalei o pacote (Install-Package SymbolSource.Server.Basic);
  3. Alterei o appSetting 'SrcSrvPath' no web.config para apontar para o full path do SrcSrv (que foi instalado com o WinDbg);
  4. Publiquei o site;
  5. Alterei o Pool p/ 32 bits, conforme é possível ver mais abaixo:
  6. Dei permissão ao usuário do Pool no diretório 'Data'.





Depois disso, é possível disponibilizar o symbol package da seguinte forma:

nuget push Meu.Pacote.0.0.1.symbols.nupkg -source http://{SymbolServerSiteUrl}/nuget

Depois disso, adicione um Symbol Location ao Visual Studio com a URL descrita em 'Visual Studio Debugging URL' na página no nosso SymbolSource.

Outras informações:
http://msdn.microsoft.com/en-us/magazine/cc163563.aspx
http://www.lionhack.com/2014/01/14/advanced-dotnet-debugging-pdbs-and-symbols/

Nenhum comentário: