From 134984143183f5cd88a3238bb7bb0e6b6b7032a7 Mon Sep 17 00:00:00 2001
From: Christian Jordan <svorre2304@gmail.com>
Date: Sun, 24 Jul 2022 01:30:59 +0200
Subject: [PATCH 1/5] Separated platform specific implementation into separate
 files

This is needed for windows support.
Though the windows implementation is currently blank.

It compiles and runs on windows and linux, but I haven't tested macos.
---
 Cargo.toml                                    |  12 +-
 src/error.rs                                  |   9 +-
 src/lib.rs                                    |   5 +-
 src/platform/mod.rs                           |  19 ++
 .../platform_nix}/addr_validate.rs            |   0
 src/platform/platform_nix/profiler.rs         | 185 +++++++++++++++++
 src/{ => platform/platform_nix}/timer.rs      |   0
 .../platform_windows/addr_validate.rs         |   3 +
 src/platform/platform_windows/profiler.rs     |   8 +
 src/platform/platform_windows/timer.rs        |  55 +++++
 src/profiler.rs                               | 190 +-----------------
 src/report.rs                                 |   2 +-
 12 files changed, 298 insertions(+), 190 deletions(-)
 create mode 100644 src/platform/mod.rs
 rename src/{ => platform/platform_nix}/addr_validate.rs (100%)
 create mode 100644 src/platform/platform_nix/profiler.rs
 rename src/{ => platform/platform_nix}/timer.rs (100%)
 create mode 100644 src/platform/platform_windows/addr_validate.rs
 create mode 100644 src/platform/platform_windows/profiler.rs
 create mode 100644 src/platform/platform_windows/timer.rs

diff --git a/Cargo.toml b/Cargo.toml
index 1c113036..ab625380 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,7 +25,7 @@ backtrace = { version = "0.3" }
 once_cell = "1.9"
 libc = "^0.2.66"
 log = "0.4"
-nix = { version = "0.24", default-features = false, features = ["signal", "fs"] }
+
 parking_lot = "0.12"
 tempfile = "3.1"
 thiserror = "1.0"
@@ -37,7 +37,15 @@ inferno = { version = "0.11", default-features = false, features = ["nameattr"],
 prost = { version = "0.10", optional = true }
 prost-derive = { version = "0.10", optional = true }
 protobuf = { version = "2.0", optional = true }
-criterion = {version = "0.3", optional = true}
+criterion = { version = "0.3", optional = true }
+
+[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
+nix = { version = "0.24", default-features = false, features = [
+    "signal",
+    "fs",
+] }
+
+[target.'cfg(target_os = "windows")'.dependencies]
 
 [dependencies.symbolic-demangle]
 version = "9.0"
diff --git a/src/error.rs b/src/error.rs
index 6d5747ea..d487cf4d 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,9 +1,16 @@
 // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
 
+// TODO Windows error is not finished
 #[derive(Debug, thiserror::Error)]
 pub enum Error {
+    #[cfg(target_os = "windows")]
     #[error("{0}")]
-    NixError(#[from] nix::Error),
+    OsError(i32),
+
+    #[cfg(any(target_os = "linux", target_os = "macos"))]
+    #[error("{0}")]
+    OsError(#[from] nix::Error),
+
     #[error("{0}")]
     IoError(#[from] std::io::Error),
     #[error("create profiler error")]
diff --git a/src/lib.rs b/src/lib.rs
index ed87e566..560ee79f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -45,7 +45,7 @@ pub const MAX_DEPTH: usize = 32;
 /// Define the MAX supported thread name length. TODO: make this variable mutable.
 pub const MAX_THREAD_NAME: usize = 16;
 
-mod addr_validate;
+mod platform;
 
 mod backtrace;
 mod collector;
@@ -53,12 +53,11 @@ mod error;
 mod frames;
 mod profiler;
 mod report;
-mod timer;
 
-pub use self::addr_validate::validate;
 pub use self::collector::{Collector, HashCounter};
 pub use self::error::{Error, Result};
 pub use self::frames::{Frames, Symbol};
+pub use self::platform::addr_validate::validate;
 pub use self::profiler::{ProfilerGuard, ProfilerGuardBuilder};
 pub use self::report::{Report, ReportBuilder, UnresolvedReport};
 
diff --git a/src/platform/mod.rs b/src/platform/mod.rs
new file mode 100644
index 00000000..10cacd48
--- /dev/null
+++ b/src/platform/mod.rs
@@ -0,0 +1,19 @@
+#[cfg(any(target_os = "linux", target_os = "macos"))]
+mod platform_nix {
+    pub mod addr_validate;
+    pub mod profiler;
+    pub mod timer;
+}
+
+#[cfg(target_os = "windows")]
+mod platform_windows {
+    pub mod addr_validate;
+    pub mod profiler;
+    pub mod timer;
+}
+
+#[cfg(any(target_os = "linux", target_os = "macos"))]
+pub use platform_nix::*;
+
+#[cfg(target_os = "windows")]
+pub use platform_windows::*;
diff --git a/src/addr_validate.rs b/src/platform/platform_nix/addr_validate.rs
similarity index 100%
rename from src/addr_validate.rs
rename to src/platform/platform_nix/addr_validate.rs
diff --git a/src/platform/platform_nix/profiler.rs b/src/platform/platform_nix/profiler.rs
new file mode 100644
index 00000000..9561193a
--- /dev/null
+++ b/src/platform/platform_nix/profiler.rs
@@ -0,0 +1,185 @@
+use std::os::raw::c_int;
+use std::time::SystemTime;
+
+use crate::backtrace::{Frame, Trace, TraceImpl};
+use smallvec::SmallVec;
+
+use nix::sys::signal;
+
+use crate::error::{Error, Result};
+use crate::profiler::PROFILER;
+use crate::{MAX_DEPTH, MAX_THREAD_NAME};
+
+pub fn register() -> Result<()> {
+    let handler = signal::SigHandler::SigAction(perf_signal_handler);
+    let sigaction = signal::SigAction::new(
+        handler,
+        signal::SaFlags::SA_SIGINFO,
+        signal::SigSet::empty(),
+    );
+    unsafe { signal::sigaction(signal::SIGPROF, &sigaction) }?;
+
+    Ok(())
+}
+pub fn unregister() -> Result<()> {
+    let handler = signal::SigHandler::SigIgn;
+    unsafe { signal::signal(signal::SIGPROF, handler) }?;
+
+    Ok(())
+}
+
+fn write_thread_name_fallback(current_thread: libc::pthread_t, name: &mut [libc::c_char]) {
+    let mut len = 0;
+    let mut base = 1;
+
+    while current_thread as u128 > base && len < MAX_THREAD_NAME {
+        base *= 10;
+        len += 1;
+    }
+
+    let mut index = 0;
+    while index < len && base > 1 {
+        base /= 10;
+
+        name[index] = match (48 + (current_thread as u128 / base) % 10).try_into() {
+            Ok(digit) => digit,
+            Err(_) => {
+                log::error!("fail to convert thread_id to string");
+                0
+            }
+        };
+
+        index += 1;
+    }
+}
+
+#[cfg(not(all(any(target_os = "linux", target_os = "macos"), target_env = "gnu")))]
+fn write_thread_name(current_thread: libc::pthread_t, name: &mut [libc::c_char]) {
+    write_thread_name_fallback(current_thread, name);
+}
+
+#[cfg(all(any(target_os = "linux", target_os = "macos"), target_env = "gnu"))]
+fn write_thread_name(current_thread: libc::pthread_t, name: &mut [libc::c_char]) {
+    let name_ptr = name as *mut [libc::c_char] as *mut libc::c_char;
+    let ret = unsafe { libc::pthread_getname_np(current_thread, name_ptr, MAX_THREAD_NAME) };
+
+    if ret != 0 {
+        write_thread_name_fallback(current_thread, name);
+    }
+}
+
+struct ErrnoProtector(libc::c_int);
+
+impl ErrnoProtector {
+    fn new() -> Self {
+        unsafe {
+            #[cfg(target_os = "linux")]
+            {
+                let errno = *libc::__errno_location();
+                Self(errno)
+            }
+            #[cfg(target_os = "macos")]
+            {
+                let errno = *libc::__error();
+                Self(errno)
+            }
+        }
+    }
+}
+
+impl Drop for ErrnoProtector {
+    fn drop(&mut self) {
+        unsafe {
+            #[cfg(target_os = "linux")]
+            {
+                *libc::__errno_location() = self.0;
+            }
+            #[cfg(target_os = "macos")]
+            {
+                *libc::__error() = self.0;
+            }
+        }
+    }
+}
+
+#[no_mangle]
+#[cfg_attr(
+    not(all(any(target_arch = "x86_64", target_arch = "aarch64"))),
+    allow(unused_variables)
+)]
+extern "C" fn perf_signal_handler(
+    _signal: c_int,
+    _siginfo: *mut libc::siginfo_t,
+    ucontext: *mut libc::c_void,
+) {
+    let _errno = ErrnoProtector::new();
+
+    if let Some(mut guard) = PROFILER.try_write() {
+        if let Ok(profiler) = guard.as_mut() {
+            #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
+            if !ucontext.is_null() {
+                let ucontext: *mut libc::ucontext_t = ucontext as *mut libc::ucontext_t;
+
+                #[cfg(all(target_arch = "x86_64", target_os = "linux"))]
+                let addr =
+                    unsafe { (*ucontext).uc_mcontext.gregs[libc::REG_RIP as usize] as usize };
+
+                #[cfg(all(target_arch = "x86_64", target_os = "macos"))]
+                let addr = unsafe {
+                    let mcontext = (*ucontext).uc_mcontext;
+                    if mcontext.is_null() {
+                        0
+                    } else {
+                        (*mcontext).__ss.__rip as usize
+                    }
+                };
+
+                #[cfg(all(target_arch = "aarch64", target_os = "linux"))]
+                let addr = unsafe { (*ucontext).uc_mcontext.pc as usize };
+
+                #[cfg(all(target_arch = "aarch64", target_os = "macos"))]
+                let addr = unsafe {
+                    let mcontext = (*ucontext).uc_mcontext;
+                    if mcontext.is_null() {
+                        0
+                    } else {
+                        (*mcontext).__ss.__pc as usize
+                    }
+                };
+
+                if profiler.is_blocklisted(addr) {
+                    return;
+                }
+            }
+
+            let mut bt: SmallVec<[<TraceImpl as Trace>::Frame; MAX_DEPTH]> =
+                SmallVec::with_capacity(MAX_DEPTH);
+            let mut index = 0;
+
+            let sample_timestamp: SystemTime = SystemTime::now();
+            TraceImpl::trace(ucontext, |frame| {
+                let ip = Frame::ip(frame);
+                if profiler.is_blocklisted(ip) {
+                    return false;
+                }
+
+                if index < MAX_DEPTH {
+                    bt.push(frame.clone());
+                    index += 1;
+                    true
+                } else {
+                    false
+                }
+            });
+
+            let current_thread = unsafe { libc::pthread_self() };
+            let mut name = [0; MAX_THREAD_NAME];
+            let name_ptr = &mut name as *mut [libc::c_char] as *mut libc::c_char;
+
+            write_thread_name(current_thread, &mut name);
+
+            let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) };
+            profiler.sample(bt, name.to_bytes(), current_thread as u64, sample_timestamp);
+        }
+    }
+}
diff --git a/src/timer.rs b/src/platform/platform_nix/timer.rs
similarity index 100%
rename from src/timer.rs
rename to src/platform/platform_nix/timer.rs
diff --git a/src/platform/platform_windows/addr_validate.rs b/src/platform/platform_windows/addr_validate.rs
new file mode 100644
index 00000000..20954778
--- /dev/null
+++ b/src/platform/platform_windows/addr_validate.rs
@@ -0,0 +1,3 @@
+pub fn validate() -> bool {
+    unimplemented!()
+}
diff --git a/src/platform/platform_windows/profiler.rs b/src/platform/platform_windows/profiler.rs
new file mode 100644
index 00000000..4f65cc89
--- /dev/null
+++ b/src/platform/platform_windows/profiler.rs
@@ -0,0 +1,8 @@
+use crate::error::Result;
+
+pub fn register() -> Result<()> {
+    Ok(())
+}
+pub fn unregister() -> Result<()> {
+    Ok(())
+}
diff --git a/src/platform/platform_windows/timer.rs b/src/platform/platform_windows/timer.rs
new file mode 100644
index 00000000..88335c62
--- /dev/null
+++ b/src/platform/platform_windows/timer.rs
@@ -0,0 +1,55 @@
+// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
+
+use std::os::raw::c_int;
+use std::time::{Duration, Instant, SystemTime};
+
+pub struct Timer {
+    pub frequency: c_int,
+    pub start_time: SystemTime,
+    pub start_instant: Instant,
+}
+
+impl Timer {
+    pub fn new(frequency: c_int) -> Timer {
+        Timer {
+            frequency,
+            start_time: SystemTime::now(),
+            start_instant: Instant::now(),
+        }
+    }
+
+    /// Returns a `ReportTiming` struct having this timer's frequency and start
+    /// time; and the time elapsed since its creation as duration.
+    pub fn timing(&self) -> ReportTiming {
+        ReportTiming {
+            frequency: self.frequency,
+            start_time: self.start_time,
+            duration: self.start_instant.elapsed(),
+        }
+    }
+}
+
+impl Drop for Timer {
+    fn drop(&mut self) {}
+}
+
+/// Timing metadata for a collected report.
+#[derive(Clone)]
+pub struct ReportTiming {
+    /// Frequency at which samples were collected.
+    pub frequency: i32,
+    /// Collection start time.
+    pub start_time: SystemTime,
+    /// Collection duration.
+    pub duration: Duration,
+}
+
+impl Default for ReportTiming {
+    fn default() -> Self {
+        Self {
+            frequency: 1,
+            start_time: SystemTime::UNIX_EPOCH,
+            duration: Default::default(),
+        }
+    }
+}
diff --git a/src/profiler.rs b/src/profiler.rs
index 4bc72ad2..1023fcd3 100644
--- a/src/profiler.rs
+++ b/src/profiler.rs
@@ -1,10 +1,8 @@
 // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
 
-use std::convert::TryInto;
 use std::os::raw::c_int;
 use std::time::SystemTime;
 
-use nix::sys::signal;
 use once_cell::sync::Lazy;
 use parking_lot::RwLock;
 use smallvec::SmallVec;
@@ -12,13 +10,14 @@ use smallvec::SmallVec;
 #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
 use findshlibs::{Segment, SharedLibrary, TargetSharedLibrary};
 
-use crate::backtrace::{Frame, Trace, TraceImpl};
+use crate::backtrace::{Trace, TraceImpl};
 use crate::collector::Collector;
 use crate::error::{Error, Result};
 use crate::frames::UnresolvedFrames;
+use crate::platform;
+use crate::platform::timer::Timer;
 use crate::report::ReportBuilder;
-use crate::timer::Timer;
-use crate::{MAX_DEPTH, MAX_THREAD_NAME};
+use crate::MAX_DEPTH;
 
 pub(crate) static PROFILER: Lazy<RwLock<Result<Profiler>>> =
     Lazy::new(|| RwLock::new(Profiler::new()));
@@ -158,162 +157,6 @@ impl<'a> Drop for ProfilerGuard<'a> {
     }
 }
 
-fn write_thread_name_fallback(current_thread: libc::pthread_t, name: &mut [libc::c_char]) {
-    let mut len = 0;
-    let mut base = 1;
-
-    while current_thread as u128 > base && len < MAX_THREAD_NAME {
-        base *= 10;
-        len += 1;
-    }
-
-    let mut index = 0;
-    while index < len && base > 1 {
-        base /= 10;
-
-        name[index] = match (48 + (current_thread as u128 / base) % 10).try_into() {
-            Ok(digit) => digit,
-            Err(_) => {
-                log::error!("fail to convert thread_id to string");
-                0
-            }
-        };
-
-        index += 1;
-    }
-}
-
-#[cfg(not(all(any(target_os = "linux", target_os = "macos"), target_env = "gnu")))]
-fn write_thread_name(current_thread: libc::pthread_t, name: &mut [libc::c_char]) {
-    write_thread_name_fallback(current_thread, name);
-}
-
-#[cfg(all(any(target_os = "linux", target_os = "macos"), target_env = "gnu"))]
-fn write_thread_name(current_thread: libc::pthread_t, name: &mut [libc::c_char]) {
-    let name_ptr = name as *mut [libc::c_char] as *mut libc::c_char;
-    let ret = unsafe { libc::pthread_getname_np(current_thread, name_ptr, MAX_THREAD_NAME) };
-
-    if ret != 0 {
-        write_thread_name_fallback(current_thread, name);
-    }
-}
-
-struct ErrnoProtector(libc::c_int);
-
-impl ErrnoProtector {
-    fn new() -> Self {
-        unsafe {
-            #[cfg(target_os = "linux")]
-            {
-                let errno = *libc::__errno_location();
-                Self(errno)
-            }
-            #[cfg(target_os = "macos")]
-            {
-                let errno = *libc::__error();
-                Self(errno)
-            }
-        }
-    }
-}
-
-impl Drop for ErrnoProtector {
-    fn drop(&mut self) {
-        unsafe {
-            #[cfg(target_os = "linux")]
-            {
-                *libc::__errno_location() = self.0;
-            }
-            #[cfg(target_os = "macos")]
-            {
-                *libc::__error() = self.0;
-            }
-        }
-    }
-}
-
-#[no_mangle]
-#[cfg_attr(
-    not(all(any(target_arch = "x86_64", target_arch = "aarch64"))),
-    allow(unused_variables)
-)]
-extern "C" fn perf_signal_handler(
-    _signal: c_int,
-    _siginfo: *mut libc::siginfo_t,
-    ucontext: *mut libc::c_void,
-) {
-    let _errno = ErrnoProtector::new();
-
-    if let Some(mut guard) = PROFILER.try_write() {
-        if let Ok(profiler) = guard.as_mut() {
-            #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
-            if !ucontext.is_null() {
-                let ucontext: *mut libc::ucontext_t = ucontext as *mut libc::ucontext_t;
-
-                #[cfg(all(target_arch = "x86_64", target_os = "linux"))]
-                let addr =
-                    unsafe { (*ucontext).uc_mcontext.gregs[libc::REG_RIP as usize] as usize };
-
-                #[cfg(all(target_arch = "x86_64", target_os = "macos"))]
-                let addr = unsafe {
-                    let mcontext = (*ucontext).uc_mcontext;
-                    if mcontext.is_null() {
-                        0
-                    } else {
-                        (*mcontext).__ss.__rip as usize
-                    }
-                };
-
-                #[cfg(all(target_arch = "aarch64", target_os = "linux"))]
-                let addr = unsafe { (*ucontext).uc_mcontext.pc as usize };
-
-                #[cfg(all(target_arch = "aarch64", target_os = "macos"))]
-                let addr = unsafe {
-                    let mcontext = (*ucontext).uc_mcontext;
-                    if mcontext.is_null() {
-                        0
-                    } else {
-                        (*mcontext).__ss.__pc as usize
-                    }
-                };
-
-                if profiler.is_blocklisted(addr) {
-                    return;
-                }
-            }
-
-            let mut bt: SmallVec<[<TraceImpl as Trace>::Frame; MAX_DEPTH]> =
-                SmallVec::with_capacity(MAX_DEPTH);
-            let mut index = 0;
-
-            let sample_timestamp: SystemTime = SystemTime::now();
-            TraceImpl::trace(ucontext, |frame| {
-                let ip = Frame::ip(frame);
-                if profiler.is_blocklisted(ip) {
-                    return false;
-                }
-
-                if index < MAX_DEPTH {
-                    bt.push(frame.clone());
-                    index += 1;
-                    true
-                } else {
-                    false
-                }
-            });
-
-            let current_thread = unsafe { libc::pthread_self() };
-            let mut name = [0; MAX_THREAD_NAME];
-            let name_ptr = &mut name as *mut [libc::c_char] as *mut libc::c_char;
-
-            write_thread_name(current_thread, &mut name);
-
-            let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) };
-            profiler.sample(bt, name.to_bytes(), current_thread as u64, sample_timestamp);
-        }
-    }
-}
-
 impl Profiler {
     fn new() -> Result<Self> {
         Ok(Profiler {
@@ -327,7 +170,7 @@ impl Profiler {
     }
 
     #[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64")))]
-    fn is_blocklisted(&self, addr: usize) -> bool {
+    pub(crate) fn is_blocklisted(&self, addr: usize) -> bool {
         for libs in &self.blocklist_segments {
             if addr > libs.0 && addr < libs.1 {
                 return true;
@@ -343,7 +186,7 @@ impl Profiler {
         if self.running {
             Err(Error::Running)
         } else {
-            self.register_signal_handler()?;
+            platform::profiler::register()?;
             self.running = true;
 
             Ok(())
@@ -361,7 +204,7 @@ impl Profiler {
     pub fn stop(&mut self) -> Result<()> {
         log::info!("stopping cpu profiler");
         if self.running {
-            self.unregister_signal_handler()?;
+            platform::profiler::unregister()?;
             self.init()?;
 
             Ok(())
@@ -370,25 +213,6 @@ impl Profiler {
         }
     }
 
-    fn register_signal_handler(&self) -> Result<()> {
-        let handler = signal::SigHandler::SigAction(perf_signal_handler);
-        let sigaction = signal::SigAction::new(
-            handler,
-            signal::SaFlags::SA_SIGINFO,
-            signal::SigSet::empty(),
-        );
-        unsafe { signal::sigaction(signal::SIGPROF, &sigaction) }?;
-
-        Ok(())
-    }
-
-    fn unregister_signal_handler(&self) -> Result<()> {
-        let handler = signal::SigHandler::SigIgn;
-        unsafe { signal::signal(signal::SIGPROF, handler) }?;
-
-        Ok(())
-    }
-
     // This function has to be AS-safe
     pub fn sample(
         &mut self,
diff --git a/src/report.rs b/src/report.rs
index a19b940e..a3648a88 100644
--- a/src/report.rs
+++ b/src/report.rs
@@ -6,8 +6,8 @@ use std::fmt::{Debug, Formatter};
 use parking_lot::RwLock;
 
 use crate::frames::{Frames, UnresolvedFrames};
+use crate::platform::timer::ReportTiming;
 use crate::profiler::Profiler;
-use crate::timer::ReportTiming;
 
 use crate::{Error, Result};
 

From 017a1164d632ac2309751b079a347072f153f6d8 Mon Sep 17 00:00:00 2001
From: Christian Jordan <svorre2304@gmail.com>
Date: Mon, 25 Jul 2022 00:01:44 +0200
Subject: [PATCH 2/5] rename from platform_* to *_impl

---
 src/platform/mod.rs                           |   8 +-
 .../addr_validate.rs                          |   0
 .../{platform_nix => nix_impl}/profiler.rs    | 111 +++++++++++++++++-
 .../{platform_nix => nix_impl}/timer.rs       |   0
 .../addr_validate.rs                          |   0
 .../profiler.rs                               |   0
 .../timer.rs                                  |   0
 7 files changed, 114 insertions(+), 5 deletions(-)
 rename src/platform/{platform_nix => nix_impl}/addr_validate.rs (100%)
 rename src/platform/{platform_nix => nix_impl}/profiler.rs (67%)
 rename src/platform/{platform_nix => nix_impl}/timer.rs (100%)
 rename src/platform/{platform_windows => windows_impl}/addr_validate.rs (100%)
 rename src/platform/{platform_windows => windows_impl}/profiler.rs (100%)
 rename src/platform/{platform_windows => windows_impl}/timer.rs (100%)

diff --git a/src/platform/mod.rs b/src/platform/mod.rs
index 10cacd48..2db88d9c 100644
--- a/src/platform/mod.rs
+++ b/src/platform/mod.rs
@@ -1,19 +1,19 @@
 #[cfg(any(target_os = "linux", target_os = "macos"))]
-mod platform_nix {
+mod nix_impl {
     pub mod addr_validate;
     pub mod profiler;
     pub mod timer;
 }
 
 #[cfg(target_os = "windows")]
-mod platform_windows {
+mod windows_impl {
     pub mod addr_validate;
     pub mod profiler;
     pub mod timer;
 }
 
 #[cfg(any(target_os = "linux", target_os = "macos"))]
-pub use platform_nix::*;
+pub use nix_impl::*;
 
 #[cfg(target_os = "windows")]
-pub use platform_windows::*;
+pub use windows_impl::*;
diff --git a/src/platform/platform_nix/addr_validate.rs b/src/platform/nix_impl/addr_validate.rs
similarity index 100%
rename from src/platform/platform_nix/addr_validate.rs
rename to src/platform/nix_impl/addr_validate.rs
diff --git a/src/platform/platform_nix/profiler.rs b/src/platform/nix_impl/profiler.rs
similarity index 67%
rename from src/platform/platform_nix/profiler.rs
rename to src/platform/nix_impl/profiler.rs
index 9561193a..4b0015db 100644
--- a/src/platform/platform_nix/profiler.rs
+++ b/src/platform/nix_impl/profiler.rs
@@ -6,7 +6,7 @@ use smallvec::SmallVec;
 
 use nix::sys::signal;
 
-use crate::error::{Error, Result};
+use crate::error::Result;
 use crate::profiler::PROFILER;
 use crate::{MAX_DEPTH, MAX_THREAD_NAME};
 
@@ -183,3 +183,112 @@ extern "C" fn perf_signal_handler(
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use std::cell::RefCell;
+    use std::ffi::c_void;
+    use std::ptr::null_mut;
+
+    #[cfg(not(target_env = "gnu"))]
+    #[allow(clippy::wrong_self_convention)]
+    #[allow(non_upper_case_globals)]
+    static mut __malloc_hook: Option<extern "C" fn(size: usize) -> *mut c_void> = None;
+
+    extern "C" {
+        #[cfg(target_env = "gnu")]
+        static mut __malloc_hook: Option<extern "C" fn(size: usize) -> *mut c_void>;
+
+        fn malloc(size: usize) -> *mut c_void;
+    }
+
+    thread_local! {
+        static FLAG: RefCell<bool> = RefCell::new(false);
+    }
+
+    extern "C" fn malloc_hook(size: usize) -> *mut c_void {
+        unsafe {
+            __malloc_hook = None;
+        }
+
+        FLAG.with(|flag| {
+            flag.replace(true);
+        });
+        let p = unsafe { malloc(size) };
+
+        unsafe {
+            __malloc_hook = Some(malloc_hook);
+        }
+
+        p
+    }
+
+    #[inline(never)]
+    fn is_prime_number(v: usize, prime_numbers: &[usize]) -> bool {
+        if v < 10000 {
+            let r = prime_numbers.binary_search(&v);
+            return r.is_ok();
+        }
+
+        for n in prime_numbers {
+            if v % n == 0 {
+                return false;
+            }
+        }
+
+        true
+    }
+
+    #[inline(never)]
+    fn prepare_prime_numbers() -> Vec<usize> {
+        // bootstrap: Generate a prime table of 0..10000
+        let mut prime_number_table: [bool; 10000] = [true; 10000];
+        prime_number_table[0] = false;
+        prime_number_table[1] = false;
+        for i in 2..10000 {
+            if prime_number_table[i] {
+                let mut v = i * 2;
+                while v < 10000 {
+                    prime_number_table[v] = false;
+                    v += i;
+                }
+            }
+        }
+        let mut prime_numbers = vec![];
+        for (i, item) in prime_number_table.iter().enumerate().skip(2) {
+            if *item {
+                prime_numbers.push(i);
+            }
+        }
+        prime_numbers
+    }
+
+    #[cfg(target_os = "linux")]
+    #[test]
+    fn malloc_free() {
+        trigger_lazy();
+
+        let prime_numbers = prepare_prime_numbers();
+
+        let mut _v = 0;
+
+        unsafe {
+            __malloc_hook = Some(malloc_hook);
+        }
+        for i in 2..50000 {
+            if is_prime_number(i, &prime_numbers) {
+                _v += 1;
+                perf_signal_handler(27, null_mut(), null_mut());
+            }
+        }
+        unsafe {
+            __malloc_hook = None;
+        }
+
+        FLAG.with(|flag| {
+            assert!(!*flag.borrow());
+        });
+    }
+}
diff --git a/src/platform/platform_nix/timer.rs b/src/platform/nix_impl/timer.rs
similarity index 100%
rename from src/platform/platform_nix/timer.rs
rename to src/platform/nix_impl/timer.rs
diff --git a/src/platform/platform_windows/addr_validate.rs b/src/platform/windows_impl/addr_validate.rs
similarity index 100%
rename from src/platform/platform_windows/addr_validate.rs
rename to src/platform/windows_impl/addr_validate.rs
diff --git a/src/platform/platform_windows/profiler.rs b/src/platform/windows_impl/profiler.rs
similarity index 100%
rename from src/platform/platform_windows/profiler.rs
rename to src/platform/windows_impl/profiler.rs
diff --git a/src/platform/platform_windows/timer.rs b/src/platform/windows_impl/timer.rs
similarity index 100%
rename from src/platform/platform_windows/timer.rs
rename to src/platform/windows_impl/timer.rs

From 33b0180980f6d262a841fdcfa9a6d395cc69c9df Mon Sep 17 00:00:00 2001
From: Christian Jordan <svorre2304@gmail.com>
Date: Mon, 25 Jul 2022 00:14:28 +0200
Subject: [PATCH 3/5] remove linux tests from profiler

they were moved into nix_impl/profiler in previous commit
---
 src/profiler.rs | 110 ------------------------------------------------
 1 file changed, 110 deletions(-)

diff --git a/src/profiler.rs b/src/profiler.rs
index 1023fcd3..1a69ef54 100644
--- a/src/profiler.rs
+++ b/src/profiler.rs
@@ -227,113 +227,3 @@ impl Profiler {
         if let Ok(()) = self.data.add(frames, 1) {}
     }
 }
-
-#[cfg(test)]
-#[cfg(target_os = "linux")]
-mod tests {
-    use super::*;
-
-    use std::cell::RefCell;
-    use std::ffi::c_void;
-    use std::ptr::null_mut;
-
-    #[cfg(not(target_env = "gnu"))]
-    #[allow(clippy::wrong_self_convention)]
-    #[allow(non_upper_case_globals)]
-    static mut __malloc_hook: Option<extern "C" fn(size: usize) -> *mut c_void> = None;
-
-    extern "C" {
-        #[cfg(target_env = "gnu")]
-        static mut __malloc_hook: Option<extern "C" fn(size: usize) -> *mut c_void>;
-
-        fn malloc(size: usize) -> *mut c_void;
-    }
-
-    thread_local! {
-        static FLAG: RefCell<bool> = RefCell::new(false);
-    }
-
-    extern "C" fn malloc_hook(size: usize) -> *mut c_void {
-        unsafe {
-            __malloc_hook = None;
-        }
-
-        FLAG.with(|flag| {
-            flag.replace(true);
-        });
-        let p = unsafe { malloc(size) };
-
-        unsafe {
-            __malloc_hook = Some(malloc_hook);
-        }
-
-        p
-    }
-
-    #[inline(never)]
-    fn is_prime_number(v: usize, prime_numbers: &[usize]) -> bool {
-        if v < 10000 {
-            let r = prime_numbers.binary_search(&v);
-            return r.is_ok();
-        }
-
-        for n in prime_numbers {
-            if v % n == 0 {
-                return false;
-            }
-        }
-
-        true
-    }
-
-    #[inline(never)]
-    fn prepare_prime_numbers() -> Vec<usize> {
-        // bootstrap: Generate a prime table of 0..10000
-        let mut prime_number_table: [bool; 10000] = [true; 10000];
-        prime_number_table[0] = false;
-        prime_number_table[1] = false;
-        for i in 2..10000 {
-            if prime_number_table[i] {
-                let mut v = i * 2;
-                while v < 10000 {
-                    prime_number_table[v] = false;
-                    v += i;
-                }
-            }
-        }
-        let mut prime_numbers = vec![];
-        for (i, item) in prime_number_table.iter().enumerate().skip(2) {
-            if *item {
-                prime_numbers.push(i);
-            }
-        }
-        prime_numbers
-    }
-
-    #[cfg(target_os = "linux")]
-    #[test]
-    fn malloc_free() {
-        trigger_lazy();
-
-        let prime_numbers = prepare_prime_numbers();
-
-        let mut _v = 0;
-
-        unsafe {
-            __malloc_hook = Some(malloc_hook);
-        }
-        for i in 2..50000 {
-            if is_prime_number(i, &prime_numbers) {
-                _v += 1;
-                perf_signal_handler(27, null_mut(), null_mut());
-            }
-        }
-        unsafe {
-            __malloc_hook = None;
-        }
-
-        FLAG.with(|flag| {
-            assert!(!*flag.borrow());
-        });
-    }
-}

From 601129ab85fec53f4ab544ad789edd8b686b7acc Mon Sep 17 00:00:00 2001
From: Christian Jordan <svorre2304@gmail.com>
Date: Mon, 25 Jul 2022 00:15:03 +0200
Subject: [PATCH 4/5] move platform specific backtrace into ./platform

---
 src/{backtrace/mod.rs => backtrace.rs}        | 24 ++-------------
 src/{backtrace => platform}/backtrace_rs.rs   |  4 +--
 src/platform/mod.rs                           | 30 +++++++++++++++++++
 .../nix_impl}/frame_pointer.rs                |  6 ++--
 4 files changed, 37 insertions(+), 27 deletions(-)
 rename src/{backtrace/mod.rs => backtrace.rs} (65%)
 rename src/{backtrace => platform}/backtrace_rs.rs (85%)
 rename src/{backtrace => platform/nix_impl}/frame_pointer.rs (97%)

diff --git a/src/backtrace/mod.rs b/src/backtrace.rs
similarity index 65%
rename from src/backtrace/mod.rs
rename to src/backtrace.rs
index 44cd3214..59a569b7 100644
--- a/src/backtrace/mod.rs
+++ b/src/backtrace.rs
@@ -3,6 +3,8 @@
 use libc::c_void;
 use std::path::PathBuf;
 
+pub use crate::platform::TraceImpl;
+
 pub trait Symbol: Sized {
     fn name(&self) -> Option<Vec<u8>>;
     fn addr(&self) -> Option<*mut c_void>;
@@ -43,25 +45,3 @@ pub trait Trace {
     where
         Self: Sized;
 }
-
-#[cfg(not(all(
-    any(target_arch = "x86_64", target_arch = "aarch64"),
-    feature = "frame-pointer"
-)))]
-mod backtrace_rs;
-#[cfg(not(all(
-    any(target_arch = "x86_64", target_arch = "aarch64"),
-    feature = "frame-pointer"
-)))]
-pub use backtrace_rs::Trace as TraceImpl;
-
-#[cfg(all(
-    any(target_arch = "x86_64", target_arch = "aarch64"),
-    feature = "frame-pointer"
-))]
-pub mod frame_pointer;
-#[cfg(all(
-    any(target_arch = "x86_64", target_arch = "aarch64"),
-    feature = "frame-pointer"
-))]
-pub use frame_pointer::Trace as TraceImpl;
diff --git a/src/backtrace/backtrace_rs.rs b/src/platform/backtrace_rs.rs
similarity index 85%
rename from src/backtrace/backtrace_rs.rs
rename to src/platform/backtrace_rs.rs
index 1b7e3663..b2f18049 100644
--- a/src/backtrace/backtrace_rs.rs
+++ b/src/platform/backtrace_rs.rs
@@ -1,4 +1,4 @@
-impl super::Frame for backtrace::Frame {
+impl crate::backtrace::Frame for backtrace::Frame {
     type S = backtrace::Symbol;
 
     fn ip(&self) -> usize {
@@ -16,7 +16,7 @@ impl super::Frame for backtrace::Frame {
 
 pub struct Trace {}
 
-impl super::Trace for Trace {
+impl crate::backtrace::Trace for Trace {
     type Frame = backtrace::Frame;
 
     fn trace<F: FnMut(&Self::Frame) -> bool>(_: *mut libc::c_void, cb: F) {
diff --git a/src/platform/mod.rs b/src/platform/mod.rs
index 2db88d9c..effbefad 100644
--- a/src/platform/mod.rs
+++ b/src/platform/mod.rs
@@ -3,6 +3,29 @@ mod nix_impl {
     pub mod addr_validate;
     pub mod profiler;
     pub mod timer;
+
+    #[cfg(all(
+        any(target_arch = "x86_64", target_arch = "aarch64"),
+        feature = "frame-pointer",
+    ))]
+    mod frame_pointer;
+    #[cfg(all(
+        any(target_arch = "x86_64", target_arch = "aarch64"),
+        feature = "frame-pointer",
+    ))]
+    pub use frame_pointer::Trace as TraceImpl;
+
+    #[cfg(not(all(
+        any(target_arch = "x86_64", target_arch = "aarch64"),
+        feature = "frame-pointer",
+    )))]
+    #[path = "../backtrace_rs.rs"]
+    mod backtrace_rs;
+    #[cfg(not(all(
+        any(target_arch = "x86_64", target_arch = "aarch64"),
+        feature = "frame-pointer",
+    )))]
+    pub use backtrace_rs::Trace as TraceImpl;
 }
 
 #[cfg(target_os = "windows")]
@@ -10,6 +33,13 @@ mod windows_impl {
     pub mod addr_validate;
     pub mod profiler;
     pub mod timer;
+
+    #[cfg(feature = "frame-pointer")]
+    std::compile_error!("frame-pointer feature is currently not supported on windows.");
+
+    #[path = "../backtrace_rs.rs"]
+    mod backtrace_rs;
+    pub use backtrace_rs::Trace as TraceImpl;
 }
 
 #[cfg(any(target_os = "linux", target_os = "macos"))]
diff --git a/src/backtrace/frame_pointer.rs b/src/platform/nix_impl/frame_pointer.rs
similarity index 97%
rename from src/backtrace/frame_pointer.rs
rename to src/platform/nix_impl/frame_pointer.rs
index 1810e4a0..f95bc4c8 100644
--- a/src/backtrace/frame_pointer.rs
+++ b/src/platform/nix_impl/frame_pointer.rs
@@ -4,7 +4,7 @@ use std::ptr::null_mut;
 
 use libc::c_void;
 
-use crate::addr_validate::validate;
+use crate::validate;
 
 #[derive(Clone, Debug)]
 pub struct Frame {
@@ -16,7 +16,7 @@ extern "C" {
 
 }
 
-impl super::Frame for Frame {
+impl crate::backtrace::Frame for Frame {
     type S = backtrace::Symbol;
 
     fn ip(&self) -> usize {
@@ -37,7 +37,7 @@ impl super::Frame for Frame {
 }
 
 pub struct Trace {}
-impl super::Trace for Trace {
+impl crate::backtrace::Trace for Trace {
     type Frame = Frame;
 
     fn trace<F: FnMut(&Self::Frame) -> bool>(ucontext: *mut libc::c_void, mut cb: F) {

From ced8cdabf37e7b5bd29c5fda837a764c3c16f72e Mon Sep 17 00:00:00 2001
From: Christian Jordan <svorre2304@gmail.com>
Date: Mon, 25 Jul 2022 00:32:50 +0200
Subject: [PATCH 5/5] Use fmt::write instead of push_str

To make clippy happy
the .unwrap on write! shouldn't fail
---
 src/report.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/report.rs b/src/report.rs
index a3648a88..39863538 100644
--- a/src/report.rs
+++ b/src/report.rs
@@ -159,7 +159,7 @@ impl Debug for Report {
 mod flamegraph {
     use super::*;
     use inferno::flamegraph;
-    use std::io::Write;
+    use std::{fmt::Write as _, io::Write};
 
     impl Report {
         /// `flamegraph` will write an svg flamegraph into `writer` **only available with `flamegraph` feature**
@@ -188,13 +188,13 @@ mod flamegraph {
 
                     for frame in key.frames.iter().rev() {
                         for symbol in frame.iter().rev() {
-                            line.push_str(&format!("{}", symbol));
+                            write!(line, "{}", symbol).unwrap();
                             line.push(';');
                         }
                     }
 
                     line.pop().unwrap_or_default();
-                    line.push_str(&format!(" {}", value));
+                    write!(line, " {}", value).unwrap();
 
                     line
                 })