1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/trace_event/trace_event_impl.h"
7 #include "base/format_macros.h"
8 #include "base/json/string_escape.h"
9 #include "base/stl_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/trace_event/trace_event.h"
15 #include "base/trace_event/trace_log.h"
18 namespace trace_event
{
22 size_t GetAllocLength(const char* str
) { return str
? strlen(str
) + 1 : 0; }
24 // Copies |*member| into |*buffer|, sets |*member| to point to this new
25 // location, and then advances |*buffer| by the amount written.
26 void CopyTraceEventParameter(char** buffer
,
30 size_t written
= strlcpy(*buffer
, *member
, end
- *buffer
) + 1;
31 DCHECK_LE(static_cast<int>(written
), end
- *buffer
);
39 TraceEvent::TraceEvent()
40 : duration_(TimeDelta::FromInternalValue(-1)),
42 category_group_enabled_(NULL
),
45 phase_(TRACE_EVENT_PHASE_BEGIN
),
47 for (int i
= 0; i
< kTraceMaxNumArgs
; ++i
)
49 memset(arg_values_
, 0, sizeof(arg_values_
));
52 TraceEvent::~TraceEvent() {
55 void TraceEvent::CopyFrom(const TraceEvent
& other
) {
56 timestamp_
= other
.timestamp_
;
57 thread_timestamp_
= other
.thread_timestamp_
;
58 duration_
= other
.duration_
;
60 context_id_
= other
.context_id_
;
61 category_group_enabled_
= other
.category_group_enabled_
;
63 thread_id_
= other
.thread_id_
;
64 phase_
= other
.phase_
;
65 flags_
= other
.flags_
;
66 parameter_copy_storage_
= other
.parameter_copy_storage_
;
68 for (int i
= 0; i
< kTraceMaxNumArgs
; ++i
) {
69 arg_names_
[i
] = other
.arg_names_
[i
];
70 arg_types_
[i
] = other
.arg_types_
[i
];
71 arg_values_
[i
] = other
.arg_values_
[i
];
72 convertable_values_
[i
] = other
.convertable_values_
[i
];
76 void TraceEvent::Initialize(
79 ThreadTicks thread_timestamp
,
81 const unsigned char* category_group_enabled
,
83 unsigned long long id
,
84 unsigned long long context_id
,
85 unsigned long long bind_id
,
87 const char** arg_names
,
88 const unsigned char* arg_types
,
89 const unsigned long long* arg_values
,
90 const scoped_refptr
<ConvertableToTraceFormat
>* convertable_values
,
92 timestamp_
= timestamp
;
93 thread_timestamp_
= thread_timestamp
;
94 duration_
= TimeDelta::FromInternalValue(-1);
96 context_id_
= context_id
;
97 category_group_enabled_
= category_group_enabled
;
99 thread_id_
= thread_id
;
104 // Clamp num_args since it may have been set by a third_party library.
105 num_args
= (num_args
> kTraceMaxNumArgs
) ? kTraceMaxNumArgs
: num_args
;
107 for (; i
< num_args
; ++i
) {
108 arg_names_
[i
] = arg_names
[i
];
109 arg_types_
[i
] = arg_types
[i
];
111 if (arg_types
[i
] == TRACE_VALUE_TYPE_CONVERTABLE
)
112 convertable_values_
[i
] = convertable_values
[i
];
114 arg_values_
[i
].as_uint
= arg_values
[i
];
116 for (; i
< kTraceMaxNumArgs
; ++i
) {
117 arg_names_
[i
] = NULL
;
118 arg_values_
[i
].as_uint
= 0u;
119 convertable_values_
[i
] = NULL
;
120 arg_types_
[i
] = TRACE_VALUE_TYPE_UINT
;
123 bool copy
= !!(flags
& TRACE_EVENT_FLAG_COPY
);
124 size_t alloc_size
= 0;
126 alloc_size
+= GetAllocLength(name
);
127 for (i
= 0; i
< num_args
; ++i
) {
128 alloc_size
+= GetAllocLength(arg_names_
[i
]);
129 if (arg_types_
[i
] == TRACE_VALUE_TYPE_STRING
)
130 arg_types_
[i
] = TRACE_VALUE_TYPE_COPY_STRING
;
134 bool arg_is_copy
[kTraceMaxNumArgs
];
135 for (i
= 0; i
< num_args
; ++i
) {
136 // No copying of convertable types, we retain ownership.
137 if (arg_types_
[i
] == TRACE_VALUE_TYPE_CONVERTABLE
)
140 // We only take a copy of arg_vals if they are of type COPY_STRING.
141 arg_is_copy
[i
] = (arg_types_
[i
] == TRACE_VALUE_TYPE_COPY_STRING
);
143 alloc_size
+= GetAllocLength(arg_values_
[i
].as_string
);
147 parameter_copy_storage_
= new RefCountedString
;
148 parameter_copy_storage_
->data().resize(alloc_size
);
149 char* ptr
= string_as_array(¶meter_copy_storage_
->data());
150 const char* end
= ptr
+ alloc_size
;
152 CopyTraceEventParameter(&ptr
, &name_
, end
);
153 for (i
= 0; i
< num_args
; ++i
) {
154 CopyTraceEventParameter(&ptr
, &arg_names_
[i
], end
);
157 for (i
= 0; i
< num_args
; ++i
) {
158 if (arg_types_
[i
] == TRACE_VALUE_TYPE_CONVERTABLE
)
161 CopyTraceEventParameter(&ptr
, &arg_values_
[i
].as_string
, end
);
163 DCHECK_EQ(end
, ptr
) << "Overrun by " << ptr
- end
;
167 void TraceEvent::Reset() {
168 // Only reset fields that won't be initialized in Initialize(), or that may
169 // hold references to other objects.
170 duration_
= TimeDelta::FromInternalValue(-1);
171 parameter_copy_storage_
= NULL
;
172 for (int i
= 0; i
< kTraceMaxNumArgs
; ++i
)
173 convertable_values_
[i
] = NULL
;
176 void TraceEvent::UpdateDuration(const TraceTicks
& now
,
177 const ThreadTicks
& thread_now
) {
178 DCHECK_EQ(duration_
.ToInternalValue(), -1);
179 duration_
= now
- timestamp_
;
180 thread_duration_
= thread_now
- thread_timestamp_
;
183 void TraceEvent::EstimateTraceMemoryOverhead(
184 TraceEventMemoryOverhead
* overhead
) {
185 overhead
->Add("TraceEvent", sizeof(*this));
187 // TODO(primiano): parameter_copy_storage_ is refcounted and, in theory,
188 // could be shared by several events and we might overcount. In practice
189 // this is unlikely but it's worth checking.
190 if (parameter_copy_storage_
)
191 overhead
->AddRefCountedString(*parameter_copy_storage_
.get());
193 for (size_t i
= 0; i
< kTraceMaxNumArgs
; ++i
) {
194 if (arg_types_
[i
] == TRACE_VALUE_TYPE_CONVERTABLE
)
195 convertable_values_
[i
]->EstimateTraceMemoryOverhead(overhead
);
200 void TraceEvent::AppendValueAsJSON(unsigned char type
,
201 TraceEvent::TraceValue value
,
204 case TRACE_VALUE_TYPE_BOOL
:
205 *out
+= value
.as_bool
? "true" : "false";
207 case TRACE_VALUE_TYPE_UINT
:
208 StringAppendF(out
, "%" PRIu64
, static_cast<uint64
>(value
.as_uint
));
210 case TRACE_VALUE_TYPE_INT
:
211 StringAppendF(out
, "%" PRId64
, static_cast<int64
>(value
.as_int
));
213 case TRACE_VALUE_TYPE_DOUBLE
: {
214 // FIXME: base/json/json_writer.cc is using the same code,
215 // should be made into a common method.
217 double val
= value
.as_double
;
218 if (std::isfinite(val
)) {
219 real
= DoubleToString(val
);
220 // Ensure that the number has a .0 if there's no decimal or 'e'. This
221 // makes sure that when we read the JSON back, it's interpreted as a
222 // real rather than an int.
223 if (real
.find('.') == std::string::npos
&&
224 real
.find('e') == std::string::npos
&&
225 real
.find('E') == std::string::npos
) {
228 // The JSON spec requires that non-integer values in the range (-1,1)
229 // have a zero before the decimal point - ".52" is not valid, "0.52" is.
230 if (real
[0] == '.') {
232 } else if (real
.length() > 1 && real
[0] == '-' && real
[1] == '.') {
233 // "-.1" bad "-0.1" good
236 } else if (std::isnan(val
)){
237 // The JSON spec doesn't allow NaN and Infinity (since these are
238 // objects in EcmaScript). Use strings instead.
240 } else if (val
< 0) {
241 real
= "\"-Infinity\"";
243 real
= "\"Infinity\"";
245 StringAppendF(out
, "%s", real
.c_str());
248 case TRACE_VALUE_TYPE_POINTER
:
249 // JSON only supports double and int numbers.
250 // So as not to lose bits from a 64-bit pointer, output as a hex string.
251 StringAppendF(out
, "\"0x%" PRIx64
"\"", static_cast<uint64
>(
252 reinterpret_cast<intptr_t>(
255 case TRACE_VALUE_TYPE_STRING
:
256 case TRACE_VALUE_TYPE_COPY_STRING
:
257 EscapeJSONString(value
.as_string
? value
.as_string
: "NULL", true, out
);
260 NOTREACHED() << "Don't know how to print this value";
265 void TraceEvent::AppendAsJSON(
267 const ArgumentFilterPredicate
& argument_filter_predicate
) const {
268 int64 time_int64
= timestamp_
.ToInternalValue();
269 int process_id
= TraceLog::GetInstance()->process_id();
270 const char* category_group_name
=
271 TraceLog::GetCategoryGroupName(category_group_enabled_
);
273 // Category group checked at category creation time.
274 DCHECK(!strchr(name_
, '"'));
275 StringAppendF(out
, "{\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64
277 "\"ph\":\"%c\",\"cat\":\"%s\",\"name\":\"%s\",\"args\":",
278 process_id
, thread_id_
, time_int64
, phase_
, category_group_name
,
281 // Output argument names and values, stop at first NULL argument name.
282 bool strip_args
= arg_names_
[0] && !argument_filter_predicate
.is_null() &&
283 !argument_filter_predicate
.Run(category_group_name
, name_
);
286 *out
+= "\"__stripped__\"";
290 for (int i
= 0; i
< kTraceMaxNumArgs
&& arg_names_
[i
]; ++i
) {
294 *out
+= arg_names_
[i
];
297 if (arg_types_
[i
] == TRACE_VALUE_TYPE_CONVERTABLE
)
298 convertable_values_
[i
]->AppendAsTraceFormat(out
);
300 AppendValueAsJSON(arg_types_
[i
], arg_values_
[i
], out
);
306 if (phase_
== TRACE_EVENT_PHASE_COMPLETE
) {
307 int64 duration
= duration_
.ToInternalValue();
309 StringAppendF(out
, ",\"dur\":%" PRId64
, duration
);
310 if (!thread_timestamp_
.is_null()) {
311 int64 thread_duration
= thread_duration_
.ToInternalValue();
312 if (thread_duration
!= -1)
313 StringAppendF(out
, ",\"tdur\":%" PRId64
, thread_duration
);
317 // Output tts if thread_timestamp is valid.
318 if (!thread_timestamp_
.is_null()) {
319 int64 thread_time_int64
= thread_timestamp_
.ToInternalValue();
320 StringAppendF(out
, ",\"tts\":%" PRId64
, thread_time_int64
);
323 // Output async tts marker field if flag is set.
324 if (flags_
& TRACE_EVENT_FLAG_ASYNC_TTS
) {
325 StringAppendF(out
, ", \"use_async_tts\":1");
328 // If id_ is set, print it out as a hex string so we don't loose any
329 // bits (it might be a 64-bit pointer).
330 if (flags_
& TRACE_EVENT_FLAG_HAS_ID
)
331 StringAppendF(out
, ",\"id\":\"0x%" PRIx64
"\"", static_cast<uint64
>(id_
));
333 if (flags_
& TRACE_EVENT_FLAG_BIND_TO_ENCLOSING
)
334 StringAppendF(out
, ",\"bp\":\"e\"");
336 if ((flags_
& TRACE_EVENT_FLAG_FLOW_OUT
) ||
337 (flags_
& TRACE_EVENT_FLAG_FLOW_IN
)) {
338 StringAppendF(out
, ",\"bind_id\":\"0x%" PRIx64
"\"",
339 static_cast<uint64
>(bind_id_
));
341 if (flags_
& TRACE_EVENT_FLAG_FLOW_IN
)
342 StringAppendF(out
, ",\"flow_in\":true");
343 if (flags_
& TRACE_EVENT_FLAG_FLOW_OUT
)
344 StringAppendF(out
, ",\"flow_out\":true");
346 // Similar to id_, print the context_id as hex if present.
347 if (flags_
& TRACE_EVENT_FLAG_HAS_CONTEXT_ID
)
348 StringAppendF(out
, ",\"cid\":\"0x%" PRIx64
"\"",
349 static_cast<uint64
>(context_id_
));
351 // Instant events also output their scope.
352 if (phase_
== TRACE_EVENT_PHASE_INSTANT
) {
354 switch (flags_
& TRACE_EVENT_FLAG_SCOPE_MASK
) {
355 case TRACE_EVENT_SCOPE_GLOBAL
:
356 scope
= TRACE_EVENT_SCOPE_NAME_GLOBAL
;
359 case TRACE_EVENT_SCOPE_PROCESS
:
360 scope
= TRACE_EVENT_SCOPE_NAME_PROCESS
;
363 case TRACE_EVENT_SCOPE_THREAD
:
364 scope
= TRACE_EVENT_SCOPE_NAME_THREAD
;
367 StringAppendF(out
, ",\"s\":\"%c\"", scope
);
373 void TraceEvent::AppendPrettyPrinted(std::ostringstream
* out
) const {
374 *out
<< name_
<< "[";
375 *out
<< TraceLog::GetCategoryGroupName(category_group_enabled_
);
379 for (int i
= 0; i
< kTraceMaxNumArgs
&& arg_names_
[i
]; ++i
) {
382 *out
<< arg_names_
[i
] << ":";
383 std::string value_as_text
;
385 if (arg_types_
[i
] == TRACE_VALUE_TYPE_CONVERTABLE
)
386 convertable_values_
[i
]->AppendAsTraceFormat(&value_as_text
);
388 AppendValueAsJSON(arg_types_
[i
], arg_values_
[i
], &value_as_text
);
390 *out
<< value_as_text
;
396 } // namespace trace_event