0

Present drivefs logs on drive-internals page.

BUG=chromium:856874

Change-Id: I9e7e9645d120b27868618053cffd5953908b99b1
Reviewed-on: https://chromium-review.googlesource.com/1184801
Reviewed-by: Luciano Pacheco <lucmult@chromium.org>
Reviewed-by: Sam McNally <sammc@chromium.org>
Commit-Queue: Sergei Datsenko <dats@chromium.org>
Cr-Commit-Position: refs/heads/master@{#585718}
This commit is contained in:
Sergei Datsenko
2018-08-24 06:07:38 +00:00
committed by Commit Bot
parent 44f72ec65c
commit 4663663d7e
8 changed files with 189 additions and 31 deletions

@ -559,6 +559,13 @@ base::FilePath DriveIntegrationService::GetMountPointPath() const {
: drive::util::GetDriveMountPointPath(profile_);
}
base::FilePath DriveIntegrationService::GetDriveFsLogPath() const {
return drivefs_holder_
? drivefs_holder_->drivefs_host()->GetDataPath().Append(
"Logs/drivefs.txt")
: base::FilePath();
}
bool DriveIntegrationService::GetRelativeDrivePath(
const base::FilePath& local_path,
base::FilePath* drive_path) const {

@ -115,6 +115,9 @@ class DriveIntegrationService : public KeyedService,
// |IsMounted()|.
base::FilePath GetMountPointPath() const;
// Returns the path of DriveFS log if enabled or empty path.
base::FilePath GetDriveFsLogPath() const;
// Returns true if |local_path| resides inside |GetMountPointPath()|.
// In this case |drive_path| will contain 'drive' path of this file, e.g.
// reparented to the mount point.

@ -142,5 +142,9 @@
<h2 id="event-log-section">Event Log</h2>
<ul id="event-log">
</ul>
<h2 id="service-log-section">Service Log</h2>
<ul id="service-log">
</ul>
</body>
</html>

@ -221,6 +221,15 @@ function updateEventLog(log) {
updateKeyValueList(ul, log);
}
/**
* Updates the service log section.
* @param {Array} log Log lines.
*/
function updateServiceLog(log) {
var ul = $('service-log');
updateKeyValueList(ul, log);
}
/**
* Creates an element named |elementName| containing the content |text|.
* @param {string} elementName Name of the new element to be created.

@ -7,6 +7,7 @@
#include <stddef.h>
#include <stdint.h>
#include <fstream>
#include <memory>
#include <utility>
@ -17,6 +18,8 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/path_service.h"
#include "base/strings/pattern.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
#include "base/task/post_task.h"
@ -52,6 +55,32 @@ namespace chromeos {
namespace {
constexpr char kKey[] = "key";
constexpr char kValue[] = "value";
constexpr char kClass[] = "class";
constexpr const char* const kLogLevelName[] = {"info", "warning", "error"};
size_t SeverityToLogLevelNameIndex(logging::LogSeverity severity) {
if (severity <= logging::LOG_INFO)
return 0;
if (severity == logging::LOG_WARNING)
return 1;
return 2;
}
size_t LogMarkToLogLevelNameIndex(char mark) {
switch (mark) {
case 'I':
case 'V':
return 0;
case 'W':
return 1;
default:
return 2;
}
}
// Gets metadata of all files and directories in |root_path|
// recursively. Stores the result as a list of dictionaries like:
//
@ -189,27 +218,73 @@ std::string FormatEntry(const base::FilePath& path,
return out;
}
std::string SeverityToString(logging::LogSeverity severity) {
switch (severity) {
case logging::LOG_INFO:
return "info";
case logging::LOG_WARNING:
return "warning";
case logging::LOG_ERROR:
return "error";
default: // Treat all other higher severities as ERROR.
return "error";
}
// Appends {'key': key, 'value': value, 'class': clazz} dictionary to the
// |list|.
void AppendKeyValue(base::ListValue* list,
std::string key,
std::string value,
std::string clazz = std::string()) {
auto dict = std::make_unique<base::DictionaryValue>();
dict->SetPath({kKey}, base::Value(std::move(key)));
dict->SetPath({kValue}, base::Value(std::move(value)));
if (!clazz.empty())
dict->SetPath({kClass}, base::Value(std::move(clazz)));
list->GetList().push_back(std::move(*dict));
}
// Appends {'key': key, 'value': value} dictionary to the |list|.
void AppendKeyValue(base::ListValue* list,
const std::string& key,
const std::string& value) {
auto dict = std::make_unique<base::DictionaryValue>();
dict->SetString("key", key);
dict->SetString("value", value);
list->Append(std::move(dict));
ino_t GetInodeValue(const base::FilePath& path) {
struct stat file_stats;
if (stat(path.value().c_str(), &file_stats) != 0)
return 0;
return file_stats.st_ino;
}
std::pair<ino_t, base::ListValue> GetServiceLogContents(
const base::FilePath& log_path,
ino_t inode,
int from_line_number) {
base::ListValue result;
std::ifstream log(log_path.value());
if (log.good()) {
ino_t new_inode = GetInodeValue(log_path);
if (new_inode != inode) {
// Apparently log was recreated. Re-read the log.
from_line_number = 0;
inode = new_inode;
}
base::Time time;
constexpr char kTimestampPattern[] = R"(????-??-??T??:??:??.???Z? )";
const size_t pattern_length = strlen(kTimestampPattern);
std::string line;
int line_number = 0;
while (log.good()) {
std::getline(log, line);
if (line.empty() || ++line_number <= from_line_number) {
continue;
}
base::StringPiece log_line = line;
size_t severity_index = 0;
if (base::MatchPattern(log_line.substr(0, pattern_length),
kTimestampPattern) &&
google_apis::util::GetTimeFromString(
log_line.substr(0, pattern_length - 2), &time)) {
severity_index = LogMarkToLogLevelNameIndex(line[pattern_length - 2]);
line = line.substr(pattern_length);
}
const char* const severity = kLogLevelName[severity_index];
AppendKeyValue(&result,
google_apis::util::FormatTimeAsStringLocaltime(time),
base::StrCat({"[", severity, "] ", line}),
base::StrCat({"log-", severity}));
}
}
return {inode, std::move(result)};
}
// Class to handle messages from chrome://drive-internals.
@ -255,6 +330,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler {
void UpdateCacheContentsSection(
drive::DebugInfoCollector* debug_info_collector);
void UpdateEventLogSection();
void UpdateServiceLogSection();
void UpdatePathConfigurationsSection();
// Called when GetGCacheContents() is complete.
@ -306,9 +382,21 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler {
// Called after file system reset for ResetDriveFileSystem is done.
void ResetFinished(bool success);
// Called when service logs are read.
void OnServiceLogRead(std::pair<ino_t, base::ListValue>);
// The last event sent to the JavaScript side.
int last_sent_event_id_;
// The last line of service log sent to the JS side.
int last_sent_line_number_;
// The inode of the log file.
ino_t service_log_file_inode_;
// Service log file is being parsed.
bool service_log_file_is_processing_ = false;
base::WeakPtrFactory<DriveInternalsWebUIHandler> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(DriveInternalsWebUIHandler);
};
@ -461,6 +549,9 @@ void DriveInternalsWebUIHandler::OnPageLoaded(const base::ListValue* args) {
// and resent whole the logs to the page.
last_sent_event_id_ = -1;
UpdateEventLogSection();
last_sent_line_number_ = 0;
service_log_file_inode_ = 0;
UpdateServiceLogSection();
}
void DriveInternalsWebUIHandler::UpdateDriveRelatedPreferencesSection() {
@ -763,20 +854,54 @@ void DriveInternalsWebUIHandler::UpdateEventLogSection() {
if (log[i].id <= last_sent_event_id_)
continue;
std::string severity = SeverityToString(log[i].severity);
auto dict = std::make_unique<base::DictionaryValue>();
dict->SetString("key",
google_apis::util::FormatTimeAsStringLocaltime(log[i].when));
dict->SetString("value", "[" + severity + "] " + log[i].what);
dict->SetString("class", "log-" + severity);
list.Append(std::move(dict));
const char* const severity =
kLogLevelName[SeverityToLogLevelNameIndex(log[i].severity)];
AppendKeyValue(&list,
google_apis::util::FormatTimeAsStringLocaltime(log[i].when),
base::StrCat({"[", severity, "] ", log[i].what}),
base::StrCat({"log-", severity}));
last_sent_event_id_ = log[i].id;
}
if (!list.empty())
web_ui()->CallJavascriptFunctionUnsafe("updateEventLog", list);
}
void DriveInternalsWebUIHandler::UpdateServiceLogSection() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (service_log_file_is_processing_)
return;
service_log_file_is_processing_ = true;
drive::DriveIntegrationService* integration_service = GetIntegrationService();
if (!integration_service)
return;
base::FilePath log_path = integration_service->GetDriveFsLogPath();
if (log_path.empty())
return;
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindOnce(&GetServiceLogContents, log_path, service_log_file_inode_,
last_sent_line_number_),
base::BindOnce(&DriveInternalsWebUIHandler::OnServiceLogRead,
weak_ptr_factory_.GetWeakPtr()));
}
void DriveInternalsWebUIHandler::OnServiceLogRead(
std::pair<ino_t, base::ListValue> response) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (service_log_file_inode_ != response.first) {
service_log_file_inode_ = response.first;
last_sent_line_number_ = 0;
}
if (!response.second.empty()) {
web_ui()->CallJavascriptFunctionUnsafe("updateServiceLog", response.second);
last_sent_line_number_ += response.second.GetList().size();
}
service_log_file_is_processing_ = false;
}
void DriveInternalsWebUIHandler::UpdatePathConfigurationsSection() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@ -901,6 +1026,7 @@ void DriveInternalsWebUIHandler::OnPeriodicUpdate(const base::ListValue* args) {
return;
UpdateEventLogSection();
UpdateServiceLogSection();
drive::JobListInterface* job_list = integration_service->job_list();
if (job_list) {

@ -79,10 +79,8 @@ class DriveFsHost::MountState : public mojom::DriveFsDelegate,
pending_token_(base::UnguessableToken::Create()),
binding_(this) {
source_path_ = base::StrCat({kMountScheme, pending_token_.ToString()});
std::string datadir_option = base::StrCat(
{"datadir=", host_->profile_path_.Append(kDataPath)
.Append(host_->delegate_->GetObfuscatedAccountId())
.value()});
std::string datadir_option =
base::StrCat({"datadir=", host_->GetDataPath().value()});
auto bootstrap =
mojo::MakeProxy(mojo_connection_delegate_->InitializeMojoConnection());
mojom::DriveFsDelegatePtr delegate;
@ -365,6 +363,11 @@ const base::FilePath& DriveFsHost::GetMountPath() const {
return mount_state_->mount_path();
}
base::FilePath DriveFsHost::GetDataPath() const {
return profile_path_.Append(kDataPath).Append(
delegate_->GetObfuscatedAccountId());
}
mojom::DriveFs* DriveFsHost::GetDriveFsInterface() const {
if (!mount_state_ || !mount_state_->mounted()) {
return nullptr;

@ -100,6 +100,9 @@ class COMPONENT_EXPORT(DRIVEFS) DriveFsHost
// |IsMounted()| returns true.
const base::FilePath& GetMountPath() const;
// Returns the path where DriveFS keeps its data and caches.
base::FilePath GetDataPath() const;
mojom::DriveFs* GetDriveFsInterface() const;
private:

@ -372,6 +372,9 @@ class DriveFsHostTest : public ::testing::Test, public mojom::DriveFsBootstrap {
TEST_F(DriveFsHostTest, Basic) {
EXPECT_FALSE(host_->IsMounted());
EXPECT_EQ(base::FilePath("/path/to/profile/GCache/v2/salt-g-ID"),
host_->GetDataPath());
ASSERT_NO_FATAL_FAILURE(DoMount());
EXPECT_EQ(base::FilePath("/media/drivefsroot/salt-g-ID"),