1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef BASE_TRACE_EVENT_TRACE_CONVERSION_HELPER_H_
6 #define BASE_TRACE_EVENT_TRACE_CONVERSION_HELPER_H_
7
8 #include <sstream>
9 #include <string>
10
11 #include "base/strings/string_number_conversions.h"
12 #include "base/template_util.h"
13 #include "base/trace_event/traced_value.h"
14
15 namespace base {
16
17 namespace trace_event {
18
19 // Helpers for base::trace_event::ValueToString.
20 namespace internal {
21
22 // Return std::string representation given by |value|'s ostream operator<<.
23 template <typename ValueType>
OstreamValueToString(const ValueType & value)24 std::string OstreamValueToString(const ValueType& value) {
25 std::stringstream ss;
26 ss << value;
27 return ss.str();
28 }
29
30 // Use SFINAE to decide how to extract a string from the given parameter.
31
32 // Check if |value| can be used as a parameter of |base::NumberToString|. If
33 // std::string is not constructible from the returned value of
34 // |base::NumberToString| cause compilation error.
35 //
36 // |base::NumberToString| does not do locale specific formatting and should be
37 // faster than using |std::ostream::operator<<|.
38 template <typename ValueType>
decltype(base::NumberToString (std::declval<const ValueType> ()),std::string ())39 decltype(base::NumberToString(std::declval<const ValueType>()), std::string())
40 ValueToStringHelper(base::internal::priority_tag<5>,
41 const ValueType& value,
42 std::string /* unused */) {
43 return base::NumberToString(value);
44 }
45
46 // If there is |ValueType::ToString| whose return value can be used to construct
47 // |std::string|, use this. Else use other methods.
48 template <typename ValueType>
49 decltype(std::string(std::declval<const ValueType>().ToString()))
ValueToStringHelper(base::internal::priority_tag<4>,const ValueType & value,std::string)50 ValueToStringHelper(base::internal::priority_tag<4>,
51 const ValueType& value,
52 std::string /* unused */) {
53 return value.ToString();
54 }
55
56 // If |std::ostream::operator<<| can be used, use it. Useful for |void*|.
57 template <typename ValueType>
58 decltype(
59 std::declval<std::ostream>().operator<<(std::declval<const ValueType>()),
60 std::string())
ValueToStringHelper(base::internal::priority_tag<3>,const ValueType & value,std::string)61 ValueToStringHelper(base::internal::priority_tag<3>,
62 const ValueType& value,
63 std::string /* unused */) {
64 return OstreamValueToString(value);
65 }
66
67 // Use |ValueType::operator<<| if applicable.
68 template <typename ValueType>
69 decltype(operator<<(std::declval<std::ostream&>(),
70 std::declval<const ValueType&>()),
71 std::string())
ValueToStringHelper(base::internal::priority_tag<2>,const ValueType & value,std::string)72 ValueToStringHelper(base::internal::priority_tag<2>,
73 const ValueType& value,
74 std::string /* unused */) {
75 return OstreamValueToString(value);
76 }
77
78 // If there is |ValueType::data| whose return value can be used to construct
79 // |std::string|, use it.
80 template <typename ValueType>
81 decltype(std::string(std::declval<const ValueType>().data()))
ValueToStringHelper(base::internal::priority_tag<1>,const ValueType & value,std::string)82 ValueToStringHelper(base::internal::priority_tag<1>,
83 const ValueType& value,
84 std::string /* unused */) {
85 return value.data();
86 }
87
88 // Fallback returns the |fallback_value|. Needs to have |ValueToStringPriority|
89 // with the highest number (to be called last).
90 template <typename ValueType>
ValueToStringHelper(base::internal::priority_tag<0>,const ValueType &,std::string fallback_value)91 std::string ValueToStringHelper(base::internal::priority_tag<0>,
92 const ValueType& /* unused */,
93 std::string fallback_value) {
94 return fallback_value;
95 }
96
97 /*********************************************
98 ********* SetTracedValueArg methods. *********
99 *********************************************/
100
101 // base::internal::priority_tag parameter is there to define ordering in which
102 // the following methods will be considered. Note that for instance |bool| type
103 // is also |std::is_integral|, so we need to test |bool| before testing for
104 // integral.
105 template <typename T>
106 typename std::enable_if<std::is_same<T, bool>::value>::type
SetTracedValueArgHelper(base::internal::priority_tag<6>,TracedValue * traced_value,const char * name,const T & value)107 SetTracedValueArgHelper(base::internal::priority_tag<6>,
108 TracedValue* traced_value,
109 const char* name,
110 const T& value) {
111 traced_value->SetBoolean(name, value);
112 }
113
114 // std::is_integral<bool>::value == true
115 // This needs to be considered only when T is not bool (has higher
116 // base::internal::priority_tag).
117 template <typename T>
118 typename std::enable_if<std::is_integral<T>::value>::type
SetTracedValueArgHelper(base::internal::priority_tag<5>,TracedValue * traced_value,const char * name,const T & value)119 SetTracedValueArgHelper(base::internal::priority_tag<5>,
120 TracedValue* traced_value,
121 const char* name,
122 const T& value) {
123 // Avoid loss of precision.
124 if (sizeof(int) < sizeof(value)) {
125 // TODO(crbug.com/1111787): Add 64-bit support to TracedValue.
126 traced_value->SetString(name, base::NumberToString(value));
127 } else {
128 traced_value->SetInteger(name, value);
129 }
130 }
131
132 // Any floating point type is converted to double.
133 template <typename T>
134 typename std::enable_if<std::is_floating_point<T>::value>::type
SetTracedValueArgHelper(base::internal::priority_tag<4>,TracedValue * traced_value,const char * name,const T & value)135 SetTracedValueArgHelper(base::internal::priority_tag<4>,
136 TracedValue* traced_value,
137 const char* name,
138 const T& value) {
139 traced_value->SetDouble(name, static_cast<double>(value));
140 }
141
142 // |void*| is traced natively.
143 template <typename T>
144 typename std::enable_if<std::is_same<T, void*>::value>::type
SetTracedValueArgHelper(base::internal::priority_tag<3>,TracedValue * traced_value,const char * name,const T & value)145 SetTracedValueArgHelper(base::internal::priority_tag<3>,
146 TracedValue* traced_value,
147 const char* name,
148 const T& value) {
149 traced_value->SetPointer(name, value);
150 }
151
152 // |const char*| is traced natively.
153 template <typename T>
154 typename std::enable_if<std::is_same<T, const char*>::value>::type
SetTracedValueArgHelper(base::internal::priority_tag<2>,TracedValue * traced_value,const char * name,const T & value)155 SetTracedValueArgHelper(base::internal::priority_tag<2>,
156 TracedValue* traced_value,
157 const char* name,
158 const T& value) {
159 traced_value->SetString(name, value);
160 }
161
162 // If an instance of |base::StringPiece| can be constructed from an instance of
163 // |T| trace |value| as a string.
164 template <typename T>
decltype(base::StringPiece (std::declval<const T> ()),void ())165 decltype(base::StringPiece(std::declval<const T>()), void())
166 SetTracedValueArgHelper(base::internal::priority_tag<1>,
167 TracedValue* traced_value,
168 const char* name,
169 const T& value) {
170 traced_value->SetString(name, value);
171 }
172
173 // Fallback.
174 template <typename T>
SetTracedValueArgHelper(base::internal::priority_tag<0>,TracedValue * traced_value,const char * name,const T &)175 void SetTracedValueArgHelper(base::internal::priority_tag<0>,
176 TracedValue* traced_value,
177 const char* name,
178 const T& /* unused */) {
179 // TODO(crbug.com/1111787): Add fallback to |ValueToString|. Crashes on
180 // operator<< have been seen with it.
181 traced_value->SetString(name, "<value>");
182 }
183
184 } // namespace internal
185
186 // The function to be used.
187 template <typename ValueType>
188 std::string ValueToString(const ValueType& value,
189 std::string fallback_value = "<value>") {
190 return internal::ValueToStringHelper(base::internal::priority_tag<5>(), value,
191 std::move(fallback_value));
192 }
193
194 // Method to trace |value| into the given |traced_value|. Support types where
195 // there is |TracedValue::SetT| natively.
196 //
197 // TODO(crbug.com/1111787): Add support for:
198 // absl::optional
199 // AsValueInto (T& and T*)
200 // array and map types
201 // fallback to ValueToString
202 template <typename ValueType>
SetTracedValueArg(TracedValue * traced_value,const char * name,const ValueType & value)203 void SetTracedValueArg(TracedValue* traced_value,
204 const char* name,
205 const ValueType& value) {
206 internal::SetTracedValueArgHelper(base::internal::priority_tag<6>(),
207 traced_value, name, value);
208 }
209
210 // Parameter pack support: do nothing for an empty parameter pack.
211 //
212 // Inline this to avoid linker duplicate symbol error.
SetTracedValueArg(TracedValue * traced_value,const char * name)213 inline void SetTracedValueArg(TracedValue* traced_value, const char* name) {}
214
215 // Parameter pack support. All of the packed parameters are traced under the
216 // same name. Serves to trace a parameter pack, all parameters having the same
217 // name (of the parameter pack) is desired.
218 //
219 // Example use when |args| is a parameter pack:
220 // SetTracedValueArg(traced_value, name, args...);
221 template <typename ValueType, typename... ValueTypes>
SetTracedValueArg(TracedValue * traced_value,const char * name,const ValueType & value,const ValueTypes &...args)222 void SetTracedValueArg(TracedValue* traced_value,
223 const char* name,
224 const ValueType& value,
225 const ValueTypes&... args) {
226 SetTracedValueArg(traced_value, name, value);
227 // Trace the rest from the parameter pack.
228 SetTracedValueArg(traced_value, name, args...);
229 }
230
231 } // namespace trace_event
232 } // namespace base
233
234 #endif // BASE_TRACE_EVENT_TRACE_CONVERSION_HELPER_H_
235