diff --git a/CPUSTRES/CPUSTRES.rc b/CPUSTRES/CPUSTRES.rc
new file mode 100644
index 0000000..d3073e2
Binary files /dev/null and b/CPUSTRES/CPUSTRES.rc differ
diff --git a/CPUSTRES/CPUStressEx.rc b/CPUSTRES/CPUStressEx.rc
index 9f8af89..84555a5 100644
--- a/CPUSTRES/CPUStressEx.rc
+++ b/CPUSTRES/CPUStressEx.rc
@@ -107,6 +107,7 @@ BEGIN
MENUITEM SEPARATOR
MENUITEM "Create &Thread", ID_PROCESS_CREATETHREAD
MENUITEM "Create &4 Threads", ID_PROCESS_CREATE4THREADS
+ MENUITEM "&Queue Thread Pool Work", ID_PROCESS_QUEUETHREADPOOLWORK
MENUITEM SEPARATOR
MENUITEM "&Refresh\tF5", ID_PROCESS_REFRESH
END
@@ -251,12 +252,12 @@ END
IDD_ABOUTBOX DIALOGEX 0, 0, 263, 84
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "About CPUStressEx"
+CAPTION "About CPUSTRES"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
ICON IDR_MAINFRAME,IDC_STATIC,14,14,20,20
- LTEXT "CPUSTRES v2.0",IDC_STATIC,42,14,114,8,SS_NOPREFIX
- LTEXT "Copyright (C) 2016 Pavel Yosifovich",IDC_STATIC,42,26,170,8
+ LTEXT "CPUSTRES v2.1",IDC_STATIC,42,14,114,8,SS_NOPREFIX
+ LTEXT "Copyright (C) 2016-2018 Pavel Yosifovich",IDC_STATIC,42,26,170,8
DEFPUSHBUTTON "OK",IDOK,106,63,50,14,WS_GROUP
CONTROL "Sysinternals - www.sysinternals.com",IDC_LINK,"MfcLink",WS_TABSTOP,39,38,182,14
END
@@ -596,6 +597,12 @@ BEGIN
AFX_IDS_SCTASKLIST "Activate Task List"
END
+STRINGTABLE
+BEGIN
+ ID_STATUS_THREAD "Total Threads: 000"
+ ID_STATUS_PROCESSCPU "Process CPU: 100.10%"
+END
+
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
diff --git a/CPUSTRES/CPUStressEx.vcxproj b/CPUSTRES/CPUStressEx.vcxproj
index e5f1607..d2a3f46 100644
--- a/CPUSTRES/CPUStressEx.vcxproj
+++ b/CPUSTRES/CPUStressEx.vcxproj
@@ -147,6 +147,7 @@
Windows
true
true
+ false
false
@@ -173,6 +174,7 @@
Windows
true
true
+ false
false
@@ -192,6 +194,7 @@
+
@@ -217,6 +220,7 @@
+
diff --git a/CPUSTRES/CPUStressEx.vcxproj.filters b/CPUSTRES/CPUStressEx.vcxproj.filters
index 369628a..aeb71bb 100644
--- a/CPUSTRES/CPUStressEx.vcxproj.filters
+++ b/CPUSTRES/CPUStressEx.vcxproj.filters
@@ -51,6 +51,9 @@
Header Files
+
+ Header Files
+
@@ -88,6 +91,9 @@
Resource Files
+
+ Resource Files
+
diff --git a/CPUSTRES/ChildView.cpp b/CPUSTRES/ChildView.cpp
index cc63db2..ae01332 100644
--- a/CPUSTRES/ChildView.cpp
+++ b/CPUSTRES/ChildView.cpp
@@ -59,6 +59,10 @@ BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_COMMAND(ID_CPUSETS_PROCESSCPUSET, &CChildView::OnCpusetsProcesscpuset)
ON_COMMAND(ID_CPUSETS_THREADSELECTEDCPUSET, &CChildView::OnCpusetsThreadselectedcpuset)
ON_UPDATE_COMMAND_UI(ID_CPUSETS_THREADSELECTEDCPUSET, &CChildView::OnUpdateCpusetsThreadselectedcpuset)
+ ON_COMMAND(ID_PROCESS_QUEUETHREADPOOLWORK, &CChildView::OnProcessQueuethreadpoolwork)
+
+ ON_UPDATE_COMMAND_UI(ID_STATUS_THREAD, OnUpdateStatusThreads)
+ ON_UPDATE_COMMAND_UI(ID_STATUS_PROCESSCPU, OnUpdateStatusProcessCpu)
END_MESSAGE_MAP()
void CChildView::DoDataExchange(CDataExchange* pDX) {
@@ -111,10 +115,15 @@ int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct) {
m_List.InsertColumn(4, L"Priority", LVCFMT_LEFT, 100);
m_List.InsertColumn(5, L"Ideal CPU", LVCFMT_CENTER, 80);
m_List.InsertColumn(6, L"Affinity", LVCFMT_RIGHT, 120);
+ m_List.InsertColumn(7, L"CPU (%)", LVCFMT_RIGHT, 80);
+
+ VERIFY(::QueryPerformanceFrequency(&m_QueryFrequency));
+ VERIFY(::QueryPerformanceCounter(&m_LastQueryCount));
CreateThreads();
SetTimer(1, 10000, nullptr);
+ SetTimer(2, 1000, nullptr);
return 0;
}
@@ -170,6 +179,7 @@ PCWSTR CChildView::ThreadPriorityToString(int priority) {
void CChildView::UpdateThreadProcessIndices() {
auto threads = CGlobals::EnumerateThreads(::GetCurrentProcessId());
+ m_TotalThreads = static_cast(threads.size());
if (m_CurrentThreadIds == threads)
return;
@@ -192,6 +202,21 @@ void CChildView::UpdateThreadProcessIndices() {
}
}
+BOOL CChildView::QueueItemForThreadPool(int busyPercent) {
+ return ::QueueUserWorkItem([](auto param) {
+ return BurnSomeCycles(static_cast(reinterpret_cast(param)));
+ }, reinterpret_cast(static_cast(busyPercent)), WT_EXECUTEDEFAULT);
+}
+
+DWORD CChildView::BurnSomeCycles(int percent) {
+ int time = (int)::GetTickCount();
+ while ((int)::GetTickCount() - time < percent * 10)
+ ; // burn CPU cycles
+
+ ::Sleep(1000 - percent * 10);
+ return percent;
+}
+
unique_ptr CChildView::CreateThread() {
auto thread = make_unique();
return thread;
@@ -342,7 +367,15 @@ void CChildView::OnThreadKill() {
}
void CChildView::OnTimer(UINT_PTR nIDEvent) {
- UpdateThreadProcessIndices();
+ switch (nIDEvent) {
+ case 1:
+ UpdateThreadProcessIndices();
+ break;
+
+ case 2:
+ UpdateCPUTimes();
+ break;
+ }
}
void CChildView::OnProcessRefresh() {
@@ -414,3 +447,54 @@ void CChildView::OnCpusetsThreadselectedcpuset() {
void CChildView::OnUpdateCpusetsThreadselectedcpuset(CCmdUI *pCmdUI) {
pCmdUI->Enable(m_List.GetSelectedCount() == 1);
}
+
+
+void CChildView::OnProcessQueuethreadpoolwork() {
+ if (!QueueItemForThreadPool(50)) {
+ AfxMessageBox(L"Failed to queue work item to thread pool");
+ return;
+ }
+}
+
+void CChildView::OnUpdateStatusThreads(CCmdUI* pCmdUI) {
+ CString text;
+ text.Format(L"Total threads: %d", m_TotalThreads);
+ pCmdUI->SetText(text);
+}
+
+void CChildView::UpdateCPUTimes() {
+ int i = 0;
+ CString text;
+ for (auto& thread : m_Threads) {
+ if (thread->IsActive()) {
+ auto cpu = thread->GetCPUTime(m_QueryFrequency) / CGlobals::GetProcessorCount();
+ text.Format(L"%.2f", cpu / 100000.0);
+ m_List.SetItemText(i, 7, text);
+ }
+ else {
+ m_List.SetItemText(i, 7, L"");
+ }
+ i++;
+ }
+
+ FILETIME dummy, kernel, user;
+ VERIFY(::GetProcessTimes(::GetCurrentProcess(), &dummy, &dummy, &kernel, &user));
+
+ LARGE_INTEGER counter;
+ VERIFY(::QueryPerformanceCounter(&counter));
+ auto total = *(long long*)&kernel + *(long long*)&user;
+ ULONG cpu = 0;
+ if (m_LastProcessTimes > 0) {
+ cpu = static_cast((total - m_LastProcessTimes) * 1000000LL / ((counter.QuadPart - m_LastQueryCount.QuadPart) * 1000000LL / m_QueryFrequency.QuadPart));
+ m_ProcessCPU = cpu / CGlobals::GetProcessorCount() / 100000.0;
+ }
+
+ m_LastQueryCount = counter;
+ m_LastProcessTimes = total;
+}
+
+void CChildView::OnUpdateStatusProcessCpu(CCmdUI* pCmdUI) {
+ CString text;
+ text.Format(L"Process CPU: %.2f %%", m_ProcessCPU);
+ pCmdUI->SetText(text);
+}
diff --git a/CPUSTRES/ChildView.h b/CPUSTRES/ChildView.h
index 74bdada..d08bff5 100644
--- a/CPUSTRES/ChildView.h
+++ b/CPUSTRES/ChildView.h
@@ -33,6 +33,10 @@ private:
std::vector < std::unique_ptr> m_Threads;
std::vector m_CurrentThreadIds;
bool m_AutoRefreshThreadIndices = true;
+ int m_TotalThreads;
+ LARGE_INTEGER m_LastQueryCount, m_QueryFrequency;
+ long long m_LastProcessTimes = 0;
+ double m_ProcessCPU;
void CreateThreads();
std::unique_ptr CreateThread();
@@ -40,6 +44,9 @@ private:
static PCWSTR ActivityLevelToString(ActivityLevel level);
static PCWSTR ThreadPriorityToString(int priority);
void UpdateThreadProcessIndices();
+ BOOL QueueItemForThreadPool(int busyPercent);
+ static DWORD CALLBACK BurnSomeCycles(int percent);
+ void UpdateCPUTimes();
// Generated message map functions
protected:
@@ -76,9 +83,12 @@ protected:
afx_msg void OnOptionsAutorefreshthreadindices();
afx_msg void OnUpdateOptionsAutorefreshthreadindices(CCmdUI *pCmdUI);
afx_msg void OnCpusetsSystemcpuset();
-public:
afx_msg void OnCpusetsProcesscpuset();
afx_msg void OnCpusetsThreadselectedcpuset();
afx_msg void OnUpdateCpusetsThreadselectedcpuset(CCmdUI *pCmdUI);
+ afx_msg void OnProcessQueuethreadpoolwork();
+
+ void OnUpdateStatusThreads(CCmdUI*);
+ void OnUpdateStatusProcessCpu(CCmdUI*);
};
diff --git a/CPUSTRES/MainFrm.cpp b/CPUSTRES/MainFrm.cpp
index a55b6f9..16ea516 100644
--- a/CPUSTRES/MainFrm.cpp
+++ b/CPUSTRES/MainFrm.cpp
@@ -39,7 +39,7 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
return -1;
// create a view to occupy the client area of the frame
- if(!m_wndView.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(), this, AFX_IDW_PANE_FIRST)) {
+ if(!m_wndView.Create(nullptr, nullptr, AFX_WS_DEFAULT_VIEW, CRect(), this, AFX_IDW_PANE_FIRST)) {
TRACE0("Failed to create view window\n");
return -1;
}
@@ -49,6 +49,11 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
return -1;
}
+ m_StatusBar.Create(this);
+ UINT indicators[] = { ID_STATUS_THREAD, ID_STATUS_PROCESSCPU };
+
+ m_StatusBar.SetIndicators(indicators, _countof(indicators));
+
SetProcessAffinityText();
SetProcessPriorityClassText();
diff --git a/CPUSTRES/MainFrm.h b/CPUSTRES/MainFrm.h
index 1cae3d1..4c50b3b 100644
--- a/CPUSTRES/MainFrm.h
+++ b/CPUSTRES/MainFrm.h
@@ -37,6 +37,7 @@ public:
protected: // control bar embedded members
CDialogBar m_wndDlgBar;
CChildView m_wndView;
+ CStatusBar m_StatusBar;
// Generated message map functions
protected:
diff --git a/CPUSTRES/RCa25180 b/CPUSTRES/RCa25180
new file mode 100644
index 0000000..563649c
Binary files /dev/null and b/CPUSTRES/RCa25180 differ
diff --git a/CPUSTRES/Thread.cpp b/CPUSTRES/Thread.cpp
index 64a87be..145b39d 100644
--- a/CPUSTRES/Thread.cpp
+++ b/CPUSTRES/Thread.cpp
@@ -10,7 +10,7 @@
CThread::CThread() {
int cpus = CGlobals::GetProcessorCount();
- if(cpus == sizeof(DWORD_PTR) * 8)
+ if (cpus == sizeof(DWORD_PTR) * 8)
m_Affinity = (DWORD_PTR)-1;
else
m_Affinity = (((DWORD_PTR)1) << cpus) - 1;
@@ -21,7 +21,7 @@ CThread::CThread() {
CThread::~CThread() {
::CloseHandle(m_hThread);
::CloseHandle(m_hTerminate);
-}
+}
void CThread::Suspend() {
if (!IsActive()) return;
@@ -46,7 +46,7 @@ DWORD CThread::ThreadFunction() {
for (;;) {
if (::WaitForSingleObject(hTerminate, 0) == WAIT_OBJECT_0)
break;
-
+
auto level = m_ActivityLevel;
if (level != ActivityLevel::Maximum) {
auto time = ::GetTickCount();
@@ -77,11 +77,28 @@ int CThread::GetIdealCPU() const {
return n.Number;
}
+ULONG CThread::GetCPUTime(const LARGE_INTEGER& frequency) const {
+ FILETIME kernel, user, dummy;
+ VERIFY(::GetThreadTimes(m_hThread, &dummy, &dummy, &kernel, &user));
+ LARGE_INTEGER counter;
+ VERIFY(::QueryPerformanceCounter(&counter));
+ auto total = *(long long*)&kernel + *(long long*)&user;
+ ULONG cpu = 0;
+ if (m_LastCpu > 0) {
+ cpu = static_cast((total - m_LastCpu) * 1000000LL / ((counter.QuadPart - m_LastCounter.QuadPart) * 1000000LL / frequency.QuadPart));
+ }
+
+ m_LastCounter = counter;
+ m_LastCpu = total;
+
+ return cpu;
+}
+
void CThread::Terminate() {
m_ActivityLevel = ActivityLevel::None;
::SetEvent(m_hTerminate);
Resume();
- if(::WaitForSingleObject(m_hThread, 500) == WAIT_TIMEOUT)
+ if (::WaitForSingleObject(m_hThread, 500) == WAIT_TIMEOUT)
::TerminateThread(m_hThread, 1);
}
diff --git a/CPUSTRES/Thread.h b/CPUSTRES/Thread.h
index 02785d7..fa2e7ed 100644
--- a/CPUSTRES/Thread.h
+++ b/CPUSTRES/Thread.h
@@ -50,6 +50,8 @@ public:
DWORD SetIdealCPU(int n);
int GetIdealCPU() const;
+ ULONG GetCPUTime(const LARGE_INTEGER& frequency) const;
+
void Terminate();
void Suspend();
void Resume();
@@ -67,6 +69,8 @@ private:
ActivityLevel m_ActivityLevel = ActivityLevel::Low;
bool m_Active = false;
DWORD_PTR m_Affinity;
+ mutable long long m_LastCpu = 0;
+ mutable LARGE_INTEGER m_LastCounter;
};
diff --git a/CPUSTRES/resource.h b/CPUSTRES/resource.h
index ee7571e..60f70fd 100644
--- a/CPUSTRES/resource.h
+++ b/CPUSTRES/resource.h
@@ -5,6 +5,8 @@
#define IDD_ABOUTBOX 100
#define IDR_MAINFRAME 128
#define IDR_CPUStressExTYPE 130
+#define ID_STATUS_THREAD 310
+#define ID_STATUS_PROCESSCPU 311
#define IDI_PLAY 312
#define IDI_KILL 313
#define IDI_PAUSE 314
@@ -21,7 +23,6 @@
#define IDC_MFCLINK1 1008
#define IDC_LINK 1008
#define IDC_CPUSETS 1009
-#define IDC_BUTTON1 1010
#define ID_THREAD_ACTIVE 32771
#define ID_THREAD_ACTIVITYLEVEL 32772
#define ID_ACTIVITYLEVEL_LOW 32773
@@ -58,14 +59,15 @@
#define ID_CPUSETS_SYSTEMCPUSET 32807
#define ID_CPUSETS_PROCESSCPUSET 32808
#define ID_CPUSETS_THREADSELECTEDCPUSET 32809
+#define ID_PROCESS_QUEUETHREADPOOLWORK 32810
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 323
-#define _APS_NEXT_COMMAND_VALUE 32810
+#define _APS_NEXT_COMMAND_VALUE 32811
#define _APS_NEXT_CONTROL_VALUE 1011
-#define _APS_NEXT_SYMED_VALUE 310
+#define _APS_NEXT_SYMED_VALUE 312
#endif
#endif
diff --git a/CPUSTRES/resource1.h b/CPUSTRES/resource1.h
new file mode 100644
index 0000000..9f31b16
--- /dev/null
+++ b/CPUSTRES/resource1.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by CPUSTRES.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/MemLimit/MemLimit.cpp b/MemLimit/MemLimit.cpp
index 0f2f8c5..aa7322d 100644
Binary files a/MemLimit/MemLimit.cpp and b/MemLimit/MemLimit.cpp differ