| // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "metrics/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, |
| int num_samples) |
| : type_(sample_type), |
| name_(metric_name), |
| sample_(sample), |
| min_(min), |
| max_(max), |
| bucket_count_(bucket_count), |
| num_samples_(num_samples) {} |
| |
| bool MetricSample::IsValid() const { |
| if (type() == INVALID || |
| name().find(' ') != std::string::npos || |
| name().find('\0') != std::string::npos || |
| name().empty()) { |
| LOG(ERROR) << "Invalid sample type or name for histogram \"" |
| << name() << "\""; |
| return false; |
| } |
| if (type() == LINEAR_HISTOGRAM && max() == 1) { |
| // No buckets: this is quietly ignored by Chrome, so better catch it here. |
| LOG(ERROR) << "No buckets for linear histogram \"" << name() << "\""; |
| return false; |
| } |
| if (type() == HISTOGRAM) { |
| // Avoid integer overflow by forcing 64-bit ops. |
| int64_t max64 = max(); |
| if (bucket_count() > max64 - min() + 2) { |
| // Too many buckets: this is also quietly ignored by Chrome. |
| // Note: a value x such that min <= x < max goes into a regular bucket. |
| // Values outside that range go in the overflow and underflow buckets. |
| LOG(ERROR) << "Too many buckets (" << bucket_count() |
| << ") for histogram \"" << name() |
| << "\", max for this range is " << max64 - min() + 2; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| 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) { |
| if (num_samples_ > 1) { |
| return base::StringPrintf("histogram%c%s %d %d %d %d %d%c", |
| '\0', |
| name().c_str(), |
| sample_, |
| min_, |
| max_, |
| bucket_count_, |
| num_samples_, |
| '\0'); |
| } else { |
| return base::StringPrintf("histogram%c%s %d %d %d %d%c", |
| '\0', |
| name().c_str(), |
| sample_, |
| min_, |
| max_, |
| bucket_count_, |
| '\0'); |
| } |
| } else if (type_ == USER_ACTION) { |
| CHECK_EQ(type_, USER_ACTION); |
| return base::StringPrintf("useraction%c%s%c", '\0', name().c_str(), '\0'); |
| } |
| |
| NOTREACHED() << "Invalid sample type" << type_; |
| return std::string(); |
| } |
| |
| 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_; |
| } |
| |
| int MetricSample::num_samples() const { |
| CHECK_EQ(type_, HISTOGRAM); |
| return num_samples_; |
| } |
| |
| // static |
| MetricSample MetricSample::CrashSample(const std::string& crash_name) { |
| return MetricSample(CRASH, crash_name, 0, 0, 0, 0); |
| } |
| |
| // static |
| MetricSample MetricSample::HistogramSample( |
| const std::string& histogram_name, |
| int sample, |
| int min, |
| int max, |
| int bucket_count, |
| int num_samples) { |
| return MetricSample(HISTOGRAM, histogram_name, sample, min, max, bucket_count, |
| num_samples); |
| } |
| |
| // static |
| MetricSample MetricSample::ParseHistogram( |
| const std::string& serialized_histogram) { |
| std::vector<std::string> parts = base::SplitString( |
| serialized_histogram, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| |
| if (parts.size() != 5 && parts.size() != 6) |
| return 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 MetricSample(); |
| } |
| |
| int num_samples = 1; |
| if (parts.size() == 6) { |
| if (!base::StringToInt(parts[5], &num_samples)) { |
| return MetricSample(); |
| } |
| } |
| |
| return HistogramSample(parts[0], sample, min, max, bucket_count, num_samples); |
| } |
| |
| // static |
| MetricSample MetricSample::SparseHistogramSample( |
| const std::string& histogram_name, int sample) { |
| return MetricSample(SPARSE_HISTOGRAM, histogram_name, sample, 0, 0, 0); |
| } |
| |
| // static |
| MetricSample MetricSample::ParseSparseHistogram( |
| const std::string& serialized_histogram) { |
| std::vector<std::string> parts = base::SplitString( |
| serialized_histogram, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| if (parts.size() != 2) |
| return MetricSample(); |
| int sample; |
| if (parts[0].empty() || !base::StringToInt(parts[1], &sample)) |
| return MetricSample(); |
| |
| return SparseHistogramSample(parts[0], sample); |
| } |
| |
| // static |
| MetricSample MetricSample::LinearHistogramSample( |
| const std::string& histogram_name, int sample, int max) { |
| return MetricSample(LINEAR_HISTOGRAM, histogram_name, sample, 0, max, 0); |
| } |
| |
| // static |
| MetricSample MetricSample::ParseLinearHistogram( |
| const std::string& serialized_histogram) { |
| int sample, max; |
| std::vector<std::string> parts = base::SplitString( |
| serialized_histogram, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| if (parts.size() != 3) |
| return MetricSample(); |
| if (parts[0].empty() || !base::StringToInt(parts[1], &sample) || |
| !base::StringToInt(parts[2], &max)) { |
| return MetricSample(); |
| } |
| |
| return LinearHistogramSample(parts[0], sample, max); |
| } |
| |
| // static |
| MetricSample MetricSample::UserActionSample(const std::string& action_name) { |
| return MetricSample(USER_ACTION, action_name, 0, 0, 0, 0); |
| } |
| |
| bool MetricSample::IsEqual(const MetricSample& metric) const { |
| return type_ == metric.type_ && name_ == metric.name_ && |
| sample_ == metric.sample_ && min_ == metric.min_ && |
| max_ == metric.max_ && bucket_count_ == metric.bucket_count_ && |
| num_samples_ == metric.num_samples_; |
| } |
| |
| } // namespace metrics |