metrics: Add guest mode detection to metrics library and client

Change-Id: I2c27bd999330395ba3568820ea76198b202bd7f4

BUG=7203
TEST=Verify metrics_client -c and -g toggling consent and guest mode.

Review URL: http://codereview.chromium.org/3571009
This commit is contained in:
Ken Mixter 2010-10-01 15:38:42 -07:00
parent fd55798dc0
commit eafbbdf3df
5 changed files with 254 additions and 59 deletions

View file

@ -7,63 +7,29 @@
#include "metrics_library.h"
int main(int argc, char** argv) {
bool send_to_autotest = false;
bool send_to_chrome = true;
bool send_enum = false;
bool secs_to_msecs = false;
int name_index = 1;
bool print_usage = false;
if (argc >= 3) {
// Parse arguments
int flag;
while ((flag = getopt(argc, argv, "abet")) != -1) {
switch (flag) {
case 'a':
send_to_autotest = true;
send_to_chrome = false;
break;
case 'b':
send_to_chrome = true;
send_to_autotest = true;
break;
case 'e':
send_enum = true;
break;
case 't':
secs_to_msecs = true;
break;
default:
print_usage = true;
break;
}
}
name_index = optind;
} else {
print_usage = true;
}
int num_args = send_enum ? 3 : 5;
if ((name_index + num_args) != argc ||
(send_enum && secs_to_msecs)) {
print_usage = true;
}
if (print_usage) {
fprintf(stderr,
"Usage: metrics_client [-ab] [-t] name sample min max nbuckets\n"
" metrics_client [-ab] -e name sample max\n"
"\n"
" default: send metric with integer values to Chrome only\n"
" |min| > 0, |min| <= sample < |max|\n"
" -a: send metric (name/sample) to Autotest only\n"
" -b: send metric to both Chrome and Autotest\n"
" -e: send linear/enumeration histogram data\n"
" -t: convert sample from double seconds to int milliseconds\n");
return 1;
}
void ShowUsage() {
fprintf(stderr,
"Usage: metrics_client [-ab] [-t] name sample min max nbuckets\n"
" metrics_client [-ab] -e name sample max\n"
" metrics_client [-cg]\n"
"\n"
" default: send metric with integer values to Chrome only\n"
" |min| > 0, |min| <= sample < |max|\n"
" -a: send metric (name/sample) to Autotest only\n"
" -b: send metric to both Chrome and Autotest\n"
" -c: return exit status 0 if user consents to stats, 1 otherwise\n"
" -e: send linear/enumeration histogram data\n"
" -g: return exit status 0 if machine in guest mode, 1 otherwise\n"
" -t: convert sample from double seconds to int milliseconds\n");
exit(1);
}
static int SendStats(char* argv[],
int name_index,
bool send_enum,
bool secs_to_msecs,
bool send_to_autotest,
bool send_to_chrome) {
const char* name = argv[name_index];
int sample;
if (secs_to_msecs) {
@ -92,3 +58,89 @@ int main(int argc, char** argv) {
}
return 0;
}
static int HasConsent() {
MetricsLibrary metrics_lib;
metrics_lib.Init();
return metrics_lib.AreMetricsEnabled() ? 0 : 1;
}
static int IsGuestMode() {
MetricsLibrary metrics_lib;
metrics_lib.Init();
return metrics_lib.IsGuestMode() ? 0 : 1;
}
int main(int argc, char** argv) {
enum Mode {
kModeSendStats,
kModeHasConsent,
kModeIsGuestMode
} mode = kModeSendStats;
bool send_to_autotest = false;
bool send_to_chrome = true;
bool send_enum = false;
bool secs_to_msecs = false;
bool print_usage = false;
// Parse arguments
int flag;
while ((flag = getopt(argc, argv, "abcegt")) != -1) {
switch (flag) {
case 'a':
mode = kModeSendStats;
send_to_autotest = true;
send_to_chrome = false;
break;
case 'b':
mode = kModeSendStats;
send_to_chrome = true;
send_to_autotest = true;
break;
case 'c':
mode = kModeHasConsent;
break;
case 'e':
send_enum = true;
break;
case 'g':
mode = kModeIsGuestMode;
break;
case 't':
secs_to_msecs = true;
break;
default:
print_usage = true;
break;
}
}
int name_index = optind;
int expected_args = 0;
if (mode == kModeSendStats)
expected_args = send_enum ? 3 : 5;
if ((name_index + expected_args) != argc) {
ShowUsage();
}
switch(mode) {
case kModeSendStats:
if (send_enum && secs_to_msecs) {
ShowUsage();
}
return SendStats(argv,
name_index,
send_enum,
secs_to_msecs,
send_to_autotest,
send_to_chrome);
case kModeHasConsent:
return HasConsent();
case kModeIsGuestMode:
return IsGuestMode();
default:
ShowUsage();
return 0;
}
}

View file

@ -210,7 +210,6 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) {
DeleteFrequencyCounters();
ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);

View file

@ -44,13 +44,84 @@ MetricsLibrary::MetricsLibrary()
: uma_events_file_(NULL),
consent_file_(kConsentFile) {}
// We take buffer and buffer_size as parameters in order to simplify testing
// of various alignments of the |device_name| with |buffer_size|.
bool MetricsLibrary::IsDeviceMounted(const char* device_name,
const char* mounts_file,
char* buffer,
int buffer_size,
bool* result) {
if (buffer == NULL || buffer_size < 1)
return false;
int mounts_fd = open(mounts_file, O_RDONLY);
if (mounts_fd < 0)
return false;
// match_offset describes:
// -1 -- not beginning of line
// 0..strlen(device_name)-1 -- this offset in device_name is next to match
// strlen(device_name) -- matched full name, just need a space.
int match_offset = 0;
bool match = false;
while (!match) {
int read_size = read(mounts_fd, buffer, buffer_size);
if (read_size <= 0) {
if (errno == -EINTR)
continue;
break;
}
for (int i = 0; i < read_size; ++i) {
if (buffer[i] == '\n') {
match_offset = 0;
continue;
}
if (match_offset < 0) {
continue;
}
if (device_name[match_offset] == '\0') {
if (buffer[i] == ' ') {
match = true;
break;
}
match_offset = -1;
continue;
}
if (buffer[i] == device_name[match_offset]) {
++match_offset;
} else {
match_offset = -1;
}
}
}
close(mounts_fd);
*result = match;
return true;
}
bool MetricsLibrary::IsGuestMode() {
char buffer[256];
bool result = false;
if (!IsDeviceMounted("guestfs",
"/proc/mounts",
buffer,
sizeof(buffer),
&result)) {
return false;
}
return result;
}
bool MetricsLibrary::AreMetricsEnabled() {
static struct stat stat_buffer;
time_t this_check_time = time(NULL);
if (this_check_time != cached_enabled_time_) {
cached_enabled_time_ = this_check_time;
cached_enabled_ = (stat(consent_file_, &stat_buffer) >= 0);
if (stat(consent_file_, &stat_buffer) >= 0 &&
!IsGuestMode())
cached_enabled_ = true;
else
cached_enabled_ = false;
}
return cached_enabled_;
}

View file

@ -27,6 +27,9 @@ class MetricsLibrary : public MetricsLibraryInterface {
// Initializes the library.
void Init();
// Returns whether or not the machine is running in guest mode.
bool IsGuestMode();
// Returns whether or not metrics collection is enabled.
bool AreMetricsEnabled();
@ -75,9 +78,18 @@ class MetricsLibrary : public MetricsLibraryInterface {
FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabled);
FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage);
FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong);
FRIEND_TEST(MetricsLibraryTest, IsDeviceMounted);
FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome);
FRIEND_TEST(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation);
// Sets |*result| to whether or not the |mounts_file| indicates that
// the |device_name| is currently mounted. Uses |buffer| of
// |buffer_size| to read the file. Returns false if any error.
bool IsDeviceMounted(const char* device_name,
const char* mounts_file,
char* buffer, int buffer_size,
bool* result);
// Sends message of size |length| to Chrome for transport to UMA and
// returns true on success.
bool SendMessageToChrome(int32_t length, const char* message);

View file

@ -11,8 +11,8 @@
#include "metrics_library.h"
static const FilePath kTestUMAEventsFile("test-uma-events");
static const char kTestConsent[] = "test-consent";
static const char kTestMounts[] = "test-mounts";
static void SetMetricsEnabled(bool enabled) {
if (enabled)
@ -35,6 +35,8 @@ class MetricsLibraryTest : public testing::Test {
}
virtual void TearDown() {
file_util::Delete(FilePath(kTestConsent), false);
file_util::Delete(FilePath(kTestMounts), false);
file_util::Delete(kTestUMAEventsFile, false);
}
@ -44,6 +46,65 @@ class MetricsLibraryTest : public testing::Test {
MetricsLibrary lib_;
};
TEST_F(MetricsLibraryTest, IsDeviceMounted) {
static const char kTestContents[] =
"0123456789abcde 0123456789abcde\nguestfs foo bar\n";
char buffer[1024];
int block_sizes[] = { 1, 2, 3, 4, 5, 6, 8, 12, 14, 16, 32, 1024 };
bool result;
EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
"nonexistent",
buffer,
1,
&result));
ASSERT_TRUE(file_util::WriteFile(FilePath(kTestMounts),
kTestContents,
strlen(kTestContents)));
EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
kTestMounts,
buffer,
0,
&result));
for (size_t i = 0; i < arraysize(block_sizes); ++i) {
EXPECT_TRUE(lib_.IsDeviceMounted("0123456789abcde",
kTestMounts,
buffer,
block_sizes[i],
&result));
EXPECT_TRUE(result);
EXPECT_TRUE(lib_.IsDeviceMounted("guestfs",
kTestMounts,
buffer,
block_sizes[i],
&result));
EXPECT_TRUE(result);
EXPECT_TRUE(lib_.IsDeviceMounted("0123456",
kTestMounts,
buffer,
block_sizes[i],
&result));
EXPECT_FALSE(result);
EXPECT_TRUE(lib_.IsDeviceMounted("9abcde",
kTestMounts,
buffer,
block_sizes[i],
&result));
EXPECT_FALSE(result);
EXPECT_TRUE(lib_.IsDeviceMounted("foo",
kTestMounts,
buffer,
block_sizes[i],
&result));
EXPECT_FALSE(result);
EXPECT_TRUE(lib_.IsDeviceMounted("bar",
kTestMounts,
buffer,
block_sizes[i],
&result));
EXPECT_FALSE(result);
}
}
TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) {
SetMetricsEnabled(false);
EXPECT_FALSE(lib_.AreMetricsEnabled());