commit 5b510f317fea86fb3c630fad31bbd81ddc73ba3d
parent 980b9514847cbde6f6a386faf9831b792130addb
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Thu, 6 Mar 2025 15:33:21 +0100
Dump state cache in case of fatal error
This will help debug what went wrong. The output file name contains the
read and write offsets of the status cache, as well as the identifier of
the process that crashed.
Diffstat:
| M | src/ssp_rng_proxy.c | | | 82 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
1 file changed, 80 insertions(+), 2 deletions(-)
diff --git a/src/ssp_rng_proxy.c b/src/ssp_rng_proxy.c
@@ -13,6 +13,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+#define _POSIX_C_SOURCE 200809L /* getpid */
+
#include "ssp_rng_c.h"
#include <rsys/dynamic_array_char.h>
@@ -22,6 +24,7 @@
#include <rsys/stretchy_array.h>
#include <limits.h>
+#include <unistd.h> /* getpid */
#define BUCKET_SIZE_DEFAULT 1000000 /* #RNs per bucket */
@@ -211,6 +214,72 @@ rng_state_cache_is_empty(struct rng_state_cache* cache)
}
static res_T
+rng_state_cache_dump(struct rng_state_cache* cache)
+{
+ /* Output file into which cache stream is dumped */
+ char name[128] = {0};
+ pid_t process = 0; /* Process identifier */
+ FILE* fp = NULL;
+
+ /* Temporary memory to copy cache stream */
+ void* mem = NULL;
+
+ /* Miscellaneous */
+ long offset = 0;
+ int n = 0;
+ res_T res = RES_OK;
+
+ ASSERT(cache);
+
+ /* No cache to dump */
+ if(!cache->stream) goto exit;
+
+ process = getpid();
+
+ #define TRY(Cond, Err) { \
+ if(!(Cond)) { \
+ fprintf(stderr, "%s:%d: %s\n", FUNC_NAME, __LINE__, strerror(Err)); \
+ switch(Err) { \
+ case EIO: res = RES_IO_ERR; break; \
+ case ENOMEM: res = RES_MEM_ERR; break; \
+ default: res = RES_UNKNOWN_ERR; break; \
+ } \
+ goto error; \
+ } \
+ } (void)0
+
+ /* Define the state cache filename */
+ n = snprintf(name, sizeof(name), "rng_cache_r%lu_w%lu_%d",
+ cache->read, cache->write, process);
+ TRY(n >= 0, errno);
+ TRY((unsigned long)n < sizeof(name), ENOMEM);
+
+ /* Create the output file */
+ TRY((fp = fopen(name, "w")) != NULL, errno);
+
+ /* Requests the offset at the end of the file,
+ * i.e. the size of the cache stream */
+ TRY(fseek(cache->stream, 0, SEEK_END) == 0, errno);
+ TRY((offset = ftell(cache->stream)) >= 0, errno);
+
+ rewind(cache->stream);
+
+ /* Load cache stream in mem, and then dump it to the state cache */
+ TRY((mem = mem_alloc(offset)) != NULL, ENOMEM);
+ TRY((fread(mem, offset, 1, cache->stream)) == 1, EIO);
+ TRY((fwrite(mem, offset, 1, fp)) == 1, EIO);
+
+ #undef TRY
+
+exit:
+ if(mem) mem_rm(mem);
+ if(fp) CHK(fclose(fp) == 0);
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
rng_state_cache_read
(struct rng_state_cache* cache,
struct ssp_rng* rng,
@@ -238,6 +307,7 @@ rng_state_cache_read
fseek(cache->stream, cache->read, SEEK_SET);
res = ssp_rng_read(rng, cache->stream);
if(res != RES_OK) {
+ CHK(rng_state_cache_dump(cache) == RES_OK);
mutex_unlock(mutex);
goto error;
}
@@ -497,7 +567,10 @@ rng_proxy_next_ran_pool
/* Register a new state for *all* buckets */
FOR_EACH(ibucket, 0, sa_size(proxy->states)) {
res = rng_state_cache_write(proxy->states + ibucket, proxy->rng);
- if(res != RES_OK) FATAL("RNG proxy: cannot write to state cache\n");
+ if(res != RES_OK) {
+ rng_state_cache_dump(proxy->states + ibucket);
+ FATAL("RNG proxy: cannot write to state cache\n");
+ }
ssp_rng_discard(proxy->rng, proxy->bucket_size);
}
/* Discard RNs to reach the next sequence */
@@ -513,7 +586,12 @@ rng_proxy_next_ran_pool
(proxy->states + bucket_name,
proxy->pools[bucket_name],
proxy->mutex);
- if(res != RES_OK) FATAL("RNG proxy: cannot read from state cache\n");
+ if(res != RES_OK) {
+ mutex_lock(proxy->mutex);
+ rng_state_cache_dump(proxy->states + bucket_name);
+ mutex_unlock(proxy->mutex);
+ FATAL("RNG proxy: cannot read from state cache\n");
+ }
/* Update the sequence of the bucket RNG */
proxy->per_bucket_sequence_id[bucket_name] += 1;