1use crate::util::constants::LOG_BYTES_IN_MBYTE;
2use crate::util::os::*;
3use crate::util::Address;
4use std::default::Default;
5use std::fmt::Debug;
6use std::str::FromStr;
7use strum_macros::EnumString;
8
9pub const DEFAULT_STRESS_FACTOR: usize = usize::MAX;
12
13#[derive(Copy, Clone, EnumString, Debug)]
16pub enum NurseryZeroingOptions {
17 Temporal,
19 Nontemporal,
21 Concurrent,
23 Adaptive,
25}
26
27#[derive(Copy, Clone, EnumString, Debug, PartialEq, Eq)]
29pub enum PlanSelector {
30 NoGC,
33 SemiSpace,
36 GenCopy,
38 GenImmix,
40 MarkSweep,
42 PageProtect,
45 Immix,
47 MarkCompact,
49 Compressor,
51 StickyImmix,
53 ConcurrentImmix,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct PerfEventOptions {
66 pub events: Vec<(String, i32, i32)>,
68}
69
70impl PerfEventOptions {
71 fn parse_perf_events(events: &str) -> Result<Vec<(String, i32, i32)>, String> {
72 events
73 .split(';')
74 .filter(|e| !e.is_empty())
75 .map(|e| {
76 let e: Vec<&str> = e.split(',').collect();
77 if e.len() != 3 {
78 Err("Please supply (event name, pid, cpu)".into())
79 } else {
80 let event_name = e[0].into();
81 let pid = e[1]
82 .parse()
83 .map_err(|_| String::from("Failed to parse cpu"))?;
84 let cpu = e[2]
85 .parse()
86 .map_err(|_| String::from("Failed to parse cpu"))?;
87 Ok((event_name, pid, cpu))
88 }
89 })
90 .collect()
91 }
92}
93
94impl FromStr for PerfEventOptions {
95 type Err = String;
96
97 fn from_str(s: &str) -> Result<Self, Self::Err> {
98 PerfEventOptions::parse_perf_events(s).map(|events| PerfEventOptions { events })
99 }
100}
101
102#[cfg(target_pointer_width = "64")]
105pub const DEFAULT_MIN_NURSERY: usize = 2 << LOG_BYTES_IN_MBYTE;
106#[cfg(target_pointer_width = "64")]
109pub const DEFAULT_MAX_NURSERY: usize = (1 << 20) << LOG_BYTES_IN_MBYTE;
110
111#[cfg(target_pointer_width = "32")]
114pub const DEFAULT_MIN_NURSERY: usize = 2 << LOG_BYTES_IN_MBYTE;
115pub const DEFAULT_MAX_NURSERY_32: usize = 32 << LOG_BYTES_IN_MBYTE;
117#[cfg(target_pointer_width = "32")]
120pub const DEFAULT_MAX_NURSERY: usize = DEFAULT_MAX_NURSERY_32;
121
122pub const DEFAULT_PROPORTIONAL_MIN_NURSERY: f64 = 0.25;
124pub const DEFAULT_PROPORTIONAL_MAX_NURSERY: f64 = 1.0;
126
127fn always_valid<T>(_: &T) -> bool {
128 true
129}
130
131enum SetOptionByStringError {
133 InvalidKey,
135 ValueParseError,
137 ValueValidationError,
139}
140
141#[derive(Clone)]
145pub struct MMTKOption<T: Debug + Clone + FromStr> {
146 value: T,
148 validator: fn(&T) -> bool,
150}
151
152impl<T: Debug + Clone + FromStr> MMTKOption<T> {
153 pub fn new(value: T, validator: fn(&T) -> bool) -> Self {
155 MMTKOption { value, validator }
168 }
169
170 pub fn set(&mut self, value: T) -> bool {
172 if (self.validator)(&value) {
173 self.value = value;
174 return true;
175 }
176 false
177 }
178}
179
180impl<T: Debug + Clone + FromStr> std::ops::Deref for MMTKOption<T> {
182 type Target = T;
183
184 fn deref(&self) -> &Self::Target {
185 &self.value
186 }
187}
188
189macro_rules! options {
190 ($($(#[$outer:meta])*$name:ident: $type:ty [$validator:expr] = $default:expr),*,) => [
191 options!($(#[$outer])*$($name: $type [$validator] = $default),*);
192 ];
193 ($($(#[$outer:meta])*$name:ident: $type:ty [$validator:expr] = $default:expr),*) => [
194 #[derive(Clone)]
203 pub struct Options {
204 $($(#[$outer])*pub $name: MMTKOption<$type>),*
205 }
206
207 impl Options {
208 fn set_from_string_inner(&mut self, s: &str, val: &str) -> Result<(), SetOptionByStringError> {
210 match s {
211 $(stringify!($name) => {
213 let Ok(typed_val) = val.parse::<$type>() else {
214 return Err(SetOptionByStringError::ValueParseError);
215 };
216
217 if !self.$name.set(typed_val) {
218 return Err(SetOptionByStringError::ValueValidationError);
219 }
220
221 Ok(())
222 })*
223 _ => Err(SetOptionByStringError::InvalidKey)
224 }
225 }
226
227 fn new() -> Self {
229 Options {
230 $($name: MMTKOption::new($default, $validator)),*
231 }
232 }
233 }
234 ]
235}
236
237impl Default for Options {
238 fn default() -> Self {
240 Self::new()
241 }
242}
243
244impl Options {
245 pub fn set_from_string(&mut self, s: &str, val: &str) -> bool {
268 self.set_from_string_inner(s, val).is_ok()
269 }
270
271 pub fn set_bulk_from_string(&mut self, options: &str) -> bool {
284 for opt in options.replace(',', " ").split_ascii_whitespace() {
285 let kv_pair: Vec<&str> = opt.split('=').collect();
286 if kv_pair.len() != 2 {
287 return false;
288 }
289
290 let key = kv_pair[0];
291 let val = kv_pair[1];
292 if let Err(e) = self.set_from_string_inner(key, val) {
293 match e {
294 SetOptionByStringError::InvalidKey => {
295 panic!("Invalid Options key: {}", key);
296 }
297 SetOptionByStringError::ValueParseError => {
298 eprintln!("Warn: unable to set {}={:?}. Can't parse value. Default value will be used.", key, val);
299 }
300 SetOptionByStringError::ValueValidationError => {
301 eprintln!("Warn: unable to set {}={:?}. Invalid value. Default value will be used.", key, val);
302 }
303 }
304 return false;
305 }
306 }
307
308 true
309 }
310
311 pub fn read_env_var_settings(&mut self) {
316 const PREFIX: &str = "MMTK_";
317 for (key, val) in std::env::vars() {
318 if let Some(rest_of_key) = key.strip_prefix(PREFIX) {
320 let lowercase: &str = &rest_of_key.to_lowercase();
321 if let Err(e) = self.set_from_string_inner(lowercase, &val) {
322 match e {
323 SetOptionByStringError::InvalidKey => {
324 }
326 SetOptionByStringError::ValueParseError => {
327 eprintln!("Warn: unable to set {}={:?}. Can't parse value. Default value will be used.", key, val);
328 }
329 SetOptionByStringError::ValueValidationError => {
330 eprintln!("Warn: unable to set {}={:?}. Invalid value. Default value will be used.", key, val);
331 }
332 }
333 }
334 }
335 }
336 }
337
338 pub fn is_stress_test_gc_enabled(&self) -> bool {
341 *self.stress_factor != DEFAULT_STRESS_FACTOR
342 || *self.analysis_factor != DEFAULT_STRESS_FACTOR
343 }
344}
345
346#[derive(Clone, Debug, PartialEq)]
347pub enum AffinityKind {
350 OsDefault,
352 RoundRobin(Vec<CoreId>),
357 AllInSet(Vec<CoreId>),
360}
361
362impl AffinityKind {
363 fn parse_cpulist(cpulist: &str) -> Result<AffinityKind, String> {
376 let mut cpuset = vec![];
377
378 if cpulist.is_empty() {
379 return Ok(AffinityKind::OsDefault);
380 }
381
382 let mut all_in_set = false;
387 let kind_split: Vec<&str> = cpulist.splitn(2, ':').collect();
388 if kind_split.len() == 2 {
389 match kind_split[0] {
390 "RoundRobin" => {
391 all_in_set = false;
392 }
393 "AllInSet" => {
394 all_in_set = true;
395 }
396 _ => {
397 return Err(format!("Unknown affinity kind: {}", kind_split[0]));
398 }
399 }
400 }
401
402 let cpulist = if kind_split.len() == 2 {
403 kind_split[1]
404 } else {
405 kind_split[0]
406 };
407
408 for split in cpulist.split(',') {
410 if !split.contains('-') {
411 if !split.is_empty() {
412 if let Ok(core) = split.parse::<u16>() {
413 cpuset.push(core);
414 cpuset.sort_unstable();
415 cpuset.dedup();
416 continue;
417 }
418 }
419 } else {
420 let range: Vec<&str> = split.split('-').collect();
422 if range.len() == 2 {
423 if let Ok(start) = range[0].parse::<u16>() {
424 if let Ok(end) = range[1].parse::<u16>() {
425 if start >= end {
426 return Err(
427 "Starting core id in range should be less than the end"
428 .to_string(),
429 );
430 }
431
432 for cpu in start..=end {
433 cpuset.push(cpu);
434 cpuset.sort_unstable();
435 cpuset.dedup();
436 }
437
438 continue;
439 }
440 }
441 }
442 }
443
444 return Err("Core ids have been incorrectly specified".to_string());
445 }
446
447 if all_in_set {
448 Ok(AffinityKind::AllInSet(cpuset))
449 } else {
450 Ok(AffinityKind::RoundRobin(cpuset))
451 }
452 }
453
454 pub fn validate(&self) -> bool {
458 let num_cpu = OS::get_total_num_cpus();
459
460 if let AffinityKind::RoundRobin(cpuset) = self {
461 for cpu in cpuset {
462 if cpu >= &num_cpu {
463 return false;
464 }
465 }
466 }
467
468 true
469 }
470}
471
472impl FromStr for AffinityKind {
473 type Err = String;
474
475 fn from_str(s: &str) -> Result<Self, Self::Err> {
476 AffinityKind::parse_cpulist(s)
477 }
478}
479
480#[derive(Copy, Clone, Debug)]
481pub enum NurserySize {
484 Bounded {
487 min: usize,
489 max: usize,
491 },
492 ProportionalBounded {
494 min: f64,
496 max: f64,
498 },
499 Fixed(usize),
503}
504
505impl NurserySize {
506 fn validate(&self) -> bool {
508 match *self {
509 NurserySize::Bounded { min, max } => min <= max,
510 NurserySize::ProportionalBounded { min, max } => {
511 0.0f64 < min && min <= max && max <= 1.0f64
512 }
513 NurserySize::Fixed(_) => true,
514 }
515 }
516}
517
518impl FromStr for NurserySize {
519 type Err = String;
520
521 fn from_str(s: &str) -> Result<Self, Self::Err> {
522 let parts: Vec<&str> = s.split(':').collect();
523 if parts.len() != 2 {
524 return Err("Invalid format".to_string());
525 }
526
527 let variant = parts[0];
528 let values: Vec<&str> = parts[1].split(',').collect();
529
530 fn default_or_parse<T: FromStr>(val: &str, default_value: T) -> Result<T, String> {
531 if val == "_" {
532 Ok(default_value)
533 } else {
534 val.parse::<T>()
535 .map_err(|_| format!("Failed to parse {:?}", std::any::type_name::<T>()))
536 }
537 }
538
539 match variant {
540 "Bounded" => {
541 if values.len() == 2 {
542 let min = default_or_parse(values[0], DEFAULT_MIN_NURSERY)?;
543 let max = default_or_parse(values[1], DEFAULT_MAX_NURSERY)?;
544 Ok(NurserySize::Bounded { min, max })
545 } else {
546 Err("Bounded requires two values".to_string())
547 }
548 }
549 "ProportionalBounded" => {
550 if values.len() == 2 {
551 let min = default_or_parse(values[0], DEFAULT_PROPORTIONAL_MIN_NURSERY)?;
552 let max = default_or_parse(values[1], DEFAULT_PROPORTIONAL_MAX_NURSERY)?;
553 Ok(NurserySize::ProportionalBounded { min, max })
554 } else {
555 Err("ProportionalBounded requires two values".to_string())
556 }
557 }
558 "Fixed" => {
559 if values.len() == 1 {
560 let size = values[0]
561 .parse::<usize>()
562 .map_err(|_| "Invalid size value".to_string())?;
563 Ok(NurserySize::Fixed(size))
564 } else {
565 Err("Fixed requires one value".to_string())
566 }
567 }
568 _ => Err("Unknown variant".to_string()),
569 }
570 }
571}
572
573#[cfg(test)]
574mod nursery_size_parsing_tests {
575 use super::*;
576
577 #[test]
578 fn test_bounded() {
579 let result = "Bounded:1,2".parse::<NurserySize>().unwrap();
581 if let NurserySize::Bounded { min, max } = result {
582 assert_eq!(min, 1);
583 assert_eq!(max, 2);
584 } else {
585 panic!("Failed: {:?}", result);
586 }
587
588 let result = "Bounded:_,2".parse::<NurserySize>().unwrap();
590 if let NurserySize::Bounded { min, max } = result {
591 assert_eq!(min, DEFAULT_MIN_NURSERY);
592 assert_eq!(max, 2);
593 } else {
594 panic!("Failed: {:?}", result);
595 }
596
597 let result = "Bounded:1,_".parse::<NurserySize>().unwrap();
599 if let NurserySize::Bounded { min, max } = result {
600 assert_eq!(min, 1);
601 assert_eq!(max, DEFAULT_MAX_NURSERY);
602 } else {
603 panic!("Failed: {:?}", result);
604 }
605
606 let result = "Bounded:_,_".parse::<NurserySize>().unwrap();
608 if let NurserySize::Bounded { min, max } = result {
609 assert_eq!(min, DEFAULT_MIN_NURSERY);
610 assert_eq!(max, DEFAULT_MAX_NURSERY);
611 } else {
612 panic!("Failed: {:?}", result);
613 }
614 }
615
616 #[test]
617 fn test_proportional() {
618 let result = "ProportionalBounded:0.1,0.8"
620 .parse::<NurserySize>()
621 .unwrap();
622 if let NurserySize::ProportionalBounded { min, max } = result {
623 assert_eq!(min, 0.1);
624 assert_eq!(max, 0.8);
625 } else {
626 panic!("Failed: {:?}", result);
627 }
628
629 let result = "ProportionalBounded:_,0.8".parse::<NurserySize>().unwrap();
631 if let NurserySize::ProportionalBounded { min, max } = result {
632 assert_eq!(min, DEFAULT_PROPORTIONAL_MIN_NURSERY);
633 assert_eq!(max, 0.8);
634 } else {
635 panic!("Failed: {:?}", result);
636 }
637
638 let result = "ProportionalBounded:0.1,_".parse::<NurserySize>().unwrap();
640 if let NurserySize::ProportionalBounded { min, max } = result {
641 assert_eq!(min, 0.1);
642 assert_eq!(max, DEFAULT_PROPORTIONAL_MAX_NURSERY);
643 } else {
644 panic!("Failed: {:?}", result);
645 }
646
647 let result = "ProportionalBounded:_,_".parse::<NurserySize>().unwrap();
649 if let NurserySize::ProportionalBounded { min, max } = result {
650 assert_eq!(min, DEFAULT_PROPORTIONAL_MIN_NURSERY);
651 assert_eq!(max, DEFAULT_PROPORTIONAL_MAX_NURSERY);
652 } else {
653 panic!("Failed: {:?}", result);
654 }
655 }
656}
657
658#[derive(Copy, Clone, Debug, PartialEq, Eq)]
660pub enum GCTriggerSelector {
661 FixedHeapSize(usize),
663 DynamicHeapSize(usize, usize),
666 Delegated,
668}
669
670impl GCTriggerSelector {
671 const K: u64 = 1024;
672 const M: u64 = 1024 * Self::K;
673 const G: u64 = 1024 * Self::M;
674 const T: u64 = 1024 * Self::G;
675
676 pub fn max_heap_size(&self) -> usize {
678 match self {
679 Self::FixedHeapSize(s) => *s,
680 Self::DynamicHeapSize(_, s) => *s,
681 _ => unreachable!("Cannot get max heap size"),
682 }
683 }
684
685 fn parse_size(s: &str) -> Result<usize, String> {
689 let s = s.to_lowercase();
690 if s.ends_with(char::is_alphabetic) {
691 let num = s[0..s.len() - 1]
692 .parse::<u64>()
693 .map_err(|e| e.to_string())?;
694 let size = if s.ends_with('k') {
695 num.checked_mul(Self::K)
696 } else if s.ends_with('m') {
697 num.checked_mul(Self::M)
698 } else if s.ends_with('g') {
699 num.checked_mul(Self::G)
700 } else if s.ends_with('t') {
701 num.checked_mul(Self::T)
702 } else {
703 return Err(format!(
704 "Unknown size descriptor: {:?}",
705 &s[(s.len() - 1)..]
706 ));
707 };
708
709 if let Some(size) = size {
710 size.try_into()
711 .map_err(|_| format!("size overflow: {}", size))
712 } else {
713 Err(format!("size overflow: {}", s))
714 }
715 } else {
716 s.parse::<usize>().map_err(|e| e.to_string())
717 }
718 }
719
720 fn validate(&self) -> bool {
722 match self {
723 Self::FixedHeapSize(size) => *size > 0,
724 Self::DynamicHeapSize(min, max) => min <= max,
725 Self::Delegated => true,
726 }
727 }
728}
729
730impl FromStr for GCTriggerSelector {
731 type Err = String;
732
733 fn from_str(s: &str) -> Result<Self, Self::Err> {
734 use regex::Regex;
735 lazy_static! {
736 static ref FIXED_HEAP_REGEX: Regex =
737 Regex::new(r"^FixedHeapSize:(?P<size>\d+[kKmMgGtT]?)$").unwrap();
738 static ref DYNAMIC_HEAP_REGEX: Regex =
739 Regex::new(r"^DynamicHeapSize:(?P<min>\d+[kKmMgGtT]?),(?P<max>\d+[kKmMgGtT]?)$")
740 .unwrap();
741 }
742
743 if s.is_empty() {
744 return Err("No GC trigger policy is supplied".to_string());
745 }
746
747 if let Some(captures) = FIXED_HEAP_REGEX.captures(s) {
748 return Self::parse_size(&captures["size"]).map(Self::FixedHeapSize);
749 } else if let Some(captures) = DYNAMIC_HEAP_REGEX.captures(s) {
750 let min = Self::parse_size(&captures["min"])?;
751 let max = Self::parse_size(&captures["max"])?;
752 return Ok(Self::DynamicHeapSize(min, max));
753 } else if s.starts_with("Delegated") {
754 return Ok(Self::Delegated);
755 }
756
757 Err(format!("Failed to parse the GC trigger option: {:?}", s))
758 }
759}
760
761#[cfg(test)]
762mod gc_trigger_tests {
763 use super::*;
764
765 #[test]
766 fn test_parse_size() {
767 assert_eq!(GCTriggerSelector::parse_size("0"), Ok(0));
769 assert_eq!(GCTriggerSelector::parse_size("1K"), Ok(1024));
770 assert_eq!(GCTriggerSelector::parse_size("1k"), Ok(1024));
771 assert_eq!(GCTriggerSelector::parse_size("2M"), Ok(2 * 1024 * 1024));
772 assert_eq!(GCTriggerSelector::parse_size("2m"), Ok(2 * 1024 * 1024));
773 assert_eq!(
774 GCTriggerSelector::parse_size("2G"),
775 Ok(2 * 1024 * 1024 * 1024)
776 );
777 assert_eq!(
778 GCTriggerSelector::parse_size("2g"),
779 Ok(2 * 1024 * 1024 * 1024)
780 );
781 #[cfg(target_pointer_width = "64")]
782 assert_eq!(
783 GCTriggerSelector::parse_size("2T"),
784 Ok(2 * 1024 * 1024 * 1024 * 1024)
785 );
786
787 assert_eq!(
789 GCTriggerSelector::parse_size(""),
790 Err("cannot parse integer from empty string".to_string())
791 );
792
793 assert!(GCTriggerSelector::parse_size("-1").is_err());
795
796 assert!(GCTriggerSelector::parse_size("k").is_err());
798 }
799
800 #[test]
801 #[cfg(target_pointer_width = "32")]
802 fn test_parse_overflow_size() {
803 assert_eq!(
804 GCTriggerSelector::parse_size("4G"),
805 Err("size overflow: 4294967296".to_string())
806 );
807 assert_eq!(GCTriggerSelector::parse_size("4294967295"), Ok(4294967295));
808 }
809
810 #[test]
811 fn test_parse_fixed_heap() {
812 assert_eq!(
813 GCTriggerSelector::from_str("FixedHeapSize:1024"),
814 Ok(GCTriggerSelector::FixedHeapSize(1024))
815 );
816 assert_eq!(
817 GCTriggerSelector::from_str("FixedHeapSize:4m"),
818 Ok(GCTriggerSelector::FixedHeapSize(4 * 1024 * 1024))
819 );
820 #[cfg(target_pointer_width = "64")]
821 assert_eq!(
822 GCTriggerSelector::from_str("FixedHeapSize:4t"),
823 Ok(GCTriggerSelector::FixedHeapSize(
824 4 * 1024 * 1024 * 1024 * 1024
825 ))
826 );
827
828 assert!(GCTriggerSelector::from_str("FixedHeapSize").is_err());
830 assert!(GCTriggerSelector::from_str("FixedHeapSize:").is_err());
831 assert!(GCTriggerSelector::from_str("FixedHeapSize:-1").is_err());
832 }
833
834 #[test]
835 fn test_parse_dynamic_heap() {
836 assert_eq!(
837 GCTriggerSelector::from_str("DynamicHeapSize:1024,2048"),
838 Ok(GCTriggerSelector::DynamicHeapSize(1024, 2048))
839 );
840 assert_eq!(
841 GCTriggerSelector::from_str("DynamicHeapSize:1024,1024"),
842 Ok(GCTriggerSelector::DynamicHeapSize(1024, 1024))
843 );
844 assert_eq!(
845 GCTriggerSelector::from_str("DynamicHeapSize:1m,2m"),
846 Ok(GCTriggerSelector::DynamicHeapSize(
847 1024 * 1024,
848 2 * 1024 * 1024
849 ))
850 );
851
852 assert!(GCTriggerSelector::from_str("DynamicHeapSize:1024,1024,").is_err());
854 }
855
856 #[test]
857 fn test_validate() {
858 assert!(GCTriggerSelector::FixedHeapSize(1024).validate());
859 assert!(GCTriggerSelector::DynamicHeapSize(1024, 2048).validate());
860 assert!(GCTriggerSelector::DynamicHeapSize(1024, 1024).validate());
861
862 assert!(!GCTriggerSelector::FixedHeapSize(0).validate());
863 assert!(!GCTriggerSelector::DynamicHeapSize(2048, 1024).validate());
864 }
865}
866
867options! {
868 plan: PlanSelector [always_valid] = PlanSelector::GenImmix,
870 threads: usize [|v: &usize| *v > 0] = num_cpus::get(),
872 use_short_stack_scans: bool [always_valid] = false,
874 use_return_barrier: bool [always_valid] = false,
876 eager_complete_sweep: bool [always_valid] = false,
878 ignore_system_gc: bool [always_valid] = false,
880 nursery: NurserySize [|v: &NurserySize| v.validate()]
887 = NurserySize::ProportionalBounded { min: DEFAULT_PROPORTIONAL_MIN_NURSERY, max: DEFAULT_PROPORTIONAL_MAX_NURSERY },
888 full_heap_system_gc: bool [always_valid] = false,
890 no_finalizer: bool [always_valid] = false,
892 no_reference_types: bool [always_valid] = false,
896 nursery_zeroing: NurseryZeroingOptions [always_valid] = NurseryZeroingOptions::Temporal,
898 stress_factor: usize [always_valid] = DEFAULT_STRESS_FACTOR,
900 analysis_factor: usize [always_valid] = DEFAULT_STRESS_FACTOR,
902 precise_stress: bool [always_valid] = true,
908 vm_space_start: Address [always_valid] = Address::ZERO,
910 vm_space_size: usize [|v: &usize| *v > 0] = 0xdc0_0000,
912 work_perf_events: PerfEventOptions [|_| cfg!(all(feature = "perf_counter", feature = "work_packet_stats"))] = PerfEventOptions {events: vec![]},
919 phase_perf_events: PerfEventOptions [|_| cfg!(feature = "perf_counter")] = PerfEventOptions {events: vec![]},
922 perf_exclude_kernel: bool [|_| cfg!(feature = "perf_counter")] = false,
925 thread_affinity: AffinityKind [|v: &AffinityKind| v.validate()] = AffinityKind::OsDefault,
951 gc_trigger: GCTriggerSelector [|v: &GCTriggerSelector| v.validate()] = GCTriggerSelector::FixedHeapSize((OS::get_system_total_memory().unwrap_or(4 * 1024 * 1024 * 1024) as f64 * 0.5f64) as usize),
954 transparent_hugepages: bool [|v: &bool| !v || cfg!(target_os = "linux")] = false,
957 count_live_bytes_in_gc: bool [always_valid] = false,
959 immix_always_defrag: bool [always_valid] = false,
961 immix_defrag_every_block: bool [always_valid] = false,
964 immix_defrag_headroom_percent: usize [|v: &usize| *v <= 50] = 2,
968 concurrent_immix_disable_concurrent_marking: bool [always_valid] = false
970}
971
972#[cfg(test)]
973mod tests {
974 use super::DEFAULT_STRESS_FACTOR;
975 use super::*;
976 use crate::util::options::Options;
977 use crate::util::test_util::{serial_test, with_cleanup};
978
979 #[test]
980 fn no_env_var() {
981 serial_test(|| {
982 let mut options = Options::default();
983 options.read_env_var_settings();
984 assert_eq!(*options.stress_factor, DEFAULT_STRESS_FACTOR);
985 })
986 }
987
988 #[test]
989 fn with_valid_env_var() {
990 serial_test(|| {
991 with_cleanup(
992 || {
993 std::env::set_var("MMTK_STRESS_FACTOR", "4096");
994
995 let mut options = Options::default();
996 options.read_env_var_settings();
997 assert_eq!(*options.stress_factor, 4096);
998 },
999 || {
1000 std::env::remove_var("MMTK_STRESS_FACTOR");
1001 },
1002 )
1003 })
1004 }
1005
1006 #[test]
1007 fn with_multiple_valid_env_vars() {
1008 serial_test(|| {
1009 with_cleanup(
1010 || {
1011 std::env::set_var("MMTK_STRESS_FACTOR", "4096");
1012 std::env::set_var("MMTK_NO_FINALIZER", "true");
1013
1014 let mut options = Options::default();
1015 options.read_env_var_settings();
1016 assert_eq!(*options.stress_factor, 4096);
1017 assert!(*options.no_finalizer);
1018 },
1019 || {
1020 std::env::remove_var("MMTK_STRESS_FACTOR");
1021 std::env::remove_var("MMTK_NO_FINALIZER");
1022 },
1023 )
1024 })
1025 }
1026
1027 #[test]
1028 fn with_invalid_env_var_value() {
1029 serial_test(|| {
1030 with_cleanup(
1031 || {
1032 std::env::set_var("MMTK_STRESS_FACTOR", "abc");
1034
1035 let mut options = Options::default();
1036 options.read_env_var_settings();
1037 assert_eq!(*options.stress_factor, DEFAULT_STRESS_FACTOR);
1038 },
1039 || {
1040 std::env::remove_var("MMTK_STRESS_FACTOR");
1041 },
1042 )
1043 })
1044 }
1045
1046 #[test]
1047 fn with_invalid_env_var_key() {
1048 serial_test(|| {
1049 with_cleanup(
1050 || {
1051 std::env::set_var("MMTK_ABC", "42");
1053
1054 let mut options = Options::default();
1055 options.read_env_var_settings();
1056 assert_eq!(*options.stress_factor, DEFAULT_STRESS_FACTOR);
1057 },
1058 || {
1059 std::env::remove_var("MMTK_ABC");
1060 },
1061 )
1062 })
1063 }
1064
1065 #[test]
1066 fn ignore_env_var() {
1067 serial_test(|| {
1068 with_cleanup(
1069 || {
1070 std::env::set_var("MMTK_STRESS_FACTOR", "42");
1071
1072 let options = Options::default();
1073 assert_eq!(*options.stress_factor, DEFAULT_STRESS_FACTOR);
1075 },
1076 || {
1077 std::env::remove_var("MMTK_STRESS_FACTOR");
1078 },
1079 )
1080 })
1081 }
1082
1083 #[test]
1084 fn test_str_option_default() {
1085 serial_test(|| {
1086 let options = Options::default();
1087 assert_eq!(
1088 *options.work_perf_events,
1089 PerfEventOptions { events: vec![] }
1090 );
1091 })
1092 }
1093
1094 #[test]
1095 #[cfg(all(feature = "perf_counter", feature = "work_packet_stats"))]
1096 fn test_work_perf_events_option_from_env_var() {
1097 serial_test(|| {
1098 with_cleanup(
1099 || {
1100 std::env::set_var("MMTK_WORK_PERF_EVENTS", "PERF_COUNT_HW_CPU_CYCLES,0,-1");
1101
1102 let mut options = Options::default();
1103 options.read_env_var_settings();
1104 assert_eq!(
1105 *options.work_perf_events,
1106 PerfEventOptions {
1107 events: vec![("PERF_COUNT_HW_CPU_CYCLES".into(), 0, -1)]
1108 }
1109 );
1110 },
1111 || {
1112 std::env::remove_var("MMTK_WORK_PERF_EVENTS");
1113 },
1114 )
1115 })
1116 }
1117
1118 #[test]
1119 #[cfg(all(feature = "perf_counter", feature = "work_packet_stats"))]
1120 fn test_invalid_work_perf_events_option_from_env_var() {
1121 serial_test(|| {
1122 with_cleanup(
1123 || {
1124 std::env::set_var("MMTK_WORK_PERF_EVENTS", "PERF_COUNT_HW_CPU_CYCLES");
1126
1127 let mut options = Options::default();
1128 options.read_env_var_settings();
1129 assert_eq!(
1131 *options.work_perf_events,
1132 PerfEventOptions { events: vec![] }
1133 );
1134 },
1135 || {
1136 std::env::remove_var("MMTK_WORK_PERF_EVENTS");
1137 },
1138 )
1139 })
1140 }
1141
1142 #[test]
1143 #[cfg(not(feature = "perf_counter"))]
1144 fn test_phase_perf_events_option_without_feature() {
1145 serial_test(|| {
1146 with_cleanup(
1147 || {
1148 std::env::set_var("MMTK_PHASE_PERF_EVENTS", "PERF_COUNT_HW_CPU_CYCLES,0,-1");
1150
1151 let mut options = Options::default();
1152 options.read_env_var_settings();
1153 assert_eq!(
1155 *options.work_perf_events,
1156 PerfEventOptions { events: vec![] }
1157 );
1158 },
1159 || {
1160 std::env::remove_var("MMTK_PHASE_PERF_EVENTS");
1161 },
1162 )
1163 })
1164 }
1165
1166 #[test]
1167 fn test_thread_affinity_invalid_option() {
1168 serial_test(|| {
1169 with_cleanup(
1170 || {
1171 std::env::set_var("MMTK_THREAD_AFFINITY", "0-");
1172
1173 let mut options = Options::default();
1174 options.read_env_var_settings();
1175 assert_eq!(*options.thread_affinity, AffinityKind::OsDefault);
1177 },
1178 || {
1179 std::env::remove_var("MMTK_THREAD_AFFINITY");
1180 },
1181 )
1182 })
1183 }
1184
1185 #[cfg(target_os = "linux")]
1186 #[test]
1187 fn test_thread_affinity_single_core() {
1188 serial_test(|| {
1189 with_cleanup(
1190 || {
1191 std::env::set_var("MMTK_THREAD_AFFINITY", "0");
1192
1193 let mut options = Options::default();
1194 options.read_env_var_settings();
1195 assert_eq!(
1196 *options.thread_affinity,
1197 AffinityKind::RoundRobin(vec![0_u16])
1198 );
1199 },
1200 || {
1201 std::env::remove_var("MMTK_THREAD_AFFINITY");
1202 },
1203 )
1204 })
1205 }
1206
1207 #[cfg(target_os = "linux")]
1208 #[test]
1209 fn test_thread_affinity_generate_core_list() {
1210 serial_test(|| {
1211 with_cleanup(
1212 || {
1213 let mut vec = vec![0_u16];
1214 let mut cpu_list = String::new();
1215 let num_cpus = OS::get_total_num_cpus();
1216
1217 cpu_list.push('0');
1218 for cpu in 1..num_cpus {
1219 cpu_list.push_str(format!(",{}", cpu).as_str());
1220 vec.push(cpu);
1221 }
1222
1223 std::env::set_var("MMTK_THREAD_AFFINITY", cpu_list);
1224 let mut options = Options::default();
1225 options.read_env_var_settings();
1226 assert_eq!(*options.thread_affinity, AffinityKind::RoundRobin(vec));
1227 },
1228 || {
1229 std::env::remove_var("MMTK_THREAD_AFFINITY");
1230 },
1231 )
1232 })
1233 }
1234
1235 #[test]
1236 fn test_thread_affinity_single_range() {
1237 serial_test(|| {
1238 let affinity = "0-1".parse::<AffinityKind>();
1239 assert_eq!(affinity, Ok(AffinityKind::RoundRobin(vec![0_u16, 1_u16])));
1240 })
1241 }
1242
1243 #[test]
1244 fn test_thread_affinity_complex_core_list() {
1245 serial_test(|| {
1246 let affinity = "0,1-2,4".parse::<AffinityKind>();
1247 assert_eq!(
1248 affinity,
1249 Ok(AffinityKind::RoundRobin(vec![0_u16, 1_u16, 2_u16, 4_u16]))
1250 );
1251 })
1252 }
1253
1254 #[test]
1255 fn test_thread_affinity_space_in_core_list() {
1256 serial_test(|| {
1257 let affinity = "0,1-2,4, 6".parse::<AffinityKind>();
1258 assert_eq!(
1259 affinity,
1260 Err("Core ids have been incorrectly specified".to_string())
1261 );
1262 })
1263 }
1264
1265 #[test]
1266 fn test_thread_affinity_bad_core_list() {
1267 serial_test(|| {
1268 let affinity = "0,1-2,4,".parse::<AffinityKind>();
1269 assert_eq!(
1270 affinity,
1271 Err("Core ids have been incorrectly specified".to_string())
1272 );
1273 })
1274 }
1275
1276 #[test]
1277 fn test_thread_affinity_range_start_greater_than_end() {
1278 serial_test(|| {
1279 let affinity = "1-0".parse::<AffinityKind>();
1280 assert_eq!(
1281 affinity,
1282 Err("Starting core id in range should be less than the end".to_string())
1283 );
1284 })
1285 }
1286
1287 #[test]
1288 fn test_thread_affinity_bad_range_option() {
1289 serial_test(|| {
1290 let affinity = "0-1-4".parse::<AffinityKind>();
1291 assert_eq!(
1292 affinity,
1293 Err("Core ids have been incorrectly specified".to_string())
1294 );
1295 })
1296 }
1297
1298 #[test]
1299 fn test_thread_affinity_allinset() {
1300 serial_test(|| {
1301 let affinity = "AllInSet:0,1".parse::<AffinityKind>();
1302 assert_eq!(affinity, Ok(AffinityKind::AllInSet(vec![0_u16, 1_u16])));
1303 })
1304 }
1305
1306 #[test]
1307 fn test_thread_affinity_bad_affinity_kind() {
1308 serial_test(|| {
1309 let affinity = "AllIn:0,1".parse::<AffinityKind>();
1310 assert_eq!(affinity, Err("Unknown affinity kind: AllIn".to_string()));
1311 })
1312 }
1313
1314 #[test]
1315 fn test_process_valid() {
1316 serial_test(|| {
1317 let mut options = Options::default();
1318 let success = options.set_from_string("no_finalizer", "true");
1319 assert!(success);
1320 assert!(*options.no_finalizer);
1321 })
1322 }
1323
1324 #[test]
1325 fn test_process_invalid() {
1326 serial_test(|| {
1327 let mut options = Options::default();
1328 let default_no_finalizer = *options.no_finalizer;
1329 let success = options.set_from_string("no_finalizer", "100");
1330 assert!(!success);
1331 assert_eq!(*options.no_finalizer, default_no_finalizer);
1332 })
1333 }
1334
1335 #[test]
1336 fn test_process_bulk_empty() {
1337 serial_test(|| {
1338 let mut options = Options::default();
1339 let success = options.set_bulk_from_string("");
1340 assert!(success);
1341 })
1342 }
1343
1344 #[test]
1345 fn test_process_bulk_valid() {
1346 serial_test(|| {
1347 let mut options = Options::default();
1348 let success = options.set_bulk_from_string("no_finalizer=true stress_factor=42");
1349 assert!(success);
1350 assert!(*options.no_finalizer);
1351 assert_eq!(*options.stress_factor, 42);
1352 })
1353 }
1354
1355 #[test]
1356 fn test_process_bulk_comma_separated_valid() {
1357 serial_test(|| {
1358 let mut options = Options::default();
1359 let success = options.set_bulk_from_string("no_finalizer=true,stress_factor=42");
1360 assert!(success);
1361 assert!(*options.no_finalizer);
1362 assert_eq!(*options.stress_factor, 42);
1363 })
1364 }
1365
1366 #[test]
1367 fn test_process_bulk_invalid() {
1368 serial_test(|| {
1369 let mut options = Options::default();
1370 let success = options.set_bulk_from_string("no_finalizer=true stress_factor=a");
1371 assert!(!success);
1372 })
1373 }
1374
1375 #[test]
1376 fn test_set_typed_option_valid() {
1377 serial_test(|| {
1378 let mut options = Options::default();
1379 let success = options.no_finalizer.set(true);
1380 assert!(success);
1381 assert!(*options.no_finalizer);
1382 })
1383 }
1384
1385 #[test]
1386 fn test_set_typed_option_invalid() {
1387 serial_test(|| {
1388 let mut options = Options::default();
1389 let threads = *options.threads;
1390 let success = options.threads.set(0);
1391 assert!(!success);
1392 assert_eq!(*options.threads, threads);
1393 })
1394 }
1395}