Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DEPENDENCIES
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02
uwebsockets https://github.com/uNetworking/uWebSockets v20.79.0
core https://github.com/sourcemeta/core 9ba55aa07360c270d289f5c74a9e9fef4e0cf33d
core https://github.com/sourcemeta/core b890ad46edb22694e9fecb194facdafbfd682648
blaze https://github.com/sourcemeta/blaze 714bea1948f551918682236c75c1d386f8051adb
jsonbinpack https://github.com/sourcemeta/jsonbinpack a97365f29e673cd7a278da8b87a8b083ae81774d
jsonschema https://github.com/sourcemeta/jsonschema 11d4305852cc9084ff9c758d44e595ca7b0c839c
Expand Down
1 change: 1 addition & 0 deletions enterprise/server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ target_link_libraries(sourcemeta_one_enterprise_server INTERFACE sourcemeta::one
target_link_libraries(sourcemeta_one_enterprise_server INTERFACE sourcemeta::blaze::output)
target_link_libraries(sourcemeta_one_enterprise_server INTERFACE sourcemeta::core::jsonld)
target_link_libraries(sourcemeta_one_enterprise_server INTERFACE sourcemeta::core::numeric)
target_link_libraries(sourcemeta_one_enterprise_server INTERFACE sourcemeta::core::process)
target_link_libraries(sourcemeta_one_enterprise_server INTERFACE sourcemeta::core::http)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <sourcemeta/core/json.h>
#include <sourcemeta/core/jsonrpc.h>
#include <sourcemeta/core/mcp.h>
#include <sourcemeta/core/process.h>
#include <sourcemeta/core/text.h>
#include <sourcemeta/core/uritemplate.h>

Expand All @@ -14,26 +15,14 @@

#include <algorithm> // std::ranges::transform
#include <array> // std::array
#include <chrono> // std::chrono::duration
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
#include <cstddef> // std::size_t
#include <cstdint> // std::uint64_t
#include <filesystem> // std::filesystem::path
#include <format> // std::format
#include <span> // std::span
#include <string> // std::string
#include <string_view> // std::string_view
#include <vector> // std::vector

#if defined(__APPLE__)
#include <libproc.h> // proc_pidinfo, PROC_PIDLISTFDS, PROC_PIDLISTFD_SIZE
#include <mach/mach.h> // mach_task_self, task_info, MACH_TASK_BASIC_INFO
#include <sys/resource.h> // getrusage, getrlimit, RUSAGE_SELF, RLIMIT_NOFILE
#include <unistd.h> // getpid
#elif defined(__linux__)
#include <fstream> // std::ifstream
#include <sstream> // std::istringstream
#include <sys/resource.h> // getrlimit, RLIMIT_NOFILE
#include <system_error> // std::error_code
#include <unistd.h> // sysconf, _SC_CLK_TCK, _SC_PAGESIZE
#endif

class ActionMetrics_v1 : public sourcemeta::one::RouterAction {
public:
Expand Down Expand Up @@ -108,118 +97,6 @@ class ActionMetrics_v1 : public sourcemeta::one::RouterAction {
}

private:
// What a process says about itself, which the platform answers rather than
// this program keeping count of. Anything a platform cannot cheaply say is
// left out rather than guessed at
struct Process {
double cpu_seconds{0};
std::uint64_t resident_bytes{0};
std::uint64_t virtual_bytes{0};
std::uint64_t open_descriptors{0};
std::uint64_t maximum_descriptors{0};
};

[[nodiscard]] static auto descriptor_limit() -> std::uint64_t {
rlimit limit{};
if (getrlimit(RLIMIT_NOFILE, &limit) != 0) {
return 0;
}

return static_cast<std::uint64_t>(limit.rlim_cur);
}

#if defined(__APPLE__)

[[nodiscard]] static auto read_process() -> Process {
Process sample;

rusage usage{};
if (getrusage(RUSAGE_SELF, &usage) == 0) {
sample.cpu_seconds =
static_cast<double>(usage.ru_utime.tv_sec) +
static_cast<double>(usage.ru_utime.tv_usec) / 1000000.0 +
static_cast<double>(usage.ru_stime.tv_sec) +
static_cast<double>(usage.ru_stime.tv_usec) / 1000000.0;
}

mach_task_basic_info info{};
mach_msg_type_number_t count{MACH_TASK_BASIC_INFO_COUNT};
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
reinterpret_cast<task_info_t>(&info),
&count) == KERN_SUCCESS) {
sample.resident_bytes = info.resident_size;
sample.virtual_bytes = info.virtual_size;
}

const auto descriptors{
proc_pidinfo(getpid(), PROC_PIDLISTFDS, 0, nullptr, 0)};
if (descriptors > 0) {
sample.open_descriptors =
static_cast<std::uint64_t>(descriptors) / PROC_PIDLISTFD_SIZE;
}

sample.maximum_descriptors = descriptor_limit();
return sample;
}

#elif defined(__linux__)

// The second field is a command name in parentheses that may itself contain
// spaces, so what follows it is found from the last parenthesis rather than
// by counting separators from the beginning
[[nodiscard]] static auto read_process() -> Process {
Process sample;

std::ifstream stream{"/proc/self/stat"};
if (stream.is_open()) {
std::string line;
std::getline(stream, line);
const auto comm{line.rfind(')')};
if (comm != std::string::npos) {
std::istringstream fields{line.substr(comm + 1)};
std::vector<std::string> tokens;
std::string token;
while (fields >> token) {
tokens.push_back(token);
}

const auto ticks{static_cast<double>(sysconf(_SC_CLK_TCK))};
if (tokens.size() > 21 && ticks > 0) {
sample.cpu_seconds =
(std::stod(tokens.at(11)) + std::stod(tokens.at(12))) / ticks;
sample.virtual_bytes = std::stoull(tokens.at(20));
sample.resident_bytes =
std::stoull(tokens.at(21)) *
static_cast<std::uint64_t>(sysconf(_SC_PAGESIZE));
}
}
}

// Reading the list takes a descriptor of its own, which the list then
// includes, so what is counted is one more than what was open
std::error_code error;
const std::filesystem::directory_iterator descriptors{"/proc/self/fd",
error};
if (!error) {
std::uint64_t listed{0};
for (const auto &entry : descriptors) {
static_cast<void>(entry);
listed += 1;
}

sample.open_descriptors = listed > 0 ? listed - 1 : 0;
}

sample.maximum_descriptors = descriptor_limit();
return sample;
}

#else

[[nodiscard]] static auto read_process() -> Process { return {}; }

#endif

static auto family(std::string &output, const std::string_view name,
const std::string_view help, const std::string_view type)
-> void {
Expand All @@ -230,7 +107,9 @@ class ActionMetrics_v1 : public sourcemeta::one::RouterAction {
[[nodiscard]] auto serialize() const -> std::string {
const auto &metrics{sourcemeta::one::http_metrics()};
const auto state{metrics.snapshot()};
const auto process{read_process()};
const auto usage{sourcemeta::core::process_usage()};
const auto descriptors{sourcemeta::core::process_descriptors()};
const auto started{sourcemeta::core::process_start_time()};

std::string edition{sourcemeta::one::edition()};
std::ranges::transform(edition, edition.begin(),
Expand All @@ -246,34 +125,52 @@ class ActionMetrics_v1 : public sourcemeta::one::RouterAction {
"sourcemeta_one_build_info{{version=\"{}\",edition=\"{}\"}} 1\n\n",
sourcemeta::one::version(), edition);

family(output, "process_start_time_seconds",
"Start time of the process since the Unix epoch", "gauge");
output +=
std::format("process_start_time_seconds {}\n\n", metrics.started());
// A platform that cannot answer is not made to. A series nobody receives
// is one nobody plots a flat zero for
if (started.has_value()) {
family(output, "process_start_time_seconds",
"Start time of the process since the Unix epoch", "gauge");
output += std::format(
"process_start_time_seconds {}\n\n",
std::chrono::duration<double>{started.value().time_since_epoch()}
.count());
}

family(output, "process_cpu_seconds_total",
"Total user and system CPU time spent", "counter");
output +=
std::format("process_cpu_seconds_total {}\n\n", process.cpu_seconds);
if (usage.cpu_time.has_value()) {
family(output, "process_cpu_seconds_total",
"Total user and system CPU time spent", "counter");
output += std::format(
"process_cpu_seconds_total {}\n\n",
std::chrono::duration<double>{usage.cpu_time.value()}.count());
}

family(output, "process_resident_memory_bytes", "Resident memory size",
"gauge");
output += std::format("process_resident_memory_bytes {}\n\n",
process.resident_bytes);
if (usage.resident_bytes.has_value()) {
family(output, "process_resident_memory_bytes", "Resident memory size",
"gauge");
output += std::format("process_resident_memory_bytes {}\n\n",
usage.resident_bytes.value());
}

family(output, "process_virtual_memory_bytes", "Virtual memory size",
"gauge");
output += std::format("process_virtual_memory_bytes {}\n\n",
process.virtual_bytes);
if (usage.virtual_bytes.has_value()) {
family(output, "process_virtual_memory_bytes", "Virtual memory size",
"gauge");
output += std::format("process_virtual_memory_bytes {}\n\n",
usage.virtual_bytes.value());
}

family(output, "process_open_fds", "Number of open file descriptors",
"gauge");
output += std::format("process_open_fds {}\n\n", process.open_descriptors);
if (descriptors.open.has_value()) {
family(output, "process_open_fds", "Number of open file descriptors",
"gauge");
output +=
std::format("process_open_fds {}\n\n", descriptors.open.value());
}

family(output, "process_max_fds", "Maximum number of open file descriptors",
"gauge");
output +=
std::format("process_max_fds {}\n\n", process.maximum_descriptors);
if (descriptors.maximum.has_value()) {
family(output, "process_max_fds",
"Maximum number of open file descriptors", "gauge");
output +=
std::format("process_max_fds {}\n\n", descriptors.maximum.value());
}

family(output, "sourcemeta_one_http_requests_in_flight",
"Requests currently being served", "gauge");
Expand Down
11 changes: 0 additions & 11 deletions src/http/include/sourcemeta/one/http_metrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <algorithm> // std::ranges::lower_bound
#include <array> // std::array
#include <atomic> // std::atomic
#include <chrono> // std::chrono::system_clock, std::chrono::duration
#include <cstddef> // std::size_t
#include <cstdint> // std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t
#include <iterator> // std::distance
Expand Down Expand Up @@ -61,10 +60,6 @@ class HTTPMetrics {
// counted before this is said
auto start(const std::size_t handlers) -> void {
this->handlers_ = handlers;
this->started_ =
std::chrono::duration<double>{
std::chrono::system_clock::now().time_since_epoch()}
.count();
for (auto &shard : this->shards_) {
const std::scoped_lock guard{shard.mutex};
shard.buckets.assign(handlers, {});
Expand Down Expand Up @@ -150,11 +145,6 @@ class HTTPMetrics {
return result;
}

// When this server began, as seconds since the Unix epoch
[[nodiscard]] auto started() const noexcept -> double {
return this->started_;
}

private:
// A series is named by what answered and what it answered together, so
// neither alone identifies one
Expand Down Expand Up @@ -194,7 +184,6 @@ class HTTPMetrics {
std::atomic<std::uint64_t> entered_{0};
std::atomic<std::uint64_t> answered_{0};
std::atomic<std::uint64_t> dropped_{0};
double started_{0};
};

// What this process has served, which there is one of because there is one
Expand Down
4 changes: 2 additions & 2 deletions vendor/core/DEPENDENCIES

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions vendor/core/cmake/FindLibDeflate.cmake

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions vendor/core/config.cmake.in

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 8 additions & 13 deletions vendor/core/src/core/dns/hostname.cc

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading