From b32c4feb0fc3eb0dce8e1c98f01a45342ff0434c Mon Sep 17 00:00:00 2001 From: Peter DeLong Date: Mon, 14 Aug 2017 16:40:47 -0700 Subject: [PATCH] Add hooks to track which threads belong to which thread pools Summary: Keep track of which threads belong to which thread pools for use when debugging. This will be paired with a gdb script that associates threads with their thread pools Reviewed By: andriigrynenko Differential Revision: D5514729 fbshipit-source-id: 57ada4dd1aaaec5d7026e4eee05b0ec4e7434c77 --- folly/GlobalThreadPoolList.cpp | 218 +++++++++++++++++++++++++++++++++ folly/GlobalThreadPoolList.h | 61 +++++++++ 2 files changed, 279 insertions(+) create mode 100644 folly/GlobalThreadPoolList.cpp create mode 100644 folly/GlobalThreadPoolList.h diff --git a/folly/GlobalThreadPoolList.cpp b/folly/GlobalThreadPoolList.cpp new file mode 100644 index 00000000..075baa72 --- /dev/null +++ b/folly/GlobalThreadPoolList.cpp @@ -0,0 +1,218 @@ +/* + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace folly { + +namespace { + +class ThreadListHook { + public: + ThreadListHook(ThreadPoolListHook* poolId, std::thread::id threadId); + ~ThreadListHook(); + + private: + ThreadListHook() {} + ThreadPoolListHook* poolId_; + std::thread::id threadId_; +}; + +class GlobalThreadPoolListImpl { + public: + GlobalThreadPoolListImpl() {} + + void registerThreadPool(ThreadPoolListHook* threadPoolId, std::string name); + + void unregisterThreadPool(ThreadPoolListHook* threadPoolId); + + void registerThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId); + + void unregisterThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId); + + private: + struct PoolInfo { + ThreadPoolListHook* addr; + std::string name; + std::vector threads; + }; + + struct Pools { + // Just a vector since ease of access from gdb is the most important + // property + std::vector poolsInfo_; + + std::vector* FOLLY_NULLABLE + getThreadVector(void* threadPoolId) { + for (auto& elem : vector()) { + if (elem.addr == threadPoolId) { + return &elem.threads; + } + } + + return nullptr; + } + + std::vector& vector() { + return poolsInfo_; + } + }; + + Pools pools_; +}; + +class GlobalThreadPoolList { + public: + GlobalThreadPoolList() {} + + static GlobalThreadPoolList& instance(); + + void registerThreadPool(ThreadPoolListHook* threadPoolId, std::string name); + + void unregisterThreadPool(ThreadPoolListHook* threadPoolId); + + void registerThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId); + + void unregisterThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId); + + GlobalThreadPoolList(GlobalThreadPoolList const&) = delete; + void operator=(GlobalThreadPoolList const&) = delete; + + private: + folly::Synchronized globalListImpl_; + folly::ThreadLocalPtr threadHook_; +}; + +} // namespace + +GlobalThreadPoolList& GlobalThreadPoolList::instance() { + static folly::Indestructible ret; + return *ret; +} + +void GlobalThreadPoolList::registerThreadPool( + ThreadPoolListHook* threadPoolId, + std::string name) { + globalListImpl_->registerThreadPool(threadPoolId, name); +} + +void GlobalThreadPoolList::unregisterThreadPool( + ThreadPoolListHook* threadPoolId) { + globalListImpl_->unregisterThreadPool(threadPoolId); +} + +void GlobalThreadPoolList::registerThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId) { + DCHECK(!threadHook_); + threadHook_.reset(make_unique(threadPoolId, threadId)); + + globalListImpl_->registerThreadPoolThread(threadPoolId, threadId); +} + +void GlobalThreadPoolList::unregisterThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId) { + (void)threadPoolId; + (void)threadId; + globalListImpl_->unregisterThreadPoolThread(threadPoolId, threadId); +} + +void GlobalThreadPoolListImpl::registerThreadPool( + ThreadPoolListHook* threadPoolId, + std::string name) { + PoolInfo info; + info.name = name; + info.addr = threadPoolId; + pools_.vector().push_back(info); +} + +void GlobalThreadPoolListImpl::unregisterThreadPool( + ThreadPoolListHook* threadPoolId) { + auto& vector = pools_.vector(); + vector.erase( + std::remove_if( + vector.begin(), + vector.end(), + [=](PoolInfo& i) { return i.addr == threadPoolId; }), + vector.end()); +} + +void GlobalThreadPoolListImpl::registerThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId) { + auto vec = pools_.getThreadVector(threadPoolId); + if (vec == nullptr) { + return; + } + + vec->push_back(threadId); +} + +void GlobalThreadPoolListImpl::unregisterThreadPoolThread( + ThreadPoolListHook* threadPoolId, + std::thread::id threadId) { + auto vec = pools_.getThreadVector(threadPoolId); + if (vec == nullptr) { + return; + } + + vec->erase(std::remove(vec->begin(), vec->end(), threadId), vec->end()); +} + +ThreadListHook::ThreadListHook( + ThreadPoolListHook* poolId, + std::thread::id threadId) { + poolId_ = poolId; + threadId_ = threadId; +} + +ThreadListHook::~ThreadListHook() { + GlobalThreadPoolList::instance().unregisterThreadPoolThread( + poolId_, threadId_); +} + +ThreadPoolListHook::ThreadPoolListHook(std::string name) { + GlobalThreadPoolList::instance().registerThreadPool(this, name); +} + +ThreadPoolListHook::~ThreadPoolListHook() { + GlobalThreadPoolList::instance().unregisterThreadPool(this); +} + +void ThreadPoolListHook::registerThread() { + GlobalThreadPoolList::instance().registerThreadPoolThread( + this, std::this_thread::get_id()); +} + +} // folly diff --git a/folly/GlobalThreadPoolList.h b/folly/GlobalThreadPoolList.h new file mode 100644 index 00000000..8e13df08 --- /dev/null +++ b/folly/GlobalThreadPoolList.h @@ -0,0 +1,61 @@ +/* + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace folly { + +/** + * A hook for tracking which threads belong to which thread pools. + * This is used only by a gdb extension to aid in debugging. You won't be able + * to see any useful information from within C++ code. + * + * An instance of ThreadPoolListHook should be created in the thread pool class + * that you want to keep track of. Then, to register a thread you call + * registerThread() on your instance of ThreadPoolListHook from that thread. + * + * When a thread exits it will be removed from the list + * When the thread pool is destroyed, it will be removed from the list + */ +class ThreadPoolListHook { + public: + /** + * Name is used to identify the thread pool when listing threads. + */ + explicit ThreadPoolListHook(std::string name); + ~ThreadPoolListHook(); + + /** + * Call this from any new thread that the thread pool creates. + */ + void registerThread(); + + ThreadPoolListHook(const ThreadPoolListHook& other) = delete; + ThreadPoolListHook& operator=(const ThreadPoolListHook&) = delete; + + private: + ThreadPoolListHook(); +}; + +} // folly -- 2.34.1