metrics: Convert Metrics to DBusDaemon

In order to remove glib, convert Metrics from the glib message loop to
DBusDaemon, which is based on base::MessageLoop.  Also use the DBusDaemon's
dbus::Bus object directly to set up the CrashReporter signal handling, as
the ObjectProxy used by chromeos-dbus-bindings requires all signals be sent
from registered name owners.

BUG=chromium:431838
TEST=Unittests and hwtests pass.
TEST=`test_that -b panther <IP> platform_MetricsUploader` passes
CQ-DEPEND=CL:236652

Change-Id: I6bc1f66999a43065b0d48325b031cd36bb782b76
Reviewed-on: https://chromium-review.googlesource.com/234359
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Steve Fung <stevefung@chromium.org>
Tested-by: Steve Fung <stevefung@chromium.org>
This commit is contained in:
Steve Fung 2014-12-01 13:38:21 -08:00 committed by chrome-internal-fetch
parent 73b40b468b
commit e86591e585
4 changed files with 120 additions and 116 deletions

View file

@ -12,6 +12,7 @@
#include <inttypes.h>
#include <math.h>
#include <string.h>
#include <sysexits.h>
#include <time.h>
#include <base/files/file_path.h>
@ -24,7 +25,8 @@
#include <base/strings/stringprintf.h>
#include <base/sys_info.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <dbus/dbus.h>
#include <dbus/message.h>
#include "uploader/upload_service.h"
using base::FilePath;
@ -43,6 +45,8 @@ namespace {
const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
const char kCrashReporterUserCrashSignal[] = "UserCrash";
const char kCrashReporterMatchRule[] =
"type='signal',interface='%s',path='/',member='%s'";
const int kSecondsPerMinute = 60;
const int kMinutesPerHour = 60;
@ -53,7 +57,7 @@ const int kDaysPerWeek = 7;
const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
// Interval between calls to UpdateStats().
const guint kUpdateStatsIntervalMs = 300000;
const uint32_t kUpdateStatsIntervalMs = 300000;
const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected";
const char kUncleanShutdownDetectedFile[] =
@ -135,8 +139,7 @@ static const int kMemuseIntervals[] = {
};
MetricsDaemon::MetricsDaemon()
: update_stats_timeout_id_(-1),
memuse_final_time_(0),
: memuse_final_time_(0),
memuse_interval_index_(0),
read_sectors_(0),
write_sectors_(0),
@ -147,8 +150,6 @@ MetricsDaemon::MetricsDaemon()
latest_cpu_use_ticks_(0) {}
MetricsDaemon::~MetricsDaemon() {
if (update_stats_timeout_id_ > -1)
g_source_remove(update_stats_timeout_id_);
}
double MetricsDaemon::GetActiveTime() {
@ -162,10 +163,7 @@ double MetricsDaemon::GetActiveTime() {
}
}
void MetricsDaemon::Run(bool run_as_daemon) {
if (run_as_daemon && daemon(0, 0) != 0)
return;
int MetricsDaemon::Run() {
if (CheckSystemCrash(kKernelCrashDetectedFile)) {
ProcessKernelCrash();
}
@ -183,7 +181,7 @@ void MetricsDaemon::Run(bool run_as_daemon) {
version_cumulative_cpu_use_->Set(0);
}
Loop();
return chromeos::DBusDaemon::Run();
}
void MetricsDaemon::RunUploaderTest() {
@ -223,6 +221,7 @@ void MetricsDaemon::Init(bool testing,
const string& metrics_file,
const string& config_root) {
testing_ = testing;
uploader_active_ = uploader_active;
config_root_ = config_root;
DCHECK(metrics_lib != nullptr);
metrics_lib_ = metrics_lib;
@ -276,6 +275,17 @@ void MetricsDaemon::Init(bool testing,
vmstats_path_ = vmstats_path;
scaling_max_freq_path_ = scaling_max_freq_path;
cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
// If testing, initialize Stats Reporter without connecting DBus
if (testing_)
StatsReporterInit();
}
int MetricsDaemon::OnInit() {
int return_code = chromeos::DBusDaemon::OnInit();
if (return_code != EX_OK)
return return_code;
StatsReporterInit();
// Start collecting meminfo stats.
@ -283,56 +293,67 @@ void MetricsDaemon::Init(bool testing,
memuse_final_time_ = GetActiveTime() + kMemuseIntervals[0];
ScheduleMemuseCallback(kMemuseIntervals[0]);
// Don't setup D-Bus and GLib in test mode.
if (testing)
return;
if (testing_)
return EX_OK;
g_type_init();
dbus_threads_init_default();
bus_->AssertOnDBusThread();
CHECK(bus_->SetUpAsyncOperations());
DBusError error;
dbus_error_init(&error);
if (bus_->is_connected()) {
const std::string match_rule =
base::StringPrintf(kCrashReporterMatchRule,
kCrashReporterInterface,
kCrashReporterUserCrashSignal);
DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
LOG_IF(FATAL, dbus_error_is_set(&error)) <<
"No D-Bus connection: " << SAFE_MESSAGE(error);
bus_->AddFilterFunction(&MetricsDaemon::MessageFilter, this);
dbus_connection_setup_with_g_main(connection, nullptr);
DBusError error;
dbus_error_init(&error);
bus_->AddMatch(match_rule, &error);
vector<string> matches;
matches.push_back(
base::StringPrintf("type='signal',interface='%s',path='/',member='%s'",
kCrashReporterInterface,
kCrashReporterUserCrashSignal));
// Registers D-Bus matches for the signals we would like to catch.
for (vector<string>::const_iterator it = matches.begin();
it != matches.end(); ++it) {
const char* match = it->c_str();
DLOG(INFO) << "adding dbus match: " << match;
dbus_bus_add_match(connection, match, &error);
LOG_IF(FATAL, dbus_error_is_set(&error)) <<
"unable to add a match: " << SAFE_MESSAGE(error);
if (dbus_error_is_set(&error)) {
LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
<< error.name << ": " << error.message;
return EX_SOFTWARE;
}
} else {
LOG(ERROR) << "DBus isn't connected.";
return EX_UNAVAILABLE;
}
// Adds the D-Bus filter routine to be called back whenever one of
// the registered D-Bus matches is successful. The daemon is not
// activated for D-Bus messages that don't match.
CHECK(dbus_connection_add_filter(connection, MessageFilter, this, nullptr));
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
base::Unretained(this)),
base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
update_stats_timeout_id_ =
g_timeout_add(kUpdateStatsIntervalMs, &HandleUpdateStatsTimeout, this);
if (uploader_active) {
if (uploader_active_) {
LOG(INFO) << "uploader enabled";
upload_service_.reset(new UploadService(new SystemProfileCache(), server_));
upload_service_->Init(upload_interval_, metrics_file_);
}
return EX_OK;
}
void MetricsDaemon::Loop() {
GMainLoop* loop = g_main_loop_new(nullptr, false);
g_main_loop_run(loop);
void MetricsDaemon::OnShutdown(int* return_code) {
if (!testing_ && bus_->is_connected()) {
const std::string match_rule =
base::StringPrintf(kCrashReporterMatchRule,
kCrashReporterInterface,
kCrashReporterUserCrashSignal);
bus_->RemoveFilterFunction(&MetricsDaemon::MessageFilter, this);
DBusError error;
dbus_error_init(&error);
bus_->RemoveMatch(match_rule, &error);
if (dbus_error_is_set(&error)) {
LOG(ERROR) << "Failed to remove match rule \"" << match_rule << "\". Got "
<< error.name << ": " << error.message;
}
}
chromeos::DBusDaemon::OnShutdown(return_code);
}
// static
@ -481,7 +502,9 @@ void MetricsDaemon::ScheduleStatsCallback(int wait) {
if (testing_) {
return;
}
g_timeout_add_seconds(wait, StatsCallbackStatic, this);
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&MetricsDaemon::StatsCallback, base::Unretained(this)),
base::TimeDelta::FromSeconds(wait));
}
bool MetricsDaemon::DiskStatsReadStats(uint64_t* read_sectors,
@ -637,12 +660,6 @@ void MetricsDaemon::SendCpuThrottleMetrics() {
SendLinearSample(kMetricScaledCpuFrequencyName, percent, 101, 102);
}
// static
gboolean MetricsDaemon::StatsCallbackStatic(void* handle) {
(static_cast<MetricsDaemon*>(handle))->StatsCallback();
return false; // one-time callback
}
// Collects disk and vm stats alternating over a short and a long interval.
void MetricsDaemon::StatsCallback() {
@ -756,25 +773,31 @@ void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
if (testing_) {
return;
}
g_timeout_add_seconds(wait, MeminfoCallbackStatic, this);
base::TimeDelta waitDelta = base::TimeDelta::FromSeconds(wait);
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
base::TimeDelta::FromMilliseconds(kMetricMeminfoInterval)),
waitDelta);
}
// static
gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) {
return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback();
}
bool MetricsDaemon::MeminfoCallback() {
void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) {
string meminfo_raw;
const FilePath meminfo_path("/proc/meminfo");
if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
return false;
return;
}
// Make both calls even if the first one fails.
bool success = ProcessMeminfo(meminfo_raw);
return ReportZram(base::FilePath(FILE_PATH_LITERAL("/sys/block/zram0"))) &&
bool reschedule =
ReportZram(base::FilePath(FILE_PATH_LITERAL("/sys/block/zram0"))) &&
success;
if (reschedule) {
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
base::TimeDelta::FromMilliseconds(kMetricMeminfoInterval)),
wait);
}
}
// static
@ -941,14 +964,9 @@ void MetricsDaemon::ScheduleMemuseCallback(double interval) {
if (testing_) {
return;
}
g_timeout_add_seconds(interval, MemuseCallbackStatic, this);
}
// static
gboolean MetricsDaemon::MemuseCallbackStatic(void* handle) {
MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
daemon->MemuseCallback();
return false;
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&MetricsDaemon::MemuseCallback, base::Unretained(this)),
base::TimeDelta::FromSeconds(interval));
}
void MetricsDaemon::MemuseCallback() {
@ -1139,8 +1157,10 @@ void MetricsDaemon::UpdateStats(TimeTicks now_ticks,
}
}
// static
gboolean MetricsDaemon::HandleUpdateStatsTimeout(gpointer data) {
static_cast<MetricsDaemon*>(data)->UpdateStats(TimeTicks::Now(), Time::Now());
return TRUE;
void MetricsDaemon::HandleUpdateStatsTimeout() {
UpdateStats(TimeTicks::Now(), Time::Now());
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
base::Unretained(this)),
base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
}

View file

@ -7,8 +7,6 @@
#include <stdint.h>
#include <dbus/dbus.h>
#include <glib.h>
#include <map>
#include <string>
#include <vector>
@ -16,6 +14,7 @@
#include <base/files/file_path.h>
#include <base/memory/scoped_ptr.h>
#include <base/time/time.h>
#include <chromeos/daemons/dbus_daemon.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include "metrics/metrics_library.h"
@ -24,12 +23,12 @@
using chromeos_metrics::PersistentInteger;
class MetricsDaemon {
class MetricsDaemon : public chromeos::DBusDaemon {
public:
MetricsDaemon();
~MetricsDaemon();
// Initializes.
// Initializes metrics class variables.
void Init(bool testing,
bool uploader_active,
MetricsLibraryInterface* metrics_lib,
@ -42,9 +41,14 @@ class MetricsDaemon {
const std::string& metrics_file,
const std::string& config_root);
// Does all the work. If |run_as_daemon| is true, daemonizes by
// forking.
void Run(bool run_as_daemon);
// Initializes DBus and MessageLoop variables before running the MessageLoop.
int OnInit() override;
// Clean up data set up in OnInit before shutting down message loop.
void OnShutdown(int* return_code) override;
// Does all the work.
int Run() override;
// Triggers an upload event and exit. (Used to test UploadService)
void RunUploaderTest();
@ -147,9 +151,6 @@ class MetricsDaemon {
// Returns the active time since boot (uptime minus sleep time) in seconds.
double GetActiveTime();
// Creates the event loop and enters it.
void Loop();
// D-Bus filter callback.
static DBusHandlerResult MessageFilter(DBusConnection* connection,
DBusMessage* message,
@ -227,22 +228,14 @@ class MetricsDaemon {
// Parse cumulative vm statistics from a C string. Returns true for success.
bool VmStatsParseStats(const char* stats, struct VmstatRecord* record);
// Reports disk and vm statistics (static version for glib). Arguments are a
// glib artifact.
static gboolean StatsCallbackStatic(void* handle);
// Reports disk and vm statistics.
void StatsCallback();
// Schedules meminfo collection callback.
void ScheduleMeminfoCallback(int wait);
// Reports memory statistics (static version for glib). Argument is a glib
// artifact.
static gboolean MeminfoCallbackStatic(void* handle);
// Reports memory statistics. Returns false on failure.
bool MeminfoCallback();
// Reports memory statistics. Reschedules callback on success.
void MeminfoCallback(base::TimeDelta wait);
// Parses content of /proc/meminfo and sends fields of interest to UMA.
// Returns false on errors. |meminfo_raw| contains the content of
@ -259,9 +252,6 @@ class MetricsDaemon {
// Schedule a memory use callback in |interval| seconds.
void ScheduleMemuseCallback(double interval);
// Static wrapper for MemuseCallback. Always returns false.
static gboolean MemuseCallbackStatic(void* handle);
// Calls MemuseCallbackWork, and possibly schedules next callback, if enough
// active time has passed. Otherwise reschedules itself to simulate active
// time callbacks (i.e. wall clock time minus sleep time).
@ -288,7 +278,7 @@ class MetricsDaemon {
void UpdateStats(base::TimeTicks now_ticks, base::Time now_wall_time);
// Invoked periodically by |update_stats_timeout_id_| to call UpdateStats().
static gboolean HandleUpdateStatsTimeout(gpointer data);
void HandleUpdateStatsTimeout();
// Reports zram statistics.
bool ReportZram(const base::FilePath& zram_dir);
@ -301,6 +291,9 @@ class MetricsDaemon {
// Test mode.
bool testing_;
// Whether the uploader is enabled or disabled.
bool uploader_active_;
// Root of the configuration files to use.
std::string config_root_;
@ -315,16 +308,6 @@ class MetricsDaemon {
// The last time that UpdateStats() was called.
base::TimeTicks last_update_stats_time_;
// ID of a GLib timeout that repeatedly runs UpdateStats().
gint update_stats_timeout_id_;
// Sleep period until the next daily usage aggregation performed by
// the daily use monitor (see ScheduleUseMonitor).
int usemon_interval_;
// Scheduled daily use monitor source (see ScheduleUseMonitor).
GSource* usemon_source_;
// End time of current memuse stat collection interval.
double memuse_final_time_;

View file

@ -73,6 +73,11 @@ int main(int argc, char** argv) {
// Also log to stderr when not running as daemon.
chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogHeader |
(FLAGS_daemon ? 0 : chromeos::kLogToStderr));
if (FLAGS_daemon && daemon(0, 0) != 0) {
return errno;
}
MetricsLibrary metrics_lib;
metrics_lib.Init();
MetricsDaemon daemon;
@ -88,12 +93,10 @@ int main(int argc, char** argv) {
FLAGS_metrics_file,
FLAGS_config_root);
base::AtExitManager at_exit_manager;
if (FLAGS_uploader_test) {
daemon.RunUploaderTest();
return 0;
}
daemon.Run(FLAGS_daemon);
daemon.Run();
}

View file

@ -29,6 +29,7 @@ using base::TimeTicks;
using std::string;
using std::vector;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::AtLeast;
using ::testing::Return;
using ::testing::StrictMock;
@ -227,7 +228,7 @@ TEST_F(MetricsDaemonTest, ReportDailyUse) {
TEST_F(MetricsDaemonTest, MessageFilter) {
// Ignore calls to SendToUMA.
EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0));
EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AnyNumber());
DBusMessage* msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
DBusHandlerResult res =
@ -265,7 +266,6 @@ TEST_F(MetricsDaemonTest, SendSample) {
TEST_F(MetricsDaemonTest, ReportDiskStats) {
uint64_t read_sectors_now, write_sectors_now;
CreateFakeDiskStatsFile(kFakeDiskStats1.c_str());
daemon_.DiskStatsReadStats(&read_sectors_now, &write_sectors_now);
EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]);
@ -399,8 +399,6 @@ TEST_F(MetricsDaemonTest, SendZramMetrics) {
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
// Some libchrome calls need this.
base::AtExitManager at_exit_manager;
return RUN_ALL_TESTS();
}