forked from microsoft/vs-threading
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathThreadPoolExhausted.cpp
More file actions
143 lines (124 loc) · 3.94 KB
/
Copy pathThreadPoolExhausted.cpp
File metadata and controls
143 lines (124 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include "stdafx.h"
#include "dbgexts.h"
#include "Helpers.h"
static int s_iThresholdToDetectThreadPoolExhausted = 20;
static int s_iThresholdToBlameCallStack = 10;
struct Thread
{
int m_id;
std::vector<std::string> m_frames;
};
struct LessThanFrames
{
bool operator() (const std::vector<std::string> *pLeft, const std::vector<std::string> *pRight) const
{
if (pLeft->size() != pRight->size())
{
return pLeft->size() < pRight->size();
}
for (size_t i = 0; i < pLeft->size(); ++i)
{
int result = pLeft->at(i).compare(pRight->at(i));
if (result != 0)
{
return result < 0;
}
}
return false;
}
};
bool IsThreadPoolExhausted(
PDEBUG_CLIENT pDebugClient,
const std::string &strSOS)
{
// !ThreadPool
std::string strOutput;
ThreadPoolStatus threadPoolStatus;
if (SUCCEEDED(Execute(pDebugClient, GetFullCommand(strSOS, "ThreadPool"), strOutput))
&& GetThreadPoolStatus(strOutput, threadPoolStatus)
&& threadPoolStatus.m_iRunningWorkers >= s_iThresholdToDetectThreadPoolExhausted)
{
return true;
}
return false;
}
bool IsClrThreadPoolWorkingThread(const std::string &strOutput)
{
std::stringstream ss(strOutput);
std::string strLine;
while (std::getline(ss, strLine))
{
if (strLine.find("clr!ThreadpoolMgr::ExecuteWorkRequest") != std::string::npos)
{
return true;
}
}
return false;
}
HRESULT OnThreadPoolExhausted(
PDEBUG_CLIENT pDebugClient,
const std::string &strSOS)
{
CComQIPtr<IDebugControl> srpControl(pDebugClient);
HRESULT hr = S_OK;
int iThreads = GetNumberOfThreads(pDebugClient);
std::string strOutput;
std::vector<Thread> threads;
threads.reserve(iThreads);
for (int i = 1; i < iThreads; ++i) // Skip the main thread
{
char szCommand[0x20] = { 0 };
if (sprintf_s(szCommand, "~%dk", i) > 0
&& SUCCEEDED(Execute(pDebugClient, szCommand, strOutput))
&& IsClrThreadPoolWorkingThread(strOutput))
{
std::vector<std::string> frames;
std::stringstream ss(strOutput);
std::string strLine;
std::string strFunc;
while (std::getline(ss, strLine))
{
if (ParseKStackFrame(strLine, strFunc))
{
frames.push_back(strFunc);
}
}
if (!frames.empty())
{
threads.push_back({ i, std::move(frames) });
}
}
}
for (const Thread &thread : threads)
{
srpControl->Output(DEBUG_OUTPUT_VERBOSE, "%d\n", thread.m_id);
for (const std::string &strFrame : thread.m_frames)
{
srpControl->Output(DEBUG_OUTPUT_VERBOSE, "\t%s\n", strFrame.c_str());
}
}
// Group the threads by the frames
std::map<const std::vector<std::string> *, int, LessThanFrames> groups;
for (const Thread &thread : threads)
{
const std::vector<std::string> *pKey = &thread.m_frames;
++groups[pKey];
}
// Sort by count of the threads
std::vector<std::pair<const std::vector<std::string> *, int>> items(groups.size());
std::copy(groups.cbegin(), groups.cend(), items.begin());
std::sort(items.begin(), items.end(), [](const auto &x, const auto &y) { return x.second > y.second; });
for (const auto &item : items)
{
if (item.second >= s_iThresholdToBlameCallStack)
{
srpControl->Output(DEBUG_OUTPUT_NORMAL, "Detected thread pool exhausted due to this callstack: (%d threads)\n", item.second);
for (const std::string &strFrame : *item.first)
{
srpControl->Output(DEBUG_OUTPUT_NORMAL, "\t%s\n", strFrame.c_str());
}
srpControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
}
}
return hr;
}