diff --git a/README.md b/README.md index 5d9b16e..93a5c3f 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ Your DarkBot base must be: - Latest changes from: - Including PR: - Including PR: +- Including PR: After building, copy these files into your DarkBot `lib` directory: diff --git a/client/eu_darkbot_api_DarkTanos.cpp b/client/eu_darkbot_api_DarkTanos.cpp index ccdef09..866f41c 100644 --- a/client/eu_darkbot_api_DarkTanos.cpp +++ b/client/eu_darkbot_api_DarkTanos.cpp @@ -69,11 +69,15 @@ JNIEXPORT jboolean JNICALL Java_eu_darkbot_api_DarkTanos_isValid JNIEXPORT jlong JNICALL Java_eu_darkbot_api_DarkTanos_getMemoryUsage (JNIEnv *, jobject) { - if (client.FlashPid() > 0) - { - return ProcUtil::GetMemoryUsage(client.FlashPid()) / 1024; - } - return ProcUtil::GetMemoryUsage(client.Pid()) / 1024; + pid_t pid = client.FlashPid() > 0 ? client.FlashPid() : client.Pid(); + return ProcUtil::GetMemoryUsage(pid) / 1024; +} + +JNIEXPORT jdouble JNICALL Java_eu_darkbot_api_DarkTanos_getCpuUsage + (JNIEnv *, jobject) +{ + pid_t pid = client.FlashPid() > 0 ? client.FlashPid() : client.Pid(); + return ProcUtil::GetCpuUsage(pid); } JNIEXPORT jint JNICALL Java_eu_darkbot_api_DarkTanos_getVersion diff --git a/client/eu_darkbot_api_DarkTanos.h b/client/eu_darkbot_api_DarkTanos.h index bdd5472..77a6150 100644 --- a/client/eu_darkbot_api_DarkTanos.h +++ b/client/eu_darkbot_api_DarkTanos.h @@ -71,6 +71,14 @@ JNIEXPORT jboolean JNICALL Java_eu_darkbot_api_DarkTanos_isValid JNIEXPORT jlong JNICALL Java_eu_darkbot_api_DarkTanos_getMemoryUsage (JNIEnv *, jobject); +/* + * Class: eu_darkbot_api_DarkTanos + * Method: getCpuUsage + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_eu_darkbot_api_DarkTanos_getCpuUsage + (JNIEnv *, jobject); + /* * Class: eu_darkbot_api_DarkTanos * Method: getVersion diff --git a/client/proc_util.cpp b/client/proc_util.cpp index 03201b9..b461d20 100644 --- a/client/proc_util.cpp +++ b/client/proc_util.cpp @@ -2,10 +2,15 @@ #include #include #include +#include +#include + #include #include "masked_bmh.h" +#include +#include #include #include @@ -134,28 +139,131 @@ std::vector ProcUtil::GetPages(pid_t pid, const std::string & uint64_t ProcUtil::GetMemoryUsage(pid_t pid) { - if (std::ifstream fi { "/proc/"+std::to_string(pid)+"/stat" }) + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/stat", pid); + + char buf[512]; + int fd = ::open(path, O_RDONLY); + if (fd < 0) return 0; + ssize_t n = ::read(fd, buf, sizeof(buf) - 1); + ::close(fd); + if (n <= 0) return 0; + buf[n] = '\0'; + + // Skip comm field (may contain spaces/parens): scan past last ')' + const char *rp = strrchr(buf, ')'); + if (!rp || rp[1] == '\0') return 0; + + char state; + int ppid, pgrp, session, tty_nr, tpgid; + unsigned int flags; + unsigned long minflt, cminflt, majflt, cmajflt, utime, stime; + long cutime, cstime, priority, nice, num_threads, itrealvalue; + unsigned long long starttime; + unsigned long vsize; + long rss; + + if (sscanf(rp + 2, + "%c %d %d %d %d %d %u " + "%lu %lu %lu %lu %lu %lu %ld %ld " + "%ld %ld %ld %ld %llu " + "%lu %ld", + &state, &ppid, &pgrp, &session, &tty_nr, &tpgid, &flags, + &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime, + &cutime, &cstime, + &priority, &nice, &num_threads, &itrealvalue, + &starttime, + &vsize, &rss) != 22) + return 0; + + const long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; + return static_cast(rss) * static_cast(page_size_kb); +} + +double ProcUtil::GetCpuUsage(pid_t pid) +{ + struct CpuStat { + uint64_t proc_ticks = 0; + uint64_t start_time = 0; + uint64_t last_ms = 0; + double cached = 0.0; + }; + static thread_local std::unordered_map prev; + static const double clk_tck = static_cast(sysconf(_SC_CLK_TCK)); + + auto &p = prev[pid]; + + // Fast-path: vDSO monotonic clock check before any /proc I/O. + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + const uint64_t now_ms = static_cast(now.tv_sec) * 1000u + + static_cast(now.tv_nsec) / 1'000'000u; + const uint64_t elapsed_ms = now_ms - p.last_ms; + if (elapsed_ms < 250u) + return p.cached; + + // Denominator expressed in jiffies from elapsed wall time. + const double total_delta = static_cast(elapsed_ms) * clk_tck / 1000.0; + + // --- Read /proc//stat --- + uint64_t proc_ticks = 0; + uint64_t start_time = 0; + { + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/stat", pid); + + char buf[512]; + int fd = ::open(path, O_RDONLY); + if (fd < 0) return p.cached; + ssize_t n = ::read(fd, buf, sizeof(buf) - 1); + ::close(fd); + if (n <= 0) return p.cached; + buf[n] = '\0'; + + // Skip over comm field which may contain spaces: find last ')' + const char *rp = strrchr(buf, ')'); + if (!rp || rp[1] == '\0') return p.cached; + + char state; + int ppid, pgrp, session, tty_nr, tpgid; + unsigned int flags; + unsigned long minflt, cminflt, majflt, cmajflt, utime, stime; + long cutime, cstime, priority, nice, num_threads, itrealvalue; + unsigned long long starttime; + + if (sscanf(rp + 2, + "%c %d %d %d %d %d %u " + "%lu %lu %lu %lu %lu %lu %ld %ld " + "%ld %ld %ld %ld %llu", + &state, &ppid, &pgrp, &session, &tty_nr, &tpgid, &flags, + &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime, + &cutime, &cstime, + &priority, &nice, &num_threads, &itrealvalue, + &starttime) != 20) + return p.cached; + + proc_ticks = utime + stime; + start_time = starttime; + } + + p.last_ms = now_ms; + + if (p.last_ms > 0 && p.start_time == start_time) { - std::string _pid, comm, state, ppid, pgrp, session, tty_nr; - std::string tpgid, flags, minflt, cminflt, majflt, cmajflt; - std::string utime, stime, cutime, cstime, priority, nice; - std::string O, itrealvalue, starttime; - - // the two fields we want - uint64_t vsize; - int64_t rss; - - fi >> _pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr - >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt - >> utime >> stime >> cutime >> cstime >> priority >> nice - >> O >> itrealvalue >> starttime >> vsize >> rss; // don't care about the rest - - int64_t page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages - auto vm_usage = vsize / 1024; - auto resident_set = rss * page_size_kb; - return resident_set; + const double proc_delta = static_cast(proc_ticks - p.proc_ticks); + const double current = (proc_delta / total_delta) * 100.0; + constexpr double alpha = 0.35; + p.cached = p.cached * (1.0 - alpha) + current * alpha; } - return 0; + else + { + // Reset baseline when a PID is reused or on the first sample. + p.cached = 0.0; + } + + p.proc_ticks = proc_ticks; + p.start_time = start_time; + return p.cached; } int ProcUtil::QueryMemory(pid_t pid, unsigned char *query, const char *mask, uintptr_t *out, uint32_t amount) diff --git a/client/proc_util.h b/client/proc_util.h index d2014fe..535c13d 100644 --- a/client/proc_util.h +++ b/client/proc_util.h @@ -45,6 +45,7 @@ namespace ProcUtil std::vector GetPages(pid_t pid, const std::string &name = ""); uint64_t GetMemoryUsage(pid_t pid); + double GetCpuUsage(pid_t pid); class Process {