Qual é uma boa maneira de entender a estrutura geral de uma base de código?


8

Às vezes, é útil no meu trabalho modificar o código-fonte de outra pessoa ou descobrir como desenvolver coisas específicas para o seu próprio aplicativo. No entanto, nem todos os softwares possuem boa documentação.

Qual é uma boa maneira de entender a estrutura geral de uma base de código?

Por exemplo, quais rotinas chama quais rotinas, etc. Eu poderia usar uma ferramenta de documentação como o Doxygen para esse fim, no entanto, estava pensando se há uma estratégia melhor?


5
Acho que essa é uma pergunta razoável, mas também acho que ela provavelmente deveria ser feita aos programadores . não é uma ciência estritamente computacional, mas uma programação mais conceitual.
Geoff Oxberry

2
Existem técnicas para fazer isso específicas para códigos científicos?
David Ketcheson

Eu uso o Doxygen porque é fácil e pode fornecer imagens agradáveis ​​com gráficos que mostram como as rotinas estão conectadas. Observe que pode ser necessário criar uma rotina principal que envolva tudo, para obter uma imagem tão completa.
Allan P. Engsig-Karup

@ DavidKetcheson: Eu não acho que existem métodos para fazer isso específico para códigos científicos. Dada a forte tradição de códigos de pesquisa não documentados, talvez devesse existir.
Geoff Oxberry

Respostas:


9

Os seguintes segmentos estão relacionados tangencialmente:

Na primeira parte da minha tese, passei 18 meses modificando o código não documentado do Fortran, e uma das primeiras tarefas foi tentar entender a estrutura geral de uma base de código. A coisa mais importante que sugiro que você faça é tomar notas em um arquivo de texto sempre que descobrir algo. Você não precisa reaprender ou redescobrir coisas durante esse processo demorado e frustrante.

No meu caso, não havia "API" para falar, pois os argumentos das funções não eram auto-documentados, porque o programador anterior usava o estilo Fortran 77 e, portanto, identificadores curtos com pouca ou nenhuma sugestão do que Eles significavam. Não houve testes e, como é o Fortran, não há cabeçalhos. Adicionando ainda mais diversão à mistura, havia algumas funções aqui e ali escritas em C ou C ++.

Coisas que funcionaram para mim (supondo que você trabalhe no Linux):

  • grep. Aprenda a amar grep; você o usará no shell para descobrir onde as funções são declaradas e chamadas, e a saída informará em quais arquivos procurar.
  • O comando "find" no seu editor de código favorito ou IDE. Depois que você souber em qual arquivo procurar, o comando "find" economizará seu tempo procurando por chamadas de função.
  • Refatoração agressiva, se você puder alterar a base de código. Fiz os nomes das variáveis ​​se auto-documentarem para não precisar gastar esforço mental reaprendendo coisas que já havia descoberto. Também simplifiquei o design do código, conforme o descobri, tornando-o menos confuso. Não há mais programa principal de 1000 linhas!
  • Comentários agressivos. Novamente, se você puder alterar a base de código, comente as coisas para que você saiba o que descobriu. Eu não usei o Doxygen, mas o Doxygen é bom para isso.
  • nm. Pode ser útil em bibliotecas se você não tiver o código-fonte para elas, mas deseja saber se uma função que você encontrou está nessa biblioteca. No entanto, só funciona se os símbolos da biblioteca não tiverem sido removidos.
  • Percorra o código com um depurador. É muito mais eficiente do que usar printdeclarações. ddde gdbsão ótimos, e em praticamente todos os sistemas Linux existentes. Sinta-se livre para usar seu depurador favorito.
  • Bug dos desenvolvedores. Esta opção é realmente melhor para perguntas muito específicas. Se você for até eles (como eu) e disser: "Eu não entendo o que está acontecendo aqui", eles podem ter pena de você e tentar explicar as coisas em detalhes gerais, mas isso será de uso limitado em a longo prazo. Você terá que fazer o trabalho de perna, porque os desenvolvedores não fizeram isso por você na documentação e na explicação da estrutura de sua base de código por escrito. Os desenvolvedores são realmente bons quando você está realmente preso a coisas pequenas (se eles se lembram do que fizeram).

Coisas que eu gostaria de ter pensado antes, ou simplesmente não eram opções para mim:

  • Doxigênio. Se você ajustar as opções do Doxyfile, o Doxygen gerará automaticamente muita documentação para você, mesmo sem a sintaxe especial para comentários do Doxygen, que pode ser um bom ponto de partida; Eu usei isso em projetos posteriores que encontrei e tem sido incrivelmente útil.
  • Teste de unidade. Se você puder alterar a base de código e tiver alguma idéia do que deve fazer, escreva testes de unidade para várias funções. (É uma habilidade útil para aprender, independentemente.)
  • Se você estiver trabalhando com C / C ++, observe os cabeçalhos.
  • Escreva exemplos de programas. Não é uma opção para mim nesse projeto Fortran, mas tem sido útil para mim ao escolher APIs de terceiros. Além disso, veja os programas de exemplo, se houver algum.
  • Use gcove lcovfaça uma análise de cobertura em execuções típicas do código, se você tiver exemplos ou executáveis ​​para trabalhar. Se houver exemplos que devem executar grandes partes da base de código, essas duas ferramentas combinadas informarão quantas vezes cada linha de código é visitada. É mais útil com os sinalizadores de depuração ativados. Se uma parte do código não é visitada, provavelmente é menos importante entendê-lo imediatamente. Se uma parte do código é visitada muito, provavelmente vale a pena entender o que é. Talvez o código seja executado muito porque é um loop sem importância, ou pode ser uma função importante na qual muitas outras funções dependem. Você não pode dizer apenas sobre a análise de cobertura, mas a análise de cobertura fornece uma idéia de onde você deve concentrar seu tempo.
  • Ferramentas de análise de código estático como splintpodem dizer se algo suspeito está acontecendo no código, como algumas variáveis ​​nunca são usadas.
  • Criação de perfil. Mais uma vez, fornece dados que não informam imediatamente o que é ou não importante, mas sugerem o que pode ser importante. Se você gastar muito tempo na CPU chamando uma função, convém examinar e ver o que ela faz. Você também pode usar a saída de criação de perfil com dote graphvizpara gerar gráficos de chamada e ver quantas vezes as funções são chamadas, como a análise de cobertura. Para códigos complexos, uma análise gráfica pode ser muito mais útil.
  • Se você está trabalhando em C, o Frama-C deve ser útil na análise de código, mas nunca o usei porque parecia muito complicado para valer o esforço. Eu trabalho em C puro, mas na maioria das vezes eu escrevo código; Eu nunca trabalhei com código C não documentado.

2
Qual é a maneira mais comum de entender um aplicativo C ++ muito grande? no Stack Overflow é específico de C ++, mas outra questão existente de alguma antiguidade.
dmckee --- gatinho ex-moderador

Boa lista. Coloquei tudo no git e uso "git grep". E também o histórico do git para arquivos individuais, se disponível.
Ondřej Čertík

3

Eu sempre digo aos meus alunos para lerem um código de baixo para cima: você começa em main () e vê o que ele chama. Normalmente, este é apenas um pequeno número de funções. Em seguida, você analisa as funções chamadas de main () que normalmente definem o fluxo geral do algoritmo (loop de tempo, montagem, solucionador, saída, etc). Suba mais ou menos dois níveis para obter uma visão geral do algoritmo a 30.000 pés. O restante pode ser encontrado na documentação do doxygen etc.

Mas como eu disse, a mensagem é: Leia o código de baixo para cima.


3
Seu conselho é bem aceito, mas não parece ler código de baixo para cima. Em vez disso, parece ler de cima para baixo. Main () (ou Programa, no Fortran) é a unidade de programa mais avançada em um código executável. Além disso, seu conselho exige alguns ajustes quando o código em questão é uma biblioteca que define uma API. Não há função main (); nesse caso, você precisa combinar sua estratégia de ver quais funções chamam para decidir quais funções são importantes para você, geralmente observando (ou codificando) programas de exemplo.
Geoff Oxberry

Pode haver alguma confusão sobre "de baixo para cima" porque os programadores que aprenderam Pascal no início frequentemente colocam o ponto de entrada no final (ou na parte inferior) do arquivo - porque Pascal fez apenas uma passagem e tudo teve que ser apresentado antes dele foi chamado. Como Geoff, no entanto, prefiro a lógica na qual Main () está no "topo" nocional de uma árvore que cresce para baixo.
dmckee --- gatinho ex-moderador

Sim, eu quis dizer a maneira do dmckee de ter main () no final do arquivo porque, caso contrário, seria necessário encaminhar tudo - declarar tudo.
Wolfgang Bangerth
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.