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