fix: reduce manual resource management (#1046)

This commit is contained in:
Sergiu Deitsch 2024-01-07 21:12:40 +01:00 committed by GitHub
parent 1f9ac49c7d
commit 42a185cd88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 237 additions and 187 deletions

View File

@ -965,12 +965,13 @@ namespace google {
LOG_OCCURRENCES, &what_to_do) \ LOG_OCCURRENCES, &what_to_do) \
.stream() .stream()
namespace glog_internal_namespace_ { namespace logging {
namespace internal {
template <bool> template <bool>
struct CompileAssert {}; struct CompileAssert {};
struct CrashReason; struct CrashReason;
} // namespace internal
} // namespace glog_internal_namespace_ } // namespace logging
#define LOG_EVERY_N(severity, n) \ #define LOG_EVERY_N(severity, n) \
SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog)
@ -1334,7 +1335,7 @@ class GLOG_EXPORT LogMessage {
void (LogMessage::*send_method)()); void (LogMessage::*send_method)());
// Used to fill in crash information during LOG(FATAL) failures. // Used to fill in crash information during LOG(FATAL) failures.
void RecordCrashReason(glog_internal_namespace_::CrashReason* reason); void RecordCrashReason(logging::internal::CrashReason* reason);
// Counts of messages sent at each priority: // Counts of messages sent at each priority:
static int64 num_messages_[NUM_SEVERITIES]; // under log_mutex static int64 num_messages_[NUM_SEVERITIES]; // under log_mutex

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc. // Copyright (c) 2024, Google Inc.
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
@ -73,7 +73,7 @@ extern void (*g_logging_fail_func)();
extern void GetExistingTempDirectories(std::vector<std::string>& list); extern void GetExistingTempDirectories(std::vector<std::string>& list);
extern int posix_strerror_r(int err, char* buf, size_t len); extern int posix_strerror_r(int err, char* buf, size_t len);
extern std::string StrError(int err); extern std::string StrError(int err);
} } // namespace google
#undef GLOG_EXPORT #undef GLOG_EXPORT
#define GLOG_EXPORT #define GLOG_EXPORT
@ -300,60 +300,53 @@ static inline void RunSpecifiedBenchmarks() {
class CapturedStream { class CapturedStream {
public: public:
CapturedStream(int fd, string filename) CapturedStream(int fd, string filename)
: fd_(fd), : fd_(fd), filename_(std::move(filename)) {
filename_(std::move(filename)) {
Capture(); Capture();
} }
~CapturedStream() {
if (uncaptured_fd_ != -1) {
CHECK(close(uncaptured_fd_) != -1);
}
}
// Start redirecting output to a file // Start redirecting output to a file
void Capture() { void Capture() {
// Keep original stream for later // Keep original stream for later
CHECK(uncaptured_fd_ == -1) << ", Stream " << fd_ << " already captured!"; CHECK(!uncaptured_fd_) << ", Stream " << fd_ << " already captured!";
uncaptured_fd_ = dup(fd_); uncaptured_fd_.reset(dup(fd_));
CHECK(uncaptured_fd_ != -1); CHECK(uncaptured_fd_);
// Open file to save stream to // Open file to save stream to
int cap_fd = open(filename_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, FileDescriptor cap_fd{open(filename_.c_str(), O_CREAT | O_TRUNC | O_WRONLY,
S_IRUSR | S_IWUSR); S_IRUSR | S_IWUSR)};
CHECK(cap_fd != -1); CHECK(cap_fd);
// Send stdout/stderr to this file // Send stdout/stderr to this file
fflush(nullptr); fflush(nullptr);
CHECK(dup2(cap_fd, fd_) != -1); CHECK(dup2(cap_fd.get(), fd_) != -1);
CHECK(close(cap_fd) != -1); CHECK(cap_fd.close() != -1);
} }
// Remove output redirection // Remove output redirection
void StopCapture() { void StopCapture() {
// Restore original stream // Restore original stream
if (uncaptured_fd_ != -1) { if (uncaptured_fd_) {
fflush(nullptr); fflush(nullptr);
CHECK(dup2(uncaptured_fd_, fd_) != -1); CHECK(dup2(uncaptured_fd_.get(), fd_) != -1);
} }
} }
const string& filename() const { return filename_; } const string& filename() const { return filename_; }
private: private:
int fd_; // file descriptor being captured int fd_; // file descriptor being captured
int uncaptured_fd_{-1}; // where the stream was originally being sent to FileDescriptor
string filename_; // file where stream is being saved uncaptured_fd_; // where the stream was originally being sent to
string filename_; // file where stream is being saved
}; };
static CapturedStream* s_captured_streams[STDERR_FILENO + 1]; static std::unique_ptr<CapturedStream> s_captured_streams[STDERR_FILENO + 1];
// Redirect a file descriptor to a file. // Redirect a file descriptor to a file.
// fd - Should be STDOUT_FILENO or STDERR_FILENO // fd - Should be STDOUT_FILENO or STDERR_FILENO
// filename - File where output should be stored // filename - File where output should be stored
static inline void CaptureTestOutput(int fd, const string& filename) { static inline void CaptureTestOutput(int fd, const string& filename) {
CHECK((fd == STDOUT_FILENO) || (fd == STDERR_FILENO)); CHECK((fd == STDOUT_FILENO) || (fd == STDERR_FILENO));
CHECK(s_captured_streams[fd] == nullptr); CHECK(s_captured_streams[fd] == nullptr);
s_captured_streams[fd] = new CapturedStream(fd, filename); s_captured_streams[fd] = std::make_unique<CapturedStream>(fd, filename);
} }
static inline void CaptureTestStdout() { static inline void CaptureTestStdout() {
CaptureTestOutput(STDOUT_FILENO, FLAGS_test_tmpdir + "/captured.out"); CaptureTestOutput(STDOUT_FILENO, FLAGS_test_tmpdir + "/captured.out");
@ -369,7 +362,7 @@ static inline size_t GetFileSize(FILE* file) {
// Read the entire content of a file as a string // Read the entire content of a file as a string
static inline string ReadEntireFile(FILE* file) { static inline string ReadEntireFile(FILE* file) {
const size_t file_size = GetFileSize(file); const size_t file_size = GetFileSize(file);
char* const buffer = new char[file_size]; std::vector<char> content(file_size);
size_t bytes_last_read = 0; // # of bytes read in the last fread() size_t bytes_last_read = 0; // # of bytes read in the last fread()
size_t bytes_read = 0; // # of bytes read so far size_t bytes_read = 0; // # of bytes read so far
@ -380,32 +373,26 @@ static inline string ReadEntireFile(FILE* file) {
// pre-determined file size is reached. // pre-determined file size is reached.
do { do {
bytes_last_read = bytes_last_read =
fread(buffer + bytes_read, 1, file_size - bytes_read, file); fread(content.data() + bytes_read, 1, file_size - bytes_read, file);
bytes_read += bytes_last_read; bytes_read += bytes_last_read;
} while (bytes_last_read > 0 && bytes_read < file_size); } while (bytes_last_read > 0 && bytes_read < file_size);
const string content = string(buffer, buffer + bytes_read); return std::string(content.data(), bytes_read);
delete[] buffer;
return content;
} }
// Get the captured stdout (when fd is STDOUT_FILENO) or stderr (when // Get the captured stdout (when fd is STDOUT_FILENO) or stderr (when
// fd is STDERR_FILENO) as a string // fd is STDERR_FILENO) as a string
static inline string GetCapturedTestOutput(int fd) { static inline string GetCapturedTestOutput(int fd) {
CHECK(fd == STDOUT_FILENO || fd == STDERR_FILENO); CHECK(fd == STDOUT_FILENO || fd == STDERR_FILENO);
CapturedStream* const cap = s_captured_streams[fd]; std::unique_ptr<CapturedStream> cap = std::move(s_captured_streams[fd]);
CHECK(cap) << ": did you forget CaptureTestStdout() or CaptureTestStderr()?"; CHECK(cap) << ": did you forget CaptureTestStdout() or CaptureTestStderr()?";
// Make sure everything is flushed. // Make sure everything is flushed.
cap->StopCapture(); cap->StopCapture();
// Read the captured file. // Read the captured file.
FILE* const file = fopen(cap->filename().c_str(), "r"); std::unique_ptr<FILE> file{fopen(cap->filename().c_str(), "r")};
const string content = ReadEntireFile(file); const string content = ReadEntireFile(file.get());
fclose(file); file.reset();
delete cap;
s_captured_streams[fd] = nullptr;
return content; return content;
} }
@ -479,11 +466,11 @@ static inline void StringReplace(string* str, const string& oldsub,
} }
static inline string Munge(const string& filename) { static inline string Munge(const string& filename) {
FILE* fp = fopen(filename.c_str(), "rb"); std::unique_ptr<FILE> fp{fopen(filename.c_str(), "rb")};
CHECK(fp != nullptr) << filename << ": couldn't open"; CHECK(fp != nullptr) << filename << ": couldn't open";
char buf[4096]; char buf[4096];
string result; string result;
while (fgets(buf, 4095, fp)) { while (fgets(buf, 4095, fp.get())) {
string line = MungeLine(buf); string line = MungeLine(buf);
const size_t str_size = 256; const size_t str_size = 256;
char null_str[str_size]; char null_str[str_size];
@ -502,19 +489,17 @@ static inline string Munge(const string& filename) {
StringReplace(&line, "__ENOEXEC__", StrError(ENOEXEC)); StringReplace(&line, "__ENOEXEC__", StrError(ENOEXEC));
result += line + "\n"; result += line + "\n";
} }
fclose(fp);
return result; return result;
} }
static inline void WriteToFile(const string& body, const string& file) { static inline void WriteToFile(const string& body, const string& file) {
FILE* fp = fopen(file.c_str(), "wb"); std::unique_ptr<FILE> fp{fopen(file.c_str(), "wb")};
fwrite(body.data(), 1, body.size(), fp); fwrite(body.data(), 1, body.size(), fp.get());
fclose(fp);
} }
static inline bool MungeAndDiffTest(const string& golden_filename, static inline bool MungeAndDiffTest(const string& golden_filename,
CapturedStream* cap) { CapturedStream* cap) {
if (cap == s_captured_streams[STDOUT_FILENO]) { if (cap == s_captured_streams[STDOUT_FILENO].get()) {
CHECK(cap) << ": did you forget CaptureTestStdout()?"; CHECK(cap) << ": did you forget CaptureTestStdout()?";
} else { } else {
CHECK(cap) << ": did you forget CaptureTestStderr()?"; CHECK(cap) << ": did you forget CaptureTestStderr()?";
@ -549,11 +534,13 @@ static inline bool MungeAndDiffTest(const string& golden_filename,
} }
static inline bool MungeAndDiffTestStderr(const string& golden_filename) { static inline bool MungeAndDiffTestStderr(const string& golden_filename) {
return MungeAndDiffTest(golden_filename, s_captured_streams[STDERR_FILENO]); return MungeAndDiffTest(golden_filename,
s_captured_streams[STDERR_FILENO].get());
} }
static inline bool MungeAndDiffTestStdout(const string& golden_filename) { static inline bool MungeAndDiffTestStdout(const string& golden_filename) {
return MungeAndDiffTest(golden_filename, s_captured_streams[STDOUT_FILENO]); return MungeAndDiffTest(golden_filename,
s_captured_streams[STDOUT_FILENO].get());
} }
// Save flags used from logging_unittest.cc. // Save flags used from logging_unittest.cc.

View File

@ -1011,11 +1011,12 @@ bool LogFileObject::CreateLogfile(const string& time_pid_string) {
// demand that the file is unique for our timestamp (fail if it exists). // demand that the file is unique for our timestamp (fail if it exists).
flags = flags | O_EXCL; flags = flags | O_EXCL;
} }
int fd = open(filename, flags, static_cast<mode_t>(FLAGS_logfile_mode)); FileDescriptor fd{
if (fd == -1) return false; open(filename, flags, static_cast<mode_t>(FLAGS_logfile_mode))};
if (!fd) return false;
#ifdef HAVE_FCNTL #ifdef HAVE_FCNTL
// Mark the file close-on-exec. We don't really care if this fails // Mark the file close-on-exec. We don't really care if this fails
fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
// Mark the file as exclusive write access to avoid two clients logging to the // Mark the file as exclusive write access to avoid two clients logging to the
// same file. This applies particularly when !FLAGS_timestamp_in_logfile_name // same file. This applies particularly when !FLAGS_timestamp_in_logfile_name
@ -1034,17 +1035,15 @@ bool LogFileObject::CreateLogfile(const string& time_pid_string) {
w_lock.l_whence = SEEK_SET; w_lock.l_whence = SEEK_SET;
w_lock.l_len = 0; w_lock.l_len = 0;
int wlock_ret = fcntl(fd, F_SETLK, &w_lock); int wlock_ret = fcntl(fd.get(), F_SETLK, &w_lock);
if (wlock_ret == -1) { if (wlock_ret == -1) {
close(fd); // as we are failing already, do not check errors here
return false; return false;
} }
#endif #endif
// fdopen in append mode so if the file exists it will fseek to the end // fdopen in append mode so if the file exists it will fseek to the end
file_.reset(fdopen(fd, "a")); // Make a FILE*. file_.reset(fdopen(fd.release(), "a")); // Make a FILE*.
if (file_ == nullptr) { // Man, we're screwed! if (file_ == nullptr) { // Man, we're screwed!
close(fd);
if (FLAGS_timestamp_in_logfile_name) { if (FLAGS_timestamp_in_logfile_name) {
unlink(filename); // Erase the half-baked evidence: an unusable log file, unlink(filename); // Erase the half-baked evidence: an unusable log file,
// only if we just created it. // only if we just created it.
@ -1506,7 +1505,7 @@ bool LogCleaner::IsLogLastModifiedOver(
// for exclusive use by the first thread, and one for shared use by // for exclusive use by the first thread, and one for shared use by
// all other threads. // all other threads.
static std::mutex fatal_msg_lock; static std::mutex fatal_msg_lock;
static CrashReason crash_reason; static logging::internal::CrashReason crash_reason;
static bool fatal_msg_exclusive = true; static bool fatal_msg_exclusive = true;
static LogMessage::LogMessageData fatal_msg_data_exclusive; static LogMessage::LogMessageData fatal_msg_data_exclusive;
static LogMessage::LogMessageData fatal_msg_data_shared; static LogMessage::LogMessageData fatal_msg_data_shared;
@ -1861,8 +1860,7 @@ void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
} }
} }
void LogMessage::RecordCrashReason( void LogMessage::RecordCrashReason(logging::internal::CrashReason* reason) {
glog_internal_namespace_::CrashReason* reason) {
reason->filename = fatal_msg_data_exclusive.fullname_; reason->filename = fatal_msg_data_exclusive.fullname_;
reason->line_number = fatal_msg_data_exclusive.line_; reason->line_number = fatal_msg_data_exclusive.line_;
reason->message = fatal_msg_data_exclusive.message_text_ + reason->message = fatal_msg_data_exclusive.message_text_ +
@ -2396,8 +2394,8 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
if (strncmp(procfd_prefix, path, strlen(procfd_prefix))) flags |= O_NOFOLLOW; if (strncmp(procfd_prefix, path, strlen(procfd_prefix))) flags |= O_NOFOLLOW;
# endif # endif
int fd = open(path, flags); FileDescriptor fd{open(path, flags)};
if (fd == -1) { if (!fd) {
if (errno == EFBIG) { if (errno == EFBIG) {
// The log file in question has got too big for us to open. The // The log file in question has got too big for us to open. The
// real fix for this would be to compile logging.cc (or probably // real fix for this would be to compile logging.cc (or probably
@ -2405,7 +2403,7 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
// rather scary. // rather scary.
// Instead just truncate the file to something we can manage // Instead just truncate the file to something we can manage
# ifdef HAVE__CHSIZE_S # ifdef HAVE__CHSIZE_S
if (_chsize_s(fd, 0) != 0) { if (_chsize_s(fd.get(), 0) != 0) {
# else # else
if (truncate(path, 0) == -1) { if (truncate(path, 0) == -1) {
# endif # endif
@ -2419,16 +2417,16 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
return; return;
} }
if (fstat(fd, &statbuf) == -1) { if (fstat(fd.get(), &statbuf) == -1) {
PLOG(ERROR) << "Unable to fstat()"; PLOG(ERROR) << "Unable to fstat()";
goto out_close_fd; return;
} }
// See if the path refers to a regular file bigger than the // See if the path refers to a regular file bigger than the
// specified limit // specified limit
if (!S_ISREG(statbuf.st_mode)) goto out_close_fd; if (!S_ISREG(statbuf.st_mode)) return;
if (statbuf.st_size <= static_cast<off_t>(limit)) goto out_close_fd; if (statbuf.st_size <= static_cast<off_t>(limit)) return;
if (statbuf.st_size <= static_cast<off_t>(keep)) goto out_close_fd; if (statbuf.st_size <= static_cast<off_t>(keep)) return;
// This log file is too large - we need to truncate it // This log file is too large - we need to truncate it
LOG(INFO) << "Truncating " << path << " to " << keep << " bytes"; LOG(INFO) << "Truncating " << path << " to " << keep << " bytes";
@ -2437,8 +2435,10 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
read_offset = statbuf.st_size - static_cast<off_t>(keep); read_offset = statbuf.st_size - static_cast<off_t>(keep);
write_offset = 0; write_offset = 0;
ssize_t bytesin, bytesout; ssize_t bytesin, bytesout;
while ((bytesin = pread(fd, copybuf, sizeof(copybuf), read_offset)) > 0) { while ((bytesin = pread(fd.get(), copybuf, sizeof(copybuf), read_offset)) >
bytesout = pwrite(fd, copybuf, static_cast<size_t>(bytesin), write_offset); 0) {
bytesout =
pwrite(fd.get(), copybuf, static_cast<size_t>(bytesin), write_offset);
if (bytesout == -1) { if (bytesout == -1) {
PLOG(ERROR) << "Unable to write to " << path; PLOG(ERROR) << "Unable to write to " << path;
break; break;
@ -2454,15 +2454,13 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
// end of the file after our last read() above, we lose their latest // end of the file after our last read() above, we lose their latest
// data. Too bad ... // data. Too bad ...
# ifdef HAVE__CHSIZE_S # ifdef HAVE__CHSIZE_S
if (_chsize_s(fd, write_offset) != 0) { if (_chsize_s(fd.get(), write_offset) != 0) {
# else # else
if (ftruncate(fd, write_offset) == -1) { if (ftruncate(fd.get(), write_offset) == -1) {
# endif # endif
PLOG(ERROR) << "Unable to truncate " << path; PLOG(ERROR) << "Unable to truncate " << path;
} }
out_close_fd:
close(fd);
#else #else
LOG(ERROR) << "No log truncation support."; LOG(ERROR) << "No log truncation support.";
#endif #endif

View File

@ -31,19 +31,6 @@
#include <fcntl.h> #include <fcntl.h>
#include "config.h"
#include "utilities.h"
#ifdef HAVE_GLOB_H
# include <glob.h>
#endif
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#include <chrono> #include <chrono>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
@ -58,10 +45,23 @@
#include <thread> #include <thread>
#include <vector> #include <vector>
#include "config.h"
#ifdef HAVE_GLOB_H
# include <glob.h>
#endif
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#include "base/commandlineflags.h" #include "base/commandlineflags.h"
#include "glog/logging.h" #include "glog/logging.h"
#include "glog/raw_logging.h" #include "glog/raw_logging.h"
#include "googletest.h" #include "googletest.h"
#include "utilities.h"
#ifdef GLOG_USE_GFLAGS #ifdef GLOG_USE_GFLAGS
# include <gflags/gflags.h> # include <gflags/gflags.h>
@ -785,19 +785,17 @@ static void CheckFile(const string& name, const string& expected_string,
GetFiles(name + "*", &files); GetFiles(name + "*", &files);
CHECK_EQ(files.size(), 1UL); CHECK_EQ(files.size(), 1UL);
FILE* file = fopen(files[0].c_str(), "r"); std::unique_ptr<std::FILE> file{fopen(files[0].c_str(), "r")};
CHECK(file != nullptr) << ": could not open " << files[0]; CHECK(file != nullptr) << ": could not open " << files[0];
char buf[1000]; char buf[1000];
while (fgets(buf, sizeof(buf), file) != nullptr) { while (fgets(buf, sizeof(buf), file.get()) != nullptr) {
char* first = strstr(buf, expected_string.c_str()); char* first = strstr(buf, expected_string.c_str());
// if first == nullptr, not found. // if first == nullptr, not found.
// Terser than if (checkInFileOrNot && first != nullptr || !check... // Terser than if (checkInFileOrNot && first != nullptr || !check...
if (checkInFileOrNot != (first == nullptr)) { if (checkInFileOrNot != (first == nullptr)) {
fclose(file);
return; return;
} }
} }
fclose(file);
LOG(FATAL) << "Did " << (checkInFileOrNot ? "not " : "") << "find " LOG(FATAL) << "Did " << (checkInFileOrNot ? "not " : "") << "find "
<< expected_string << " in " << files[0]; << expected_string << " in " << files[0];
} }
@ -977,8 +975,8 @@ static void TestErrno() {
static void TestOneTruncate(const char* path, uint64 limit, uint64 keep, static void TestOneTruncate(const char* path, uint64 limit, uint64 keep,
size_t dsize, size_t ksize, size_t expect) { size_t dsize, size_t ksize, size_t expect) {
int fd; FileDescriptor fd{open(path, O_RDWR | O_CREAT | O_TRUNC, 0600)};
CHECK_ERR(fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0600)); CHECK_ERR(fd);
const char *discardstr = "DISCARDME!", *keepstr = "KEEPME!"; const char *discardstr = "DISCARDME!", *keepstr = "KEEPME!";
const size_t discard_size = strlen(discardstr), keep_size = strlen(keepstr); const size_t discard_size = strlen(discardstr), keep_size = strlen(keepstr);
@ -987,13 +985,13 @@ static void TestOneTruncate(const char* path, uint64 limit, uint64 keep,
size_t written = 0; size_t written = 0;
while (written < dsize) { while (written < dsize) {
size_t bytes = min(dsize - written, discard_size); size_t bytes = min(dsize - written, discard_size);
CHECK_ERR(write(fd, discardstr, bytes)); CHECK_ERR(write(fd.get(), discardstr, bytes));
written += bytes; written += bytes;
} }
written = 0; written = 0;
while (written < ksize) { while (written < ksize) {
size_t bytes = min(ksize - written, keep_size); size_t bytes = min(ksize - written, keep_size);
CHECK_ERR(write(fd, keepstr, bytes)); CHECK_ERR(write(fd.get(), keepstr, bytes));
written += bytes; written += bytes;
} }
@ -1001,25 +999,22 @@ static void TestOneTruncate(const char* path, uint64 limit, uint64 keep,
// File should now be shorter // File should now be shorter
struct stat statbuf; struct stat statbuf;
CHECK_ERR(fstat(fd, &statbuf)); CHECK_ERR(fstat(fd.get(), &statbuf));
CHECK_EQ(static_cast<size_t>(statbuf.st_size), expect); CHECK_EQ(static_cast<size_t>(statbuf.st_size), expect);
CHECK_ERR(lseek(fd, 0, SEEK_SET)); CHECK_ERR(lseek(fd.get(), 0, SEEK_SET));
// File should contain the suffix of the original file // File should contain the suffix of the original file
const size_t buf_size = static_cast<size_t>(statbuf.st_size) + 1; const size_t buf_size = static_cast<size_t>(statbuf.st_size) + 1;
char* buf = new char[buf_size]; std::vector<char> buf(buf_size);
memset(buf, 0, buf_size); CHECK_ERR(read(fd.get(), buf.data(), buf_size));
CHECK_ERR(read(fd, buf, buf_size));
const char* p = buf; const char* p = buf.data();
size_t checked = 0; size_t checked = 0;
while (checked < expect) { while (checked < expect) {
size_t bytes = min(expect - checked, keep_size); size_t bytes = min(expect - checked, keep_size);
CHECK(!memcmp(p, keepstr, bytes)); CHECK(!memcmp(p, keepstr, bytes));
checked += bytes; checked += bytes;
} }
close(fd);
delete[] buf;
} }
static void TestTruncate() { static void TestTruncate() {
@ -1155,13 +1150,11 @@ static void TestLogPeriodically() {
} }
namespace google { namespace google {
namespace glog_internal_namespace_ { inline namespace glog_internal_namespace_ {
extern // in logging.cc // in logging.cc
bool extern bool SafeFNMatch_(const char* pattern, size_t patt_len, const char* str,
SafeFNMatch_(const char* pattern, size_t patt_len, const char* str, size_t str_len);
size_t str_len);
} // namespace glog_internal_namespace_ } // namespace glog_internal_namespace_
using glog_internal_namespace_::SafeFNMatch_;
} // namespace google } // namespace google
static bool WrapSafeFNMatch(string pattern, string str) { static bool WrapSafeFNMatch(string pattern, string str) {
@ -1352,28 +1345,26 @@ static void TestLogSinkWaitTillSent() {
TEST(Strerror, logging) { TEST(Strerror, logging) {
int errcode = EINTR; int errcode = EINTR;
char* msg = strdup(strerror(errcode)); std::string msg = strerror(errcode);
const size_t buf_size = strlen(msg) + 1; const size_t buf_size = msg.size() + 1;
char* buf = new char[buf_size]; std::vector<char> buf(buf_size);
CHECK_EQ(posix_strerror_r(errcode, nullptr, 0), -1); CHECK_EQ(posix_strerror_r(errcode, nullptr, 0), -1);
buf[0] = 'A'; buf[0] = 'A';
CHECK_EQ(posix_strerror_r(errcode, buf, 0), -1); CHECK_EQ(posix_strerror_r(errcode, buf.data(), 0), -1);
CHECK_EQ(buf[0], 'A'); CHECK_EQ(buf[0], 'A');
CHECK_EQ(posix_strerror_r(errcode, nullptr, buf_size), -1); CHECK_EQ(posix_strerror_r(errcode, nullptr, buf_size), -1);
#if defined(GLOG_OS_MACOSX) || defined(GLOG_OS_FREEBSD) || \ #if defined(GLOG_OS_MACOSX) || defined(GLOG_OS_FREEBSD) || \
defined(GLOG_OS_OPENBSD) defined(GLOG_OS_OPENBSD)
// MacOSX or FreeBSD considers this case is an error since there is // MacOSX or FreeBSD considers this case is an error since there is
// no enough space. // no enough space.
CHECK_EQ(posix_strerror_r(errcode, buf, 1), -1); CHECK_EQ(posix_strerror_r(errcode, buf.data(), 1), -1);
#else #else
CHECK_EQ(posix_strerror_r(errcode, buf, 1), 0); CHECK_EQ(posix_strerror_r(errcode, buf.data(), 1), 0);
#endif #endif
CHECK_STREQ(buf, ""); CHECK_STREQ(buf.data(), "");
CHECK_EQ(posix_strerror_r(errcode, buf, buf_size), 0); CHECK_EQ(posix_strerror_r(errcode, buf.data(), buf_size), 0);
CHECK_STREQ(buf, msg); CHECK_STREQ(buf.data(), msg.c_str());
delete[] buf;
CHECK_EQ(msg, StrError(errcode)); CHECK_EQ(msg, StrError(errcode));
free(msg);
} }
// Simple routines to look at the sizes of generated code for LOG(FATAL) and // Simple routines to look at the sizes of generated code for LOG(FATAL) and

View File

@ -125,7 +125,7 @@ inline static bool VADoRawLog(char** buf, size_t* size, const char* format,
static const int kLogBufSize = 3000; static const int kLogBufSize = 3000;
static std::once_flag crashed; static std::once_flag crashed;
static CrashReason crash_reason; static logging::internal::CrashReason crash_reason;
static char crash_buf[kLogBufSize + 1] = {0}; // Will end in '\0' static char crash_buf[kLogBufSize + 1] = {0}; // Will end in '\0'
namespace { namespace {

View File

@ -376,22 +376,6 @@ static bool GetSymbolFromObjectFile(const int fd, uint64_t pc, char* out,
} }
namespace { namespace {
// Thin wrapper around a file descriptor so that the file descriptor
// gets closed for sure.
struct FileDescriptor {
const int fd_;
explicit FileDescriptor(int fd) : fd_(fd) {}
~FileDescriptor() {
if (fd_ >= 0) {
close(fd_);
}
}
int get() { return fd_; }
private:
FileDescriptor(const FileDescriptor&) = delete;
void operator=(const FileDescriptor&) = delete;
};
// Helper class for reading lines from file. // Helper class for reading lines from file.
// //
@ -520,14 +504,14 @@ static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(
int maps_fd; int maps_fd;
NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY)); NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY));
FileDescriptor wrapped_maps_fd(maps_fd); FileDescriptor wrapped_maps_fd(maps_fd);
if (wrapped_maps_fd.get() < 0) { if (!wrapped_maps_fd) {
return -1; return -1;
} }
int mem_fd; int mem_fd;
NO_INTR(mem_fd = open("/proc/self/mem", O_RDONLY)); NO_INTR(mem_fd = open("/proc/self/mem", O_RDONLY));
FileDescriptor wrapped_mem_fd(mem_fd); FileDescriptor wrapped_mem_fd(mem_fd);
if (wrapped_mem_fd.get() < 0) { if (!wrapped_mem_fd) {
return -1; return -1;
} }

View File

@ -71,6 +71,12 @@ bool IsGoogleLoggingInitialized() {
return g_program_invocation_short_name != nullptr; return g_program_invocation_short_name != nullptr;
} }
inline namespace glog_internal_namespace_ {
constexpr int FileDescriptor::InvalidHandle;
} // namespace glog_internal_namespace_
} // namespace google } // namespace google
// The following APIs are all internal. // The following APIs are all internal.
@ -181,7 +187,7 @@ DumpStackTraceAndExit() {
namespace google { namespace google {
namespace glog_internal_namespace_ { inline namespace glog_internal_namespace_ {
const char* ProgramInvocationShortName() { const char* ProgramInvocationShortName() {
if (g_program_invocation_short_name != nullptr) { if (g_program_invocation_short_name != nullptr) {
@ -252,10 +258,10 @@ void DumpStackTraceToString(string* stacktrace) {
// We use an atomic operation to prevent problems with calling CrashReason // We use an atomic operation to prevent problems with calling CrashReason
// from inside the Mutex implementation (potentially through RAW_CHECK). // from inside the Mutex implementation (potentially through RAW_CHECK).
static std::atomic<const CrashReason*> g_reason{nullptr}; static std::atomic<const logging::internal::CrashReason*> g_reason{nullptr};
void SetCrashReason(const CrashReason* r) { void SetCrashReason(const logging::internal::CrashReason* r) {
const CrashReason* expected = nullptr; const logging::internal::CrashReason* expected = nullptr;
g_reason.compare_exchange_strong(expected, r); g_reason.compare_exchange_strong(expected, r);
} }

View File

@ -35,6 +35,13 @@
#ifndef UTILITIES_H__ #ifndef UTILITIES_H__
#define UTILITIES_H__ #define UTILITIES_H__
#include <cstddef>
#include <cstdio>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
// printf macros for size_t, in the style of inttypes.h // printf macros for size_t, in the style of inttypes.h
#ifdef _LP64 #ifdef _LP64
# define __PRIS_PREFIX "z" # define __PRIS_PREFIX "z"
@ -53,12 +60,6 @@
#define PRIXS __PRIS_PREFIX "X" #define PRIXS __PRIS_PREFIX "X"
#define PRIoS __PRIS_PREFIX "o" #define PRIoS __PRIS_PREFIX "o"
#include <cstdio>
#include <memory>
#include <string>
#include <thread>
#include <type_traits>
#include "glog/logging.h" #include "glog/logging.h"
#if defined(GLOG_USE_WINDOWS_PORT) #if defined(GLOG_USE_WINDOWS_PORT)
@ -146,7 +147,26 @@ using ssize_t = std::ptrdiff_t;
namespace google { namespace google {
namespace glog_internal_namespace_ { namespace logging {
namespace internal {
struct CrashReason {
CrashReason() = default;
const char* filename{nullptr};
int line_number{0};
const char* message{nullptr};
// We'll also store a bit of stack trace context at the time of crash as
// it may not be available later on.
void* stack[32];
int depth{0};
};
} // namespace internal
} // namespace logging
inline namespace glog_internal_namespace_ {
#if defined(__has_attribute) #if defined(__has_attribute)
# if __has_attribute(noinline) # if __has_attribute(noinline)
@ -179,20 +199,7 @@ const char* const_basename(const char* filepath);
void DumpStackTraceToString(std::string* stacktrace); void DumpStackTraceToString(std::string* stacktrace);
struct CrashReason { void SetCrashReason(const logging::internal::CrashReason* r);
CrashReason() = default;
const char* filename{nullptr};
int line_number{0};
const char* message{nullptr};
// We'll also store a bit of stack trace context at the time of crash as
// it may not be available later on.
void* stack[32];
int depth{0};
};
void SetCrashReason(const CrashReason* r);
void InitGoogleLoggingUtilities(const char* argv0); void InitGoogleLoggingUtilities(const char* argv0);
void ShutdownGoogleLoggingUtilities(); void ShutdownGoogleLoggingUtilities();
@ -215,12 +222,96 @@ class ScopedExit final {
Functor functor_; Functor functor_;
}; };
// Thin wrapper around a file descriptor so that the file descriptor
// gets closed for sure.
class GLOG_NO_EXPORT FileDescriptor final {
static constexpr int InvalidHandle = -1;
public:
constexpr FileDescriptor() noexcept : FileDescriptor{nullptr} {}
constexpr explicit FileDescriptor(int fd) noexcept : fd_{fd} {}
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr FileDescriptor(std::nullptr_t) noexcept : fd_{InvalidHandle} {}
FileDescriptor(const FileDescriptor& other) = delete;
FileDescriptor& operator=(const FileDescriptor& other) = delete;
FileDescriptor(FileDescriptor&& other) noexcept : fd_{other.release()} {}
FileDescriptor& operator=(FileDescriptor&& other) noexcept {
// Close the file descriptor being held and assign a new file descriptor
// previously held by 'other' without closing it.
reset(other.release());
return *this;
}
constexpr explicit operator bool() const noexcept {
return fd_ != InvalidHandle;
}
constexpr int get() const noexcept { return fd_; }
int release() noexcept { return std::exchange(fd_, InvalidHandle); }
void reset(std::nullptr_t) noexcept { safe_close(); }
void reset() noexcept { reset(nullptr); }
void reset(int fd) noexcept {
reset();
fd_ = fd;
}
int close() noexcept { return unsafe_close(); }
~FileDescriptor() { safe_close(); }
private:
int unsafe_close() noexcept { return ::close(release()); }
void safe_close() noexcept {
if (*this) {
unsafe_close();
}
}
int fd_;
};
// Provide variants of (in)equality comparison operators to avoid constructing
// temporaries.
constexpr bool operator==(const FileDescriptor& lhs, int rhs) noexcept {
return lhs.get() == rhs;
}
constexpr bool operator==(int lhs, const FileDescriptor& rhs) noexcept {
return rhs == lhs;
}
constexpr bool operator!=(const FileDescriptor& lhs, int rhs) noexcept {
return !(lhs == rhs);
}
constexpr bool operator!=(int lhs, const FileDescriptor& rhs) noexcept {
return !(lhs == rhs);
}
constexpr bool operator==(const FileDescriptor& lhs, std::nullptr_t) noexcept {
return !lhs;
}
constexpr bool operator==(std::nullptr_t, const FileDescriptor& rhs) noexcept {
return !rhs;
}
constexpr bool operator!=(const FileDescriptor& lhs, std::nullptr_t) noexcept {
return static_cast<bool>(lhs);
}
constexpr bool operator!=(std::nullptr_t, const FileDescriptor& rhs) noexcept {
return static_cast<bool>(rhs);
}
} // namespace glog_internal_namespace_ } // namespace glog_internal_namespace_
} // namespace google } // namespace google
using namespace google::glog_internal_namespace_;
template <> template <>
struct std::default_delete<std::FILE> { struct std::default_delete<std::FILE> {
void operator()(FILE* p) const noexcept { fclose(p); } void operator()(FILE* p) const noexcept { fclose(p); }

View File

@ -50,18 +50,14 @@ using std::string;
namespace google { namespace google {
namespace glog_internal_namespace_ { inline namespace glog_internal_namespace_ {
// Used by logging_unittests.cc so can't make it static here.
GLOG_EXPORT bool SafeFNMatch_(const char* pattern, size_t patt_len,
const char* str, size_t str_len);
// Implementation of fnmatch that does not need 0-termination // Implementation of fnmatch that does not need 0-termination
// of arguments and does not allocate any memory, // of arguments and does not allocate any memory,
// but we only support "*" and "?" wildcards, not the "[...]" patterns. // but we only support "*" and "?" wildcards, not the "[...]" patterns.
// It's not a static function for the unittest. // It's not a static function for the unittest.
GLOG_EXPORT bool SafeFNMatch_(const char* pattern, size_t patt_len, GLOG_NO_EXPORT bool SafeFNMatch_(const char* pattern, size_t patt_len,
const char* str, size_t str_len) { const char* str, size_t str_len) {
size_t p = 0; size_t p = 0;
size_t s = 0; size_t s = 0;
while (true) { while (true) {

View File

@ -35,6 +35,7 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <cwchar> #include <cwchar>
#include <memory>
/* Indicates that d_type field is available in dirent structure */ /* Indicates that d_type field is available in dirent structure */
#define _DIRENT_HAVE_D_TYPE #define _DIRENT_HAVE_D_TYPE
@ -622,8 +623,6 @@ static WIN32_FIND_DATAW* dirent_next(_WDIR* dirp) {
* Open directory stream using plain old C-string. * Open directory stream using plain old C-string.
*/ */
static DIR* opendir(const char* dirname) { static DIR* opendir(const char* dirname) {
struct DIR* dirp;
/* Must have directory name */ /* Must have directory name */
if (dirname == nullptr || dirname[0] == '\0') { if (dirname == nullptr || dirname[0] == '\0') {
dirent_set_errno(ENOENT); dirent_set_errno(ENOENT);
@ -631,7 +630,9 @@ static DIR* opendir(const char* dirname) {
} }
/* Allocate memory for DIR structure */ /* Allocate memory for DIR structure */
dirp = (DIR*)malloc(sizeof(struct DIR)); std::unique_ptr<DIR, decltype(&free)> dirp{
static_cast<DIR*>(malloc(sizeof(struct DIR))), &free};
if (!dirp) { if (!dirp) {
return nullptr; return nullptr;
} }
@ -649,23 +650,18 @@ static DIR* opendir(const char* dirname) {
* the output buffer is too small to contain the resulting * the output buffer is too small to contain the resulting
* string. * string.
*/ */
goto exit_free; return nullptr;
} }
/* Open directory stream using wide-character name */ /* Open directory stream using wide-character name */
dirp->wdirp = _wopendir(wname); dirp->wdirp = _wopendir(wname);
if (!dirp->wdirp) { if (!dirp->wdirp) {
goto exit_free; return nullptr;
} }
} }
/* Success */ /* Success */
return dirp; return dirp.release();
/* Failure */
exit_free:
free(dirp);
return nullptr;
} }
/* /*