Skip to content

Commit 1497101

Browse files
feat: added uptime counting, more docs and commments
1 parent 32cf8f1 commit 1497101

File tree

4 files changed

+97
-7
lines changed

4 files changed

+97
-7
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ I heard that the original dev wasn't planning to make a Windows version for it.
2525
He was probably right.
2626
Oh it is such a pain to work with the windows kernel.
2727
Windows is the most popular and the most user friendly and organized looking OS but the SECOND you peek inside it is such a garbling mess of WEIRD stuff. It's so weird.
28-
Some quirks I've noticed:
29-
- In Linux, when a parent process dies (such as, a process spawns a child process and then ends itself), the kernel politely adopts the child process to avoid it from floating as an orphan. Windows, on the other hand, is a merciless masochistic psychopath and will leave processes connected to a ghost PID. Sometimes, Windows frees the parent PID up and something immediately snatches it, so a parent PID can be deceiving.
28+
Some quirks I've noticed since I started working on this:
29+
- In Linux, when a parent process dies (such as, a process spawns a child process and then ends itself), the kernel politely adopts the child process to avoid it from floating as an orphan. Windows, on the other hand, is a merciless sadistic psychopath and will leave processes connected to a ghost PID. Sometimes, Windows frees the parent PID up and something immediately snatches it, so a parent PID can be deceiving.
3030
- The kernel stuff is complicated as heck for no reason. Like everything is so separated and needs very weirdly specific workarounds that it's almost eerie.
31+
- To get the uptime of a process, the Windows kernel politely tells you the raw FILETIME of a process. But that isn't exactly... readable. In fact, it represents the amount of 100-nanosecond intervals since January 1, 1601 in the UTC timezone. Microsoft, WHAT? Literally every other piece of software uses Unix time, which is the number of **seconds** since January 1, 1970 in the UTC timezone. In Linux, you can just read the /proc/PID/stat file and get the uptime in seconds.
32+
- Everything in Windows that's stupid like this is simply because of legacy reasons. While newer versions of Windows look like a shiny new (although heavily bloated) OS with a nice user friendly UI and a whole crap ton of WebView2's and RAM hogs, the kernel is still the same mess of code that was written like 30 years ago. To cut them some slack, the Windows NT kernel was worked on by a bunch of people simulatenously which can lead to conflicting ideas, decisions, and other stuff. Linux, on the other hand, was written by one person (Linus Torvalds) and a small team of volunteers, so it has a much more consistent design than whatever this monstrosity is. Anyways, the other reason is compatibility. Even if Microsoft _wants_ to change something, millions of facilities use custom-written applications that rely on old behavior, especially things like hospitals and other infrastructure. (I bet banks are still written in COBOL or FORTRAN tho lol)
3133

3234

33-
I could've definitely done this in one sitting if I only had like 5 more hours but I decided i will stop right here and this is pretty good for the initial commit. 300-400 lines of C++ ain't that bad for 3 hours, and I want to spend the remaining 2 hours of this day to spend New Years with my family.
3435

3536

3637
## AI assistance disclaimer

main.cpp

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
#include <chrono>
1313
#include <thread>
1414
#include <filesystem>
15+
#include <iomanip>
16+
#include <sstream>
17+
#include <ctime>
18+
19+
#define windows_time_to_unix_epoch(x) ((x) - 116444736000000000LL) / 10000000LL
20+
// The above macro converts Windows FILETIME to Unix epoch time in seconds.
21+
// I explain more about why this is needed below and in the README.
22+
// TLDR: FILETIME is an arbitrary number and we need math to convert it into something useful.
23+
// I took this macro from https://stackoverflow.com/a/74650247
24+
// Thanks!
1525

1626
#pragma comment(lib, "advapi32.lib") // For Security/Registry (Elevation check)
1727
#pragma comment(lib, "iphlpapi.lib") // For Network stuff (Port to PID mapping)
@@ -53,6 +63,12 @@ bool IsVirtualTerminalModeEnabled() {
5363

5464
return (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0;
5565
}
66+
// The above function checks if Virtual Terminal mode is enabled.
67+
// This means that the terminal can render things like ANSI escape codes for colors and stuff.
68+
// If we tried spitting out escape codes in a terminal that doesn't support it, it would look like unreadable garbage,
69+
// and that's probably not very pleasant to the user. This is very rare and I have yet to encounter a terminal that doesn't support it,
70+
// but I'm sure there's someone out there using some ANCIENT old version of Windows that doesn't support it, and we want to support this for all versions.
71+
// Who knows, I might even test this on windows XP hahahahahaha...
5672

5773
bool EnableDebugPrivilege() {
5874
HANDLE hToken;
@@ -128,7 +144,7 @@ std::string WideToString(const std::wstring& wstr) {
128144
}
129145
// The above stupid function is to convert wide strings (used by Windows API) to normal strings (used by C++ standard library) because cout chokes on wide strings.
130146

131-
// Helper to get creation time of a PID
147+
132148
ULONGLONG GetProcessCreationTime(DWORD pid) {
133149
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
134150
if (!hProcess) return 0;
@@ -144,8 +160,54 @@ ULONGLONG GetProcessCreationTime(DWORD pid) {
144160
CloseHandle(hProcess);
145161
return 0;
146162
}
163+
// Process uptime helper
164+
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesstimes
165+
// While this does indeed give you the time since the process was created,
166+
// it actually returns a raw FILETIME, meaning it is an unsigned 64-bit integer that
167+
// shows the amount of 100-nanosecond intervals since January 1, 1601 (UTC), which is just straight up not readable, and
168+
// in my opinion INSANELY arbritrary, but I guess Microsoft is Microsoft...
169+
// In short, we need to do some more math on this.
170+
171+
std::string GetReadableFileTime(DWORD pid) {
172+
ULONGLONG creationTime = GetProcessCreationTime(pid);
173+
if (creationTime == 0) return "N/A";
174+
time_t unixTime = windows_time_to_unix_epoch(creationTime);
175+
// Here's the macro we defined earlier! Now we finally have something useful to work with.
176+
time_t now = std::time(nullptr);
177+
double diffSeconds = std::difftime(now, unixTime);
178+
179+
180+
std::string ago;
181+
if (diffSeconds < 60) ago = std::to_string((int)diffSeconds) + " seconds ago";
182+
else if (diffSeconds < 3600) ago = std::to_string((int)diffSeconds / 60) + " minutes ago";
183+
else if (diffSeconds < 86400) ago = std::to_string((int)diffSeconds / 3600) + " hours ago";
184+
else ago = std::to_string((int)diffSeconds / 86400) + " days ago";
185+
186+
187+
std::tm bt{};
188+
localtime_s(&bt, &unixTime);
189+
190+
std::ostringstream oss;
191+
oss << ago << " (" << std::put_time(&bt, "%a %Y-%m-%d %H:%M:%S %z") << ")";
192+
193+
// All this shenanginanny stuff we do with the timestamp is to make it look just like witr's output, which I quote from the README in that repo:
194+
// Started : 2 days ago (Mon 2025-02-02 11:42:10 +05:30)
195+
196+
return oss.str();
197+
}
198+
147199

148200
void PrintAncestry(DWORD pid, int depth = 0) {
201+
202+
/*
203+
TODO: This tree is flipped. The output should be like this, as shown in the original witr:
204+
systemd (pid 1)
205+
└─ PM2 v5.3.1: God (pid 1481580)
206+
└─ python (pid 1482060)
207+
208+
209+
*/
210+
149211
if (pid == 0 || pid == 4) return;
150212

151213
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
@@ -178,7 +240,7 @@ void PrintAncestry(DWORD pid, int depth = 0) {
178240
ULONGLONG parentTime = GetProcessCreationTime(parentPid);
179241

180242
// If parentTime is 0, the parent is dead.
181-
// If parentTime > childTime, the parent is an impostor (recycled PID).
243+
// If parentTime > childTime, the parent is an impostor among us (recycled PID).
182244
if (parentTime != 0 && parentTime < childTime) {
183245
PrintAncestry(parentPid, depth + 1);
184246
} else {
@@ -205,7 +267,7 @@ void PIDinspect(DWORD pid) { // ooh guys look i'm in the void
205267
return;
206268
}
207269

208-
// Query executable path
270+
209271
char exePath[MAX_PATH] = {0};
210272
DWORD size = MAX_PATH;
211273
if (QueryFullProcessImageNameA(hProcess, 0, exePath, &size)) {
@@ -216,10 +278,37 @@ void PIDinspect(DWORD pid) { // ooh guys look i'm in the void
216278
<< "\n Maybe Access is Denied or the process is living in RAM." << std::endl;
217279
}
218280

219-
// Print ancestry chain
281+
282+
// TODO: add color text
283+
220284
std::cout << "\nProcess Ancestry:\n";
221285
PrintAncestry(pid);
222286

287+
std::cout << "\nStarted: " << GetReadableFileTime(pid) << std::endl;
288+
/*
289+
TODO:
290+
This definitely needs a lot more details to be complete like witr. Unfortunately, windows needs even more shenanigans and a whole
291+
lotta more code and admin access to get the same details. I will explain this some other day.
292+
293+
This is the output from witr for reference:
294+
Target : node
295+
296+
Process : node (pid 14233)
297+
User : pm2
298+
Command : node index.js
299+
Started : 2 days ago (Mon 2025-02-02 11:42:10 +05:30)
300+
Restarts : 1
301+
302+
Why It Exists :
303+
systemd (pid 1) → pm2 (pid 5034) → node (pid 14233)
304+
305+
Source : pm2
306+
307+
Working Dir : /opt/apps/expense-manager
308+
Git Repo : expense-manager (main)
309+
Listening : 127.0.0.1:5001
310+
*/
311+
223312
CloseHandle(hProcess);
224313
}
225314

main.exe

29.5 KB
Binary file not shown.

main.obj

121 KB
Binary file not shown.

0 commit comments

Comments
 (0)