FieldValue.h revision 8a8d16ceea1e5b7a2f8c41e17b5d993035f50f5d
1/*
2 * Copyright (C) 2018 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#pragma once
17
18#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
19
20namespace android {
21namespace os {
22namespace statsd {
23
24class HashableDimensionKey;
25struct Matcher;
26struct Field;
27struct FieldValue;
28
29const int32_t kAttributionField = 1;
30const int32_t kMaxLogDepth = 2;
31const int32_t kLastBitMask = 0x80;
32const int32_t kClearLastBitDeco = 0x7f;
33
34enum Type { INT, LONG, FLOAT, STRING };
35
36
37static int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth) {
38    int32_t field = 0;
39    for (int32_t i = 0; i <= depth; i++) {
40        int32_t shiftBits = 8 * (kMaxLogDepth - i);
41        field |= (pos[i] << shiftBits);
42    }
43
44    if (includeDepth) {
45        field |= (depth << 24);
46    }
47    return field;
48}
49
50static int32_t encodeMatcherMask(int32_t mask[], int32_t depth) {
51    return getEncodedField(mask, depth, false) | 0xff000000;
52}
53
54// Get the encoded field for a leaf with a [field] number at depth 0;
55static int32_t getSimpleField(size_t field) {
56    return ((int32_t)field << 8 * 2);
57}
58
59/**
60 * Field is a wrapper class for 2 integers that represents the field of a log element in its Atom
61 * proto.
62 * [mTag]: the atom id.
63 * [mField]: encoded path from the root (atom) to leaf.
64 *
65 * For example:
66 * WakeLockStateChanged {
67 *    repeated AttributionNode = 1;
68 *    int state = 2;
69 *    string tag = 3;
70 * }
71 * Read from logd, the items are structured as below:
72 * [[[1000, "tag"], [2000, "tag2"],], 2,"hello"]
73 *
74 * When we read through the list, we will encode each field in a 32bit integer.
75 * 8bit segments   |--------|--------|--------|--------|
76 *                    Depth   field0 [L]field1 [L]field1
77 *
78 *  The first 8 bits are the depth of the field. for example, the uid 1000 has depth 2.
79 *  The following 3 8-bit are for the item's position at each level.
80 *  The first bit of each 8bits field is reserved to mark if the item is the last item at that level
81 *  this is to make matching easier later.
82 *
83 *  The above wakelock event is translated into FieldValue pairs.
84 *  0x02010101->1000
85 *  0x02010182->tag
86 *  0x02018201->2000
87 *  0x02018282->tag2
88 *  0x00020000->2
89 *  0x00030000->"hello"
90 *
91 *  This encoding is the building block for the later operations.
92 *  Please see the definition for Matcher below to see how the matching is done.
93 */
94struct Field {
95private:
96    int32_t mTag;
97    int32_t mField;
98
99public:
100    Field(int32_t tag, int32_t pos[], int32_t depth) : mTag(tag) {
101        mField = getEncodedField(pos, depth, true);
102    }
103
104    Field(const Field& from) : mTag(from.getTag()), mField(from.getField()) {
105    }
106
107    Field(int32_t tag, int32_t field) : mTag(tag), mField(field){};
108
109    inline void setField(int32_t field) {
110        mField = field;
111    }
112
113    inline void setTag(int32_t tag) {
114        mTag = tag;
115    }
116
117    inline void decorateLastPos(int32_t depth) {
118        int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth);
119        mField |= mask;
120    }
121
122    inline int32_t getTag() const {
123        return mTag;
124    }
125
126    inline int32_t getDepth() const {
127        return (mField >> 24);
128    }
129
130    inline int32_t getPath(int32_t depth) const {
131        if (depth > 2 || depth < 0) return 0;
132
133        int32_t field = (mField & 0x00ffffff);
134        int32_t mask = 0xffffffff;
135        return (field & (mask << 8 * (kMaxLogDepth - depth)));
136    }
137
138    inline int32_t getPrefix(int32_t depth) const {
139        if (depth == 0) return 0;
140        return getPath(depth - 1);
141    }
142
143    inline int32_t getField() const {
144        return mField;
145    }
146
147    inline int32_t getRawPosAtDepth(int32_t depth) const {
148        int32_t field = (mField & 0x00ffffff);
149        int32_t shift = 8 * (kMaxLogDepth - depth);
150        int32_t mask = 0xff << shift;
151
152        return (field & mask) >> shift;
153    }
154
155    inline int32_t getPosAtDepth(int32_t depth) const {
156        return getRawPosAtDepth(depth) & kClearLastBitDeco;
157    }
158
159    // Check if the first bit of the 8-bit segment for depth is 1
160    inline bool isLastPos(int32_t depth) const {
161        int32_t field = (mField & 0x00ffffff);
162        int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth);
163        return (field & mask) != 0;
164    }
165
166    // if the 8-bit segment is all 0's
167    inline bool isAnyPosMatcher(int32_t depth) const {
168        return getDepth() >= depth && getRawPosAtDepth(depth) == 0;
169    }
170    // if the 8bit is 0x80 (1000 0000)
171    inline bool isLastPosMatcher(int32_t depth) const {
172        return getDepth() >= depth && getRawPosAtDepth(depth) == kLastBitMask;
173    }
174
175    inline bool operator==(const Field& that) const {
176        return mTag == that.getTag() && mField == that.getField();
177    };
178
179    inline bool operator!=(const Field& that) const {
180        return mTag != that.getTag() || mField != that.getField();
181    };
182
183    bool operator<(const Field& that) const {
184        if (mTag != that.getTag()) {
185            return mTag < that.getTag();
186        }
187
188        if (mField != that.getField()) {
189            return mField < that.getField();
190        }
191
192        return false;
193    }
194    bool matches(const Matcher& that) const;
195};
196
197/**
198 * Matcher represents a leaf matcher in the FieldMatcher in statsd_config.
199 *
200 * It contains all information needed to match one or more leaf node.
201 * All information is encoded in a Field(2 ints) and a bit mask(1 int).
202 *
203 * For example, to match the first/any/last uid field in attribution chain in Atom 10,
204 * we have the following FieldMatcher in statsd_config
205 *    FieldMatcher {
206 *        field:10
207 *         FieldMatcher {
208 *              field:1
209 *              position: any/last/first
210 *              FieldMatcher {
211 *                  field:1
212 *              }
213 *          }
214 *     }
215 *
216 * We translate the FieldMatcher into a Field, and mask
217 * First: [Matcher Field] 0x02010101  [Mask]0xffff7fff
218 * Last:  [Matcher Field] 0x02018001  [Mask]0xffff80ff
219 * Any:   [Matcher Field] 0x02010001  [Mask]0xffff00ff
220 *
221 * [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if
222 * the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are
223 * equal. Nothing can beat the performance of this matching algorithm.
224 *
225 * TODO: ADD EXAMPLE HERE.
226 */
227struct Matcher {
228    Matcher(const Field& matcher, int32_t mask) : mMatcher(matcher), mMask(mask){};
229
230    const Field mMatcher;
231    const int32_t mMask;
232
233    bool hasAnyPositionMatcher(int* prefix) const {
234        if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(2) == 0) {
235            (*prefix) = mMatcher.getPrefix(2);
236            return true;
237        }
238        return false;
239    }
240};
241
242/**
243 * A wrapper for a union type to contain multiple types of values.
244 *
245 */
246struct Value {
247    Value(int32_t v) {
248        int_value = v;
249        type = INT;
250    }
251
252    Value(int64_t v) {
253        long_value = v;
254        type = LONG;
255    }
256
257    Value(float v) {
258        float_value = v;
259        type = FLOAT;
260    }
261
262    Value(const std::string& v) {
263        str_value = v;
264        type = STRING;
265    }
266
267    void setInt(int32_t v) {
268        int_value = v;
269        type = INT;
270    }
271
272    void setLong(int64_t v) {
273        long_value = v;
274        type = LONG;
275    }
276
277    union {
278        int32_t int_value;
279        int64_t long_value;
280        float float_value;
281    };
282    std::string str_value;
283
284    Type type;
285
286    std::string toString() const;
287
288    Type getType() const {
289        return type;
290    }
291
292    Value(const Value& from);
293
294    bool operator==(const Value& that) const;
295    bool operator!=(const Value& that) const;
296
297    bool operator<(const Value& that) const;
298
299private:
300    Value(){};
301};
302
303/**
304 * Represents a log item, or a dimension item (They are essentially the same).
305 */
306struct FieldValue {
307    FieldValue(const Field& field, const Value& value) : mField(field), mValue(value) {
308    }
309    bool operator==(const FieldValue& that) const {
310        return mField == that.mField && mValue == that.mValue;
311    }
312    bool operator!=(const FieldValue& that) const {
313        return mField != that.mField || mValue != that.mValue;
314    }
315    bool operator<(const FieldValue& that) const {
316        if (mField != that.mField) {
317            return mField < that.mField;
318        }
319
320        if (mValue != that.mValue) {
321            return mValue < that.mValue;
322        }
323
324        return false;
325    }
326
327    Field mField;
328    Value mValue;
329};
330
331bool isAttributionUidField(const FieldValue& value);
332
333void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
334
335bool isAttributionUidField(const Field& field, const Value& value);
336}  // namespace statsd
337}  // namespace os
338}  // namespace android
339