StatsLogProcessor.cpp revision 7c334a129e93e405a72e8299a1cd928af079d14f
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "Log.h"
18#include "statslog.h"
19
20#include "StatsLogProcessor.h"
21#include "metrics/CountMetricProducer.h"
22#include "stats_util.h"
23
24#include <log/log_event_list.h>
25#include <utils/Errors.h>
26
27using namespace android;
28using android::util::FIELD_COUNT_REPEATED;
29using android::util::FIELD_TYPE_BOOL;
30using android::util::FIELD_TYPE_FLOAT;
31using android::util::FIELD_TYPE_INT32;
32using android::util::FIELD_TYPE_INT64;
33using android::util::FIELD_TYPE_MESSAGE;
34using android::util::FIELD_TYPE_STRING;
35using android::util::ProtoOutputStream;
36using std::make_unique;
37using std::unique_ptr;
38using std::vector;
39
40namespace android {
41namespace os {
42namespace statsd {
43
44// for ConfigMetricsReport
45const int FIELD_ID_CONFIG_KEY = 1;
46const int FIELD_ID_METRICS = 2;
47const int FIELD_ID_UID_MAP = 3;
48// for ConfigKey
49const int FIELD_ID_UID = 1;
50const int FIELD_ID_NAME = 2;
51
52StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
53                                     const sp<AnomalyMonitor>& anomalyMonitor,
54                                     const std::function<void(const ConfigKey&)>& sendBroadcast)
55    : mUidMap(uidMap), mAnomalyMonitor(anomalyMonitor), mSendBroadcast(sendBroadcast) {
56}
57
58StatsLogProcessor::~StatsLogProcessor() {
59}
60
61void StatsLogProcessor::onAnomalyAlarmFired(
62        const uint64_t timestampNs,
63        unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet) {
64    for (const auto& anomaly : anomalySet) {
65        for (const auto& itr : mMetricsManagers) {
66            itr.second->onAnomalyAlarmFired(timestampNs, anomaly);
67        }
68    }
69}
70
71// TODO: what if statsd service restarts? How do we know what logs are already processed before?
72void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
73    // pass the event to metrics managers.
74    for (auto& pair : mMetricsManagers) {
75        pair.second->onLogEvent(msg);
76        flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second);
77    }
78
79    // Hard-coded logic to update the isolated uid's in the uid-map.
80    // The field numbers need to be currently updated by hand with atoms.proto
81    if (msg.GetTagId() == android::util::ISOLATED_UID_CHANGED) {
82        status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR;
83        bool is_create = msg.GetBool(3, &err);
84        auto parent_uid = int(msg.GetLong(1, &err2));
85        auto isolated_uid = int(msg.GetLong(2, &err3));
86        if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) {
87            if (is_create) {
88                mUidMap->assignIsolatedUid(isolated_uid, parent_uid);
89            } else {
90                mUidMap->removeIsolatedUid(isolated_uid, parent_uid);
91            }
92        }
93    }
94}
95
96void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
97    auto it = mMetricsManagers.find(key);
98    if (it != mMetricsManagers.end()) {
99        it->second->finish();
100    }
101
102    ALOGD("Updated configuration for key %s", key.ToString().c_str());
103
104    unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
105    if (newMetricsManager->isConfigValid()) {
106        mUidMap->OnConfigUpdated(key);
107        newMetricsManager->setAnomalyMonitor(mAnomalyMonitor);
108        mMetricsManagers[key] = std::move(newMetricsManager);
109        // Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
110        ALOGD("StatsdConfig valid");
111    } else {
112        // If there is any error in the config, don't use it.
113        ALOGD("StatsdConfig NOT valid");
114    }
115}
116
117size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const {
118    auto it = mMetricsManagers.find(key);
119    if (it == mMetricsManagers.end()) {
120        ALOGW("Config source %s does not exist", key.ToString().c_str());
121        return 0;
122    }
123    return it->second->byteSize();
124}
125
126void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) {
127    auto it = mMetricsManagers.find(key);
128    if (it == mMetricsManagers.end()) {
129        ALOGW("Config source %s does not exist", key.ToString().c_str());
130        return;
131    }
132
133    // This allows another broadcast to be sent within the rate-limit period if we get close to
134    // filling the buffer again soon.
135    mBroadcastTimesMutex.lock();
136    mLastBroadcastTimes.erase(key);
137    mBroadcastTimesMutex.unlock();
138
139    ProtoOutputStream proto;
140
141    // Fill in ConfigKey.
142    long long configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
143    proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
144    proto.write(FIELD_TYPE_STRING | FIELD_ID_NAME, key.GetName());
145    proto.end(configKeyToken);
146
147    // Fill in StatsLogReport's.
148    for (auto& m : it->second->onDumpReport()) {
149        // Add each vector of StatsLogReport into a repeated field.
150        proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS,
151                    reinterpret_cast<char*>(m.get()->data()), m.get()->size());
152    }
153
154    // Fill in UidMap.
155    auto uidMap = mUidMap->getOutput(key);
156    const int uidMapSize = uidMap.ByteSize();
157    char uidMapBuffer[uidMapSize];
158    uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize);
159    proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize);
160
161    if (outData != nullptr) {
162        outData->clear();
163        outData->resize(proto.size());
164        size_t pos = 0;
165        auto iter = proto.data();
166        while (iter.readBuffer() != NULL) {
167            size_t toRead = iter.currentToRead();
168            std::memcpy(&((*outData)[pos]), iter.readBuffer(), toRead);
169            pos += toRead;
170            iter.rp()->move(toRead);
171        }
172    }
173}
174
175void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
176    auto it = mMetricsManagers.find(key);
177    if (it != mMetricsManagers.end()) {
178        it->second->finish();
179        mMetricsManagers.erase(it);
180        mUidMap->OnConfigRemoved(key);
181    }
182
183    std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
184    mLastBroadcastTimes.erase(key);
185}
186
187void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs,
188                                         const ConfigKey& key,
189                                         const unique_ptr<MetricsManager>& metricsManager) {
190    std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
191
192    size_t totalBytes = metricsManager->byteSize();
193    if (totalBytes > .9 * kMaxSerializedBytes) { // Send broadcast so that receivers can pull data.
194        auto lastFlushNs = mLastBroadcastTimes.find(key);
195        if (lastFlushNs != mLastBroadcastTimes.end()) {
196            if (timestampNs - lastFlushNs->second < kMinBroadcastPeriod) {
197                return;
198            }
199        }
200        mLastBroadcastTimes[key] = timestampNs;
201        ALOGD("StatsD requesting broadcast for %s", key.ToString().c_str());
202        mSendBroadcast(key);
203    } else if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data.
204        // We ignore the return value so we force each metric producer to clear its contents.
205        metricsManager->onDumpReport();
206        ALOGD("StatsD had to toss out metrics for %s", key.ToString().c_str());
207    }
208}
209
210}  // namespace statsd
211}  // namespace os
212}  // namespace android
213