Este documento mostra como ativar ferramentas de depuração especiais ao usar a AGDE. Essas ferramentas podem ajudar com erros de corrupção de memória e substituição difíceis de diagnosticar.
Limpador HWAddress e Limpador de endereços
Limpador HWAddress (HWASan) eLimpador de endereços (ASan) são ferramentas de depuração de corrupção de memória que ajudam a depurar erros de corrupção de memória e substituição, como estes:
- Overflows e underflows de buffer de pilha
- Overflows e underflows de buffer de heap
- Uso de pilha fora do escopo
- Erros double free e wild free
- Uso da pilha após o retorno (somente HWASan)
Recomendamos ativar o HWASan ou o ASan somente quando você estiver depurando um problema ou como parte de testes automatizados. Embora essas ferramentas tenham bom desempenho, o uso delas gera uma penalidade.
Comportamento do tempo de execução
Quando ativado, o HWASan e o ASan verificam automaticamente a corrupção de memória no app.
Se um erro de memória for detectado, o app falha com um erro de SIGBART
(cancelamento de sinal)
e mostra uma mensagem detalhada no logcat. Uma cópia da mensagem também é
gravada em um arquivo em /data/tombstones
.
A mensagem de erro é semelhante a esta:
ERROR: HWAddressSanitizer: tag-mismatch on address 0x0042a0826510 at pc 0x007b24d90a0c
WRITE of size 1 at 0x0042a0826510 tags: 32/3d (ptr/mem) in thread T0
#0 0x7b24d90a08 (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x2a08)
#1 0x7b8f1e4ccc (/apex/com.android.art/lib64/libart.so+0x198ccc)
#2 0x7b8f1db364 (/apex/com.android.art/lib64/libart.so+0x18f364)
#3 0x7b8f2ad8d4 (/apex/com.android.art/lib64/libart.so+0x2618d4)
0x0042a0826510 is located 0 bytes to the right of 16-byte region [0x0042a0826500,0x0042a0826510)
allocated here:
#0 0x7b92a322bc (/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so+0x212bc)
#1 0x7b24d909e0 (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x29e0)
#2 0x7b8f1e4ccc (/apex/com.android.art/lib64/libart.so+0x198ccc)
Pré-requisitos
Requisitos do HWASan
Para usar o HWASan:
- Use a AGDE 24.1.99 ou mais recente.
- O app precisa ser criado usando o NDK 26 ou mais recente.
- O app precisa ser criado com o SDK de destino 34 ou mais recente.
- O destino precisa ser um dispositivo
arm64-v8a
com o Android 14 (nível 34 da API) ou mais recente.
Usar a biblioteca C++ padrão compartilhada no projeto
Devido a um problema conhecido, o ASan é incompatível com o tratamento de exceções de C++ ao usar libc++_static
. Esse problema não ocorre quando libc++_shared
é usado.
O HWASan tem sua própria implementação dos operadores new
e delete
, que não podem
ser usados se a biblioteca padrão estiver estaticamente vinculada ao projeto.
Para mudar essa configuração, consulte a seção Como vincular a biblioteca C++ padrão deste documento.
Ativar a geração de ponteiro do frame
O HWASan e o ASan usam um unwinder baseado em ponteiro de frames rápido para gerar informações de stack trace para eventos de alocação e desalocação de memória. Isso significa que você precisa ativar a geração de ponteiro do frame nas configurações do compilador C++ para usar esses recursos. Ou seja, é necessário desativar a otimização da omissão do ponteiro do frame.
Para mudar essa configuração, consulte a seção Como ativar a geração de ponteiro do frame deste documento.
Como configurar o projeto do Visual Studio para usar o HWASan ou o ASan
Como ativar o HWASan ou o ASan
Para ativar o HWASan ou o ASan, acesse Configuration Properties > General nas Property Pages (páginas de propriedades) do projeto.
Figura 1: a opção Properties do projeto na janela do Solution Explorer do Visual Studio.
Figura 2: a configuração Address Sanitizer (ASan) nas propriedades gerais do projeto.
Para ativar o HWASan no projeto, mude a configuração Address Sanitizer (ASan) para Hardware ASan Enabled (fsanitize=hwaddress).
Para ativar o ASan no projeto, mude a configuração Address Sanitizer (ASan) para ASan Enabled (fsanitize=hwaddress).
Como ativar a geração de ponteiro do frame
A geração de ponteiro do frame é controlada pela configuração do compilador C/C++ Omit Frame Pointer e pode ser encontrada nas Property Pages do projeto em Configuration Properties > C/C++ > Optimization.
Figura 3: onde encontrar a configuração Omit Frame Pointer.
Ao usar o HWASan ou o ASan, defina a configuração Omit Frame Pointer como No (-fno-omit-frame-pointer).
Como vincular a biblioteca C++ padrão no modo de biblioteca compartilhada
A configuração do modo do vinculador para a biblioteca C++ padrão pode ser encontrada nas Property Pages do projeto em Configuration Properties > General, na seção Project Defaults.
Figura 4: onde encontrar a configuração do modo do vinculador para a biblioteca C++ padrão.
Ao usar o HWASan ou o ASan, defina Use of STL como Use C++ Standard Libraries (.so). Esse valor vincula a biblioteca C++ padrão ao projeto como uma biblioteca compartilhada, necessária para que o HWASan e o ASan funcionem corretamente.
Como criar uma configuração do build para uso do Limpador de endereços
Se você preferir usar o HWASan ou o ASan temporariamente, talvez não queira criar uma nova configuração do build apenas para uso dela. Isso pode acontecer se o projeto for pequeno, se você estiver explorando o recurso ou em resposta a um problema descoberto durante os testes.
No entanto, se você achar que ela é útil e planeja usá-la regularmente, crie uma nova configuração do build para o HWASan ou o ASan, conforme demonstrado no exemplo Teapot. Isso pode ser feito se, por exemplo, você executar o Limpador de endereços regularmente como parte dos testes de unidade ou durante testes de fumaça feitos de um dia para o outro.
A criação de uma configuração do build separada pode ser especialmente útil se você tiver um projeto grande que consome um grande número de bibliotecas de terceiros diferentes, onde você normalmente as vincula estaticamente à biblioteca C++ padrão. As configurações de build dedicadas podem ajudar a garantir que as configurações do projeto permaneçam sempre precisas.
Para criar uma configuração de buiild, nas Property Pages do projeto, clique
no botão
Configuration Manager… e abra o menu suspenso
Active solution configuration. Selecione
Como usar o HWASan com alocadores de memória personalizados
O HWASan intercepta automaticamente a memória alocada por malloc
(ou new
)
para injetar tags em ponteiros e verificar incompatibilidades.
No entanto, ao usar um alocador de memória personalizado, o HWASan não consegue interceptar automaticamente os métodos de alocação de memória personalizados. Portanto, se você quer usar o HWASan com seu alocador de memória personalizado, instrumente o alocador de memória para chamar o HWASan explicitamente. Isso pode ser feito com apenas algumas linhas de código.
Pré-requisitos
Os métodos HWASan que você precisa chamar são definidos neste cabeçalho:
#include "sanitizer/hwasan_interface.h"
Instrumentar o método de alocação de memória
Aloque objetos com granularidade e alinhamento de bloco de 16 bytes. Por exemplo, se você tiver um alocador de pool que serve objetos de tamanho fixo de 24 bytes, arredonde suas alocações para 32 bytes e alinhamento para 16 bytes.
Gere uma tag de 8 bits. A tag não pode usar valores de 0 a 16, porque eles são reservados para uso interno.
Ative o HWASan para começar a rastrear a região de memória com essa tag:
__hwasan_tag_memory((void*) address, tag, size);
Injete a tag nos 8 bits superiores do ponteiro:
address = __hwasan_tag_pointer((void*) address, tag);
Instrumentar seu método de dealocação de memória
Redefina a tag da região de memória para que outros acessos pelos ponteiros marcados existentes falhem:
__hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), 0, size);
Como trabalhar com um pool de objetos pré-alocado
Se o alocador de memória pré-alocar objetos em um pool e retornar objetos de volta ao pool em vez de realmente liberá-los, o método de dealocação poderá substituir diretamente a tag da memória e do ponteiro com um novo valor:
```
__hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), tag, size);
ptr = __hwasan_tag_pointer((void*)ptr, tag);
```
Se você usar essa técnica, seus métodos de alocação não precisarão marcar ponteiros ou blocos de memória, mas marcar os ponteiros e blocos de memória ao pré-alocar os objetos no pool. Consulte Exemplo de PoolAllocator para ver um exemplo que usa esse estilo.