The TRACE_LINK_IDS macro

We would like to be able to make two event IDs identical; so,
e.g. we can do something like the following:

TRACE_EVENT_ASYNC_BEGIN0("cat", "an_async_event", "0x1000");
TRACE_EVENT_LINK_IDS("cat", "a_link_event", "0x1000", "0x2000");
TRACE_EVENT_ASYNC_END("cat", "an_async_event", "0x2000");

This was done in https://codereview.chromium.org/2142023003. But,
in that patch, I misused the "bind_id" field in the
implementation of the macro to avoid introducing a new field.
That field overwriting caused some confusions because "bind_id"
was introduced for flow events.

This patch fixes the "bind_id" misusage. Also, it makes sure that
the TRACE_ID_LOCAL and TRACE_ID_GLOBAL macros that were
introduced in https://codereview.chromium.org/2253973003 work
properly when nested inside the TRACE_LINK_IDS macro.

For more context about why the TRACE_LINK_IDS was introduced in
the first place:
https://docs.google.com/document/d/1s0DKjNJk85hDuRp5IqujwZvPML-MPsyAtDeksMwBw7s

BUG=catapult:#2465

Review-Url: https://codereview.chromium.org/2381083003
Cr-Commit-Position: refs/heads/master@{#425069}


CrOS-Libchrome-Original-Commit: ecfbdf571cc36bc1f9282032a27177d482202580
diff --git a/base/trace_event/common/trace_event_common.h b/base/trace_event/common/trace_event_common.h
index 182ed5f..e87665b 100644
--- a/base/trace_event/common/trace_event_common.h
+++ b/base/trace_event/common/trace_event_common.h
@@ -953,15 +953,15 @@
   INTERNAL_TRACE_EVENT_SCOPED_CONTEXT(category_group, name, context)
 
 // Macro to specify that two trace IDs are identical. For example,
-// TRACE_BIND_IDS(
+// TRACE_LINK_IDS(
 //     "category", "name",
 //     TRACE_ID_WITH_SCOPE("net::URLRequest", 0x1000),
 //     TRACE_ID_WITH_SCOPE("blink::ResourceFetcher::FetchRequest", 0x2000))
 // tells the trace consumer that events with ID ("net::URLRequest", 0x1000) from
 // the current process have the same ID as events with ID
 // ("blink::ResourceFetcher::FetchRequest", 0x2000).
-#define TRACE_BIND_IDS(category_group, name, id, bind_id) \
-  INTERNAL_TRACE_EVENT_ADD_BIND_IDS(category_group, name, id, bind_id);
+#define TRACE_LINK_IDS(category_group, name, id, linked_id) \
+  INTERNAL_TRACE_EVENT_ADD_LINK_IDS(category_group, name, id, linked_id);
 
 // Macro to efficiently determine if a given category group is enabled.
 #define TRACE_EVENT_CATEGORY_GROUP_ENABLED(category_group, ret)             \
@@ -1028,7 +1028,7 @@
 #define TRACE_EVENT_PHASE_CLOCK_SYNC ('c')
 #define TRACE_EVENT_PHASE_ENTER_CONTEXT ('(')
 #define TRACE_EVENT_PHASE_LEAVE_CONTEXT (')')
-#define TRACE_EVENT_PHASE_BIND_IDS ('=')
+#define TRACE_EVENT_PHASE_LINK_IDS ('=')
 
 // Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT.
 #define TRACE_EVENT_FLAG_NONE (static_cast<unsigned int>(0))
diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h
index eca7c07..20ce662 100644
--- a/base/trace_event/trace_event.h
+++ b/base/trace_event/trace_event.h
@@ -335,29 +335,20 @@
     }                                                                       \
   } while (0)
 
-// This macro ignores whether the bind_id is local, global, or mangled.
-#define INTERNAL_TRACE_EVENT_ADD_BIND_IDS(category_group, name, id, bind_id,  \
-                                          ...)                                \
+// The linked ID will not be mangled.
+#define INTERNAL_TRACE_EVENT_ADD_LINK_IDS(category_group, name, id1, id2)     \
     do {                                                                      \
       INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group);                 \
       if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
-        trace_event_internal::TraceID source_id((id));                        \
+        trace_event_internal::TraceID source_id((id1));                       \
         unsigned int source_flags = source_id.id_flags();                     \
-        trace_event_internal::TraceID target_id((bind_id));                   \
-        if (target_id.scope() == trace_event_internal::kGlobalScope) {        \
-          trace_event_internal::AddTraceEvent(                                \
-              TRACE_EVENT_PHASE_BIND_IDS,                                     \
-              INTERNAL_TRACE_EVENT_UID(category_group_enabled),               \
-              name, source_id.scope(), source_id.raw_id(),                    \
-              source_flags, target_id.raw_id(), ##__VA_ARGS__);               \
-        } else {                                                              \
-          trace_event_internal::AddTraceEvent(                                \
-              TRACE_EVENT_PHASE_BIND_IDS,                                     \
-              INTERNAL_TRACE_EVENT_UID(category_group_enabled),               \
-              name, source_id.scope(), source_id.raw_id(),                    \
-              source_flags, target_id.raw_id(),                               \
-              "bind_scope", target_id.scope(), ##__VA_ARGS__);                \
-        }                                                                     \
+        trace_event_internal::TraceID target_id((id2));                       \
+        trace_event_internal::AddTraceEvent(                                  \
+            TRACE_EVENT_PHASE_LINK_IDS,                                       \
+            INTERNAL_TRACE_EVENT_UID(category_group_enabled),                 \
+            name, source_id.scope(), source_id.raw_id(), source_flags,        \
+            trace_event_internal::kNoId,                                      \
+            "linked_id", target_id.AsConvertableToTraceFormat());             \
       }                                                                       \
     } while (0)
 
@@ -415,7 +406,7 @@
 // TraceID encapsulates an ID that can either be an integer or pointer. Pointers
 // are by default mangled with the Process ID so that they are unlikely to
 // collide when the same pointer is used on different processes.
-class TraceID {
+class BASE_EXPORT TraceID {
  public:
   // Can be combined with WithScope.
   class LocalId {
@@ -541,6 +532,9 @@
   const char* scope() const { return scope_; }
   unsigned int id_flags() const { return id_flags_; }
 
+  std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
+  AsConvertableToTraceFormat() const;
+
  private:
   const char* scope_ = nullptr;
   unsigned long long raw_id_;
diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc
index d41500d..f9792d0 100644
--- a/base/trace_event/trace_event_impl.cc
+++ b/base/trace_event/trace_event_impl.cc
@@ -8,6 +8,7 @@
 
 #include "base/format_macros.h"
 #include "base/json/string_escape.h"
+#include "base/memory/ptr_util.h"
 #include "base/process/process_handle.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
@@ -15,6 +16,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
 #include "base/trace_event/trace_log.h"
 
 namespace base {
@@ -391,8 +393,7 @@
     StringAppendF(out, ",\"bp\":\"e\"");
 
   if ((flags_ & TRACE_EVENT_FLAG_FLOW_OUT) ||
-      (flags_ & TRACE_EVENT_FLAG_FLOW_IN) ||
-      phase_ == TRACE_EVENT_PHASE_BIND_IDS) {
+      (flags_ & TRACE_EVENT_FLAG_FLOW_IN)) {
     StringAppendF(out, ",\"bind_id\":\"0x%" PRIx64 "\"",
                   static_cast<uint64_t>(bind_id_));
   }
@@ -448,3 +449,40 @@
 
 }  // namespace trace_event
 }  // namespace base
+
+namespace trace_event_internal {
+
+std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
+TraceID::AsConvertableToTraceFormat() const {
+  auto value = base::MakeUnique<base::trace_event::TracedValue>();
+
+  if (scope_ != kGlobalScope)
+    value->SetString("scope", scope_);
+  switch (id_flags_) {
+    case TRACE_EVENT_FLAG_HAS_ID:
+      value->SetString(
+          "id",
+          base::StringPrintf("0x%" PRIx64, static_cast<uint64_t>(raw_id_)));
+      break;
+    case TRACE_EVENT_FLAG_HAS_GLOBAL_ID:
+      value->BeginDictionary("id2");
+      value->SetString(
+          "global",
+          base::StringPrintf("0x%" PRIx64, static_cast<uint64_t>(raw_id_)));
+      value->EndDictionary();
+      break;
+    case TRACE_EVENT_FLAG_HAS_LOCAL_ID:
+      value->BeginDictionary("id2");
+      value->SetString(
+          "local",
+          base::StringPrintf("0x%" PRIx64, static_cast<uint64_t>(raw_id_)));
+      value->EndDictionary();
+      break;
+    default:
+      NOTREACHED() << "Unrecognized ID flag";
+  }
+
+  return std::move(value);
+}
+
+}  // namespace trace_event_internal
diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc
index cec48b7..8455378 100644
--- a/base/trace_event/trace_event_unittest.cc
+++ b/base/trace_event/trace_event_unittest.cc
@@ -31,6 +31,7 @@
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_buffer.h"
+#include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_synthetic_delay.h"
 #include "base/values.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -520,10 +521,14 @@
     TRACE_EVENT_SCOPED_CONTEXT("all", "TRACE_EVENT_SCOPED_CONTEXT call",
                                context_id);
 
-    TRACE_BIND_IDS("all", "TRACE_BIND_IDS simple call", 0x1000, 0x2000);
-    TRACE_BIND_IDS("all", "TRACE_BIND_IDS scoped call",
+    TRACE_LINK_IDS("all", "TRACE_LINK_IDS simple call", 0x1000, 0x2000);
+    TRACE_LINK_IDS("all", "TRACE_LINK_IDS scoped call",
                    TRACE_ID_WITH_SCOPE("scope 1", 0x1000),
                    TRACE_ID_WITH_SCOPE("scope 2", 0x2000));
+    TRACE_LINK_IDS("all", "TRACE_LINK_IDS to a local ID", 0x1000,
+                   TRACE_ID_LOCAL(0x2000));
+    TRACE_LINK_IDS("all", "TRACE_LINK_IDS to a global ID", 0x1000,
+                   TRACE_ID_GLOBAL(0x2000));
 
     TRACE_EVENT_ASYNC_BEGIN0("all", "async default process scope", 0x1000);
     TRACE_EVENT_ASYNC_BEGIN0("all", "async local id", TRACE_ID_LOCAL(0x2000));
@@ -972,42 +977,76 @@
     EXPECT_EQ("0x20151021", id);
   }
 
-  EXPECT_FIND_("TRACE_BIND_IDS simple call");
+  EXPECT_FIND_("TRACE_LINK_IDS simple call");
   {
     std::string ph;
     EXPECT_TRUE((item && item->GetString("ph", &ph)));
     EXPECT_EQ("=", ph);
 
     EXPECT_FALSE((item && item->HasKey("scope")));
-    std::string id;
-    EXPECT_TRUE((item && item->GetString("id", &id)));
-    EXPECT_EQ("0x1000", id);
+    std::string id1;
+    EXPECT_TRUE((item && item->GetString("id", &id1)));
+    EXPECT_EQ("0x1000", id1);
 
-    EXPECT_FALSE((item && item->HasKey("args.bind_scope")));
-    std::string bind_id;
-    EXPECT_TRUE((item && item->GetString("bind_id", &id)));
-    EXPECT_EQ("0x2000", id);
+    EXPECT_FALSE((item && item->HasKey("args.linked_id.scope")));
+    std::string id2;
+    EXPECT_TRUE((item && item->GetString("args.linked_id.id", &id2)));
+    EXPECT_EQ("0x2000", id2);
   }
 
-  EXPECT_FIND_("TRACE_BIND_IDS scoped call");
+  EXPECT_FIND_("TRACE_LINK_IDS scoped call");
   {
     std::string ph;
     EXPECT_TRUE((item && item->GetString("ph", &ph)));
     EXPECT_EQ("=", ph);
 
-    std::string id_scope;
-    EXPECT_TRUE((item && item->GetString("scope", &id_scope)));
-    EXPECT_EQ("scope 1", id_scope);
-    std::string id;
-    EXPECT_TRUE((item && item->GetString("id", &id)));
-    EXPECT_EQ("0x1000", id);
+    std::string scope1;
+    EXPECT_TRUE((item && item->GetString("scope", &scope1)));
+    EXPECT_EQ("scope 1", scope1);
+    std::string id1;
+    EXPECT_TRUE((item && item->GetString("id", &id1)));
+    EXPECT_EQ("0x1000", id1);
 
-    std::string bind_scope;
-    EXPECT_TRUE((item && item->GetString("args.bind_scope", &bind_scope)));
-    EXPECT_EQ("scope 2", bind_scope);
-    std::string bind_id;
-    EXPECT_TRUE((item && item->GetString("bind_id", &id)));
-    EXPECT_EQ("0x2000", id);
+    std::string scope2;
+    EXPECT_TRUE((item && item->GetString("args.linked_id.scope", &scope2)));
+    EXPECT_EQ("scope 2", scope2);
+    std::string id2;
+    EXPECT_TRUE((item && item->GetString("args.linked_id.id", &id2)));
+    EXPECT_EQ("0x2000", id2);
+  }
+
+  EXPECT_FIND_("TRACE_LINK_IDS to a local ID");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("=", ph);
+
+    EXPECT_FALSE((item && item->HasKey("scope")));
+    std::string id1;
+    EXPECT_TRUE((item && item->GetString("id", &id1)));
+    EXPECT_EQ("0x1000", id1);
+
+    EXPECT_FALSE((item && item->HasKey("args.linked_id.scope")));
+    std::string id2;
+    EXPECT_TRUE((item && item->GetString("args.linked_id.id2.local", &id2)));
+    EXPECT_EQ("0x2000", id2);
+  }
+
+  EXPECT_FIND_("TRACE_LINK_IDS to a global ID");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("=", ph);
+
+    EXPECT_FALSE((item && item->HasKey("scope")));
+    std::string id1;
+    EXPECT_TRUE((item && item->GetString("id", &id1)));
+    EXPECT_EQ("0x1000", id1);
+
+    EXPECT_FALSE((item && item->HasKey("args.linked_id.scope")));
+    std::string id2;
+    EXPECT_TRUE((item && item->GetString("args.linked_id.id2.global", &id2)));
+    EXPECT_EQ("0x2000", id2);
   }
 
   EXPECT_FIND_("async default process scope");