Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(396)

Unified Diff: base/debug/trace_event.h

Issue 8590015: trace_event: distinguish between scoped begin/end and global start/finish events (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: ETW test compile Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | base/debug/trace_event.cc » ('j') | base/debug/trace_event.cc » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/debug/trace_event.h
diff --git a/base/debug/trace_event.h b/base/debug/trace_event.h
index e581094c1a3657d53aafc18406a824739e9301ee..641b28087ca0a9e31a5d4fc368069d8857978c8c 100644
--- a/base/debug/trace_event.h
+++ b/base/debug/trace_event.h
@@ -34,6 +34,31 @@
// The trace system will automatically add to this information the
// current process id, thread id, and a timestamp in microseconds.
//
+// BEGIN and END events should typically be used in the same scope. To trace
nduca 2011/12/01 02:55:26 typically->clearly state the nesting requirement.
jbates 2011/12/01 23:07:50 Done.
+// an asynchronous procedure such as IPC send/receive, use START and FINISH:
+// void* ipc_send(...) {
+// static int send_count = 0;
+// ++send_count;
+// TRACE_EVENT_START0("ipc", "message", send_count);
+// }
+// void ipc_recv(...) {
+// static int recv_count = 0;
jar (doing other things) 2011/12/01 01:47:49 This is not multi-thread safe. At first, I though
nduca 2011/12/01 02:55:26 +1, even though its docs, it should be "legitimate
jbates 2011/12/01 23:07:50 Done.
+// ++recv_count;
+// TRACE_EVENT_FINISH0("ipc", "message", send_count);
+// }
+// The third parameter is a unique ID to match START/FINISH pairs.
+// START and FINISH can occur on any thread of any traced process. Pointers can
+// be used for the ID parameter, and they will be mangled internally so that
+// the same pointer on two different processes will not match. For example:
+// void* trace_malloc(size) {
+// void* ptr = malloc(size);
+// TRACE_EVENT_START0("memory", "malloc", ptr);
+// }
+// void trace_free(ptr) {
+// free(ptr);
+// TRACE_EVENT_FINISH0("memory", "malloc", ptr);
jar (doing other things) 2011/12/01 01:47:49 FYI: If you put the free after the event, then you
nduca 2011/12/01 02:55:26 I suggest using an example closer to that ReadTran
jbates 2011/12/01 23:07:50 Done.
+// }
+//
// Trace event also supports counters, which is a way to track a quantity
// as it varies over time. Counters are created with the following macro:
// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter", g_myCounterValue);
@@ -137,17 +162,17 @@
// Only these macros result in publishing data to ETW as currently implemented.
#define TRACE_EVENT_BEGIN_ETW(name, id, extra) \
base::debug::TraceLog::AddTraceEventEtw( \
- base::debug::TRACE_EVENT_PHASE_BEGIN, \
+ TRACE_EVENT_PHASE_BEGIN, \
name, reinterpret_cast<const void*>(id), extra)
#define TRACE_EVENT_END_ETW(name, id, extra) \
base::debug::TraceLog::AddTraceEventEtw( \
- base::debug::TRACE_EVENT_PHASE_END, \
+ TRACE_EVENT_PHASE_END, \
name, reinterpret_cast<const void*>(id), extra)
#define TRACE_EVENT_INSTANT_ETW(name, id, extra) \
base::debug::TraceLog::AddTraceEventEtw( \
- base::debug::TRACE_EVENT_PHASE_INSTANT, \
+ TRACE_EVENT_PHASE_INSTANT, \
name, reinterpret_cast<const void*>(id), extra)
// Records a pair of begin and end events called "name" for the current
@@ -190,20 +215,20 @@
TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, NULL, 0)
#define TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \
arg2_name, arg2_val) \
- INTERNAL_TRACE_EVENT_ADD(base::debug::TRACE_EVENT_PHASE_INSTANT, \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
category, name, arg1_name, arg1_val, arg2_name, arg2_val, \
- base::debug::TraceLog::EVENT_FLAG_NONE)
+ TRACE_EVENT_FLAG_NONE)
#define TRACE_EVENT_COPY_INSTANT0(category, name) \
TRACE_EVENT_COPY_INSTANT1(category, name, NULL, 0)
#define TRACE_EVENT_COPY_INSTANT1(category, name, arg1_name, arg1_val) \
TRACE_EVENT_COPY_INSTANT2(category, name, arg1_name, arg1_val, NULL, 0)
#define TRACE_EVENT_COPY_INSTANT2(category, name, arg1_name, arg1_val, \
arg2_name, arg2_val) \
- INTERNAL_TRACE_EVENT_ADD(base::debug::TRACE_EVENT_PHASE_INSTANT, \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
category, name, \
arg1_name, base::debug::TraceValue::ForceCopy(arg1_val), \
arg2_name, base::debug::TraceValue::ForceCopy(arg2_val), \
- base::debug::TraceLog::EVENT_FLAG_COPY)
+ TRACE_EVENT_FLAG_COPY)
// Records a single BEGIN event called "name" immediately, with 0, 1 or 2
// associated arguments. If the category is not enabled, then this
@@ -216,20 +241,20 @@
TRACE_EVENT_BEGIN2(category, name, arg1_name, arg1_val, NULL, 0)
#define TRACE_EVENT_BEGIN2(category, name, arg1_name, arg1_val, \
arg2_name, arg2_val) \
- INTERNAL_TRACE_EVENT_ADD(base::debug::TRACE_EVENT_PHASE_BEGIN, \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
category, name, arg1_name, arg1_val, arg2_name, arg2_val, \
- base::debug::TraceLog::EVENT_FLAG_NONE)
+ TRACE_EVENT_FLAG_NONE)
#define TRACE_EVENT_COPY_BEGIN0(category, name) \
TRACE_EVENT_COPY_BEGIN1(category, name, NULL, 0)
#define TRACE_EVENT_COPY_BEGIN1(category, name, arg1_name, arg1_val) \
TRACE_EVENT_COPY_BEGIN2(category, name, arg1_name, arg1_val, NULL, 0)
#define TRACE_EVENT_COPY_BEGIN2(category, name, arg1_name, arg1_val, \
arg2_name, arg2_val) \
- INTERNAL_TRACE_EVENT_ADD(base::debug::TRACE_EVENT_PHASE_BEGIN, \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
category, name, \
arg1_name, base::debug::TraceValue::ForceCopy(arg1_val), \
arg2_name, base::debug::TraceValue::ForceCopy(arg2_val), \
- base::debug::TraceLog::EVENT_FLAG_COPY)
+ TRACE_EVENT_FLAG_COPY)
// Records a single END event for "name" immediately. If the category
// is not enabled, then this does nothing.
@@ -241,20 +266,20 @@
TRACE_EVENT_END2(category, name, arg1_name, arg1_val, NULL, 0)
#define TRACE_EVENT_END2(category, name, arg1_name, arg1_val, \
arg2_name, arg2_val) \
- INTERNAL_TRACE_EVENT_ADD(base::debug::TRACE_EVENT_PHASE_END, \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
category, name, arg1_name, arg1_val, arg2_name, arg2_val, \
- base::debug::TraceLog::EVENT_FLAG_NONE)
+ TRACE_EVENT_FLAG_NONE)
#define TRACE_EVENT_COPY_END0(category, name) \
TRACE_EVENT_COPY_END1(category, name, NULL, 0)
#define TRACE_EVENT_COPY_END1(category, name, arg1_name, arg1_val) \
TRACE_EVENT_COPY_END2(category, name, arg1_name, arg1_val, NULL, 0)
#define TRACE_EVENT_COPY_END2(category, name, arg1_name, arg1_val, \
arg2_name, arg2_val) \
- INTERNAL_TRACE_EVENT_ADD(base::debug::TRACE_EVENT_PHASE_END, \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
category, name, \
arg1_name, base::debug::TraceValue::ForceCopy(arg1_val), \
arg2_name, base::debug::TraceValue::ForceCopy(arg2_val), \
- base::debug::TraceLog::EVENT_FLAG_COPY)
+ TRACE_EVENT_FLAG_COPY)
// Time threshold event:
// Only record the event if the duration is greater than the specified
@@ -293,14 +318,68 @@
value2_name, value2_val) \
INTERNAL_TRACE_EVENT_ADD_COUNTER( \
category, name, value1_name, value1_val, value2_name, value2_val, \
- base::debug::TraceLog::EVENT_FLAG_NONE)
+ TRACE_EVENT_FLAG_NONE)
#define TRACE_COPY_COUNTER2(category, name, value1_name, value1_val, \
value2_name, value2_val) \
INTERNAL_TRACE_EVENT_ADD_COUNTER( \
category, name, \
value1_name, value1_val, \
value2_name, value2_val, \
- base::debug::TraceLog::EVENT_FLAG_COPY)
+ TRACE_EVENT_FLAG_COPY)
+
+
+// Records a single START event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+// literals). They may not include " chars.
+// - |id| is used to match the START event with the FINISH event. It must either
+// be a pointer or an integer value up to 64 bits. If it's a pointer, the bits
+// will be xored with a hash of the process ID so that the same pointer on
+// two different processes will not collide.
+#define TRACE_EVENT_START0(category, name, id) \
+ TRACE_EVENT_START1(category, name, id, NULL, 0)
+#define TRACE_EVENT_START1(category, name, id, arg1_name, arg1_val) \
+ TRACE_EVENT_START2(category, name, id, arg1_name, arg1_val, NULL, 0)
+#define TRACE_EVENT_START2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_START, \
+ category, name, id, arg1_name, arg1_val, arg2_name, arg2_val, \
+ TRACE_EVENT_FLAG_HAS_ID)
+#define TRACE_EVENT_COPY_START0(category, name, id) \
+ TRACE_EVENT_COPY_START1(category, name, id, NULL, 0)
+#define TRACE_EVENT_COPY_START1(category, name, id, arg1_name, arg1_val) \
+ TRACE_EVENT_COPY_START2(category, name, id, arg1_name, arg1_val, NULL, 0)
+#define TRACE_EVENT_COPY_START2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_START, \
+ category, name, id, \
+ arg1_name, base::debug::TraceValue::ForceCopy(arg1_val), \
+ arg2_name, base::debug::TraceValue::ForceCopy(arg2_val), \
+ TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_COPY)
+
+// Records a single FINISH event for "name" immediately. If the category
+// is not enabled, then this does nothing.
+#define TRACE_EVENT_FINISH0(category, name, id) \
+ TRACE_EVENT_FINISH1(category, name, id, NULL, 0)
+#define TRACE_EVENT_FINISH1(category, name, id, arg1_name, arg1_val) \
+ TRACE_EVENT_FINISH2(category, name, id, arg1_name, arg1_val, NULL, 0)
+#define TRACE_EVENT_FINISH2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FINISH, \
+ category, name, id, arg1_name, arg1_val, arg2_name, arg2_val, \
+ TRACE_EVENT_FLAG_HAS_ID)
+#define TRACE_EVENT_COPY_FINISH0(category, name, id) \
+ TRACE_EVENT_COPY_FINISH1(category, name, id, NULL, 0)
+#define TRACE_EVENT_COPY_FINISH1(category, name, id, arg1_name, arg1_val) \
+ TRACE_EVENT_COPY_FINISH2(category, name, id, arg1_name, arg1_val, NULL, 0)
+#define TRACE_EVENT_COPY_FINISH2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FINISH, \
+ category, name, id, \
+ arg1_name, base::debug::TraceValue::ForceCopy(arg1_val), \
+ arg2_name, base::debug::TraceValue::ForceCopy(arg2_val), \
+ TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_COPY)
// Implementation detail: trace event macros create temporary variables
@@ -324,7 +403,7 @@
INTERNAL_TRACE_EVENT_UID(catstatic) = \
base::debug::TraceLog::GetCategory(category);
-// Implementation detail: internal macro to create static category and add begin
+// Implementation detail: internal macro to create static category and add
// event if the category is enabled.
#define INTERNAL_TRACE_EVENT_ADD( \
phase, category, name, arg1_name, arg1_val, arg2_name, arg2_val, flags) \
@@ -332,7 +411,7 @@
if (INTERNAL_TRACE_EVENT_UID(catstatic)->enabled) { \
base::debug::TraceLog::GetInstance()->AddTraceEvent( \
phase, INTERNAL_TRACE_EVENT_UID(catstatic), \
- name, arg1_name, arg1_val, arg2_name, arg2_val, -1, 0, flags); \
+ name, 0, arg1_name, arg1_val, arg2_name, arg2_val, -1, 0, flags); \
}
// Implementation detail: internal macro to create static category and
@@ -356,10 +435,10 @@
INTERNAL_TRACE_EVENT_UID(profileScope); \
if (INTERNAL_TRACE_EVENT_UID(catstatic)->enabled) { \
base::debug::TraceLog::GetInstance()->AddTraceEvent( \
- base::debug::TRACE_EVENT_PHASE_BEGIN, \
+ TRACE_EVENT_PHASE_BEGIN, \
INTERNAL_TRACE_EVENT_UID(catstatic), \
- name, arg1_name, arg1_val, arg2_name, arg2_val, -1, 0, \
- base::debug::TraceLog::EVENT_FLAG_NONE); \
+ name, 0, arg1_name, arg1_val, arg2_name, arg2_val, -1, 0, \
+ TRACE_EVENT_FLAG_NONE); \
INTERNAL_TRACE_EVENT_UID(profileScope).Initialize( \
INTERNAL_TRACE_EVENT_UID(catstatic), name); \
}
@@ -375,15 +454,26 @@
if (INTERNAL_TRACE_EVENT_UID(catstatic)->enabled) { \
int INTERNAL_TRACE_EVENT_UID(begin_event_id) = \
base::debug::TraceLog::GetInstance()->AddTraceEvent( \
- base::debug::TRACE_EVENT_PHASE_BEGIN, \
+ TRACE_EVENT_PHASE_BEGIN, \
INTERNAL_TRACE_EVENT_UID(catstatic), \
- name, arg1_name, arg1_val, arg2_name, arg2_val, -1, 0, \
- base::debug::TraceLog::EVENT_FLAG_NONE); \
+ name, 0, arg1_name, arg1_val, arg2_name, arg2_val, -1, 0, \
+ TRACE_EVENT_FLAG_NONE); \
INTERNAL_TRACE_EVENT_UID(profileScope).Initialize( \
INTERNAL_TRACE_EVENT_UID(catstatic), name, \
INTERNAL_TRACE_EVENT_UID(begin_event_id), threshold); \
}
+// Implementation detail: internal macro to create static category and add
+// event if the category is enabled.
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category, name, id, \
+ arg1_name, arg1_val, arg2_name, arg2_val, flags) \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \
+ if (INTERNAL_TRACE_EVENT_UID(catstatic)->enabled) { \
+ base::debug::TraceLog::GetInstance()->AddTraceEvent( \
+ phase, INTERNAL_TRACE_EVENT_UID(catstatic), \
+ name, id, arg1_name, arg1_val, arg2_name, arg2_val, -1, 0, flags); \
+ }
+
template <typename Type>
struct StaticMemorySingletonTraits;
@@ -402,13 +492,19 @@ struct TraceCategory {
const size_t kTraceMaxNumArgs = 2;
// Phase indicates the nature of an event entry. E.g. part of a begin/end pair.
-enum TraceEventPhase {
- TRACE_EVENT_PHASE_BEGIN,
- TRACE_EVENT_PHASE_END,
- TRACE_EVENT_PHASE_INSTANT,
- TRACE_EVENT_PHASE_METADATA,
- TRACE_EVENT_PHASE_COUNTER
-};
+typedef char TraceEventPhase;
+#define TRACE_EVENT_PHASE_BEGIN (base::debug::TraceEventPhase('B'))
+#define TRACE_EVENT_PHASE_END (base::debug::TraceEventPhase('E'))
+#define TRACE_EVENT_PHASE_INSTANT (base::debug::TraceEventPhase('I'))
+#define TRACE_EVENT_PHASE_START (base::debug::TraceEventPhase('S'))
+#define TRACE_EVENT_PHASE_FINISH (base::debug::TraceEventPhase('F'))
+#define TRACE_EVENT_PHASE_METADATA (base::debug::TraceEventPhase('M'))
+#define TRACE_EVENT_PHASE_COUNTER (base::debug::TraceEventPhase('C'))
+
+typedef uint8 TraceEventFlags;
+#define TRACE_EVENT_FLAG_NONE (base::debug::TraceEventFlags(0))
+#define TRACE_EVENT_FLAG_COPY (base::debug::TraceEventFlags(1<<0))
+#define TRACE_EVENT_FLAG_HAS_ID (base::debug::TraceEventFlags(1<<1))
nduca 2011/12/01 02:55:26 <3
// Simple union of values. This is much lighter weight than base::Value, which
// requires dynamic allocation and a vtable. To keep the trace runtime overhead
@@ -535,6 +631,26 @@ class BASE_EXPORT TraceValue {
Value value_;
};
+// TraceID encapsulates an ID that can either be an integer or pointer. Pointers
+// are mangled with the Process ID so that they are unlikely to collide when the
+// same pointer is used on different processes.
+class BASE_EXPORT TraceID {
+ public:
+ TraceID() : data_(NULL) {}
+ TraceID(void* rhs);
+ TraceID(unsigned long long rhs) : data_(static_cast<uint64>(rhs)) {}
+ TraceID(unsigned long rhs) : data_(static_cast<uint64>(rhs)) {}
+ TraceID(unsigned int rhs) : data_(static_cast<uint64>(rhs)) {}
+ TraceID(long long rhs) : data_(static_cast<uint64>(rhs)) {}
+ TraceID(long rhs) : data_(static_cast<uint64>(rhs)) {}
+ TraceID(int rhs) : data_(static_cast<uint64>(rhs)) {}
+
+ uint64 data() const { return data_; }
+
+ private:
+ uint64 data_;
+};
+
// Output records are "Events" and can be obtained via the
// OutputCallback whenever the tracing system decides to flush. This
// can happen at any time, on any thread, or you can programatically
@@ -542,19 +658,24 @@ class BASE_EXPORT TraceValue {
class BASE_EXPORT TraceEvent {
public:
TraceEvent();
- TraceEvent(unsigned long process_id,
- unsigned long thread_id,
+ TraceEvent(int thread_id,
TimeTicks timestamp,
TraceEventPhase phase,
const TraceCategory* category,
const char* name,
+ TraceID id,
const char* arg1_name, const TraceValue& arg1_val,
const char* arg2_name, const TraceValue& arg2_val,
- bool copy);
+ TraceEventFlags flags);
~TraceEvent();
- static const char* GetPhaseString(TraceEventPhase phase);
- static TraceEventPhase GetPhase(const char* phase);
+ static char GetPhaseChar(TraceEventPhase phase) {
+ return static_cast<char>(phase);
+ }
+
+ static TraceEventPhase GetPhase(const char* phase) {
+ return static_cast<TraceEventPhase>(*phase);
+ }
// Serialize event data to JSON
static void AppendEventsAsJSON(const std::vector<TraceEvent>& events,
@@ -574,15 +695,18 @@ class BASE_EXPORT TraceEvent {
const char* name() const { return name_; }
private:
- unsigned long process_id_;
- unsigned long thread_id_;
+ // Note: these are ordered by size (largest first) for optimal packing.
TimeTicks timestamp_;
- TraceEventPhase phase_;
+ // id_ can be used to store phase-specific data.
+ TraceID id_;
+ TraceValue arg_values_[kTraceMaxNumArgs];
+ const char* arg_names_[kTraceMaxNumArgs];
const TraceCategory* category_;
const char* name_;
- const char* arg_names_[kTraceMaxNumArgs];
- TraceValue arg_values_[kTraceMaxNumArgs];
scoped_refptr<base::RefCountedString> parameter_copy_storage_;
+ int thread_id_;
+ TraceEventPhase phase_;
+ TraceEventFlags flags_;
};
@@ -632,12 +756,6 @@ class BASE_EXPORT TraceResultBuffer {
class BASE_EXPORT TraceLog {
public:
- // Flags for passing to AddTraceEvent.
- enum EventFlags {
- EVENT_FLAG_NONE = 0,
- EVENT_FLAG_COPY = 1<<0
- };
-
static TraceLog* GetInstance();
// Get set of known categories. This can change as new code paths are reached.
@@ -710,11 +828,12 @@ class BASE_EXPORT TraceLog {
int AddTraceEvent(TraceEventPhase phase,
const TraceCategory* category,
const char* name,
+ TraceID id,
const char* arg1_name, TraceValue arg1_val,
const char* arg2_name, TraceValue arg2_val,
int threshold_begin_id,
int64 threshold,
- EventFlags flags);
+ TraceEventFlags flags);
static void AddTraceEventEtw(TraceEventPhase phase,
const char* name,
const void* id,
@@ -730,7 +849,13 @@ class BASE_EXPORT TraceLog {
const char* name,
const char* arg1_name, int32 arg1_val,
const char* arg2_name, int32 arg2_val,
- EventFlags flags);
+ TraceEventFlags flags);
+
+ // Mangle |id| with a hash based on the process ID so that if |id| occurs on
+ // more than one process, it will not collide.
+ uint64 GetIntraProcessID(uint64 id) const { return id ^ process_id_hash_; }
nduca 2011/12/01 02:55:26 Why this versus just tracking id and a bool flag s
jbates 2011/12/01 23:07:50 Sounds good, but sadly I think I would have to dou
+
+ int process_id() const { return process_id_; }
// Exposed for unittesting:
@@ -747,6 +872,8 @@ class BASE_EXPORT TraceLog {
return logged_events_[index];
}
+ void SetProcessID(int process_id);
+
private:
// This allows constructor and destructor to be private and usable only
// by the Singleton class.
@@ -767,7 +894,12 @@ class BASE_EXPORT TraceLog {
std::vector<std::string> included_categories_;
std::vector<std::string> excluded_categories_;
- base::hash_map<PlatformThreadId, std::string> thread_names_;
+ base::hash_map<int, std::string> thread_names_;
+
+ // XORed with TraceID to make it unlikely to collide with other processes.
+ uint64 process_id_hash_;
+
+ int process_id_;
DISALLOW_COPY_AND_ASSIGN(TraceLog);
};
« no previous file with comments | « no previous file | base/debug/trace_event.cc » ('j') | base/debug/trace_event.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698