Skip to content

Commit 3fc72c0

Browse files
authored
Add getting CPU usage information on Linux. (#1)
* Add getting CPU usage information on Linux. * Add getting CPU usage information on Linux.
1 parent 469d1b9 commit 3fc72c0

File tree

5 files changed

+136
-46
lines changed

5 files changed

+136
-46
lines changed

CMakeLists.txt

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,43 @@
11
# Copyright (c) 2023 ttldtor.
22
# SPDX-License-Identifier: BSL-1.0
33

4-
cmake_minimum_required(VERSION 3.26)
4+
cmake_minimum_required(VERSION 3.22)
5+
56
project(process)
7+
include(GNUInstallDirs)
8+
9+
set(TTLDTOR_PROCESS "1.0.0" CACHE STRING "The Process package version")
610

711
set(CMAKE_CXX_STANDARD 20)
12+
set(CMAKE_CXX_STANDARD 20)
13+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
14+
set(CMAKE_C_STANDARD 11)
15+
set(CMAKE_C_STANDARD_REQUIRED ON)
16+
set(CXX_EXTENSIONS OFF)
17+
set(C_EXTENSIONS OFF)
18+
19+
# Determine if process is built as a subproject (using add_subdirectory)
20+
# or if it is the master project.
21+
if (NOT DEFINED TTLDTOR_PROCESS_ROOT_PROJECT)
22+
set(TTLDTOR_PROCESS_ROOT_PROJECT OFF)
23+
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
24+
set(TTLDTOR_PROCESS_ROOT_PROJECT ON)
25+
message(STATUS "CMake version: ${CMAKE_VERSION}")
26+
endif ()
27+
endif ()
28+
29+
option(TTLDTOR_PROCESS_SAMPLES "Enable building samples for Process" ${TTLDTOR_PROCESS_ROOT_PROJECT})
830

931
add_library(process src/process.cpp)
1032
target_include_directories(process PUBLIC include)
1133

34+
add_library(process::process ALIAS process)
35+
1236
if (WIN32)
1337
target_link_libraries(process PRIVATE psapi)
1438
endif ()
1539

16-
add_executable(process_test test/main.cpp)
17-
target_link_libraries(process_test process)
40+
if (TTLDTOR_PROCESS_SAMPLES)
41+
add_executable(process_test test/main.cpp)
42+
target_link_libraries(process_test process)
43+
endif ()

README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,56 @@
11
# Process
22
Cross-platform library for obtaining metrics of the current process
3+
4+
Status:
5+
* [x] Windows
6+
* [x] Linux
7+
* [ ] MacOS
8+
* [ ] FreeBSD
9+
* [ ] AIX
10+
* [ ] ...
11+
12+
Example:
13+
14+
```cpp
15+
#include <process/process.hpp>
16+
17+
#include <chrono>
18+
#include <iostream>
19+
#include <thread>
20+
#include <vector>
21+
22+
using namespace std::literals;
23+
using namespace ttldtor::process;
24+
25+
int main() {
26+
auto now = [] {
27+
return duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
28+
};
29+
std::cout << "Physical memory usage: " << Process::getPhysicalMemorySize() / 1024 << "KB" << std::endl;
30+
std::cout << "Total CPU time: " << Process::getTotalProcessorTime().count() << "ms" << std::endl;
31+
32+
auto t1 = now();
33+
34+
while (now() - t1 <= 5) {
35+
static std::size_t i = 0;
36+
(void)(i++);
37+
}
38+
39+
std::cout << "Total CPU time: " << Process::getTotalProcessorTime().count() << "ms" << std::endl;
40+
std::cout << "Physical memory usage: " << Process::getPhysicalMemorySize() / 1024 << "KB" << std::endl;
41+
std::vector<std::byte> vb(10 * 1024 * 1024);
42+
std::cout << "Physical memory usage: " << Process::getPhysicalMemorySize() / 1024 << "KB" << std::endl;
43+
vb.resize(1);
44+
vb.shrink_to_fit();
45+
std::cout << "Physical memory usage: " << Process::getPhysicalMemorySize() / 1024 << "KB" << std::endl;
46+
}
47+
```
48+
49+
```text
50+
Physical memory usage: 3676KB
51+
Total CPU time: 15ms
52+
Total CPU time: 5687ms
53+
Physical memory usage: 3720KB
54+
Physical memory usage: 13964KB
55+
Physical memory usage: 3720KB
56+
```

include/process/process.hpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,27 @@ namespace ttldtor::process {
3838
struct TTLDTOR_PROCESS_EXPORT Process {
3939

4040
/**
41-
* Returns the kernel (privileged) CPU time for the current process in ms.
41+
* Returns the kernel (privileged, system) CPU time for the current process in ms.
4242
*
43-
* @return The kernel (privileged) CPU time for the current process in ms.
43+
* @return The kernel (privileged, system) CPU time for the current process in ms.
4444
*/
4545
static std::chrono::milliseconds getKernelProcessorTime() noexcept;
4646

4747
/**
48-
* Returns the kernel (privileged) CPU time for the current process in ms.
48+
* Returns the kernel (privileged, system) CPU time for the current process in ms.
4949
*
50-
* @return The kernel (privileged) CPU time for the current process in ms.
50+
* @return The kernel (privileged, system) CPU time for the current process in ms.
5151
*/
52-
static std::chrono::milliseconds PrivilegedProcessorTime() noexcept {
52+
static std::chrono::milliseconds getPrivilegedProcessorTime() noexcept {
53+
return getKernelProcessorTime();
54+
}
55+
56+
/**
57+
* Returns the kernel (privileged, system) CPU time for the current process in ms.
58+
*
59+
* @return The kernel (privileged, system) CPU time for the current process in ms.
60+
*/
61+
static std::chrono::milliseconds getSystemProcessorTime() noexcept {
5362
return getKernelProcessorTime();
5463
}
5564

src/process.cpp

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
#include <process/process.hpp>
55

6+
#include <atomic>
67
#include <chrono>
78
#include <cstring>
89
#include <fstream>
910
#include <iostream>
1011
#include <string>
1112
#include <type_traits>
12-
#include <atomic>
1313

1414
namespace ttldtor::process {
1515

@@ -121,26 +121,18 @@ std::uint64_t Process::getPrivateMemorySize() noexcept {
121121
}
122122
} // namespace ttldtor::process
123123
#elif defined(__linux__)
124-
int parseLine(char *line) {
125-
// This assumes that a digit will be found and the line ends in " Kb".
126-
int i = strlen(line);
127-
const char *p = line;
128-
while (*p < '0' || *p > '9')
129-
p++;
130-
line[i - 3] = '\0';
131-
i = atoi(p);
132-
return i;
133-
}
124+
125+
# include <sys/resource.h>
134126

135127
struct Parser {
136128
enum ParseResultType { KEY_NOT_FOUND, VALUE_NOT_FOUND, OK };
137129

138-
struct ParseResult {
130+
struct ParseStatusResult {
139131
ParseResultType resultType;
140132
std::uint64_t value;
141133
};
142134

143-
static ParseResult parse(const std::string &s, const std::string &key) noexcept {
135+
static ParseStatusResult parseStatus(const std::string &s, const std::string &key) noexcept {
144136
if (auto foundKeyPos = s.find(key); foundKeyPos != std::string::npos) {
145137
if (auto foundValuePos = s.find_first_of("0123456789", foundKeyPos + 6);
146138
foundValuePos != std::string::npos) {
@@ -158,36 +150,43 @@ struct Parser {
158150
}
159151
};
160152

153+
namespace ttldtor::process {
154+
struct RUsageResult {
155+
std::chrono::milliseconds sysTime{};
156+
std::chrono::milliseconds userTime{};
157+
std::chrono::milliseconds totalTime{};
158+
159+
explicit RUsageResult(const rusage &ru)
160+
: sysTime{static_cast<std::uint64_t>(ru.ru_stime.tv_sec) * 1000000ULL +
161+
static_cast<std::uint64_t>(ru.ru_stime.tv_usec)},
162+
userTime{static_cast<std::uint64_t>(ru.ru_utime.tv_sec) * 1000000ULL +
163+
static_cast<std::uint64_t>(ru.ru_utime.tv_usec)},
164+
totalTime{sysTime + userTime} {
165+
}
166+
};
161167

168+
std::chrono::milliseconds Process::getKernelProcessorTime() noexcept {
169+
rusage ru{};
162170

163-
static unsigned long long lastTotalUser, lastTotalUserLow, lastTotalSys, lastTotalIdle;
171+
getrusage(RUSAGE_SELF, &ru);
164172

165-
void init() {
166-
FILE *file = fopen("/proc/stat", "r");
167-
fscanf(file, "cpu %llu %llu %llu %llu", &lastTotalUser, &lastTotalUserLow, &lastTotalSys, &lastTotalIdle);
168-
fclose(file);
173+
return RUsageResult{ru}.sysTime;
169174
}
170175

171-
namespace ttldtor::process {
172-
/*TODO: implement
173-
* https://github.com/dotnet/runtime/blob/de0ab156194eb64deae0e1018db9a58f7b02f4a3/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs#L817
174-
* https://github.com/dotnet/runtime/blob/de0ab156194eb64deae0e1018db9a58f7b02f4a3/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs#L130
175-
*/
176+
std::chrono::milliseconds Process::getUserProcessorTime() noexcept {
177+
rusage ru{};
176178

177-
std::chrono::milliseconds Process::getKernelProcessorTime() noexcept {
178-
return std::chrono::milliseconds(0);
179-
}
179+
getrusage(RUSAGE_SELF, &ru);
180180

181-
std::chrono::milliseconds Process::getUserProcessorTime() noexcept {
182-
return std::chrono::milliseconds(0);
181+
return RUsageResult{ru}.userTime;
183182
}
184183

185184
std::chrono::milliseconds Process::getTotalProcessorTime() noexcept {
186-
init();
185+
rusage ru{};
187186

188-
// std::cout << (lastTotalUser + lastTotalSys) << std::endl;
187+
getrusage(RUSAGE_SELF, &ru);
189188

190-
return std::chrono::milliseconds((lastTotalUser + lastTotalSys) * 10);
189+
return RUsageResult{ru}.totalTime;
191190
}
192191

193192
std::uint64_t Process::getWorkingSetSize() noexcept {
@@ -200,7 +199,7 @@ std::uint64_t Process::getWorkingSetSize() noexcept {
200199
std::string s{};
201200

202201
while (!std::getline(is, s).fail()) {
203-
auto result = Parser::parse(s, "VmRSS:");
202+
auto result = Parser::parseStatus(s, "VmRSS:");
204203

205204
if (result.resultType == Parser::KEY_NOT_FOUND) {
206205
continue;
@@ -222,7 +221,7 @@ std::uint64_t Process::getPrivateMemorySize() noexcept {
222221
std::string s{};
223222

224223
while (!std::getline(is, s).fail()) {
225-
auto result = Parser::parse(s, "VmSize:");
224+
auto result = Parser::parseStatus(s, "VmSize:");
226225

227226
if (result.resultType == Parser::KEY_NOT_FOUND) {
228227
continue;

test/main.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
#include <process/process.hpp>
22

3+
#include <chrono>
34
#include <iostream>
45
#include <thread>
5-
#include <chrono>
66
#include <vector>
77

88
using namespace std::literals;
99
using namespace ttldtor::process;
1010

1111
int main() {
12-
auto now = [] { return duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now().time_since_epoch()).count(); };
12+
auto now = [] {
13+
return duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
14+
};
1315
std::cout << "Physical memory usage: " << Process::getPhysicalMemorySize() / 1024 << "KB" << std::endl;
14-
std::cout << "Total CPU time: " << Process::getTotalProcessorTime() << std::endl;
16+
std::cout << "Total CPU time: " << Process::getTotalProcessorTime().count() << "ms" << std::endl;
1517

1618
auto t1 = now();
1719

@@ -20,9 +22,9 @@ int main() {
2022
(void)(i++);
2123
}
2224

23-
std::cout << "Total CPU time: " << Process::getTotalProcessorTime() << std::endl;
25+
std::cout << "Total CPU time: " << Process::getTotalProcessorTime().count() << "ms" << std::endl;
2426
std::cout << "Physical memory usage: " << Process::getPhysicalMemorySize() / 1024 << "KB" << std::endl;
25-
std::vector<std::byte> vb(10000000);
27+
std::vector<std::byte> vb(10 * 1024 * 1024);
2628
std::cout << "Physical memory usage: " << Process::getPhysicalMemorySize() / 1024 << "KB" << std::endl;
2729
vb.resize(1);
2830
vb.shrink_to_fit();

0 commit comments

Comments
 (0)