Об утилите cachestat я узнал из статьи Brendan Gregg - Analyzing a High Rate of Paging.

В ней расследуются причины аномально долгого процессинга файлов размером в 100Gbytes, тогда как файлы в 40Gbytes обрабатывались на несколько порядков быстрее.

В том кейсе точно поставить диагноз - большой объект не помещался в кеш, что приводило к trashing’у страниц памяти, автору помог инструмент cachestat.

Если в кратце, то утилита подсчитывает соотношение попаданий в кеш файловой системы относительно общего числа операций на чтение - cache hit ratio:

# cachestat
    HITS   MISSES  DIRTIES HITRATIO   BUFFERS_MB  CACHED_MB
    1132        0        4  100.00%          277       4367
     161        0       36  100.00%          277       4372
      16        0       28  100.00%          277       4372
   17154    13750       15   55.51%          277       4422
      19        0        1  100.00%          277       4422

На всякий случай напомню, что каждый промах чреват мажорным Page Fault, что приводит чтению с диска, а это явная деградация производительности, которую хочется избегать.

На мой взгляд значение HITRATIO может явным образом подсвечивать голодание системы по памяти, а значит полезно иметь его в виде метрики для постоянного отслеживания.

конкретный порог после которого надо бить тревогу подбирается опытным путем, но кмк все что ниже 85-90% стоит внимания.

К счастью в мой любимый ebpf_exporter cachestat уже был портирован.

Но вот незадача - текущая версия разрабатывалась под ядра 5.16 и выше, а в них немного поломали API (1, 2), таким образом ядра < 5.16 остались за бортом, а например дефолтное ядро для debian 11 - 5.10.

Я добавил PR, решающий эту проблему - для версий ядер < 5.16 можно использовать cachestat-pre-kernel-5.16, а для более свежих cachestat плагин.

Так же попутно заменил трасировку kprobe в пользу более быстрых kfunc/tracepoint(см.benchmark), что безусловно хорошо для промышленной эксплуатации.

PromQL запрос:

(1 - (
    sum by (instance) (ebpf_exporter_page_cache_ops_total{op="page_add_lru"}) 
    - 
    sum by (instance) (ebpf_exporter_page_cache_ops_total{op="page_mark_dirties"})
)
/
(
    sum by (instance) (ebpf_exporter_page_cache_ops_total{op="cache_access"}) 
      - 
    sum by (instance) (ebpf_exporter_page_cache_ops_total{op="cache_writes"})
))

даст подобный незамысловатый график:

grafana

Удачи!