facebook / rocksdb

A library that provides an embeddable, persistent key-value store for fast storage.

Home Page:http://rocksdb.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

StdLogger truncating last letter in some cases

dfa1 opened this issue · comments

Note: Please use Issues only for bug reports. For questions, discussions, feature requests, etc. post to dev group: https://groups.google.com/forum/#!forum/rocksdb or https://www.facebook.com/groups/rocksdb.dev
Tested on Linux with RocksDB 9.0.0 via Java API, but I believe it is happening in the C++ util/stderr_logger.cc.

Expected behavior

...
** File Read Latency Histogram By Level [default] **
2024/04/23-18:26:52.184784 111a8 [/db_impl/db_impl.cc:498] Shutdown: canceling all background work
2024/04/23-18:26:52.185084 111a8 [/db_impl/db_impl.cc:692] Shutdown complete

Actual behavior

...
** File Read Latency Histogram By Level [default] **
2024/04/23-18:26:52.184784 111a8 [/db_impl/db_impl.cc:498] Shutdown: canceling all background wor
2024/04/23-18:26:52.185084 111a8 [/db_impl/db_impl.cc:692] Shutdown complet

(notice wor instead of work and complet instead of complete, but other logs looks good)

Steps to reproduce the behavior

Use StdLogger in Options.

I've also noticed the same issue (using the c api to call this from rust). The issue is most likely the c++ code.

commented

Hello, I think the issue is in the incorrect calculation of the log_suffix_len in the util/stderr_logger.cc.

According the documentation https://en.cppreference.com/w/c/io/vfprintf, the vfprintf(buffer, bufsz, format, vlist) function

writes the results to a character string buffer. At most bufsz - 1 characters are written. The resulting character string will be terminated with a null character, unless bufsz is zero. If bufsz is zero, nothing is written and buffer may be a null pointer, however the return value (number of bytes that would be written not including the null terminator) is still calculated and returned.

So it means that if we call

const size_t log_suffix_len = vsnprintf(nullptr, 0, format, ap_copy);

log_suffix_len contains number of bytes that are needed but not including null byte.

Later we call

written += vsnprintf(buf.get() + written, log_suffix_len, format, ap);

where bufsz = log_suffix_len and from the documentation we know that vfprinf writes at most bufsz - 1 characters. So it will truncate the last character to have space for null byte.

Possible fix

I believe the following patch could help

diff --git a/util/stderr_logger.cc b/util/stderr_logger.cc
index 69e9989f0..ced24727f 100644
--- a/util/stderr_logger.cc
+++ b/util/stderr_logger.cc
@@ -38,12 +38,11 @@ void StderrLogger::Logv(const char* format, va_list ap) {

   va_list ap_copy;
   va_copy(ap_copy, ap);
-  const size_t log_suffix_len = vsnprintf(nullptr, 0, format, ap_copy);
+  const size_t log_suffix_len = vsnprintf(nullptr, 0, format, ap_copy) + 1;
   va_end(ap_copy);

   // Allocate space for the context, log_prefix, and log itself
-  // Extra byte for null termination
-  size_t buf_len = ctx_len + log_prefix_len + log_suffix_len + 1;
+  size_t buf_len = ctx_len + log_prefix_len + log_suffix_len;
   std::unique_ptr<char[]> buf(new char[buf_len]);

   // If the logger was created without a prefix, the prefix is a nullptr
@@ -56,7 +55,6 @@ void StderrLogger::Logv(const char* format, va_list ap) {
                t.tm_sec, static_cast<int>(now_tv.tv_usec),
                static_cast<long long unsigned int>(thread_id), prefix);
   written += vsnprintf(buf.get() + written, log_suffix_len, format, ap);
-  buf[written] = '\0';

   fprintf(stderr, "%s%c", buf.get(), '\n');
 }

@PatrikValo that looks good to me, mind making a pr?