Skip to content

Commit

Permalink
Introduce cache_maps flag to Normalizer
Browse files Browse the repository at this point in the history
Reading and parsing the /proc/<pid>/maps file is not free in terms of
performance. Doing the same work again and again for the same process
may not be an acceptable price to pay in all contexts.
This adds the 'cache_maps' flag to the Normalizer type, which, in the
future, will determine whether the /proc/<pid>/maps contents are read
for every normalization operation or only once, with the result being
cached. The downside to caching the result is that we may end up unable
to normalize an address for a mapping added after /proc/<pid>/maps was
cached, because there does not appear to be any API that would allow us
to determine up-to-dateness of this file. As such, we do not cache this
file by default.

Signed-off-by: Daniel Müller <deso@posteo.net>
  • Loading branch information
d-e-s-o authored and danielocfb committed Feb 29, 2024
1 parent 68aa89b commit 6b52131
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Unreleased
----------
- Added `cache_maps` property to `normalize::Normalizer` type
- Reduced number of allocations performed on address normalization and
process symbolization paths

Expand Down
50 changes: 45 additions & 5 deletions src/normalize/normalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,29 @@ pub struct Output<M> {

/// A builder for configurable construction of [`Normalizer`] objects.
///
/// By default all features are enabled.
/// By default reading of build IDs is enabled, while caching of
/// `/proc/<pid>/maps` entries is disabled.
#[derive(Clone, Debug)]
pub struct Builder {
/// Whether or not to cache `/proc/<pid>/maps` contents.
///
/// Setting this flag to `true` is not generally recommended, because it
/// could result in addresses corresponding to mappings added after caching
/// may not be normalized successfully, as there is no reasonable way of
/// detecting staleness.
cache_maps: bool,
/// Whether to read and report build IDs as part of the normalization
/// process.
build_ids: bool,
}

impl Builder {
/// Enable/disable the caching of `/proc/<pid>/maps` entries.
pub fn enable_maps_caching(mut self, enable: bool) -> Builder {
self.cache_maps = enable;
self
}

/// Enable/disable the reading of build IDs.
pub fn enable_build_ids(mut self, enable: bool) -> Builder {
self.build_ids = enable;
Expand All @@ -55,15 +69,24 @@ impl Builder {

/// Create the [`Normalizer`] object.
pub fn build(self) -> Normalizer {
let Builder { build_ids } = self;

Normalizer { build_ids }
let Builder {
cache_maps,
build_ids,
} = self;

Normalizer {
cache_maps,
build_ids,
}
}
}

impl Default for Builder {
fn default() -> Self {
Self { build_ids: true }
Self {
cache_maps: true,
build_ids: true,
}
}
}

Expand All @@ -76,21 +99,38 @@ impl Default for Builder {
/// things) and converting them to "normalized" virtual addresses as
/// they are present in, say, an ELF binary or a DWARF debug info file,
/// and one would be able to see them using tools such as readelf(1).
///
/// If caching of `/proc/<pid>/maps` is enabled, an instance of this type is the
/// unit at which caching happens. If you are normalizing address in a large
/// number of processes over time, you may want to consider creating a new
/// `Normalizer` instance regularly to free up cached data.
#[derive(Debug, Default)]
pub struct Normalizer {
/// Whether or not to cache `/proc/<pid>/maps` contents.
///
/// Setting this flag to `true` is not generally recommended, because it
/// could result in addresses corresponding to mappings added after caching
/// may not be normalized successfully, as there is no reasonable way of
/// detecting staleness.
cache_maps: bool,
/// Flag indicating whether or not to read build IDs as part of the
/// normalization process.
build_ids: bool,
}

impl Normalizer {
/// Create a new [`Normalizer`].
///
/// This method is just a short hand for instantiating a `Normalizer` from
/// the default [`Builder`].
#[inline]
pub fn new() -> Self {
Builder::default().build()
}

/// Retrieve a [`Builder`] object for configurable construction of a
/// [`Normalizer`].
#[inline]
pub fn builder() -> Builder {
Builder::default()
}
Expand Down

0 comments on commit 6b52131

Please sign in to comment.