Make FML_LOG safe from static initialization (flutter/engine#42219)

I ran into this while trying to get some printing going for places where we're creating thread local keys. 

Supposedly, just including `<iostream>` should statically initialize `std::cout/cerr`, but it gets really hard to reason about whether your statically initialized code is going to be initialized before or after that happens. I tried making sure that the TU for `fml/logging.cc` did that initialization statically, but that also failed in the verison of the test included here (it passed in some other iterations that modified run_all_unittests.cc). We _could_ make sure it happens each and every time we touch `std::cerr` but ... we could also just use `fprintf(stderr, ...)` and it works just fine.

/cc @flar who ran into problems around this a little while back and was asking about it.
This commit is contained in:
Dan Field
2023-05-22 17:02:17 -07:00
committed by GitHub
parent d84c05920b
commit ccd8afabde
2 changed files with 40 additions and 2 deletions

View File

@@ -123,8 +123,9 @@ LogMessage::~LogMessage() {
fx_logger_log_with_source(fx_log_get_logger(), fx_severity, nullptr, file_,
line_, stream_.str().c_str());
#else
std::cerr << stream_.str();
std::cerr.flush();
// Don't use std::cerr here, because it may not be initialized properly yet.
fprintf(stderr, "%s", stream_.str().c_str());
fflush(stderr);
#endif
if (severity_ >= LOG_FATAL) {

View File

@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <signal.h>
#include "flutter/fml/build_config.h"
#include "flutter/fml/log_settings.h"
#include "flutter/fml/logging.h"
@@ -19,6 +21,41 @@
namespace fml {
namespace testing {
#ifndef OS_FUCHSIA
class MakeSureFmlLogDoesNotSegfaultWhenStaticallyCalled {
public:
MakeSureFmlLogDoesNotSegfaultWhenStaticallyCalled() {
SegfaultCatcher catcher;
// If this line causes a segfault, FML is using a method of logging that is
// not safe from static initialization on your platform.
FML_LOG(INFO)
<< "This log exists to verify that static logging from FML works.";
}
private:
struct SegfaultCatcher {
typedef void (*sighandler_t)(int);
SegfaultCatcher() {
handler = ::signal(SIGSEGV, SegfaultHandler);
FML_CHECK(handler != SIG_ERR);
}
~SegfaultCatcher() { FML_CHECK(::signal(SIGSEGV, handler) != SIG_ERR); }
static void SegfaultHandler(int signal) {
fprintf(stderr,
"FML failed to handle logging from static initialization.\n");
exit(signal);
}
sighandler_t handler;
};
};
static MakeSureFmlLogDoesNotSegfaultWhenStaticallyCalled fml_log_static_check_;
#endif // !defined(OS_FUCHSIA)
int UnreachableScopeWithoutReturnDoesNotMakeCompilerMad() {
KillProcess();
// return 0; <--- Missing but compiler is fine.