Commit 2db01ebc authored by Misha Shneerson's avatar Misha Shneerson Committed by Facebook GitHub Bot

Add BPF task stats tracepoint to TM and CPU thread pool executor

Summary:
Allows attaching a BPF script to observe tasks behavior

Sample script, attaching those probes to a test binary (./buck-out/gen/common/services/cpp/test/thrift_2_async_server) is provided.

Reviewed By: yfeldblum

Differential Revision: D21526163

fbshipit-source-id: e976ddada1921c8c904d65aa9a558bbb45bf158f
parent 1d98189f
...@@ -115,7 +115,7 @@ void IOThreadPoolExecutor::add( ...@@ -115,7 +115,7 @@ void IOThreadPoolExecutor::add(
auto ioThread = pickThread(); auto ioThread = pickThread();
auto task = Task(std::move(func), expiration, std::move(expireCallback)); auto task = Task(std::move(func), expiration, std::move(expireCallback));
auto wrappedFunc = [ioThread, task = std::move(task)]() mutable { auto wrappedFunc = [this, ioThread, task = std::move(task)]() mutable {
runTask(ioThread, std::move(task)); runTask(ioThread, std::move(task));
ioThread->pendingTasks--; ioThread->pendingTasks--;
}; };
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <folly/executors/GlobalThreadPoolList.h> #include <folly/executors/GlobalThreadPoolList.h>
#include <folly/synchronization/AsymmetricMemoryBarrier.h> #include <folly/synchronization/AsymmetricMemoryBarrier.h>
#include <folly/tracing/StaticTracepoint.h>
namespace folly { namespace folly {
...@@ -54,7 +55,9 @@ ThreadPoolExecutor::ThreadPoolExecutor( ...@@ -54,7 +55,9 @@ ThreadPoolExecutor::ThreadPoolExecutor(
taskStatsCallbacks_(std::make_shared<TaskStatsCallbackRegistry>()), taskStatsCallbacks_(std::make_shared<TaskStatsCallbackRegistry>()),
threadPoolHook_("folly::ThreadPoolExecutor"), threadPoolHook_("folly::ThreadPoolExecutor"),
minThreads_(minThreads), minThreads_(minThreads),
threadTimeout_(FLAGS_threadtimeout_ms) {} threadTimeout_(FLAGS_threadtimeout_ms) {
namePrefix_ = getNameHelper();
}
ThreadPoolExecutor::~ThreadPoolExecutor() { ThreadPoolExecutor::~ThreadPoolExecutor() {
joinKeepAliveOnce(); joinKeepAliveOnce();
...@@ -102,6 +105,21 @@ void ThreadPoolExecutor::runTask(const ThreadPtr& thread, Task&& task) { ...@@ -102,6 +105,21 @@ void ThreadPoolExecutor::runTask(const ThreadPtr& thread, Task&& task) {
} }
stats.runTime = std::chrono::steady_clock::now() - startTime; stats.runTime = std::chrono::steady_clock::now() - startTime;
} }
// Times in this USDT use granularity of std::chrono::steady_clock::duration,
// which is platform dependent. On Facebook servers, the granularity is
// nanoseconds. We explicitly do not perform any unit conversions to avoid
// unneccessary costs and leave it to consumers of this data to know what
// effective clock resolution is.
FOLLY_SDT(
folly,
thread_pool_executor_task_stats,
namePrefix_.c_str(),
stats.requestId,
stats.enqueueTime.time_since_epoch().count(),
stats.waitTime.count(),
stats.runTime.count());
thread->idle = true; thread->idle = true;
thread->lastActiveTime = std::chrono::steady_clock::now(); thread->lastActiveTime = std::chrono::steady_clock::now();
thread->taskStatsCallbacks->callbackList.withRLock([&](auto& callbacks) { thread->taskStatsCallbacks->callbackList.withRLock([&](auto& callbacks) {
...@@ -301,12 +319,15 @@ size_t ThreadPoolExecutor::getPendingTaskCount() const { ...@@ -301,12 +319,15 @@ size_t ThreadPoolExecutor::getPendingTaskCount() const {
return getPendingTaskCountImpl(); return getPendingTaskCountImpl();
} }
std::string ThreadPoolExecutor::getName() const { const std::string& ThreadPoolExecutor::getName() const {
return namePrefix_;
}
std::string ThreadPoolExecutor::getNameHelper() const {
auto ntf = dynamic_cast<NamedThreadFactory*>(threadFactory_.get()); auto ntf = dynamic_cast<NamedThreadFactory*>(threadFactory_.get());
if (ntf == nullptr) { if (ntf == nullptr) {
return folly::demangle(typeid(*this).name()).toStdString(); return folly::demangle(typeid(*this).name()).toStdString();
} }
return ntf->getNamePrefix(); return ntf->getNamePrefix();
} }
......
...@@ -80,6 +80,7 @@ class ThreadPoolExecutor : public DefaultKeepAliveExecutor { ...@@ -80,6 +80,7 @@ class ThreadPoolExecutor : public DefaultKeepAliveExecutor {
void setThreadFactory(std::shared_ptr<ThreadFactory> threadFactory) { void setThreadFactory(std::shared_ptr<ThreadFactory> threadFactory) {
CHECK(numThreads() == 0); CHECK(numThreads() == 0);
threadFactory_ = std::move(threadFactory); threadFactory_ = std::move(threadFactory);
namePrefix_ = getNameHelper();
} }
std::shared_ptr<ThreadFactory> getThreadFactory() const { std::shared_ptr<ThreadFactory> getThreadFactory() const {
...@@ -122,7 +123,7 @@ class ThreadPoolExecutor : public DefaultKeepAliveExecutor { ...@@ -122,7 +123,7 @@ class ThreadPoolExecutor : public DefaultKeepAliveExecutor {
PoolStats getPoolStats() const; PoolStats getPoolStats() const;
size_t getPendingTaskCount() const; size_t getPendingTaskCount() const;
std::string getName() const; const std::string& getName() const;
struct TaskStats { struct TaskStats {
TaskStats() : expired(false), waitTime(0), runTime(0), requestId(0) {} TaskStats() : expired(false), waitTime(0), runTime(0), requestId(0) {}
...@@ -212,7 +213,7 @@ class ThreadPoolExecutor : public DefaultKeepAliveExecutor { ...@@ -212,7 +213,7 @@ class ThreadPoolExecutor : public DefaultKeepAliveExecutor {
std::shared_ptr<folly::RequestContext> context_; std::shared_ptr<folly::RequestContext> context_;
}; };
static void runTask(const ThreadPtr& thread, Task&& task); void runTask(const ThreadPtr& thread, Task&& task);
// The function that will be bound to pool threads. It must call // The function that will be bound to pool threads. It must call
// thread->startupBaton.post() when it's ready to consume work. // thread->startupBaton.post() when it's ready to consume work.
...@@ -298,7 +299,10 @@ class ThreadPoolExecutor : public DefaultKeepAliveExecutor { ...@@ -298,7 +299,10 @@ class ThreadPoolExecutor : public DefaultKeepAliveExecutor {
std::queue<ThreadPtr> queue_; std::queue<ThreadPtr> queue_;
}; };
std::string getNameHelper() const;
std::shared_ptr<ThreadFactory> threadFactory_; std::shared_ptr<ThreadFactory> threadFactory_;
std::string namePrefix_;
const bool isWaitForAll_; // whether to wait till event base loop exits const bool isWaitForAll_; // whether to wait till event base loop exits
ThreadList threadList_; ThreadList threadList_;
......
...@@ -45,7 +45,7 @@ class NamedThreadFactory : public ThreadFactory { ...@@ -45,7 +45,7 @@ class NamedThreadFactory : public ThreadFactory {
prefix_ = prefix.str(); prefix_ = prefix.str();
} }
std::string getNamePrefix() { const std::string& getNamePrefix() {
return prefix_; return prefix_;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment