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