Files
ortools-clone/ortools/base/logging_utilities.cc
Corentin Le Molgat fca3456c45 Sync from Google
2022-03-03 14:09:10 +01:00

330 lines
10 KiB
C++

// Copyright 2010-2021 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ortools/base/logging_utilities.h"
#include <signal.h>
#include <stdio.h>
#if !defined(_MSC_VER)
#include <pwd.h>
#include <syslog.h>
#include <unistd.h> // For geteuid.
#endif
#if defined(_MSC_VER) // windows compatibility layer.
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN /* We always want minimal includes */
#endif
#include <direct.h> /* for _getcwd() */
#include <io.h> /* because we so often use open/close/etc */
#include <process.h> /* for _getpid() */
#include <stdarg.h> /* template_dictionary.cc uses va_copy */
#include <string.h> /* for _strnicmp(), strerror_s() */
#include <time.h> /* for localtime_s() */
#include <windows.h>
#include <winsock.h> /* for gethostname */
/* Note: the C++ #includes are all together at the bottom. This file is
* used by both C and C++ code, so we put all the C++ together.
*/
/* 4244: otherwise we get problems when subtracting two size_t's to an int
* 4251: it's complaining about a private struct I've chosen not to dllexport
* 4355: we use this in a constructor, but we do it safely
* 4715: for some reason VC++ stopped realizing you can't return after abort()
* 4800: we know we're casting ints/char*'s to bools, and we're ok with that
* 4996: Yes, we're ok using "unsafe" functions like fopen() and strerror()
* 4312: Converting uint32_t to a pointer when testing %p
* 4267: also subtracting two size_t to int
* 4722: Destructor never returns due to abort()
*/
#pragma warning(disable : 4244 4251 4355 4715 4800 4996 4267 4312 4722)
/* file I/O */
#define PATH_MAX 1024
#define access _access
#define getcwd _getcwd
#define open _open
#define read _read
#define write _write
#define lseek _lseek
#define close _close
#define popen _popen
#define pclose _pclose
#define R_OK 04 /* read-only (for access()) */
#define S_ISDIR(m) (((m)&_S_IFMT) == _S_IFDIR)
#define O_WRONLY _O_WRONLY
#define O_CREAT _O_CREAT
#define O_EXCL _O_EXCL
#ifndef __MINGW32__
enum { STDIN_FILENO = 0, STDOUT_FILENO = 1, STDERR_FILENO = 2 };
#endif
#define S_IRUSR S_IREAD
#define S_IWUSR S_IWRITE
/* Not quite as lightweight as a hard-link, but more than good enough for us. */
#define link(oldpath, newpath) CopyFileA(oldpath, newpath, false)
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
/* In windows-land, hash<> is called hash_compare<> (from xhash.h) */
/* VC11 provides std::hash */
#if defined(_MSC_VER) && (_MSC_VER < 1700)
#define hash hash_compare
#endif
/* We can't just use _vsnprintf and _snprintf as drop-in-replacements,
* because they don't always NUL-terminate. :-( We also can't use the
* name vsnprintf, since windows defines that (but not snprintf (!)).
*/
// These call the windows _vsnprintf, but always NUL-terminate.
int safe_vsnprintf(char* str, size_t size, const char* format, va_list ap) {
if (size == 0) // not even room for a \0?
return -1; // not what C99 says to do, but what windows does
str[size - 1] = '\0';
return _vsnprintf(str, size - 1, format, ap);
}
#define vsnprintf(str, size, format, ap) safe_vsnprintf(str, size, format, ap)
#ifndef va_copy
#define va_copy(dst, src) (dst) = (src)
#endif
/* Windows doesn't support specifying the number of buckets as a
* hash_map constructor arg, so we leave this blank.
*/
#define CTEMPLATE_SMALL_HASHTABLE
#define DEFAULT_TEMPLATE_ROOTDIR ".."
// ----------------------------------- SYSTEM/PROCESS
typedef int pid_t;
#define getpid _getpid
// ----------------------------------- THREADS
typedef DWORD pthread_t;
#define pthread_self GetCurrentThreadId
inline struct tm* localtime_r(const time_t* timep, struct tm* result) {
localtime_s(result, timep);
return result;
}
inline char* strerror_r(int errnum, char* buf, size_t buflen) {
strerror_s(buf, buflen, errnum);
return buf;
}
#endif // _MSC_VER
namespace google {
static const char* g_program_invocation_short_name = nullptr;
static pthread_t g_main_thread_id;
} // namespace google
// The following APIs are all internal.
#include "absl/debugging/failure_signal_handler.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
#include "absl/time/time.h"
#include "ortools/base/commandlineflags.h"
ABSL_FLAG(bool, symbolize_stacktrace, true,
"Symbolize the stack trace in the tombstone");
namespace google {
typedef void DebugWriter(const char*, void*);
// The %p field width for printf() functions is two characters per byte.
// For some environments, add two extra bytes for the leading "0x".
static const int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
static void DebugWriteToStderr(const char* data, void*) {
// This one is signal-safe.
if (write(STDERR_FILENO, data, strlen(data)) < 0) {
// Ignore errors.
}
}
static void DebugWriteToString(const char* data, void* arg) {
reinterpret_cast<std::string*>(arg)->append(data);
}
// Print a program counter and its symbol name.
static void DumpPCAndSymbol(DebugWriter* writerfn, void* arg, void* pc,
const char* const prefix) {
char tmp[1024];
const char* symbol = "(unknown)";
// Symbolizes the previous address of pc because pc may be in the
// next function. The overrun happens when the function ends with
// a call to a function annotated noreturn (e.g. CHECK).
if (absl::Symbolize(reinterpret_cast<char*>(pc) - 1, tmp, sizeof(tmp))) {
symbol = tmp;
}
char buf[1024];
snprintf(buf, sizeof(buf), "%s@ %*p %s\n", prefix, kPrintfPointerFieldWidth,
pc, symbol);
writerfn(buf, arg);
}
static void DumpPC(DebugWriter* writerfn, void* arg, void* pc,
const char* const prefix) {
char buf[100];
snprintf(buf, sizeof(buf), "%s@ %*p\n", prefix, kPrintfPointerFieldWidth, pc);
writerfn(buf, arg);
}
// Dump current stack trace as directed by writerfn
static void DumpStackTrace(int skip_count, DebugWriter* writerfn, void* arg) {
// Print stack trace
void* stack[32];
int depth = absl::GetStackTrace(stack, ABSL_ARRAYSIZE(stack), skip_count + 1);
for (int i = 0; i < depth; i++) {
if (absl::GetFlag(FLAGS_symbolize_stacktrace)) {
DumpPCAndSymbol(writerfn, arg, stack[i], " ");
} else {
DumpPC(writerfn, arg, stack[i], " ");
}
}
}
static void DumpStackTraceAndExit() {
DumpStackTrace(1, DebugWriteToStderr, nullptr);
abort();
}
namespace logging_internal {
const char* ProgramInvocationShortName() {
if (g_program_invocation_short_name != nullptr) {
return g_program_invocation_short_name;
} else {
// TODO(user): Use /proc/self/cmdline and so?
return "UNKNOWN";
}
}
bool IsGoogleLoggingInitialized() {
return g_program_invocation_short_name != nullptr;
}
unsigned int GetTID() {
return static_cast<unsigned int>(absl::base_internal::GetTID());
}
int64_t CycleClock_Now() { return absl::ToUnixMicros(absl::Now()); }
int64_t UsecToCycles(int64_t usec) { return usec; }
static int32_t g_main_thread_pid = getpid();
int32_t GetMainThreadPid() { return g_main_thread_pid; }
const char* const_basename(const char* filepath) {
const char* base = strrchr(filepath, '/');
#ifdef _MSC_VER // Look for either path separator in Windows
if (!base) base = strrchr(filepath, '\\');
#endif
return base ? (base + 1) : filepath;
}
static std::string g_my_user_name; // NOLINT
const std::string& MyUserName() { return g_my_user_name; }
static void MyUserNameInitializer() {
#if defined(_MSC_VER)
const char* user = getenv("USERNAME");
#else
const char* user = getenv("USER");
#endif
if (user != nullptr) {
g_my_user_name = user;
} else {
#if !defined(_MSC_VER) // Not windows.
struct passwd pwd;
struct passwd* result = nullptr;
char buffer[1024] = {'\0'};
uid_t uid = geteuid();
int pwuid_res = getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &result);
if (pwuid_res == 0) {
g_my_user_name = pwd.pw_name;
} else {
snprintf(buffer, sizeof(buffer), "uid%d", uid);
g_my_user_name = buffer;
}
#endif // _defined(_MSC_VER)
if (g_my_user_name.empty()) {
g_my_user_name = "invalid-user";
}
}
}
class GoogleInitializer {
public:
typedef void (*void_function)(void);
GoogleInitializer(const char*, void_function f) { f(); }
};
#define REGISTER_MODULE_INITIALIZER(name, body) \
namespace { \
static void google_init_module_##name() { body; } \
GoogleInitializer google_initializer_module_##name( \
#name, google_init_module_##name); \
}
REGISTER_MODULE_INITIALIZER(logging_utilities, MyUserNameInitializer());
void DumpStackTraceToString(std::string* stacktrace) {
DumpStackTrace(1, DebugWriteToString, stacktrace);
}
// We use an atomic operation to prevent problems with calling CrashReason
// from inside the Mutex implementation (potentially through RAW_CHECK).
static const CrashReason* g_reason = 0;
void SetCrashReason(const CrashReason* r) {
sync_val_compare_and_swap(&g_reason, reinterpret_cast<const CrashReason*>(0),
r);
}
void InitGoogleLoggingUtilities(const char* argv0) {
CHECK(!IsGoogleLoggingInitialized())
<< "You called InitGoogleLogging() twice!";
const char* slash = strrchr(argv0, '/');
#ifdef _MSC_VER
if (!slash) slash = strrchr(argv0, '\\');
#endif
g_program_invocation_short_name = slash ? slash + 1 : argv0;
g_main_thread_id = pthread_self();
InstallFailureFunction(&DumpStackTraceAndExit);
}
void ShutdownGoogleLoggingUtilities() {
CHECK(IsGoogleLoggingInitialized())
<< "You called ShutdownGoogleLogging() without calling "
"InitGoogleLogging() first!";
g_program_invocation_short_name = nullptr;
#if !defined(_MSC_VER)
closelog();
#endif // !defined(_MSC_VER)
}
} // namespace logging_internal
} // namespace google