StatsdStats.cpp revision 484524a246ffe453f8cd89b698a279c23b0bde1f
1/*
2 * Copyright 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#define DEBUG false  // STOPSHIP if true
17#include "Log.h"
18
19#include "StatsdStats.h"
20
21#include <android/util/ProtoOutputStream.h>
22#include "../stats_log_util.h"
23#include "statslog.h"
24
25namespace android {
26namespace os {
27namespace statsd {
28
29using android::util::FIELD_COUNT_REPEATED;
30using android::util::FIELD_TYPE_BOOL;
31using android::util::FIELD_TYPE_FLOAT;
32using android::util::FIELD_TYPE_INT32;
33using android::util::FIELD_TYPE_INT64;
34using android::util::FIELD_TYPE_MESSAGE;
35using android::util::FIELD_TYPE_STRING;
36using android::util::ProtoOutputStream;
37using std::lock_guard;
38using std::map;
39using std::string;
40using std::vector;
41
42const int FIELD_ID_BEGIN_TIME = 1;
43const int FIELD_ID_END_TIME = 2;
44const int FIELD_ID_CONFIG_STATS = 3;
45const int FIELD_ID_ATOM_STATS = 7;
46const int FIELD_ID_UIDMAP_STATS = 8;
47const int FIELD_ID_ANOMALY_ALARM_STATS = 9;
48const int FIELD_ID_PULLED_ATOM_STATS = 10;
49const int FIELD_ID_LOGGER_ERROR_STATS = 11;
50
51const int FIELD_ID_MATCHER_STATS_NAME = 1;
52const int FIELD_ID_MATCHER_STATS_COUNT = 2;
53
54const int FIELD_ID_CONDITION_STATS_NAME = 1;
55const int FIELD_ID_CONDITION_STATS_COUNT = 2;
56
57const int FIELD_ID_METRIC_STATS_NAME = 1;
58const int FIELD_ID_METRIC_STATS_COUNT = 2;
59
60const int FIELD_ID_ATOM_STATS_TAG = 1;
61const int FIELD_ID_ATOM_STATS_COUNT = 2;
62
63const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1;
64
65const int FIELD_ID_LOGGER_STATS_TIME = 1;
66const int FIELD_ID_LOGGER_STATS_ERROR_CODE = 2;
67
68std::map<int, long> StatsdStats::kPullerCooldownMap = {
69        {android::util::KERNEL_WAKELOCK, 1},
70        {android::util::WIFI_BYTES_TRANSFER, 1},
71        {android::util::MOBILE_BYTES_TRANSFER, 1},
72        {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG, 1},
73        {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG, 1},
74        {android::util::SUBSYSTEM_SLEEP_STATE, 1},
75        {android::util::CPU_TIME_PER_FREQ, 1},
76        {android::util::CPU_TIME_PER_UID, 1},
77        {android::util::CPU_TIME_PER_UID_FREQ, 1},
78};
79
80// TODO: add stats for pulled atoms.
81StatsdStats::StatsdStats() {
82    mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1);
83    mStartTimeSec = time(nullptr);
84}
85
86StatsdStats& StatsdStats::getInstance() {
87    static StatsdStats statsInstance;
88    return statsInstance;
89}
90
91void StatsdStats::addToIceBoxLocked(const StatsdStatsReport_ConfigStats& stats) {
92    // The size of mIceBox grows strictly by one at a time. It won't be > kMaxIceBoxSize.
93    if (mIceBox.size() == kMaxIceBoxSize) {
94        mIceBox.pop_front();
95    }
96    mIceBox.push_back(stats);
97}
98
99void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount,
100                                     int matchersCount, int alertsCount, bool isValid) {
101    lock_guard<std::mutex> lock(mLock);
102    int32_t nowTimeSec = time(nullptr);
103
104    // If there is an existing config for the same key, icebox the old config.
105    noteConfigRemovedInternalLocked(key);
106
107    StatsdStatsReport_ConfigStats configStats;
108    configStats.set_uid(key.GetUid());
109    configStats.set_id(key.GetId());
110    configStats.set_creation_time_sec(nowTimeSec);
111    configStats.set_metric_count(metricsCount);
112    configStats.set_condition_count(conditionsCount);
113    configStats.set_matcher_count(matchersCount);
114    configStats.set_alert_count(alertsCount);
115    configStats.set_is_valid(isValid);
116
117    if (isValid) {
118        mConfigStats[key] = configStats;
119    } else {
120        configStats.set_deletion_time_sec(nowTimeSec);
121        addToIceBoxLocked(configStats);
122    }
123}
124
125void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) {
126    auto it = mConfigStats.find(key);
127    if (it != mConfigStats.end()) {
128        int32_t nowTimeSec = time(nullptr);
129        it->second.set_deletion_time_sec(nowTimeSec);
130        // Add condition stats, metrics stats, matcher stats, alert stats
131        addSubStatsToConfigLocked(key, it->second);
132        // Remove them after they are added to the config stats.
133        mMatcherStats.erase(key);
134        mMetricsStats.erase(key);
135        mAlertStats.erase(key);
136        mConditionStats.erase(key);
137        addToIceBoxLocked(it->second);
138        mConfigStats.erase(it);
139    }
140}
141
142void StatsdStats::noteConfigRemoved(const ConfigKey& key) {
143    lock_guard<std::mutex> lock(mLock);
144    noteConfigRemovedInternalLocked(key);
145}
146
147void StatsdStats::noteBroadcastSent(const ConfigKey& key) {
148    noteBroadcastSent(key, time(nullptr));
149}
150
151void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) {
152    lock_guard<std::mutex> lock(mLock);
153    auto it = mConfigStats.find(key);
154    if (it == mConfigStats.end()) {
155        ALOGE("Config key %s not found!", key.ToString().c_str());
156        return;
157    }
158    if (it->second.broadcast_sent_time_sec_size() >= kMaxTimestampCount) {
159        auto timestampList = it->second.mutable_broadcast_sent_time_sec();
160        // This is O(N) operation. It shouldn't happen often, and N is only 20.
161        timestampList->erase(timestampList->begin());
162    }
163    it->second.add_broadcast_sent_time_sec(timeSec);
164}
165
166void StatsdStats::noteDataDropped(const ConfigKey& key) {
167    noteDataDropped(key, time(nullptr));
168}
169
170void StatsdStats::noteDataDropped(const ConfigKey& key, int32_t timeSec) {
171    lock_guard<std::mutex> lock(mLock);
172    auto it = mConfigStats.find(key);
173    if (it == mConfigStats.end()) {
174        ALOGE("Config key %s not found!", key.ToString().c_str());
175        return;
176    }
177    if (it->second.data_drop_time_sec_size() >= kMaxTimestampCount) {
178        auto timestampList = it->second.mutable_data_drop_time_sec();
179        // This is O(N) operation. It shouldn't happen often, and N is only 20.
180        timestampList->erase(timestampList->begin());
181    }
182    it->second.add_data_drop_time_sec(timeSec);
183}
184
185void StatsdStats::noteMetricsReportSent(const ConfigKey& key) {
186    noteMetricsReportSent(key, time(nullptr));
187}
188
189void StatsdStats::noteMetricsReportSent(const ConfigKey& key, int32_t timeSec) {
190    lock_guard<std::mutex> lock(mLock);
191    auto it = mConfigStats.find(key);
192    if (it == mConfigStats.end()) {
193        ALOGE("Config key %s not found!", key.ToString().c_str());
194        return;
195    }
196    if (it->second.dump_report_time_sec_size() >= kMaxTimestampCount) {
197        auto timestampList = it->second.mutable_dump_report_time_sec();
198        // This is O(N) operation. It shouldn't happen often, and N is only 20.
199        timestampList->erase(timestampList->begin());
200    }
201    it->second.add_dump_report_time_sec(timeSec);
202}
203
204void StatsdStats::noteUidMapDropped(int snapshots, int deltas) {
205    lock_guard<std::mutex> lock(mLock);
206    mUidMapStats.set_dropped_snapshots(mUidMapStats.dropped_snapshots() + snapshots);
207    mUidMapStats.set_dropped_changes(mUidMapStats.dropped_changes() + deltas);
208}
209
210void StatsdStats::setUidMapSnapshots(int snapshots) {
211    lock_guard<std::mutex> lock(mLock);
212    mUidMapStats.set_snapshots(snapshots);
213}
214
215void StatsdStats::setUidMapChanges(int changes) {
216    lock_guard<std::mutex> lock(mLock);
217    mUidMapStats.set_changes(changes);
218}
219
220void StatsdStats::setCurrentUidMapMemory(int bytes) {
221    lock_guard<std::mutex> lock(mLock);
222    mUidMapStats.set_bytes_used(bytes);
223}
224
225void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
226    lock_guard<std::mutex> lock(mLock);
227    // if name doesn't exist before, it will create the key with count 0.
228    auto& conditionSizeMap = mConditionStats[key];
229    if (size > conditionSizeMap[id]) {
230        conditionSizeMap[id] = size;
231    }
232}
233
234void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
235    lock_guard<std::mutex> lock(mLock);
236    // if name doesn't exist before, it will create the key with count 0.
237    auto& metricsDimensionMap = mMetricsStats[key];
238    if (size > metricsDimensionMap[id]) {
239        metricsDimensionMap[id] = size;
240    }
241}
242
243void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) {
244    lock_guard<std::mutex> lock(mLock);
245    auto& matcherStats = mMatcherStats[key];
246    matcherStats[id]++;
247}
248
249void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t& id) {
250    lock_guard<std::mutex> lock(mLock);
251    auto& alertStats = mAlertStats[key];
252    alertStats[id]++;
253}
254
255void StatsdStats::noteRegisteredAnomalyAlarmChanged() {
256    lock_guard<std::mutex> lock(mLock);
257    mAnomalyAlarmRegisteredStats++;
258}
259
260void StatsdStats::updateMinPullIntervalSec(int pullAtomId, long intervalSec) {
261    lock_guard<std::mutex> lock(mLock);
262    mPulledAtomStats[pullAtomId].minPullIntervalSec = intervalSec;
263}
264
265void StatsdStats::notePull(int pullAtomId) {
266    lock_guard<std::mutex> lock(mLock);
267    mPulledAtomStats[pullAtomId].totalPull++;
268}
269
270void StatsdStats::notePullFromCache(int pullAtomId) {
271    lock_guard<std::mutex> lock(mLock);
272    mPulledAtomStats[pullAtomId].totalPullFromCache++;
273}
274
275void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
276    lock_guard<std::mutex> lock(mLock);
277
278    if (timeSec < mStartTimeSec) {
279        return;
280    }
281
282    if (atomId > android::util::kMaxPushedAtomId) {
283        ALOGW("not interested in atom %d", atomId);
284        return;
285    }
286
287    mPushedAtomStats[atomId]++;
288}
289
290void StatsdStats::noteLoggerError(int error) {
291    lock_guard<std::mutex> lock(mLock);
292    // grows strictly one at a time. so it won't > kMaxLoggerErrors
293    if (mLoggerErrors.size() == kMaxLoggerErrors) {
294        mLoggerErrors.pop_front();
295    }
296    mLoggerErrors.push_back(std::make_pair(time(nullptr), error));
297}
298
299void StatsdStats::reset() {
300    lock_guard<std::mutex> lock(mLock);
301    resetInternalLocked();
302}
303
304void StatsdStats::resetInternalLocked() {
305    // Reset the historical data, but keep the active ConfigStats
306    mStartTimeSec = time(nullptr);
307    mIceBox.clear();
308    mConditionStats.clear();
309    mMetricsStats.clear();
310    std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0);
311    mAlertStats.clear();
312    mAnomalyAlarmRegisteredStats = 0;
313    mMatcherStats.clear();
314    mLoggerErrors.clear();
315    for (auto& config : mConfigStats) {
316        config.second.clear_broadcast_sent_time_sec();
317        config.second.clear_data_drop_time_sec();
318        config.second.clear_dump_report_time_sec();
319        config.second.clear_matcher_stats();
320        config.second.clear_condition_stats();
321        config.second.clear_metric_stats();
322        config.second.clear_alert_stats();
323    }
324}
325
326void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key,
327                                      StatsdStatsReport_ConfigStats& configStats) {
328    // Add matcher stats
329    if (mMatcherStats.find(key) != mMatcherStats.end()) {
330        const auto& matcherStats = mMatcherStats[key];
331        for (const auto& stats : matcherStats) {
332            auto output = configStats.add_matcher_stats();
333            output->set_id(stats.first);
334            output->set_matched_times(stats.second);
335            VLOG("matcher %lld matched %d times",
336                (long long)stats.first, stats.second);
337        }
338    }
339    // Add condition stats
340    if (mConditionStats.find(key) != mConditionStats.end()) {
341        const auto& conditionStats = mConditionStats[key];
342        for (const auto& stats : conditionStats) {
343            auto output = configStats.add_condition_stats();
344            output->set_id(stats.first);
345            output->set_max_tuple_counts(stats.second);
346            VLOG("condition %lld max output tuple size %d",
347                (long long)stats.first, stats.second);
348        }
349    }
350    // Add metrics stats
351    if (mMetricsStats.find(key) != mMetricsStats.end()) {
352        const auto& conditionStats = mMetricsStats[key];
353        for (const auto& stats : conditionStats) {
354            auto output = configStats.add_metric_stats();
355            output->set_id(stats.first);
356            output->set_max_tuple_counts(stats.second);
357            VLOG("metrics %lld max output tuple size %d",
358                (long long)stats.first, stats.second);
359        }
360    }
361    // Add anomaly detection alert stats
362    if (mAlertStats.find(key) != mAlertStats.end()) {
363        const auto& alertStats = mAlertStats[key];
364        for (const auto& stats : alertStats) {
365            auto output = configStats.add_alert_stats();
366            output->set_id(stats.first);
367            output->set_alerted_times(stats.second);
368            VLOG("alert %lld declared %d times", (long long)stats.first, stats.second);
369        }
370    }
371}
372
373void StatsdStats::dumpStats(FILE* out) const {
374    lock_guard<std::mutex> lock(mLock);
375    time_t t = mStartTimeSec;
376    struct tm* tm = localtime(&t);
377    char timeBuffer[80];
378    strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p\n", tm);
379    fprintf(out, "Stats collection start second: %s\n", timeBuffer);
380    fprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size());
381    for (const auto& configStats : mIceBox) {
382        fprintf(out,
383                "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
384                "#matcher=%d, #alert=%d,  valid=%d\n",
385                configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
386                configStats.deletion_time_sec(), configStats.metric_count(),
387                configStats.condition_count(), configStats.matcher_count(),
388                configStats.alert_count(), configStats.is_valid());
389
390        for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
391            fprintf(out, "\tbroadcast time: %d\n", broadcastTime);
392        }
393
394        for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
395            fprintf(out, "\tdata drop time: %d\n", dataDropTime);
396        }
397    }
398    fprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size());
399    for (auto& pair : mConfigStats) {
400        auto& key = pair.first;
401        auto& configStats = pair.second;
402
403        fprintf(out,
404                "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
405                "#matcher=%d, #alert=%d,  valid=%d\n",
406                configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
407                configStats.deletion_time_sec(), configStats.metric_count(),
408                configStats.condition_count(), configStats.matcher_count(),
409                configStats.alert_count(), configStats.is_valid());
410        for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
411            fprintf(out, "\tbroadcast time: %d\n", broadcastTime);
412        }
413
414        for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
415            fprintf(out, "\tdata drop time: %d\n", dataDropTime);
416        }
417
418        for (const auto& dumpTime : configStats.dump_report_time_sec()) {
419            fprintf(out, "\tdump report time: %d\n", dumpTime);
420        }
421
422        // Add matcher stats
423        auto matcherIt = mMatcherStats.find(key);
424        if (matcherIt != mMatcherStats.end()) {
425            const auto& matcherStats = matcherIt->second;
426            for (const auto& stats : matcherStats) {
427                fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first,
428                        stats.second);
429            }
430        }
431        // Add condition stats
432        auto conditionIt = mConditionStats.find(key);
433        if (conditionIt != mConditionStats.end()) {
434            const auto& conditionStats = conditionIt->second;
435            for (const auto& stats : conditionStats) {
436                fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first,
437                        stats.second);
438            }
439        }
440        // Add metrics stats
441        auto metricIt = mMetricsStats.find(key);
442        if (metricIt != mMetricsStats.end()) {
443            const auto& conditionStats = metricIt->second;
444            for (const auto& stats : conditionStats) {
445                fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first,
446                        stats.second);
447            }
448        }
449        // Add anomaly detection alert stats
450        auto alertIt = mAlertStats.find(key);
451        if (alertIt != mAlertStats.end()) {
452            const auto& alertStats = alertIt->second;
453            for (const auto& stats : alertStats) {
454                fprintf(out, "alert %lld declared %d times\n", (long long)stats.first,
455                        stats.second);
456            }
457        }
458    }
459    fprintf(out, "********Pushed Atom stats***********\n");
460    const size_t atomCounts = mPushedAtomStats.size();
461    for (size_t i = 2; i < atomCounts; i++) {
462        if (mPushedAtomStats[i] > 0) {
463            fprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
464        }
465    }
466
467    fprintf(out, "********Pulled Atom stats***********\n");
468    for (const auto& pair : mPulledAtomStats) {
469        fprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull,
470             (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec);
471    }
472
473    if (mAnomalyAlarmRegisteredStats > 0) {
474        fprintf(out, "********AnomalyAlarmStats stats***********\n");
475        fprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats);
476    }
477
478    fprintf(out,
479            "UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes "
480            "lost=%d\n",
481            mUidMapStats.bytes_used(), mUidMapStats.snapshots(), mUidMapStats.changes(),
482            mUidMapStats.dropped_snapshots(), mUidMapStats.dropped_changes());
483
484    for (const auto& error : mLoggerErrors) {
485        time_t error_time = error.first;
486        struct tm* error_tm = localtime(&error_time);
487        char buffer[80];
488        strftime(buffer, sizeof(buffer), "%Y-%m-%d %I:%M%p\n", error_tm);
489        fprintf(out, "Logger error %d at %s\n", error.second, buffer);
490    }
491}
492
493void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
494    lock_guard<std::mutex> lock(mLock);
495
496    ProtoOutputStream proto;
497    proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTimeSec);
498    proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)time(nullptr));
499
500    for (const auto& configStats : mIceBox) {
501        const int numBytes = configStats.ByteSize();
502        vector<char> buffer(numBytes);
503        configStats.SerializeToArray(&buffer[0], numBytes);
504        proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0],
505                    buffer.size());
506    }
507
508    for (auto& pair : mConfigStats) {
509        auto& configStats = pair.second;
510        addSubStatsToConfigLocked(pair.first, configStats);
511
512        const int numBytes = configStats.ByteSize();
513        vector<char> buffer(numBytes);
514        configStats.SerializeToArray(&buffer[0], numBytes);
515        proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0],
516                    buffer.size());
517        // reset the sub stats, the source of truth is in the individual map
518        // they will be repopulated when dumpStats() is called again.
519        configStats.clear_matcher_stats();
520        configStats.clear_condition_stats();
521        configStats.clear_metric_stats();
522        configStats.clear_alert_stats();
523    }
524
525    const size_t atomCounts = mPushedAtomStats.size();
526    for (size_t i = 2; i < atomCounts; i++) {
527        if (mPushedAtomStats[i] > 0) {
528            long long token =
529                    proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
530            proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i);
531            proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]);
532            proto.end(token);
533        }
534    }
535
536    for (const auto& pair : mPulledAtomStats) {
537        android::os::statsd::writePullerStatsToStream(pair, &proto);
538    }
539
540    if (mAnomalyAlarmRegisteredStats > 0) {
541        long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ANOMALY_ALARM_STATS);
542        proto.write(FIELD_TYPE_INT32 | FIELD_ID_ANOMALY_ALARMS_REGISTERED,
543                    mAnomalyAlarmRegisteredStats);
544        proto.end(token);
545    }
546
547    const int numBytes = mUidMapStats.ByteSize();
548    vector<char> buffer(numBytes);
549    mUidMapStats.SerializeToArray(&buffer[0], numBytes);
550    proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS, &buffer[0], buffer.size());
551
552    for (const auto& error : mLoggerErrors) {
553        long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_LOGGER_ERROR_STATS |
554                                      FIELD_COUNT_REPEATED);
555        proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOGGER_STATS_TIME, error.first);
556        proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOGGER_STATS_ERROR_CODE, error.second);
557        proto.end(token);
558    }
559
560    output->clear();
561    size_t bufferSize = proto.size();
562    output->resize(bufferSize);
563
564    size_t pos = 0;
565    auto it = proto.data();
566    while (it.readBuffer() != NULL) {
567        size_t toRead = it.currentToRead();
568        std::memcpy(&((*output)[pos]), it.readBuffer(), toRead);
569        pos += toRead;
570        it.rp()->move(toRead);
571    }
572
573    if (reset) {
574        resetInternalLocked();
575    }
576
577    VLOG("reset=%d, returned proto size %lu", reset, (unsigned long)bufferSize);
578}
579
580}  // namespace statsd
581}  // namespace os
582}  // namespace android