chromeos: logging: maintain chrome.LATEST and chrome.PREVIOUS links
In many situations the chrome log is restarted after a crash, and
the previous log (with the information that matters) is not included
in feedback reports. This changes logging for Chrome OS only (which
already has a bunch of specialized code) to maintain two symlinks
to the two most recent logs.
BUG=chromium:731158
TEST=tested on chromebook, with changes to debugd and chromeos-login
Change-Id: Ia7d037fcbf04c392f6fdc00394c6f73bf92685d9
Reviewed-on: https://chromium-review.googlesource.com/599026
Commit-Queue: Luigi Semenzato <[email protected]>
Reviewed-by: Dan Erat <[email protected]>
Reviewed-by: Lei Zhang <[email protected]>
Cr-Commit-Position: refs/heads/master@{#493506}
diff --git a/chrome/common/logging_chrome.cc b/chrome/common/logging_chrome.cc
index dc58f8d2..8899916 100644
--- a/chrome/common/logging_chrome.cc
+++ b/chrome/common/logging_chrome.cc
@@ -161,21 +161,51 @@
base::FilePath SetUpSymlinkIfNeeded(const base::FilePath& symlink_path,
bool new_log) {
DCHECK(!symlink_path.empty());
+ // For backward compatibility, set up a .../chrome symlink to
+ // .../chrome.LATEST as needed. This code needs to run only
+ // after the migration (i.e. the addition of chrome.LATEST).
+ if (symlink_path.Extension() == ".LATEST") {
+ base::FilePath extensionless_path = symlink_path.ReplaceExtension("");
+ base::FilePath target_path;
- // If not starting a new log, then just log through the existing
- // symlink, but if the symlink doesn't exist, create it. If
- // starting a new log, then delete the old symlink and make a new
- // one to a fresh log file.
+ base::ReadSymbolicLink(extensionless_path, &target_path);
+ if (target_path != symlink_path) {
+ // No link, or wrong link. Clean up. This should happen only once in
+ // each log directory after the OS version update, but some of those
+ // directories may not be accessed for a long time, so this code needs to
+ // stay in forever :/
+ if (base::PathExists(extensionless_path) &&
+ !base::DeleteFile(extensionless_path, false)) {
+ DPLOG(WARNING) << "Cannot delete " << extensionless_path.value();
+ }
+ // After cleaning up, create the symlink.
+ if (!base::CreateSymbolicLink(symlink_path, extensionless_path)) {
+ DPLOG(ERROR) << "Cannot create " << extensionless_path.value();
+ }
+ }
+ }
+
+ // If not starting a new log, then just log through the existing symlink, but
+ // if the symlink doesn't exist, create it.
+ //
+ // If starting a new log, then rename the old symlink as
+ // symlink_path.PREVIOUS and make a new symlink to a fresh log file.
base::FilePath target_path;
bool symlink_exists = base::PathExists(symlink_path);
if (new_log || !symlink_exists) {
target_path = GenerateTimestampedName(symlink_path, base::Time::Now());
- // We don't care if the unlink fails; we're going to continue anyway.
- if (::unlink(symlink_path.value().c_str()) == -1) {
- if (symlink_exists) // only warn if we might expect it to succeed.
- DPLOG(WARNING) << "Unable to unlink " << symlink_path.value();
+ if (symlink_exists) {
+ base::FilePath previous_symlink_path =
+ symlink_path.ReplaceExtension(".PREVIOUS");
+ // Rename symlink to .PREVIOUS. This nukes an existing symlink just like
+ // the rename(2) syscall does.
+ if (!base::ReplaceFile(symlink_path, previous_symlink_path, nullptr)) {
+ DPLOG(WARNING) << "Cannot rename " << symlink_path.value() << " to "
+ << previous_symlink_path.value();
+ }
}
+ // If all went well, the symlink no longer exists. Recreate it.
if (!base::CreateSymbolicLink(target_path, symlink_path)) {
DPLOG(ERROR) << "Unable to create symlink " << symlink_path.value()
<< " pointing at " << target_path.value();
@@ -231,7 +261,6 @@
OldFileDeletionState delete_old_log_file) {
DCHECK(!chrome_logging_initialized_) <<
"Attempted to initialize logging when it was already initialized.";
-
LoggingDestination logging_dest = DetermineLoggingDestination(command_line);
LogLockingState log_locking_state = LOCK_LOG_FILE;
base::FilePath log_path;
@@ -379,10 +408,17 @@
bool DialogsAreSuppressed() {
return dialogs_are_suppressed_;
}
+
+#if defined(OS_CHROMEOS)
base::FilePath GenerateTimestampedName(const base::FilePath& base_path,
base::Time timestamp) {
base::Time::Exploded time_deets;
timestamp.LocalExplode(&time_deets);
+ base::FilePath new_path = base_path;
+ // Assume that the base_path is "chrome.LATEST", and remove the extension.
+ // Ideally we would also check the value of base_path, but we cannot reliably
+ // log anything here, and aborting seems too harsh a choice.
+ new_path = new_path.ReplaceExtension("");
std::string suffix = base::StringPrintf("_%02d%02d%02d-%02d%02d%02d",
time_deets.year,
time_deets.month,
@@ -390,7 +426,8 @@
time_deets.hour,
time_deets.minute,
time_deets.second);
- return base_path.InsertBeforeExtensionASCII(suffix);
+ return new_path.InsertBeforeExtensionASCII(suffix);
}
+#endif // defined(OS_CHROMEOS)
} // namespace logging