diff --git a/common/FileUtil.cpp b/common/FileUtil.cpp index 95d0e138c5ca..8641a4924d71 100644 --- a/common/FileUtil.cpp +++ b/common/FileUtil.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef __linux__ #include #elif defined IOS @@ -387,6 +388,7 @@ namespace FileUtil std::unique_ptr> readFile(const std::string& path, int maxSize) { auto data = std::make_unique>(maxSize); + data->resize(0); return (readFile(path, *data, maxSize) >= 0) ? std::move(data) : nullptr; } @@ -427,6 +429,41 @@ namespace FileUtil return rootPath.toString(); } + ssize_t read(int fd, void* buf, size_t nbytes) + { + ssize_t off = 0; + char* p = static_cast(buf); + for (;;) + { + if (static_cast(nbytes) <= 0) + { + // Nothing to read. + break; + } + + ssize_t n; + while ((n = ::read(fd, p, nbytes)) < 0 && errno == EINTR) + { + } + + if (n <= 0) + { + if (n == 0) // EOF. + break; + + return -1; // Error. + } + + assert(n >= 0 && "Expected a positive read byte-count"); + p += n; + off += n; + assert(static_cast(n) <= nbytes && "Unexpectedly read more than requested"); + nbytes -= n; + } + + return off; + } + } // namespace FileUtil namespace diff --git a/common/FileUtil.hpp b/common/FileUtil.hpp index 7b314758b9af..144768061d67 100644 --- a/common/FileUtil.hpp +++ b/common/FileUtil.hpp @@ -148,7 +148,11 @@ namespace FileUtil /// have equal size and every byte of their contents match. bool compareFileContents(const std::string& rhsPath, const std::string& lhsPath); - /// Reads the whole file into the given buffer. Only for small files. + /// Read nbytes from fd into buf. Retries on EINTR. + /// Returns the number of bytes read, or -1 on error. + ssize_t read(int fd, void* buf, size_t nbytes); + + /// Reads the whole file appending onto the given buffer. Only for small files. /// Does *not* clear the buffer before writing to it. Returns the number of bytes read, -1 for error. template ssize_t readFile(const std::string& path, T& data, int maxSize = 256 * 1024) @@ -165,38 +169,15 @@ namespace FileUtil } const std::size_t originalSize = data.size(); - auto remainingSize = st.st_size; + const auto remainingSize = (st.st_size > 0 ? st.st_size : maxSize); data.resize(originalSize + remainingSize); - off_t off = originalSize; - for (;;) - { - if (remainingSize == 0) - { - // Nothing to read. - break; - } - int n; - while ((n = ::read(fd, &data[off], remainingSize)) < 0 && errno == EINTR) - { - } + const ssize_t n = read(fd, &data[originalSize], remainingSize); + ::close(fd); - if (n <= 0) - { - if (n == 0) // EOF. - break; - - ::close(fd); - data.resize(originalSize); - return -1; // Error. - } - - off += n; - remainingSize -= n; - } + data.resize(originalSize + (n <= 0 ? 0 : n)); - close(fd); - return st.st_size; + return n; } /// Reads the whole file to memory. Only for small files. diff --git a/kit/Kit.cpp b/kit/Kit.cpp index fdd73fbbe97a..379c42a27c07 100644 --- a/kit/Kit.cpp +++ b/kit/Kit.cpp @@ -2529,8 +2529,14 @@ void Document::dumpState(std::ostream& oss) << "\n\tduringLoad: " << _duringLoad << "\n\tmodified: " << name(_modified) << "\n\tbgSaveProc: " << _isBgSaveProcess - << "\n\tbgSaveDisabled: "<< _isBgSaveDisabled - << "\n"; + << "\n\tbgSaveDisabled: "<< _isBgSaveDisabled; + + std::string smap; + if (const ssize_t size = FileUtil::readFile("/proc/self/smaps_rollup", smap); size <= 0) + oss << "\n smaps_rollup: "; + else + oss << "\n smaps_rollup: " << smap; + oss << '\n'; // dumpState: // TODO: _websocketHandler - but this is an odd one. @@ -2542,7 +2548,7 @@ void Document::dumpState(std::ostream& oss) << " editorId: " << it.second->getDoc()->getEditorId() << " mobileAppDocId: " << it.second->getDoc()->getMobileAppDocId(); } - oss << "\n"; + oss << '\n'; _deltaPool.dumpState(oss); _sessions.dumpState(oss); @@ -2557,7 +2563,7 @@ void Document::dumpState(std::ostream& oss) oss << "\n\t\tviewId: " << it.first << " last update time(ms): " << ms; } - oss << "\n"; + oss << '\n'; oss << "\tspeedCount:"; for (const auto &it : _speedCount) @@ -2578,14 +2584,14 @@ void Document::dumpState(std::ostream& oss) << " readOnly: " << it.second.isReadOnly() << " connected: " << it.second.isConnected(); } - oss << "\n"; + oss << '\n'; char *pState = nullptr; _loKit->dumpState("", &pState); oss << "lok state:\n"; if (pState) oss << pState; - oss << "\n"; + oss << '\n'; } #if !defined BUILDING_TESTS && !MOBILEAPP && !LIBFUZZER diff --git a/test/WopiProofTests.cpp b/test/WopiProofTests.cpp index 0081de1d488e..9572e60a3504 100644 --- a/test/WopiProofTests.cpp +++ b/test/WopiProofTests.cpp @@ -157,7 +157,7 @@ void WopiProofTests::testOurProof() LOK_ASSERT_EQUAL(pairs[2].first, std::string("X-WOPI-ProofOld")); std::string proofOld = pairs[2].second; - int64_t ticks = std::stoll(timestamp.c_str(), nullptr, 10); + int64_t ticks = std::stoll(timestamp, nullptr, 10); verifySignature(access_token, uri, ticks, modulus, exponent, proof, testname); verifySignature(access_token, uri, ticks, modulus, exponent, proofOld, testname); @@ -175,7 +175,7 @@ void WopiProofTests::testOurProof() LOK_ASSERT_EQUAL(pairs[2].first, std::string("X-WOPI-ProofOld")); proofOld = pairs[2].second; - ticks = std::stoll(timestamp.c_str(), nullptr, 10); + ticks = std::stoll(timestamp, nullptr, 10); verifySignature(access_token, uri, ticks, modulus, exponent, proof, testname); verifySignature(access_token, uri, ticks, modulus, exponent, proofOld, testname); } diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp index 9b7da26e8a68..3cbc8e798688 100644 --- a/wsd/Admin.cpp +++ b/wsd/Admin.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -695,6 +696,17 @@ void Admin::pollingThread() } } + bool dumpMetrics = true; + if (_dumpMetrics.compare_exchange_strong(dumpMetrics, false)) + { + std::ostringstream oss; + oss << "Admin Metrics:\n"; + getMetrics(oss); + const std::string str = oss.str(); + fprintf(stderr, "%s\n", str.c_str()); + LOG_TRC(str); + } + // Handle websockets & other work. const auto timeout = std::chrono::milliseconds(capAndRoundInterval( std::min(std::min(std::min(cpuWait, memWait), netWait), cleanupWait))); diff --git a/wsd/Admin.hpp b/wsd/Admin.hpp index 3b5e93725727..265822101951 100644 --- a/wsd/Admin.hpp +++ b/wsd/Admin.hpp @@ -173,6 +173,9 @@ class Admin : public SocketPoll void getMetrics(std::ostringstream &metrics); + /// Will dump the metrics in the log and stderr from the Admin SocketPoll. + static void dumpMetrics() { instance()._dumpMetrics = true; } + // delete entry from _monitorSocket map void deleteMonitorSocket(const std::string &uriWithoutParam); @@ -226,6 +229,9 @@ class Admin : public SocketPoll uint64_t _lastRecvCount; std::string _forkitLogLevel; + /// When set, the metrics will be dumped into the log and stderr. + std::atomic_bool _dumpMetrics; + struct MonitorConnectRecord { void setWhen(std::chrono::steady_clock::time_point when) { _when = when; } diff --git a/wsd/COOLWSD.cpp b/wsd/COOLWSD.cpp index 6c75191d3e80..d3968823ce1d 100644 --- a/wsd/COOLWSD.cpp +++ b/wsd/COOLWSD.cpp @@ -4104,6 +4104,12 @@ class COOLWSDServer << "\n UserInterface: " << COOLWSD::UserInterface ; + std::string smap; + if (const ssize_t size = FileUtil::readFile("/proc/self/smaps_rollup", smap); size <= 0) + os << "\n smaps_rollup: "; + else + os << "\n smaps_rollup: " << smap; + #if !MOBILEAPP if (FetchHttpSession) { @@ -4954,6 +4960,10 @@ void dump_state() const std::string msg = oss.str(); fprintf(stderr, "%s\n", msg.c_str()); LOG_TRC(msg); + +#if !MOBILEAPP + Admin::dumpMetrics(); +#endif } void lslr_childroot()