Skip to main content

one_collect/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT license.
3#![doc = include_str!("../README.md")]
4
5use std::hash::{Hash, Hasher};
6
7#[repr(C)]
8#[derive(Default, Eq, PartialEq, Copy, Clone)]
9pub struct Guid {
10    pub data1: u32,
11    pub data2: u16,
12    pub data3: u16,
13    pub data4: [u8; 8],
14}
15
16impl Hash for Guid {
17    fn hash<H: Hasher>(
18        &self,
19        state: &mut H) {
20        state.write_u32(self.data1);
21        state.write_u16(self.data2);
22        state.write_u16(self.data3);
23    }
24}
25
26impl Guid {
27    pub const fn from_u128(uuid: u128) -> Self {
28        Self {
29            data1: (uuid >> 96) as u32,
30            data2: (uuid >> 80 & 0xffff) as u16,
31            data3: (uuid >> 64 & 0xffff) as u16,
32            data4: (uuid as u64).to_be_bytes()
33        }
34    }
35
36    pub fn to_bytes(&self) -> [u8; 16] {
37        unsafe { std::mem::transmute(*self) }
38    }
39
40    /// Derive a GUID from a namespace and a name by hashing `namespace ++ name`
41    /// with SHA-1 and laying the digest into the GUID fields (RFC 4122 v5 style).
42    ///
43    /// Only the version nibble is set (to 5); the variant bits are deliberately
44    /// left untouched so the output stays byte-identical to the pre-existing
45    /// `helpers::dotnet::scripting::guid_from_provider` algorithm. This means the
46    /// result is *not* a strictly conformant v5 UUID.
47    ///
48    /// Callers choose the byte encoding of `name` (UTF-8 for ETW session names,
49    /// uppercase UTF-16BE for `EventSource` provider names).
50    pub fn v5_from_name(namespace: &[u8], name: &[u8]) -> Self {
51        use sha1::{Sha1, Digest};
52
53        let mut hasher = Sha1::new();
54        hasher.update(namespace);
55        hasher.update(name);
56        let result = hasher.finalize();
57
58        let a = u32::from_ne_bytes([result[0], result[1], result[2], result[3]]);
59        let b = u16::from_ne_bytes([result[4], result[5]]);
60        let mut c = u16::from_ne_bytes([result[6], result[7]]);
61
62        /* High 4 bits of octet 7 to 5, as per RFC 4122 */
63        c = (c & 0x0FFF) | 0x5000;
64
65        Self {
66            data1: a,
67            data2: b,
68            data3: c,
69            data4: [
70                result[8], result[9], result[10], result[11],
71                result[12], result[13], result[14], result[15],
72            ],
73        }
74    }
75}
76
77pub mod event;
78pub mod sharing;
79pub mod helpers;
80pub mod intern;
81pub mod os;
82
83mod ruwind;
84
85/// Public types from the stack unwinding implementation that appear in
86/// `one_collect`'s public API.
87pub mod unwind {
88    pub use crate::ruwind::{ModuleKey, UnwindType};
89    pub use crate::ruwind::elf::ElfLoadHeader;
90}
91
92#[cfg(any(doc, target_os = "linux"))]
93pub mod tracefs;
94#[cfg(any(doc, target_os = "linux"))]
95pub mod procfs;
96#[cfg(any(doc, target_os = "linux"))]
97pub mod perf_event;
98#[cfg(any(doc, target_os = "linux"))]
99pub mod openat;
100#[cfg(any(doc, target_os = "linux"))]
101pub mod user_events;
102
103#[cfg(any(doc, target_os = "windows"))]
104pub mod etw;
105
106#[cfg(feature = "scripting")]
107pub mod scripting;
108
109pub use sharing::{Writable, ReadOnly};
110
111pub mod pathbuf_ext;
112pub use pathbuf_ext::{PathBufInteger};
113
114pub type IOResult<T> = std::io::Result<T>;
115pub type IOError = std::io::Error;
116
117const fn page_size_to_mask(page_size: u64) -> u64 {
118    !((page_size - 1) as u64)
119}
120
121pub fn io_error(message: &str) -> IOError {
122    IOError::new(
123        std::io::ErrorKind::Other,
124        message)
125}
126
127#[cfg(test)]
128mod tests {
129    use super::Guid;
130
131    const NS: &[u8] = b"one_collect test namespace";
132
133    #[test]
134    fn v5_from_name_is_deterministic() {
135        assert_eq!(
136            Guid::v5_from_name(NS, b"record-trace").to_bytes(),
137            Guid::v5_from_name(NS, b"record-trace").to_bytes());
138    }
139
140    #[test]
141    fn v5_from_name_distinct_names_distinct_guids() {
142        assert_ne!(
143            Guid::v5_from_name(NS, b"session-a").to_bytes(),
144            Guid::v5_from_name(NS, b"session-b").to_bytes());
145    }
146
147    #[test]
148    fn v5_from_name_distinct_namespaces_distinct_guids() {
149        assert_ne!(
150            Guid::v5_from_name(b"namespace-a", b"same-name").to_bytes(),
151            Guid::v5_from_name(b"namespace-b", b"same-name").to_bytes());
152    }
153
154    #[test]
155    fn v5_from_name_sets_version_nibble_to_5() {
156        let guid = Guid::v5_from_name(NS, b"record-trace");
157        assert_eq!(guid.data3 & 0xF000, 0x5000);
158    }
159}
160