aboutsummaryrefslogtreecommitdiff
path: root/build.rs
diff options
context:
space:
mode:
Diffstat (limited to 'build.rs')
-rw-r--r--build.rs386
1 files changed, 386 insertions, 0 deletions
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..a48351a
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,386 @@
+#[cfg(not(feature = "performance-counter"))]
+fn main() {}
+
+#[cfg(feature = "performance-counter")]
+fn main() {
+ performance_counter::main();
+}
+
+#[cfg(feature = "performance-counter")]
+mod performance_counter {
+
+ extern crate phf_codegen;
+ extern crate serde_json;
+ extern crate csv;
+
+ use std::ascii::AsciiExt;
+ use std::env;
+ use std::fs::File;
+ use std::io::{BufWriter, BufReader, Write};
+ use std::path::Path;
+ use std::collections::HashMap;
+ use std::mem;
+
+ use self::serde_json::Value;
+
+ include!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/perfcnt/intel/description.rs"));
+
+ /// HACK: We need to convert parsed strings to static because we're reusing
+ /// the struct definition which declare strings as static in the generated code.
+ fn string_to_static_str<'a>(s: &'a str) -> &'static str {
+ unsafe {
+ let ret = mem::transmute(&s as &str);
+ mem::forget(s);
+ ret
+ }
+ }
+
+ fn parse_bool(input: &str) -> bool {
+ match input.trim() {
+ "0" => false,
+ "1" => true,
+ _ => panic!("Unknown boolean value {}", input),
+ }
+ }
+
+ fn parse_hex_numbers(split_str_parts: Vec<&str>) -> Vec<u64> {
+ split_str_parts.iter()
+ .map(|x| {
+ assert!(x.starts_with("0x"));
+ match u64::from_str_radix(&x[2..], 16) {
+ Ok(u) => u,
+ Err(e) => panic!("{}: Can not parse {}", e, x)
+ }
+ }).collect()
+ }
+
+ fn parse_number(value_str: &str) -> u64 {
+ if value_str.len() > 2 && value_str[..2].starts_with("0x") {
+ match u64::from_str_radix(&value_str[2..], 16) {
+ Ok(u) => u,
+ Err(e) => panic!("{}: Can not parse {}", e, value_str)
+ }
+ }
+ else {
+ match u64::from_str_radix(&value_str, 10) {
+ Ok(u) => u,
+ Err(e) => panic!("{}: Can not parse {}", e, value_str)
+ }
+ }
+ }
+
+ fn parse_counter_values(value_str: &str) -> u64 {
+ value_str.split(",").map(|x| x.trim())
+ .filter(|x| x.len() > 0)
+ .map(|x| {
+ match u64::from_str_radix(&x, 10) {
+ Ok(u) => u,
+ Err(e) => panic!("{}: Can not parse {} in {}", e, x, value_str)
+ }
+ }).fold(0, |acc, c| { assert!(c < 8); acc | 1 << c })
+ }
+
+ fn parse_null_string(value_str: &str) -> Option<&str> {
+ if value_str != "null" {
+ Some(value_str)
+ }
+ else {
+ None
+ }
+ }
+
+ fn parse_counters(value_str: &str) -> Counter {
+ if value_str.to_lowercase().starts_with("fixed counter") {
+ let mask: u64 = parse_counter_values(&value_str["fixed counter".len()..]);
+ Counter::Fixed(mask as u8)
+ }
+ else if value_str.to_lowercase().starts_with("fixed") {
+ let mask: u64 = parse_counter_values(&value_str["fixed".len()..]);
+ Counter::Fixed(mask as u8)
+ }
+ else {
+ let mask: u64 = parse_counter_values(value_str);
+ Counter::Programmable(mask as u8)
+ }
+ }
+
+ fn parse_pebs(value_str: &str) -> PebsType {
+ match value_str.trim() {
+ "0" => PebsType::Regular,
+ "1" => PebsType::PebsOrRegular,
+ "2" => PebsType::PebsOnly,
+ _ => panic!("Unknown PEBS type: {}", value_str),
+ }
+ }
+
+ fn parse_performance_counters(input: &str, variable: &str, file: &mut BufWriter<File>) {
+ let mut builder = phf_codegen::Map::new();
+ let f = File::open(input).unwrap();
+ let reader = BufReader::new(f);
+ let data: Value = serde_json::from_reader(reader).unwrap();
+ let mut all_events = HashMap::new();
+
+ if data.is_array() {
+ let entries = data.as_array().unwrap();
+ for entry in entries.iter() {
+
+ if !entry.is_object() {
+ panic!("Expected JSON object.");
+ }
+ let pcn = entry.as_object().unwrap();
+
+ let mut event_code = Tuple::One(0);
+ let mut umask = Tuple::One(0);
+ let mut event_name = "";
+ let mut brief_description = "";
+ let mut public_description = None;
+ let mut counter = Counter::Fixed(0);
+ let mut counter_ht_off = Counter::Fixed(0);
+ let mut pebs_counters = None;
+ let mut sample_after_value = 0;
+ let mut msr_index = MSRIndex::None;
+ let mut msr_value = 0;
+ let mut taken_alone = false;
+ let mut counter_mask = 0;
+ let mut invert = false;
+ let mut any_thread = false;
+ let mut edge_detect = false;
+ let mut pebs = PebsType::Regular;
+ let mut precise_store = false;
+ let mut data_la = false;
+ let mut l1_hit_indication = false;
+ let mut errata = None;
+ let mut offcore = false;
+ let mut unit = None;
+ let mut filter = None;
+ let mut extsel = false;
+
+ let mut do_insert: bool = false;
+
+ for (key, value) in pcn.iter() {
+ if !value.is_string() {
+ panic!("Not a string");
+ }
+
+ println!("key = {} value = {}", key, value.as_string().unwrap());
+ let value_string = value.as_string().unwrap();
+ let value_str = string_to_static_str(value_string).trim();
+ let split_str_parts: Vec<&str> = value_string.split(",").map(|x| x.trim()).collect();
+
+ match key.as_str() {
+ "EventName" => {
+ if !all_events.contains_key(value_str.clone()) {
+ all_events.insert(value_str, 0);
+ assert!(all_events.contains_key(value_str));
+ do_insert = true;
+ }
+ else {
+ do_insert = false;
+ println!("WARN: Key {} already exists.", value_str);
+ }
+ event_name = value_str;
+ }
+ "EventCode" => {
+ let split_parts: Vec<u64> = parse_hex_numbers(split_str_parts);
+ match split_parts.len() {
+ 1 => event_code = Tuple::One(split_parts[0] as u8),
+ 2 => event_code = Tuple::Two(split_parts[0] as u8, split_parts[1] as u8),
+ _ => panic!("More than two event codes?")
+ }
+ },
+ "UMask" => {
+ let split_parts: Vec<u64> = parse_hex_numbers(split_str_parts);
+ match split_parts.len() {
+ 1 => umask = Tuple::One(split_parts[0] as u8),
+ 2 => umask = Tuple::Two(split_parts[0] as u8, split_parts[1] as u8),
+ _ => panic!("More than two event codes?")
+ }
+ },
+ "BriefDescription" => brief_description = value_str,
+ "PublicDescription" => {
+ if brief_description != value_str && value_str != "tbd" {
+ public_description = Some(value_str);
+ }
+ else {
+ public_description = None;
+ }
+ },
+ "Counter" => counter = parse_counters(value_str),
+ "CounterHTOff" => counter_ht_off = parse_counters(value_str),
+ "PEBScounters" => pebs_counters = Some(parse_counters(value_str)),
+ "SampleAfterValue" => sample_after_value = parse_number(value_str),
+ "MSRIndex" => {
+ let split_parts: Vec<u64> = value_str
+ .split(",")
+ .map(|x| x.trim())
+ .map(|x| parse_number(x))
+ .collect();
+
+ msr_index = match split_parts.len() {
+ 1 => {
+ if split_parts[0] != 0 {
+ MSRIndex::One(split_parts[0] as u8)
+ }
+ else {
+ MSRIndex::None
+ }
+ },
+ 2 => MSRIndex::Two(split_parts[0] as u8, split_parts[1] as u8),
+ _ => panic!("More than two MSR indexes?")
+ }
+ },
+ "MSRValue" => msr_value = parse_number(value_str),
+ "TakenAlone" => taken_alone = parse_bool(value_str),
+ "CounterMask" => counter_mask = parse_number(value_str) as u8,
+ "Invert" => invert = parse_bool(value_str),
+ "AnyThread" => any_thread = parse_bool(value_str),
+ "EdgeDetect" => edge_detect = parse_bool(value_str),
+ "PEBS" => pebs = parse_pebs(value_str),
+ "PRECISE_STORE" => precise_store = parse_bool(value_str),
+ "Data_LA" => data_la = parse_bool(value_str),
+ "L1_Hit_Indication" => l1_hit_indication = parse_bool(value_str),
+ "Errata" => errata = parse_null_string(value_str),
+ "Offcore" => offcore = parse_bool(value_str),
+ "Unit" => unit = parse_null_string(value_str),
+ "Filter" => filter = parse_null_string(value_str),
+ "ExtSel" => extsel = parse_bool(value_str),
+ "ELLC" => {/* Ignored due to missing documentation. */ },
+ _ => panic!("Unknown member: {} in file {}", key, input),
+ };
+ }
+
+ let ipcd = IntelPerformanceCounterDescription::new(
+ event_code,
+ umask,
+ event_name,
+ brief_description,
+ public_description,
+ counter,
+ counter_ht_off,
+ pebs_counters,
+ sample_after_value,
+ msr_index,
+ msr_value,
+ taken_alone,
+ counter_mask,
+ invert,
+ any_thread,
+ edge_detect,
+ pebs,
+ precise_store,
+ data_la,
+ l1_hit_indication,
+ errata,
+ offcore,
+ unit,
+ filter,
+ extsel
+ );
+
+ //println!("{:?}", ipcd.event_name);
+ if do_insert == true {
+ builder.entry(ipcd.event_name, format!("{:?}", ipcd).as_str());
+ }
+ }
+ }
+ else {
+ panic!("JSON data is not an array.");
+ }
+
+
+ write!(file, "pub const {}: phf::Map<&'static str, IntelPerformanceCounterDescription> = ", variable).unwrap();
+ builder.build(file).unwrap();
+ write!(file, ";\n").unwrap();
+ }
+
+ fn make_file_name<'a>(path: &'a Path) -> (String, String) {
+ let stem = path.file_stem().unwrap().to_str().unwrap();
+
+ // File name without _core*.json
+ println!("{:?}", path);
+ let mut core_start = stem.find("_core");
+ if core_start.is_none() {
+ core_start = stem.find("_uncore");
+ }
+ assert!(!core_start.is_none());
+ let (output_file, _) = stem.split_at(core_start.unwrap());
+
+ // File name without _V*.json at the end:
+ let version_start = stem.find("_V").unwrap();
+ let (variable, _) = stem.split_at(version_start);
+ let uppercase = variable.to_ascii_uppercase();
+ let variable_clean = uppercase.replace("-", "_");
+ let variable_upper = variable_clean.as_str();
+
+ (output_file.to_string(), variable_upper.to_string())
+ }
+
+ pub fn get_file_suffix(file_name: String) -> &'static str {
+ if file_name.contains("_core_") {
+ "core"
+ }
+ else if file_name.contains("_uncore_") {
+ "uncore"
+ }
+ else if file_name.contains("_matrix_") {
+ "matrix"
+ }
+ else if file_name.contains("_FP_ARITH_INST_") {
+ "fparith"
+ }
+ else {
+ panic!("Unknown suffix {}", file_name);
+ }
+ }
+
+ pub fn main() {
+ println!("cargo:rerun-if-changed=build.rs");
+ println!("cargo:rerun-if-changed=x86data/perfmon_data");
+
+ let mut rdr = csv::Reader::from_file("./x86data/perfmon_data/mapfile.csv").unwrap();
+ let mut data_files = HashMap::new();
+
+ // Parse CSV
+ for record in rdr.decode() {
+ let (family_model, version, file_name, event_type): (String, String, String, String) = record.unwrap();
+ // TODO: Parse offcore counter descriptions.
+ if !data_files.contains_key(&file_name) {
+ let suffix = get_file_suffix(file_name.clone());
+ if suffix == "core" || suffix == "uncore" {
+ data_files.insert(file_name.clone(), (family_model + "-" + suffix, version, event_type));
+ }
+ }
+ }
+
+ // Build hash-table to select performance counter for each architecture
+ let path = Path::new(&env::var("OUT_DIR").unwrap()).join("counters.rs");
+ let mut filewriter = BufWriter::new(File::create(&path).unwrap());
+
+ let mut builder = phf_codegen::Map::new();
+ for (file, data) in &data_files {
+ let (ref family_model, _, _): (String, String, String) = *data;
+ let path = Path::new(file.as_str());
+ let (_, ref variable_upper) = make_file_name(&path);
+ builder.entry(family_model.as_str(), format!("{}", variable_upper.as_str()).as_str());
+ }
+
+ write!(&mut filewriter, "pub static {}: phf::Map<&'static str, phf::Map<&'static str, IntelPerformanceCounterDescription>> = ", "COUNTER_MAP").unwrap();
+ builder.build(&mut filewriter).unwrap();
+ write!(&mut filewriter, ";\n").unwrap();
+
+ // Parse all JSON files and write the hash-tables
+ for (file, data) in &data_files {
+ let suffix = get_file_suffix(file.clone());
+ if suffix == "core" || suffix == "uncore" {
+ let (ref family_model, ref version, ref event_type): (String, String, String) = *data;
+ println!("Processing {:?} {} {} {}", file, family_model, version, event_type);
+
+ let path = Path::new(file.as_str());
+ let (_, ref variable_upper) = make_file_name(&path);
+ parse_performance_counters(format!("x86data/perfmon_data{}", file).as_str(),
+ variable_upper, &mut filewriter);
+ }
+ }
+
+ }
+}