Skip to content

Commit 48b766a

Browse files
authored
Merge pull request #246 from dhardy/log-feature
Add log feature
2 parents 9762749 + a01a825 commit 48b766a

File tree

7 files changed

+57
-17
lines changed

7 files changed

+57
-17
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ matrix:
2828
script:
2929
- cargo test
3030
- cargo test --tests --no-default-features
31-
- cargo test --features serde-1
31+
- cargo test --features serde-1,log
3232
- cargo test --tests --no-default-features --features=serde-1
3333
- cargo test --manifest-path rand-derive/Cargo.toml
3434

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ libc = { version = "0.2", optional = true }
3232
winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "profileapi", "winnt"], optional = true }
3333

3434
[dependencies]
35+
log = { version = "0.4", optional = true }
36+
3537
serde = {version="1",optional=true}
3638
serde_derive = {version="1", optional=true}
3739

README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ changes since the `0.3` series, but nevertheless contains some significant
3333
new code, including a new "external" entropy source (`JitterRng`) and `no_std`
3434
support.
3535

36-
Version `0.5` is in development and contains significant performance
37-
improvements for the ISAAC random number generators.
36+
Version `0.5` is in development and will contain significant breaking changes.
3837

3938
## Examples
4039

@@ -68,21 +67,22 @@ println!("i32: {}, u32: {}", rng.gen::<i32>(), rng.gen::<u32>())
6867
By default, `rand` is built with all stable features available. The following
6968
optional features are available:
7069

70+
- `alloc` can be used instead of `std` to provide `Vec` and `Box`
7171
- `i128_support` enables support for generating `u128` and `i128` values
72+
- `log` enables some logging via the `log` crate
7273
- `nightly` enables all unstable features (`i128_support`)
74+
- `serde-1` enables serialisation for some types, via Serde version 1
7375
- `std` enabled by default; by setting "default-features = false" `no_std`
7476
mode is activated; this removes features depending on `std` functionality:
75-
76-
- `OsRng` is entirely unavailable
77-
- `JitterRng` code is still present, but a nanosecond timer must be
78-
provided via `JitterRng::new_with_timer`
79-
- Since no external entropy is available, it is not possible to create
80-
generators with fresh seeds (user must provide entropy)
81-
- `thread_rng`, `weak_rng` and `random` are all disabled
82-
- exponential, normal and gamma type distributions are unavailable
83-
since `exp` and `log` functions are not provided in `core`
84-
- any code requiring `Vec` or `Box`
85-
- `alloc` can be used instead of `std` to provide `Vec` and `Box`
77+
- `OsRng` is entirely unavailable
78+
- `JitterRng` code is still present, but a nanosecond timer must be
79+
provided via `JitterRng::new_with_timer`
80+
- Since no external entropy is available, it is not possible to create
81+
generators with fresh seeds (user must provide entropy)
82+
- `thread_rng`, `weak_rng` and `random` are all disabled
83+
- exponential, normal and gamma type distributions are unavailable
84+
since `exp` and `log` functions are not provided in `core`
85+
- any code requiring `Vec` or `Box`
8686

8787
## Testing
8888

src/jitter.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ impl JitterRng {
142142
// This allows the timer test to run multiple times; we don't care.
143143
rounds = ec.test_timer()?;
144144
JITTER_ROUNDS.store(rounds as usize, Ordering::Relaxed);
145+
info!("JitterRng: using {} rounds per u64 output", rounds);
145146
}
146147
ec.set_rounds(rounds);
147148
Ok(ec)
@@ -422,6 +423,8 @@ impl JitterRng {
422423
}
423424

424425
fn gen_entropy(&mut self) -> u64 {
426+
trace!("JitterRng: collecting entropy");
427+
425428
// Prime `self.prev_time`, and run the noice sources to make sure the
426429
// first loop round collects the expected entropy.
427430
let _ = self.measure_jitter();
@@ -444,6 +447,7 @@ impl JitterRng {
444447
/// to collect 64 bits of entropy. Otherwise a `TimerError` with the cause
445448
/// of the failure will be returned.
446449
pub fn test_timer(&mut self) -> Result<u32, TimerError> {
450+
debug!("JitterRng: testing timer ...");
447451
// We could add a check for system capabilities such as `clock_getres`
448452
// or check for `CONFIG_X86_TSC`, but it does not make much sense as the
449453
// following sanity checks verify that we have a high-resolution timer.

src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,10 +249,18 @@
249249

250250
#[cfg(feature="std")] extern crate std as core;
251251
#[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc;
252+
252253
#[cfg(test)] #[cfg(feature="serde-1")] extern crate bincode;
253254
#[cfg(feature="serde-1")] extern crate serde;
254255
#[cfg(feature="serde-1")] #[macro_use] extern crate serde_derive;
255256

257+
#[cfg(feature = "log")] #[macro_use] extern crate log;
258+
#[cfg(not(feature = "log"))] macro_rules! trace { ($($x:tt)*) => () }
259+
#[cfg(not(feature = "log"))] macro_rules! debug { ($($x:tt)*) => () }
260+
#[cfg(all(feature="std", not(feature = "log")))] macro_rules! info { ($($x:tt)*) => () }
261+
#[cfg(all(feature="std", not(feature = "log")))] macro_rules! warn { ($($x:tt)*) => () }
262+
263+
256264
use core::{marker, mem};
257265
#[cfg(feature="std")] use std::cell::RefCell;
258266
#[cfg(feature="std")] use std::rc::Rc;
@@ -846,9 +854,11 @@ impl<R: SeedableRng> NewRng for R {
846854
T::from_rng(&mut r)
847855
}
848856

857+
trace!("Seeding new RNG");
849858
new_os().or_else(|e1| {
859+
warn!("OsRng failed [falling back to JitterRng]: {:?}", e1);
850860
new_jitter().map_err(|_e2| {
851-
// TODO: log
861+
warn!("JitterRng failed: {:?}", _e2);
852862
// TODO: can we somehow return both error sources?
853863
Error::with_cause(
854864
ErrorKind::Unavailable,

src/os.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,35 @@ impl Rng for OsRng {
6969
const MAX_WAIT: u32 = (10 * 1000) / WAIT_DUR_MS; // max 10s
7070
const TRANSIENT_STEP: u32 = MAX_WAIT / 8;
7171
let mut err_count = 0;
72+
let mut log_err = 0; // log when err_count >= log_err
7273

7374
loop {
7475
if let Err(e) = self.try_fill_bytes(dest) {
75-
// TODO: add logging to explain why we wait and the full cause
76+
if log_err == 0 {
77+
warn!("OsRng failed: {:?}", e);
78+
}
79+
7680
if e.kind().should_retry() {
7781
if err_count > MAX_WAIT {
7882
panic!("Too many RNG errors or timeout; last error: {}", e.msg());
7983
}
8084

8185
if e.kind().should_wait() {
86+
err_count += 1;
87+
if err_count >= log_err {
88+
log_err += TRANSIENT_STEP;
89+
warn!("OsRng: waiting and retrying ...");
90+
}
8291
#[cfg(feature="std")]{
8392
let dur = ::std::time::Duration::from_millis(WAIT_DUR_MS as u64);
8493
::std::thread::sleep(dur);
8594
}
86-
err_count += 1;
8795
} else {
8896
err_count += TRANSIENT_STEP;
97+
if err_count >= log_err {
98+
log_err += TRANSIENT_STEP;
99+
warn!("OsRng: retrying ...");
100+
}
89101
}
90102

91103
continue;
@@ -130,6 +142,7 @@ impl ReadRng {
130142
let mutex = unsafe{ READ_RNG_FILE.as_ref().unwrap() };
131143
let mut guard = mutex.lock().unwrap();
132144
if (*guard).is_none() {
145+
info!("OsRng: opening random device {}", path.as_ref().display());
133146
let file = File::open(path).map_err(|err| Error::with_cause(
134147
ErrorKind::Unavailable,
135148
"error opening random device",
@@ -142,6 +155,7 @@ impl ReadRng {
142155
}
143156

144157
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
158+
trace!("OsRng: reading {} bytes from random device", dest.len());
145159
if dest.len() == 0 { return Ok(()); }
146160

147161
// Since we have an instance of Self, we can assume that our memory was
@@ -211,6 +225,7 @@ mod imp {
211225
fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
212226

213227
fn getrandom_try_fill(v: &mut [u8]) -> Result<(), Error> {
228+
trace!("OsRng: reading {} bytes via getrandom", v.len());
214229
let mut read = 0;
215230
let len = v.len();
216231
while read < len {
@@ -253,6 +268,7 @@ mod imp {
253268
static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;
254269

255270
CHECKER.call_once(|| {
271+
debug!("OsRng: testing getrandom");
256272
let mut buf: [u8; 0] = [];
257273
let result = getrandom(&mut buf);
258274
let available = if result == -1 {
@@ -262,6 +278,7 @@ mod imp {
262278
true
263279
};
264280
AVAILABLE.store(available, Ordering::Relaxed);
281+
info!("OsRng: using {}", if available { "getrandom" } else { "/dev/urandom" });
265282
});
266283

267284
AVAILABLE.load(Ordering::Relaxed)
@@ -320,6 +337,7 @@ mod imp {
320337
}
321338

322339
pub fn try_fill_bytes(&mut self, v: &mut [u8]) -> Result<(), Error> {
340+
trace!("OsRng: reading {} bytes via cloadabi::random_get", v.len());
323341
if unsafe { cloudabi::random_get(v) } == cloudabi::errno::SUCCESS {
324342
Ok(())
325343
} else {
@@ -360,6 +378,7 @@ mod imp {
360378
Ok(OsRng)
361379
}
362380
pub fn try_fill_bytes(&mut self, v: &mut [u8]) -> Result<(), Error> {
381+
trace!("OsRng: reading {} bytes via SecRandomCopyBytes", v.len());
363382
let ret = unsafe {
364383
SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
365384
};
@@ -395,6 +414,7 @@ mod imp {
395414
// kern.arandom permits a maximum buffer size of 256 bytes
396415
for s in v.chunks_mut(256) {
397416
let mut s_len = s.len();
417+
trace!("OsRng: reading {} bytes via kern.arandom", v.len());
398418
let ret = unsafe {
399419
libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
400420
s.as_mut_ptr() as *mut _, &mut s_len,
@@ -429,6 +449,7 @@ mod imp {
429449
pub fn try_fill_bytes(&mut self, v: &mut [u8]) -> Result<(), Error> {
430450
// getentropy(2) permits a maximum buffer size of 256 bytes
431451
for s in v.chunks_mut(256) {
452+
trace!("OsRng: reading {} bytes via getentropy", s.len());
432453
let ret = unsafe {
433454
libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
434455
};
@@ -482,6 +503,7 @@ mod imp {
482503
}
483504
pub fn try_fill_bytes(&mut self, v: &mut [u8]) -> Result<(), Error> {
484505
for s in v.chunks_mut(fuchsia_zircon::sys::ZX_CPRNG_DRAW_MAX_LEN) {
506+
trace!("OsRng: reading {} bytes via cprng_draw", s.len());
485507
let mut filled = 0;
486508
while filled < s.len() {
487509
match fuchsia_zircon::cprng_draw(&mut s[filled..]) {
@@ -523,6 +545,7 @@ mod imp {
523545
// RtlGenRandom takes an ULONG (u32) for the length so we need to
524546
// split up the buffer.
525547
for slice in v.chunks_mut(<ULONG>::max_value() as usize) {
548+
trace!("OsRng: reading {} bytes via RtlGenRandom", slice.len());
526549
let ret = unsafe {
527550
RtlGenRandom(slice.as_mut_ptr() as PVOID, slice.len() as ULONG)
528551
};

src/reseeding.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ impl<R: Rng, Rsdr: Reseeder<R>> ReseedingRng<R, Rsdr> {
4747
/// generated exceed the threshold.
4848
pub fn reseed_if_necessary(&mut self) {
4949
if self.bytes_generated >= self.generation_threshold {
50+
trace!("Reseeding RNG after {} bytes", self.bytes_generated);
5051
self.reseeder.reseed(&mut self.rng).unwrap();
5152
self.bytes_generated = 0;
5253
}

0 commit comments

Comments
 (0)