From 7d460cef3f77380d4fd3a9fb4dba25d4fccfd3da Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 19:51:50 +0000 Subject: [PATCH] Fix crashes and memory leaks reachable from documented commands --- libCacheSim/bin/cli_reader_utils.c | 6 ++ libCacheSim/bin/mrcProfiler/cli_parser.cpp | 10 +-- libCacheSim/bin/traceAnalyzer/cli_parser.cpp | 5 +- libCacheSim/cache/eviction/ARC.c | 1 + libCacheSim/cache/eviction/ARCv0.c | 1 + libCacheSim/cache/eviction/BeladySize.c | 1 + libCacheSim/cache/eviction/CAR.c | 1 + libCacheSim/cache/eviction/Cacheus.c | 7 ++- libCacheSim/cache/eviction/Clock.c | 1 + libCacheSim/cache/eviction/Clock2QPlus.c | 4 +- libCacheSim/cache/eviction/ClockPro.c | 1 + libCacheSim/cache/eviction/FIFO_Merge.c | 1 + libCacheSim/cache/eviction/FIFO_Reinsertion.c | 1 + libCacheSim/cache/eviction/GLCache/GLCache.c | 4 ++ libCacheSim/cache/eviction/Hyperbolic.c | 1 + libCacheSim/cache/eviction/LRUProb.c | 1 + libCacheSim/cache/eviction/LeCaR.c | 1 + libCacheSim/cache/eviction/MQ.c | 1 + libCacheSim/cache/eviction/QDLP.c | 7 ++- libCacheSim/cache/eviction/RandomLRU.c | 2 + libCacheSim/cache/eviction/S3FIFO.c | 1 + libCacheSim/cache/eviction/S3FIFOd.c | 16 ++++- libCacheSim/cache/eviction/S3FIFOv0.c | 7 ++- libCacheSim/cache/eviction/SLRU.c | 36 +++++++++-- libCacheSim/cache/eviction/SLRUv0.c | 14 ++++- libCacheSim/cache/eviction/Size.c | 1 + libCacheSim/cache/eviction/TwoQ.c | 1 + libCacheSim/cache/eviction/WTinyLFU.c | 61 ++++++++++++++++--- libCacheSim/cache/eviction/cpp/LRU_K.cpp | 1 + libCacheSim/cache/eviction/fifo/LP_ARC.c | 1 + libCacheSim/cache/eviction/fifo/LP_SFIFO.c | 7 ++- libCacheSim/cache/eviction/fifo/LP_TwoQ.c | 1 + libCacheSim/cache/eviction/fifo/SFIFO.c | 1 + libCacheSim/cache/eviction/fifo/SFIFOv0.c | 7 ++- libCacheSim/cache/eviction/other/S3LRU.c | 1 + libCacheSim/cache/eviction/other/flashProb.c | 12 ++-- libCacheSim/cache/eviction/plugin_cache.c | 5 +- libCacheSim/cache/prefetch/Mithril.c | 4 ++ libCacheSim/cache/prefetch/PG.c | 4 ++ 39 files changed, 201 insertions(+), 37 deletions(-) diff --git a/libCacheSim/bin/cli_reader_utils.c b/libCacheSim/bin/cli_reader_utils.c index eff306813..4b36a6fdf 100644 --- a/libCacheSim/bin/cli_reader_utils.c +++ b/libCacheSim/bin/cli_reader_utils.c @@ -58,6 +58,12 @@ trace_type_e trace_type_str_to_enum(const char *trace_type_str, } bool is_true(const char *arg) { + /* options declared OPTION_ARG_OPTIONAL are passed a NULL arg when the bare + * flag is used, e.g. `--verbose`; treat the flag's presence as true */ + if (arg == NULL) { + return true; + } + if (strcasecmp(arg, "true") == 0 || strcasecmp(arg, "1") == 0 || strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "y") == 0) { return true; diff --git a/libCacheSim/bin/mrcProfiler/cli_parser.cpp b/libCacheSim/bin/mrcProfiler/cli_parser.cpp index 43f2788f3..18e810bbb 100644 --- a/libCacheSim/bin/mrcProfiler/cli_parser.cpp +++ b/libCacheSim/bin/mrcProfiler/cli_parser.cpp @@ -73,24 +73,24 @@ static struct argp_option options[] = { 1}, {NULL, 0, NULL, 0, "mrc profiler options:", 0}, - {"algo", OPTION_CACHE_ALGORITHM, "LRU", OPTION_ARG_OPTIONAL, + {"algo", OPTION_CACHE_ALGORITHM, "ALGO", 0, "Which algorithm to profile. Only Support LRU for SHARDS.", 2}, - {"size", OPTION_MRC_SIZE, "0.01,1,100", OPTION_ARG_OPTIONAL, + {"size", OPTION_MRC_SIZE, "SIZES", 0, "MRC profile size. Support two formats " "[start_size,end_size,#test_points|size1,size2,size3,...,size_n]. For " "size settings, both explicit sizes (e.g., 1GiB) and WSS-based sizes (a " "floating-point number between 0 and 1) are supported.", 2}, - {"profiler", OPTION_PROFILER, "SHARDS", OPTION_ARG_OPTIONAL, + {"profiler", OPTION_PROFILER, "PROFILER", 0, "Which profiler to use. Support SHARDS|MINISIM", 2}, - {"profiler-params", OPTION_PROFILER_PARAMS, "", OPTION_ARG_OPTIONAL, + {"profiler-params", OPTION_PROFILER_PARAMS, "PARAMS", 0, "Profiler parameters. ", 2}, {"ignore-obj-size", OPTION_IGNORE_OBJ_SIZE, NULL, OPTION_ARG_OPTIONAL, "Ignore object size", 2}, {NULL, 0, NULL, 0, "common parameters:", 0}, - {"output", OPTION_OUTPUT_PATH, "", OPTION_ARG_OPTIONAL, "Output path", 3}, + {"output", OPTION_OUTPUT_PATH, "PATH", 0, "Output path", 3}, {"verbose", OPTION_VERBOSE, NULL, OPTION_ARG_OPTIONAL, "Produce verbose output", 3}, {NULL, 0, NULL, 0, NULL, 0}}; diff --git a/libCacheSim/bin/traceAnalyzer/cli_parser.cpp b/libCacheSim/bin/traceAnalyzer/cli_parser.cpp index 588e48665..7251abc92 100644 --- a/libCacheSim/bin/traceAnalyzer/cli_parser.cpp +++ b/libCacheSim/bin/traceAnalyzer/cli_parser.cpp @@ -110,7 +110,7 @@ static struct argp_option options[] = { {NULL, 0, NULL, 0, "common parameters:", 0}, - {"output", OPTION_OUTPUT_PATH, "", OPTION_ARG_OPTIONAL, "Output path", 8}, + {"output", OPTION_OUTPUT_PATH, "PATH", 0, "Output path", 8}, {"verbose", OPTION_VERBOSE, NULL, OPTION_ARG_OPTIONAL, "Produce verbose output", 8}, {NULL, 0, NULL, 0, NULL, 0}}; @@ -219,7 +219,8 @@ static char args_doc[] = "trace_path trace_type [--task1] [--task2] ..."; /* Program documentation. */ static char doc[] = - "example: ./bin/traceAnalyzer ../data/trace.vscsi vscsi --common\n\n" + "example: ./bin/traceAnalyzer ../data/cloudPhysicsIO.vscsi vscsi " + "--common\n\n" "trace_type: txt/csv/twr/vscsi/oracleGeneralBin and more\n" "if using csv trace, considering specifying -t obj-id-is-num=true\n\n" "task: " diff --git a/libCacheSim/cache/eviction/ARC.c b/libCacheSim/cache/eviction/ARC.c index c8b22d88e..2b25147f4 100644 --- a/libCacheSim/cache/eviction/ARC.c +++ b/libCacheSim/cache/eviction/ARC.c @@ -627,6 +627,7 @@ static void ARC_parse_params(cache_t *cache, if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", ARC_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/ARCv0.c b/libCacheSim/cache/eviction/ARCv0.c index 98e8c6831..e4613062b 100644 --- a/libCacheSim/cache/eviction/ARCv0.c +++ b/libCacheSim/cache/eviction/ARCv0.c @@ -544,6 +544,7 @@ static void ARCv0_parse_params(cache_t *cache, if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", ARCv0_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/BeladySize.c b/libCacheSim/cache/eviction/BeladySize.c index 78f6d0236..d0ebb00fa 100644 --- a/libCacheSim/cache/eviction/BeladySize.c +++ b/libCacheSim/cache/eviction/BeladySize.c @@ -320,6 +320,7 @@ static void BeladySize_parse_params(cache_t *cache, } } else if (strcasecmp(key, "print") == 0) { printf("current parameters: %s\n", BeladySize_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s, support %s\n", cache->cache_name, diff --git a/libCacheSim/cache/eviction/CAR.c b/libCacheSim/cache/eviction/CAR.c index 74eaea502..8b4c23da7 100644 --- a/libCacheSim/cache/eviction/CAR.c +++ b/libCacheSim/cache/eviction/CAR.c @@ -488,6 +488,7 @@ static void CAR_parse_params(cache_t *cache, } } else if (strcasecmp(key, "print") == 0) { printf("current parameters: %s\n", CAR_current_params(cache, params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s, example parameters %s\n", diff --git a/libCacheSim/cache/eviction/Cacheus.c b/libCacheSim/cache/eviction/Cacheus.c index 30b86be3f..541fc6fb4 100644 --- a/libCacheSim/cache/eviction/Cacheus.c +++ b/libCacheSim/cache/eviction/Cacheus.c @@ -72,7 +72,12 @@ cache_t *Cacheus_init(const common_cache_params_t ccache_params, const char *cache_specific_params) { common_cache_params_t updated_cc_params = ccache_params; /* reduce the hash table size */ - updated_cc_params.hashpower -= 2; + /* only shrink an explicitly requested hash power: cache_struct_init reads + * a non-positive value as "use the default", and clamping would turn that + * sentinel into a 16-bucket table. */ + if (updated_cc_params.hashpower > 0) { + updated_cc_params.hashpower = MAX(4, updated_cc_params.hashpower - 2); + } cache_t *cache = cache_struct_init("Cacheus", updated_cc_params, cache_specific_params); diff --git a/libCacheSim/cache/eviction/Clock.c b/libCacheSim/cache/eviction/Clock.c index d6c6a4003..d13190bfa 100644 --- a/libCacheSim/cache/eviction/Clock.c +++ b/libCacheSim/cache/eviction/Clock.c @@ -328,6 +328,7 @@ static void Clock_parse_params(cache_t *cache, } } else if (strcasecmp(key, "print") == 0) { printf("current parameters: %s\n", Clock_current_params(cache, params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s, example parameters %s\n", diff --git a/libCacheSim/cache/eviction/Clock2QPlus.c b/libCacheSim/cache/eviction/Clock2QPlus.c index 7b6bb4658..373664fcf 100644 --- a/libCacheSim/cache/eviction/Clock2QPlus.c +++ b/libCacheSim/cache/eviction/Clock2QPlus.c @@ -491,7 +491,8 @@ static void Clock2QPlus_parse_params(cache_t *cache, params_str++; } - if (key == NULL || value == NULL) { + /* "print" is a bare flag, not a key=value pair */ + if (key == NULL || (value == NULL && strcasecmp(key, "print") != 0)) { ERROR("invalid parameter string: missing key or value\n"); exit(1); } @@ -506,6 +507,7 @@ static void Clock2QPlus_parse_params(cache_t *cache, params->move_to_main_threshold = atoi(value); } else if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", Clock2QPlus_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/ClockPro.c b/libCacheSim/cache/eviction/ClockPro.c index c1fa84273..6e070c764 100644 --- a/libCacheSim/cache/eviction/ClockPro.c +++ b/libCacheSim/cache/eviction/ClockPro.c @@ -514,6 +514,7 @@ static void ClockPro_parse_params(cache_t *cache, } else if (strcasecmp(key, "print") == 0) { printf("current parameters: %s\n", ClockPro_current_params(cache, params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/FIFO_Merge.c b/libCacheSim/cache/eviction/FIFO_Merge.c index 763ab61f3..afc358635 100644 --- a/libCacheSim/cache/eviction/FIFO_Merge.c +++ b/libCacheSim/cache/eviction/FIFO_Merge.c @@ -386,6 +386,7 @@ static void FIFO_Merge_parse_params(cache_t *cache, } else if (strcasecmp(key, "print") == 0) { printf("%s parameters: %s\n", cache->cache_name, FIFO_Merge_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/FIFO_Reinsertion.c b/libCacheSim/cache/eviction/FIFO_Reinsertion.c index 3d68a7848..279ae3bd6 100644 --- a/libCacheSim/cache/eviction/FIFO_Reinsertion.c +++ b/libCacheSim/cache/eviction/FIFO_Reinsertion.c @@ -409,6 +409,7 @@ static void FIFO_Reinsertion_parse_params(cache_t *cache, } else if (strcasecmp(key, "print") == 0) { printf("%s parameters: %s\n", cache->cache_name, FIFO_Reinsertion_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/GLCache/GLCache.c b/libCacheSim/cache/eviction/GLCache/GLCache.c index 12ef0c56b..ba788204b 100644 --- a/libCacheSim/cache/eviction/GLCache/GLCache.c +++ b/libCacheSim/cache/eviction/GLCache/GLCache.c @@ -59,6 +59,7 @@ const char *GLCache_default_params(void) { static void GLCache_parse_init_params(const char *cache_specific_params, GLCache_params_t *params) { char *params_str = strdup(cache_specific_params); + char *old_params_str = params_str; while (params_str != NULL && params_str[0] != '\0') { char *key = strsep((char **)¶ms_str, "="); @@ -104,13 +105,16 @@ static void GLCache_parse_init_params(const char *cache_specific_params, } else if (strcasecmp(key, "print") == 0 || strcasecmp(key, "default") == 0) { printf("default params: %s\n", GLCache_default_params()); + free(old_params_str); exit(0); } else { ERROR("GLCache does not have parameter %s\n", key); printf("default params: %s\n", GLCache_default_params()); + free(old_params_str); exit(1); } } + free(old_params_str); } // *********************************************************************** diff --git a/libCacheSim/cache/eviction/Hyperbolic.c b/libCacheSim/cache/eviction/Hyperbolic.c index fb4badc80..462e6eddb 100644 --- a/libCacheSim/cache/eviction/Hyperbolic.c +++ b/libCacheSim/cache/eviction/Hyperbolic.c @@ -276,6 +276,7 @@ static void Hyperbolic_parse_params(cache_t *cache, } } else if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", Hyperbolic_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s, support %s\n", cache->cache_name, diff --git a/libCacheSim/cache/eviction/LRUProb.c b/libCacheSim/cache/eviction/LRUProb.c index 23ada9e8c..1bc32bc40 100644 --- a/libCacheSim/cache/eviction/LRUProb.c +++ b/libCacheSim/cache/eviction/LRUProb.c @@ -280,6 +280,7 @@ static void LRU_Prob_parse_params(cache_t *cache, } else if (strcasecmp(key, "print") == 0) { printf("current parameters: %s\n", LRU_Prob_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/LeCaR.c b/libCacheSim/cache/eviction/LeCaR.c index eacf53358..228cd1dd7 100644 --- a/libCacheSim/cache/eviction/LeCaR.c +++ b/libCacheSim/cache/eviction/LeCaR.c @@ -621,6 +621,7 @@ static void LeCaR_parse_params(cache_t *cache, params->w_lru = (double)strtod(value, &end); } else if (strcasecmp(key, "print") == 0) { printf("current parameters: %s\n", LeCaR_current_params(cache, params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/MQ.c b/libCacheSim/cache/eviction/MQ.c index 727f2ac9f..32e3a6748 100644 --- a/libCacheSim/cache/eviction/MQ.c +++ b/libCacheSim/cache/eviction/MQ.c @@ -505,6 +505,7 @@ static void MQ_parse_params(cache_t *cache, const char *cache_specific_params) { } } else if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", MQ_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/QDLP.c b/libCacheSim/cache/eviction/QDLP.c index b1edd82fd..d42b12ab5 100644 --- a/libCacheSim/cache/eviction/QDLP.c +++ b/libCacheSim/cache/eviction/QDLP.c @@ -437,8 +437,12 @@ static inline bool QDLP_can_insert(cache_t *cache, const request_t *req) { // *********************************************************************** static const char *QDLP_current_params(QDLP_params_t *params) { static __thread char params_str[128]; + /* main_cache is only built after the parameters are parsed, so report the + * configured type, which is what `-e print` runs against */ snprintf(params_str, 128, "fifo-size-ratio=%.4lf,main-cache=%s\n", - params->small_size_ratio, params->main_cache->cache_name); + params->small_size_ratio, + params->main_cache == NULL ? params->main_cache_type + : params->main_cache->cache_name); return params_str; } @@ -470,6 +474,7 @@ static void QDLP_parse_params(cache_t *cache, strncpy(params->main_cache_type, value, 30); } else if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", QDLP_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/RandomLRU.c b/libCacheSim/cache/eviction/RandomLRU.c index 450157d24..8293e7b74 100644 --- a/libCacheSim/cache/eviction/RandomLRU.c +++ b/libCacheSim/cache/eviction/RandomLRU.c @@ -98,6 +98,7 @@ cache_t *RandomLRU_init(const common_cache_params_t ccache_params, static void RandomLRU_free(cache_t *cache) { RandomLRU_params_t *params = (RandomLRU_params_t *)(cache->eviction_params); free(params->eviction_candidates); + free(params); cache_struct_free(cache); } @@ -273,6 +274,7 @@ static void RandomLRU_parse_params(cache_t *cache, params->n_samples = (int)strtol(value, &end, 0); } else if (strcasecmp(key, "print") == 0) { printf("current parameters: n-samples=%d\n", params->n_samples); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/S3FIFO.c b/libCacheSim/cache/eviction/S3FIFO.c index bcbde8a93..0a3288212 100644 --- a/libCacheSim/cache/eviction/S3FIFO.c +++ b/libCacheSim/cache/eviction/S3FIFO.c @@ -475,6 +475,7 @@ static void S3FIFO_parse_params(cache_t *cache, params->move_to_main_threshold = atoi(value); } else if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", S3FIFO_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/S3FIFOd.c b/libCacheSim/cache/eviction/S3FIFOd.c index f04b8c75c..3c2e63641 100644 --- a/libCacheSim/cache/eviction/S3FIFOd.c +++ b/libCacheSim/cache/eviction/S3FIFOd.c @@ -152,7 +152,11 @@ cache_t *S3FIFOd_init(const common_cache_params_t ccache_params, } ccache_params_local.cache_size = ccache_params.cache_size / 10; - ccache_params_local.hashpower -= 4; + /* see Cacheus_init: a non-positive hash power is the "use the default" + * sentinel and must survive untouched. */ + if (ccache_params_local.hashpower > 0) { + ccache_params_local.hashpower = MAX(4, ccache_params_local.hashpower - 4); + } params->small_eviction = FIFO_init(ccache_params_local, NULL); params->main_eviction = FIFO_init(ccache_params_local, NULL); snprintf(params->small_eviction->cache_name, CACHE_NAME_ARRAY_LEN, @@ -185,6 +189,9 @@ static void S3FIFOd_free(cache_t *cache) { params->small_fifo->cache_free(params->small_fifo); params->ghost_fifo->cache_free(params->ghost_fifo); params->main_fifo->cache_free(params->main_fifo); + /* init also builds these two to track evicted objects */ + params->small_eviction->cache_free(params->small_eviction); + params->main_eviction->cache_free(params->main_eviction); free(cache->eviction_params); cache_struct_free(cache); } @@ -530,8 +537,12 @@ static inline bool S3FIFOd_can_insert(cache_t *cache, const request_t *req) { // *********************************************************************** static const char *S3FIFOd_current_params(S3FIFOd_params_t *params) { static __thread char params_str[128]; + /* main_fifo is only built after the parameters are parsed, so report the + * configured type, which is what `-e print` runs against */ snprintf(params_str, 128, "fifo-size-ratio=%.4lf,main-cache=%s\n", - params->small_fifo_size_ratio, params->main_fifo->cache_name); + params->small_fifo_size_ratio, + params->main_fifo == NULL ? params->main_fifo_type + : params->main_fifo->cache_name); return params_str; } @@ -563,6 +574,7 @@ static void S3FIFOd_parse_params(cache_t *cache, params->move_to_main_threshold = atoi(value); } else if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", S3FIFOd_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/S3FIFOv0.c b/libCacheSim/cache/eviction/S3FIFOv0.c index a9caf2a51..2bd01b988 100644 --- a/libCacheSim/cache/eviction/S3FIFOv0.c +++ b/libCacheSim/cache/eviction/S3FIFOv0.c @@ -478,8 +478,12 @@ static inline bool S3FIFOv0_can_insert(cache_t *cache, const request_t *req) { // *********************************************************************** static const char *S3FIFOv0_current_params(S3FIFOv0_params_t *params) { static __thread char params_str[128]; + /* main_fifo is only built after the parameters are parsed, so it is still + * NULL when the user asks for the parameters with `-e print`; it is always a + * plain FIFO in this variant */ snprintf(params_str, 128, "small-size-ratio=%.4lf,main-cache=%s\n", - params->small_size_ratio, params->main_fifo->cache_name); + params->small_size_ratio, + params->main_fifo == NULL ? "FIFO" : params->main_fifo->cache_name); return params_str; } @@ -510,6 +514,7 @@ static void S3FIFOv0_parse_params(cache_t *cache, params->move_to_main_threshold = atoi(value); } else if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", S3FIFOv0_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/SLRU.c b/libCacheSim/cache/eviction/SLRU.c index 75c1136df..6390e00bd 100644 --- a/libCacheSim/cache/eviction/SLRU.c +++ b/libCacheSim/cache/eviction/SLRU.c @@ -403,14 +403,28 @@ static bool SLRU_remove(cache_t *cache, obj_id_t obj_id) { // **** parameter set up functions **** // **** **** // *********************************************************************** +/* share of the cache given to one segment, in percent. lru_max_n_bytes is only + * allocated after the parameters are parsed, so it is still NULL when the user + * asks for the parameters with `-e print`; until seg-size says otherwise the + * segments are evenly sized */ +static int SLRU_seg_pct(const cache_t *cache, const SLRU_params_t *params, + const int seg) { + if (params->lru_max_n_bytes == NULL) { + return 100 / params->n_seg; + } + return (int)(params->lru_max_n_bytes[seg] * 100 / cache->cache_size); +} + static const char *SLRU_current_params(cache_t *cache, SLRU_params_t *params) { static __thread char params_str[128]; - int n = snprintf(params_str, 128, "n-seg=%d,seg-size=%d", params->n_seg, - (int)(params->lru_max_n_bytes[0] * 100 / cache->cache_size)); - for (int i = 1; i < params->n_seg; i++) { - n += snprintf(params_str + n, 128 - n, ":%d", - (int)(params->lru_max_n_bytes[i] * 100 / cache->cache_size)); + int n = snprintf(params_str, sizeof(params_str), "n-seg=%d,seg-size=%d", + params->n_seg, SLRU_seg_pct(cache, params, 0)); + + for (int i = 1; i < params->n_seg && n > 0 && n < (int)sizeof(params_str); + i++) { + n += snprintf(params_str + n, sizeof(params_str) - n, ":%d", + SLRU_seg_pct(cache, params, i)); } return params_str; @@ -439,16 +453,27 @@ static void SLRU_parse_params(cache_t *cache, if (strlen(end) > 2) { ERROR("param parsing error, find string \"%s\" after number\n", end); } + /* n_seg divides the cache size and the reported percentages */ + if (params->n_seg < 1 || params->n_seg > SLRU_MAX_N_SEG) { + ERROR("n-seg must be between 1 and %d, got %d\n", SLRU_MAX_N_SEG, + params->n_seg); + } } else if (strcasecmp(key, "seg-size") == 0) { int n_seg = 0; int64_t seg_size_sum = 0; int64_t seg_size_array[SLRU_MAX_N_SEG]; char *v = strsep((char **)&value, ":"); while (v != NULL) { + if (n_seg >= SLRU_MAX_N_SEG) { + ERROR("seg-size accepts at most %d segments\n", SLRU_MAX_N_SEG); + } seg_size_array[n_seg++] = (int64_t)strtol(v, &end, 0); seg_size_sum += seg_size_array[n_seg - 1]; v = strsep((char **)&value, ":"); } + if (n_seg < 1 || seg_size_sum <= 0) { + ERROR("seg-size needs at least one segment with a positive size\n"); + } params->n_seg = n_seg; params->lru_max_n_bytes = calloc(params->n_seg, sizeof(int64_t)); for (int i = 0; i < n_seg; i++) { @@ -462,6 +487,7 @@ static void SLRU_parse_params(cache_t *cache, } } else if (strcasecmp(key, "print") == 0) { printf("current parameters: %s\n", SLRU_current_params(cache, params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/SLRUv0.c b/libCacheSim/cache/eviction/SLRUv0.c index 190c2ba79..aad375fa2 100644 --- a/libCacheSim/cache/eviction/SLRUv0.c +++ b/libCacheSim/cache/eviction/SLRUv0.c @@ -82,6 +82,7 @@ cache_t *SLRUv0_init(const common_cache_params_t ccache_params, cache->eviction_params = (SLRUv0_params_t *)malloc(sizeof(SLRUv0_params_t)); SLRUv0_params_t *params = (SLRUv0_params_t *)(cache->eviction_params); + memset(params, 0, sizeof(SLRUv0_params_t)); SLRUv0_parse_params(cache, DEFAULT_CACHE_PARAMS); if (cache_specific_params != NULL) { @@ -92,7 +93,12 @@ cache_t *SLRUv0_init(const common_cache_params_t ccache_params, common_cache_params_t ccache_params_local = ccache_params; ccache_params_local.cache_size /= params->n_seg; - ccache_params_local.hashpower = MIN(16, ccache_params_local.hashpower - 4); + /* see Cacheus_init: a non-positive hash power is the "use the default" + * sentinel and must survive untouched. */ + if (ccache_params_local.hashpower > 0) { + ccache_params_local.hashpower = + MAX(4, MIN(16, ccache_params_local.hashpower - 4)); + } params->LRUs[0] = LRU_init(ccache_params_local, NULL); for (int i = 1; i < params->n_seg; i++) { params->LRUs[i] = LRU_init(ccache_params_local, NULL); @@ -113,6 +119,7 @@ static void SLRUv0_free(cache_t *cache) { for (int i = 0; i < params->n_seg; i++) params->LRUs[i]->cache_free(params->LRUs[i]); free(params->LRUs); + free(params); cache_struct_free(cache); } @@ -373,6 +380,7 @@ static void SLRUv0_parse_params(cache_t *cache, } else if (strcasecmp(key, "print") == 0) { printf("current parameters: %s\n", SLRUv0_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); @@ -396,7 +404,6 @@ static void SLRUv0_parse_params(cache_t *cache, */ static void SLRUv0_cool(cache_t *cache, const request_t *req, int i) { SLRUv0_params_t *params = (SLRUv0_params_t *)(cache->eviction_params); - request_t *saved_req = new_request(); cache_t *lru = params->LRUs[i]; // the last LRU is evict-only, do not move to a lower lru if (i == 0) { @@ -404,6 +411,9 @@ static void SLRUv0_cool(cache_t *cache, const request_t *req, int i) { return; }; + // only needed once we know the object is moving to a lower lru + request_t *saved_req = new_request(); + // the evicted object move to lower lru cache_obj_t *obj_evicted = lru->to_evict(lru, req); copy_cache_obj_to_request(saved_req, obj_evicted); diff --git a/libCacheSim/cache/eviction/Size.c b/libCacheSim/cache/eviction/Size.c index 4a7489502..9cc86ad1c 100644 --- a/libCacheSim/cache/eviction/Size.c +++ b/libCacheSim/cache/eviction/Size.c @@ -85,6 +85,7 @@ static void Size_free(cache_t *cache) { node = pqueue_pop(params->pq); } pqueue_free(params->pq); + my_free(sizeof(Size_params_t), params); cache_struct_free(cache); } diff --git a/libCacheSim/cache/eviction/TwoQ.c b/libCacheSim/cache/eviction/TwoQ.c index 31b8d6bfb..c58602be2 100644 --- a/libCacheSim/cache/eviction/TwoQ.c +++ b/libCacheSim/cache/eviction/TwoQ.c @@ -352,6 +352,7 @@ static void TwoQ_parse_params(cache_t *cache, params->Aout_size_ratio = strtod(value, NULL); } else if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", TwoQ_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/WTinyLFU.c b/libCacheSim/cache/eviction/WTinyLFU.c index 891911259..752ab3a1d 100644 --- a/libCacheSim/cache/eviction/WTinyLFU.c +++ b/libCacheSim/cache/eviction/WTinyLFU.c @@ -96,13 +96,11 @@ cache_t *WTinyLFU_init(const common_cache_params_t ccache_params, cache->eviction_params = (WTinyLFU_params_t *)malloc(sizeof(WTinyLFU_params_t)); WTinyLFU_params_t *params = (WTinyLFU_params_t *)(cache->eviction_params); + memset(params, 0, sizeof(WTinyLFU_params_t)); - if (ccache_params.consider_obj_metadata) { - cache->obj_md_size = params->main_cache->obj_md_size; - // TODO: not sure whether it works - } else { - cache->obj_md_size = 0; - } + /* obj_md_size is set once main_cache exists; it is read from main_cache, + * which is only built further down */ + cache->obj_md_size = 0; WTinyLFU_parse_params(cache, DEFAULT_PARAMS); if (cache_specific_params != NULL) { @@ -144,6 +142,24 @@ cache_t *WTinyLFU_init(const common_cache_params_t ccache_params, ERROR("WTinyLFU does not support %s \n", params->main_cache_type); } + if (ccache_params.consider_obj_metadata) { + /* The window and the main cache can charge different per-object overheads + * (LRU and SLRU reserve 16 bytes, FIFO none), so neither value alone + * describes the pair. This one is what the parent-level size check in + * cache_can_insert_default() uses, so take the larger of the two: an object + * that does not fit under the heavier policy does not fit in this cache. + * WTinyLFU_can_insert() checks each sub-cache against its own overhead. */ + /* Every incoming object is inserted into the window, and this field is + * what cache_get_base()'s capacity loop charges an incoming object, so it + * is the window's overhead rather than the pair's maximum. The other two + * sites each charge the cache the object is actually entering: + * WTinyLFU_can_insert() the window, WTinyLFU_evict() the main cache on + * promotion. Using the maximum here made the loop reserve up to 40 bytes + * for a 16-byte window insertion with an ARC, LeCaR or Cacheus main + * cache. */ + cache->obj_md_size = params->LRU->obj_md_size; + } + snprintf(cache->cache_name, CACHE_NAME_ARRAY_LEN, "WTinyLFU-w%.2lf-%s", params->window_size, params->main_cache_type); @@ -192,6 +208,7 @@ static void WTinyLFU_free(cache_t *cache) { minimalIncrementCBF_free(params->CBF); free(params->CBF); free_request(params->req_local); + free(params); cache_struct_free(cache); } @@ -275,8 +292,13 @@ static void WTinyLFU_evict(cache_t *cache, const request_t *req) { /** only when main_cache is full, evict an obj from the main_cache **/ // if main_cache has enough space, insert the obj into main_cache + /* charge the main cache its own per-object overhead, not the composite's. + * cache->obj_md_size is the larger of the two sub-caches, so that a + * caller asking the composite what it reserves is not told less than it + * really does; using it here would bill a FIFO or Clock main cache for + * the window's 16 bytes and call it full early. */ if (main_cache->get_occupied_byte(main_cache) + - params->req_local->obj_size + cache->obj_md_size <= + params->req_local->obj_size + main_cache->obj_md_size <= main_cache->cache_size) { main_cache->insert(main_cache, params->req_local); @@ -346,6 +368,17 @@ static bool WTinyLFU_remove(cache_t *cache, obj_id_t obj_id) { return false; } +/* main_cache is only built after the parameters are parsed, so report the + * configured type, which is what `-e print` runs against */ +static const char *WTinyLFU_current_params(WTinyLFU_params_t *params) { + static __thread char params_str[128]; + snprintf(params_str, 128, "main-cache=%s,window-size=%.4lf", + params->main_cache == NULL ? params->main_cache_type + : params->main_cache->cache_name, + params->window_size); + return params_str; +} + static void WTinyLFU_parse_params(cache_t *cache, const char *cache_specific_params) { WTinyLFU_params_t *params = (WTinyLFU_params_t *)cache->eviction_params; @@ -353,6 +386,7 @@ static void WTinyLFU_parse_params(cache_t *cache, // params->max_request_num = 32 * cache->cache_size; // 32 * cache_size char *params_str = strdup(cache_specific_params); + char *old_params_str = params_str; while (params_str != NULL && params_str[0] != '\0') { /* different parameters are separated by comma, * key and value are separated by = */ @@ -372,12 +406,17 @@ static void WTinyLFU_parse_params(cache_t *cache, ERROR("window_size must be in [0, 1)\n"); exit(1); } + } else if (strcasecmp(key, "print") == 0) { + printf("current parameters: %s\n", WTinyLFU_current_params(params)); + free(old_params_str); + exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); + free(old_params_str); exit(1); } } - return; + free(old_params_str); } /* WTinyLFU cannot an object larger than segment size */ @@ -385,8 +424,12 @@ bool WTinyLFU_can_insert(cache_t *cache, const request_t *req) { WTinyLFU_params_t *params = (WTinyLFU_params_t *)cache->eviction_params; bool can_insert = cache_can_insert_default(cache, req); + /* An object enters through the window, so the window's own per-object + * overhead decides whether it fits there — not the main cache's, which can + * differ. The main cache checks itself with its own overhead. */ return can_insert && - (req->obj_size + cache->obj_md_size <= params->LRU->cache_size) && + (req->obj_size + params->LRU->obj_md_size <= + params->LRU->cache_size) && (params->main_cache->can_insert(params->main_cache, req)); } diff --git a/libCacheSim/cache/eviction/cpp/LRU_K.cpp b/libCacheSim/cache/eviction/cpp/LRU_K.cpp index 9dc290936..526877a7f 100644 --- a/libCacheSim/cache/eviction/cpp/LRU_K.cpp +++ b/libCacheSim/cache/eviction/cpp/LRU_K.cpp @@ -117,6 +117,7 @@ static void LRU_K_parse_params(cache_t *cache, lruk->k = static_cast(k_val); } else if (strcasecmp(key, "print") == 0) { printf("LRU_K parameters: k=%d\n", lruk->k); + free(to_free); exit(0); } else { ERROR("LRU_K does not have parameter %s\n", key); diff --git a/libCacheSim/cache/eviction/fifo/LP_ARC.c b/libCacheSim/cache/eviction/fifo/LP_ARC.c index 8f8ec0da8..ee7660c9e 100644 --- a/libCacheSim/cache/eviction/fifo/LP_ARC.c +++ b/libCacheSim/cache/eviction/fifo/LP_ARC.c @@ -504,6 +504,7 @@ static void LP_ARC_parse_params(cache_t *cache, if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", LP_ARC_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/fifo/LP_SFIFO.c b/libCacheSim/cache/eviction/fifo/LP_SFIFO.c index 85b2a0ce6..075feeba2 100644 --- a/libCacheSim/cache/eviction/fifo/LP_SFIFO.c +++ b/libCacheSim/cache/eviction/fifo/LP_SFIFO.c @@ -89,7 +89,11 @@ cache_t *LP_SFIFO_init(const common_cache_params_t ccache_params, } common_cache_params_t ccache_params_local = ccache_params; - ccache_params_local.hashpower -= 2; + /* see Cacheus_init: a non-positive hash power is the "use the default" + * sentinel and must survive untouched. */ + if (ccache_params_local.hashpower > 0) { + ccache_params_local.hashpower = MAX(4, ccache_params_local.hashpower - 2); + } params->fifos = malloc(sizeof(cache_t *) * params->n_seg); for (int i = 0; i < params->n_seg; i++) { @@ -406,6 +410,7 @@ static void LP_SFIFO_parse_params(cache_t *cache, } else if (strcasecmp(key, "print") == 0) { printf("current parameters: %s\n", LP_SFIFO_current_params(cache, params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/fifo/LP_TwoQ.c b/libCacheSim/cache/eviction/fifo/LP_TwoQ.c index 15b6f50b5..f10ba071e 100644 --- a/libCacheSim/cache/eviction/fifo/LP_TwoQ.c +++ b/libCacheSim/cache/eviction/fifo/LP_TwoQ.c @@ -365,6 +365,7 @@ static void LP_TwoQ_parse_params(cache_t *cache, params->Aout_size_ratio = strtod(value, NULL); } else if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", LP_TwoQ_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/fifo/SFIFO.c b/libCacheSim/cache/eviction/fifo/SFIFO.c index ad3d123ef..98390bc22 100644 --- a/libCacheSim/cache/eviction/fifo/SFIFO.c +++ b/libCacheSim/cache/eviction/fifo/SFIFO.c @@ -393,6 +393,7 @@ static void SFIFO_parse_params(cache_t *cache, } else if (strcasecmp(key, "print") == 0) { printf("current parameters: %s\n", SFIFO_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/fifo/SFIFOv0.c b/libCacheSim/cache/eviction/fifo/SFIFOv0.c index 4ee5a8db2..96ee1af84 100644 --- a/libCacheSim/cache/eviction/fifo/SFIFOv0.c +++ b/libCacheSim/cache/eviction/fifo/SFIFOv0.c @@ -101,7 +101,11 @@ cache_t *SFIFOv0_init(const common_cache_params_t ccache_params, common_cache_params_t ccache_params_local = ccache_params; ccache_params_local.cache_size /= params->n_queues; - ccache_params_local.hashpower /= MIN(16, ccache_params_local.hashpower - 4); + /* the divisor reaches zero once hashpower is 4 or less; guarded rather than + * rewritten, since dividing here (unlike the assignment SLRUv0 does) looks + * deliberate enough not to change behind the author's back */ + ccache_params_local.hashpower /= + MAX(1, MIN(16, ccache_params_local.hashpower - 4)); for (int i = 0; i < params->n_queues; i++) { params->FIFOs[i] = FIFO_init(ccache_params_local, NULL); } @@ -402,6 +406,7 @@ static void SFIFOv0_parse_params(cache_t *cache, } else if (strcasecmp(key, "print") == 0) { printf("current parameters: %s\n", SFIFOv0_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/other/S3LRU.c b/libCacheSim/cache/eviction/other/S3LRU.c index 7fe72adca..ed7550ebd 100644 --- a/libCacheSim/cache/eviction/other/S3LRU.c +++ b/libCacheSim/cache/eviction/other/S3LRU.c @@ -495,6 +495,7 @@ static void S3LRU_parse_params(cache_t *cache, params->promote_on_hit = atoi(value); } else if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", S3LRU_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/other/flashProb.c b/libCacheSim/cache/eviction/other/flashProb.c index 980bf65f2..ed76a2f02 100644 --- a/libCacheSim/cache/eviction/other/flashProb.c +++ b/libCacheSim/cache/eviction/other/flashProb.c @@ -359,10 +359,13 @@ static inline int64_t flashProb_get_n_obj(const cache_t *cache) { // *********************************************************************** static const char *flashProb_current_params(flashProb_params_t *params) { static __thread char params_str[128]; - snprintf(params_str, 128, - "ram-size-ratio=%.4lf,disk-admit-prob=%.4lf,ram-cache=%s\n", - params->ram_size_ratio, params->disk_admit_prob, - params->ram->cache_name); + /* ram is only built after the parameters are parsed, so report the + * configured type, which is what `-e print` runs against */ + snprintf( + params_str, 128, + "ram-size-ratio=%.4lf,disk-admit-prob=%.4lf,ram-cache=%s\n", + params->ram_size_ratio, params->disk_admit_prob, + params->ram == NULL ? params->ram_cache_type : params->ram->cache_name); return params_str; } @@ -395,6 +398,7 @@ static void flashProb_parse_params(cache_t *cache, strncpy(params->disk_cache_type, value, 15); } else if (strcasecmp(key, "print") == 0) { printf("parameters: %s\n", flashProb_current_params(params)); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/eviction/plugin_cache.c b/libCacheSim/cache/eviction/plugin_cache.c index 2362ca6b8..8b36b43ec 100644 --- a/libCacheSim/cache/eviction/plugin_cache.c +++ b/libCacheSim/cache/eviction/plugin_cache.c @@ -400,8 +400,8 @@ static void pluginCache_parse_params(cache_t *cache, char *key = strsep((char **)¶ms_str, "="); char *value = strsep((char **)¶ms_str, ","); - // Check if value is NULL - if (value == NULL) { + // Check if value is NULL; "print" is a bare flag, not a key=value pair + if (value == NULL && (key == NULL || strcasecmp(key, "print") != 0)) { ERROR("Parameter '%s' is missing a value in cache '%s'\n", key, cache->cache_name); exit(1); @@ -426,6 +426,7 @@ static void pluginCache_parse_params(cache_t *cache, params->cache_name = strdup(value); } else if (strcasecmp(key, "print") == 0) { printf("current parameters: plugin_path=%s\n", params->plugin_path); + free(old_params_str); exit(0); } else { ERROR("%s does not have parameter %s\n", cache->cache_name, key); diff --git a/libCacheSim/cache/prefetch/Mithril.c b/libCacheSim/cache/prefetch/Mithril.c index 1fc94eb14..4cfffabc5 100644 --- a/libCacheSim/cache/prefetch/Mithril.c +++ b/libCacheSim/cache/prefetch/Mithril.c @@ -76,6 +76,7 @@ static void set_Mithril_default_init_params( static void Mithril_parse_init_params(const char *cache_specific_params, Mithril_init_params_t *init_params) { char *params_str = strdup(cache_specific_params); + char *old_params_str = params_str; while (params_str != NULL && params_str[0] != '\0') { char *key = strsep((char **)¶ms_str, "="); @@ -122,13 +123,16 @@ static void Mithril_parse_init_params(const char *cache_specific_params, } else if (strcasecmp(key, "print") == 0 || strcasecmp(key, "default") == 0) { printf("default params: %s\n", Mithril_default_params()); + free(old_params_str); exit(0); } else { ERROR("Mithril does not have parameter %s\n", key); printf("default params: %s\n", Mithril_default_params()); + free(old_params_str); exit(1); } } + free(old_params_str); } static void set_Mithril_params(Mithril_params_t *Mithril_params, diff --git a/libCacheSim/cache/prefetch/PG.c b/libCacheSim/cache/prefetch/PG.c index f3760c42e..931496b3d 100644 --- a/libCacheSim/cache/prefetch/PG.c +++ b/libCacheSim/cache/prefetch/PG.c @@ -56,6 +56,7 @@ static void set_PG_default_init_params(PG_init_params_t *init_params) { static void PG_parse_init_params(const char *cache_specific_params, PG_init_params_t *init_params) { char *params_str = strdup(cache_specific_params); + char *old_params_str = params_str; while (params_str != NULL && params_str[0] != '\0') { char *key = strsep((char **)¶ms_str, "="); @@ -74,13 +75,16 @@ static void PG_parse_init_params(const char *cache_specific_params, } else if (strcasecmp(key, "print") == 0 || strcasecmp(key, "default") == 0) { printf("default params: %s\n", PG_default_params()); + free(old_params_str); exit(0); } else { ERROR("pg does not have parameter %s\n", key); printf("default params: %s\n", PG_default_params()); + free(old_params_str); exit(1); } } + free(old_params_str); } static void set_PG_params(PG_params_t *PG_params, PG_init_params_t *init_params,