Esta página descreve as várias melhorias na contabilização de memória introduzidas no Android 12.
Estatísticas de DMA-BUF em sysfs
No Android 11 e no Android 12, debugfs não pode ser
montado em builds de usuário. Por isso, as estatísticas de DMA-BUF foram adicionadas a sysfs no diretório /sys/kernel/dmabuf/buffers do Android 12.
| Caminho | Descrição |
|---|---|
/sys/kernel/dmabuf/buffers
|
O diretório /sys/kernel/dmabuf/buffers contém um
snapshot do estado interno de cada DMA-BUF.
/sys/kernel/dmabuf/buffers/<inode_number> contém as estatísticas do DMA-BUF com o número de inode exclusivo <inode_number>.
|
/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name
|
Esse arquivo somente leitura contém o nome do exportador DMA-BUF. |
/sys/kernel/dmabuf/buffers/<inode_number>/size
|
Esse arquivo somente leitura especifica o tamanho do DMA-BUF em bytes. |
A API libdmabufinfo analisa as estatísticas de sysfs DMA-BUF para expor estatísticas por exportador e por buffer.
Os drivers de kernel que exportam DMA-BUFs precisam definir o campo exp_name de
struct dma_buf_export_info corretamente para o nome do exportador antes de invocar a API
dma_buf_export() para criar um DMA-BUF. Isso é necessário para que o libdmabufinfo
e a ferramenta dmabuf_dump derivem estatísticas por exportador, que são
expostas no bugreport.
A ferramenta dmabuf_dump foi modificada para gerar essas informações com um novo argumento, -b.
Estatísticas do framework de heaps DMA-BUF
O ION no GKI 2.0 está sendo descontinuado em favor do framework de heaps DMA-BUF, que faz parte do kernel Linux upstream.
As seguintes estatísticas globais de ION são rastreadas no Android 11:
- Tamanho total dos DMA-BUFs exportados por cada heap ION
- Tamanho total da memória pré-alocada não utilizada armazenada por cada heap ION.
Não há uma interface disponível para expor estatísticas de heap por ION no Android 11.
A tabela a seguir compara as interfaces de estatísticas do ION com as equivalentes para dispositivos que usam o framework de heap DMA-BUF no Android 12.
| Android 11 ou dispositivos lançados com suporte ao ION no Android 12 | Dispositivos lançados com heaps DMA-BUF no Android 12 | |
|---|---|---|
| Estatísticas de ION por heap | Nenhum | Analisado das estatísticas sysfs do DMA-BUF |
| Tamanho total dos BUFs da DMA exportados | /sys/kernel/ion/total_heap_size_kb
(Não inclui o tamanho dos DMA-BUFs exportados por exportadores não ION) |
Analisado das estatísticas do sysfs DMA-BUF
(inclui o tamanho de todos os DMA-BUFs exportados). |
| Memória total agrupada por heaps | /sys/kernel/ion/total_pool_size_kb |
/sys/kernel/dma_heap/total_pool_size_kb |
Melhorar a precisão do cálculo da RAM perdida
Antes, o cálculo da RAM perdida era feito da seguinte forma:
final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- kernelUsed - memInfo.getZramTotalSizeKb();
O componente totalPss incluía o uso da memória da GPU (retornado pela interface getMemory()
do HAL Memtrack). O componente kernelUsed incluía o uso total da memória DMA-BUF.
No entanto, para dispositivos Android, a memória da GPU veio do seguinte:
- Alocações diretas feitas pelo driver de GPU usando o alocador de páginas físicas
- DMA-BUFs mapeados no espaço de endereço da GPU
Portanto, os DMA-BUFs que foram mapeados na memória para o espaço de endereço da GPU foram subtraídos duas vezes quando a RAM perdida foi calculada. O Android 12 implementa uma solução para calcular o tamanho dos DMA-BUFs mapeados no espaço de endereços da GPU, o que significa que ele é contabilizado apenas uma vez no cálculo de RAM perdida.
Confira os detalhes da solução:
- A API HAL Memtrack
getMemory(), quando chamada com PID 0, precisa informar o total global de memória privada da GPU paraMemtrackType::GLeMemtrackRecord::FLAG_SMAPS_UNACCOUNTED. getMemory()quando chamado comPID 0para umMemtrackTypediferente deGLnão pode falhar. Em vez disso, ele precisa retornar 0.- A solução tracepoint/eBPF de memória da GPU adicionada no Android 12 considera a memória total da GPU. Subtrair a memória privada total da GPU da memória total da GPU fornece o tamanho dos DMA-BUFs mapeados no espaço de endereço da GPU. O valor pode ser usado para melhorar a precisão dos cálculos de RAM perdida, contabilizando corretamente o uso da memória da GPU.
- A memória privada da GPU está incluída em
totalPssna maioria das implementações do HAL Memtrack e, portanto, precisa ser duplicada antes de ser removida delostRAM.
A solução implementada é detalhada na próxima seção.
Remover a variabilidade do Memtrack da RAM perdida
Como as implementações do HAL do Memtrack podem variar entre parceiros, a memória da GPU incluída em totalPSS do HAL nem sempre é consistente. Para remover a variabilidade de lostRAM, a memória considerada em MemtrackType::GRAPHICS e MemtrackType::GL é removida de totalPss durante o cálculo de lostRAM.
A memória MemtrackType::GRAPHICS é removida de totalPss e substituída pela memória totalExportedDmabuf no cálculo lostRAM em ActivityManagerService.java, conforme mostrado no código a seguir:
final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
. . .
final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped;
. . .
// Account unmapped dmabufs as part of the kernel memory allocations
kernelUsed += dmabufUnmapped;
// Replace Memtrack HAL reported Graphics category with mapped dmabufs
totalPss -= totalMemtrackGraphics;
totalPss += dmabufMapped;
A memória MemtrackType::GL é removida de totalPss e substituída pela memória da GPU particular (gpuPrivateUsage) no cálculo de lostRAM em ActivityManagerService.java, conforme mostrado no código a seguir:
final long gpuUsage = Debug.getGpuTotalUsageKb();
. . .
final long gpuPrivateUsage = Debug.getGpuPrivateMemoryKb();
. . .
// Replace the Memtrack HAL-reported GL category with private GPU allocations.
// Count it as part of the kernel memory allocations.
totalPss -= totalMemtrackGl;
kernelUsed += gpuPrivateUsage;
Cálculo atualizado da RAM perdida
A memória total da GPU particular e a memória total do buffer DMA exportado estão contidas em kernelUsed + totalPss, que é removido de lostRAM. Isso elimina a contagem dupla e a variabilidade do Memtrack do cálculo da RAM perdida.
final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- kernelUsed - memInfo.getZramTotalSizeKb();
Validação
Os testes do VTS aplicam a regra de que dispositivos lançados no Android
12 com uma versão do kernel do Linux 5.4 ou mais recente oferecem suporte à
API getGpuDeviceInfo().
Uma nova API HAL Memtrack getGpuDeviceInfo() precisa retornar informações sobre o dispositivo de GPU em uso.
Isso oferece uma melhor contabilização da memória e visibilidade do buffer de DMA e do uso da memória da GPU. Implemente a HAL AIDL memtrack para melhorar a RAM perdida e a contabilidade de memória. Esse recurso não depende dos Serviços do Google.
Implementação
Esse recurso depende da HAL Memtrack da AIDL, e as instruções para implementá-la no Android 12 estão incluídas no código como comentários. Há uma previsão de que todos os HALs de HIDL sejam convertidos para AIDL em versões futuras.
As seguintes APIs foram adicionadas a core/java/android/os/Debug.java:
/**
* Return total memory size in kilobytes for exported DMA-BUFs or -1 if
* the DMA-BUF sysfs stats at /sys/kernel/dmabuf/buffers could not be read.
*
* @hide
*/
public static native long getDmabufTotalExportedKb();
/**
* Return memory size in kilobytes allocated for DMA-BUF heap pools or -1 if
* /sys/kernel/dma_heap/total_pools_kb could not be read.
*
* @hide
*/
public static native long getDmabufHeapPoolsSizeKb();
Para verificar se a versão funciona conforme o esperado, integre os tracepoints nos
drivers de GPU e implemente a API AIDL memtrack HAL getMemory() para
retornar corretamente a memória total global privada da GPU quando chamada com PID 0 para
MemtrackType::GL e MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.