GLIBC: depurar vazamentos de memória: como interpretar a saída de mtrace ()
-
03-07-2019 - |
Pergunta
Eu estou tentando depurar um problema de vazamento de memória. Estou usando mtrace () para obter a / livre traço malloc / realloc. Eu corri minha prog e tem agora um arquivo de log enorme. Por enquanto, tudo bem. Mas eu tenho problemas de interpretação do arquivo. Olhe para estas linhas:
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x1502570 0x68
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x1502620 0x30
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x2aaab43a1700 0xa80
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x1501460 0xa64
O estranho nisso é que uma chamada (o mesmo endereço de retorno) é responsável por 4 alocações.
Ainda mais estranho:
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x2aaab43a1700 0xa2c
…
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x2aaab43a1700 0xa80
Entre essas duas linhas do bloco 0x2aaab43a1700 nunca está sendo liberado.
Alguém sabe como explicar isso? Como se poderia chamar resultado em 4 alocações? E como poderia malloc retornar um endereço que já foi alocado anteriormente?
edição 2008/09/30: O script para analisar a saída mtrace () fornecida pelo GLIBC (mtrace.pl) não é de nenhuma ajuda aqui. Ela só vai dizer: Alloc 0x2aaab43a1700 duplicar. Mas como isso pôde acontecer?
Solução
A função que está alocando a memória está sendo chamado mais de uma vez. O endereço chamador aponta para o código que fez a alocação, e que o código é simplesmente ser executado mais de uma vez.
Aqui está um exemplo em C:
void *allocate (void)
{
return (malloc(1000));
}
int main()
{
mtrace();
allocate();
allocate();
}
A saída do mtrace é:
Memory not freed: ----------------- Address Size Caller 0x0000000000601460 0x3e8 at 0x4004f6 0x0000000000601850 0x3e8 at 0x4004f6
Observe como o endereço chamador é idêntico? É por isso que o script mtrace análise está dizendo que eles são idênticos, porque o mesmo bug está sendo visto mais que uma vez, resultando em vários vazamentos de memória.
Compilando com bandeiras depura (-g) é útil se você pode:
Memory not freed: ----------------- Address Size Caller 0x0000000000601460 0x3e8 at /home/andrjohn/development/playground/test.c:6 0x0000000000601850 0x3e8 at /home/andrjohn/development/playground/test.c:6
Outras dicas
Você está olhando para a saída direta de mtrace, que é extremamente confuso e contraditório. Felizmente, existe um script Perl (chamada mtrace, encontrado dentro glibc-utils), que pode facilmente ajudar a análise dessa produção.
Compilar sua compilação com depuração, e run mtrace como tal:
$ gcc -g -o test test.c
$ MALLOC_TRACE=mtrace.out ./test
$ mtrace test mtrace.out
Memory not freed:
-----------------
Address Size Caller
0x094d9378 0x400 at test.c:6
A saída deve ser um muito mais fácil de digerir.
Uma explicação possível é que a mesma função é alocar diferentes tamanhos de buffer? Um exemplo é strdup.
Para a segunda pergunta, é possível que o tempo de execução está alocando alguns área de rascunho "estático", que não se destina a ser libertado até que o processo é encerrado. E nesse ponto, o sistema operacional irá limpar-se após o processo de qualquer maneira.
Pense nisso desta maneira:. Em Java, não há destruidores, e há garantias de que a finalização será sempre chamado para qualquer objeto
Tente executar o aplicativo sob valgrind. Pode dar-lhe uma melhor visão sobre o que está realmente a ser vazada.