1 /*
2  * Copyright 2019 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 #pragma once
18 
19 #include <chrono>
20 #include <cmath>
21 #include <functional>
22 #include <string>
23 
24 #include <cutils/compiler.h>
25 #include <utils/Trace.h>
26 
27 namespace android {
28 
29 namespace {
30 template <class Rep, class Period>
signbit(std::chrono::duration<Rep,Period> v)31 bool signbit(std::chrono::duration<Rep, Period> v) {
32     return std::signbit(std::chrono::duration_cast<std::chrono::nanoseconds>(v).count());
33 }
34 
35 template <typename Enum, typename std::enable_if<std::is_enum<Enum>::value>::type* = nullptr>
signbit(Enum e)36 bool signbit(Enum e) {
37     return std::signbit(static_cast<typename std::underlying_type<Enum>::type>(e));
38 }
39 
40 template <typename T, typename std::enable_if<!std::is_enum<T>::value>::type* = nullptr>
signbit(T t)41 bool signbit(T t) {
42     return std::signbit(t);
43 }
44 
45 template <typename T>
to_int64(T v)46 int64_t to_int64(T v) {
47     return int64_t(v);
48 }
49 
50 template <class Rep, class Period>
to_int64(std::chrono::duration<Rep,Period> v)51 int64_t to_int64(std::chrono::duration<Rep, Period> v) {
52     return int64_t(v.count());
53 }
54 } // namespace
55 
56 template <typename T>
57 class TracedOrdinal {
58 public:
59     static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()) ||
60                           std::is_same<std::chrono::nanoseconds, T>() || std::is_enum<T>(),
61                   "Type is not supported. Please test it with systrace before adding "
62                   "it to the list.");
63 
TracedOrdinal(std::string name,T initialValue)64     TracedOrdinal(std::string name, T initialValue)
65           : mName(std::move(name)), mHasGoneNegative(signbit(initialValue)), mData(initialValue) {
66         trace();
67     }
68 
get()69     T get() const { return mData; }
70 
T()71     operator T() const { return get(); }
72 
73     TracedOrdinal& operator=(T other) {
74         mData = other;
75         mHasGoneNegative = mHasGoneNegative || signbit(mData);
76         trace();
77         return *this;
78     }
79 
80 private:
trace()81     void trace() {
82         if (CC_LIKELY(!ATRACE_ENABLED())) {
83             return;
84         }
85 
86         if (mNameNegative.empty()) {
87             mNameNegative = mName + "Negative";
88         }
89 
90         if (!signbit(mData)) {
91             ATRACE_INT64(mName.c_str(), to_int64(mData));
92             if (mHasGoneNegative) {
93                 ATRACE_INT64(mNameNegative.c_str(), 0);
94             }
95         } else {
96             ATRACE_INT64(mNameNegative.c_str(), -to_int64(mData));
97             ATRACE_INT64(mName.c_str(), 0);
98         }
99     }
100 
101     const std::string mName;
102     std::string mNameNegative;
103     bool mHasGoneNegative;
104     T mData;
105 };
106 
107 } // namespace android
108