Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 42890f5b authored by Bertrand Simonnet's avatar Bertrand Simonnet Committed by Android Git Automerger
Browse files

am 3fd57f26: am 66255db0: Merge "metricsd: Only collect metrics over a short period."

* commit '3fd57f26':
  metricsd: Only collect metrics over a short period.
parents 0b2bce06 3fd57f26
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ metrics_client_sources := \
  metrics_client.cc

metrics_daemon_sources := \
  collectors/averaged_statistics_collector.cc \
  collectors/disk_usage_collector.cc \
  metrics_daemon.cc \
  metrics_daemon_main.cc \
@@ -40,6 +41,8 @@ metrics_daemon_sources := \
  serialization/serialization_utils.cc

metrics_tests_sources := \
  collectors/averaged_statistics_collector.cc \
  collectors/averaged_statistics_collector_test.cc \
  collectors/disk_usage_collector.cc \
  metrics_daemon.cc \
  metrics_daemon_test.cc \
@@ -146,6 +149,7 @@ include $(BUILD_EXECUTABLE)
# ========================================================
include $(CLEAR_VARS)
LOCAL_MODULE := metrics_tests
LOCAL_CLANG := true
LOCAL_CFLAGS := $(metrics_CFLAGS)
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
+216 −0
Original line number Diff line number Diff line
/*
 * 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 "averaged_statistics_collector.h"

#include <base/files/file_util.h>
#include <base/files/file_path.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>

#include "metrics_daemon.h"

namespace {

// disk stats metrics

// The {Read,Write}Sectors numbers are in sectors/second.
// A sector is usually 512 bytes.
const char kReadSectorsHistogramName[] = "Platform.ReadSectors";
const char kWriteSectorsHistogramName[] = "Platform.WriteSectors";
const int kDiskMetricsStatItemCount = 11;

// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
// sectors.
const int kSectorsIOMax = 500000;  // sectors/second
const int kSectorsBuckets = 50;    // buckets

// Page size is 4k, sector size is 0.5k.  We're not interested in page fault
// rates that the disk cannot sustain.
const int kPageFaultsMax = kSectorsIOMax / 8;  // Page faults/second
const int kPageFaultsBuckets = 50;

// Major page faults, i.e. the ones that require data to be read from disk.
const char kPageFaultsHistogramName[] = "Platform.PageFaults";

// Swap in and Swap out
const char kSwapInHistogramName[] = "Platform.SwapIn";
const char kSwapOutHistogramName[] = "Platform.SwapOut";

const int kIntervalBetweenCollection = 60;  // seconds
const int kCollectionDuration = 1;  // seconds

}  // namespace

AveragedStatisticsCollector::AveragedStatisticsCollector(
    MetricsLibraryInterface* metrics_library,
    const std::string& diskstats_path,
    const std::string& vmstats_path) :
  metrics_lib_(metrics_library),
  diskstats_path_(diskstats_path),
  vmstats_path_(vmstats_path) {
}

void AveragedStatisticsCollector::ScheduleWait() {
  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
      base::Bind(&AveragedStatisticsCollector::WaitCallback,
                 base::Unretained(this)),
      base::TimeDelta::FromSeconds(
          kIntervalBetweenCollection - kCollectionDuration));
}

void AveragedStatisticsCollector::ScheduleCollect() {
  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
      base::Bind(&AveragedStatisticsCollector::CollectCallback,
                 base::Unretained(this)),
      base::TimeDelta::FromSeconds(kCollectionDuration));
}

void AveragedStatisticsCollector::WaitCallback() {
  ReadInitialValues();
  ScheduleCollect();
}

void AveragedStatisticsCollector::CollectCallback() {
  Collect();
  ScheduleWait();
}

void AveragedStatisticsCollector::ReadInitialValues() {
  stats_start_time_ = MetricsDaemon::GetActiveTime();
  DiskStatsReadStats(&read_sectors_, &write_sectors_);
  VmStatsReadStats(&vmstats_);
}

bool AveragedStatisticsCollector::DiskStatsReadStats(
    uint64_t* read_sectors, uint64_t* write_sectors) {
  CHECK(read_sectors);
  CHECK(write_sectors);
  std::string line;
  if (diskstats_path_.empty()) {
    return false;
  }

  if (!base::ReadFileToString(base::FilePath(diskstats_path_), &line)) {
    PLOG(WARNING) << "Could not read disk stats from "
                  << diskstats_path_.value();
    return false;
  }

  std::vector<std::string> parts = base::SplitString(
      line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  if (parts.size() != kDiskMetricsStatItemCount) {
    LOG(ERROR) << "Could not parse disk stat correctly. Expected "
               << kDiskMetricsStatItemCount << " elements but got "
               << parts.size();
    return false;
  }
  if (!base::StringToUint64(parts[2], read_sectors)) {
    LOG(ERROR) << "Couldn't convert read sectors " << parts[2] << " to uint64";
    return false;
  }
  if (!base::StringToUint64(parts[6], write_sectors)) {
    LOG(ERROR) << "Couldn't convert write sectors " << parts[6] << " to uint64";
    return false;
  }

  return true;
}

bool AveragedStatisticsCollector::VmStatsParseStats(
    const char* stats, struct VmstatRecord* record) {
  CHECK(stats);
  CHECK(record);
  base::StringPairs pairs;
  base::SplitStringIntoKeyValuePairs(stats, ' ', '\n', &pairs);

  for (base::StringPairs::iterator it = pairs.begin();
       it != pairs.end(); ++it) {
    if (it->first == "pgmajfault" &&
        !base::StringToUint64(it->second, &record->page_faults)) {
      return false;
    }
    if (it->first == "pswpin" &&
        !base::StringToUint64(it->second, &record->swap_in)) {
      return false;
    }
    if (it->first == "pswpout" &&
        !base::StringToUint64(it->second, &record->swap_out)) {
      return false;
    }
  }
  return true;
}

bool AveragedStatisticsCollector::VmStatsReadStats(struct VmstatRecord* stats) {
  CHECK(stats);
  std::string value_string;
  if (!base::ReadFileToString(vmstats_path_, &value_string)) {
    LOG(WARNING) << "cannot read " << vmstats_path_.value();
    return false;
  }
  return VmStatsParseStats(value_string.c_str(), stats);
}

void AveragedStatisticsCollector::Collect() {
  uint64_t read_sectors_now, write_sectors_now;
  struct VmstatRecord vmstats_now;
  double time_now = MetricsDaemon::GetActiveTime();
  double delta_time = time_now - stats_start_time_;
  bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
                                              &write_sectors_now);

  int delta_read = read_sectors_now - read_sectors_;
  int delta_write = write_sectors_now - write_sectors_;
  int read_sectors_per_second = delta_read / delta_time;
  int write_sectors_per_second = delta_write / delta_time;
  bool vmstats_success = VmStatsReadStats(&vmstats_now);
  uint64_t delta_faults = vmstats_now.page_faults - vmstats_.page_faults;
  uint64_t delta_swap_in = vmstats_now.swap_in - vmstats_.swap_in;
  uint64_t delta_swap_out = vmstats_now.swap_out - vmstats_.swap_out;
  uint64_t page_faults_per_second = delta_faults / delta_time;
  uint64_t swap_in_per_second = delta_swap_in / delta_time;
  uint64_t swap_out_per_second = delta_swap_out / delta_time;
  if (diskstats_success) {
    metrics_lib_->SendToUMA(kReadSectorsHistogramName,
                            read_sectors_per_second,
                            1,
                            kSectorsIOMax,
                            kSectorsBuckets);
    metrics_lib_->SendToUMA(kWriteSectorsHistogramName,
                            write_sectors_per_second,
                            1,
                            kSectorsIOMax,
                            kSectorsBuckets);
  }
  if (vmstats_success) {
    metrics_lib_->SendToUMA(kPageFaultsHistogramName,
                            page_faults_per_second,
                            1,
                            kPageFaultsMax,
                            kPageFaultsBuckets);
    metrics_lib_->SendToUMA(kSwapInHistogramName,
                            swap_in_per_second,
                            1,
                            kPageFaultsMax,
                            kPageFaultsBuckets);
    metrics_lib_->SendToUMA(kSwapOutHistogramName,
                            swap_out_per_second,
                            1,
                            kPageFaultsMax,
                            kPageFaultsBuckets);
  }
}
+79 −0
Original line number Diff line number Diff line
/*
 * 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_COLLECTORS_AVERAGED_STATISTICS_COLLECTOR_H_
#define METRICSD_COLLECTORS_AVERAGED_STATISTICS_COLLECTOR_H_

#include "metrics/metrics_library.h"

class AveragedStatisticsCollector {
 public:
  AveragedStatisticsCollector(MetricsLibraryInterface* metrics_library,
                              const std::string& diskstats_path,
                              const std::string& vmstat_path);

  // Schedule a wait period.
  void ScheduleWait();

  // Schedule a collection period.
  void ScheduleCollect();

  // Callback used by the main loop.
  void CollectCallback();

  // Callback used by the main loop.
  void WaitCallback();

  // Read and store the initial values at the beginning of a collection cycle.
  void ReadInitialValues();

  // Collect the disk usage statistics and report them.
  void Collect();

 private:
  friend class AveragedStatisticsTest;
  FRIEND_TEST(AveragedStatisticsTest, ParseDiskStats);
  FRIEND_TEST(AveragedStatisticsTest, ParseVmStats);

  // Record for retrieving and reporting values from /proc/vmstat
  struct VmstatRecord {
    uint64_t page_faults;    // major faults
    uint64_t swap_in;        // pages swapped in
    uint64_t swap_out;       // pages swapped out
  };

  // Read the disk read/write statistics for the main disk.
  bool DiskStatsReadStats(uint64_t* read_sectors, uint64_t* write_sectors);

  // Parse the content of the vmstats file into |record|.
  bool VmStatsParseStats(const char* stats, struct VmstatRecord* record);

  // Read the vmstats into |stats|.
  bool VmStatsReadStats(struct VmstatRecord* stats);

  MetricsLibraryInterface* metrics_lib_;
  base::FilePath diskstats_path_;
  base::FilePath vmstats_path_;

  // Values observed at the beginning of the collection period.
  uint64_t read_sectors_;
  uint64_t write_sectors_;
  struct VmstatRecord vmstats_;

  double stats_start_time_;
};

#endif  // METRICSD_COLLECTORS_AVERAGED_STATISTICS_COLLECTOR_H_
+99 −0
Original line number Diff line number Diff line
/*
 * 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 "averaged_statistics_collector.h"

#include <inttypes.h>

#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/memory/scoped_ptr.h>
#include <base/strings/stringprintf.h>
#include <gtest/gtest.h>


static const char kFakeDiskStatsFormat[] =
    "    1793     1788    %" PRIu64 "   105580    "
    "    196      175     %" PRIu64 "    30290    "
    "    0    44060   135850\n";
static const uint64_t kFakeReadSectors[] = {80000, 100000};
static const uint64_t kFakeWriteSectors[] = {3000, 4000};


class AveragedStatisticsTest : public testing::Test {
 protected:
  std::string kFakeDiskStats0;
  std::string kFakeDiskStats1;

  virtual void SetUp() {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    disk_stats_path_ = temp_dir_.path().Append("disk_stats");
    collector_.reset(new AveragedStatisticsCollector(
        &metrics_lib_, disk_stats_path_.value(), ""));

    kFakeDiskStats0 = base::StringPrintf(kFakeDiskStatsFormat,
                                         kFakeReadSectors[0],
                                         kFakeWriteSectors[0]);
    kFakeDiskStats1 = base::StringPrintf(kFakeDiskStatsFormat,
                                         kFakeReadSectors[1],
                                         kFakeWriteSectors[1]);

    CreateFakeDiskStatsFile(kFakeDiskStats0);
  }

  // Creates or overwrites an input file containing fake disk stats.
  void CreateFakeDiskStatsFile(const std::string& fake_stats) {
    EXPECT_EQ(base::WriteFile(disk_stats_path_,
                              fake_stats.data(), fake_stats.size()),
              fake_stats.size());
  }

  // Collector used for tests.
  scoped_ptr<AveragedStatisticsCollector> collector_;

  // Temporary directory used for tests.
  base::ScopedTempDir temp_dir_;

  // Path for the fake files.
  base::FilePath disk_stats_path_;

  MetricsLibrary metrics_lib_;
};

TEST_F(AveragedStatisticsTest, ParseDiskStats) {
  uint64_t read_sectors_now, write_sectors_now;
  CreateFakeDiskStatsFile(kFakeDiskStats0);
  ASSERT_TRUE(collector_->DiskStatsReadStats(&read_sectors_now,
                                             &write_sectors_now));
  EXPECT_EQ(read_sectors_now, kFakeReadSectors[0]);
  EXPECT_EQ(write_sectors_now, kFakeWriteSectors[0]);

  CreateFakeDiskStatsFile(kFakeDiskStats1);
  ASSERT_TRUE(collector_->DiskStatsReadStats(&read_sectors_now,
                                             &write_sectors_now));
  EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]);
  EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]);
}

TEST_F(AveragedStatisticsTest, ParseVmStats) {
  static char kVmStats[] = "pswpin 1345\npswpout 8896\n"
    "foo 100\nbar 200\npgmajfault 42\netcetc 300\n";
  struct AveragedStatisticsCollector::VmstatRecord stats;
  EXPECT_TRUE(collector_->VmStatsParseStats(kVmStats, &stats));
  EXPECT_EQ(stats.page_faults, 42);
  EXPECT_EQ(stats.swap_in, 1345);
  EXPECT_EQ(stats.swap_out, 8896);
}
+8 −245

File changed.

Preview size limit exceeded, changes collapsed.

Loading