Merge "metricsd: Log over binder."

This commit is contained in:
Bertrand Simonnet 2015-12-04 17:05:36 +00:00 committed by Gerrit Code Review
commit c5e921fe52
22 changed files with 487 additions and 1155 deletions

View File

@ -18,8 +18,6 @@ metrics_cpp_extension := .cc
libmetrics_sources := \
c_metrics_library.cc \
metrics_library.cc \
serialization/metric_sample.cc \
serialization/serialization_utils.cc \
timer.cc
metrics_client_sources := \
@ -30,18 +28,18 @@ metrics_collector_common := \
collectors/cpu_usage_collector.cc \
collectors/disk_usage_collector.cc \
metrics_collector.cc \
persistent_integer.cc \
persistent_integer.cc
metricsd_common := \
persistent_integer.cc \
serialization/metric_sample.cc \
serialization/serialization_utils.cc \
uploader/bn_metricsd_impl.cc \
uploader/crash_counters.cc \
uploader/metrics_hashes.cc \
uploader/metrics_log_base.cc \
uploader/metrics_log.cc \
uploader/sender_http.cc \
uploader/system_profile_cache.cc \
uploader/upload_service.cc \
uploader/upload_service.cc
metrics_collector_tests_sources := \
collectors/averaged_statistics_collector_test.cc \
@ -49,14 +47,13 @@ metrics_collector_tests_sources := \
metrics_collector_test.cc \
metrics_library_test.cc \
persistent_integer_test.cc \
serialization/serialization_utils_unittest.cc \
timer_test.cc \
timer_test.cc
metricsd_tests_sources := \
uploader/metrics_hashes_unittest.cc \
uploader/metrics_log_base_unittest.cc \
uploader/mock/sender_mock.cc \
uploader/upload_service_test.cc \
uploader/upload_service_test.cc
metrics_CFLAGS := -Wall \
-Wno-char-subscripts \
@ -70,7 +67,7 @@ metrics_CPPFLAGS := -Wno-non-virtual-dtor \
-fvisibility=default
metrics_includes := external/gtest/include \
$(LOCAL_PATH)/include
libmetrics_shared_libraries := libchrome libbrillo
libmetrics_shared_libraries := libchrome libbinder libbrillo libutils
metrics_collector_shared_libraries := $(libmetrics_shared_libraries) \
libbrillo-dbus \
libbrillo-http \
@ -78,14 +75,24 @@ metrics_collector_shared_libraries := $(libmetrics_shared_libraries) \
libdbus \
libmetrics \
librootdev \
libweaved \
libweaved
metricsd_shared_libraries := \
libbinder \
libbrillo \
libbrillo-http \
libchrome \
libprotobuf-cpp-lite \
libupdate_engine_client \
libutils
# Static proxy library for the binder interface.
# ========================================================
include $(CLEAR_VARS)
LOCAL_MODULE := metricsd_binder_proxy
LOCAL_SHARED_LIBRARIES := libbinder libutils
LOCAL_SRC_FILES := aidl/android/brillo/metrics/IMetricsd.aidl
include $(BUILD_STATIC_LIBRARY)
# Shared library for metrics.
# ========================================================
@ -100,6 +107,7 @@ LOCAL_RTTI_FLAG := -frtti
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES := $(libmetrics_shared_libraries)
LOCAL_SRC_FILES := $(libmetrics_sources)
LOCAL_STATIC_LIBRARIES := metricsd_binder_proxy
include $(BUILD_SHARED_LIBRARY)
# CLI client for metrics.
@ -114,6 +122,7 @@ LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
LOCAL_SHARED_LIBRARIES := $(libmetrics_shared_libraries) \
libmetrics
LOCAL_SRC_FILES := $(metrics_client_sources)
LOCAL_STATIC_LIBRARIES := metricsd_binder_proxy
include $(BUILD_EXECUTABLE)
# Protobuf library for metricsd.
@ -144,6 +153,7 @@ LOCAL_RTTI_FLAG := -frtti
LOCAL_SHARED_LIBRARIES := $(metrics_collector_shared_libraries)
LOCAL_SRC_FILES := $(metrics_collector_common) \
metrics_collector_main.cc
LOCAL_STATIC_LIBRARIES := metricsd_binder_proxy
include $(BUILD_EXECUTABLE)
# metricsd daemon.
@ -158,9 +168,8 @@ LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
LOCAL_INIT_RC := metricsd.rc
LOCAL_REQUIRED_MODULES := \
metrics_collector
LOCAL_RTTI_FLAG := -frtti
LOCAL_SHARED_LIBRARIES := $(metricsd_shared_libraries)
LOCAL_STATIC_LIBRARIES := metricsd_protos
LOCAL_STATIC_LIBRARIES := metricsd_protos metricsd_binder_proxy
LOCAL_SRC_FILES := $(metricsd_common) \
metricsd_main.cc
include $(BUILD_EXECUTABLE)
@ -173,10 +182,9 @@ LOCAL_CFLAGS := $(metrics_CFLAGS)
LOCAL_CLANG := true
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
LOCAL_RTTI_FLAG := -frtti
LOCAL_SHARED_LIBRARIES := $(metricsd_shared_libraries) libmetrics
LOCAL_SHARED_LIBRARIES := $(metricsd_shared_libraries)
LOCAL_SRC_FILES := $(metricsd_tests_sources) $(metricsd_common)
LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_protos
LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_protos metricsd_binder_proxy
include $(BUILD_NATIVE_TEST)
# Unit tests for metrics_collector.
@ -191,7 +199,7 @@ LOCAL_RTTI_FLAG := -frtti
LOCAL_SHARED_LIBRARIES := $(metrics_collector_shared_libraries)
LOCAL_SRC_FILES := $(metrics_collector_tests_sources) \
$(metrics_collector_common)
LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock
LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_binder_proxy
include $(BUILD_NATIVE_TEST)
# Weave schema files

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.brillo.metrics;
interface IMetricsd {
oneway void recordHistogram(String name, int sample, int min, int max,
int nbuckets);
oneway void recordLinearHistogram(String name, int sample, int max);
oneway void recordSparseHistogram(String name, int sample);
oneway void recordCrash(String type);
}

View File

@ -22,7 +22,6 @@ static const char kSharedMetricsDirectory[] = "/data/misc/metrics/";
static const char kMetricsdDirectory[] = "/data/misc/metricsd/";
static const char kMetricsCollectorDirectory[] =
"/data/misc/metrics_collector/";
static const char kMetricsEventsFileName[] = "uma-events";
static const char kMetricsGUIDFileName[] = "Sysinfo.GUID";
static const char kMetricsServer[] = "https://clients4.google.com/uma/v2";
static const char kConsentFileName[] = "enabled";

View File

@ -24,9 +24,17 @@
#include <base/compiler_specific.h>
#include <base/files/file_path.h>
#include <base/macros.h>
#include <base/memory/scoped_ptr.h>
#include <binder/IServiceManager.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
namespace android {
namespace brillo {
namespace metrics {
class IMetricsd;
} // namespace metrics
} // namespace brillo
} // namespace android
class MetricsLibraryInterface {
public:
virtual void Init() = 0;
@ -152,6 +160,10 @@ class MetricsLibrary : public MetricsLibraryInterface {
char* buffer, int buffer_size,
bool* result);
// Connects to IMetricsd if the proxy does not exist or is not alive.
// Don't block if we fail to get the proxy for any reason.
bool CheckService();
// Time at which we last checked if metrics were enabled.
time_t cached_enabled_time_;
@ -161,7 +173,8 @@ class MetricsLibrary : public MetricsLibraryInterface {
// True iff we should cache the enabled/disabled status.
bool use_caching_;
base::FilePath uma_events_file_;
android::sp<android::IServiceManager> manager_;
android::sp<android::brillo::metrics::IMetricsd> metricsd_proxy_;
base::FilePath consent_file_;
DISALLOW_COPY_AND_ASSIGN(MetricsLibrary);

View File

@ -21,11 +21,8 @@
#include "constants.h"
#include "metrics/metrics_library.h"
#include "serialization/metric_sample.h"
#include "serialization/serialization_utils.h"
enum Mode {
kModeDumpLogs,
kModeSendSample,
kModeSendEnumSample,
kModeSendSparseSample,
@ -48,7 +45,6 @@ void ShowUsage() {
" |min| > 0, |min| <= sample < |max|\n"
" -c: return exit status 0 if user consents to stats, 1 otherwise,\n"
" in guest mode always return 1\n"
" -d: dump cached logs to the console\n"
" -e: send linear/enumeration histogram data\n"
" -g: return exit status 0 if machine in guest mode, 1 otherwise\n"
" -s: send a sparse histogram sample\n"
@ -139,59 +135,17 @@ static int IsGuestMode() {
return metrics_lib.IsGuestMode() ? 0 : 1;
}
static int DumpLogs() {
base::FilePath events_file = base::FilePath(metrics::kSharedMetricsDirectory)
.Append(metrics::kMetricsEventsFileName);
printf("Metrics from %s\n\n", events_file.value().data());
ScopedVector<metrics::MetricSample> metrics;
metrics::SerializationUtils::ReadMetricsFromFile(events_file.value(),
&metrics);
for (ScopedVector<metrics::MetricSample>::const_iterator i = metrics.begin();
i != metrics.end(); ++i) {
const metrics::MetricSample* sample = *i;
printf("name: %s\t", sample->name().c_str());
printf("type: ");
switch (sample->type()) {
case metrics::MetricSample::CRASH:
printf("CRASH");
break;
case metrics::MetricSample::HISTOGRAM:
printf("HISTOGRAM");
break;
case metrics::MetricSample::LINEAR_HISTOGRAM:
printf("LINEAR_HISTOGRAM");
break;
case metrics::MetricSample::SPARSE_HISTOGRAM:
printf("SPARSE_HISTOGRAM");
break;
case metrics::MetricSample::USER_ACTION:
printf("USER_ACTION");
break;
}
printf("\n");
}
return 0;
}
int main(int argc, char** argv) {
enum Mode mode = kModeSendSample;
bool secs_to_msecs = false;
// Parse arguments
int flag;
while ((flag = getopt(argc, argv, "abcdegstuv")) != -1) {
while ((flag = getopt(argc, argv, "abcegstuv")) != -1) {
switch (flag) {
case 'c':
mode = kModeHasConsent;
break;
case 'd':
mode = kModeDumpLogs;
break;
case 'e':
mode = kModeSendEnumSample;
break;
@ -252,8 +206,6 @@ int main(int argc, char** argv) {
return HasConsent();
case kModeIsGuestMode:
return IsGuestMode();
case kModeDumpLogs:
return DumpLogs();
default:
ShowUsage();
return 0;

View File

@ -18,19 +18,21 @@
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <binder/IServiceManager.h>
#include <errno.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <utils/String16.h>
#include <cstdio>
#include <cstring>
#include "android/brillo/metrics/IMetricsd.h"
#include "constants.h"
#include "serialization/metric_sample.h"
#include "serialization/serialization_utils.h"
static const char kCrosEventHistogramName[] = "Platform.CrOSEvent";
static const int kCrosEventHistogramMax = 100;
static const char kMetricsServiceName[] = "android.brillo.metrics.IMetricsd";
/* Add new cros events here.
*
@ -53,6 +55,10 @@ static const char *kCrosEventNames[] = {
"TPM.EarlyResetDuringCommand", // 12
};
using android::binder::Status;
using android::brillo::metrics::IMetricsd;
using android::String16;
MetricsLibrary::MetricsLibrary() {}
MetricsLibrary::~MetricsLibrary() {}
@ -123,6 +129,17 @@ bool MetricsLibrary::IsGuestMode() {
return result && (access("/var/run/state/logged-in", F_OK) == 0);
}
bool MetricsLibrary::CheckService() {
if (metricsd_proxy_.get() &&
android::IInterface::asBinder(metricsd_proxy_)->isBinderAlive())
return true;
const String16 name(kMetricsServiceName);
metricsd_proxy_ = android::interface_cast<IMetricsd>(
android::defaultServiceManager()->checkService(name));
return metricsd_proxy_.get();
}
bool MetricsLibrary::AreMetricsEnabled() {
static struct stat stat_buffer;
time_t this_check_time = time(nullptr);
@ -135,7 +152,6 @@ bool MetricsLibrary::AreMetricsEnabled() {
void MetricsLibrary::Init() {
base::FilePath dir = base::FilePath(metrics::kSharedMetricsDirectory);
uma_events_file_ = dir.Append(metrics::kMetricsEventsFileName);
consent_file_ = dir.Append(metrics::kConsentFileName);
cached_enabled_ = false;
cached_enabled_time_ = 0;
@ -148,54 +164,52 @@ void MetricsLibrary::InitWithNoCaching() {
}
void MetricsLibrary::InitForTest(const base::FilePath& metrics_directory) {
uma_events_file_ = metrics_directory.Append(metrics::kMetricsEventsFileName);
consent_file_ = metrics_directory.Append(metrics::kConsentFileName);
cached_enabled_ = false;
cached_enabled_time_ = 0;
use_caching_ = true;
}
bool MetricsLibrary::SendToUMA(const std::string& name,
int sample,
int min,
int max,
int nbuckets) {
return metrics::SerializationUtils::WriteMetricToFile(
*metrics::MetricSample::HistogramSample(name, sample, min, max, nbuckets)
.get(),
uma_events_file_.value());
bool MetricsLibrary::SendToUMA(
const std::string& name, int sample, int min, int max, int nbuckets) {
return CheckService() &&
metricsd_proxy_->recordHistogram(String16(name.c_str()), sample, min,
max, nbuckets)
.isOk();
}
bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample,
bool MetricsLibrary::SendEnumToUMA(const std::string& name,
int sample,
int max) {
return metrics::SerializationUtils::WriteMetricToFile(
*metrics::MetricSample::LinearHistogramSample(name, sample, max).get(),
uma_events_file_.value());
return CheckService() &&
metricsd_proxy_->recordLinearHistogram(String16(name.c_str()), sample,
max)
.isOk();
}
bool MetricsLibrary::SendBoolToUMA(const std::string& name, bool sample) {
return metrics::SerializationUtils::WriteMetricToFile(
*metrics::MetricSample::LinearHistogramSample(name,
sample ? 1 : 0, 2).get(),
uma_events_file_.value());
return CheckService() &&
metricsd_proxy_->recordLinearHistogram(String16(name.c_str()),
sample ? 1 : 0, 2)
.isOk();
}
bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) {
return metrics::SerializationUtils::WriteMetricToFile(
*metrics::MetricSample::SparseHistogramSample(name, sample).get(),
uma_events_file_.value());
return CheckService() &&
metricsd_proxy_->recordSparseHistogram(String16(name.c_str()), sample)
.isOk();
}
bool MetricsLibrary::SendUserActionToUMA(const std::string& action) {
return metrics::SerializationUtils::WriteMetricToFile(
*metrics::MetricSample::UserActionSample(action).get(),
uma_events_file_.value());
// Deprecated.
// TODO(bsimonnet): Delete this method entirely once all the callers are
// removed (b/25818567).
return true;
}
bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) {
return metrics::SerializationUtils::WriteMetricToFile(
*metrics::MetricSample::CrashSample(crash_kind).get(),
uma_events_file_.value());
bool MetricsLibrary::SendCrashToUMA(const char* crash_kind) {
return CheckService() &&
metricsd_proxy_->recordCrash(String16(crash_kind)).isOk();
}
bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) {

View File

@ -29,7 +29,6 @@ class MetricsLibraryTest : public testing::Test {
virtual void SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
lib_.InitForTest(temp_dir_.path());
EXPECT_EQ(0, WriteFile(lib_.uma_events_file_, "", 0));
// Defeat metrics enabled caching between tests.
lib_.cached_enabled_time_ = 0;
}

View File

@ -14,34 +14,34 @@
* limitations under the License.
*/
#include <thread>
#include <base/at_exit.h>
#include <base/command_line.h>
#include <base/files/file_path.h>
#include <base/logging.h>
#include <base/metrics/statistics_recorder.h>
#include <base/strings/string_util.h>
#include <base/time/time.h>
#include <brillo/flag_helper.h>
#include <brillo/syslog_logging.h>
#include "constants.h"
#include "uploader/bn_metricsd_impl.h"
#include "uploader/crash_counters.h"
#include "uploader/upload_service.h"
int main(int argc, char** argv) {
DEFINE_bool(foreground, false, "Don't daemonize");
// Upload the metrics once and exit. (used for testing)
DEFINE_bool(uploader_test,
false,
"run the uploader once and exit");
DEFINE_bool(uploader_test, false, "run the uploader once and exit");
// Upload Service flags.
DEFINE_int32(upload_interval_secs,
1800,
DEFINE_int32(upload_interval_secs, 1800,
"Interval at which metrics_daemon sends the metrics. (needs "
"-uploader)");
DEFINE_string(server,
metrics::kMetricsServer,
DEFINE_string(server, metrics::kMetricsServer,
"Server to upload the metrics to. (needs -uploader)");
DEFINE_string(private_directory, metrics::kMetricsdDirectory,
"Path to the private directory used by metricsd "
@ -56,8 +56,8 @@ int main(int argc, char** argv) {
brillo::FlagHelper::Init(argc, argv, "Brillo metrics daemon.");
int logging_location = (FLAGS_foreground ? brillo::kLogToStderr
: brillo::kLogToSyslog);
int logging_location =
(FLAGS_foreground ? brillo::kLogToStderr : brillo::kLogToSyslog);
if (FLAGS_logtosyslog)
logging_location = brillo::kLogToSyslog;
@ -76,10 +76,18 @@ int main(int argc, char** argv) {
return errno;
}
UploadService service(
std::shared_ptr<CrashCounters> counters(new CrashCounters);
UploadService upload_service(
FLAGS_server, base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs),
base::FilePath(FLAGS_private_directory),
base::FilePath(FLAGS_shared_directory));
base::FilePath(FLAGS_shared_directory), counters);
service.Run();
base::StatisticsRecorder::Initialize();
// Create and start the binder thread.
BnMetricsdImpl binder_service(counters);
std::thread binder_thread(&BnMetricsdImpl::Run, &binder_service);
upload_service.Run();
}

View File

@ -1,209 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "serialization/metric_sample.h"
#include <string>
#include <vector>
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
namespace metrics {
MetricSample::MetricSample(MetricSample::SampleType sample_type,
const std::string& metric_name,
int sample,
int min,
int max,
int bucket_count)
: type_(sample_type),
name_(metric_name),
sample_(sample),
min_(min),
max_(max),
bucket_count_(bucket_count) {
}
MetricSample::~MetricSample() {
}
bool MetricSample::IsValid() const {
return name().find(' ') == std::string::npos &&
name().find('\0') == std::string::npos && !name().empty();
}
std::string MetricSample::ToString() const {
if (type_ == CRASH) {
return base::StringPrintf("crash%c%s%c",
'\0',
name().c_str(),
'\0');
} else if (type_ == SPARSE_HISTOGRAM) {
return base::StringPrintf("sparsehistogram%c%s %d%c",
'\0',
name().c_str(),
sample_,
'\0');
} else if (type_ == LINEAR_HISTOGRAM) {
return base::StringPrintf("linearhistogram%c%s %d %d%c",
'\0',
name().c_str(),
sample_,
max_,
'\0');
} else if (type_ == HISTOGRAM) {
return base::StringPrintf("histogram%c%s %d %d %d %d%c",
'\0',
name().c_str(),
sample_,
min_,
max_,
bucket_count_,
'\0');
} else {
// The type can only be USER_ACTION.
CHECK_EQ(type_, USER_ACTION);
return base::StringPrintf("useraction%c%s%c",
'\0',
name().c_str(),
'\0');
}
}
int MetricSample::sample() const {
CHECK_NE(type_, USER_ACTION);
CHECK_NE(type_, CRASH);
return sample_;
}
int MetricSample::min() const {
CHECK_EQ(type_, HISTOGRAM);
return min_;
}
int MetricSample::max() const {
CHECK_NE(type_, CRASH);
CHECK_NE(type_, USER_ACTION);
CHECK_NE(type_, SPARSE_HISTOGRAM);
return max_;
}
int MetricSample::bucket_count() const {
CHECK_EQ(type_, HISTOGRAM);
return bucket_count_;
}
// static
scoped_ptr<MetricSample> MetricSample::CrashSample(
const std::string& crash_name) {
return scoped_ptr<MetricSample>(
new MetricSample(CRASH, crash_name, 0, 0, 0, 0));
}
// static
scoped_ptr<MetricSample> MetricSample::HistogramSample(
const std::string& histogram_name,
int sample,
int min,
int max,
int bucket_count) {
return scoped_ptr<MetricSample>(new MetricSample(
HISTOGRAM, histogram_name, sample, min, max, bucket_count));
}
// static
scoped_ptr<MetricSample> MetricSample::ParseHistogram(
const std::string& serialized_histogram) {
std::vector<std::string> parts;
base::SplitString(serialized_histogram, ' ', &parts);
if (parts.size() != 5)
return scoped_ptr<MetricSample>();
int sample, min, max, bucket_count;
if (parts[0].empty() || !base::StringToInt(parts[1], &sample) ||
!base::StringToInt(parts[2], &min) ||
!base::StringToInt(parts[3], &max) ||
!base::StringToInt(parts[4], &bucket_count)) {
return scoped_ptr<MetricSample>();
}
return HistogramSample(parts[0], sample, min, max, bucket_count);
}
// static
scoped_ptr<MetricSample> MetricSample::SparseHistogramSample(
const std::string& histogram_name,
int sample) {
return scoped_ptr<MetricSample>(
new MetricSample(SPARSE_HISTOGRAM, histogram_name, sample, 0, 0, 0));
}
// static
scoped_ptr<MetricSample> MetricSample::ParseSparseHistogram(
const std::string& serialized_histogram) {
std::vector<std::string> parts;
base::SplitString(serialized_histogram, ' ', &parts);
if (parts.size() != 2)
return scoped_ptr<MetricSample>();
int sample;
if (parts[0].empty() || !base::StringToInt(parts[1], &sample))
return scoped_ptr<MetricSample>();
return SparseHistogramSample(parts[0], sample);
}
// static
scoped_ptr<MetricSample> MetricSample::LinearHistogramSample(
const std::string& histogram_name,
int sample,
int max) {
return scoped_ptr<MetricSample>(
new MetricSample(LINEAR_HISTOGRAM, histogram_name, sample, 0, max, 0));
}
// static
scoped_ptr<MetricSample> MetricSample::ParseLinearHistogram(
const std::string& serialized_histogram) {
std::vector<std::string> parts;
int sample, max;
base::SplitString(serialized_histogram, ' ', &parts);
if (parts.size() != 3)
return scoped_ptr<MetricSample>();
if (parts[0].empty() || !base::StringToInt(parts[1], &sample) ||
!base::StringToInt(parts[2], &max)) {
return scoped_ptr<MetricSample>();
}
return LinearHistogramSample(parts[0], sample, max);
}
// static
scoped_ptr<MetricSample> MetricSample::UserActionSample(
const std::string& action_name) {
return scoped_ptr<MetricSample>(
new MetricSample(USER_ACTION, action_name, 0, 0, 0, 0));
}
bool MetricSample::IsEqual(const MetricSample& metric) {
return type_ == metric.type_ && name_ == metric.name_ &&
sample_ == metric.sample_ && min_ == metric.min_ &&
max_ == metric.max_ && bucket_count_ == metric.bucket_count_;
}
} // namespace metrics

View File

@ -1,131 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef METRICS_SERIALIZATION_METRIC_SAMPLE_H_
#define METRICS_SERIALIZATION_METRIC_SAMPLE_H_
#include <string>
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
namespace metrics {
// This class is used by libmetrics (ChromeOS) to serialize
// and deserialize measurements to send them to a metrics sending service.
// It is meant to be a simple container with serialization functions.
class MetricSample {
public:
// Types of metric sample used.
enum SampleType {
CRASH,
HISTOGRAM,
LINEAR_HISTOGRAM,
SPARSE_HISTOGRAM,
USER_ACTION
};
~MetricSample();
// Returns true if the sample is valid (can be serialized without ambiguity).
//
// This function should be used to filter bad samples before serializing them.
bool IsValid() const;
// Getters for type and name. All types of metrics have these so we do not
// need to check the type.
SampleType type() const { return type_; }
const std::string& name() const { return name_; }
// Getters for sample, min, max, bucket_count.
// Check the metric type to make sure the request make sense. (ex: a crash
// sample does not have a bucket_count so we crash if we call bucket_count()
// on it.)
int sample() const;
int min() const;
int max() const;
int bucket_count() const;
// Returns a serialized version of the sample.
//
// The serialized message for each type is:
// crash: crash\0|name_|\0
// user action: useraction\0|name_|\0
// histogram: histogram\0|name_| |sample_| |min_| |max_| |bucket_count_|\0
// sparsehistogram: sparsehistogram\0|name_| |sample_|\0
// linearhistogram: linearhistogram\0|name_| |sample_| |max_|\0
std::string ToString() const;
// Builds a crash sample.
static scoped_ptr<MetricSample> CrashSample(const std::string& crash_name);
// Builds a histogram sample.
static scoped_ptr<MetricSample> HistogramSample(
const std::string& histogram_name,
int sample,
int min,
int max,
int bucket_count);
// Deserializes a histogram sample.
static scoped_ptr<MetricSample> ParseHistogram(const std::string& serialized);
// Builds a sparse histogram sample.
static scoped_ptr<MetricSample> SparseHistogramSample(
const std::string& histogram_name,
int sample);
// Deserializes a sparse histogram sample.
static scoped_ptr<MetricSample> ParseSparseHistogram(
const std::string& serialized);
// Builds a linear histogram sample.
static scoped_ptr<MetricSample> LinearHistogramSample(
const std::string& histogram_name,
int sample,
int max);
// Deserializes a linear histogram sample.
static scoped_ptr<MetricSample> ParseLinearHistogram(
const std::string& serialized);
// Builds a user action sample.
static scoped_ptr<MetricSample> UserActionSample(
const std::string& action_name);
// Returns true if sample and this object represent the same sample (type,
// name, sample, min, max, bucket_count match).
bool IsEqual(const MetricSample& sample);
private:
MetricSample(SampleType sample_type,
const std::string& metric_name,
const int sample,
const int min,
const int max,
const int bucket_count);
const SampleType type_;
const std::string name_;
const int sample_;
const int min_;
const int max_;
const int bucket_count_;
DISALLOW_COPY_AND_ASSIGN(MetricSample);
};
} // namespace metrics
#endif // METRICS_SERIALIZATION_METRIC_SAMPLE_H_

View File

@ -1,265 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "serialization/serialization_utils.h"
#include <sys/file.h>
#include <string>
#include <vector>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "serialization/metric_sample.h"
#define READ_WRITE_ALL_FILE_FLAGS \
(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
namespace metrics {
namespace {
// Reads the next message from |file_descriptor| into |message|.
//
// |message| will be set to the empty string if no message could be read (EOF)
// or the message was badly constructed.
//
// Returns false if no message can be read from this file anymore (EOF or
// unrecoverable error).
bool ReadMessage(int fd, std::string* message) {
CHECK(message);
int result;
int32_t message_size;
const int32_t message_hdr_size = sizeof(message_size);
// The file containing the metrics do not leave the device so the writer and
// the reader will always have the same endianness.
result = HANDLE_EINTR(read(fd, &message_size, sizeof(message_size)));
if (result < 0) {
DPLOG(ERROR) << "reading metrics message header";
return false;
}
if (result == 0) {
// This indicates a normal EOF.
return false;
}
if (result < message_hdr_size) {
DLOG(ERROR) << "bad read size " << result << ", expecting "
<< sizeof(message_size);
return false;
}
// kMessageMaxLength applies to the entire message: the 4-byte
// length field and the content.
if (message_size > SerializationUtils::kMessageMaxLength) {
DLOG(ERROR) << "message too long : " << message_size;
if (HANDLE_EINTR(lseek(fd, message_size - 4, SEEK_CUR)) == -1) {
DLOG(ERROR) << "error while skipping message. abort";
return false;
}
// Badly formatted message was skipped. Treat the badly formatted sample as
// an empty sample.
message->clear();
return true;
}
if (message_size < message_hdr_size) {
DLOG(ERROR) << "message too short : " << message_size;
return false;
}
message_size -= message_hdr_size; // The message size includes itself.
char buffer[SerializationUtils::kMessageMaxLength];
if (!base::ReadFromFD(fd, buffer, message_size)) {
DPLOG(ERROR) << "reading metrics message body";
return false;
}
*message = std::string(buffer, message_size);
return true;
}
// Opens the metrics log file at |filename| in the given |mode|.
//
// Returns the file descriptor wrapped in a valid ScopedFD on success.
base::ScopedFD OpenMetricsFile(const std::string& filename, mode_t mode) {
struct stat stat_buf;
int result;
result = stat(filename.c_str(), &stat_buf);
if (result < 0) {
if (errno != ENOENT)
DPLOG(ERROR) << filename << ": bad metrics file stat";
// Nothing to collect---try later.
return base::ScopedFD();
}
if (stat_buf.st_size == 0) {
// Also nothing to collect.
return base::ScopedFD();
}
base::ScopedFD fd(open(filename.c_str(), mode));
if (fd.get() < 0) {
DPLOG(ERROR) << filename << ": cannot open";
return base::ScopedFD();
}
return fd.Pass();
}
// Parses the contents of the metrics log file descriptor |fd| into |metrics|.
void ReadAllMetricsFromFd(int fd, ScopedVector<MetricSample>* metrics) {
for (;;) {
std::string message;
if (!ReadMessage(fd, &message))
break;
scoped_ptr<MetricSample> sample = SerializationUtils::ParseSample(message);
if (sample)
metrics->push_back(sample.release());
}
}
} // namespace
scoped_ptr<MetricSample> SerializationUtils::ParseSample(
const std::string& sample) {
if (sample.empty())
return scoped_ptr<MetricSample>();
std::vector<std::string> parts;
base::SplitString(sample, '\0', &parts);
// We should have two null terminated strings so split should produce
// three chunks.
if (parts.size() != 3) {
DLOG(ERROR) << "splitting message on \\0 produced " << parts.size()
<< " parts (expected 3)";
return scoped_ptr<MetricSample>();
}
const std::string& name = parts[0];
const std::string& value = parts[1];
if (base::LowerCaseEqualsASCII(name, "crash")) {
return MetricSample::CrashSample(value);
} else if (base::LowerCaseEqualsASCII(name, "histogram")) {
return MetricSample::ParseHistogram(value);
} else if (base::LowerCaseEqualsASCII(name, "linearhistogram")) {
return MetricSample::ParseLinearHistogram(value);
} else if (base::LowerCaseEqualsASCII(name, "sparsehistogram")) {
return MetricSample::ParseSparseHistogram(value);
} else if (base::LowerCaseEqualsASCII(name, "useraction")) {
return MetricSample::UserActionSample(value);
} else {
DLOG(ERROR) << "invalid event type: " << name << ", value: " << value;
}
return scoped_ptr<MetricSample>();
}
void SerializationUtils::ReadMetricsFromFile(
const std::string& filename,
ScopedVector<MetricSample>* metrics) {
base::ScopedFD fd(OpenMetricsFile(filename, O_RDONLY));
if (!fd.is_valid()) {
return;
}
// This processes all messages in the log.
ReadAllMetricsFromFd(fd.get(), metrics);
}
void SerializationUtils::ReadAndTruncateMetricsFromFile(
const std::string& filename,
ScopedVector<MetricSample>* metrics) {
base::ScopedFD fd(OpenMetricsFile(filename, O_RDWR));
if (!fd.is_valid()) {
return;
}
int result = flock(fd.get(), LOCK_EX);
if (result < 0) {
DPLOG(ERROR) << filename << ": cannot lock";
return;
}
// This processes all messages in the log. When all messages are
// read and processed, or an error occurs, truncate the file to zero size.
ReadAllMetricsFromFd(fd.get(), metrics);
result = ftruncate(fd.get(), 0);
if (result < 0)
DPLOG(ERROR) << "truncate metrics log";
result = flock(fd.get(), LOCK_UN);
if (result < 0)
DPLOG(ERROR) << "unlock metrics log";
}
bool SerializationUtils::WriteMetricToFile(const MetricSample& sample,
const std::string& filename) {
if (!sample.IsValid())
return false;
base::ScopedFD file_descriptor(open(filename.c_str(),
O_WRONLY | O_APPEND | O_CREAT,
READ_WRITE_ALL_FILE_FLAGS));
if (file_descriptor.get() < 0) {
DPLOG(ERROR) << filename << ": cannot open";
return false;
}
fchmod(file_descriptor.get(), READ_WRITE_ALL_FILE_FLAGS);
// Grab a lock to avoid chrome truncating the file
// underneath us. Keep the file locked as briefly as possible.
// Freeing file_descriptor will close the file and and remove the lock.
if (HANDLE_EINTR(flock(file_descriptor.get(), LOCK_EX)) < 0) {
DPLOG(ERROR) << filename << ": cannot lock";
return false;
}
std::string msg = sample.ToString();
int32 size = msg.length() + sizeof(int32);
if (size > kMessageMaxLength) {
DLOG(ERROR) << "cannot write message: too long";
return false;
}
// The file containing the metrics samples will only be read by programs on
// the same device so we do not check endianness.
if (!base::WriteFileDescriptor(file_descriptor.get(),
reinterpret_cast<char*>(&size),
sizeof(size))) {
DPLOG(ERROR) << "error writing message length";
return false;
}
if (!base::WriteFileDescriptor(
file_descriptor.get(), msg.c_str(), msg.size())) {
DPLOG(ERROR) << "error writing message";
return false;
}
return true;
}
} // namespace metrics

View File

@ -1,64 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
#define METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
#include <string>
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
namespace metrics {
class MetricSample;
// Metrics helpers to serialize and deserialize metrics collected by
// ChromeOS.
namespace SerializationUtils {
// Deserializes a sample passed as a string and return a sample.
// The return value will either be a scoped_ptr to a Metric sample (if the
// deserialization was successful) or a NULL scoped_ptr.
scoped_ptr<MetricSample> ParseSample(const std::string& sample);
// Reads all samples from a file. The file contents remain unchanged.
void ReadMetricsFromFile(const std::string& filename,
ScopedVector<MetricSample>* metrics);
// Reads all samples from a file and truncates the file when done.
void ReadAndTruncateMetricsFromFile(const std::string& filename,
ScopedVector<MetricSample>* metrics);
// Serializes a sample and write it to filename.
// The format for the message is:
// message_size, serialized_message
// where
// * message_size is the total length of the message (message_size +
// serialized_message) on 4 bytes
// * serialized_message is the serialized version of sample (using ToString)
//
// NB: the file will never leave the device so message_size will be written
// with the architecture's endianness.
bool WriteMetricToFile(const MetricSample& sample, const std::string& filename);
// Maximum length of a serialized message
static const int kMessageMaxLength = 1024;
} // namespace SerializationUtils
} // namespace metrics
#endif // METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_

View File

@ -1,181 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "serialization/serialization_utils.h"
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <gtest/gtest.h>
#include "serialization/metric_sample.h"
namespace metrics {
namespace {
class SerializationUtilsTest : public testing::Test {
protected:
SerializationUtilsTest() {
bool success = temporary_dir.CreateUniqueTempDir();
if (success) {
base::FilePath dir_path = temporary_dir.path();
filename = dir_path.value() + "chromeossampletest";
filepath = base::FilePath(filename);
}
}
void SetUp() override { base::DeleteFile(filepath, false); }
void TestSerialization(MetricSample* sample) {
std::string serialized(sample->ToString());
ASSERT_EQ('\0', serialized[serialized.length() - 1]);
scoped_ptr<MetricSample> deserialized =
SerializationUtils::ParseSample(serialized);
ASSERT_TRUE(deserialized);
EXPECT_TRUE(sample->IsEqual(*deserialized.get()));
}
std::string filename;
base::ScopedTempDir temporary_dir;
base::FilePath filepath;
};
TEST_F(SerializationUtilsTest, CrashSerializeTest) {
TestSerialization(MetricSample::CrashSample("test").get());
}
TEST_F(SerializationUtilsTest, HistogramSerializeTest) {
TestSerialization(
MetricSample::HistogramSample("myhist", 13, 1, 100, 10).get());
}
TEST_F(SerializationUtilsTest, LinearSerializeTest) {
TestSerialization(
MetricSample::LinearHistogramSample("linearhist", 12, 30).get());
}
TEST_F(SerializationUtilsTest, SparseSerializeTest) {
TestSerialization(MetricSample::SparseHistogramSample("mysparse", 30).get());
}
TEST_F(SerializationUtilsTest, UserActionSerializeTest) {
TestSerialization(MetricSample::UserActionSample("myaction").get());
}
TEST_F(SerializationUtilsTest, IllegalNameAreFilteredTest) {
scoped_ptr<MetricSample> sample1 =
MetricSample::SparseHistogramSample("no space", 10);
scoped_ptr<MetricSample> sample2 = MetricSample::LinearHistogramSample(
base::StringPrintf("here%cbhe", '\0'), 1, 3);
EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample1.get(), filename));
EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample2.get(), filename));
int64 size = 0;
ASSERT_TRUE(!PathExists(filepath) || base::GetFileSize(filepath, &size));
EXPECT_EQ(0, size);
}
TEST_F(SerializationUtilsTest, BadInputIsCaughtTest) {
std::string input(
base::StringPrintf("sparsehistogram%cname foo%c", '\0', '\0'));
EXPECT_EQ(NULL, MetricSample::ParseSparseHistogram(input).get());
}
TEST_F(SerializationUtilsTest, MessageSeparatedByZero) {
scoped_ptr<MetricSample> crash = MetricSample::CrashSample("mycrash");
SerializationUtils::WriteMetricToFile(*crash.get(), filename);
int64 size = 0;
ASSERT_TRUE(base::GetFileSize(filepath, &size));
// 4 bytes for the size
// 5 bytes for crash
// 7 bytes for mycrash
// 2 bytes for the \0
// -> total of 18
EXPECT_EQ(size, 18);
}
TEST_F(SerializationUtilsTest, MessagesTooLongAreDiscardedTest) {
// Creates a message that is bigger than the maximum allowed size.
// As we are adding extra character (crash, \0s, etc), if the name is
// kMessageMaxLength long, it will be too long.
std::string name(SerializationUtils::kMessageMaxLength, 'c');
scoped_ptr<MetricSample> crash = MetricSample::CrashSample(name);
EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*crash.get(), filename));
int64 size = 0;
ASSERT_TRUE(base::GetFileSize(filepath, &size));
EXPECT_EQ(0, size);
}
TEST_F(SerializationUtilsTest, ReadLongMessageTest) {
base::File test_file(filepath,
base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
std::string message(SerializationUtils::kMessageMaxLength + 1, 'c');
int32 message_size = message.length() + sizeof(int32);
test_file.WriteAtCurrentPos(reinterpret_cast<const char*>(&message_size),
sizeof(message_size));
test_file.WriteAtCurrentPos(message.c_str(), message.length());
test_file.Close();
scoped_ptr<MetricSample> crash = MetricSample::CrashSample("test");
SerializationUtils::WriteMetricToFile(*crash.get(), filename);
ScopedVector<MetricSample> samples;
SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &samples);
ASSERT_EQ(size_t(1), samples.size());
ASSERT_TRUE(samples[0] != NULL);
EXPECT_TRUE(crash->IsEqual(*samples[0]));
}
TEST_F(SerializationUtilsTest, WriteReadTest) {
scoped_ptr<MetricSample> hist =
MetricSample::HistogramSample("myhist", 1, 2, 3, 4);
scoped_ptr<MetricSample> crash = MetricSample::CrashSample("mycrash");
scoped_ptr<MetricSample> lhist =
MetricSample::LinearHistogramSample("linear", 1, 10);
scoped_ptr<MetricSample> shist =
MetricSample::SparseHistogramSample("mysparse", 30);
scoped_ptr<MetricSample> action = MetricSample::UserActionSample("myaction");
SerializationUtils::WriteMetricToFile(*hist.get(), filename);
SerializationUtils::WriteMetricToFile(*crash.get(), filename);
SerializationUtils::WriteMetricToFile(*lhist.get(), filename);
SerializationUtils::WriteMetricToFile(*shist.get(), filename);
SerializationUtils::WriteMetricToFile(*action.get(), filename);
ScopedVector<MetricSample> vect;
SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &vect);
ASSERT_EQ(vect.size(), size_t(5));
for (int i = 0; i < 5; i++) {
ASSERT_TRUE(vect[0] != NULL);
}
EXPECT_TRUE(hist->IsEqual(*vect[0]));
EXPECT_TRUE(crash->IsEqual(*vect[1]));
EXPECT_TRUE(lhist->IsEqual(*vect[2]));
EXPECT_TRUE(shist->IsEqual(*vect[3]));
EXPECT_TRUE(action->IsEqual(*vect[4]));
int64 size = 0;
ASSERT_TRUE(base::GetFileSize(filepath, &size));
ASSERT_EQ(0, size);
}
} // namespace
} // namespace metrics

View File

@ -0,0 +1,98 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "uploader/bn_metricsd_impl.h"
#include <base/metrics/histogram.h>
#include <base/metrics/sparse_histogram.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <utils/String16.h>
#include <utils/String8.h>
using android::binder::Status;
using android::String16;
static const char16_t kCrashTypeKernel[] = u"kernel";
static const char16_t kCrashTypeUncleanShutdown[] = u"uncleanshutdown";
static const char16_t kCrashTypeUser[] = u"user";
BnMetricsdImpl::BnMetricsdImpl(const std::shared_ptr<CrashCounters>& counters)
: counters_(counters) {
CHECK(counters_);
}
void BnMetricsdImpl::Run() {
android::defaultServiceManager()->addService(getInterfaceDescriptor(), this);
android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
android::IPCThreadState::self()->disableBackgroundScheduling(true);
android::IPCThreadState::self()->joinThreadPool();
}
Status BnMetricsdImpl::recordHistogram(
const String16& name, int sample, int min, int max, int nbuckets) {
base::HistogramBase* histogram = base::Histogram::FactoryGet(
android::String8(name).string(), min, max, nbuckets,
base::Histogram::kUmaTargetedHistogramFlag);
// |histogram| may be null if a client reports two contradicting histograms
// with the same name but different limits.
// FactoryGet will print a useful message if that is the case.
if (histogram) {
histogram->Add(sample);
}
return Status::ok();
}
Status BnMetricsdImpl::recordLinearHistogram(const String16& name,
int sample,
int max) {
base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
android::String8(name).string(), 1, max, max + 1,
base::Histogram::kUmaTargetedHistogramFlag);
// |histogram| may be null if a client reports two contradicting histograms
// with the same name but different limits.
// FactoryGet will print a useful message if that is the case.
if (histogram) {
histogram->Add(sample);
}
return Status::ok();
}
Status BnMetricsdImpl::recordSparseHistogram(const String16& name, int sample) {
base::HistogramBase* histogram = base::SparseHistogram::FactoryGet(
android::String8(name).string(),
base::Histogram::kUmaTargetedHistogramFlag);
// |histogram| may be null if a client reports two contradicting histograms
// with the same name but different limits.
// FactoryGet will print a useful message if that is the case.
if (histogram) {
histogram->Add(sample);
}
return Status::ok();
}
Status BnMetricsdImpl::recordCrash(const String16& type) {
if (type == kCrashTypeUser) {
counters_->IncrementUserCrashCount();
} else if (type == kCrashTypeKernel) {
counters_->IncrementKernelCrashCount();
} else if (type == kCrashTypeUncleanShutdown) {
counters_->IncrementUncleanShutdownCount();
} else {
LOG(ERROR) << "Unknown crash type received: " << type;
}
return Status::ok();
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef METRICSD_UPLOADER_BN_METRICSD_IMPL_H_
#define METRICSD_UPLOADER_BN_METRICSD_IMPL_H_
#include "android/brillo/metrics/BnMetricsd.h"
#include "uploader/crash_counters.h"
class BnMetricsdImpl : public android::brillo::metrics::BnMetricsd {
public:
explicit BnMetricsdImpl(const std::shared_ptr<CrashCounters>& counters);
virtual ~BnMetricsdImpl() = default;
// Starts the binder main loop.
void Run();
// Records a histogram.
android::binder::Status recordHistogram(const android::String16& name,
int sample,
int min,
int max,
int nbuckets) override;
// Records a linear histogram.
android::binder::Status recordLinearHistogram(const android::String16& name,
int sample,
int max) override;
// Records a sparse histogram.
android::binder::Status recordSparseHistogram(const android::String16& name,
int sample) override;
// Records a crash.
android::binder::Status recordCrash(const android::String16& type) override;
private:
std::shared_ptr<CrashCounters> counters_;
};
#endif // METRICSD_UPLOADER_BN_METRICSD_IMPL_H_

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "uploader/crash_counters.h"
CrashCounters::CrashCounters()
: kernel_crashes_(0), unclean_shutdowns_(0), user_crashes_(0) {}
void CrashCounters::IncrementKernelCrashCount() {
kernel_crashes_++;
}
unsigned int CrashCounters::GetAndResetKernelCrashCount() {
return kernel_crashes_.exchange(0);
}
void CrashCounters::IncrementUncleanShutdownCount() {
unclean_shutdowns_++;
}
unsigned int CrashCounters::GetAndResetUncleanShutdownCount() {
return unclean_shutdowns_.exchange(0);
}
void CrashCounters::IncrementUserCrashCount() {
user_crashes_++;
}
unsigned int CrashCounters::GetAndResetUserCrashCount() {
return user_crashes_.exchange(0);
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef METRICSD_UPLOADER_CRASH_COUNTERS_H_
#define METRICSD_UPLOADER_CRASH_COUNTERS_H_
#include <atomic>
// This class is used to keep track of the crash counters.
// An instance of it will be used by both the binder thread (to increment the
// counters) and the uploader thread (to gather and reset the counters).
// As such, the internal counters are atomic uints to allow concurrent access.
class CrashCounters {
public:
CrashCounters();
void IncrementKernelCrashCount();
unsigned int GetAndResetKernelCrashCount();
void IncrementUserCrashCount();
unsigned int GetAndResetUserCrashCount();
void IncrementUncleanShutdownCount();
unsigned int GetAndResetUncleanShutdownCount();
private:
std::atomic_uint kernel_crashes_;
std::atomic_uint unclean_shutdowns_;
std::atomic_uint user_crashes_;
};
#endif // METRICSD_UPLOADER_CRASH_COUNTERS_H_

View File

@ -27,25 +27,25 @@ MetricsLog::MetricsLog()
: MetricsLogBase("", 0, metrics::MetricsLogBase::ONGOING_LOG, "") {
}
void MetricsLog::IncrementUserCrashCount() {
void MetricsLog::IncrementUserCrashCount(unsigned int count) {
metrics::SystemProfileProto::Stability* stability(
uma_proto()->mutable_system_profile()->mutable_stability());
int current = stability->other_user_crash_count();
stability->set_other_user_crash_count(current + 1);
stability->set_other_user_crash_count(current + count);
}
void MetricsLog::IncrementKernelCrashCount() {
void MetricsLog::IncrementKernelCrashCount(unsigned int count) {
metrics::SystemProfileProto::Stability* stability(
uma_proto()->mutable_system_profile()->mutable_stability());
int current = stability->kernel_crash_count();
stability->set_kernel_crash_count(current + 1);
stability->set_kernel_crash_count(current + count);
}
void MetricsLog::IncrementUncleanShutdownCount() {
void MetricsLog::IncrementUncleanShutdownCount(unsigned int count) {
metrics::SystemProfileProto::Stability* stability(
uma_proto()->mutable_system_profile()->mutable_stability());
int current = stability->unclean_system_shutdown_count();
stability->set_unclean_system_shutdown_count(current + 1);
stability->set_unclean_system_shutdown_count(current + count);
}
bool MetricsLog::PopulateSystemProfile(SystemProfileSetter* profile_setter) {

View File

@ -14,8 +14,8 @@
* limitations under the License.
*/
#ifndef METRICS_UPLOADER_METRICS_LOG_H_
#define METRICS_UPLOADER_METRICS_LOG_H_
#ifndef METRICSD_UPLOADER_METRICS_LOG_H_
#define METRICSD_UPLOADER_METRICS_LOG_H_
#include <string>
@ -34,15 +34,20 @@ class MetricsLog : public metrics::MetricsLogBase {
// SystemProfileSetter.
MetricsLog();
void IncrementUserCrashCount();
void IncrementKernelCrashCount();
void IncrementUncleanShutdownCount();
// Increment the crash counters in the protobuf.
// These methods don't have to be thread safe as metrics logs are only
// accessed by the uploader thread.
void IncrementUserCrashCount(unsigned int count);
void IncrementKernelCrashCount(unsigned int count);
void IncrementUncleanShutdownCount(unsigned int count);
// Populate the system profile with system information using setter.
bool PopulateSystemProfile(SystemProfileSetter* setter);
private:
friend class UploadServiceTest;
FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
FRIEND_TEST(UploadServiceTest, LogContainsCrashCounts);
FRIEND_TEST(UploadServiceTest, LogKernelCrash);
FRIEND_TEST(UploadServiceTest, LogUncleanShutdown);
FRIEND_TEST(UploadServiceTest, LogUserCrash);
@ -51,4 +56,4 @@ class MetricsLog : public metrics::MetricsLogBase {
DISALLOW_COPY_AND_ASSIGN(MetricsLog);
};
#endif // METRICS_UPLOADER_METRICS_LOG_H_
#endif // METRICSD_UPLOADER_METRICS_LOG_H_

View File

@ -33,8 +33,6 @@
#include <base/sha1.h>
#include "constants.h"
#include "serialization/metric_sample.h"
#include "serialization/serialization_utils.h"
#include "uploader/metrics_log.h"
#include "uploader/sender_http.h"
#include "uploader/system_profile_setter.h"
@ -44,21 +42,19 @@ const int UploadService::kMaxFailedUpload = 10;
UploadService::UploadService(const std::string& server,
const base::TimeDelta& upload_interval,
const base::FilePath& private_metrics_directory,
const base::FilePath& shared_metrics_directory)
const base::FilePath& shared_metrics_directory,
const std::shared_ptr<CrashCounters> counters)
: histogram_snapshot_manager_(this),
sender_(new HttpSender(server)),
failed_upload_count_(metrics::kFailedUploadCountName,
private_metrics_directory),
counters_(counters),
upload_interval_(upload_interval) {
metrics_file_ =
shared_metrics_directory.Append(metrics::kMetricsEventsFileName);
staged_log_path_ = private_metrics_directory.Append(metrics::kStagedLogName);
consent_file_ = shared_metrics_directory.Append(metrics::kConsentFileName);
}
int UploadService::OnInit() {
base::StatisticsRecorder::Initialize();
system_profile_setter_.reset(new SystemProfileCache());
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
@ -70,7 +66,6 @@ int UploadService::OnInit() {
}
void UploadService::InitForTest(SystemProfileSetter* setter) {
base::StatisticsRecorder::Initialize();
system_profile_setter_.reset(setter);
}
@ -102,8 +97,7 @@ void UploadService::UploadEvent() {
return;
}
// Previous upload successful, reading metrics sample from the file.
ReadMetrics();
// Previous upload successful, stage another log.
GatherHistograms();
StageCurrentLog();
@ -143,76 +137,24 @@ void UploadService::Reset() {
failed_upload_count_.Set(0);
}
void UploadService::ReadMetrics() {
CHECK(!HasStagedLog()) << "cannot read metrics until the old logs have been "
<< "discarded";
ScopedVector<metrics::MetricSample> vector;
metrics::SerializationUtils::ReadAndTruncateMetricsFromFile(
metrics_file_.value(), &vector);
int i = 0;
for (ScopedVector<metrics::MetricSample>::iterator it = vector.begin();
it != vector.end(); it++) {
metrics::MetricSample* sample = *it;
AddSample(*sample);
i++;
}
VLOG(1) << i << " samples read";
}
void UploadService::AddSample(const metrics::MetricSample& sample) {
base::HistogramBase* counter;
switch (sample.type()) {
case metrics::MetricSample::CRASH:
AddCrash(sample.name());
break;
case metrics::MetricSample::HISTOGRAM:
counter = base::Histogram::FactoryGet(
sample.name(), sample.min(), sample.max(), sample.bucket_count(),
base::Histogram::kUmaTargetedHistogramFlag);
counter->Add(sample.sample());
break;
case metrics::MetricSample::SPARSE_HISTOGRAM:
counter = base::SparseHistogram::FactoryGet(
sample.name(), base::HistogramBase::kUmaTargetedHistogramFlag);
counter->Add(sample.sample());
break;
case metrics::MetricSample::LINEAR_HISTOGRAM:
counter = base::LinearHistogram::FactoryGet(
sample.name(),
1,
sample.max(),
sample.max() + 1,
base::Histogram::kUmaTargetedHistogramFlag);
counter->Add(sample.sample());
break;
case metrics::MetricSample::USER_ACTION:
GetOrCreateCurrentLog()->RecordUserAction(sample.name());
break;
default:
break;
}
}
void UploadService::AddCrash(const std::string& crash_name) {
if (crash_name == "user") {
GetOrCreateCurrentLog()->IncrementUserCrashCount();
} else if (crash_name == "kernel") {
GetOrCreateCurrentLog()->IncrementKernelCrashCount();
} else if (crash_name == "uncleanshutdown") {
GetOrCreateCurrentLog()->IncrementUncleanShutdownCount();
} else {
DLOG(ERROR) << "crash name unknown" << crash_name;
}
}
void UploadService::GatherHistograms() {
base::StatisticsRecorder::Histograms histograms;
base::StatisticsRecorder::GetHistograms(&histograms);
histogram_snapshot_manager_.PrepareDeltas(
base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag);
// Gather and reset the crash counters, shared with the binder threads.
unsigned int kernel_crashes = counters_->GetAndResetKernelCrashCount();
unsigned int unclean_shutdowns = counters_->GetAndResetUncleanShutdownCount();
unsigned int user_crashes = counters_->GetAndResetUserCrashCount();
// Only create a log if the counters have changed.
if (kernel_crashes > 0 || unclean_shutdowns > 0 || user_crashes > 0) {
GetOrCreateCurrentLog()->IncrementKernelCrashCount(kernel_crashes);
GetOrCreateCurrentLog()->IncrementUncleanShutdownCount(unclean_shutdowns);
GetOrCreateCurrentLog()->IncrementUserCrashCount(user_crashes);
}
}
void UploadService::RecordDelta(const base::HistogramBase& histogram,

View File

@ -25,20 +25,12 @@
#include <brillo/daemons/daemon.h>
#include "persistent_integer.h"
#include "uploader/crash_counters.h"
#include "uploader/metrics_log.h"
#include "uploader/proto/chrome_user_metrics_extension.pb.h"
#include "uploader/sender.h"
#include "uploader/system_profile_cache.h"
namespace metrics {
class ChromeUserMetricsExtension;
class CrashSample;
class HistogramSample;
class LinearHistogramSample;
class MetricSample;
class SparseHistogramSample;
class UserActionSample;
}
class SystemProfileSetter;
// Service responsible for uploading the metrics periodically to the server.
@ -57,22 +49,21 @@ class SystemProfileSetter;
// - if the upload is successful, we discard the log (therefore
// transitioning back to no staged log)
// - if the upload fails, we keep the log to try again later.
// We do not try to read the metrics that are stored on
// the disk as we want to avoid storing the metrics in memory.
//
// * if no staged logs are present:
// Read all metrics from the disk, aggregate them and try to send them.
// Take a snapshot of the aggregated metrics, save it to disk and try to send
// it:
// - if the upload succeeds, we discard the staged log (transitioning back
// to the no staged log state)
// - if the upload fails, we keep the staged log in memory to retry
// uploading later.
// - if the upload fails, we continue and will retry to upload later.
//
class UploadService : public base::HistogramFlattener, public brillo::Daemon {
public:
UploadService(const std::string& server,
const base::TimeDelta& upload_interval,
const base::FilePath& private_metrics_directory,
const base::FilePath& shared_metrics_directory);
const base::FilePath& shared_metrics_directory,
const std::shared_ptr<CrashCounters> counters);
// Initializes the upload service.
int OnInit();
@ -106,6 +97,7 @@ class UploadService : public base::HistogramFlattener, public brillo::Daemon {
FRIEND_TEST(UploadServiceTest, EmptyLogsAreNotSent);
FRIEND_TEST(UploadServiceTest, FailedSendAreRetried);
FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
FRIEND_TEST(UploadServiceTest, LogContainsCrashCounts);
FRIEND_TEST(UploadServiceTest, LogEmptyAfterUpload);
FRIEND_TEST(UploadServiceTest, LogEmptyByDefault);
FRIEND_TEST(UploadServiceTest, LogFromTheMetricsLibrary);
@ -125,15 +117,6 @@ class UploadService : public base::HistogramFlattener, public brillo::Daemon {
// Resets the internal state.
void Reset();
// Reads all the metrics from the disk.
void ReadMetrics();
// Adds a generic sample to the current log.
void AddSample(const metrics::MetricSample& sample);
// Adds a crash to the current log.
void AddCrash(const std::string& crash_name);
// Returns true iff metrics reporting is enabled.
bool AreMetricsEnabled();
@ -163,11 +146,11 @@ class UploadService : public base::HistogramFlattener, public brillo::Daemon {
scoped_ptr<Sender> sender_;
chromeos_metrics::PersistentInteger failed_upload_count_;
scoped_ptr<MetricsLog> current_log_;
std::shared_ptr<CrashCounters> counters_;
base::TimeDelta upload_interval_;
base::FilePath consent_file_;
base::FilePath metrics_file_;
base::FilePath staged_log_path_;
bool testing_;

View File

@ -20,12 +20,12 @@
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/logging.h>
#include <base/metrics/sparse_histogram.h>
#include <base/metrics/statistics_recorder.h>
#include <base/sys_info.h>
#include "constants.h"
#include "metrics/metrics_library_mock.h"
#include "persistent_integer.h"
#include "serialization/metric_sample.h"
#include "uploader/metrics_log.h"
#include "uploader/mock/mock_system_profile_setter.h"
#include "uploader/mock/sender_mock.h"
@ -39,6 +39,10 @@ class UploadServiceTest : public testing::Test {
protected:
virtual void SetUp() {
CHECK(dir_.CreateUniqueTempDir());
// Make sure the statistics recorder is inactive (contains no metrics) then
// initialize it.
ASSERT_FALSE(base::StatisticsRecorder::IsActive());
base::StatisticsRecorder::Initialize();
base::FilePath private_dir = dir_.path().Append("private");
base::FilePath shared_dir = dir_.path().Append("shared");
@ -46,11 +50,12 @@ class UploadServiceTest : public testing::Test {
EXPECT_TRUE(base::CreateDirectory(private_dir));
EXPECT_TRUE(base::CreateDirectory(shared_dir));
metrics_lib_.InitForTest(shared_dir);
ASSERT_EQ(0, base::WriteFile(shared_dir.Append(metrics::kConsentFileName),
"", 0));
upload_service_.reset(
new UploadService("", base::TimeDelta(), private_dir, shared_dir));
counters_.reset(new CrashCounters);
upload_service_.reset(new UploadService("", base::TimeDelta(), private_dir,
shared_dir, counters_));
upload_service_->sender_.reset(new SenderMock);
upload_service_->InitForTest(new MockSystemProfileSetter);
@ -58,8 +63,17 @@ class UploadServiceTest : public testing::Test {
upload_service_->Reset();
}
scoped_ptr<metrics::MetricSample> Crash(const std::string& name) {
return metrics::MetricSample::CrashSample(name);
void SendSparseHistogram(const std::string& name, int sample) {
base::HistogramBase* histogram = base::SparseHistogram::FactoryGet(
name, base::Histogram::kUmaTargetedHistogramFlag);
histogram->Add(sample);
}
void SendHistogram(
const std::string& name, int sample, int min, int max, int nbuckets) {
base::HistogramBase* histogram = base::Histogram::FactoryGet(
name, min, max, nbuckets, base::Histogram::kUmaTargetedHistogramFlag);
histogram->Add(sample);
}
void SetTestingProperty(const std::string& name, const std::string& value) {
@ -71,57 +85,26 @@ class UploadServiceTest : public testing::Test {
base::WriteFile(filepath, value.data(), value.size()));
}
const metrics::SystemProfileProto_Stability GetCurrentStability() {
EXPECT_TRUE(upload_service_->current_log_);
return upload_service_->current_log_->uma_proto()->system_profile().stability();
}
base::ScopedTempDir dir_;
scoped_ptr<UploadService> upload_service_;
MetricsLibrary metrics_lib_;
scoped_ptr<base::AtExitManager> exit_manager_;
std::shared_ptr<CrashCounters> counters_;
};
// Tests that the right crash increments a values.
TEST_F(UploadServiceTest, LogUserCrash) {
upload_service_->AddSample(*Crash("user").get());
MetricsLog* log = upload_service_->current_log_.get();
metrics::ChromeUserMetricsExtension* proto = log->uma_proto();
EXPECT_EQ(1, proto->system_profile().stability().other_user_crash_count());
}
TEST_F(UploadServiceTest, LogUncleanShutdown) {
upload_service_->AddSample(*Crash("uncleanshutdown"));
EXPECT_EQ(1, upload_service_->current_log_
->uma_proto()
->system_profile()
.stability()
.unclean_system_shutdown_count());
}
TEST_F(UploadServiceTest, LogKernelCrash) {
upload_service_->AddSample(*Crash("kernel"));
EXPECT_EQ(1, upload_service_->current_log_
->uma_proto()
->system_profile()
.stability()
.kernel_crash_count());
}
TEST_F(UploadServiceTest, UnknownCrashIgnored) {
upload_service_->AddSample(*Crash("foo"));
// The log should be empty.
EXPECT_FALSE(upload_service_->current_log_);
}
TEST_F(UploadServiceTest, FailedSendAreRetried) {
SenderMock* sender = new SenderMock();
upload_service_->sender_.reset(sender);
sender->set_should_succeed(false);
upload_service_->AddSample(*Crash("user"));
SendSparseHistogram("hello", 1);
upload_service_->UploadEvent();
EXPECT_EQ(1, sender->send_call_count());
std::string sent_string = sender->last_message();
@ -137,7 +120,7 @@ TEST_F(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload) {
sender->set_should_succeed(false);
upload_service_->AddSample(*Crash("user"));
SendSparseHistogram("hello", 1);
for (int i = 0; i < UploadService::kMaxFailedUpload; i++) {
upload_service_->UploadEvent();
@ -148,7 +131,7 @@ TEST_F(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload) {
EXPECT_FALSE(upload_service_->HasStagedLog());
// Log a new sample. The failed upload counter should be reset.
upload_service_->AddSample(*Crash("user"));
SendSparseHistogram("hello", 1);
for (int i = 0; i < UploadService::kMaxFailedUpload; i++) {
upload_service_->UploadEvent();
}
@ -165,7 +148,8 @@ TEST_F(UploadServiceTest, EmptyLogsAreNotSent) {
}
TEST_F(UploadServiceTest, LogEmptyByDefault) {
UploadService upload_service("", base::TimeDelta(), dir_.path(), dir_.path());
UploadService upload_service("", base::TimeDelta(), dir_.path(), dir_.path(),
std::make_shared<CrashCounters>());
// current_log_ should be initialized later as it needs AtExitManager to exit
// in order to gather system information from SysInfo.
@ -176,34 +160,28 @@ TEST_F(UploadServiceTest, CanSendMultipleTimes) {
SenderMock* sender = new SenderMock();
upload_service_->sender_.reset(sender);
upload_service_->AddSample(*Crash("user"));
SendSparseHistogram("hello", 1);
upload_service_->UploadEvent();
std::string first_message = sender->last_message();
SendSparseHistogram("hello", 2);
upload_service_->AddSample(*Crash("kernel"));
upload_service_->UploadEvent();
EXPECT_NE(first_message, sender->last_message());
}
TEST_F(UploadServiceTest, LogEmptyAfterUpload) {
upload_service_->AddSample(*Crash("user"));
EXPECT_TRUE(upload_service_->current_log_);
SendSparseHistogram("hello", 2);
upload_service_->UploadEvent();
EXPECT_FALSE(upload_service_->current_log_);
}
TEST_F(UploadServiceTest, LogContainsAggregatedValues) {
scoped_ptr<metrics::MetricSample> histogram =
metrics::MetricSample::HistogramSample("foo", 10, 0, 42, 10);
upload_service_->AddSample(*histogram.get());
scoped_ptr<metrics::MetricSample> histogram2 =
metrics::MetricSample::HistogramSample("foo", 11, 0, 42, 10);
upload_service_->AddSample(*histogram2.get());
SendHistogram("foo", 11, 0, 42, 10);
SendHistogram("foo", 12, 0, 42, 10);
upload_service_->GatherHistograms();
metrics::ChromeUserMetricsExtension* proto =
@ -211,6 +189,37 @@ TEST_F(UploadServiceTest, LogContainsAggregatedValues) {
EXPECT_EQ(1, proto->histogram_event().size());
}
TEST_F(UploadServiceTest, LogContainsCrashCounts) {
// By default, there is no current log.
upload_service_->GatherHistograms();
EXPECT_FALSE(upload_service_->current_log_);
// If the user crash counter is incremented, we add the count to the current
// log.
counters_->IncrementUserCrashCount();
upload_service_->GatherHistograms();
EXPECT_EQ(1, GetCurrentStability().other_user_crash_count());
// If the kernel crash counter is incremented, we add the count to the current
// log.
counters_->IncrementKernelCrashCount();
upload_service_->GatherHistograms();
EXPECT_EQ(1, GetCurrentStability().kernel_crash_count());
// If the kernel crash counter is incremented, we add the count to the current
// log.
counters_->IncrementUncleanShutdownCount();
counters_->IncrementUncleanShutdownCount();
upload_service_->GatherHistograms();
EXPECT_EQ(2, GetCurrentStability().unclean_system_shutdown_count());
// If no counter is incremented, the reported numbers don't change.
upload_service_->GatherHistograms();
EXPECT_EQ(1, GetCurrentStability().other_user_crash_count());
EXPECT_EQ(1, GetCurrentStability().kernel_crash_count());
EXPECT_EQ(2, GetCurrentStability().unclean_system_shutdown_count());
}
TEST_F(UploadServiceTest, ExtractChannelFromString) {
EXPECT_EQ(
SystemProfileCache::ProtoChannelFromString(
@ -234,13 +243,12 @@ TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) {
SetTestingProperty(metrics::kProductId, "hello");
SetTestingProperty(metrics::kProductVersion, "1.2.3.4");
scoped_ptr<metrics::MetricSample> histogram =
metrics::MetricSample::SparseHistogramSample("myhistogram", 1);
SendSparseHistogram("hello", 1);
// Reset to create the new log with the profile setter.
upload_service_->system_profile_setter_.reset(
new SystemProfileCache(true, dir_.path()));
upload_service_->Reset();
upload_service_->AddSample(*histogram.get());
upload_service_->UploadEvent();
EXPECT_EQ(1, sender->send_call_count());
@ -281,21 +289,6 @@ TEST_F(UploadServiceTest, SessionIdIncrementedAtInitialization) {
EXPECT_EQ(cache.profile_.session_id, session_id + 1);
}
// Test that we can log metrics from the metrics library and have the uploader
// upload them.
TEST_F(UploadServiceTest, LogFromTheMetricsLibrary) {
SenderMock* sender = new SenderMock();
upload_service_->sender_.reset(sender);
upload_service_->UploadEvent();
EXPECT_EQ(0, sender->send_call_count());
metrics_lib_.SendEnumToUMA("testname", 2, 10);
upload_service_->UploadEvent();
EXPECT_EQ(1, sender->send_call_count());
}
// The product id must be set for metrics to be uploaded.
// If it is not set, the system profile cache should fail to initialize.
TEST_F(UploadServiceTest, ProductIdMandatory) {