1 // Copyright 2012 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 #include "base/threading/thread_id_name_manager.h" 6 7 #include <stdlib.h> 8 #include <string.h> 9 10 #include "base/check.h" 11 #include "base/containers/contains.h" 12 #include "base/containers/cxx20_erase.h" 13 #include "base/memory/singleton.h" 14 #include "base/strings/string_util.h" 15 #include "base/trace_event/heap_profiler_allocation_context_tracker.h" // no-presubmit-check 16 #include "third_party/abseil-cpp/absl/base/attributes.h" 17 18 namespace base { 19 namespace { 20 21 static const char kDefaultName[] = ""; 22 static std::string* g_default_name; 23 24 ABSL_CONST_INIT thread_local const char* thread_name = kDefaultName; 25 } 26 27 ThreadIdNameManager::Observer::~Observer() = default; 28 ThreadIdNameManager()29ThreadIdNameManager::ThreadIdNameManager() 30 : main_process_name_(nullptr), main_process_id_(kInvalidThreadId) { 31 g_default_name = new std::string(kDefaultName); 32 33 AutoLock locked(lock_); 34 name_to_interned_name_[kDefaultName] = g_default_name; 35 } 36 37 ThreadIdNameManager::~ThreadIdNameManager() = default; 38 GetInstance()39ThreadIdNameManager* ThreadIdNameManager::GetInstance() { 40 return Singleton<ThreadIdNameManager, 41 LeakySingletonTraits<ThreadIdNameManager> >::get(); 42 } 43 GetDefaultInternedString()44const char* ThreadIdNameManager::GetDefaultInternedString() { 45 return g_default_name->c_str(); 46 } 47 RegisterThread(PlatformThreadHandle::Handle handle,PlatformThreadId id)48void ThreadIdNameManager::RegisterThread(PlatformThreadHandle::Handle handle, 49 PlatformThreadId id) { 50 AutoLock locked(lock_); 51 thread_id_to_handle_[id] = handle; 52 thread_handle_to_interned_name_[handle] = 53 name_to_interned_name_[kDefaultName]; 54 } 55 AddObserver(Observer * obs)56void ThreadIdNameManager::AddObserver(Observer* obs) { 57 AutoLock locked(lock_); 58 DCHECK(!base::Contains(observers_, obs)); 59 observers_.push_back(obs); 60 } 61 RemoveObserver(Observer * obs)62void ThreadIdNameManager::RemoveObserver(Observer* obs) { 63 AutoLock locked(lock_); 64 DCHECK(base::Contains(observers_, obs)); 65 base::Erase(observers_, obs); 66 } 67 SetName(const std::string & name)68void ThreadIdNameManager::SetName(const std::string& name) { 69 PlatformThreadId id = PlatformThread::CurrentId(); 70 std::string* leaked_str = nullptr; 71 { 72 AutoLock locked(lock_); 73 auto iter = name_to_interned_name_.find(name); 74 if (iter != name_to_interned_name_.end()) { 75 leaked_str = iter->second; 76 } else { 77 leaked_str = new std::string(name); 78 name_to_interned_name_[name] = leaked_str; 79 } 80 81 auto id_to_handle_iter = thread_id_to_handle_.find(id); 82 83 thread_name = leaked_str->c_str(); 84 for (Observer* obs : observers_) 85 obs->OnThreadNameChanged(leaked_str->c_str()); 86 87 // The main thread of a process will not be created as a Thread object which 88 // means there is no PlatformThreadHandler registered. 89 if (id_to_handle_iter == thread_id_to_handle_.end()) { 90 main_process_name_ = leaked_str; 91 main_process_id_ = id; 92 return; 93 } 94 thread_handle_to_interned_name_[id_to_handle_iter->second] = leaked_str; 95 } 96 97 // Add the leaked thread name to heap profiler context tracker. The name added 98 // is valid for the lifetime of the process. AllocationContextTracker cannot 99 // call GetName(which holds a lock) during the first allocation because it can 100 // cause a deadlock when the first allocation happens in the 101 // ThreadIdNameManager itself when holding the lock. 102 trace_event::AllocationContextTracker::SetCurrentThreadName( 103 leaked_str->c_str()); 104 } 105 GetName(PlatformThreadId id)106const char* ThreadIdNameManager::GetName(PlatformThreadId id) { 107 AutoLock locked(lock_); 108 109 if (id == main_process_id_) 110 return main_process_name_->c_str(); 111 112 auto id_to_handle_iter = thread_id_to_handle_.find(id); 113 if (id_to_handle_iter == thread_id_to_handle_.end()) 114 return name_to_interned_name_[kDefaultName]->c_str(); 115 116 auto handle_to_name_iter = 117 thread_handle_to_interned_name_.find(id_to_handle_iter->second); 118 return handle_to_name_iter->second->c_str(); 119 } 120 GetNameForCurrentThread()121const char* ThreadIdNameManager::GetNameForCurrentThread() { 122 return thread_name; 123 } 124 RemoveName(PlatformThreadHandle::Handle handle,PlatformThreadId id)125void ThreadIdNameManager::RemoveName(PlatformThreadHandle::Handle handle, 126 PlatformThreadId id) { 127 AutoLock locked(lock_); 128 auto handle_to_name_iter = thread_handle_to_interned_name_.find(handle); 129 130 DCHECK(handle_to_name_iter != thread_handle_to_interned_name_.end()); 131 thread_handle_to_interned_name_.erase(handle_to_name_iter); 132 133 auto id_to_handle_iter = thread_id_to_handle_.find(id); 134 DCHECK((id_to_handle_iter!= thread_id_to_handle_.end())); 135 // The given |id| may have been re-used by the system. Make sure the 136 // mapping points to the provided |handle| before removal. 137 if (id_to_handle_iter->second != handle) 138 return; 139 140 thread_id_to_handle_.erase(id_to_handle_iter); 141 } 142 GetIds()143std::vector<PlatformThreadId> ThreadIdNameManager::GetIds() { 144 AutoLock locked(lock_); 145 146 std::vector<PlatformThreadId> ids; 147 ids.reserve(thread_id_to_handle_.size()); 148 for (const auto& iter : thread_id_to_handle_) 149 ids.push_back(iter.first); 150 151 return ids; 152 } 153 154 } // namespace base 155