text:
#include "stdafx.h"

#include "cpuid/cpuid.hpp"
#include "cache-and-topology.hpp"
#include "features.hpp"
#include "standard.hpp"
#include "hypervisors.hpp"

#include "utility.hpp"

#include <map>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <tuple>

#include <boost/algorithm/string.hpp>

#include <gsl/gsl>

#include <fmt/format.h>

#if defined(_MSC_VER)
#pragma warning(disable: 26446) // warning c26446: Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4).
#endif

namespace xp = boost::xpressive;

namespace cpuid {

#if defined(_MSC_VER)

register_set_t cpuid(leaf_type leaf, subleaf_type subleaf) noexcept {
	register_set_t regs = {};
	std::array<int, 4> raw_regs;
	__cpuidex(raw_regs.data(), gsl::narrow_cast<int>(leaf), gsl::narrow_cast<int>(subleaf));
	regs[eax] = gsl::narrow_cast<std::uint32_t>(raw_regs[eax]);
	regs[ebx] = gsl::narrow_cast<std::uint32_t>(raw_regs[ebx]);
	regs[ecx] = gsl::narrow_cast<std::uint32_t>(raw_regs[ecx]);
	regs[edx] = gsl::narrow_cast<std::uint32_t>(raw_regs[edx]);
	return regs;
}

#else

register_set_t cpuid(leaf_type leaf, subleaf_type subleaf) noexcept {
	register_set_t regs = {};
	std::array<unsigned int, 4> raw_regs;
	__get_cpuid_count(gsl::narrow_cast<int>(leaf), gsl::narrow_cast<int>(subleaf), &raw_regs[eax], &raw_regs[ebx], &raw_regs[ecx], &raw_regs[edx]);
	regs[eax] = gsl::narrow_cast<std::uint32_t>(raw_regs[eax]);
	regs[ebx] = gsl::narrow_cast<std::uint32_t>(raw_regs[ebx]);
	regs[ecx] = gsl::narrow_cast<std::uint32_t>(raw_regs[ecx]);
	regs[edx] = gsl::narrow_cast<std::uint32_t>(raw_regs[edx]);
	return regs;
}

#endif

template<std::size_t N, std::size_t... Is>
constexpr std::array<char, N - 1> to_array(const char(&str)[N], std::index_sequence<Is...>) {
	return { str[Is]... };
}

template<std::size_t N>
constexpr std::array<char, N - 1> to_array(const char(&str)[N]) {
	return to_array(str, std::make_index_sequence<N - 1>{});
}

vendor_type get_vendor_from_name(const register_set_t& regs) {
	static const std::map<std::array<char, 12>, vendor_type> vendors = {
		{ to_array("AMDisbetter!"), vendor_type::amd },
		{ to_array("AuthenticAMD"), vendor_type::amd },
		{ to_array("CentaurHauls"), vendor_type::centaur },
		{ to_array("CyrixInstead"), vendor_type::cyrix },
		{ to_array("GenuineIntel"), vendor_type::intel },
		{ to_array("TransmetaCPU"), vendor_type::transmeta },
		{ to_array("GenuineTMx86"), vendor_type::transmeta },
		{ to_array("Geode by NSC"), vendor_type::nat_semi },
		{ to_array("NexGenDriven"), vendor_type::nexgen },
		{ to_array("RiseRiseRise"), vendor_type::rise },
		{ to_array("SiS SiS SiS "), vendor_type::sis },
		{ to_array("UMC UMC UMC "), vendor_type::umc },
		{ to_array("VIA VIA VIA "), vendor_type::via },
		{ to_array("Vortex86 SoC"), vendor_type::vortex },
	};

	const std::array<char, 12> vndr = bit_cast<decltype(vndr)>(
		std::array<std::uint32_t, 3> {
			regs[ebx],
			regs[edx],
			regs[ecx]
		}
	);

	const auto it = vendors.find(vndr);
	return it != vendors.end() ? it->second : vendor_type::unknown;
}

vendor_type get_hypervisor_from_name(const register_set_t& regs) {
	static const std::map<std::array<char, 12>, vendor_type> vendors = {
		{ to_array("bhyve byhve "),    vendor_type::bhyve },
		{ to_array("KVMKVMKVM\0\0\0"), vendor_type::kvm },
		{ to_array("Microsoft Hv"),    vendor_type::hyper_v },
		{ to_array(" lrpepyh vr\0"),   vendor_type::parallels },
		{ to_array("VMwareVMware"),    vendor_type::vmware },
		{ to_array("XenVMMXenVMM"),    vendor_type::xen_hvm }
	};

	const std::array<char, 12> vndr = bit_cast<decltype(vndr)>(
		std::array<std::uint32_t, 3> {
			regs[ebx],
			regs[ecx],
			regs[edx]
		}
	);

	const auto it = vendors.find(vndr);
	return it != vendors.end() ? it->second : vendor_type::unknown;
}

model_t get_model(vendor_type vendor, const register_set_t& regs) noexcept {
	const split_model_t a = bit_cast<decltype(a)>(regs[eax]);

	model_t model = {};
	model.family = a.family;
	model.model = a.model;
	model.stepping = a.stepping;
	switch(vendor & vendor_type::any_silicon) {
	case vendor_type::intel:
		{
			if(a.family == 0xf) {
				model.family += a.extended_family;
			}
			if(a.family == 0x6 || a.family == 0xf) {
				model.model += (a.extended_model << 4_u32);
			}
		}
		break;
	case vendor_type::amd:
		{
			model.family += a.extended_family;
			model.model += a.extended_model << 4_u32;
		}
		break;
	default:
		break;
	}
	return model;
}

uint8_t get_initial_apic_id(const cpu_t& cpu) {
	const id_info_t b = bit_cast<decltype(b)>(cpu.leaves.at(leaf_type::version_info).at(subleaf_type::main)[ebx]);
	return b.initial_apic_id;
}

uint32_t get_apic_id(const cpu_t& cpu) {
	switch(cpu.vendor & vendor_type::any_silicon) {
	case vendor_type::intel:
		if(cpu.leaves.find(leaf_type::extended_topology_v2) != cpu.leaves.end()) {
			return cpu.leaves.at(leaf_type::extended_topology_v2).at(subleaf_type::main).at(edx);
		} else if(cpu.leaves.find(leaf_type::extended_topology) != cpu.leaves.end()) {
			return cpu.leaves.at(leaf_type::extended_topology).at(subleaf_type::main).at(edx);
		}
		break;
	case vendor_type::amd:
		if(cpu.leaves.find(leaf_type::extended_apic) != cpu.leaves.end()) {
			return cpu.leaves.at(leaf_type::extended_apic).at(subleaf_type::main).at(eax);
		}
		break;
	default:
		break;
	}
	return get_initial_apic_id(cpu);
}

using leaf_print = void(*)(fmt::memory_buffer& out, const cpu_t& cpu);
using leaf_enumerate = void(*)(cpu_t& cpu);

struct filter_t
{
	leaf_type leaf;
	subleaf_type subleaf;
	register_type reg;
	std::uint32_t mask;
};

bool operator==(const filter_t& lhs, const filter_t& rhs) noexcept {
	return lhs.leaf    == rhs.leaf
	    && lhs.subleaf == rhs.subleaf
	    && lhs.reg     == rhs.reg
	    && lhs.mask    == rhs.mask;
}

constexpr static filter_t no_filter = { };

struct leaf_descriptor_t
{
	vendor_type vendor;
	leaf_enumerate enumerator;
	leaf_print printer;
	filter_t filter;
};

void enumerate_null(cpu_t&) noexcept {
}

void print_null(fmt::memory_buffer&, const cpu_t&) noexcept {
}

const std::multimap<leaf_type, leaf_descriptor_t> descriptors = {
	{ leaf_type::basic_info                     , { any                    , nullptr                        , print_basic_info                     , {} } },
	{ leaf_type::version_info                   , { any                    , nullptr                        , print_version_info                   , {} } },
	{ leaf_type::cache_and_tlb                  , { intel                  , nullptr                        , print_cache_tlb_info                 , {} } },
	{ leaf_type::serial_number                  , { intel       | transmeta, nullptr                        , print_serial_number                  , { leaf_type::version_info                   , subleaf_type::main, edx, 0x0004'0000_u32 } } },
	{ leaf_type::deterministic_cache            , { intel                  , enumerate_deterministic_cache  , print_deterministic_cache            , {} } },
	{ leaf_type::monitor_mwait                  , { intel | amd            , nullptr                        , print_mwait_parameters               , { leaf_type::version_info                   , subleaf_type::main, ecx, 0x0000'0008_u32 } } },
	{ leaf_type::thermal_and_power              , { intel | amd            , nullptr                        , print_thermal_and_power              , {} } },
	{ leaf_type::extended_features              , { any                    , enumerate_extended_features    , print_extended_features              , {} } },
	{ leaf_type::reserved_1                     , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::direct_cache_access            , { intel                  , nullptr                        , print_direct_cache_access            , { leaf_type::version_info                   , subleaf_type::main, ecx, 0x0004'0000_u32 } } },
	{ leaf_type::performance_monitoring         , { intel                  , nullptr                        , print_performance_monitoring         , { leaf_type::version_info                   , subleaf_type::main, ecx, 0x0000'8000_u32 } } },
	{ leaf_type::extended_topology              , { intel                  , enumerate_extended_topology    , print_extended_topology              , {} } },
	{ leaf_type::reserved_2                     , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_state                 , { intel | amd            , enumerate_extended_state       , print_extended_state                 , { leaf_type::version_info                   , subleaf_type::main, ecx, 0x0400'0000_u32 } } },
	{ leaf_type::reserved_3                     , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::rdt_monitoring                 , { intel                  , enumerate_rdt_monitoring       , print_rdt_monitoring                 , { leaf_type::extended_features              , subleaf_type::main, ebx, 0x0000'1000_u32 } } },
	{ leaf_type::rdt_allocation                 , { intel                  , enumerate_rdt_allocation       , print_rdt_allocation                 , { leaf_type::extended_features              , subleaf_type::main, ebx, 0x0000'8000_u32 } } },
	{ leaf_type::reserved_4                     , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::sgx_info                       , { intel                  , enumerate_sgx_info             , print_sgx_info                       , { leaf_type::extended_features              , subleaf_type::main, ebx, 0x0000'0004_u32 } } },
	{ leaf_type::reserved_5                     , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::processor_trace                , { intel                  , enumerate_processor_trace      , print_processor_trace                , { leaf_type::extended_features              , subleaf_type::main, ebx, 0x0200'0000_u32 } } },
	{ leaf_type::time_stamp_counter             , { intel                  , nullptr                        , print_time_stamp_counter             , {} } },
	{ leaf_type::processor_frequency            , { intel                  , nullptr                        , print_processor_frequency            , {} } },
	{ leaf_type::system_on_chip_vendor          , { intel                  , enumerate_system_on_chip_vendor, print_system_on_chip_vendor          , {} } },
	{ leaf_type::deterministic_tlb              , { intel                  , enumerate_deterministic_tlb    , print_deterministic_tlb              , {} } },
	{ leaf_type::reserved_6                     , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::reserved_7                     , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::pconfig                        , { any                    , enumerate_pconfig              , print_pconfig                        , { leaf_type::extended_features              , subleaf_type::main, edx, 0x0004'0000_u32 } } },
	{ leaf_type::reserved_8                     , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::reserved_9                     , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::reserved_10                    , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_topology_v2           , { intel                  , enumerate_extended_topology_v2 , print_extended_topology_v2           , {} } },

	{ leaf_type::hypervisor_limit               , { any                    , nullptr                        , print_hypervisor_limit               , {} } },
	{ leaf_type::hyper_v_signature              , { hyper_v                , nullptr                        , print_hyper_v_signature              , {} } },
	{ leaf_type::hyper_v_system_identity        , { hyper_v                , nullptr                        , print_hyper_v_system_identity        , {} } },
	{ leaf_type::hyper_v_features               , { hyper_v                , nullptr                        , print_hyper_v_features               , {} } },
	{ leaf_type::hyper_v_enlightenment_recs     , { hyper_v                , nullptr                        , print_hyper_v_enlightenment_recs     , {} } },
	{ leaf_type::hyper_v_implementation_limits  , { hyper_v                , nullptr                        , print_hyper_v_implementation_limits  , {} } },
	{ leaf_type::hyper_v_implementation_hardware, { hyper_v                , nullptr                        , print_hyper_v_implementation_hardware, {} } },
	{ leaf_type::hyper_v_root_cpu_management    , { hyper_v                , nullptr                        , print_hyper_v_root_cpu_management    , {} } },
	{ leaf_type::hyper_v_shared_virtual_memory  , { hyper_v                , nullptr                        , print_hyper_v_shared_virtual_memory  , {} } },
	{ leaf_type::hyper_v_nested_hypervisor      , { hyper_v                , nullptr                        , print_hyper_v_nested_hypervisor      , {} } },
	{ leaf_type::hyper_v_nested_features        , { hyper_v                , nullptr                        , print_hyper_v_nested_features        , {} } },
	{ leaf_type::xen_limit                      , { xen_hvm                , nullptr                        , print_xen_limit                      , {} } },
	{ leaf_type::xen_version                    , { xen_hvm                , nullptr                        , print_xen_version                    , {} } },
	{ leaf_type::xen_features                   , { xen_hvm                , nullptr                        , print_xen_features                   , {} } },
	{ leaf_type::xen_time                       , { xen_hvm                , enumerate_xen_time             , print_xen_time                       , {} } },
	{ leaf_type::xen_hvm_features               , { xen_hvm                , nullptr                        , print_xen_hvm_features               , {} } },
	{ leaf_type::xen_pv_features                , { xen_hvm                , nullptr                        , print_xen_pv_features                , {} } },
	{ leaf_type::xen_limit_offset               , { xen_hvm                , nullptr                        , print_xen_limit                      , {} } },
	{ leaf_type::xen_version_offset             , { xen_hvm                , nullptr                        , print_xen_version                    , {} } },
	{ leaf_type::xen_features_offset            , { xen_hvm                , nullptr                        , print_xen_features                   , {} } },
	{ leaf_type::xen_time_offset                , { xen_hvm                , enumerate_xen_time             , print_xen_time                       , {} } },
	{ leaf_type::xen_hvm_features_offset        , { xen_hvm                , nullptr                        , print_xen_hvm_features               , {} } },
	{ leaf_type::xen_pv_features_offset         , { xen_hvm                , nullptr                        , print_xen_pv_features                , {} } },
	{ leaf_type::vmware_timing                  , { vmware                 , nullptr                        , print_vmware_timing                  , {} } },
	{ leaf_type::kvm_features                   , { vmware                 , nullptr                        , print_kvm_features                   , {} } },

	{ leaf_type::extended_limit                 , { any                    , nullptr                        , print_extended_limit                 , {} } },
	{ leaf_type::extended_signature_and_features, { any                    , nullptr                        , print_extended_signature_and_features, {} } },
	{ leaf_type::brand_string_0                 , { any                    , nullptr                        , print_brand_string                   , {} } },
	{ leaf_type::brand_string_1                 , { any                    , nullptr                        , print_null                           , {} } },
	{ leaf_type::brand_string_2                 , { any                    , nullptr                        , print_null                           , {} } },
	{ leaf_type::l1_cache_identifiers           , {         amd            , nullptr                        , print_l1_cache_tlb                   , {} } },
	{ leaf_type::l2_cache_identifiers           , { intel | amd            , nullptr                        , print_l2_cache_tlb                   , {} } },
	{ leaf_type::ras_advanced_power_management  , { intel | amd            , nullptr                        , print_ras_advanced_power_management  , {} } },
	{ leaf_type::address_limits                 , { intel | amd            , nullptr                        , print_address_limits                 , {} } },
	{ leaf_type::reserved_11                    , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::secure_virtual_machine         , {         amd            , nullptr                        , print_secure_virtual_machine         , { leaf_type::extended_signature_and_features, subleaf_type::main, ecx, 0x0000'0004_u32 } } },
	{ leaf_type::extended_reserved_1            , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_2            , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_3            , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_4            , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_5            , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_6            , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_7            , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_8            , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_9            , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_10           , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_11           , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_12           , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_13           , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::extended_reserved_14           , { any                    , enumerate_null                 , print_null                           , {} } },
	{ leaf_type::tlb_1g_identifiers             , {         amd            , nullptr                        , print_1g_tlb                         , {} } },
	{ leaf_type::performance_optimization       , {         amd            , nullptr                        , print_performance_optimization       , {} } },
	{ leaf_type::instruction_based_sampling     , {         amd            , nullptr                        , print_instruction_based_sampling     , { leaf_type::extended_signature_and_features, subleaf_type::main, ecx, 0x0000'0400_u32 } } },
	{ leaf_type::lightweight_profiling          , {         amd            , nullptr                        , print_lightweight_profiling          , { leaf_type::extended_signature_and_features, subleaf_type::main, ecx, 0x0000'8000_u32 } } },
	{ leaf_type::cache_properties               , {         amd            , enumerate_cache_properties     , print_cache_properties               , { leaf_type::extended_signature_and_features, subleaf_type::main, ecx, 0x0040'0000_u32 } } },
	{ leaf_type::extended_apic                  , {         amd            , nullptr                        , print_extended_apic                  , { leaf_type::extended_signature_and_features, subleaf_type::main, ecx, 0x0040'0000_u32 } } },
	{ leaf_type::encrypted_memory               , {         amd            , nullptr                        , print_encrypted_memory               , {} } }
};

void print_generic(fmt::memory_buffer& out, const cpu_t& cpu, leaf_type leaf, subleaf_type subleaf) {
	const register_set_t& regs = cpu.leaves.at(leaf).at(subleaf);
	format_to(out, "{:#010x} {:#010x} {:#010x}: {:#010x} {:#010x} {:#010x} {:#010x}\n", cpu.apic_id,
	                                                                               static_cast<std::uint32_t>(leaf),
	                                                                               static_cast<std::uint32_t>(subleaf),
	                                                                               regs[eax],
	                                                                               regs[ebx],
	                                                                               regs[ecx],
	                                                                               regs[edx]);

}

void print_generic(fmt::memory_buffer& out, const cpu_t& cpu, leaf_type leaf) {
	for(const auto& sub : cpu.leaves.at(leaf)) {
		print_generic(out, cpu, leaf, sub.first);
	}
}

void print_generic(fmt::memory_buffer& out, const cpu_t& cpu) {
	for(const auto& leaf : cpu.leaves) {
		print_generic(out, cpu, leaf.first);
	}
}

void enumerate_leaf_brute_force(cpu_t& cpu, leaf_type leaf) {
	register_set_t regs = cpuid(leaf, subleaf_type::main);
	cpu.leaves[leaf][subleaf_type::main] = regs;
	for(subleaf_type subleaf = subleaf_type{ 1 }; ; ++subleaf) {
		register_set_t previous = regs;
		regs = cpuid(leaf, subleaf);
		if(regs[eax] == 0_u32
		&& regs[ebx] == 0_u32
		&& (regs[ecx] == 0_u32 || regs[ecx] == static_cast<std::uint32_t>(subleaf))
		&& regs[edx] == 0_u32) {
			break;
		}
		if(regs[eax] == previous[eax]
		&& regs[ebx] == previous[ebx]
		&& regs[ecx] == previous[ecx]
		&& regs[edx] == previous[edx]) {
			break;
		}
		cpu.leaves[leaf][subleaf] = regs;
	}
}

void enumerate_leaf(cpu_t& cpu, leaf_type leaf, bool brute_force, bool skip_vendor_check, bool skip_feature_check) {
	const auto it = descriptors.find(leaf);
	if(!brute_force
	&& it != descriptors.end()) {
		if(skip_vendor_check
		|| ((it->second.vendor & cpu.vendor) != vendor_type::unknown)) {
			const filter_t filter = it->second.filter;
			if(skip_feature_check
			|| filter      == no_filter
			|| filter.mask == (filter.mask & cpu.leaves.at(filter.leaf).at(filter.subleaf).at(filter.reg))) {
				if(it->second.enumerator) {
					it->second.enumerator(cpu);
				} else {
					cpu.leaves[leaf][subleaf_type::main] = cpuid(leaf, subleaf_type::main);
				}
			}
		}
	} else {
		enumerate_leaf_brute_force(cpu, leaf);
	}
}

std::map<std::uint32_t, cpu_t> enumerate_file(std::istream& fin, file_format format) {
	std::map<std::uint32_t, cpu_t> logical_cpus;

	switch(format) {
	case file_format::native:
		{
			static const xp::sregex comment_line(xp::sregex::compile("#.*"));
			const std::string single_element = "(0[xX][[:xdigit:]]{1,8})";
			const std::string multiple_elements = fmt::format("{} {} {}: {} {} {} {}", single_element, single_element, single_element, single_element, single_element, single_element, single_element);
			static const xp::sregex data_line(xp::sregex::compile(multiple_elements));

			std::string line;
			while(std::getline(fin, line)) {
				xp::smatch m;
				if(xp::regex_search(line, m, comment_line) || line == "") {
					continue;
				} else if(xp::regex_search(line, m, data_line)) {
					const std::uint32_t apic_id =                           std::stoul(m[1].str(), nullptr, 16) ;
					const leaf_type     leaf    = static_cast<leaf_type   >(std::stoul(m[2].str(), nullptr, 16));
					const subleaf_type  subleaf = static_cast<subleaf_type>(std::stoul(m[3].str(), nullptr, 16));
					const register_set_t regs   = {
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[4].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[5].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[6].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[7].str(), nullptr, 16))
					};
					logical_cpus[apic_id].leaves[leaf][subleaf] = regs;
				} else {
					//std::cerr << "Unrecognized line: " << line << std::endl;
				}
			}
		}
		break;
	case file_format::etallen:
		{
			static const xp::sregex comment_line(xp::sregex::compile("#.*"));
			static const xp::sregex solo_cpu_line(xp::sregex::compile("CPU:"));
			static const xp::sregex cpu_line(xp::sregex::compile("CPU ([[:digit:]]+):"));
			const std::string single_element = "(0[xX][[:xdigit:]]{1,8})";
			const std::string multiple_elements = fmt::format("   {} {}: eax={} ebx={} ecx={} edx={}", single_element, single_element, single_element, single_element, single_element, single_element, single_element);
			static const xp::sregex data_line(xp::sregex::compile(multiple_elements));
			std::string line;
			std::uint32_t current_cpu = 0xffff'ffff_u32;
			while(std::getline(fin, line)) {
				xp::smatch m;
				if(xp::regex_search(line, m, comment_line) || line == "") {
					continue;
				} else if(xp::regex_search(line, m, solo_cpu_line)) {
					++current_cpu;
				} else if(xp::regex_search(line, m, cpu_line)) {
					current_cpu = std::stoul(m[1].str());
				} else if(xp::regex_search(line, m, data_line)) {
					const leaf_type      leaf    = static_cast<leaf_type   >(std::stoul(m[1].str(), nullptr, 16));
					const subleaf_type   subleaf = static_cast<subleaf_type>(std::stoul(m[2].str(), nullptr, 16));
					const register_set_t regs    = {
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[3].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[4].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[5].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[6].str(), nullptr, 16))
					};
					logical_cpus[current_cpu].leaves[leaf][subleaf] = regs;
				} else {
					//std::cerr << "Unrecognized line: " << line << std::endl;
				}
			}
		}
		break;
	case file_format::libcpuid:
		{
			// this is a crappy file format
			static const xp::sregex version_line(xp::sregex::compile("version=.*"));
			static const xp::sregex build_line(xp::sregex::compile("build_date=.*"));
			static const xp::sregex delimiter_line(xp::sregex::compile("-{80}"));
			const std::string single_element = "([[:xdigit:]]{8})";
			const std::string multiple_elements = fmt::format("([[:alnum:]_]+)\\[([[:digit:]]+)\\]={} {} {} {}", single_element, single_element, single_element, single_element);
			static const xp::sregex data_line(xp::sregex::compile(multiple_elements));
			constexpr std::uint32_t current_cpu = 0xffff'ffff_u32;
			std::string line;
			while(std::getline(fin, line)) {
				xp::smatch m;
				if(xp::regex_search(line, m, version_line)
				|| xp::regex_search(line, m, build_line)) {
					continue;
				} else if(xp::regex_search(line, m, data_line)) {
					std::string section       = m[1].str();
					std::uint32_t idx         = std::stoul(m[2].str());
					const register_set_t regs = {
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[3].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[4].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[5].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[6].str(), nullptr, 16))
					};
					leaf_type       leaf{ idx };
					subleaf_type subleaf{ idx };
					if(section == "basic_cpuid") {
						subleaf = subleaf_type::main;
					} else if(section == "ext_cpuid") {
						leaf = static_cast<leaf_type>(0x8000'0000_u32 + idx);
						subleaf = subleaf_type::main;
					} else if(section == "intel_fn4") {
						leaf = leaf_type::deterministic_cache;
						if((regs[eax] & 0x0000'001f_u32) == 0) {
							continue;
						}
					} else if(section == "intel_fn11") {
						leaf = leaf_type::extended_topology;
						if((regs[ecx] & 0x0000'ff00_u32) == 0_u32) {
							continue;
						}
					} else if(section == "intel_fn12h") {
						leaf = leaf_type::sgx_info;
						if((regs[eax] & 0x0000'000f_u32) == 0_u32) {
							continue;
						}
					} else if(section == "intel_fn14h") {
						leaf = leaf_type::processor_trace;
					}
					logical_cpus[current_cpu].leaves[leaf][subleaf] = regs;
				} else if(xp::regex_search(line, m, delimiter_line)) {
					break;
				} else {
					//std::cerr << "Unrecognized line: " << line << std::endl;
				}
			}
			const leaf_type highest_leaf          = leaf_type{ logical_cpus[current_cpu].leaves[leaf_type::basic_info    ][subleaf_type::main][eax] };
			const leaf_type highest_extended_leaf = leaf_type{ logical_cpus[current_cpu].leaves[leaf_type::extended_limit][subleaf_type::main][eax] };
			leaves_t corrected_leaves;
			for(leaf_type leaf = leaf_type::basic_info; leaf <= highest_leaf; ++leaf) {
				corrected_leaves[leaf] = logical_cpus[current_cpu].leaves[leaf];
			}
			for(leaf_type leaf = leaf_type::extended_limit; leaf <= highest_extended_leaf; ++leaf) {
				corrected_leaves[leaf] = logical_cpus[current_cpu].leaves[leaf];
			}
			if(corrected_leaves.find(leaf_type::processor_trace) != corrected_leaves.end()) {
				subleaves_t subleaves = corrected_leaves.at(leaf_type::processor_trace);
				const subleaf_type limit = subleaf_type{ subleaves[subleaf_type::main][eax] };
				for(subleaf_type sub = subleaf_type{ 1 }; sub < limit; ++sub) {
					if(subleaves.find(sub) != subleaves.end()) {
						subleaves[sub] = { 0x0_u32, 0x0_u32, 0x0_u32, 0x0_u32 };
					}
				}
				subleaves.erase(subleaves.lower_bound(limit), subleaves.end());
			}
			logical_cpus[current_cpu].leaves.swap(corrected_leaves);
		}
		break;
	case file_format::aida64:
		{
			const std::string single_element = "([[:xdigit:]]{8})";
			const std::string simple = fmt::format("CPUID {}: {}-{}-{}-{}", single_element, single_element, single_element, single_element, single_element);
			static const xp::sregex simple_line(xp::sregex::compile(simple));
			static const xp::sregex subleaf_line(xp::sregex::compile(fmt::format("{} \\[SL ([[:digit:]]{{2}})\\]", simple)));
			static const xp::sregex description_line(xp::sregex::compile(fmt::format("{} \\[.*\\]", simple)));
			static const xp::sregex allcpu_line(xp::sregex::compile("allcpu:.*"));
			static const xp::sregex affinity_mask_line(xp::sregex::compile("CPU#.*"));
			static const xp::sregex registers_line(xp::sregex::compile("CPUID Registers.*"));

			std::string line;
			std::uint32_t current_cpu = 0xffff'ffff_u32;
			while(std::getline(fin, line)) {
				xp::smatch m;
				if(xp::regex_search(line, m, allcpu_line)
				|| xp::regex_search(line, m, affinity_mask_line)
				|| xp::regex_search(line, m, registers_line)) {
					++current_cpu;
				} else if(xp::regex_search(line, m, subleaf_line)) {
					const leaf_type      leaf    = static_cast<leaf_type   >(std::stoul(m[1].str(), nullptr, 16));
					const subleaf_type   subleaf = static_cast<subleaf_type>(std::stoul(m[6].str(), nullptr, 10));
					const register_set_t regs    = {
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[2].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[3].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[4].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[5].str(), nullptr, 16))
					};
					logical_cpus[current_cpu].leaves[leaf][subleaf] = regs;
				} else if(xp::regex_search(line, m, description_line)
				       || xp::regex_search(line, m, simple_line)) {
					const auto get_subleaf = [&logical_cpus](const std::uint32_t current_cpu, const leaf_type leaf) -> subleaf_type {
						if(logical_cpus.find(current_cpu) != logical_cpus.end()
						&& logical_cpus[current_cpu].leaves.find(leaf) != logical_cpus[current_cpu].leaves.end()
						&& logical_cpus[current_cpu].leaves[leaf].find(subleaf_type::main) != logical_cpus[current_cpu].leaves[leaf].end()) {
							return static_cast<subleaf_type>(gsl::narrow_cast<std::uint32_t>(logical_cpus[current_cpu].leaves[leaf].size()) + 1_u32);
						} else {
							return subleaf_type::main;
						}
					};
					const leaf_type         leaf    = static_cast<leaf_type   >(std::stoul(m[1].str(), nullptr, 16));
					if(leaf == leaf_type::basic_info
					&& logical_cpus.find(current_cpu) != logical_cpus.end()
					&& logical_cpus[current_cpu].leaves.find(leaf) != logical_cpus[current_cpu].leaves.end()) {
						++current_cpu;
					}
					const subleaf_type   subleaf = get_subleaf(current_cpu, leaf);
					const register_set_t regs    = {
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[2].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[3].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[4].str(), nullptr, 16)),
						gsl::narrow_cast<std::uint32_t>(std::stoul(m[5].str(), nullptr, 16))
					};
					logical_cpus[current_cpu].leaves[leaf][subleaf] = regs;
				} else {
					//std::cerr << "Unrecognized line: " << line << std::endl;
				}
			}
		}
		break;
	case file_format::cpuinfo:
		throw std::runtime_error("/proc/cpuinfo is not allowed as an input format");
		break;
	}

	for(auto& c: logical_cpus) {
		cpu_t& cpu = c.second;
		register_set_t regs = {};

		regs = cpu.leaves.at(leaf_type::basic_info).at(subleaf_type::main);
		cpu.vendor = get_vendor_from_name(regs);

		regs = cpu.leaves.at(leaf_type::version_info).at(subleaf_type::main);
		cpu.model = get_model(cpu.vendor, regs);

		if(cpu.leaves.find(leaf_type::hypervisor_limit) != cpu.leaves.end()) {
			regs = cpu.leaves.at(leaf_type::hypervisor_limit).at(subleaf_type::main);
			if(regs[eax] != 0_u32) {
				const vendor_type hypervisor = get_hypervisor_from_name(regs);
				// something is set, and it looks like a hypervisor
				if((hypervisor & vendor_type::any_hypervisor) != vendor_type::unknown) {
					cpu.vendor = cpu.vendor | hypervisor;

					if((hypervisor & vendor_type::hyper_v) != vendor_type::unknown) {
						// xen with viridian extensions masquerades as hyper-v, and puts its own cpuid leaves 0x100 further up
						if(cpu.leaves.find(leaf_type::xen_limit_offset) != cpu.leaves.end()) {
							regs = cpu.leaves.at(leaf_type::xen_limit_offset).at(subleaf_type::main);
							const vendor_type xen_hypervisor = get_hypervisor_from_name(regs);

							if((xen_hypervisor & vendor_type::xen_hvm) != vendor_type::unknown) {
								cpu.vendor = cpu.vendor | xen_hypervisor;
							}
						}
					}
				}
			}
		}
		cpu.apic_id = get_apic_id(cpu);
	}
	if(format != file_format::native) {
		std::map<std::uint32_t, cpu_t> corrected_ids;
		for(auto& p : logical_cpus) {
			if(corrected_ids.find(p.second.apic_id) != corrected_ids.end()) {
				// duplicate APIC IDs: this happens on old multisocket systems
				// so let's just synthesize some garbage APIC IDs
				p.second.apic_id = gsl::narrow<std::uint32_t>(corrected_ids.size());
			}
			corrected_ids[p.second.apic_id] = p.second;
		}
		logical_cpus.swap(corrected_ids);
	}
	return logical_cpus;
}

std::map<std::uint32_t, cpu_t> enumerate_processors(bool brute_force, bool skip_vendor_check, bool skip_feature_check) {
	std::map<std::uint32_t, cpu_t> logical_cpus;
	run_on_every_core([=, &logical_cpus]() {
		cpu_t cpu = {};
		register_set_t regs = cpuid(leaf_type::basic_info, subleaf_type::main);
		const leaf_type highest_leaf = leaf_type{ regs[eax] };
		cpu.vendor = get_vendor_from_name(regs);

		regs = cpuid(leaf_type::version_info, subleaf_type::main);
		cpu.model = get_model(cpu.vendor, regs);

		for(leaf_type leaf = leaf_type::basic_info; leaf <= highest_leaf; ++leaf) {
			enumerate_leaf(cpu, leaf, brute_force, skip_vendor_check, skip_feature_check);
		}

		regs = cpuid(leaf_type::hypervisor_limit, subleaf_type::main);
		if(regs[eax] != 0_u32) {
			const vendor_type hypervisor = get_hypervisor_from_name(regs);
			// something is set, and it looks like a hypervisor
			if((hypervisor & vendor_type::any_hypervisor) != vendor_type::unknown) {
				cpu.vendor = cpu.vendor | hypervisor;
				const leaf_type highest_hypervisor_leaf = leaf_type{ regs[eax] };

				for(leaf_type leaf = leaf_type::hypervisor_limit; leaf <= highest_hypervisor_leaf; ++leaf) {
					enumerate_leaf(cpu, leaf, brute_force, skip_vendor_check, skip_feature_check);
				}

				if((hypervisor & vendor_type::hyper_v) != vendor_type::unknown) {
					// xen with viridian extensions masquerades as hyper-v, and puts its own cpuid leaves 0x100 further up
					regs = cpuid(leaf_type::xen_limit_offset, subleaf_type::main);
					const vendor_type xen_hypervisor = get_hypervisor_from_name(regs);

					if((xen_hypervisor & vendor_type::xen_hvm) != vendor_type::unknown) {
						cpu.vendor                       = cpu.vendor | xen_hypervisor;
						const leaf_type xen_base         = leaf_type::xen_limit_offset;
						const leaf_type highest_xen_leaf = leaf_type{ regs[eax] };

						for(leaf_type leaf = xen_base; leaf <= highest_xen_leaf; ++leaf) {
							enumerate_leaf(cpu, leaf, brute_force, skip_vendor_check, skip_feature_check);
						}
					}
				}
			}
		}
		regs = cpuid(leaf_type::extended_limit, subleaf_type::main);
		const leaf_type highest_extended_leaf = leaf_type{ regs[eax] };

		for(leaf_type leaf = leaf_type::extended_limit; leaf <= highest_extended_leaf; ++leaf) {
			enumerate_leaf(cpu, leaf, brute_force, skip_vendor_check, skip_feature_check);
		}

		cpu.apic_id = get_apic_id(cpu);
		logical_cpus[cpu.apic_id] = cpu;
	});
	return logical_cpus;
}

flag_spec_t parse_flag_spec(const std::string& flag_description) {
	const std::string hex_number       = "(?:0x)?([[:xdigit:]]+)H?";

	const std::string simple_selector  = "(?:(?:EAX=)?{hex_number:s})";
	const std::string complex_selector = R"((?:(?:\(EAX={hex_number:s}, ?ECX={hex_number:s}\))|(?:{hex_number:s}\.{hex_number:s})))";
	const std::string selector         = "(?:{simple_selector:s}|{complex_selector:s})";
	const std::string full_selector    = fmt::format(fmt::format(selector, fmt::arg("simple_selector", simple_selector),
	                                                                       fmt::arg("complex_selector", complex_selector)), fmt::arg("hex_number", hex_number));

	const std::string reg              = "(EAX|EBX|ECX|EDX)";

	const std::string flagname         = "([[:alnum:]\\._-]+)";
	const std::string single_bit       = "(?:bit )?([[:digit:]]+)";
	const std::string bit_range        = "(?:bits )?([[:digit:]]+)[-:]([[:digit:]]+)";
	const std::string bitspec          = "(?:{single_bit:s}|{bit_range:s})";
	const std::string full_bitspec     = fmt::format(bitspec, fmt::arg("single_bit", single_bit), fmt::arg("bit_range", bit_range));
	const std::string open_bracket     = R"((?:\(|\[))";
	const std::string close_bracket    = R"((?:\)|\]))";
	const std::string field            = R"((?:(\.{flagname:s} ?{open_bracket:s}{bitspec:s}{close_bracket:s})|(?:\.{flagname:s})|({open_bracket:s}{bitspec:s}{close_bracket:s})|(?:{open_bracket:s}{flagname:s}{close_bracket:s}))?)";
	const std::string full_field       = fmt::format(field, fmt::arg("flagname", flagname), fmt::arg("bitspec", full_bitspec), fmt::arg("open_bracket", open_bracket), fmt::arg("close_bracket", close_bracket));

	const std::string pattern          = R"(CPUID\.{selector:s}(?:\.|:){reg:s}{field:s})";

	const std::string full_pattern     = fmt::format(pattern, fmt::arg("selector", full_selector), fmt::arg("reg", reg), fmt::arg("field", full_field));

	static xp::sregex re_pattern(xp::sregex::compile(full_pattern, xp::sregex::optimize | xp::sregex::icase));
	xp::smatch m;
	if(!xp::regex_search(flag_description, m, re_pattern)) {
		throw std::runtime_error(fmt::format("Bad pattern: {:s}", flag_description));
	}

	flag_spec_t spec = {};

	if(m[1].matched) {
		spec.selector_eax = std::stoul(m[1].str(), nullptr, 16);
	} else if(m[2].matched) {
		spec.selector_eax = std::stoul(m[2].str(), nullptr, 16);
		spec.selector_ecx = m[3].matched ? std::stoul(m[3].str(), nullptr, 16) : 0_u32;
	} else if(m[4].matched) {
		spec.selector_eax = std::stoul(m[4].str(), nullptr, 16);
		spec.selector_ecx = m[5].matched ? std::stoul(m[5].str(), nullptr, 16) : 0_u32;
	}

	const std::string reg_name = boost::algorithm::to_lower_copy(m[6].str());
	if(reg_name == "eax") {
		spec.flag_register = eax;
	} else if(reg_name == "ebx") {
		spec.flag_register = ebx;
	} else if(reg_name == "ecx") {
		spec.flag_register = ecx;
	} else if(reg_name == "edx") {
		spec.flag_register = edx;
	}

	if(m[7].matched) {
		spec.flag_name = m[8].str();
		if(m[9].matched) {
			spec.flag_start = std::stoul(m[9].str());
			spec.flag_end   = spec.flag_start;
		} else if(m[10].matched) {
			spec.flag_start = std::stoul(m[11].str());
			spec.flag_end   = std::stoul(m[10].str());
		}
	} else if(m[12].matched) {
		spec.flag_name = m[12].str();
	} else if(m[13].matched) {
		if(m[14].matched) {
			spec.flag_start = std::stoul(m[14].str());
			spec.flag_end   = spec.flag_start;
		} else if(m[15].matched) {
			spec.flag_start = std::stoul(m[16].str());
			spec.flag_end   = std::stoul(m[15].str());
		}
	}
	else if(m[17].matched) {
		spec.flag_name = m[17].str();
	}
	boost::algorithm::to_lower(spec.flag_name);

	return spec;
}

std::string to_string(register_type reg) {
	switch(reg) {
	case eax:
		return "EAX";
	case ebx:
		return "EBX";
	case ecx:
		return "ECX";
	case edx:
		return "EDX";
	default:
		UNREACHABLE();
	}
}

std::string to_string(const flag_spec_t& spec) {
	// named flag, unknown bit:
	// CPUID.(EAX=xxH, ECX=xxH):reg.name
	// named flag, specific bit:
	// CPUID.(EAX=xxH, ECX=xxH):reg.name[yy]
	// named flag, multibit:
	// CPUID.(EAX=xxH, ECX=xxH):reg.name[high:low]
	// unnamed flag, unknown bit:
	// CPUID.(EAX=xxH, ECX=xxH):reg
	// unnamed flag, single bit:
	// CPUID.(EAX=xxH, ECX=xxH):reg[yy]
	// unnamed flag, multibit:
	// CPUID.(EAX=xxH, ECX=xxH):reg[high:low]
	if(spec.flag_name != "") {
		if(spec.flag_start == 0xffff'ffff_u32 && spec.flag_end == 0xffff'ffff_u32) {
			return fmt::format("CPUID.(EAX={:02X}H, ECX={:02X}H):{:s}.{:s}"       , spec.selector_eax, spec.selector_ecx, to_string(spec.flag_register), boost::algorithm::to_upper_copy(spec.flag_name));
		} else if(spec.flag_start == spec.flag_end) {
			return fmt::format("CPUID.(EAX={:02X}H, ECX={:02X}H):{:s}.{:s}[{}]"   , spec.selector_eax, spec.selector_ecx, to_string(spec.flag_register), boost::algorithm::to_upper_copy(spec.flag_name), spec.flag_end);
		} else {
			return fmt::format("CPUID.(EAX={:02X}H, ECX={:02X}H):{:s}.{:s}[{}:{}]", spec.selector_eax, spec.selector_ecx, to_string(spec.flag_register), boost::algorithm::to_upper_copy(spec.flag_name), spec.flag_end, spec.flag_start);
		}
	} else {
		if(spec.flag_start == 0xffff'ffff_u32 && spec.flag_end == 0xffff'ffff_u32) {
			return fmt::format("CPUID.(EAX={:02X}H, ECX={:02X}H):{:s}"            , spec.selector_eax, spec.selector_ecx, to_string(spec.flag_register));
		} else if(spec.flag_start == spec.flag_end) {
			return fmt::format("CPUID.(EAX={:02X}H, ECX={:02X}H):{:s}[{}]"        , spec.selector_eax, spec.selector_ecx, to_string(spec.flag_register),                                                  spec.flag_end);
		} else {
			return fmt::format("CPUID.(EAX={:02X}H, ECX={:02X}H):{:s}[{}:{}]"     , spec.selector_eax, spec.selector_ecx, to_string(spec.flag_register),                                                  spec.flag_end, spec.flag_start);
		}
	}
}

void print_single_flag(fmt::memory_buffer& out, const cpu_t& cpu, const flag_spec_t& spec) {
	const std::string flag_description = to_string(spec);
	const std::string flag_name_alternative = boost::algorithm::replace_all_copy(spec.flag_name, "_", ".");

	const leaf_type    leaf    = static_cast<leaf_type   >(spec.selector_eax);
	const subleaf_type subleaf = static_cast<subleaf_type>(spec.selector_ecx);
	bool handled = false;
	if(cpu.leaves.find(leaf) != cpu.leaves.end()
	&& cpu.leaves.at(leaf).find(subleaf) != cpu.leaves.at(leaf).end()) {
		const std::uint32_t value = cpu.leaves.at(leaf).at(subleaf).at(spec.flag_register);
		if(spec.flag_name == "" && spec.flag_start == 0xffff'ffff_u32 && spec.flag_end == 0xffff'ffff_u32) {
			format_to(out, "cpu {:#04x} {:s}: {:#010x}\n", cpu.apic_id, flag_description, value);
			handled = true;
		}
		if(spec.flag_name != "") {
			const auto range = all_features.equal_range(leaf);
			for(auto it = range.first; it != range.second; ++it) {
				if(it->second.find(subleaf) == it->second.end()) {
					continue;
				}
				auto sub = it->second.at(subleaf);
				if(sub.find(spec.flag_register) == sub.end()) {
					continue;
				}
				for(const feature_t& feature : sub.at(spec.flag_register)) {
					const std::string lower_mnemonic = boost::algorithm::to_lower_copy(feature.mnemonic);
					if(lower_mnemonic == spec.flag_name
					|| lower_mnemonic == flag_name_alternative) {
						unsigned long shift_amount = 0;
						bit_scan_forward(&shift_amount, feature.mask);
						const std::uint32_t result = (value & feature.mask) >> shift_amount;

						format_to(out, "cpu {:#04x} {:s}: {:#010x}\n", cpu.apic_id, flag_description, result);
						handled = true;
						break;
					}
				}
			}
		}
		if(!handled && spec.flag_start != 0xffff'ffff_u32 && spec.flag_end != 0xffff'ffff_u32) {
			const std::uint32_t mask = range_mask(spec.flag_start, spec.flag_end);
			unsigned long shift_amount = 0;
			bit_scan_forward(&shift_amount, mask);
			const std::uint32_t result = (value & mask) >> shift_amount;
			format_to(out, "cpu {:#04x} {:s}: {:#010x}\n", cpu.apic_id, flag_description, result);
			handled = true;
		}
	}

	if(!handled) {
		format_to(out, "No data found for {:s}\n", flag_description);
	}
}

void print_leaf(fmt::memory_buffer& out, const cpu_t& cpu, leaf_type leaf, bool skip_vendor_check, bool skip_feature_check) {
	const auto range = descriptors.equal_range(leaf);
	if(range.first != range.second) {
		for(auto it = range.first; it != range.second; ++it) {
			if(skip_vendor_check || ((it->second.vendor & cpu.vendor) != vendor_type::unknown)) {
				const filter_t filter = it->second.filter;
				if(skip_feature_check
				|| filter == no_filter
				|| filter.mask == (filter.mask & cpu.leaves.at(filter.leaf).at(filter.subleaf).at(filter.reg))) {
					if(it->second.printer) {
						it->second.printer(out, cpu);
					} else {
						print_generic(out, cpu, leaf);
					}
				}
			}
		}
	} else {
		print_generic(out, cpu, leaf);
		format_to(out, "\n");
	}
}

void print_leaves(fmt::memory_buffer& out, const cpu_t& cpu, bool skip_vendor_check, bool skip_feature_check) {
	for(const auto& leaf : cpu.leaves) {
		print_leaf(out, cpu, leaf.first, skip_vendor_check, skip_feature_check);
	}
}

void print_dump(fmt::memory_buffer& out, std::map<std::uint32_t, cpu_t> logical_cpus, file_format format) {
	switch(format) {
	case file_format::native:
		format_to(out, "#apic eax ecx: eax ebx ecx edx\n");
		for(const auto& p : logical_cpus) {
			print_generic(out, p.second);
			format_to(out, "\n");
		}
		break;
	case file_format::etallen:
		{
			std::uint32_t count = 0_u32;
			for(const auto& c : logical_cpus) {
				format_to(out, "CPU {:d}:\n", count);
				for(const auto& l : c.second.leaves) {
					for(const auto& s : l.second) {
						const cpu_t& cpu = c.second;
						const leaf_type leaf = l.first;
						const subleaf_type subleaf = s.first;
						const register_set_t& regs = cpu.leaves.at(leaf).at(subleaf);
						format_to(out, "   {:#010x} {:#04x}: eax={:#010x} ebx={:#010x} ecx={:#010x} edx={:#010x}\n", static_cast<std::uint32_t>(leaf),
						                                                                          static_cast<std::uint32_t>(subleaf),
						                                                                          regs[eax],
						                                                                          regs[ebx],
						                                                                          regs[ecx],
						                                                                          regs[edx]);
					}
				}
				++count;
			}
		}
		break;
	case file_format::libcpuid:
		{
			// this is a crappy file format
			format_to(out, "version=0.4.0\n");
			const cpu_t& cpu = logical_cpus.begin()->second;

			for(std::uint32_t i = 0_u32; i < 32_u32; ++i) {
				const leaf_type leaf{ i };
				if(cpu.leaves.find(leaf) != cpu.leaves.end()) {
					const register_set_t& regs = cpu.leaves.at(leaf).at(subleaf_type::main);
					format_to(out, "basic_cpuid[{:d}]={:08x} {:08x} {:08x} {:08x}\n", i, regs[eax], regs[ebx], regs[ecx], regs[edx]);
				} else {
					format_to(out, "basic_cpuid[{:d}]={:08x} {:08x} {:08x} {:08x}\n", i, 0_u32, 0_u32, 0_u32, 0_u32);
				}
			}
			for(std::uint32_t i = 0_u32; i < 32_u32; ++i) {
				const leaf_type leaf{ i + 0x8000'0000_u32 };
				if(cpu.leaves.find(leaf) != cpu.leaves.end()) {
					const register_set_t& regs = cpu.leaves.at(leaf).at(subleaf_type::main);
					format_to(out, "ext_cpuid[{:d}]={:08x} {:08x} {:08x} {:08x}\n", i, regs[eax], regs[ebx], regs[ecx], regs[edx]);
				} else {
					format_to(out, "ext_cpuid[{:d}]={:08x} {:08x} {:08x} {:08x}\n", i, 0_u32, 0_u32, 0_u32, 0_u32);
				}
			}

			const auto print_detailed_leaves = [&out, &cpu](const leaf_type leaf, const std::uint32_t limit, const std::string& label) {
				if(cpu.leaves.find(leaf) != cpu.leaves.end()) {
					const subleaves_t& subleaves = cpu.leaves.at(leaf);
					std::uint32_t i = 0_u32;
					for(const auto& s : subleaves) {
						const register_set_t& regs = s.second;
						format_to(out, "{:s}[{:d}]={:08x} {:08x} {:08x} {:08x}\n", label, i, regs[eax], regs[ebx], regs[ecx], regs[edx]);
						++i;
					}
					for(; i < limit; ++i) {
						format_to(out, "{:s}[{:d}]={:08x} {:08x} {:08x} {:08x}\n", label, i, 0_u32, 0_u32, 0_u32, 0_u32);
					}
				}
			};

			print_detailed_leaves(leaf_type::deterministic_cache, 8, "intel_fn4");
			print_detailed_leaves(leaf_type::extended_topology, 4, "intel_fn11");
			print_detailed_leaves(leaf_type::sgx_info, 4, "intel_fn12h");
			print_detailed_leaves(leaf_type::processor_trace, 4, "intel_fn14h");
		}
		break;
	case file_format::aida64:
		{
			std::uint32_t count = 0_u32;
			for(const auto& c : logical_cpus) {
				format_to(out, "CPU#{:0=03d}:\n", count);
				const cpu_t& cpu = c.second;
				for(const auto& l : c.second.leaves) {
					const leaf_type leaf = l.first;
					if(l.second.size() == 1) {
						const register_set_t& regs = cpu.leaves.at(leaf).at(subleaf_type::main);
						format_to(out, "CPUID {:08x}: {:08x}-{:08x}-{:08x}-{:08x}\n", static_cast<std::uint32_t>(leaf),
						                                                              regs[eax],
						                                                              regs[ebx],
						                                                              regs[ecx],
						                                                              regs[edx]);
					} else {
						for(const auto& s : l.second) {
							const subleaf_type subleaf = s.first;
							const register_set_t& regs = cpu.leaves.at(leaf).at(subleaf);
							format_to(out, "CPUID {:08x}: {:08x}-{:08x}-{:08x}-{:08x} [SL {:02x}]\n", static_cast<std::uint32_t>(leaf),
							                                                                         regs[eax],
							                                                                         regs[ebx],
							                                                                         regs[ecx],
							                                                                         regs[edx],
							                                                                         static_cast<std::uint32_t>(subleaf));
						}
					}
				}
				format_to(out, "\n");
				++count;
			}
		}
		break;
	case file_format::cpuinfo:
		{
			system_t system = build_topology(logical_cpus);

			std::uint32_t count = 0_u32;
			for(const auto& c : logical_cpus) {
				const auto& cpu = c.second;
				const auto get_vendor_id = [&] () {
					const register_set_t& regs = cpu.leaves.at(leaf_type::basic_info).at(subleaf_type::main);
					const std::array<char, 12> vndr = bit_cast<decltype(vndr)>(
						std::array<std::uint32_t, 3> {
							regs[ebx],
							regs[edx],
							regs[ecx]
						}
					);
					return vndr;
				};

				const auto get_brand_string = [&] () {
					const std::array<char, 48> brand = bit_cast<decltype(brand)>(
						std::array<register_set_t, 3> {
							cpu.leaves.at(leaf_type::brand_string_0).at(subleaf_type::main),
							cpu.leaves.at(leaf_type::brand_string_1).at(subleaf_type::main),
							cpu.leaves.at(leaf_type::brand_string_2).at(subleaf_type::main)
						}
					);
					return brand;
				};

				const auto get_mhz = [&] () {
					switch(cpu.vendor & vendor_type::any_silicon) {
					case vendor_type::intel:
						{
							if(cpu.leaves.find(leaf_type::processor_frequency) != cpu.leaves.end()) {
								const register_set_t& regs = cpu.leaves.at(leaf_type::processor_frequency).at(subleaf_type::main);
								struct frequency_t
								{
									std::uint32_t frequency : 16;
									std::uint32_t reserved_1 : 16;
								};

								const frequency_t a = bit_cast<decltype(a)>(regs[eax]);
								return a.frequency;
							} else if(cpu.leaves.find(leaf_type::brand_string_2) != cpu.leaves.end()) {
								const auto raw_brand = get_brand_string();
								const std::string brand(std::begin(raw_brand), std::end(raw_brand));
								const std::string::size_type hertz = brand.rfind("Hz");
								if(hertz == std::string::npos) {
									return 0_u32;
								}
								std::size_t to_megahertz = 1_u64;
								switch(brand.at(hertz - 1)) {
								case 'T': to_megahertz *= 1'000_u64; [[fallthrough]];
								case 'G': to_megahertz *= 1'000_u64; [[fallthrough]];
								case 'M':
									{
										const std::string::size_type freq_pos = brand.rfind(' ', hertz - 1);
										if(freq_pos == std::string::npos) {
											return 0_u32;
										}
										const std::string freq_str = brand.substr(freq_pos + 1, hertz - 1 - freq_pos - 1);
										const double raw_freq = std::stod(freq_str);
										const std::uint32_t frequency = gsl::narrow_cast<std::uint32_t>(raw_freq * to_megahertz);
										return frequency;
									}
									break;
								default:
									return 0_u32;
								}
							}
						}
						break;
					case vendor_type::amd:
						break;
					default:
						break;
					}
					return 0_u32;
				};

				const auto get_cache_size = [&] () {
					std::map<std::uint32_t, std::uint32_t> max_sizes;
					for(const auto& cache : system.all_caches) {
						max_sizes[cache.level] = std::max(max_sizes[cache.level], cache.total_size);
					}
					return max_sizes.empty() ? 0_u32 : std::prev(max_sizes.end())->second;
				};

				const auto get_core_id = [&] () noexcept {
					for(const auto& core : system.all_cores) {
						if(core.full_apic_id == cpu.apic_id) {
							return core.core_id;
						}
					}
					return 0_u32;
				};

				const auto get_package_id = [&] () noexcept {
					for(const auto& core : system.all_cores) {
						if(core.full_apic_id == cpu.apic_id) {
							return core.package_id;
						}
					}
					return 0_u32;
				};

				const auto get_logical_core_count = [&] () noexcept {
					const std::uint32_t package_id = get_package_id();
					std::uint32_t logicals = 0_u32;
					for(const auto& core : system.all_cores) {
						if(core.package_id == package_id) {
							++logicals;
						}
					}
					return logicals;
				};

				const auto has_feature = [&] (leaf_type leaf, subleaf_type subleaf, register_type reg, std::uint32_t bit) {
					if(cpu.leaves.find(leaf) != cpu.leaves.end()) {
						if(cpu.leaves.at(leaf).find(subleaf) != cpu.leaves.at(leaf).end()) {
							if(cpu.leaves.at(leaf).at(subleaf)[reg] & (1_u32 << bit)) {
								return true;
							}
						}
					}
					return false;
				};

				const auto get_feature_ext = [&] (leaf_type leaf, subleaf_type subleaf, register_type reg, std::uint32_t bit, const char* name) {
					return has_feature(leaf, subleaf, reg, bit) ? name : "";
				};

				const auto get_feature = [&] (leaf_type leaf, register_type reg, std::uint32_t bit, const char* name) {
					return get_feature_ext(leaf, subleaf_type::main, reg, bit, name);
				};

				const auto get_tlb_size = [&] () -> std::uint32_t {
					if(cpu.leaves.find(leaf_type::l2_cache_identifiers) != cpu.leaves.end()) {
						const register_set_t& regs = cpu.leaves.at(leaf_type::l2_cache_identifiers).at(subleaf_type::main);
						struct tlb_element
						{
							std::uint16_t entries       : 12;
							std::uint16_t associativity : 4;
						};

						struct tlb_info
						{
							tlb_element i;
							tlb_element d;
						};

						const tlb_info b = bit_cast<decltype(b)>(regs[ebx]); // 4K page
						return static_cast<std::uint32_t>(b.i.entries) + static_cast<std::uint32_t>(b.d.entries);
					}
					return 0_u32;
				};

				const auto get_flush_size = [&] () {
					const register_set_t& regs = cpu.leaves.at(leaf_type::version_info).at(subleaf_type::main);
					const id_info_t b = bit_cast<decltype(b)>(regs[ebx]);
					if(has_feature(leaf_type::version_info, subleaf_type::main, ecx, 19_u32)) {
						return b.cache_line_size * 8_u32;
					}
					return 0_u32;
				};

				const std::uint32_t tlb_size = get_tlb_size();
				const std::uint32_t flush_size = get_flush_size();

				std::uint32_t physical_bits = 36_u32;
				std::uint32_t virtual_bits = 48_u32;

				const auto get_bitness = [&] () {
					if(cpu.leaves.find(leaf_type::address_limits) != cpu.leaves.end()) {
						const register_set_t& regs = cpu.leaves.at(leaf_type::address_limits).at(subleaf_type::main);
						const struct
						{
							std::uint32_t physical_address_size : 8;
							std::uint32_t virtual_address_size  : 8;
							std::uint32_t guest_physical_size   : 8;
							std::uint32_t reserved_1            : 8;
						} a = bit_cast<decltype(a)>(regs[eax]);

						physical_bits = a.physical_address_size;
						virtual_bits  = a.virtual_address_size;
					}
				};

				get_bitness();

				const std::string flags = boost::algorithm::join(get_linux_features        (cpu), " ");
				const std::string bugs  = boost::algorithm::join(get_linux_bugs            (cpu), " ");
				const std::string pm    = boost::algorithm::join(get_linux_power_management(cpu), " ");

				format_to(out, "processor       : {:d}\n", count);
				format_to(out, "vendor_id       : {}\n", get_vendor_id());
				format_to(out, "cpu family      : {:d}\n", cpu.model.family);
				format_to(out, "model           : {:d}\n", cpu.model.model);
				format_to(out, "model name      : {}\n", get_brand_string());
				format_to(out, "stepping        : {:d}\n", cpu.model.stepping);
				format_to(out, "microcode       : {:#x}\n", 0xffff'ffff_u32);
				format_to(out, "cpu MHz         : {:d} MHz\n", get_mhz());
				format_to(out, "cache size      : {:d} KB\n", get_cache_size() / 1'024_u32);
				format_to(out, "physical id     : {:d}\n", get_package_id());
				format_to(out, "siblings        : {:d}\n", get_logical_core_count());
				format_to(out, "core id         : {:d}\n", get_core_id());
				format_to(out, "cpu cores       : {:d}\n", system.packages[get_package_id()].physical_cores.size());
				format_to(out, "apicid          : {:d}\n", get_apic_id(cpu));
				format_to(out, "initial apicid  : {:d}\n", get_initial_apic_id(cpu));
				format_to(out, "fpu             : {:s}\n", get_feature(leaf_type::version_info, edx, 0_u32, "yes"));
				format_to(out, "fpu_exception   : {:s}\n", get_feature(leaf_type::version_info, edx, 0_u32, "yes"));
				format_to(out, "cpuid level     : {:d}\n", cpu.leaves.at(leaf_type::basic_info).at(subleaf_type::main).at(eax));
				format_to(out, "wp              : yes\n");
				format_to(out, "flags           : {:s}\n", flags);
				format_to(out, "bugs            : {:s}\n", bugs);
				format_to(out, "bogomips        : \n");
				if(tlb_size != 0_u32) {
					format_to(out, "TLB size        : {:d} 4K pages\n", tlb_size);
				}
				format_to(out, "clflush size    : {:d}\n", flush_size);
				format_to(out, "cache_alignment : {:d}\n", flush_size);
				format_to(out, "address sizes   : {:d} bits physical, {:d} bits virtual\n", physical_bits, virtual_bits);
				format_to(out, "power management: {:s}\n", pm);
				format_to(out, "\n");
				++count;
			}
		}
		break;
	}
}

}

max_stars_repo_name: DrPizza/cpuid

max_issues_repo_name: DrPizza/cpuid

max_forks_repo_name: DrPizza/cpuid


text:
#define BOOST_TEST_MAIN
#include <boost/test/included/unit_test.hpp>
#include <srook/math/matrix.hpp>

BOOST_AUTO_TEST_SUITE(srook_math_matrix_test)

namespace sm = srook::math;
namespace stv = srook::tmpl::vt;

BOOST_AUTO_TEST_CASE(matrix_construct)
{
    typedef sm::row<int, int> r2;
    typedef stv::transfer_t<sm::matrix, stv::replicate_t<2, stv::transfer_t<sm::row, stv::replicate_t<2, int>>>> mtx2x2_int; // 2 x 2, int matrix type

    constexpr mtx2x2_int m1 {
       r2{ 1, 3 },
       r2{ 4, 2 }
    };
    SROOK_ATTRIBUTE_UNUSED constexpr auto m2 = m1;

    constexpr mtx2x2_int m3 { // initialized as like the above.
        1, 3,
        4, 2
    };
    SROOK_ST_ASSERT(m1 == m2 && m1 == m3);
}

/*

int main()
{
    constexpr srook::math::matrix<
        srook::math::row<int, int, int, int>,
        srook::math::row<int, int, int, int>,
        srook::math::row<int, int, int, int>,
        srook::math::row<int, int, int, int>
    > m1 {
        1, 2, 7, 6,
        2, 4, 4, 2,
        1, 8, 5, 2,
        2, 4, 3, 3
    };

    constexpr auto res = m1.compute_equations(srook::math::make_vector(6, 2, 12, 5), srook::math::gaussian_elimination_eq());
    std::cout << res << std::endl;

    constexpr srook::math::matrix<
        srook::math::row<int, int, int>,
        srook::math::row<int, int, int>,
        srook::math::row<int, int, int>
    > m2 {
        2, 2, 1,
        3, 0, 2,
        4, 3, 2
    };
    constexpr auto inv = m2.inverse();
    std::cout << inv << std::endl;

    constexpr auto det = m2.determinant();
    std::cout << det << std::endl;
}*/

BOOST_AUTO_TEST_SUITE_END()

ext: cpp

max_stars_repo_name: falgon/srookCppLibraries

max_issues_repo_name: falgon/srookCppLibraries

max_forks_repo_name: falgon/srookCppLibraries


text:
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------

#include "stdafx.h"

#include <boost/test/unit_test.hpp>
#include "Common/boost-taef.h"

namespace FailoverManagerUnitTest
{
    using namespace Common;
    using namespace Client;
    using namespace std;
    using namespace Federation;
    using namespace Reliability;
    using namespace Reliability::FailoverManagerComponent;

    class TestFMServiceLookupTable
    {
    protected:

        TestFMServiceLookupTable() { BOOST_REQUIRE(MethodSetup()); }
        TEST_METHOD_SETUP(MethodSetup);

        ~TestFMServiceLookupTable() { BOOST_REQUIRE(MethodCleanup()); }
        TEST_METHOD_CLEANUP(MethodCleanup);

        vector<ConsistencyUnitDescription> GetConsistencyUnitDescriptions(int count) const;
        LockedFailoverUnitPtr GetFailoverUnit(int64 lookupVersion);

        ComponentRootSPtr root_;
        FailoverManagerSPtr fm_;
    };

    BOOST_FIXTURE_TEST_SUITE(TestFMServiceLookupTableSuite, TestFMServiceLookupTable)

    BOOST_AUTO_TEST_CASE(TestServiceLookupTable)
    {
        auto pageSizeLimit = static_cast<size_t>(
            Federation::FederationConfig::GetConfig().SendQueueSizeLimit * FailoverConfig::GetConfig().MessageContentBufferRatio);

        // Test an empty ServiceLookupTable.
        vector<ServiceTableEntry> entriesToUpdate;
        VersionRangeCollection versionRangesToUpdate;
        int64 endVersion;
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, vector<ConsistencyUnitId>(), VersionRangeCollection(0, 0), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(0), entriesToUpdate.size());
        VERIFY_IS_TRUE(versionRangesToUpdate.IsEmpty);
        VERIFY_ARE_EQUAL(1, endVersion);

        // Add 3 services having 5 FailoverUnits each.
        for (int i = 0; i < 3; i++)
        {
            wstring serviceName = L"TestService" + StringUtility::ToWString(i);
            wstring serviceType = L"ServiceType" + StringUtility::ToWString(i);
            ServiceModel::ServiceTypeIdentifier typeId(ServiceModel::ServicePackageIdentifier(L"TestApp_App0", L"TestPackage"), serviceType);

            wstring placementConstraints = L"";
            int scaleoutCount = 0;
            ServiceDescription serviceDescription = ServiceDescription(serviceName, 0, 0, 5, 3, 2, true, true, TimeSpan::FromSeconds(60.0), TimeSpan::MaxValue, TimeSpan::FromSeconds(300.0), typeId, vector<ServiceCorrelationDescription>(), placementConstraints, scaleoutCount, vector<ServiceLoadMetricDescription>(), 0, vector<byte>());
            vector<ConsistencyUnitDescription> consistencyUnitDescriptions = GetConsistencyUnitDescriptions(5);

            ErrorCode error = fm_->ServiceCacheObj.CreateService(move(serviceDescription), move(consistencyUnitDescriptions), false);
            VERIFY_IS_TRUE(error.IsSuccess());
        }

        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, vector<ConsistencyUnitId>(), VersionRangeCollection(0, 0), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(15), entriesToUpdate.size());
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(1, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(16, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(16, fm_->FailoverUnitCacheObj.ServiceLookupTable.EndVersion);
        VERIFY_ARE_EQUAL(16, endVersion);

        // Update the ServiceLookupTable with an existing updated FailoverUnit.
        auto failoverUnit = GetFailoverUnit(1);
        fm_->FailoverUnitCacheObj.ServiceLookupTable.UpdateLookupVersion(*failoverUnit);
        fm_->FailoverUnitCacheObj.ServiceLookupTable.Update(*failoverUnit);

        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, vector<ConsistencyUnitId>(), VersionRangeCollection(0, 0), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(15), entriesToUpdate.size());
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(1, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(17, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(17, fm_->FailoverUnitCacheObj.ServiceLookupTable.EndVersion);
        VERIFY_ARE_EQUAL(17, endVersion);

        // Update the ServiceLookupTable with an existing FailoverUnit that failed persistence.
        fm_->FailoverUnitCacheObj.ServiceLookupTable.UpdateLookupVersion(*failoverUnit);
        fm_->FailoverUnitCacheObj.ServiceLookupTable.OnUpdateFailed(*failoverUnit);

        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, vector<ConsistencyUnitId>(), VersionRangeCollection(0, 0), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(15), entriesToUpdate.size());
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(1, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(18, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(18, fm_->FailoverUnitCacheObj.ServiceLookupTable.EndVersion);
        VERIFY_ARE_EQUAL(18, endVersion);

        // Update the ServiceLookupTable with an existing FailoverUnit that failed persistence previously.
        fm_->FailoverUnitCacheObj.ServiceLookupTable.UpdateLookupVersion(*failoverUnit);
        fm_->FailoverUnitCacheObj.ServiceLookupTable.Update(*failoverUnit);

        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, vector<ConsistencyUnitId>(), VersionRangeCollection(0, 0), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(15), entriesToUpdate.size());
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(1, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(19, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(19, fm_->FailoverUnitCacheObj.ServiceLookupTable.EndVersion);
        VERIFY_ARE_EQUAL(19, endVersion);

        // Three FailoverUnits getting updated simulteneously.
        auto failoverUnit1 = GetFailoverUnit(2);
        auto failoverUnit2 = GetFailoverUnit(3);
        auto failoverUnit3 = GetFailoverUnit(4);
        fm_->FailoverUnitCacheObj.ServiceLookupTable.UpdateLookupVersion(*failoverUnit1);
        fm_->FailoverUnitCacheObj.ServiceLookupTable.UpdateLookupVersion(*failoverUnit2);
        fm_->FailoverUnitCacheObj.ServiceLookupTable.UpdateLookupVersion(*failoverUnit3);
        fm_->FailoverUnitCacheObj.ServiceLookupTable.OnUpdateFailed(*failoverUnit1);
        fm_->FailoverUnitCacheObj.ServiceLookupTable.Update(*failoverUnit3);
        fm_->FailoverUnitCacheObj.ServiceLookupTable.Update(*failoverUnit2);

        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, vector<ConsistencyUnitId>(), VersionRangeCollection(0, 0), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(15), entriesToUpdate.size());
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(1, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(22, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(22, fm_->FailoverUnitCacheObj.ServiceLookupTable.EndVersion);
        VERIFY_ARE_EQUAL(22, endVersion);

        // Client has partial data
        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, vector<ConsistencyUnitId>(), VersionRangeCollection(1, 6), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(13), entriesToUpdate.size()); // version 2 & 5 will not be included
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(6, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(22, versionRangesToUpdate.VersionRanges[0].EndVersion);

        // Test too many entries
        pageSizeLimit = 1500;
        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, vector<ConsistencyUnitId>(), VersionRangeCollection(1, 6), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(12), entriesToUpdate.size()); // version 2 & 5 will not be included
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(6, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(21, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(21, endVersion);

        pageSizeLimit = 700;
        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, vector<ConsistencyUnitId>(), VersionRangeCollection(1, 6), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(5), entriesToUpdate.size()); // version 2 & 5 will not be included
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(6, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(11, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(11, endVersion);

        pageSizeLimit = 1024 * 1024;

        // Update the ServiceLookupTable with an existing FailoverUnit that failed persistence previously.
        fm_->FailoverUnitCacheObj.ServiceLookupTable.UpdateLookupVersion(*failoverUnit1);
        fm_->FailoverUnitCacheObj.ServiceLookupTable.Update(*failoverUnit1);

        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, vector<ConsistencyUnitId>(), VersionRangeCollection(0, 0), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(15), entriesToUpdate.size());
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(1, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(23, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(23, fm_->FailoverUnitCacheObj.ServiceLookupTable.EndVersion);
        VERIFY_ARE_EQUAL(23, endVersion);

        // Add another service.
        wstring serviceName = L"TestService4";
        wstring serviceType = L"ServiceType4";
        ServiceModel::ServiceTypeIdentifier typeId(ServiceModel::ServicePackageIdentifier(L"TestApp_App0", L"TestPackage"), serviceType);

        wstring placementConstraints = L"";
        int scaleoutCount = 0;
        ServiceDescription serviceDescription = ServiceDescription(serviceName, 0, 0, 5, 3, 2, true, true, TimeSpan::FromSeconds(60.0), TimeSpan::MaxValue, TimeSpan::FromSeconds(300.0), typeId, vector<ServiceCorrelationDescription>(), placementConstraints, scaleoutCount, vector<ServiceLoadMetricDescription>(), 0, vector<byte>());
        vector<ConsistencyUnitDescription> consistencyUnitDescriptions = GetConsistencyUnitDescriptions(5);

        ErrorCode error = fm_->ServiceCacheObj.CreateService(move(serviceDescription), move(consistencyUnitDescriptions), false);
        VERIFY_IS_TRUE(error.IsSuccess());

        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, vector<ConsistencyUnitId>(), VersionRangeCollection(0, 0), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(20), entriesToUpdate.size());
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(1, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(28, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(28, fm_->FailoverUnitCacheObj.ServiceLookupTable.EndVersion);
        VERIFY_ARE_EQUAL(28, endVersion);

        // Resolve request includes explicit ConsistencyUnitIds when incoming generation is equal to FM generation
        vector<ConsistencyUnitId> consistencyUnitIds;
        for (FailoverUnitId const& failoverUnitId : fm_->ServiceCacheObj.GetService(L"TestService4")->FailoverUnitIds)
        {
            consistencyUnitIds.push_back(ConsistencyUnitId(failoverUnitId.Guid));
        }

        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, consistencyUnitIds, VersionRangeCollection(0, 0), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(20), entriesToUpdate.size());
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(1, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(28, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(28, fm_->FailoverUnitCacheObj.ServiceLookupTable.EndVersion);
        VERIFY_ARE_EQUAL(28, endVersion);

        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, fm_->Generation, consistencyUnitIds, VersionRangeCollection(1, 6), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(19), entriesToUpdate.size());
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(6, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(28, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(28, fm_->FailoverUnitCacheObj.ServiceLookupTable.EndVersion);
        VERIFY_ARE_EQUAL(28, endVersion);

        // Resolve request includes explicit ConsistencyUnitIds when incoming generation is less than FM generation
        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, GenerationNumber(), consistencyUnitIds, VersionRangeCollection(0, 0), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(20), entriesToUpdate.size());
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(1, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(28, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(28, fm_->FailoverUnitCacheObj.ServiceLookupTable.EndVersion);
        VERIFY_ARE_EQUAL(28, endVersion);

        entriesToUpdate.clear();
        fm_->FailoverUnitCacheObj.ServiceLookupTable.GetUpdates(pageSizeLimit, GenerationNumber(), consistencyUnitIds, VersionRangeCollection(1, 6), entriesToUpdate, versionRangesToUpdate, endVersion);

        VERIFY_ARE_EQUAL(static_cast<size_t>(20), entriesToUpdate.size());
        VERIFY_ARE_EQUAL(static_cast<size_t>(1), versionRangesToUpdate.VersionRanges.size());
        VERIFY_ARE_EQUAL(1, versionRangesToUpdate.VersionRanges[0].StartVersion);
        VERIFY_ARE_EQUAL(28, versionRangesToUpdate.VersionRanges[0].EndVersion);
        VERIFY_ARE_EQUAL(28, fm_->FailoverUnitCacheObj.ServiceLookupTable.EndVersion);
        VERIFY_ARE_EQUAL(28, endVersion);
    }

    BOOST_AUTO_TEST_CASE(TestBroadcast)
    {
        FMServiceLookupTable & lookupTable = fm_->FailoverUnitCacheObj.ServiceLookupTable;

        //
        // Initial case when ServiceLookupTable is empty
        //
        ServiceTableUpdateMessageBody body;
        bool result = lookupTable.TryGetServiceTableUpdateMessageBody(body);
        VERIFY_IS_FALSE(result);

        // Add 3 services having 5 FailoverUnits each.
        for (int i = 0; i < 3; i++)
        {
            wstring serviceName = L"TestService" + StringUtility::ToWString(i);
            wstring serviceType = L"ServiceType" + StringUtility::ToWString(i);
            ServiceModel::ServiceTypeIdentifier typeId(ServiceModel::ServicePackageIdentifier(L"TestApp_App0", L"TestPackage"), serviceType);

            wstring placementConstraints = L"";
            int scaleoutCount = 0;
            ServiceDescription serviceDescription = ServiceDescription(serviceName, 0, 0, 5, 3, 2, true, true, TimeSpan::FromSeconds(60.0), TimeSpan::MaxValue, TimeSpan::FromSeconds(300.0), typeId, vector<ServiceCorrelationDescription>(), placementConstraints, scaleoutCount, vector<ServiceLoadMetricDescription>(), 0, vector<byte>());
            vector<ConsistencyUnitDescription> consistencyUnitDescriptions = GetConsistencyUnitDescriptions(5);

            ErrorCode error = fm_->ServiceCacheObj.CreateService(move(serviceDescription), move(consistencyUnitDescriptions), false);
            VERIFY_IS_TRUE(error.IsSuccess());
        }

        //
        // First broadcast should contain all the entries
        //
        ServiceTableUpdateMessageBody body1;
        result = lookupTable.TryGetServiceTableUpdateMessageBody(body1);

        VERIFY_IS_TRUE(result);
        VERIFY_ARE_EQUAL(body1.EndVersion, 16);
        VERIFY_ARE_EQUAL(body1.VersionRangeCollection.VersionRanges.size(), 1);
        VERIFY_ARE_EQUAL(body1.VersionRangeCollection.VersionRanges[0].StartVersion, 1);
        VERIFY_ARE_EQUAL(body1.VersionRangeCollection.VersionRanges[0].EndVersion, 16);
        VERIFY_ARE_EQUAL(body1.ServiceTableEntries.size(), 15);

        //
        // Second broadcast should also contain all the entries becaues each update is broadcasted twice (by default)
        //
        ServiceTableUpdateMessageBody body2;
        result = lookupTable.TryGetServiceTableUpdateMessageBody(body2);

        VERIFY_IS_TRUE(result);
        VERIFY_ARE_EQUAL(body2.EndVersion, 16);
        VERIFY_ARE_EQUAL(body2.VersionRangeCollection.VersionRanges.size(), 1);
        VERIFY_ARE_EQUAL(body2.VersionRangeCollection.VersionRanges[0].StartVersion, 1);
        VERIFY_ARE_EQUAL(body2.VersionRangeCollection.VersionRanges[0].EndVersion, 16);
        VERIFY_ARE_EQUAL(body2.ServiceTableEntries.size(), 15);

        //
        // Third broadcast should not contain any entry
        //
        ServiceTableUpdateMessageBody body3;
        result = lookupTable.TryGetServiceTableUpdateMessageBody(body3);

        VERIFY_IS_TRUE(result);
        VERIFY_ARE_EQUAL(body3.EndVersion, 16);
        VERIFY_IS_TRUE(body3.VersionRangeCollection.IsEmpty);
        VERIFY_ARE_EQUAL(body3.ServiceTableEntries.size(), 0);

        //
        // Broadcast after a single update should only contain one entry
        //
        LockedFailoverUnitPtr failoverUnit = this->GetFailoverUnit(1);
        lookupTable.UpdateLookupVersion(*failoverUnit);
        lookupTable.Update(*failoverUnit);
        failoverUnit.Release(false, false);

        ServiceTableUpdateMessageBody body4;
        result = lookupTable.TryGetServiceTableUpdateMessageBody(body4);

        VERIFY_IS_TRUE(result);
        VERIFY_ARE_EQUAL(body4.EndVersion, 17);
        VERIFY_ARE_EQUAL(body4.VersionRangeCollection.VersionRanges.size(), 1);
        VERIFY_ARE_EQUAL(body4.VersionRangeCollection.VersionRanges[0].StartVersion, 16);
        VERIFY_ARE_EQUAL(body4.VersionRangeCollection.VersionRanges[0].EndVersion, 17);
        VERIFY_ARE_EQUAL(body4.ServiceTableEntries.size(), 1);

        //
        // After another update, broadcast should only contain two entries
        //
        failoverUnit = GetFailoverUnit(2);
        lookupTable.UpdateLookupVersion(*failoverUnit);
        lookupTable.Update(*failoverUnit);
        failoverUnit.Release(false, false);

        ServiceTableUpdateMessageBody body5;
        result = lookupTable.TryGetServiceTableUpdateMessageBody(body5);

        VERIFY_IS_TRUE(result);
        VERIFY_ARE_EQUAL(body5.EndVersion, 18);
        VERIFY_ARE_EQUAL(body5.VersionRangeCollection.VersionRanges.size(), 1);
        VERIFY_ARE_EQUAL(body5.VersionRangeCollection.VersionRanges[0].StartVersion, 16);
        VERIFY_ARE_EQUAL(body5.VersionRangeCollection.VersionRanges[0].EndVersion, 18);
        VERIFY_ARE_EQUAL(body5.ServiceTableEntries.size(), 2);

        //
        // A failed update(FT1) should not be included in the broadcast. Since the failed
        // version is at the end, it will not be included in the broadcasted version
        // ranges.
        //
        failoverUnit = GetFailoverUnit(3);
        lookupTable.UpdateLookupVersion(*failoverUnit);
        lookupTable.OnUpdateFailed(*failoverUnit);
        failoverUnit.Release(false, false);

        ServiceTableUpdateMessageBody body6;
        result = lookupTable.TryGetServiceTableUpdateMessageBody(body6);

        VERIFY_IS_TRUE(result);
        VERIFY_ARE_EQUAL(body6.EndVersion, 19);
        VERIFY_ARE_EQUAL(body6.VersionRangeCollection.VersionRanges.size(), 1);
        VERIFY_ARE_EQUAL(body6.VersionRangeCollection.VersionRanges[0].StartVersion, 17);
        VERIFY_ARE_EQUAL(body6.VersionRangeCollection.VersionRanges[0].EndVersion, 19);
        VERIFY_ARE_EQUAL(body6.ServiceTableEntries.size(), 1);

        //
        // A successful update(FT1) after a failure(FT2) should create a hole in version ranges.
        // Since there are no entries to be broadcasted with version less than that of
        // the hole, only a single version range will be broadcased.
        //
        failoverUnit = GetFailoverUnit(4);
        lookupTable.UpdateLookupVersion(*failoverUnit);
        lookupTable.Update(*failoverUnit);
        failoverUnit.Release(false, false);

        ServiceTableUpdateMessageBody body7;
        result = lookupTable.TryGetServiceTableUpdateMessageBody(body7);

        VERIFY_IS_TRUE(result);
        VERIFY_ARE_EQUAL(body7.EndVersion, 20);
        VERIFY_ARE_EQUAL(body7.VersionRangeCollection.VersionRanges.size(), 1);
        VERIFY_ARE_EQUAL(body7.VersionRangeCollection.VersionRanges[0].StartVersion, 18);
        VERIFY_ARE_EQUAL(body7.VersionRangeCollection.VersionRanges[0].EndVersion, 20);
        VERIFY_ARE_EQUAL(body7.ServiceTableEntries.size(), 1);

        //
        // A failed update(FT1) followed by a successful update(FT2) should result in a hole in
        // the broadcasted version ranges.
        //
        failoverUnit = GetFailoverUnit(5);
        lookupTable.UpdateLookupVersion(*failoverUnit);
        lookupTable.OnUpdateFailed(*failoverUnit);
        failoverUnit.Release(false, false);

        failoverUnit = GetFailoverUnit(6);
        lookupTable.UpdateLookupVersion(*failoverUnit);
        lookupTable.Update(*failoverUnit);
        failoverUnit.Release(false, false);

        ServiceTableUpdateMessageBody body8;
        result = lookupTable.TryGetServiceTableUpdateMessageBody(body8);

        VERIFY_IS_TRUE(result);
        VERIFY_ARE_EQUAL(body8.EndVersion, 22);
        VERIFY_ARE_EQUAL(body8.VersionRangeCollection.VersionRanges.size(), 1);
        VERIFY_ARE_EQUAL(body8.VersionRangeCollection.VersionRanges[0].StartVersion, 19);
        VERIFY_ARE_EQUAL(body8.VersionRangeCollection.VersionRanges[0].EndVersion, 22);
        VERIFY_ARE_EQUAL(body8.ServiceTableEntries.size(), 2);

        //
        // A failed update(FT1) followed by an eventual successful update(FT1) should fill
        // the hole.
        //
        failoverUnit = GetFailoverUnit(7);
        lookupTable.UpdateLookupVersion(*failoverUnit);
        lookupTable.OnUpdateFailed(*failoverUnit);
        failoverUnit.Release(false, false);

        failoverUnit = GetFailoverUnit(8);
        lookupTable.UpdateLookupVersion(*failoverUnit);
        lookupTable.Update(*failoverUnit);
        failoverUnit.Release(false, false);

        failoverUnit = GetFailoverUnit(22);
        lookupTable.UpdateLookupVersion(*failoverUnit);
        lookupTable.Update(*failoverUnit);
        failoverUnit.Release(false, false);

        ServiceTableUpdateMessageBody body9;
        result = lookupTable.TryGetServiceTableUpdateMessageBody(body9);

        VERIFY_IS_TRUE(result);
        VERIFY_ARE_EQUAL(body9.EndVersion, 25);
        VERIFY_ARE_EQUAL(body9.VersionRangeCollection.VersionRanges.size(), 1);
        VERIFY_ARE_EQUAL(body9.VersionRangeCollection.VersionRanges[0].StartVersion, 20);
        VERIFY_ARE_EQUAL(body9.VersionRangeCollection.VersionRanges[0].EndVersion, 25);
        VERIFY_ARE_EQUAL(body9.ServiceTableEntries.size(), 3);
    }

    BOOST_AUTO_TEST_SUITE_END()

    bool TestFMServiceLookupTable::MethodSetup()
    {
        LoadBalancingComponent::PLBConfig::Test_Reset();
        auto & config_plb = Reliability::LoadBalancingComponent::PLBConfig::GetConfig();
        config_plb.IsTestMode = true;
        config_plb.DummyPLBEnabled = true;
        config_plb.PLBRefreshInterval = (TimeSpan::MaxValue); //set so that PLB won't spawn threads to balance empty data structures in functional tests
        config_plb.ProcessPendingUpdatesInterval = (TimeSpan::MaxValue);//set so that PLB won't spawn threads to balance empty data structures in functional tests

        FailoverConfig::Test_Reset();
        FailoverConfig & config = FailoverConfig::GetConfig();
        config.IsTestMode = true;
        config.DummyPLBEnabled = true;
        config.ServiceLocationBroadcastInterval = TimeSpan::MaxValue;
        config.PeriodicStateScanInterval = TimeSpan::MaxValue;

        root_ = make_shared<ComponentRoot>();
        fm_ = TestHelper::CreateFauxFM(*root_);

        GenerationNumber generationNumber = GenerationNumber(DateTime::Now().Ticks, fm_->Federation.Id);
        fm_->SetGeneration(generationNumber);

        return true;
    }

    bool TestFMServiceLookupTable::MethodCleanup()
    {
        fm_->Close(true /* isStoreCloseNeeded */);
        FailoverConfig::Test_Reset();

        return true;
    }

    LockedFailoverUnitPtr TestFMServiceLookupTable::GetFailoverUnit(int64 lookupVersion)
    {
        auto visitor = fm_->FailoverUnitCacheObj.CreateVisitor(false, TimeSpan::Zero);
        for (;;)
        {
            auto failoverUnit = visitor->MoveNext();
            if (!failoverUnit)
            {
                break;
            }

            if (failoverUnit->LookupVersion == lookupVersion)
            {
                return failoverUnit;
            }
        }

#if !defined(PLATFORM_UNIX)
        return nullptr;
#else
        return LockedFailoverUnitPtr();
#endif
    }

    vector<ConsistencyUnitDescription> TestFMServiceLookupTable::GetConsistencyUnitDescriptions(int count) const
    {
        vector<ConsistencyUnitDescription> consistencyUnitDescriptions;
        for (int i = 0; i < count; i++)
        {
            consistencyUnitDescriptions.push_back(ConsistencyUnitDescription());
        }

        return consistencyUnitDescriptions;
    }
}

max_stars_repo_name: gridgentoo/ServiceFabricAzure

max_issues_repo_name: gridgentoo/ServiceFabricAzure

max_forks_repo_name: gridgentoo/ServiceFabricAzure


text:
//
// lager - library for functional interactive c++ programs
// Copyright (C) 2019 Carl Bussey
//
// This file is part of lager.
//
// lager is free software: you can redistribute it and/or modify
// it under the terms of the MIT License, as detailed in the LICENSE
// file located at the root of this source code distribution,
// or here: <https://github.com/arximboldi/lager/blob/master/LICENSE>
//

#pragma once

#include <boost/variant/variant.hpp>

#include <cstdlib>
#include <random>
#include <utility>
#include <vector>

namespace sn {

struct go_left
{};
struct go_right
{};
struct go_up
{};
struct go_down
{};
struct reset
{};
struct tick
{};
using action_t = boost::variant<go_left, go_right, go_up, go_down, reset, tick>;

using point_t = std::pair<int, int>;

inline int x(point_t p) { return p.first; }

inline int y(point_t p) { return p.second; }

enum class direction
{
    left,
    up,
    right,
    down
};

struct snake_model
{
    using body_t = std::vector<point_t>;
    body_t body{};
    direction dir{};
};

struct game_model
{
    snake_model snake;
    point_t apple_pos{};
    bool over;

    static constexpr int width{25};
    static constexpr int height{25};
};

struct app_model
{
    std::mt19937 gen;
    std::uniform_int_distribution<> dist;

    game_model game;
};

template <typename Func>
inline point_t random_apple_pos(Func&& random)
{
    return {random(), random()};
}

app_model make_initial(int seed);

app_model update(app_model m, action_t action);

} // namespace sn

ext: hpp

max_stars_repo_name: LoganBarnes/lager

max_issues_repo_name: LoganBarnes/lager

max_forks_repo_name: LoganBarnes/lager


text:
/* Author: Tom Manville
 * tdmanv@umich.edu
 * 2012
 *
 * Please see LICENSE for license information.
 *
 * Based on work by:
 * Author: Hongliang Gao;   Created: Jan 27 2011
 */

#define DUMP_CYCLE_INFO

#include <stdio.h>
#include <stdlib.h>
#include <cassert>
#include <string.h>
#include <inttypes.h>
#include <fstream>
#include <iostream>
#include <vector>
#include <time.h>

#include <boost/program_options.hpp>
namespace po = boost::program_options;

#include "predictor.h"
#include "cbp3_def.h"
#include "cbp3_framework.h"

#include "trace_interface.h"
#include "cbp3_stats.h"

using namespace std;

Program_Graph *PG;
uint64_t num_run = 0;
void run( int sim_len, int period );
void reset();
cbp3_stats stats;

po::variables_map parse_options( int argc, char *argv[] ){
	po::options_description desc("Options");
	desc.add_options()
		("help,h", "produce help message")
		("trace,t", po::value< string >(), "input trace")
		("length,l", po::value<int>()->default_value(-1), "Number of uops to simulate. Negative values run whole trace")
		("period,p", po::value<int>()->default_value(-1), "Set the period to output stats. Negative values cause a single output at the end")
		("gzip,z", po::value<bool>()->implicit_value(true), "Required if the trace is compressed with gzip")
		("pred_opt0,r", po::value<int>()->default_value(-1), "Pass a value to the predictor")
		("pred_opt1,s", po::value<int>()->default_value(-1), "Pass a second value to the predictor")
		//("opts,o", po::value< string >(), "options to pass to the predictor")
		//("table_size,s", po::value<uint64_t>()->default_value(10), "log2(#table entries)")
		//("pc_bits,p", po::value<uint64_t>()->default_value(7), "# of pc bits to use in hash")
		//("use_global,g", po::value<bool>()->default_value(true), "only use/track global history")
		//("gzip,z", po::value<bool>(), "Read a trace compressed with gzip")
		//("gzip,z", po::value<bool>()->default_value(false), "Read a trace compressed with gzip")
		;

	po::variables_map vm;
	po::store(po::parse_command_line(argc, argv, desc), vm);
	po::notify(vm);

	if (vm.count("help")) {
		cout << desc << "\n";
		exit(1);
	}

	if( ! vm.count("trace") ) {
		cout << "Requires input trace\n" << desc << "\n";
		exit(1);
	}

	return vm;
}

int main ( int argc, char *argv[] )
{
    //time_t time_start = time(NULL);

	po::variables_map vm = parse_options( argc, argv );

	string trace_file       = vm["trace"].as<string>();
	int sim_len				= vm["length"].as<int>();
	int period				= vm["period"].as<int>();

	int pred_opt0			= vm["pred_opt0"].as<int>();
	int pred_opt1		    = vm["pred_opt1"].as<int>();

	bool gzip;
	if( vm.count("gzip") )
		gzip = true;
	else
		gzip = false;

// load the trace into memory
    printf("********************  CBP3  Start ***********************\n");
    printf("Trace:            %s\n", trace_file.c_str() );
    if (sim_len == -1)
        printf("Uops to simulate: Whole Trace\n\n");
    else
        printf("Uops to simulate: %i\n\n", sim_len );

 	PG = new Program_Graph( trace_file, gzip );

	if( pred_opt1 >= 0 )
		PredictorInit( pred_opt0, pred_opt1 );
	else if( pred_opt0 >= 0 )
		PredictorInit( pred_opt0 );
	else
		PredictorInit();

	reset();
	printf("\n*********  RUN  %d   *********\n", (int) num_run);

	//chunk is the number of entries to pull into memory from the trace file
	int chunk = 10000;

	while( PG->parse_trace( chunk ) ){

        run( sim_len, period );

    }

	stats.print();
    PredictorRunEnd();

// the end...
    printf("\n********************  CBP3  End   ***********************\n");
    //printf("Total Simulation Time: %ld Seconds\n", (time(NULL) - time_start));
    PredictorExit();

    return 0;
}

// live uop info that will be availabe to the predictor
// depending on the stage of the uop, fields are copied from uop_info array
vector<cbp3_queue_entry_t*> fetch_uops;
vector<cbp3_queue_entry_t*> exe_uops;
vector<cbp3_queue_entry_t*> retire_uops;

bool     rewind_marked;
uint64_t cycle;
cbp3_cycle_activity_t cycle_info;

void reset() {

    rewind_marked = false;
    cycle = 0;

	stats.reset();

    PredictorReset();
}

void run( int sim_len, int period ) {
    uint16_t num_stages[NUM_STAGES];

    while (PG->Run_a_Cycle(num_stages) && // trace does not end
            (sim_len == -1 ? 1 : stats.get_num_uops() < (uint64_t)sim_len)) { // in the simulation range

        cycle = PG->get_cycle();
        cycle_info.cycle = cycle;
        cycle_info.num_fetch = num_stages[0];
        cycle_info.num_allocate = num_stages[1];
        cycle_info.num_exe = num_stages[2];
        cycle_info.num_retire = num_stages[3];
        cycle_info.num_agu = num_stages[4];
        cycle_info.num_std = num_stages[5];

        // fill in different fields into live_uop array for each stage

		PG->get_retire_uops( retire_uops );
		PG->get_fetch_uops( fetch_uops );
		PG->get_exe_uops( exe_uops );

		//retire stage
		/*
        for (uint64_t i = 0; i < cycle_info.num_retire; i ++) {
            cbp3_queue_entry_t *uop = retire_uops[i];
            uint16_t type = uop->uop.type;
        }
		*/

		//on exe update mispredict info

        for (uint64_t i = 0; i < cycle_info.num_exe; i ++) {
            cbp3_queue_entry_t *uop = exe_uops[i];

            assert(uop->valid);

            // calculate mispred info
            uint16_t type	= uop->uop.type;

			bool is_cond	= (type & IS_BR_CONDITIONAL);
			bool is_ind		= (type & IS_BR_INDIRECT);

            bool cond_msp	= (uop->uop.br_taken != (uop->taken_pred >= 0));
            bool ind_msp	= (uop->uop.br_target != uop->target_pred);

			if( is_cond && is_ind ){
            //cout << uop->uop.br_taken << " "<< uop->taken_pred << endl;
				//cout << "THIS: " << cond_msp << endl;
			}

			if (!uop->cycle_last_pred) {
				cond_msp = true;
				ind_msp = true;
			}

			//update( bool taken, bool cond, bool ind, bool cond_msp, bool ind_msp )
			stats.update( uop->uop.br_taken, is_cond, is_ind, cond_msp, ind_msp );

            uop->cycle_exe = cycle;
        }

        PredictorRunACycle();
        stats.total_cycle++;

		if( period > 0 ){
			if( (cycle%period) == 0 ){
				stats.print();
				stats.reset();
			}
		}
    };
}

const cbp3_cycle_activity_t *get_cycle_info() {
    return &cycle_info;
}

cbp3_queue_entry_t* get_fetch_entry(uint8_t e) {
    return fetch_uops[e];
}

cbp3_queue_entry_t* get_exe_entry(uint8_t e) {
    return exe_uops[e];
}

cbp3_queue_entry_t* get_retire_entry(uint8_t e) {
    return retire_uops[e];
}

bool  report_taken_pred( cbp3_queue_entry_t* uop, int64_t pred) {
	/*
	cout << pred << endl;
	cout << endl;
	*/

    if (!uop->valid)
        return false;

    // record the prediction
    uop->cycle_last_pred = cycle;
    uop->taken_pred = pred;
    return true;
}
bool  report_target_pred( cbp3_queue_entry_t* uop, uint64_t pred) {

    if (!uop->valid)
        return false;

    // record the prediction
    uop->cycle_last_pred = cycle;
    uop->target_pred = pred;
    return true;
}

void cbp3_queue_entry_t::reset() {
    uop.reset();
    valid = false;
    cycle_fetch = 0;
    cycle_allocate = 0;
    cycle_agu = 0;
    cycle_std = 0;
    cycle_exe = 0;
    cycle_retire = 0;
    cycle_last_pred = 0;
    taken_pred = 0;
    target_pred = 0;
}

max_stars_repo_name: tronlab/bpsim

max_issues_repo_name: tronlab/bpsim

max_forks_repo_name: tronlab/bpsim