blob: dc588236bad3370e90a537edda1cd52720c18f28 [file]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/android/history_report/usage_reports_buffer_backend.h"
#include <inttypes.h>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/process_memory_dump.h"
#include "chrome/browser/android/history_report/usage_report_util.h"
#include "chrome/browser/android/proto/delta_file.pb.h"
#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/leveldatabase/leveldb_chrome.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/iterator.h"
#include "third_party/leveldatabase/src/include/leveldb/options.h"
#include "third_party/leveldatabase/src/include/leveldb/slice.h"
#include "third_party/leveldatabase/src/include/leveldb/status.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
namespace {
const base::FilePath::CharType kBufferFileName[] =
FILE_PATH_LITERAL("UsageReportsBuffer");
} // namespace
namespace history_report {
UsageReportsBufferBackend::UsageReportsBufferBackend(const base::FilePath& dir)
: db_file_name_(dir.Append(kBufferFileName)) {}
UsageReportsBufferBackend::~UsageReportsBufferBackend() {}
bool UsageReportsBufferBackend::Init() {
leveldb_env::Options options;
options.create_if_missing = true;
options.max_open_files = 0; // Use minimum number of files.
options.write_buffer_size = 500 * 1024;
std::string path = db_file_name_.value();
leveldb::Status status = leveldb_env::OpenDB(options, path, &db_);
if (status.IsCorruption()) {
LOG(ERROR) << "Deleting corrupt database";
status = leveldb_chrome::DeleteDB(db_file_name_, options);
if (!status.ok()) {
LOG(ERROR) << "Unable to delete " << db_file_name_
<< ", error: " << status.ToString();
return false;
}
status = leveldb_env::OpenDB(options, path, &db_);
}
if (!status.ok()) {
LOG(WARNING) << "Unable to open " << path << ": " << status.ToString();
return false;
}
CHECK(db_);
UMA_HISTOGRAM_COUNTS_1M(
"Search.HistoryReport.UsageReportsBuffer.LevelDBEntries",
usage_report_util::DatabaseEntries(db_.get()));
return true;
}
void UsageReportsBufferBackend::AddVisit(const std::string& id,
int64_t timestamp_ms,
bool typed_visit) {
if (!db_.get()) {
LOG(WARNING) << "AddVisit db not initilized.";
return;
}
history_report::UsageReport report;
report.set_id(id);
report.set_timestamp_ms(timestamp_ms);
report.set_typed_visit(typed_visit);
leveldb::WriteOptions writeOptions;
leveldb::Status status = db_->Put(
writeOptions,
leveldb::Slice(usage_report_util::ReportToKey(report)),
leveldb::Slice(report.SerializeAsString()));
if (!status.ok())
LOG(WARNING) << "AddVisit failed " << status.ToString();
}
std::unique_ptr<std::vector<UsageReport>>
UsageReportsBufferBackend::GetUsageReportsBatch(int batch_size) {
std::unique_ptr<std::vector<UsageReport>> reports(
new std::vector<UsageReport>());
if (!db_.get()) {
return reports;
}
reports->reserve(batch_size);
leveldb::ReadOptions options;
std::unique_ptr<leveldb::Iterator> db_iter(db_->NewIterator(options));
db_iter->SeekToFirst();
while (batch_size > 0 && db_iter->Valid()) {
history_report::UsageReport last_report;
leveldb::Slice value_slice = db_iter->value();
if (last_report.ParseFromArray(value_slice.data(), value_slice.size())) {
reports->emplace_back(std::move(last_report));
--batch_size;
}
db_iter->Next();
}
return reports;
}
void UsageReportsBufferBackend::Remove(
const std::vector<std::string>& reports) {
if (!db_.get()) {
return;
}
// TODO(haaawk): investigate if it's worth sorting the keys here to improve
// performance.
leveldb::WriteBatch updates;
for (const auto& report : reports) {
updates.Delete(leveldb::Slice(report));
}
leveldb::WriteOptions write_options;
leveldb::Status status = db_->Write(write_options, &updates);
if (!status.ok()) {
LOG(WARNING) << "Remove failed: " << status.ToString();
}
}
void UsageReportsBufferBackend::Clear() {
db_.reset();
leveldb_chrome::DeleteDB(db_file_name_, leveldb_env::Options());
Init();
}
std::string UsageReportsBufferBackend::Dump() {
std::string dump("\n UsageReportsBuffer [");
if (!db_.get()) {
dump.append("not initialized]");
return dump;
}
dump.append("num pending entries=");
dump.append(
base::NumberToString(usage_report_util::DatabaseEntries(db_.get())));
dump.append("]");
return dump;
}
bool UsageReportsBufferBackend::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) {
if (!db_)
return true;
// leveldb_env::DBTracker already records memory usage. Add ownership edge
// to the dump.
auto* tracker_dump =
leveldb_env::DBTracker::GetOrCreateAllocatorDump(pmd, db_.get());
if (!tracker_dump)
return true;
auto* dump = pmd->CreateAllocatorDump(
base::StringPrintf("history/usage_reports_buffer/leveldb_0x%" PRIXPTR,
reinterpret_cast<uintptr_t>(db_.get())));
dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
base::trace_event::MemoryAllocatorDump::kUnitsBytes,
tracker_dump->GetSizeInternal());
pmd->AddOwnershipEdge(dump->guid(), tracker_dump->guid());
return true;
}
} // namespace history_report