Skip to main content

gsym/
mapped.rs

1use std::fs::File;
2use std::path::Path;
3
4use memmap2::{Mmap, MmapOptions};
5
6use crate::{Error, Gsym, Result};
7
8/// Opaque read-only mapping used by [`MappedGsym`].
9///
10/// Obtain one through [`MappedGsym::into_inner`] when direct access to the
11/// mapped bytes is needed.
12pub struct MappedBytes(Mmap);
13
14impl std::fmt::Debug for MappedBytes {
15    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        formatter
17            .debug_struct("MappedBytes")
18            .field("len", &self.0.len())
19            .finish_non_exhaustive()
20    }
21}
22
23impl AsRef<[u8]> for MappedBytes {
24    fn as_ref(&self) -> &[u8] {
25        self.0.as_ref()
26    }
27}
28
29/// GSYM reader backed directly by a read-only memory map.
30///
31/// This is the same reader as [`Gsym`] with opaque mapped-byte storage, so it
32/// has the same query API. Mapping suits a large file with sparse lookups, where
33/// reading the whole file into memory would cost more than the lookups do.
34///
35/// Both constructors are `unsafe` because a memory map is not a snapshot. If
36/// any process truncates or rewrites the file while it is mapped, results
37/// borrowed from it can observe changed bytes or become invalid.
38/// [`Gsym::open`] reads an owned snapshot instead and carries no such
39/// requirement.
40///
41/// ```no_run
42/// use gsym::MappedGsym;
43///
44/// // SAFETY: this process controls the file and keeps it immutable while mapped.
45/// let gsym = unsafe { MappedGsym::map("app.gsym")? };
46/// if let Some(symbol) = gsym.lookup(0x401000)? {
47///     println!("{}", String::from_utf8_lossy(symbol.frames()[0].name));
48/// }
49/// # Ok::<(), gsym::Error>(())
50/// ```
51///
52/// A mapped reader and an owned one parse the same file identically:
53///
54/// ```no_run
55/// use gsym::{Gsym, MappedGsym};
56///
57/// let snapshot = Gsym::open("app.gsym")?;
58///
59/// // SAFETY: this application owns the file and keeps it immutable while mapped.
60/// let mapped = unsafe { MappedGsym::map("app.gsym")? };
61/// let file = std::fs::File::open("app.gsym")?;
62/// // SAFETY: the same file-stability guarantee applies to this mapping.
63/// let mapped_file = unsafe { MappedGsym::map_file(&file)? };
64///
65/// assert_eq!(snapshot.header(), mapped.header());
66/// assert_eq!(mapped.header(), mapped_file.header());
67/// # Ok::<(), gsym::Error>(())
68/// ```
69pub type MappedGsym = Gsym<MappedBytes>;
70
71impl Gsym<MappedBytes> {
72    /// Opens and validates a GSYM file through a read-only memory map.
73    ///
74    /// # Safety
75    ///
76    /// The mapped file must not be modified or truncated by any process for
77    /// the lifetime of the returned mapping. Use [`Gsym::open`] to read owned
78    /// bytes when that cannot be guaranteed.
79    ///
80    /// # Errors
81    ///
82    /// Returns an I/O error when the file cannot be opened or mapped, or a
83    /// format error when its GSYM metadata is invalid.
84    pub unsafe fn map(path: impl AsRef<Path>) -> Result<Self> {
85        let path = path.as_ref();
86        let file = File::open(path).map_err(|source| Error::IoAtPath {
87            operation: "open GSYM file for mapping",
88            path: path.to_path_buf(),
89            source,
90        })?;
91        // SAFETY: the caller accepted the file-stability requirement.
92        let mapping =
93            unsafe { MmapOptions::new().map(&file) }.map_err(|source| Error::IoAtPath {
94                operation: "memory-map GSYM file",
95                path: path.to_path_buf(),
96                source,
97            })?;
98        Self::parse(MappedBytes(mapping))
99    }
100
101    /// Maps and validates an already-open file.
102    ///
103    /// Use this when the file was opened elsewhere, for instance through a
104    /// descriptor passed in or a handle kept for locking. The mapping does not
105    /// keep the `File` alive, so it may be closed once this returns.
106    ///
107    /// # Safety
108    ///
109    /// `file` must not be modified or truncated by any process for the
110    /// lifetime of the returned mapping.
111    ///
112    /// # Errors
113    ///
114    /// Returns an I/O error when mapping fails or a format error for invalid
115    /// GSYM metadata.
116    pub unsafe fn map_file(file: &File) -> Result<Self> {
117        // SAFETY: guaranteed by this function's caller.
118        let mapping = unsafe { MmapOptions::new().map(file)? };
119        Self::parse(MappedBytes(mapping))
120    }
121}