StatsLogProcessor.cpp revision 8f42ba0e2c70a441bc7821dd32d5bab1c562b062
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#define DEBUG true  // STOPSHIP if true
18#include "Log.h"
19#include "statslog.h"
20
21#include <android-base/file.h>
22#include <dirent.h>
23#include "StatsLogProcessor.h"
24#include "stats_log_util.h"
25#include "android-base/stringprintf.h"
26#include "guardrail/StatsdStats.h"
27#include "metrics/CountMetricProducer.h"
28#include "external/StatsPullerManager.h"
29#include "stats_util.h"
30#include "storage/StorageManager.h"
31
32#include <log/log_event_list.h>
33#include <utils/Errors.h>
34#include <utils/SystemClock.h>
35
36using namespace android;
37using android::base::StringPrintf;
38using android::util::FIELD_COUNT_REPEATED;
39using android::util::FIELD_TYPE_BOOL;
40using android::util::FIELD_TYPE_FLOAT;
41using android::util::FIELD_TYPE_INT32;
42using android::util::FIELD_TYPE_INT64;
43using android::util::FIELD_TYPE_MESSAGE;
44using android::util::FIELD_TYPE_STRING;
45using android::util::ProtoOutputStream;
46using std::make_unique;
47using std::unique_ptr;
48using std::vector;
49
50namespace android {
51namespace os {
52namespace statsd {
53
54// for ConfigMetricsReportList
55const int FIELD_ID_CONFIG_KEY = 1;
56const int FIELD_ID_REPORTS = 2;
57// for ConfigKey
58const int FIELD_ID_UID = 1;
59const int FIELD_ID_ID = 2;
60// for ConfigMetricsReport
61// const int FIELD_ID_METRICS = 1; // written in MetricsManager.cpp
62const int FIELD_ID_UID_MAP = 2;
63const int FIELD_ID_LAST_REPORT_ELAPSED_NANOS = 3;
64const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4;
65
66#define STATS_DATA_DIR "/data/misc/stats-data"
67
68StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
69                                     const sp<AnomalyMonitor>& anomalyMonitor,
70                                     const long timeBaseSec,
71                                     const std::function<void(const ConfigKey&)>& sendBroadcast)
72    : mUidMap(uidMap),
73      mAnomalyMonitor(anomalyMonitor),
74      mSendBroadcast(sendBroadcast),
75      mTimeBaseSec(timeBaseSec),
76      mLastLogTimestamp(0) {
77    StatsPullerManager statsPullerManager;
78    statsPullerManager.SetTimeBaseSec(mTimeBaseSec);
79}
80
81StatsLogProcessor::~StatsLogProcessor() {
82}
83
84void StatsLogProcessor::onAnomalyAlarmFired(
85        const uint64_t timestampNs,
86        unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet) {
87    std::lock_guard<std::mutex> lock(mMetricsMutex);
88    for (const auto& itr : mMetricsManagers) {
89        itr.second->onAnomalyAlarmFired(timestampNs, anomalySet);
90    }
91}
92
93void updateUid(Value* value, int hostUid) {
94    int uid = value->int_value;
95    if (uid != hostUid) {
96        value->setInt(hostUid);
97    }
98}
99
100void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
101    if (android::util::kAtomsWithAttributionChain.find(event->GetTagId()) !=
102        android::util::kAtomsWithAttributionChain.end()) {
103        for (auto& value : *(event->getMutableValues())) {
104            if (value.mField.getPosAtDepth(0) > kAttributionField) {
105                break;
106            }
107            if (isAttributionUidField(value)) {
108                const int hostUid = mUidMap->getHostUidOrSelf(value.mValue.int_value);
109                updateUid(&value.mValue, hostUid);
110            }
111        }
112    } else if (android::util::kAtomsWithUidField.find(event->GetTagId()) !=
113                       android::util::kAtomsWithUidField.end() &&
114               event->getValues().size() > 0 && (event->getValues())[0].mValue.getType() == INT) {
115        Value& value = (*event->getMutableValues())[0].mValue;
116        const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
117        updateUid(&value, hostUid);
118    }
119}
120
121void StatsLogProcessor::onIsolatedUidChangedEventLocked(const LogEvent& event) {
122    status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR;
123    bool is_create = event.GetBool(3, &err);
124    auto parent_uid = int(event.GetLong(1, &err2));
125    auto isolated_uid = int(event.GetLong(2, &err3));
126    if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) {
127        if (is_create) {
128            mUidMap->assignIsolatedUid(isolated_uid, parent_uid);
129        } else {
130            mUidMap->removeIsolatedUid(isolated_uid, parent_uid);
131        }
132    } else {
133        ALOGE("Failed to parse uid in the isolated uid change event.");
134    }
135}
136
137void StatsLogProcessor::OnLogEvent(LogEvent* event) {
138    std::lock_guard<std::mutex> lock(mMetricsMutex);
139    if (event->GetElapsedTimestampNs() < mLastLogTimestamp) {
140        return;
141    }
142    mLastLogTimestamp = event->GetElapsedTimestampNs();
143    StatsdStats::getInstance().noteAtomLogged(
144        event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC);
145
146    // Hard-coded logic to update the isolated uid's in the uid-map.
147    // The field numbers need to be currently updated by hand with atoms.proto
148    if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) {
149        onIsolatedUidChangedEventLocked(*event);
150    }
151
152    if (mMetricsManagers.empty()) {
153        return;
154    }
155
156    uint64_t curTimeSec = getElapsedRealtimeSec();
157    if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
158        mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec);
159        mLastPullerCacheClearTimeSec = curTimeSec;
160    }
161
162    if (event->GetTagId() != android::util::ISOLATED_UID_CHANGED) {
163        // Map the isolated uid to host uid if necessary.
164        mapIsolatedUidToHostUidIfNecessaryLocked(event);
165    }
166
167    // pass the event to metrics managers.
168    for (auto& pair : mMetricsManagers) {
169        pair.second->onLogEvent(*event);
170        flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second));
171    }
172}
173
174void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
175    std::lock_guard<std::mutex> lock(mMetricsMutex);
176    VLOG("Updated configuration for key %s", key.ToString().c_str());
177    sp<MetricsManager> newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap);
178    auto it = mMetricsManagers.find(key);
179    if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
180        ALOGE("Can't accept more configs!");
181        return;
182    }
183
184    if (newMetricsManager->isConfigValid()) {
185        mUidMap->OnConfigUpdated(key);
186        newMetricsManager->setAnomalyMonitor(mAnomalyMonitor);
187        if (newMetricsManager->shouldAddUidMapListener()) {
188            // We have to add listener after the MetricsManager is constructed because it's
189            // not safe to create wp or sp from this pointer inside its constructor.
190            mUidMap->addListener(newMetricsManager.get());
191        }
192        mMetricsManagers[key] = newMetricsManager;
193        // Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
194        VLOG("StatsdConfig valid");
195    } else {
196        // If there is any error in the config, don't use it.
197        ALOGE("StatsdConfig NOT valid");
198    }
199}
200
201size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const {
202    std::lock_guard<std::mutex> lock(mMetricsMutex);
203    auto it = mMetricsManagers.find(key);
204    if (it == mMetricsManagers.end()) {
205        ALOGW("Config source %s does not exist", key.ToString().c_str());
206        return 0;
207    }
208    return it->second->byteSize();
209}
210
211void StatsLogProcessor::dumpStates(FILE* out, bool verbose) {
212    std::lock_guard<std::mutex> lock(mMetricsMutex);
213    fprintf(out, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size());
214    for (auto metricsManager : mMetricsManagers) {
215        metricsManager.second->dumpStates(out, verbose);
216    }
217}
218
219void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t dumpTimeStampNs,
220                                     vector<uint8_t>* outData) {
221    std::lock_guard<std::mutex> lock(mMetricsMutex);
222    onDumpReportLocked(key, dumpTimeStampNs, outData);
223}
224
225void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeStampNs,
226                                           vector<uint8_t>* outData) {
227    auto it = mMetricsManagers.find(key);
228    if (it == mMetricsManagers.end()) {
229        ALOGW("Config source %s does not exist", key.ToString().c_str());
230        return;
231    }
232
233    // This allows another broadcast to be sent within the rate-limit period if we get close to
234    // filling the buffer again soon.
235    mLastBroadcastTimes.erase(key);
236
237    ProtoOutputStream proto;
238
239    // Start of ConfigKey.
240    long long configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
241    proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
242    proto.write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId());
243    proto.end(configKeyToken);
244    // End of ConfigKey.
245
246    // Start of ConfigMetricsReport (reports).
247    long long reportsToken =
248            proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
249
250    int64_t lastReportTimeNs = it->second->getLastReportTimeNs();
251    // First, fill in ConfigMetricsReport using current data on memory, which
252    // starts from filling in StatsLogReport's.
253    it->second->onDumpReport(dumpTimeStampNs, &proto);
254
255    // Fill in UidMap.
256    auto uidMap = mUidMap->getOutput(key);
257    const int uidMapSize = uidMap.ByteSize();
258    char uidMapBuffer[uidMapSize];
259    uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize);
260    proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize);
261
262    // Fill in the timestamps.
263    proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS,
264                (long long)lastReportTimeNs);
265    proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS,
266                (long long)dumpTimeStampNs);
267
268    // End of ConfigMetricsReport (reports).
269    proto.end(reportsToken);
270
271    // Then, check stats-data directory to see there's any file containing
272    // ConfigMetricsReport from previous shutdowns to concatenate to reports.
273    StorageManager::appendConfigMetricsReport(proto);
274
275    if (outData != nullptr) {
276        outData->clear();
277        outData->resize(proto.size());
278        size_t pos = 0;
279        auto iter = proto.data();
280        while (iter.readBuffer() != NULL) {
281            size_t toRead = iter.currentToRead();
282            std::memcpy(&((*outData)[pos]), iter.readBuffer(), toRead);
283            pos += toRead;
284            iter.rp()->move(toRead);
285        }
286    }
287
288    StatsdStats::getInstance().noteMetricsReportSent(key);
289}
290
291void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
292    std::lock_guard<std::mutex> lock(mMetricsMutex);
293    auto it = mMetricsManagers.find(key);
294    if (it != mMetricsManagers.end()) {
295        mMetricsManagers.erase(it);
296        mUidMap->OnConfigRemoved(key);
297    }
298    StatsdStats::getInstance().noteConfigRemoved(key);
299
300    mLastBroadcastTimes.erase(key);
301
302    if (mMetricsManagers.empty()) {
303        mStatsPullerManager.ForceClearPullerCache();
304    }
305}
306
307void StatsLogProcessor::flushIfNecessaryLocked(
308    uint64_t timestampNs, const ConfigKey& key, MetricsManager& metricsManager) {
309    auto lastCheckTime = mLastByteSizeTimes.find(key);
310    if (lastCheckTime != mLastByteSizeTimes.end()) {
311        if (timestampNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) {
312            return;
313        }
314    }
315
316    // We suspect that the byteSize() computation is expensive, so we set a rate limit.
317    size_t totalBytes = metricsManager.byteSize();
318    mLastByteSizeTimes[key] = timestampNs;
319    if (totalBytes >
320        StatsdStats::kMaxMetricsBytesPerConfig) {  // Too late. We need to start clearing data.
321        // TODO(b/70571383): By 12/15/2017 add API to drop data directly
322        ProtoOutputStream proto;
323        metricsManager.onDumpReport(time(nullptr) * NS_PER_SEC, &proto);
324        StatsdStats::getInstance().noteDataDropped(key);
325        VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
326    } else if (totalBytes > .9 * StatsdStats::kMaxMetricsBytesPerConfig) {
327        // Send broadcast so that receivers can pull data.
328        auto lastBroadcastTime = mLastBroadcastTimes.find(key);
329        if (lastBroadcastTime != mLastBroadcastTimes.end()) {
330            if (timestampNs - lastBroadcastTime->second < StatsdStats::kMinBroadcastPeriodNs) {
331                VLOG("StatsD would've sent a broadcast but the rate limit stopped us.");
332                return;
333            }
334        }
335        mLastBroadcastTimes[key] = timestampNs;
336        VLOG("StatsD requesting broadcast for %s", key.ToString().c_str());
337        mSendBroadcast(key);
338        StatsdStats::getInstance().noteBroadcastSent(key);
339    }
340}
341
342void StatsLogProcessor::WriteDataToDisk() {
343    std::lock_guard<std::mutex> lock(mMetricsMutex);
344    for (auto& pair : mMetricsManagers) {
345        const ConfigKey& key = pair.first;
346        vector<uint8_t> data;
347        onDumpReportLocked(key, time(nullptr) * NS_PER_SEC, &data);
348        // TODO: Add a guardrail to prevent accumulation of file on disk.
349        string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR,
350             (long)getWallClockSec(), key.GetUid(), (long long)key.GetId());
351        StorageManager::writeFile(file_name.c_str(), &data[0], data.size());
352    }
353}
354
355}  // namespace statsd
356}  // namespace os
357}  // namespace android
358