rocksdb/
perf.rs

1// Copyright 2020 Tran Tuan Linh
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 at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use libc::{c_int, c_uchar, c_void};
16
17use crate::{db::DBInner, ffi, ffi_util::from_cstr, Cache, Error, DB};
18
19#[derive(Debug, Copy, Clone, PartialEq, Eq)]
20#[repr(i32)]
21pub enum PerfStatsLevel {
22    /// Unknown settings
23    Uninitialized = 0,
24    /// Disable perf stats
25    Disable,
26    /// Enables only count stats
27    EnableCount,
28    /// Count stats and enable time stats except for mutexes
29    EnableTimeExceptForMutex,
30    /// Other than time, also measure CPU time counters. Still don't measure
31    /// time (neither wall time nor CPU time) for mutexes
32    EnableTimeAndCPUTimeExceptForMutex,
33    /// Enables count and time stats
34    EnableTime,
35    /// N.B must always be the last value!
36    OutOfBound,
37}
38
39#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
40#[non_exhaustive]
41#[repr(i32)]
42pub enum PerfMetric {
43    UserKeyComparisonCount = 0,
44    BlockCacheHitCount = 1,
45    BlockReadCount = 2,
46    BlockReadByte = 3,
47    BlockReadTime = 4,
48    BlockChecksumTime = 5,
49    BlockDecompressTime = 6,
50    GetReadBytes = 7,
51    MultigetReadBytes = 8,
52    IterReadBytes = 9,
53    InternalKeySkippedCount = 10,
54    InternalDeleteSkippedCount = 11,
55    InternalRecentSkippedCount = 12,
56    InternalMergeCount = 13,
57    GetSnapshotTime = 14,
58    GetFromMemtableTime = 15,
59    GetFromMemtableCount = 16,
60    GetPostProcessTime = 17,
61    GetFromOutputFilesTime = 18,
62    SeekOnMemtableTime = 19,
63    SeekOnMemtableCount = 20,
64    NextOnMemtableCount = 21,
65    PrevOnMemtableCount = 22,
66    SeekChildSeekTime = 23,
67    SeekChildSeekCount = 24,
68    SeekMinHeapTime = 25,
69    SeekMaxHeapTime = 26,
70    SeekInternalSeekTime = 27,
71    FindNextUserEntryTime = 28,
72    WriteWalTime = 29,
73    WriteMemtableTime = 30,
74    WriteDelayTime = 31,
75    WritePreAndPostProcessTime = 32,
76    DbMutexLockNanos = 33,
77    DbConditionWaitNanos = 34,
78    MergeOperatorTimeNanos = 35,
79    ReadIndexBlockNanos = 36,
80    ReadFilterBlockNanos = 37,
81    NewTableBlockIterNanos = 38,
82    NewTableIteratorNanos = 39,
83    BlockSeekNanos = 40,
84    FindTableNanos = 41,
85    BloomMemtableHitCount = 42,
86    BloomMemtableMissCount = 43,
87    BloomSstHitCount = 44,
88    BloomSstMissCount = 45,
89    KeyLockWaitTime = 46,
90    KeyLockWaitCount = 47,
91    EnvNewSequentialFileNanos = 48,
92    EnvNewRandomAccessFileNanos = 49,
93    EnvNewWritableFileNanos = 50,
94    EnvReuseWritableFileNanos = 51,
95    EnvNewRandomRwFileNanos = 52,
96    EnvNewDirectoryNanos = 53,
97    EnvFileExistsNanos = 54,
98    EnvGetChildrenNanos = 55,
99    EnvGetChildrenFileAttributesNanos = 56,
100    EnvDeleteFileNanos = 57,
101    EnvCreateDirNanos = 58,
102    EnvCreateDirIfMissingNanos = 59,
103    EnvDeleteDirNanos = 60,
104    EnvGetFileSizeNanos = 61,
105    EnvGetFileModificationTimeNanos = 62,
106    EnvRenameFileNanos = 63,
107    EnvLinkFileNanos = 64,
108    EnvLockFileNanos = 65,
109    EnvUnlockFileNanos = 66,
110    EnvNewLoggerNanos = 67,
111    TotalMetricCount = 68,
112}
113
114/// Sets the perf stats level for current thread.
115pub fn set_perf_stats(lvl: PerfStatsLevel) {
116    unsafe {
117        ffi::rocksdb_set_perf_level(lvl as c_int);
118    }
119}
120
121/// Thread local context for gathering performance counter efficiently
122/// and transparently.
123pub struct PerfContext {
124    pub(crate) inner: *mut ffi::rocksdb_perfcontext_t,
125}
126
127impl Default for PerfContext {
128    fn default() -> Self {
129        let ctx = unsafe { ffi::rocksdb_perfcontext_create() };
130        assert!(!ctx.is_null(), "Could not create Perf Context");
131
132        Self { inner: ctx }
133    }
134}
135
136impl Drop for PerfContext {
137    fn drop(&mut self) {
138        unsafe {
139            ffi::rocksdb_perfcontext_destroy(self.inner);
140        }
141    }
142}
143
144impl PerfContext {
145    /// Reset context
146    pub fn reset(&mut self) {
147        unsafe {
148            ffi::rocksdb_perfcontext_reset(self.inner);
149        }
150    }
151
152    /// Get the report on perf
153    pub fn report(&self, exclude_zero_counters: bool) -> String {
154        unsafe {
155            let ptr =
156                ffi::rocksdb_perfcontext_report(self.inner, c_uchar::from(exclude_zero_counters));
157            let report = from_cstr(ptr);
158            libc::free(ptr as *mut c_void);
159            report
160        }
161    }
162
163    /// Returns value of a metric
164    pub fn metric(&self, id: PerfMetric) -> u64 {
165        unsafe { ffi::rocksdb_perfcontext_metric(self.inner, id as c_int) }
166    }
167}
168
169/// Memory usage stats
170pub struct MemoryUsageStats {
171    /// Approximate memory usage of all the mem-tables
172    pub mem_table_total: u64,
173    /// Approximate memory usage of un-flushed mem-tables
174    pub mem_table_unflushed: u64,
175    /// Approximate memory usage of all the table readers
176    pub mem_table_readers_total: u64,
177    /// Approximate memory usage by cache
178    pub cache_total: u64,
179}
180
181/// Wrap over memory_usage_t. Hold current memory usage of the specified DB instances and caches
182struct MemoryUsage {
183    inner: *mut ffi::rocksdb_memory_usage_t,
184}
185
186impl Drop for MemoryUsage {
187    fn drop(&mut self) {
188        unsafe {
189            ffi::rocksdb_approximate_memory_usage_destroy(self.inner);
190        }
191    }
192}
193
194impl MemoryUsage {
195    /// Approximate memory usage of all the mem-tables
196    fn approximate_mem_table_total(&self) -> u64 {
197        unsafe { ffi::rocksdb_approximate_memory_usage_get_mem_table_total(self.inner) }
198    }
199
200    /// Approximate memory usage of un-flushed mem-tables
201    fn approximate_mem_table_unflushed(&self) -> u64 {
202        unsafe { ffi::rocksdb_approximate_memory_usage_get_mem_table_unflushed(self.inner) }
203    }
204
205    /// Approximate memory usage of all the table readers
206    fn approximate_mem_table_readers_total(&self) -> u64 {
207        unsafe { ffi::rocksdb_approximate_memory_usage_get_mem_table_readers_total(self.inner) }
208    }
209
210    /// Approximate memory usage by cache
211    fn approximate_cache_total(&self) -> u64 {
212        unsafe { ffi::rocksdb_approximate_memory_usage_get_cache_total(self.inner) }
213    }
214}
215
216/// Builder for MemoryUsage
217struct MemoryUsageBuilder {
218    inner: *mut ffi::rocksdb_memory_consumers_t,
219}
220
221impl Drop for MemoryUsageBuilder {
222    fn drop(&mut self) {
223        unsafe {
224            ffi::rocksdb_memory_consumers_destroy(self.inner);
225        }
226    }
227}
228
229impl MemoryUsageBuilder {
230    /// Create new instance
231    fn new() -> Result<Self, Error> {
232        let mc = unsafe { ffi::rocksdb_memory_consumers_create() };
233        if mc.is_null() {
234            Err(Error::new(
235                "Could not create MemoryUsage builder".to_owned(),
236            ))
237        } else {
238            Ok(Self { inner: mc })
239        }
240    }
241
242    /// Add a DB instance to collect memory usage from it and add up in total stats
243    fn add_db(&mut self, db: &DB) {
244        unsafe {
245            ffi::rocksdb_memory_consumers_add_db(self.inner, db.inner.inner());
246        }
247    }
248
249    /// Add a cache to collect memory usage from it and add up in total stats
250    fn add_cache(&mut self, cache: &Cache) {
251        unsafe {
252            ffi::rocksdb_memory_consumers_add_cache(self.inner, cache.0.inner.as_ptr());
253        }
254    }
255
256    /// Build up MemoryUsage
257    fn build(&self) -> Result<MemoryUsage, Error> {
258        unsafe {
259            let mu = ffi_try!(ffi::rocksdb_approximate_memory_usage_create(self.inner));
260            Ok(MemoryUsage { inner: mu })
261        }
262    }
263}
264
265/// Get memory usage stats from DB instances and Cache instances
266pub fn get_memory_usage_stats(
267    dbs: Option<&[&DB]>,
268    caches: Option<&[&Cache]>,
269) -> Result<MemoryUsageStats, Error> {
270    let mut builder = MemoryUsageBuilder::new()?;
271    if let Some(dbs_) = dbs {
272        dbs_.iter().for_each(|db| builder.add_db(db));
273    }
274    if let Some(caches_) = caches {
275        caches_.iter().for_each(|cache| builder.add_cache(cache));
276    }
277
278    let mu = builder.build()?;
279    Ok(MemoryUsageStats {
280        mem_table_total: mu.approximate_mem_table_total(),
281        mem_table_unflushed: mu.approximate_mem_table_unflushed(),
282        mem_table_readers_total: mu.approximate_mem_table_readers_total(),
283        cache_total: mu.approximate_cache_total(),
284    })
285}