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