blob: e7c26e37ad156bf576e60d8f9f733c22dc0a9c5c [file] [log] [blame]
Ray Essick2e9c63b2017-03-29 15:16:44 -07001/*
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 LOG_TAG "MetricsSummarizer"
18#include <utils/Log.h>
19
20#include <stdlib.h>
21#include <stdint.h>
Ray Essick783bd0d2018-01-11 11:10:35 -080022#include <string>
Ray Essick2e9c63b2017-03-29 15:16:44 -070023#include <inttypes.h>
24
25#include <utils/threads.h>
26#include <utils/Errors.h>
27#include <utils/KeyedVector.h>
28#include <utils/String8.h>
29#include <utils/List.h>
30
31#include <media/IMediaAnalyticsService.h>
32
33#include "MetricsSummarizer.h"
34
35
36namespace android {
37
38#define DEBUG_SORT 0
39#define DEBUG_QUEUE 0
40
41
42MetricsSummarizer::MetricsSummarizer(const char *key)
43 : mIgnorables(NULL)
44{
45 ALOGV("MetricsSummarizer::MetricsSummarizer");
46
47 if (key == NULL) {
48 mKey = key;
49 } else {
50 mKey = strdup(key);
51 }
52
53 mSummaries = new List<MediaAnalyticsItem *>();
54}
55
56MetricsSummarizer::~MetricsSummarizer()
57{
58 ALOGV("MetricsSummarizer::~MetricsSummarizer");
59 if (mKey) {
60 free((void *)mKey);
61 mKey = NULL;
62 }
63
64 // clear the list of items we have saved
65 while (mSummaries->size() > 0) {
66 MediaAnalyticsItem * oitem = *(mSummaries->begin());
67 if (DEBUG_QUEUE) {
68 ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
69 oitem->getKey().c_str(), oitem->getSessionID(),
70 oitem->getTimestamp());
71 }
72 mSummaries->erase(mSummaries->begin());
73 delete oitem;
74 }
75}
76
77// so we know what summarizer we were using
78const char *MetricsSummarizer::getKey() {
79 const char *value = mKey;
80 if (value == NULL) {
81 value = "unknown";
82 }
83 return value;
84}
85
86// should the record be given to this summarizer
87bool MetricsSummarizer::isMine(MediaAnalyticsItem &item)
88{
Ray Essick2e9c63b2017-03-29 15:16:44 -070089 if (mKey == NULL)
90 return true;
Ray Essick783bd0d2018-01-11 11:10:35 -080091 std::string itemKey = item.getKey();
Ray Essickde000852017-04-13 10:16:25 -070092 if (strcmp(mKey, itemKey.c_str()) != 0) {
Ray Essick2e9c63b2017-03-29 15:16:44 -070093 return false;
94 }
Ray Essick2e9c63b2017-03-29 15:16:44 -070095 return true;
96}
97
Ray Essick783bd0d2018-01-11 11:10:35 -080098std::string MetricsSummarizer::dumpSummary(int &slot)
Ray Essick2e9c63b2017-03-29 15:16:44 -070099{
100 return dumpSummary(slot, NULL);
101}
102
Ray Essick783bd0d2018-01-11 11:10:35 -0800103std::string MetricsSummarizer::dumpSummary(int &slot, const char *only)
Ray Essick2e9c63b2017-03-29 15:16:44 -0700104{
Ray Essick783bd0d2018-01-11 11:10:35 -0800105 std::string value;
Ray Essick2e9c63b2017-03-29 15:16:44 -0700106
107 List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
108 if (it != mSummaries->end()) {
109 char buf[16]; // enough for "#####: "
110 for (; it != mSummaries->end(); it++) {
111 if (only != NULL && strcmp(only, (*it)->getKey().c_str()) != 0) {
112 continue;
113 }
Ray Essick783bd0d2018-01-11 11:10:35 -0800114 std::string entry = (*it)->toString();
Ray Essick2e9c63b2017-03-29 15:16:44 -0700115 snprintf(buf, sizeof(buf), "%5d: ", slot);
116 value.append(buf);
117 value.append(entry.c_str());
118 value.append("\n");
119 slot++;
120 }
121 }
122 return value;
123}
124
125void MetricsSummarizer::setIgnorables(const char **ignorables) {
126 mIgnorables = ignorables;
127}
128
129const char **MetricsSummarizer::getIgnorables() {
130 return mIgnorables;
131}
132
133void MetricsSummarizer::handleRecord(MediaAnalyticsItem *item) {
134
135 ALOGV("MetricsSummarizer::handleRecord() for %s",
136 item == NULL ? "<nothing>" : item->toString().c_str());
137
138 if (item == NULL) {
139 return;
140 }
141
142 List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
143 for (; it != mSummaries->end(); it++) {
144 bool good = sameAttributes((*it), item, getIgnorables());
Ray Essick96eaaac2017-08-31 11:45:05 -0700145 ALOGV("Match against %s says %d", (*it)->toString().c_str(), good);
Ray Essick2e9c63b2017-03-29 15:16:44 -0700146 if (good)
147 break;
148 }
149 if (it == mSummaries->end()) {
150 ALOGV("save new record");
Ray Essick96eaaac2017-08-31 11:45:05 -0700151 MediaAnalyticsItem *nitem = item->dup();
152 if (nitem == NULL) {
Ray Essick2e9c63b2017-03-29 15:16:44 -0700153 ALOGE("unable to save MediaMetrics record");
154 }
Ray Essick96eaaac2017-08-31 11:45:05 -0700155 sortProps(nitem);
156 nitem->setInt32("aggregated",1);
157 mergeRecord(*nitem, *item);
158 mSummaries->push_back(nitem);
Ray Essick2e9c63b2017-03-29 15:16:44 -0700159 } else {
160 ALOGV("increment existing record");
Ray Essick96eaaac2017-08-31 11:45:05 -0700161 (*it)->addInt32("aggregated",1);
Ray Essick2e9c63b2017-03-29 15:16:44 -0700162 mergeRecord(*(*it), *item);
163 }
164}
165
166void MetricsSummarizer::mergeRecord(MediaAnalyticsItem &/*have*/, MediaAnalyticsItem &/*item*/) {
167 // default is no further massaging.
168 ALOGV("MetricsSummarizer::mergeRecord() [default]");
169 return;
170}
171
Ray Essick96eaaac2017-08-31 11:45:05 -0700172// keep some stats for things: sums, counts, standard deviation
173// the integer version -- all of these pieces are in 64 bits
174void MetricsSummarizer::minMaxVar64(MediaAnalyticsItem &summation, const char *key, int64_t value) {
175 if (key == NULL)
176 return;
177 int len = strlen(key) + 32;
178 char *tmpKey = (char *)malloc(len);
179
180 if (tmpKey == NULL) {
181 return;
182 }
183
184 // N - count of samples
185 snprintf(tmpKey, len, "%s.n", key);
186 summation.addInt64(tmpKey, 1);
187
188 // zero - count of samples that are zero
189 if (value == 0) {
190 snprintf(tmpKey, len, "%s.zero", key);
191 int64_t zero = 0;
192 (void) summation.getInt64(tmpKey,&zero);
193 zero++;
194 summation.setInt64(tmpKey, zero);
195 }
196
197 // min
198 snprintf(tmpKey, len, "%s.min", key);
199 int64_t min = value;
200 if (summation.getInt64(tmpKey,&min)) {
201 if (min > value) {
202 summation.setInt64(tmpKey, value);
203 }
204 } else {
205 summation.setInt64(tmpKey, value);
206 }
207
208 // max
209 snprintf(tmpKey, len, "%s.max", key);
210 int64_t max = value;
211 if (summation.getInt64(tmpKey,&max)) {
212 if (max < value) {
213 summation.setInt64(tmpKey, value);
214 }
215 } else {
216 summation.setInt64(tmpKey, value);
217 }
218
219 // components for mean, stddev;
220 // stddev = sqrt(1/4*(sumx2 - (2*sumx*sumx/n) + ((sumx/n)^2)))
221 // sum x
222 snprintf(tmpKey, len, "%s.sumX", key);
223 summation.addInt64(tmpKey, value);
224 // sum x^2
225 snprintf(tmpKey, len, "%s.sumX2", key);
226 summation.addInt64(tmpKey, value*value);
227
228
229 // last thing we do -- remove the base key from the summation
230 // record so we won't get confused about it having both individual
231 // and summary information in there.
232 summation.removeProp(key);
233
234 free(tmpKey);
235}
236
Ray Essick2e9c63b2017-03-29 15:16:44 -0700237
238//
239// Comparators
240//
241
242// testing that all of 'single' is in 'summ'
243// and that the values match.
244// 'summ' may have extra fields.
245// 'ignorable' is a set of things that we don't worry about matching up
246// (usually time- or count-based values we'll sum elsewhere)
247bool MetricsSummarizer::sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) {
248
249 if (single == NULL || summ == NULL) {
250 return false;
251 }
252 ALOGV("MetricsSummarizer::sameAttributes(): summ %s", summ->toString().c_str());
253 ALOGV("MetricsSummarizer::sameAttributes(): single %s", single->toString().c_str());
254
Ray Essick96eaaac2017-08-31 11:45:05 -0700255 // keep different sources/users separate
256 if (single->mUid != summ->mUid) {
257 return false;
258 }
259
Ray Essick2e9c63b2017-03-29 15:16:44 -0700260 // this can be made better.
261 for(size_t i=0;i<single->mPropCount;i++) {
262 MediaAnalyticsItem::Prop *prop1 = &(single->mProps[i]);
263 const char *attrName = prop1->mName;
Ray Essick2e9c63b2017-03-29 15:16:44 -0700264
265 // is it something we should ignore
266 if (ignorable != NULL) {
267 const char **ig = ignorable;
Ray Essick96eaaac2017-08-31 11:45:05 -0700268 for (;*ig; ig++) {
Ray Essick2e9c63b2017-03-29 15:16:44 -0700269 if (strcmp(*ig, attrName) == 0) {
270 break;
271 }
Ray Essick2e9c63b2017-03-29 15:16:44 -0700272 }
273 if (*ig) {
274 ALOGV("we don't mind that it has attr '%s'", attrName);
275 continue;
276 }
277 }
278
279 MediaAnalyticsItem::Prop *prop2 = summ->findProp(attrName);
280 if (prop2 == NULL) {
281 ALOGV("summ doesn't have this attr");
282 return false;
283 }
284 if (prop1->mType != prop2->mType) {
285 ALOGV("mismatched attr types");
286 return false;
287 }
288 switch (prop1->mType) {
289 case MediaAnalyticsItem::kTypeInt32:
Ray Essick96eaaac2017-08-31 11:45:05 -0700290 if (prop1->u.int32Value != prop2->u.int32Value) {
291 ALOGV("mismatch values");
Ray Essick2e9c63b2017-03-29 15:16:44 -0700292 return false;
Ray Essick96eaaac2017-08-31 11:45:05 -0700293 }
Ray Essick2e9c63b2017-03-29 15:16:44 -0700294 break;
295 case MediaAnalyticsItem::kTypeInt64:
Ray Essick96eaaac2017-08-31 11:45:05 -0700296 if (prop1->u.int64Value != prop2->u.int64Value) {
297 ALOGV("mismatch values");
Ray Essick2e9c63b2017-03-29 15:16:44 -0700298 return false;
Ray Essick96eaaac2017-08-31 11:45:05 -0700299 }
Ray Essick2e9c63b2017-03-29 15:16:44 -0700300 break;
301 case MediaAnalyticsItem::kTypeDouble:
302 // XXX: watch out for floating point comparisons!
Ray Essick96eaaac2017-08-31 11:45:05 -0700303 if (prop1->u.doubleValue != prop2->u.doubleValue) {
304 ALOGV("mismatch values");
Ray Essick2e9c63b2017-03-29 15:16:44 -0700305 return false;
Ray Essick96eaaac2017-08-31 11:45:05 -0700306 }
Ray Essick2e9c63b2017-03-29 15:16:44 -0700307 break;
308 case MediaAnalyticsItem::kTypeCString:
Ray Essick96eaaac2017-08-31 11:45:05 -0700309 if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0) {
310 ALOGV("mismatch values");
Ray Essick2e9c63b2017-03-29 15:16:44 -0700311 return false;
Ray Essick96eaaac2017-08-31 11:45:05 -0700312 }
Ray Essick2e9c63b2017-03-29 15:16:44 -0700313 break;
314 case MediaAnalyticsItem::kTypeRate:
Ray Essick96eaaac2017-08-31 11:45:05 -0700315 if (prop1->u.rate.count != prop2->u.rate.count) {
316 ALOGV("mismatch values");
Ray Essick2e9c63b2017-03-29 15:16:44 -0700317 return false;
Ray Essick96eaaac2017-08-31 11:45:05 -0700318 }
319 if (prop1->u.rate.duration != prop2->u.rate.duration) {
320 ALOGV("mismatch values");
Ray Essick2e9c63b2017-03-29 15:16:44 -0700321 return false;
Ray Essick96eaaac2017-08-31 11:45:05 -0700322 }
Ray Essick2e9c63b2017-03-29 15:16:44 -0700323 break;
324 default:
Ray Essick96eaaac2017-08-31 11:45:05 -0700325 ALOGV("mismatch values in default type");
Ray Essick2e9c63b2017-03-29 15:16:44 -0700326 return false;
327 }
328 }
329
330 return true;
331}
332
Ray Essick2e9c63b2017-03-29 15:16:44 -0700333
334int MetricsSummarizer::PropSorter(const void *a, const void *b) {
335 MediaAnalyticsItem::Prop *ai = (MediaAnalyticsItem::Prop *)a;
336 MediaAnalyticsItem::Prop *bi = (MediaAnalyticsItem::Prop *)b;
337 return strcmp(ai->mName, bi->mName);
338}
339
340// we sort in the summaries so that it looks pretty in the dumpsys
341void MetricsSummarizer::sortProps(MediaAnalyticsItem *item) {
342 if (item->mPropCount != 0) {
Ray Essick2e9c63b2017-03-29 15:16:44 -0700343 qsort(item->mProps, item->mPropCount,
344 sizeof(MediaAnalyticsItem::Prop), MetricsSummarizer::PropSorter);
Ray Essick2e9c63b2017-03-29 15:16:44 -0700345 }
346}
347
348} // namespace android