Skip to content

Instantly share code, notes, and snippets.

@nico
Created April 17, 2017 15:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nico/f3a1b13d5460194c8dcbee27756079f3 to your computer and use it in GitHub Desktop.
Save nico/f3a1b13d5460194c8dcbee27756079f3 to your computer and use it in GitHub Desktop.
tim.exe memory tracking
diff --git a/tim.cc b/tim.cc
index 4d8200e..f6cfc19 100644
--- a/tim.cc
+++ b/tim.cc
@@ -16,7 +16,11 @@
#include <stdlib.h>
#include <windows.h>
+#include <psapi.h> // Must be after windows.h
+
#include <algorithm>
+#include <string>
+using namespace std;
LPWSTR FindStartOfSubCommand(LPWSTR orig_command, bool* want_avg) {
int num_args;
@@ -42,6 +46,25 @@ struct Result {
DWORD exit_code;
};
+wstring GetLastErrorString() {
+ DWORD err = GetLastError();
+
+ wchar_t* msg_buf;
+ FormatMessageW(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (wchar_t*)&msg_buf,
+ 0,
+ NULL);
+ wstring msg = msg_buf;
+ LocalFree(msg_buf);
+ return msg;
+}
+
Result Run(LPWSTR command) {
STARTUPINFO startup_info = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION process_info;
@@ -55,17 +78,51 @@ Result Run(LPWSTR command) {
QueryPerformanceFrequency(&qpc_frequency);
QueryPerformanceCounter(&qpc_start_time);
- if (!CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL,
- &startup_info, &process_info)) {
+ if (!CreateProcess(NULL, command, NULL, NULL, TRUE,
+ CREATE_SUSPENDED,
+ NULL, NULL, &startup_info, &process_info)) {
wprintf(L"tim: Could not spawn subprocess, command line:\n"
L"%ws\n"
L"You may need to prefix the command with \"cmd /c \".\n",
command);
exit(1);
}
+ HANDLE job = CreateJobObject(NULL, NULL);
+ if (!AssignProcessToJobObject(job, process_info.hProcess)) {
+ wprintf(L"failed to AssignProcessToJobObject %s\n",
+ GetLastErrorString().c_str());
+ }
+ ResumeThread(process_info.hThread);
+
CloseHandle(process_info.hThread);
WaitForSingleObject(process_info.hProcess, INFINITE);
+
+ // Collect additional stuff. This is cool, but doesn't get subprocess data.
+ PROCESS_MEMORY_COUNTERS_EX pmc;
+ IO_COUNTERS ioc;
+ GetProcessMemoryInfo(process_info.hProcess,
+ (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc));
+ GetProcessIoCounters(process_info.hProcess, &ioc);
+
GetExitCodeProcess(process_info.hProcess, &result.exit_code);
+
+ //JOBOBJECT_BASIC_PROCESS_ID_LIST* list; // needs dynamic alloc
+ JOBOBJECT_BASIC_ACCOUNTING_INFORMATION acct;
+ if (!QueryInformationJobObject(job, JobObjectBasicAccountingInformation,
+ &acct, sizeof(acct), NULL)) {
+ wprintf(L"failed to QueryInformationJobObject\n");
+ }
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit;
+ if (!QueryInformationJobObject(job, JobObjectExtendedLimitInformation,
+ &limit, sizeof(limit), NULL)) {
+ wprintf(L"failed to QueryInformationJobObject\n");
+ }
+
+ wprintf(L"%ld processes", acct.TotalProcesses);
+
+ CloseHandle(job);
+
+
CloseHandle(process_info.hProcess);
QueryPerformanceCounter(&qpc_end_time);
@@ -74,6 +131,8 @@ Result Run(LPWSTR command) {
result.qpc_elapsed_microseconds.QuadPart *= 1000000;
result.qpc_elapsed_microseconds.QuadPart /= qpc_frequency.QuadPart;
+
+
ULONGLONG end_time = GetTickCount64();
result.elapsed = end_time - start_time;
@@ -81,6 +140,10 @@ Result Run(LPWSTR command) {
result.elapsed / (60 * 1000),
(result.elapsed % (60 * 1000)) / 1000.0,
result.qpc_elapsed_microseconds.QuadPart);
+
+ wprintf(L"\npeak working set: %lld\npeak pagefile: %lld\n",
+ pmc.PeakWorkingSetSize, pmc.PeakPagefileUsage);
+ wprintf(L"\ntotal peak working set: %lld\n", limit.PeakProcessMemoryUsed);
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment