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