1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License in the LICENSE file at the
6// root of this repository, or online at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
1516//! Utilities for getting jemalloc statistics, as well as exporting them as metrics.
1718use std::time::Duration;
1920use jemalloc_pprof::JemallocProfCtl;
21use mz_ore::cast::CastFrom;
22use mz_ore::metric;
23use mz_ore::metrics::{MetricsRegistry, UIntGauge};
24use pprof_util::ProfStartTime;
25use tikv_jemalloc_ctl::{epoch, stats};
2627#[allow(non_upper_case_globals)]
28#[unsafe(export_name = "malloc_conf")]
29pub static malloc_conf: &[u8] = b"prof:true,prof_active:true,lg_prof_sample:19\0";
3031#[derive(Copy, Clone, Debug)]
32pub struct JemallocProfMetadata {
33pub start_time: Option<ProfStartTime>,
34}
3536// See stats.{allocated, active, ...} in http://jemalloc.net/jemalloc.3.html for details
37pub struct JemallocStats {
38pub active: usize,
39pub allocated: usize,
40pub metadata: usize,
41pub resident: usize,
42pub retained: usize,
43}
4445pub trait JemallocProfCtlExt {
46fn dump_stats(&mut self, json_format: bool) -> anyhow::Result<String>;
47fn stats(&self) -> anyhow::Result<JemallocStats>;
48}
4950impl JemallocProfCtlExt for JemallocProfCtl {
51fn dump_stats(&mut self, json_format: bool) -> anyhow::Result<String> {
52// Try to avoid allocations within `stats_print`
53let mut buf = Vec::with_capacity(1 << 22);
54let mut options = tikv_jemalloc_ctl::stats_print::Options::default();
55 options.json_format = json_format;
56 tikv_jemalloc_ctl::stats_print::stats_print(&mut buf, options)?;
57Ok(String::from_utf8(buf)?)
58 }
5960fn stats(&self) -> anyhow::Result<JemallocStats> {
61 JemallocStats::get()
62 }
63}
6465impl JemallocStats {
66pub fn get() -> anyhow::Result<JemallocStats> {
67 epoch::advance()?;
68Ok(JemallocStats {
69 active: stats::active::read()?,
70 allocated: stats::allocated::read()?,
71 metadata: stats::metadata::read()?,
72 resident: stats::resident::read()?,
73 retained: stats::retained::read()?,
74 })
75 }
76}
7778/// Metrics for jemalloc.
79pub struct JemallocMetrics {
80pub active: UIntGauge,
81pub allocated: UIntGauge,
82pub metadata: UIntGauge,
83pub resident: UIntGauge,
84pub retained: UIntGauge,
85}
8687impl JemallocMetrics {
88/// Registers the metrics into the provided metrics registry, and spawns
89 /// a task to keep the metrics up to date.
90// `async` indicates that the Tokio runtime context is required.
91#[allow(clippy::unused_async)]
92pub async fn register_into(registry: &MetricsRegistry) {
93let m = JemallocMetrics {
94 active: registry.register(metric!(
95 name: "jemalloc_active",
96 help: "Total number of bytes in active pages allocated by the application",
97 )),
98 allocated: registry.register(metric!(
99 name: "jemalloc_allocated",
100 help: "Total number of bytes allocated by the application",
101 )),
102 metadata: registry.register(metric!(
103 name: "jemalloc_metadata",
104 help: "Total number of bytes dedicated to metadata.",
105 )),
106 resident: registry.register(metric!(
107 name: "jemalloc_resident",
108 help: "Maximum number of bytes in physically resident data pages mapped",
109 )),
110 retained: registry.register(metric!(
111 name: "jemalloc_retained",
112 help: "Total number of bytes in virtual memory mappings",
113 )),
114 };
115116 mz_ore::task::spawn(|| "jemalloc_stats_update", async move {
117let mut interval = tokio::time::interval(Duration::from_secs(10));
118 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
119loop {
120 interval.tick().await;
121if let Err(e) = m.update() {
122tracing::warn!("Error while updating jemalloc stats: {}", e);
123 }
124 }
125 });
126 }
127128fn update(&self) -> anyhow::Result<()> {
129let s = JemallocStats::get()?;
130self.active.set(u64::cast_from(s.active));
131self.allocated.set(u64::cast_from(s.allocated));
132self.metadata.set(u64::cast_from(s.metadata));
133self.resident.set(u64::cast_from(s.resident));
134self.retained.set(u64::cast_from(s.retained));
135Ok(())
136 }
137}