330 lines
10 KiB
C++
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
|