[VM/Timeline] Add test that makes TSAN check the track metadata map

TEST=CI

Change-Id: I9e4fa4bbc08e77a1365e84f2d048384d7f0b1350
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/281720
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Derek Xu <derekx@google.com>
diff --git a/runtime/vm/os_thread.cc b/runtime/vm/os_thread.cc
index e101536..e04dc4a 100644
--- a/runtime/vm/os_thread.cc
+++ b/runtime/vm/os_thread.cc
@@ -27,7 +27,9 @@
   RecorderLockScope rl;
   TimelineEventRecorder* recorder = Timeline::recorder();
   if (recorder != nullptr && !rl.IsShuttingDown()) {
-    recorder->AddTrackMetadataBasedOnThread(thread);
+    recorder->AddTrackMetadataBasedOnThread(
+        OS::ProcessId(), OSThread::ThreadIdToIntPtr(thread.trace_id()),
+        thread.name());
   }
 }
 #endif  // defined(SUPPORT_TIMELINE)
diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
index f70b6b4..43a17b0 100644
--- a/runtime/vm/timeline.cc
+++ b/runtime/vm/timeline.cc
@@ -1072,8 +1072,10 @@
   // were initialized before this point.
   OSThreadIterator it;
   while (it.HasNext()) {
-    OSThread* thread = it.Next();
-    AddTrackMetadataBasedOnThread(*thread);
+    OSThread& thread = *it.Next();
+    AddTrackMetadataBasedOnThread(OS::ProcessId(),
+                                  OSThread::ThreadIdToIntPtr(thread.trace_id()),
+                                  thread.name());
   }
 }
 
@@ -1246,7 +1248,9 @@
 }
 
 void TimelineEventRecorder::AddTrackMetadataBasedOnThread(
-    const OSThread& thread) {
+    const intptr_t process_id,
+    const intptr_t trace_id,
+    const char* thread_name) {
   if (FLAG_timeline_recorder != nullptr &&
       // There is no way to retrieve track metadata when a callback or systrace
       // recorder is in use, so we don't need to update the map in these cases.
@@ -1254,23 +1258,19 @@
       strcmp("systrace", FLAG_timeline_recorder) != 0) {
     MutexLocker ml(&track_uuid_to_track_metadata_lock_);
 
-    intptr_t pid = OS::ProcessId();
-    intptr_t tid = OSThread::ThreadIdToIntPtr(thread.trace_id());
-    const char* thread_name = thread.name();
-
-    void* key = reinterpret_cast<void*>(tid);
-    const intptr_t hash = Utils::WordHash(tid);
+    void* key = reinterpret_cast<void*>(trace_id);
+    const intptr_t hash = Utils::WordHash(trace_id);
     SimpleHashMap::Entry* entry =
         track_uuid_to_track_metadata_.Lookup(key, hash, true);
     if (entry->value == nullptr) {
       entry->value = new TimelineTrackMetadata(
-          pid, tid,
+          process_id, trace_id,
           Utils::CreateCStringUniquePtr(
               Utils::StrDup(thread_name == nullptr ? "" : thread_name)));
     } else {
       TimelineTrackMetadata* value =
           static_cast<TimelineTrackMetadata*>(entry->value);
-      ASSERT(pid == value->pid());
+      ASSERT(process_id == value->pid());
       value->set_track_name(Utils::CreateCStringUniquePtr(
           Utils::StrDup(thread_name == nullptr ? "" : thread_name)));
     }
diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h
index 962f935..edbc125 100644
--- a/runtime/vm/timeline.h
+++ b/runtime/vm/timeline.h
@@ -848,7 +848,9 @@
   void FinishBlock(TimelineEventBlock* block);
   // This function must be called at least once for each thread that corresponds
   // to a track in the trace.
-  void AddTrackMetadataBasedOnThread(const OSThread& thread);
+  void AddTrackMetadataBasedOnThread(const intptr_t process_id,
+                                     const intptr_t trace_id,
+                                     const char* thread_name);
 
  protected:
 #ifndef PRODUCT
diff --git a/runtime/vm/timeline_test.cc b/runtime/vm/timeline_test.cc
index ee3ff18..3ada2b2 100644
--- a/runtime/vm/timeline_test.cc
+++ b/runtime/vm/timeline_test.cc
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 #include <cstring>
+#include <memory>
 
 #include "platform/assert.h"
 
@@ -373,6 +374,55 @@
   EXPECT(alpha < beta);
 }
 
+TEST_CASE(TimelineTrackMetadataRace) {
+  struct ReportMetadataArguments {
+    TimelineEventRingRecorder* recorder;
+    ThreadJoinId join_id = OSThread::kInvalidThreadJoinId;
+  };
+
+  TimelineEventRingRecorder recorder;
+  const intptr_t fake_process_id = 1;
+  const intptr_t fake_trace_id = 1;
+
+  // Try concurrently reading from / writing to the metadata map. I don't think
+  // it's possible to assert anything about the outcome, because of scheduling
+  // uncertainty. This test is just used to ensure that TSAN checks the metadata
+  // map code.
+  JSONStream js;
+  TimelineEventFilter filter;
+  const ReportMetadataArguments report_metadata_1_arguments{&recorder};
+  const ReportMetadataArguments report_metadata_2_arguments{&recorder};
+  OSThread::Start(
+      "ReportMetadata1",
+      [](uword arguments_ptr) {
+        ReportMetadataArguments& arguments =
+            *reinterpret_cast<ReportMetadataArguments*>(arguments_ptr);
+        arguments.recorder->AddTrackMetadataBasedOnThread(
+            fake_process_id, fake_trace_id, "Thread 1");
+        arguments.join_id =
+            OSThread::GetCurrentThreadJoinId(OSThread::Current());
+      },
+      reinterpret_cast<uword>(&report_metadata_1_arguments));
+  OSThread::Start(
+      "ReportMetadata2",
+      [](uword arguments_ptr) {
+        ReportMetadataArguments& arguments =
+            *reinterpret_cast<ReportMetadataArguments*>(arguments_ptr);
+        arguments.recorder->AddTrackMetadataBasedOnThread(
+            fake_process_id, fake_trace_id, "Incorrect Name");
+        arguments.join_id =
+            OSThread::GetCurrentThreadJoinId(OSThread::Current());
+      },
+      reinterpret_cast<uword>(&report_metadata_2_arguments));
+  recorder.PrintJSON(&js, &filter);
+  while (report_metadata_1_arguments.join_id == OSThread::kInvalidThreadId ||
+         report_metadata_2_arguments.join_id == OSThread::kInvalidThreadId) {
+    // Spin until the join IDs have been set.
+  }
+  OSThread::Join(report_metadata_1_arguments.join_id);
+  OSThread::Join(report_metadata_2_arguments.join_id);
+}
+
 #endif  // !PRODUCT
 
 #if defined(SUPPORT_TIMELINE)