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

Commit c9666de1 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Gauge metric producer."

parents a7d81598 1d4d6860
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ statsd_common_src := \
    src/metrics/duration_helper/OringDurationTracker.cpp \
    src/metrics/duration_helper/MaxDurationTracker.cpp \
    src/metrics/ValueMetricProducer.cpp \
    src/metrics/GaugeMetricProducer.cpp \
    src/metrics/MetricsManager.cpp \
    src/metrics/metrics_manager_util.cpp \
    src/packages/UidMap.cpp \
+16 −1
Original line number Diff line number Diff line
@@ -142,6 +142,9 @@ static StatsdConfig build_fake_config() {
    int KERNEL_WAKELOCK_TAG_ID = 1004;
    int KERNEL_WAKELOCK_NAME_KEY = 4;

    int DEVICE_TEMPERATURE_TAG_ID = 33;
    int DEVICE_TEMPERATURE_KEY = 1;

    // Count Screen ON events.
    CountMetric* metric = config.add_count_metric();
    metric->set_metric_id(1);
@@ -227,7 +230,7 @@ static StatsdConfig build_fake_config() {
    // Duration of screen on time.
    durationMetric = config.add_duration_metric();
    durationMetric->set_metric_id(8);
    durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
    durationMetric->mutable_bucket()->set_bucket_size_millis(10 * 1000L);
    durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
    durationMetric->set_what("SCREEN_IS_ON");

@@ -247,7 +250,19 @@ static StatsdConfig build_fake_config() {
    eventMetric->set_metric_id(9);
    eventMetric->set_what("SCREEN_TURNED_ON");

    // Add an GaugeMetric.
    GaugeMetric* gaugeMetric = config.add_gauge_metric();
    gaugeMetric->set_metric_id(10);
    gaugeMetric->set_what("DEVICE_TEMPERATURE");
    gaugeMetric->set_gauge_field(DEVICE_TEMPERATURE_KEY);
    gaugeMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);

    // Event matchers............
    LogEntryMatcher* temperatureEntryMatcher = config.add_log_entry_matcher();
    temperatureEntryMatcher->set_name("DEVICE_TEMPERATURE");
    temperatureEntryMatcher->mutable_simple_log_entry_matcher()->set_tag(
        DEVICE_TEMPERATURE_TAG_ID);

    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
    eventMatcher->set_name("SCREEN_TURNED_ON");
    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+232 −0
Original line number Diff line number Diff line
/*
* 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.
*/

#define DEBUG true  // STOPSHIP if true
#include "Log.h"

#include "GaugeMetricProducer.h"
#include "stats_util.h"

#include <cutils/log.h>
#include <limits.h>
#include <stdlib.h>

using std::map;
using std::string;
using std::unordered_map;
using std::vector;

namespace android {
namespace os {
namespace statsd {

GaugeMetricProducer::GaugeMetricProducer(const GaugeMetric& metric, const int conditionIndex,
                                         const sp<ConditionWizard>& wizard, const int pullTagId)
    : MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard),
      mMetric(metric),
      mPullTagId(pullTagId) {
    if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
        mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
    } else {
        mBucketSizeNs = kDefaultGaugemBucketSizeNs;
    }

    // TODO: use UidMap if uid->pkg_name is required
    mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());

    if (metric.links().size() > 0) {
        mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                               metric.links().end());
        mConditionSliced = true;
    }

    // Kicks off the puller immediately.
    if (mPullTagId != -1) {
        mStatsPullerManager.RegisterReceiver(mPullTagId, this,
                                             metric.bucket().bucket_size_millis());
    }

    VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
         (long long)mBucketSizeNs, (long long)mStartTimeNs);
}

GaugeMetricProducer::~GaugeMetricProducer() {
    VLOG("~GaugeMetricProducer() called");
}

void GaugeMetricProducer::finish() {
}

static void addSlicedGaugeToReport(const vector<KeyValuePair>& key,
                                   const vector<GaugeBucketInfo>& buckets,
                                   StatsLogReport_GaugeMetricDataWrapper& wrapper) {
    GaugeMetricData* data = wrapper.add_data();
    for (const auto& kv : key) {
        data->add_dimension()->CopyFrom(kv);
    }
    for (const auto& bucket : buckets) {
        data->add_bucket_info()->CopyFrom(bucket);
        VLOG("\t bucket [%lld - %lld] gauge: %lld", bucket.start_bucket_nanos(),
             bucket.end_bucket_nanos(), bucket.gauge());
    }
}

StatsLogReport GaugeMetricProducer::onDumpReport() {
    VLOG("gauge metric %lld dump report now...", mMetric.metric_id());

    StatsLogReport report;
    report.set_metric_id(mMetric.metric_id());
    report.set_start_report_nanos(mStartTimeNs);

    // Dump current bucket if it's stale.
    // If current bucket is still on-going, don't force dump current bucket.
    // In finish(), We can force dump current bucket.
    flushGaugeIfNeededLocked(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
    report.set_end_report_nanos(mCurrentBucketStartTimeNs);

    StatsLogReport_GaugeMetricDataWrapper* wrapper = report.mutable_gauge_metrics();

    for (const auto& pair : mPastBuckets) {
        const HashableDimensionKey& hashableKey = pair.first;
        auto it = mDimensionKeyMap.find(hashableKey);
        if (it == mDimensionKeyMap.end()) {
            ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
            continue;
        }

        VLOG("  dimension key %s", hashableKey.c_str());
        addSlicedGaugeToReport(it->second, pair.second, *wrapper);
    }
    return report;
    // TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped.
}

void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
    AutoMutex _l(mLock);
    VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
    mCondition = conditionMet;

    // Push mode. Nothing to do.
    if (mPullTagId == -1) {
        return;
    }
    // If (1) the condition is not met or (2) we already pulled the gauge metric in the current
    // bucket, do not pull gauge again.
    if (!mCondition || mCurrentSlicedBucket.size() > 0) {
        return;
    }
    vector<std::shared_ptr<LogEvent>> allData;
    if (!mStatsPullerManager.Pull(mPullTagId, &allData)) {
        ALOGE("Stats puller failed for tag: %d", mPullTagId);
        return;
    }
    for (const auto& data : allData) {
        onMatchedLogEvent(0, *data, false /*scheduledPull*/);
    }
    flushGaugeIfNeededLocked(eventTime);
}

void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
    VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
}

long GaugeMetricProducer::getGauge(const LogEvent& event) {
    status_t err = NO_ERROR;
    long val = event.GetLong(mMetric.gauge_field(), &err);
    if (err == NO_ERROR) {
        return val;
    } else {
        VLOG("Can't find value in message.");
        return -1;
    }
}

void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
    AutoMutex mutex(mLock);
    if (allData.size() == 0) {
        return;
    }
    for (const auto& data : allData) {
        onMatchedLogEvent(0, *data, true /*scheduledPull*/);
    }
    uint64_t eventTime = allData.at(0)->GetTimestampNs();
    flushGaugeIfNeededLocked(eventTime);
}

void GaugeMetricProducer::onMatchedLogEventInternal(
        const size_t matcherIndex, const HashableDimensionKey& eventKey,
        const map<string, HashableDimensionKey>& conditionKey, bool condition,
        const LogEvent& event, bool scheduledPull) {
    if (condition == false) {
        return;
    }
    uint64_t eventTimeNs = event.GetTimestampNs();
    if (eventTimeNs < mCurrentBucketStartTimeNs) {
        VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
             (long long)mCurrentBucketStartTimeNs);
        return;
    }

    // For gauge metric, we just simply use the latest guage in the given bucket.
    const long gauge = getGauge(event);
    if (gauge < 0) {
        VLOG("Invalid gauge at event Time: %lld", (long long)eventTimeNs);
        return;
    }
    mCurrentSlicedBucket[eventKey] = gauge;
    if (mPullTagId < 0) {
        flushGaugeIfNeededLocked(eventTimeNs);
    }
}

// When a new matched event comes in, we check if event falls into the current
// bucket. If not, flush the old counter to past buckets and initialize the new
// bucket.
// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
// the GaugeMetricProducer while holding the lock.
void GaugeMetricProducer::flushGaugeIfNeededLocked(const uint64_t eventTimeNs) {
    if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
        VLOG("event time is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
             (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
        return;
    }

    // Adjusts the bucket start time
    int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;

    GaugeBucketInfo info;
    info.set_start_bucket_nanos(mCurrentBucketStartTimeNs);
    info.set_end_bucket_nanos(mCurrentBucketStartTimeNs + mBucketSizeNs);

    for (const auto& slice : mCurrentSlicedBucket) {
        info.set_gauge(slice.second);
        auto& bucketList = mPastBuckets[slice.first];
        bucketList.push_back(info);

        VLOG("gauge metric %lld, dump key value: %s -> %ld", mMetric.metric_id(),
             slice.first.c_str(), slice.second);
    }
    // Reset counters
    mCurrentSlicedBucket.clear();

    mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
    VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
         (long long)mCurrentBucketStartTimeNs);
}

}  // namespace statsd
}  // namespace os
}  // namespace android
+97 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#pragma once

#include <unordered_map>

#include "../condition/ConditionTracker.h"
#include "../external/PullDataReceiver.h"
#include "../external/StatsPullerManager.h"
#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"

namespace android {
namespace os {
namespace statsd {

// This gauge metric producer first register the puller to automatically pull the gauge at the
// beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
// proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric
// producer always reports the guage at the earliest time of the bucket when the condition is met.
class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
    // TODO: Pass in the start time from MetricsManager, it should be consistent
    // for all metrics.
    GaugeMetricProducer(const GaugeMetric& countMetric, const int conditionIndex,
                        const sp<ConditionWizard>& wizard, const int pullTagId);

    virtual ~GaugeMetricProducer();

    // Handles when the pulled data arrives.
    void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;

    void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
    void onSlicedConditionMayChange(const uint64_t eventTime) override;

    void finish() override;

    StatsLogReport onDumpReport() override;

    // TODO: implements it when supporting proto stream.
    size_t byteSize() override {
        return 0;
    };

    // TODO: Implement this later.
    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
    // TODO: Implement this later.
    virtual void notifyAppRemoved(const string& apk, const int uid) override{};

protected:
    void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
                                   const std::map<std::string, HashableDimensionKey>& conditionKey,
                                   bool condition, const LogEvent& event,
                                   bool scheduledPull) override;

private:
    // The default bucket size for gauge metric is 1 second.
    static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000;
    const GaugeMetric mMetric;

    StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
    // tagId for pulled data. -1 if this is not pulled
    const int mPullTagId;

    Mutex mLock;

    // Save the past buckets and we can clear when the StatsLogReport is dumped.
    std::unordered_map<HashableDimensionKey, std::vector<GaugeBucketInfo>> mPastBuckets;

    // The current bucket.
    std::unordered_map<HashableDimensionKey, long> mCurrentSlicedBucket;

    void flushGaugeIfNeededLocked(const uint64_t newEventTime);

    long getGauge(const LogEvent& event);
};

}  // namespace statsd
}  // namespace os
}  // namespace android
+2 −1
Original line number Diff line number Diff line
@@ -146,7 +146,8 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
                auto& metricList = pair->second;
                for (const int metricIndex : metricList) {
                    // pushed metrics are never scheduled pulls
                    mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event, false);
                    mAllMetricProducers[metricIndex]->onMatchedLogEvent(
                        i, event, false /* schedulePull */);
                }
            }
        }
Loading