aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/zencore/include/zencore/system.h1
-rw-r--r--src/zencore/system.cpp169
-rw-r--r--src/zenhttp/servers/httpsys.cpp25
3 files changed, 156 insertions, 39 deletions
diff --git a/src/zencore/include/zencore/system.h b/src/zencore/include/zencore/system.h
index aec2e0ce4..bf3c15d3d 100644
--- a/src/zencore/include/zencore/system.h
+++ b/src/zencore/include/zencore/system.h
@@ -25,6 +25,7 @@ struct SystemMetrics
uint64_t AvailVirtualMemoryMiB = 0;
uint64_t PageFileMiB = 0;
uint64_t AvailPageFileMiB = 0;
+ float CpuUsagePercent = 0.0f;
};
SystemMetrics GetSystemMetrics();
diff --git a/src/zencore/system.cpp b/src/zencore/system.cpp
index e92691781..267c87e12 100644
--- a/src/zencore/system.cpp
+++ b/src/zencore/system.cpp
@@ -13,6 +13,8 @@
ZEN_THIRD_PARTY_INCLUDES_START
# include <iphlpapi.h>
# include <winsock2.h>
+# include <pdh.h>
+# pragma comment(lib, "pdh.lib")
ZEN_THIRD_PARTY_INCLUDES_END
#elif ZEN_PLATFORM_LINUX
# include <sys/utsname.h>
@@ -65,55 +67,98 @@ GetSystemMetrics()
// Determine physical core count
- DWORD BufferSize = 0;
- BOOL Result = GetLogicalProcessorInformationEx(RelationAll, nullptr, &BufferSize);
- if (int32_t Error = GetLastError(); Error != ERROR_INSUFFICIENT_BUFFER)
{
- ThrowSystemError(Error, "Failed to get buffer size for logical processor information");
- }
-
- PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)Memory::Alloc(BufferSize);
+ DWORD BufferSize = 0;
+ BOOL Result = GetLogicalProcessorInformationEx(RelationAll, nullptr, &BufferSize);
+ if (int32_t Error = GetLastError(); Error != ERROR_INSUFFICIENT_BUFFER)
+ {
+ ThrowSystemError(Error, "Failed to get buffer size for logical processor information");
+ }
- Result = GetLogicalProcessorInformationEx(RelationAll, Buffer, &BufferSize);
- if (!Result)
- {
- Memory::Free(Buffer);
- throw std::runtime_error("Failed to get logical processor information");
- }
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)Memory::Alloc(BufferSize);
- DWORD ProcessorPkgCount = 0;
- DWORD ProcessorCoreCount = 0;
- DWORD ByteOffset = 0;
- while (ByteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) <= BufferSize)
- {
- const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX& Slpi = Buffer[ByteOffset / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)];
- if (Slpi.Relationship == RelationProcessorCore)
+ Result = GetLogicalProcessorInformationEx(RelationAll, Buffer, &BufferSize);
+ if (!Result)
{
- ProcessorCoreCount++;
+ Memory::Free(Buffer);
+ throw std::runtime_error("Failed to get logical processor information");
}
- else if (Slpi.Relationship == RelationProcessorPackage)
+
+ DWORD ProcessorPkgCount = 0;
+ DWORD ProcessorCoreCount = 0;
+ DWORD LogicalProcessorCount = 0;
+
+ BYTE* Ptr = reinterpret_cast<BYTE*>(Buffer);
+ BYTE* const End = Ptr + BufferSize;
+ while (Ptr < End)
{
- ProcessorPkgCount++;
+ const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX& Slpi = *reinterpret_cast<const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*>(Ptr);
+ if (Slpi.Relationship == RelationProcessorCore)
+ {
+ ++ProcessorCoreCount;
+
+ // Count logical processors (threads) across all processor groups for this core.
+ // Each core entry lists one GROUP_AFFINITY per group it spans; each set bit
+ // in the Mask represents one logical processor (HyperThreading sibling).
+ for (WORD g = 0; g < Slpi.Processor.GroupCount; ++g)
+ {
+ LogicalProcessorCount += static_cast<DWORD>(__popcnt64(Slpi.Processor.GroupMask[g].Mask));
+ }
+ }
+ else if (Slpi.Relationship == RelationProcessorPackage)
+ {
+ ++ProcessorPkgCount;
+ }
+ Ptr += Slpi.Size;
}
- ByteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX);
- }
- Metrics.CoreCount = ProcessorCoreCount;
- Metrics.CpuCount = ProcessorPkgCount;
+ Metrics.CoreCount = ProcessorCoreCount;
+ Metrics.CpuCount = ProcessorPkgCount;
+ Metrics.LogicalProcessorCount = LogicalProcessorCount;
- Memory::Free(Buffer);
+ Memory::Free(Buffer);
+ }
// Query memory status
- MEMORYSTATUSEX MemStatus{.dwLength = sizeof(MEMORYSTATUSEX)};
- GlobalMemoryStatusEx(&MemStatus);
+ {
+ MEMORYSTATUSEX MemStatus{.dwLength = sizeof(MEMORYSTATUSEX)};
+ GlobalMemoryStatusEx(&MemStatus);
+
+ Metrics.SystemMemoryMiB = MemStatus.ullTotalPhys / 1024 / 1024;
+ Metrics.AvailSystemMemoryMiB = MemStatus.ullAvailPhys / 1024 / 1024;
+ Metrics.VirtualMemoryMiB = MemStatus.ullTotalVirtual / 1024 / 1024;
+ Metrics.AvailVirtualMemoryMiB = MemStatus.ullAvailVirtual / 1024 / 1024;
+ Metrics.PageFileMiB = MemStatus.ullTotalPageFile / 1024 / 1024;
+ Metrics.AvailPageFileMiB = MemStatus.ullAvailPageFile / 1024 / 1024;
+ }
+
+ // Query CPU usage using PDH
+ //
+ // TODO: This should be changed to not require a Sleep, perhaps by using some
+ // background metrics gathering mechanism.
+
+ {
+ PDH_HQUERY QueryHandle = nullptr;
+ PDH_HCOUNTER CounterHandle = nullptr;
- Metrics.SystemMemoryMiB = MemStatus.ullTotalPhys / 1024 / 1024;
- Metrics.AvailSystemMemoryMiB = MemStatus.ullAvailPhys / 1024 / 1024;
- Metrics.VirtualMemoryMiB = MemStatus.ullTotalVirtual / 1024 / 1024;
- Metrics.AvailVirtualMemoryMiB = MemStatus.ullAvailVirtual / 1024 / 1024;
- Metrics.PageFileMiB = MemStatus.ullTotalPageFile / 1024 / 1024;
- Metrics.AvailPageFileMiB = MemStatus.ullAvailPageFile / 1024 / 1024;
+ if (PdhOpenQueryW(nullptr, 0, &QueryHandle) == ERROR_SUCCESS)
+ {
+ if (PdhAddEnglishCounterW(QueryHandle, L"\\Processor(_Total)\\% Processor Time", 0, &CounterHandle) == ERROR_SUCCESS)
+ {
+ PdhCollectQueryData(QueryHandle);
+ Sleep(100);
+ PdhCollectQueryData(QueryHandle);
+
+ PDH_FMT_COUNTERVALUE CounterValue;
+ if (PdhGetFormattedCounterValue(CounterHandle, PDH_FMT_DOUBLE, nullptr, &CounterValue) == ERROR_SUCCESS)
+ {
+ Metrics.CpuUsagePercent = static_cast<float>(CounterValue.doubleValue);
+ }
+ }
+ PdhCloseQuery(QueryHandle);
+ }
+ }
return Metrics;
}
@@ -190,6 +235,39 @@ GetSystemMetrics()
}
}
+ // Query CPU usage
+ Metrics.CpuUsagePercent = 0.0f;
+ if (FILE* Stat = fopen("/proc/stat", "r"))
+ {
+ char Line[256];
+ unsigned long User, Nice, System, Idle, IoWait, Irq, SoftIrq;
+ static unsigned long PrevUser = 0, PrevNice = 0, PrevSystem = 0, PrevIdle = 0, PrevIoWait = 0, PrevIrq = 0, PrevSoftIrq = 0;
+
+ if (fgets(Line, sizeof(Line), Stat))
+ {
+ if (sscanf(Line, "cpu %lu %lu %lu %lu %lu %lu %lu", &User, &Nice, &System, &Idle, &IoWait, &Irq, &SoftIrq) == 7)
+ {
+ unsigned long TotalDelta = (User + Nice + System + Idle + IoWait + Irq + SoftIrq) -
+ (PrevUser + PrevNice + PrevSystem + PrevIdle + PrevIoWait + PrevIrq + PrevSoftIrq);
+ unsigned long IdleDelta = Idle - PrevIdle;
+
+ if (TotalDelta > 0)
+ {
+ Metrics.CpuUsagePercent = 100.0f * (TotalDelta - IdleDelta) / TotalDelta;
+ }
+
+ PrevUser = User;
+ PrevNice = Nice;
+ PrevSystem = System;
+ PrevIdle = Idle;
+ PrevIoWait = IoWait;
+ PrevIrq = Irq;
+ PrevSoftIrq = SoftIrq;
+ }
+ }
+ fclose(Stat);
+ }
+
// Get memory information
long Pages = sysconf(_SC_PHYS_PAGES);
long PageSize = sysconf(_SC_PAGE_SIZE);
@@ -270,6 +348,25 @@ GetSystemMetrics()
sysctlbyname("hw.packages", &Packages, &Size, nullptr, 0);
Metrics.CpuCount = Packages > 0 ? Packages : 1;
+ // Query CPU usage using host_statistics64
+ Metrics.CpuUsagePercent = 0.0f;
+ host_cpu_load_info_data_t CpuLoad;
+ mach_msg_type_number_t CpuCount = sizeof(CpuLoad) / sizeof(natural_t);
+ if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&CpuLoad, &CpuCount) == KERN_SUCCESS)
+ {
+ unsigned long TotalTicks = 0;
+ for (int i = 0; i < CPU_STATE_MAX; ++i)
+ {
+ TotalTicks += CpuLoad.cpu_ticks[i];
+ }
+
+ if (TotalTicks > 0)
+ {
+ unsigned long IdleTicks = CpuLoad.cpu_ticks[CPU_STATE_IDLE];
+ Metrics.CpuUsagePercent = 100.0f * (TotalTicks - IdleTicks) / TotalTicks;
+ }
+ }
+
// Get memory information
uint64_t MemSize = 0;
Size = sizeof(MemSize);
diff --git a/src/zenhttp/servers/httpsys.cpp b/src/zenhttp/servers/httpsys.cpp
index 14896c803..c640ba90b 100644
--- a/src/zenhttp/servers/httpsys.cpp
+++ b/src/zenhttp/servers/httpsys.cpp
@@ -331,6 +331,8 @@ public:
virtual void WriteResponseAsync(std::function<void(HttpServerRequest&)>&& ContinuationHandler) override;
virtual bool TryGetRanges(HttpRanges& Ranges) override;
+ void LogRequest(HttpMessageResponseRequest* Response);
+
using HttpServerRequest::WriteResponse;
HttpSysServerRequest(const HttpSysServerRequest&) = delete;
@@ -429,7 +431,8 @@ public:
virtual HttpSysRequestHandler* HandleCompletion(ULONG IoResult, ULONG_PTR NumberOfBytesTransferred) override;
void SuppressResponseBody(); // typically used for HEAD requests
- inline int64_t GetResponseBodySize() const { return m_TotalDataSize; }
+ inline uint16_t GetResponseCode() const { return m_ResponseCode; }
+ inline int64_t GetResponseBodySize() const { return m_TotalDataSize; }
private:
eastl::fixed_vector<HTTP_DATA_CHUNK, 16> m_HttpDataChunks;
@@ -1886,7 +1889,7 @@ HttpSysServerRequest::WriteResponse(HttpResponseCode ResponseCode)
ZEN_ASSERT(IsHandled() == false);
- auto Response = new HttpMessageResponseRequest(m_HttpTx, (uint16_t)ResponseCode);
+ HttpMessageResponseRequest* Response = new HttpMessageResponseRequest(m_HttpTx, (uint16_t)ResponseCode);
if (SuppressBody())
{
@@ -1904,6 +1907,7 @@ HttpSysServerRequest::WriteResponse(HttpResponseCode ResponseCode)
# endif
SetIsHandled();
+ LogRequest(Response);
}
void
@@ -1913,7 +1917,7 @@ HttpSysServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentTy
ZEN_ASSERT(IsHandled() == false);
- auto Response = new HttpMessageResponseRequest(m_HttpTx, (uint16_t)ResponseCode, ContentType, Blobs);
+ HttpMessageResponseRequest* Response = new HttpMessageResponseRequest(m_HttpTx, (uint16_t)ResponseCode, ContentType, Blobs);
if (SuppressBody())
{
@@ -1931,6 +1935,20 @@ HttpSysServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentTy
# endif
SetIsHandled();
+ LogRequest(Response);
+}
+
+void
+HttpSysServerRequest::LogRequest(HttpMessageResponseRequest* Response)
+{
+ if (ShouldLogRequest())
+ {
+ ZEN_INFO("{} {} {} -> {}",
+ ToString(RequestVerb()),
+ m_UriUtf8.c_str(),
+ Response->GetResponseCode(),
+ NiceBytes(Response->GetResponseBodySize()));
+ }
}
void
@@ -1959,6 +1977,7 @@ HttpSysServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentTy
# endif
SetIsHandled();
+ LogRequest(Response);
}
void