wsd: sig: forward USR2 to child processes

We now have USR2 signal that dumps the
stack-trace of each process. This is useful for
capturing the state of misbehaving instances.
COOLWSD forwards USR2 to forkit and the kits
so they dump their stacktraces too.

This patch does not change the behavior of USR1.
Specifically, unlike USR2, USR1 is not forwarded
from wsd to frk and the kits. Also unlike USR2,
USR1 dumps into stderr.

Change-Id: I1d82f678f30f430f627692cc42961b1928f69e11
Signed-off-by: Ashod Nakashian <ashod.nakashian@collabora.co.uk>
This commit is contained in:
Ashod Nakashian 2022-06-13 22:17:40 -04:00 committed by Michael Meeks
parent 3109b99d49
commit b0d6e1b859
4 changed files with 69 additions and 5 deletions

View file

@ -23,6 +23,7 @@
#include <string>
#include "Log.hpp"
#include <SigUtil.hpp>
namespace JailUtil
{
@ -122,7 +123,7 @@ static bool safeRemoveDir(const std::string& path)
void removeJail(const std::string& root)
{
LOG_INF("Removing jail [" << root << "].");
LOG_INF("Removing jail [" << root << ']');
// Unmount the tmp directory. Don't care if we fail.
const std::string tmpPath = Poco::Path(root, "tmp").toString();

View file

@ -42,6 +42,7 @@
#ifndef IOS
static std::atomic<bool> TerminationFlag(false);
static std::atomic<bool> DumpGlobalState(false);
static std::atomic<bool> ForwardSigUsr2Flag(false); //< Flags to forward SIG_USR2 to children.
static std::atomic<bool> ShutdownRequestFlag(false);
#endif
@ -93,10 +94,23 @@ namespace SigUtil
void checkDumpGlobalState(GlobalDumpStateFn dumpState)
{
#if !MOBILEAPP
assert(dumpState && "Invalid callback for checkDumpGlobalState");
if (DumpGlobalState)
{
dumpState();
DumpGlobalState = false;
dumpState();
}
#endif
}
void checkForwardSigUsr2(ForwardSigUsr2Fn forwardSigUsr2)
{
#if !MOBILEAPP
assert(forwardSigUsr2 && "Invalid callback for checkForwardSigUsr2");
if (ForwardSigUsr2Flag)
{
ForwardSigUsr2Flag = false;
forwardSigUsr2();
}
#endif
}
@ -478,7 +492,6 @@ namespace SigUtil
if (signal == SIGUSR1)
{
DumpGlobalState = true;
SocketPoll::wakeupWorld();
}
else if (signal == SIGUSR2)
{
@ -487,9 +500,12 @@ namespace SigUtil
const int numSlots = backtrace(backtraceBuffer, maxSlots);
if (numSlots > 0)
backtrace_symbols_fd(backtraceBuffer, numSlots, SignalLogFD);
ForwardSigUsr2Flag = true;
}
signalLogClose();
SocketPoll::wakeupWorld();
}
static

View file

@ -49,6 +49,10 @@ namespace SigUtil
void checkDumpGlobalState(GlobalDumpStateFn dumpState);
extern "C" { typedef void (*ForwardSigUsr2Fn)(void); }
void checkForwardSigUsr2(ForwardSigUsr2Fn forwardSigUsr2);
/// Add a message to a round-robin buffer to be dumped on fatal signal
void addActivity(const std::string &message);

View file

@ -213,7 +213,11 @@ static std::map<std::string, std::shared_ptr<DocumentBroker> > DocBrokers;
static std::mutex DocBrokersMutex;
static Poco::AutoPtr<Poco::Util::XMLConfiguration> KitXmlConfig;
extern "C" { void dump_state(void); /* easy for gdb */ }
extern "C"
{
void dump_state(void); /* easy for gdb */
void forwardSigUsr2();
}
#if ENABLE_DEBUG
static std::chrono::milliseconds careerSpanMs(std::chrono::milliseconds::zero());
@ -367,7 +371,6 @@ void COOLWSD::checkDiskSpaceAndWarnClients(const bool cacheLastCheck)
/// Remove dead and idle DocBrokers.
/// The client of idle document should've greyed-out long ago.
/// Returns true if at least one is removed.
void cleanupDocBrokers()
{
Util::assertIsLocked(DocBrokersMutex);
@ -2838,7 +2841,10 @@ void PrisonPoll::wakeupHook()
#endif
std::unique_lock<std::mutex> docBrokersLock(DocBrokersMutex, std::defer_lock);
if (docBrokersLock.try_lock())
{
cleanupDocBrokers();
SigUtil::checkForwardSigUsr2(forwardSigUsr2);
}
}
#if !MOBILEAPP
@ -5487,6 +5493,43 @@ void dump_state()
LOG_TRC(msg);
}
void forwardSigUsr2()
{
LOG_TRC("forwardSigUsr2");
Util::assertIsLocked(DocBrokersMutex);
std::lock_guard<std::mutex> newChildLock(NewChildrenMutex);
#if !MOBILEAPP
#ifndef KIT_IN_PROCESS
if (COOLWSD::ForKitProcId > 0)
{
LOG_INF("Sending SIGUSR2 to forkit " << COOLWSD::ForKitProcId);
::kill(COOLWSD::ForKitProcId, SIGUSR2);
}
#endif
#endif
for (const auto& child : NewChildren)
{
if (child && child->getPid() > 0)
{
LOG_INF("Sending SIGUSR2 to child " << child->getPid());
::kill(child->getPid(), SIGUSR2);
}
}
for (const auto& pair : DocBrokers)
{
std::shared_ptr<DocumentBroker> docBroker = pair.second;
if (docBroker)
{
LOG_INF("Sending SIGUSR2 to docBroker " << docBroker->getPid());
::kill(docBroker->getPid(), SIGUSR2);
}
}
}
// Avoid this in the Util::isFuzzing() case because libfuzzer defines its own main().
#if !MOBILEAPP && !LIBFUZZER