blob: 094163e8f305933ec5cd70dbebfdcb6b32282573 [file] [log] [blame]
// Copyright (C) 2017 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 "android/metrics/PerfStatReporter.h"
#include "android/CommonReportedInfo.h"
#include "android/base/CpuUsage.h"
#include "android/base/files/StdioStream.h"
#include "android/cmdline-option.h"
#include "android/globals.h"
#include "android/metrics/MetricsWriter.h"
#include "android/metrics/PeriodicReporter.h"
#include "android/metrics/TextMetricsWriter.h"
#include "android/metrics/proto/google_logs_publishing.pb.h"
#include "android/metrics/proto/studio_stats.pb.h"
#include "android/utils/debug.h"
#include "android/utils/file_io.h"
#define D(...) do { if (VERBOSE_CHECK(memory)) dprint(__VA_ARGS__); } while (0)
// Report PerfStats metrics in 1 minute but only once.
static const uint32_t kPerfStatReportIntervalMs = 60 * 1000;
static bool sStopping = false;
namespace android {
namespace metrics {
using android::base::CpuTime;
using android::base::CpuUsage;
using android::base::Lock;
using android::base::StdioStream;
using android::base::System;
using std::shared_ptr;
// static
PerfStatReporter::Ptr PerfStatReporter::create(
android::base::Looper* looper,
android::base::Looper::Duration checkIntervalMs) {
auto inst = Ptr(new PerfStatReporter(looper, checkIntervalMs));
return inst;
}
PerfStatReporter::PerfStatReporter(
android::base::Looper* looper,
android::base::Looper::Duration checkIntervalMs)
: mCurrPerfStats(new android_studio::EmulatorPerformanceStats),
mLooper(looper),
mWriter((android_cmdLineOptions->perf_stat != nullptr)
? TextMetricsWriter::create(base::StdioStream(
android_fopen(android_cmdLineOptions->perf_stat,
"w"),
base::StdioStream::kOwner))
: nullptr),
mCheckIntervalMs(checkIntervalMs),
// We use raw pointer to |this| instead of a shared_ptr to avoid cicrular
// ownership. mRecurrentTask promises to cancel any outstanding tasks when
// it's destructed.
mRecurrentTask(
looper,
[this]() {
refreshPerfStats();
dump();
return true;
},
mCheckIntervalMs) {
// Also start up a periodic reporter that takes whatever is in the
// current struct. Report the metrics only once.
using android::metrics::PeriodicReporter;
mMetricsReportingToken = PeriodicReporter::get().addCancelableTask(
kPerfStatReportIntervalMs,
[this](android_studio::AndroidStudioEvent* event) {
if (sStopping)
return false;
android_studio::EmulatorPerformanceStats* perfStatProto =
event->mutable_emulator_performance_stats();
fillProto(perfStatProto);
CommonReportedInfo::setPerformanceStats(perfStatProto);
mMetricsReportingToken.reset();
return true;
});
// Don't call start() here: start() launches a parallel task that calls
// shared_from_this(), which needs at least one shared pointer owning
// |this|. We can't guarantee that until the constructor call returns.
}
PerfStatReporter::~PerfStatReporter() {
stop();
}
void PerfStatReporter::start() {
mRecurrentTask.start();
}
void PerfStatReporter::stop() {
sStopping = true;
mRecurrentTask.stopAndWait();
}
void PerfStatReporter::fillProto(android_studio::EmulatorPerformanceStats* perfStatProto) {
android::base::AutoLock lock(mLock);
*perfStatProto = *mCurrPerfStats;
}
static void fillProtoMemUsage(android_studio::EmulatorPerformanceStats* stats_out);
static void fillProtoCpuUsage(android_studio::EmulatorPerformanceStats* stats_out);
void PerfStatReporter::dump() {
if (mWriter != nullptr) {
wireless_android_play_playlog::LogEvent logEvent;
const auto timeMs = System::get()->getUnixTimeUs() / 1000;
logEvent.set_event_time_ms(timeMs);
android_studio::AndroidStudioEvent event;
android_studio::EmulatorPerformanceStats* perfStatProto =
event.mutable_emulator_performance_stats();
fillProto(perfStatProto);
mWriter->write(event, &logEvent);
}
}
void PerfStatReporter::refreshPerfStats() {
android::base::AutoLock lock(mLock);
mCurrPerfStats->Clear();
uint64_t uptime = System::get()->getProcessTimes().wallClockMs;
mCurrPerfStats->set_process_uptime_us(uptime);
uint64_t guest_uptime = 0; // TODO
mCurrPerfStats->set_guest_uptime_us(guest_uptime);
fillProtoMemUsage(mCurrPerfStats.get());
fillProtoCpuUsage(mCurrPerfStats.get());
}
static void fillProtoMemUsage(android_studio::EmulatorPerformanceStats* stats_out) {
System::MemUsage rawMemUsage = System::get()->getMemUsage();
auto resources = stats_out->mutable_resource_usage();
auto memUsageProto = resources->mutable_memory_usage();
memUsageProto->set_resident_memory(rawMemUsage.resident);
memUsageProto->set_resident_memory_max(rawMemUsage.resident_max);
memUsageProto->set_virtual_memory(rawMemUsage.virt);
memUsageProto->set_virtual_memory_max(rawMemUsage.virt_max);
memUsageProto->set_total_phys_memory(rawMemUsage.total_phys_memory);
memUsageProto->set_total_page_file(rawMemUsage.total_page_file);
if (android_hw->hw_ramSize) {
memUsageProto->set_total_guest_memory(android_hw->hw_ramSize * 1024 * 1024);
}
D("MemoryReport: uptime: %" PRIu64 ", Res/ResMax/Virt/VirtMax: "
"%" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64,
stats_out->process_uptime_us(),
rawMemUsage.resident,
rawMemUsage.resident_max,
rawMemUsage.virt,
rawMemUsage.virt_max);
}
static void fillProtoCpuUsage(android_studio::EmulatorPerformanceStats* stats_out) {
CpuUsage* cpu = CpuUsage::get();
auto resources = stats_out->mutable_resource_usage();
auto mainLoopCpu = resources->mutable_main_loop_slice();
cpu->forEachUsage(
CpuUsage::UsageArea::MainLoop,
[mainLoopCpu](const CpuTime& cpuTime) {
mainLoopCpu->set_wall_time_us(cpuTime.wall_time_us);
mainLoopCpu->set_user_time_us(cpuTime.user_time_us);
mainLoopCpu->set_system_time_us(cpuTime.system_time_us);
});
cpu->forEachUsage(
CpuUsage::UsageArea::Vcpu,
[resources](const CpuTime& cpuTime) {
auto vCpu = resources->add_vcpu_slices();
vCpu->set_wall_time_us(cpuTime.wall_time_us);
vCpu->set_user_time_us(cpuTime.user_time_us);
vCpu->set_system_time_us(cpuTime.system_time_us);
});
}
} // namespace metrics
} // namespace android