diff --git a/clients/drcachesim/reader/config_reader.cpp b/clients/drcachesim/reader/config_reader.cpp
index 1625224a2cca213149efc1bd597f3262382f0dbc..0c94e1bf22f867992135dac22eb95854a4ccffc8 100644
--- a/clients/drcachesim/reader/config_reader.cpp
+++ b/clients/drcachesim/reader/config_reader.cpp
@@ -40,40 +40,30 @@ config_reader_t::config_reader_t()
     /* Empty. */
 }
 
-config_reader_t::~config_reader_t()
-{
-    fin_.close();
-}
-
 bool
-config_reader_t::configure(const std::string &config_file, cache_simulator_knobs_t &knobs,
+config_reader_t::configure(std::istream *config_file, cache_simulator_knobs_t &knobs,
                            std::map<std::string, cache_params_t> &caches)
 {
-    // Open the config file.
-    fin_.open(config_file);
-    if (!fin_.is_open()) {
-        ERRMSG("Failed to open the config file '%s'\n", config_file.c_str());
-        return false;
-    }
+    fin_ = config_file;
 
     // Walk through the configuration file.
-    while (!fin_.eof()) {
+    while (!fin_->eof()) {
         std::string param;
 
-        if (!(fin_ >> ws >> param)) {
+        if (!(*fin_ >> ws >> param)) {
             ERRMSG("Unable to read from the configuration file\n");
             return false;
         }
 
         if (param == "//") {
             // A comment.
-            if (!getline(fin_, param)) {
+            if (!getline(*fin_, param)) {
                 ERRMSG("Comment expected but not found\n");
                 return false;
             }
         } else if (param == "num_cores") {
             // Number of cache cores.
-            if (!(fin_ >> knobs.num_cores)) {
+            if (!(*fin_ >> knobs.num_cores)) {
                 ERRMSG("Error reading num_cores from the configuration file\n");
                 return false;
             }
@@ -86,7 +76,7 @@ config_reader_t::configure(const std::string &config_file, cache_simulator_knobs
         // configure TLBs.
         else if (param == "line_size") {
             // Cache line size in bytes.
-            if (!(fin_ >> knobs.line_size)) {
+            if (!(*fin_ >> knobs.line_size)) {
                 ERRMSG("Error reading line_size from the configuration file\n");
                 return false;
             }
@@ -96,20 +86,20 @@ config_reader_t::configure(const std::string &config_file, cache_simulator_knobs
             }
         } else if (param == "skip_refs") {
             // Number of references to skip.
-            if (!(fin_ >> knobs.skip_refs)) {
+            if (!(*fin_ >> knobs.skip_refs)) {
                 ERRMSG("Error reading skip_refs from the configuration file\n");
                 return false;
             }
         } else if (param == "warmup_refs") {
             // Number of references to use for caches warmup.
-            if (!(fin_ >> knobs.warmup_refs)) {
+            if (!(*fin_ >> knobs.warmup_refs)) {
                 ERRMSG("Error reading warmup_refs from "
                        "the configuration file\n");
                 return false;
             }
         } else if (param == "warmup_fraction") {
             // Fraction of cache lines that must be filled to end the warmup.
-            if (!(fin_ >> knobs.warmup_fraction)) {
+            if (!(*fin_ >> knobs.warmup_fraction)) {
                 ERRMSG("Error reading warmup_fraction from "
                        "the configuration file\n");
                 return false;
@@ -120,14 +110,14 @@ config_reader_t::configure(const std::string &config_file, cache_simulator_knobs
             }
         } else if (param == "sim_refs") {
             // Number of references to simulate.
-            if (!(fin_ >> knobs.sim_refs)) {
+            if (!(*fin_ >> knobs.sim_refs)) {
                 ERRMSG("Error reading sim_refs from the configuration file\n");
                 return false;
             }
         } else if (param == "cpu_scheduling") {
             // Whether to simulate CPU scheduling or not.
             std::string bool_val;
-            if (!(fin_ >> bool_val)) {
+            if (!(*fin_ >> bool_val)) {
                 ERRMSG("Error reading cpu_scheduling from "
                        "the configuration file\n");
                 return false;
@@ -139,14 +129,14 @@ config_reader_t::configure(const std::string &config_file, cache_simulator_knobs
             }
         } else if (param == "verbose") {
             // Verbose level.
-            if (!(fin_ >> knobs.verbose)) {
+            if (!(*fin_ >> knobs.verbose)) {
                 ERRMSG("Error reading verbose from the configuration file\n");
                 return false;
             }
         } else if (param == "coherence") {
             // Whether to simulate coherence
             std::string bool_val;
-            if (!(fin_ >> bool_val)) {
+            if (!(*fin_ >> bool_val)) {
                 ERRMSG("Error reading coherence from the configuration file\n");
                 return false;
             }
@@ -165,7 +155,7 @@ config_reader_t::configure(const std::string &config_file, cache_simulator_knobs
             caches[cache.name] = cache;
         }
 
-        if (!(fin_ >> ws)) {
+        if (!(*fin_ >> ws)) {
             ERRMSG("Unable to read from the configuration file\n");
             return false;
         }
@@ -182,7 +172,7 @@ config_reader_t::configure_cache(cache_params_t &cache)
     std::string error_msg;
 
     char c;
-    if (!(fin_ >> ws >> c)) {
+    if (!(*fin_ >> ws >> c)) {
         ERRMSG("Unable to read from the configuration file\n");
         return false;
     }
@@ -191,9 +181,9 @@ config_reader_t::configure_cache(cache_params_t &cache)
         return false;
     }
 
-    while (!fin_.eof()) {
+    while (!fin_->eof()) {
         std::string param;
-        if (!(fin_ >> ws >> param)) {
+        if (!(*fin_ >> ws >> param)) {
             ERRMSG("Unable to read from the configuration file\n");
             return false;
         }
@@ -202,14 +192,14 @@ config_reader_t::configure_cache(cache_params_t &cache)
             return true;
         } else if (param == "//") {
             // A comment.
-            if (!getline(fin_, param)) {
+            if (!getline(*fin_, param)) {
                 ERRMSG("Comment expected but not found\n");
                 return false;
             }
         } else if (param == "type") {
             // Cache type: CACHE_TYPE_INSTRUCTION, CACHE_TYPE_DATA,
             // or CACHE_TYPE_UNIFIED.
-            if (!(fin_ >> cache.type)) {
+            if (!(*fin_ >> cache.type)) {
                 ERRMSG("Error reading cache type from "
                        "the configuration file\n");
                 return false;
@@ -221,7 +211,7 @@ config_reader_t::configure_cache(cache_params_t &cache)
             }
         } else if (param == "core") {
             // CPU core this cache is associated with.
-            if (!(fin_ >> cache.core)) {
+            if (!(*fin_ >> cache.core)) {
                 ERRMSG("Error reading cache core from "
                        "the configuration file\n");
                 return false;
@@ -229,7 +219,7 @@ config_reader_t::configure_cache(cache_params_t &cache)
         } else if (param == "size") {
             // Cache size in bytes.
             std::string size_str;
-            if (!(fin_ >> size_str)) {
+            if (!(*fin_ >> size_str)) {
                 ERRMSG("Error reading cache size from "
                        "the configuration file\n");
                 return false;
@@ -245,7 +235,7 @@ config_reader_t::configure_cache(cache_params_t &cache)
             }
         } else if (param == "assoc") {
             // Cache associativity_. Must be a power of 2.
-            if (!(fin_ >> cache.assoc)) {
+            if (!(*fin_ >> cache.assoc)) {
                 ERRMSG("Error reading cache assoc from "
                        "the configuration file\n");
                 return false;
@@ -258,7 +248,7 @@ config_reader_t::configure_cache(cache_params_t &cache)
         } else if (param == "inclusive") {
             // Is the cache inclusive of its children.
             std::string bool_val;
-            if (!(fin_ >> bool_val)) {
+            if (!(*fin_ >> bool_val)) {
                 ERRMSG("Error reading cache inclusivity from "
                        "the configuration file\n");
                 return false;
@@ -271,7 +261,7 @@ config_reader_t::configure_cache(cache_params_t &cache)
         } else if (param == "parent") {
             // Name of the cache's parent. LLC's parent is main memory
             // (CACHE_PARENT_MEMORY).
-            if (!(fin_ >> cache.parent)) {
+            if (!(*fin_ >> cache.parent)) {
                 ERRMSG("Error reading cache parent from "
                        "the configuration file\n");
                 return false;
@@ -279,7 +269,7 @@ config_reader_t::configure_cache(cache_params_t &cache)
         } else if (param == "replace_policy") {
             // Cache replacement policy: REPLACE_POLICY_LRU (default),
             // REPLACE_POLICY_LFU or REPLACE_POLICY_FIFO.
-            if (!(fin_ >> cache.replace_policy)) {
+            if (!(*fin_ >> cache.replace_policy)) {
                 ERRMSG("Error reading cache replace_policy from "
                        "the configuration file\n");
                 return false;
@@ -294,7 +284,7 @@ config_reader_t::configure_cache(cache_params_t &cache)
         } else if (param == "prefetcher") {
             // Type of prefetcher: PREFETCH_POLICY_NEXTLINE
             // or PREFETCH_POLICY_NONE.
-            if (!(fin_ >> cache.prefetcher)) {
+            if (!(*fin_ >> cache.prefetcher)) {
                 ERRMSG("Error reading cache prefetcher from "
                        "the configuration file\n");
                 return false;
@@ -306,7 +296,7 @@ config_reader_t::configure_cache(cache_params_t &cache)
             }
         } else if (param == "miss_file") {
             // Name of the file to use to dump cache misses info.
-            if (!(fin_ >> cache.miss_file)) {
+            if (!(*fin_ >> cache.miss_file)) {
                 ERRMSG("Error reading cache miss_file from "
                        "the configuration file\n");
                 return false;
@@ -316,7 +306,7 @@ config_reader_t::configure_cache(cache_params_t &cache)
             return false;
         }
 
-        if (!(fin_ >> ws)) {
+        if (!(*fin_ >> ws)) {
             ERRMSG("Unable to read from the configuration file\n");
             return false;
         }
diff --git a/clients/drcachesim/reader/config_reader.h b/clients/drcachesim/reader/config_reader.h
index 6c04baaec8d2e4a2a81b39074905bc79a74bef2e..a407a1a240c6837ad0ce8c9f4e48479a2d771f8f 100644
--- a/clients/drcachesim/reader/config_reader.h
+++ b/clients/drcachesim/reader/config_reader.h
@@ -93,13 +93,12 @@ struct cache_params_t {
 class config_reader_t {
 public:
     config_reader_t();
-    ~config_reader_t();
     bool
-    configure(const std::string &config_file, cache_simulator_knobs_t &knobs,
+    configure(std::istream *config_file, cache_simulator_knobs_t &knobs,
               std::map<std::string, cache_params_t> &caches);
 
 private:
-    std::ifstream fin_;
+    std::istream *fin_;
 
     bool
     configure_cache(cache_params_t &cache);
diff --git a/clients/drcachesim/simulator/cache_simulator.cpp b/clients/drcachesim/simulator/cache_simulator.cpp
index 1d8292bf555f5bc9485a45450b24b63c926fc5ae..e9d271dfebfa0b7d713a685234e1e229e9316a79 100644
--- a/clients/drcachesim/simulator/cache_simulator.cpp
+++ b/clients/drcachesim/simulator/cache_simulator.cpp
@@ -60,7 +60,15 @@ cache_simulator_create(const cache_simulator_knobs_t &knobs)
 analysis_tool_t *
 cache_simulator_create(const std::string &config_file)
 {
-    return new cache_simulator_t(config_file);
+    std::ifstream fin;
+    fin.open(config_file);
+    if (!fin.is_open()) {
+        ERRMSG("Failed to open the config file '%s'\n", config_file.c_str());
+        return nullptr;
+    }
+    analysis_tool_t *sim = new cache_simulator_t(&fin);
+    fin.close();
+    return sim;
 }
 
 cache_simulator_t::cache_simulator_t(const cache_simulator_knobs_t &knobs)
@@ -164,7 +172,7 @@ cache_simulator_t::cache_simulator_t(const cache_simulator_knobs_t &knobs)
     }
 }
 
-cache_simulator_t::cache_simulator_t(const std::string &config_file)
+cache_simulator_t::cache_simulator_t(std::istream *config_file)
     : simulator_t()
     , l1_icaches_(NULL)
     , l1_dcaches_(NULL)
@@ -175,8 +183,7 @@ cache_simulator_t::cache_simulator_t(const std::string &config_file)
     std::map<std::string, cache_params_t> cache_params;
     config_reader_t config_reader;
     if (!config_reader.configure(config_file, knobs_, cache_params)) {
-        error_string_ =
-            "Usage error: Failed to read/parse configuration file " + config_file;
+        error_string_ = "Usage error: Failed to read/parse configuration file";
         success_ = false;
         return;
     }
diff --git a/clients/drcachesim/simulator/cache_simulator.h b/clients/drcachesim/simulator/cache_simulator.h
index 62ee67a2c927a665792204a0821330a29df86b15..b70825a1334c78087dc55ae268c088e245800bc1 100644
--- a/clients/drcachesim/simulator/cache_simulator.h
+++ b/clients/drcachesim/simulator/cache_simulator.h
@@ -52,7 +52,7 @@ public:
 
     // This constructor is used when the arbitrary cache hierarchy is
     // defined in a configuration file.
-    cache_simulator_t(const std::string &config_file);
+    cache_simulator_t(std::istream *config_file);
 
     virtual ~cache_simulator_t();
     bool