* Copyright (c) 2018-2023 Google, LLC All rights reserved.
* **********************************************************/
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Google, LLC nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, LLC OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "config_reader.h"
#include <stdint.h>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "options.h"
#include "cache_simulator_create.h"
#include "utils.h"
namespace dynamorio {
namespace drmemtrace {
config_reader_t::config_reader_t()
{
}
bool
config_reader_t::configure(std::istream *config_file, cache_simulator_knobs_t &knobs,
std::map<std::string, cache_params_t> &caches)
{
fin_ = config_file;
while (!fin_->eof()) {
std::string param;
if (!(*fin_ >> std::ws >> param)) {
ERRMSG("Unable to read from the configuration file\n");
return false;
}
if (param == "//") {
if (!getline(*fin_, param)) {
ERRMSG("Comment expected but not found\n");
return false;
}
} else if (param == "num_cores") {
if (!(*fin_ >> knobs.num_cores)) {
ERRMSG("Error reading num_cores from the configuration file\n");
return false;
}
if (knobs.num_cores == 0) {
ERRMSG("Number of cores must be >0\n");
return false;
}
}
else if (param == "line_size") {
if (!(*fin_ >> knobs.line_size)) {
ERRMSG("Error reading line_size from the configuration file\n");
return false;
}
if (knobs.line_size == 0) {
ERRMSG("Line size must be >0\n");
return false;
}
} else if (param == "skip_refs") {
if (!(*fin_ >> knobs.skip_refs)) {
ERRMSG("Error reading skip_refs from the configuration file\n");
return false;
}
} else if (param == "warmup_refs") {
if (!(*fin_ >> knobs.warmup_refs)) {
ERRMSG("Error reading warmup_refs from "
"the configuration file\n");
return false;
}
} else if (param == "warmup_fraction") {
if (!(*fin_ >> knobs.warmup_fraction)) {
ERRMSG("Error reading warmup_fraction from "
"the configuration file\n");
return false;
}
if (knobs.warmup_fraction < 0.0 || knobs.warmup_fraction > 1.0) {
ERRMSG("Warmup fraction should be in [0.0, 1.0]\n");
return false;
}
} else if (param == "sim_refs") {
if (!(*fin_ >> knobs.sim_refs)) {
ERRMSG("Error reading sim_refs from the configuration file\n");
return false;
}
} else if (param == "cpu_scheduling") {
std::string bool_val;
if (!(*fin_ >> bool_val)) {
ERRMSG("Error reading cpu_scheduling from "
"the configuration file\n");
return false;
}
if (is_true(bool_val)) {
knobs.cpu_scheduling = true;
} else {
knobs.cpu_scheduling = false;
}
} else if (param == "verbose") {
if (!(*fin_ >> knobs.verbose)) {
ERRMSG("Error reading verbose from the configuration file\n");
return false;
}
} else if (param == "coherence") {
std::string bool_val;
if (!(*fin_ >> bool_val)) {
ERRMSG("Error reading coherence from the configuration file\n");
return false;
}
if (is_true(bool_val)) {
knobs.model_coherence = true;
} else {
knobs.model_coherence = false;
}
} else if (param == "use_physical") {
std::string bool_val;
if (!(*fin_ >> bool_val)) {
ERRMSG("Error reading use_physical from the configuration file\n");
return false;
}
if (is_true(bool_val)) {
knobs.use_physical = true;
} else {
knobs.use_physical = false;
}
} else {
cache_params_t cache;
cache.name = param;
if (!configure_cache(cache)) {
return false;
}
caches[cache.name] = cache;
}
if (!(*fin_ >> std::ws)) {
ERRMSG("Unable to read from the configuration file\n");
return false;
}
}
return check_cache_config(knobs.num_cores, caches);
}
bool
config_reader_t::configure_cache(cache_params_t &cache)
{
std::string error_msg;
char c;
if (!(*fin_ >> std::ws >> c)) {
ERRMSG("Unable to read from the configuration file\n");
return false;
}
if (c != '{') {
ERRMSG("Expected '{' before cache params\n");
return false;
}
while (!fin_->eof()) {
std::string param;
if (!(*fin_ >> std::ws >> param)) {
ERRMSG("Unable to read from the configuration file\n");
return false;
}
if (param == "}") {
return true;
} else if (param == "//") {
if (!getline(*fin_, param)) {
ERRMSG("Comment expected but not found\n");
return false;
}
} else if (param == "type") {
if (!(*fin_ >> cache.type)) {
ERRMSG("Error reading cache type from "
"the configuration file\n");
return false;
}
if (cache.type != CACHE_TYPE_INSTRUCTION && cache.type != CACHE_TYPE_DATA &&
cache.type != CACHE_TYPE_UNIFIED) {
ERRMSG("Unknown cache type: %s\n", cache.type.c_str());
return false;
}
} else if (param == "core") {
if (!(*fin_ >> cache.core)) {
ERRMSG("Error reading cache core from "
"the configuration file\n");
return false;
}
} else if (param == "size") {
std::string size_str;
if (!(*fin_ >> size_str)) {
ERRMSG("Error reading cache size from "
"the configuration file\n");
return false;
}
if (!convert_string_to_size(size_str, cache.size)) {
ERRMSG("Unusable cache size %s\n", size_str.c_str());
return false;
}
if (cache.size <= 0) {
ERRMSG("Cache size (%llu) must be >0\n", (unsigned long long)cache.size);
return false;
}
} else if (param == "assoc") {
if (!(*fin_ >> cache.assoc)) {
ERRMSG("Error reading cache assoc from "
"the configuration file\n");
return false;
}
if (cache.assoc <= 0) {
ERRMSG("Cache associativity (%u) must be >0\n", cache.assoc);
return false;
}
} else if (param == "inclusive") {
std::string bool_val;
if (!(*fin_ >> bool_val)) {
ERRMSG("Error reading cache inclusivity from "
"the configuration file\n");
return false;
}
if (is_true(bool_val)) {
cache.inclusive = true;
} else {
cache.inclusive = false;
}
} else if (param == "parent") {
if (!(*fin_ >> cache.parent)) {
ERRMSG("Error reading cache parent from "
"the configuration file\n");
return false;
}
} else if (param == "replace_policy") {
if (!(*fin_ >> cache.replace_policy)) {
ERRMSG("Error reading cache replace_policy from "
"the configuration file\n");
return false;
}
if (cache.replace_policy != REPLACE_POLICY_NON_SPECIFIED &&
cache.replace_policy != REPLACE_POLICY_LRU &&
cache.replace_policy != REPLACE_POLICY_LFU &&
cache.replace_policy != REPLACE_POLICY_FIFO) {
ERRMSG("Unknown replacement policy: %s\n", cache.replace_policy.c_str());
return false;
}
} else if (param == "prefetcher") {
if (!(*fin_ >> cache.prefetcher)) {
ERRMSG("Error reading cache prefetcher from "
"the configuration file\n");
return false;
}
if (cache.prefetcher != PREFETCH_POLICY_NEXTLINE &&
cache.prefetcher != PREFETCH_POLICY_NONE) {
ERRMSG("Unknown prefetcher type: %s\n", cache.prefetcher.c_str());
return false;
}
} else if (param == "miss_file") {
if (!(*fin_ >> cache.miss_file)) {
ERRMSG("Error reading cache miss_file from "
"the configuration file\n");
return false;
}
} else {
ERRMSG("Unknown cache configuration setting '%s'\n", param.c_str());
return false;
}
if (!(*fin_ >> std::ws)) {
ERRMSG("Unable to read from the configuration file\n");
return false;
}
}
ERRMSG("Expected '}' at the end of cache params\n");
return false;
}
bool
config_reader_t::check_cache_config(int num_cores,
std::map<std::string, cache_params_t> &caches_map)
{
std::vector<int> core_inst_caches(num_cores, 0);
std::vector<int> core_data_caches(num_cores, 0);
for (auto &cache_map : caches_map) {
std::string cache_name = cache_map.first;
auto &cache = cache_map.second;
if (cache.core >= 0) {
if (cache.core >= num_cores) {
ERRMSG("Cache %s belongs to core %d which does not exist\n",
cache_name.c_str(), cache.core);
return false;
}
if (cache.type == CACHE_TYPE_INSTRUCTION ||
cache.type == CACHE_TYPE_UNIFIED) {
core_inst_caches[cache.core]++;
}
if (cache.type == CACHE_TYPE_DATA || cache.type == CACHE_TYPE_UNIFIED) {
core_data_caches[cache.core]++;
}
}
if (cache.parent != CACHE_PARENT_MEMORY) {
auto parent_it = caches_map.find(cache.parent);
if (parent_it != caches_map.end()) {
auto &parent = parent_it->second;
if (parent.type != CACHE_TYPE_UNIFIED && cache.type != parent.type) {
ERRMSG("Cache %s and its parent have incompatible types\n",
cache_name.c_str());
return false;
}
parent.children.push_back(cache_name);
} else {
ERRMSG("Cache %s has a listed parent %s that does not exist\n",
cache_name.c_str(), cache.parent.c_str());
return false;
}
std::string parent = cache.parent;
while (parent != CACHE_PARENT_MEMORY) {
if (parent == cache_name) {
ERRMSG("Cache %s & its parent %s have a cyclic reference\n",
cache_name.c_str(), cache.parent.c_str());
return false;
}
parent = caches_map.find(parent)->second.parent;
}
}
}
for (int i = 0; i < num_cores; i++) {
if (core_inst_caches[i] != 1) {
ERRMSG("Core %d has %d instruction caches. Must have exactly 1.\n", i,
core_inst_caches[i]);
return false;
}
if (core_data_caches[i] != 1) {
ERRMSG("Core %d has %d data caches. Must have exactly 1.\n", i,
core_data_caches[i]);
return false;
}
}
return true;
}
bool
config_reader_t::convert_string_to_size(const std::string &s, uint64_t &size)
{
char suffix = *s.rbegin();
int scale;
switch (suffix) {
case 'K':
case 'k': scale = 1024; break;
case 'M':
case 'm': scale = 1024 * 1024; break;
case 'G':
case 'g': scale = 1024 * 1024 * 1024; break;
default: scale = 1;
}
std::string toparse = s;
if (scale > 1)
toparse = s.substr(0, s.size() - 1);
int input = atoi(toparse.c_str());
if (input >= 0)
size = (uint64_t)(input * scale);
else {
size = 0;
return false;
}
return true;
}
}
}