Implement process.memoryUsage() and process.cpuUsage() (#3586)

* Implement process.memoryUsage() and process.cpuUsage()

* Avoid mi_process_info

* Update bench

* Update Process.cpp

* fixup

* More tests + linux fixup

* Skip it for now since it seems less accurate

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
This commit is contained in:
Jarred Sumner
2023-07-09 21:50:19 -07:00
committed by GitHub
parent ca42c820d2
commit 2f5e4fffe9
7 changed files with 638 additions and 78 deletions

View File

@@ -0,0 +1,33 @@
import { bench, run } from "./runner.mjs";
import { performance } from "perf_hooks";
bench("process.memoryUsage()", () => {
process.memoryUsage();
});
bench("process.memoryUsage.rss()", () => {
process.memoryUsage.rss();
});
bench("process.cpuUsage()", () => {
process.cpuUsage();
});
const init = process.cpuUsage();
bench("process.cpuUsage(delta)", () => {
process.cpuUsage(init);
});
bench("performance.now()", () => {
performance.now();
});
bench("process.hrtime()", () => {
process.hrtime();
});
bench("process.hrtime.bigint()", () => {
process.hrtime.bigint();
});
await run();

View File

@@ -12,8 +12,24 @@
#include "ZigConsoleClient.h"
#include <JavaScriptCore/GetterSetter.h>
#include <JavaScriptCore/JSSet.h>
#include <JavaScriptCore/LazyProperty.h>
#include <JavaScriptCore/LazyPropertyInlines.h>
#include <JavaScriptCore/VMTrapsInlines.h>
#pragma mark - Node.js Process
#if defined(__APPLE__)
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif
#if defined(__linux__)
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#if !defined(_MSC_VER)
#include <unistd.h> // setuid, getuid
#endif
@@ -335,9 +351,12 @@ extern "C" uint64_t Bun__readOriginTimer(void*);
JSC_DEFINE_HOST_FUNCTION(Process_functionHRTime,
(JSC::JSGlobalObject * globalObject_, JSC::CallFrame* callFrame))
{
Zig::GlobalObject* globalObject
= reinterpret_cast<Zig::GlobalObject*>(globalObject_);
auto& vm = globalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
uint64_t time = Bun__readOriginTimer(globalObject->bunVM());
int64_t seconds = static_cast<int64_t>(time / 1000000000);
int64_t nanoseconds = time % 1000000000;
@@ -346,7 +365,6 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionHRTime,
JSC::JSValue arg0 = callFrame->uncheckedArgument(0);
if (!arg0.isUndefinedOrNull()) {
JSArray* relativeArray = JSC::jsDynamicCast<JSC::JSArray*>(arg0);
auto throwScope = DECLARE_THROW_SCOPE(vm);
if ((!relativeArray && !arg0.isUndefinedOrNull()) || relativeArray->length() < 2) {
JSC::throwTypeError(globalObject, throwScope, "hrtime() argument must be an array or undefined"_s);
return JSC::JSValue::encode(JSC::JSValue {});
@@ -366,14 +384,28 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionHRTime,
seconds--;
nanoseconds += 1000000000;
}
throwScope.release();
}
}
auto* array = JSArray::create(vm, globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous), 2);
array->setIndexQuickly(vm, 0, JSC::jsNumber(seconds));
array->setIndexQuickly(vm, 1, JSC::jsNumber(nanoseconds));
return JSC::JSValue::encode(JSC::JSValue(array));
JSC::JSArray* array = nullptr;
{
JSC::ObjectInitializationScope initializationScope(vm);
if ((array = JSC::JSArray::tryCreateUninitializedRestricted(
initializationScope, nullptr,
globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous),
2))) {
array->initializeIndex(initializationScope, 0, JSC::jsNumber(seconds));
array->initializeIndex(initializationScope, 1, JSC::jsNumber(nanoseconds));
}
}
if (UNLIKELY(!array)) {
JSC::throwOutOfMemoryError(globalObject, throwScope);
return JSC::JSValue::encode(JSC::JSValue {});
}
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(array));
}
JSC_DEFINE_HOST_FUNCTION(Process_functionHRTimeBigInt,
@@ -680,10 +712,10 @@ static JSValue constructProcessHrtimeObject(VM& vm, JSObject* processObject)
{
auto* globalObject = processObject->globalObject();
JSC::JSFunction* hrtime = JSC::JSFunction::create(vm, globalObject, 0,
MAKE_STATIC_STRING_IMPL("hrtime"), Process_functionHRTime, ImplementationVisibility::Public);
String("hrtime"_s), Process_functionHRTime, ImplementationVisibility::Public);
JSC::JSFunction* hrtimeBigInt = JSC::JSFunction::create(vm, globalObject, 0,
MAKE_STATIC_STRING_IMPL("bigint"), Process_functionHRTimeBigInt, ImplementationVisibility::Public);
String("bigint"_s), Process_functionHRTimeBigInt, ImplementationVisibility::Public);
hrtime->putDirect(vm, JSC::Identifier::fromString(vm, "bigint"_s), hrtimeBigInt);
@@ -945,6 +977,317 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionReallyExit, (JSGlobalObject * globalObj
__builtin_unreachable();
}
template<typename Visitor>
void Process::visitChildrenImpl(JSCell* cell, Visitor& visitor)
{
Process* thisObject = jsCast<Process*>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
thisObject->cpuUsageStructure.visit(visitor);
thisObject->memoryUsageStructure.visit(visitor);
}
DEFINE_VISIT_CHILDREN(Process);
static Structure* constructCPUUsageStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
{
JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, globalObject->objectPrototype(), 2);
PropertyOffset offset;
structure = structure->addPropertyTransition(
vm,
structure,
JSC::Identifier::fromString(vm, "user"_s),
0,
offset);
structure = structure->addPropertyTransition(
vm,
structure,
JSC::Identifier::fromString(vm, "system"_s),
0,
offset);
return structure;
}
static Structure* constructMemoryUsageStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
{
JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, globalObject->objectPrototype(), 5);
PropertyOffset offset;
structure = structure->addPropertyTransition(
vm,
structure,
JSC::Identifier::fromString(vm, "rss"_s),
0,
offset);
structure = structure->addPropertyTransition(
vm,
structure,
JSC::Identifier::fromString(vm, "heapTotal"_s),
0,
offset);
structure = structure->addPropertyTransition(
vm,
structure,
JSC::Identifier::fromString(vm, "heapUsed"_s),
0,
offset);
structure = structure->addPropertyTransition(
vm,
structure,
JSC::Identifier::fromString(vm, "external"_s),
0,
offset);
structure = structure->addPropertyTransition(
vm,
structure,
JSC::Identifier::fromString(vm, "arrayBuffers"_s),
0,
offset);
return structure;
}
static Process* getProcessObject(JSC::JSGlobalObject* lexicalGlobalObject, JSValue thisValue)
{
Process* process = jsDynamicCast<Process*>(thisValue);
// Handle "var memoryUsage = process.memoryUsage; memoryUsage()"
if (UNLIKELY(!process)) {
// Handle calling this function from inside a node:vm
Zig::GlobalObject* zigGlobalObject = jsDynamicCast<Zig::GlobalObject*>(lexicalGlobalObject);
if (UNLIKELY(!zigGlobalObject)) {
zigGlobalObject = Bun__getDefaultGlobal();
}
return jsCast<Process*>(zigGlobalObject->processObject());
}
return process;
}
JSC_DEFINE_HOST_FUNCTION(Process_functionCpuUsage,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
JSC::VM& vm = globalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
struct rusage rusage;
if (getrusage(RUSAGE_SELF, &rusage) != 0) {
SystemError error;
error.errno_ = errno;
error.syscall = Bun::toString("getrusage"_s);
error.message = Bun::toString("Failed to get CPU usage"_s);
throwException(globalObject, throwScope, JSValue::decode(SystemError__toErrorInstance(&error, globalObject)));
return JSValue::encode(jsUndefined());
}
auto* process = getProcessObject(globalObject, callFrame->thisValue());
Structure* cpuUsageStructure = process->cpuUsageStructure.getInitializedOnMainThread(process);
constexpr double MICROS_PER_SEC = 1000000.0;
double user = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec;
double system = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec;
if (callFrame->argumentCount() > 0) {
JSValue comparatorValue = callFrame->argument(0);
if (!comparatorValue.isUndefined()) {
if (UNLIKELY(!comparatorValue.isObject())) {
throwTypeError(globalObject, throwScope, "Expected an object as the first argument"_s);
return JSC::JSValue::encode(JSC::jsUndefined());
}
JSC::JSObject* comparator = comparatorValue.getObject();
JSValue userValue;
JSValue systemValue;
if (LIKELY(comparator->structureID() == cpuUsageStructure->id())) {
userValue = comparator->getDirect(0);
systemValue = comparator->getDirect(1);
} else {
userValue = comparator->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "user"_s));
RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined()));
systemValue = comparator->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "system"_s));
RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined()));
}
if (UNLIKELY(!userValue || !userValue.isNumber())) {
throwTypeError(globalObject, throwScope, "Expected a number for the user property"_s);
return JSC::JSValue::encode(JSC::jsUndefined());
}
if (UNLIKELY(!systemValue || !systemValue.isNumber())) {
throwTypeError(globalObject, throwScope, "Expected a number for the system property"_s);
return JSC::JSValue::encode(JSC::jsUndefined());
}
double userComparator = userValue.asNumber();
double systemComparator = systemValue.asNumber();
user -= userComparator;
system -= systemComparator;
}
}
JSC::JSObject* result = JSC::constructEmptyObject(vm, cpuUsageStructure);
RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined()));
result->putDirectOffset(vm, 0, JSC::jsNumber(user));
result->putDirectOffset(vm, 1, JSC::jsNumber(system));
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(result));
}
static int getRSS(size_t* rss)
{
#if defined(__APPLE__)
mach_msg_type_number_t count;
task_basic_info_data_t info;
kern_return_t err;
count = TASK_BASIC_INFO_COUNT;
err = task_info(mach_task_self(),
TASK_BASIC_INFO,
reinterpret_cast<task_info_t>(&info),
&count);
if (err == KERN_SUCCESS) {
*rss = (size_t)info.resident_size;
return 0;
}
return -1;
#elif defined(__linux__)
// Taken from libuv.
char buf[1024];
const char* s;
ssize_t n;
long val;
int fd;
int i;
do
fd = open("/proc/self/stat", O_RDONLY);
while (fd == -1 && errno == EINTR);
if (fd == -1)
return errno;
do
n = read(fd, buf, sizeof(buf) - 1);
while (n == -1 && errno == EINTR);
int closeErrno = 0;
do {
closeErrno = close(fd);
} while (closeErrno == -1 && errno == EINTR);
if (n == -1)
return errno;
buf[n] = '\0';
s = strchr(buf, ' ');
if (s == NULL)
goto err;
s += 1;
if (*s != '(')
goto err;
s = strchr(s, ')');
if (s == NULL)
goto err;
for (i = 1; i <= 22; i++) {
s = strchr(s + 1, ' ');
if (s == NULL)
goto err;
}
errno = 0;
val = strtol(s, NULL, 10);
if (errno != 0)
goto err;
if (val < 0)
goto err;
*rss = val * getpagesize();
return 0;
err:
return EINVAL;
#else
#error "Unsupported platform"
#endif
}
JSC_DEFINE_HOST_FUNCTION(Process_functionMemoryUsage,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
JSC::VM& vm = globalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto* process = getProcessObject(globalObject, callFrame->thisValue());
size_t current_rss = 0;
if (getRSS(&current_rss) != 0) {
SystemError error;
error.errno_ = errno;
error.syscall = Bun::toString("memoryUsage"_s);
error.message = Bun::toString("Failed to get memory usage"_s);
throwException(globalObject, throwScope, JSValue::decode(SystemError__toErrorInstance(&error, globalObject)));
return JSC::JSValue::encode(JSC::JSValue {});
}
JSC::JSObject* result = JSC::constructEmptyObject(vm, process->memoryUsageStructure.getInitializedOnMainThread(process));
if (UNLIKELY(throwScope.exception())) {
return JSC::JSValue::encode(JSC::JSValue {});
}
// Node.js:
// {
// rss: 4935680,
// heapTotal: 1826816,
// heapUsed: 650472,
// external: 49879,
// arrayBuffers: 9386
// }
result->putDirectOffset(vm, 0, JSC::jsNumber(current_rss));
result->putDirectOffset(vm, 1, JSC::jsNumber(vm.heap.blockBytesAllocated()));
// heap.size() loops through every cell...
// TODO: add a binding for heap.sizeAfterLastCollection()
result->putDirectOffset(vm, 2, JSC::jsNumber(vm.heap.sizeAfterLastEdenCollection()));
result->putDirectOffset(vm, 3, JSC::jsNumber(vm.heap.externalMemorySize()));
// We report 0 for this because m_arrayBuffers in JSC::Heap is private and we need to add a binding
// If we use objectTypeCounts(), it's hideously slow because it loops through every single object in the heap
// TODO: add a binding for m_arrayBuffers, registerWrapper() in TypedArrayController doesn't work
result->putDirectOffset(vm, 4, JSC::jsNumber(0));
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(result));
}
JSC_DEFINE_HOST_FUNCTION(Process_functionMemoryUsageRSS,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
JSC::VM& vm = globalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
size_t current_rss = 0;
if (getRSS(&current_rss) != 0) {
SystemError error;
error.errno_ = errno;
error.syscall = Bun::toString("memoryUsage"_s);
error.message = Bun::toString("Failed to get memory usage"_s);
throwException(globalObject, throwScope, JSValue::decode(SystemError__toErrorInstance(&error, globalObject)));
return JSC::JSValue::encode(JSC::JSValue {});
}
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsNumber(current_rss)));
}
JSC_DEFINE_HOST_FUNCTION(Process_functionOpenStdin, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
auto& vm = globalObject->vm();
@@ -1010,6 +1353,19 @@ static JSValue Process_stubEmptySet(VM& vm, JSObject* processObject)
return JSSet::create(vm, globalObject->setStructure());
}
static JSValue constructMemoryUsage(VM& vm, JSObject* processObject)
{
auto* globalObject = processObject->globalObject();
JSC::JSFunction* memoryUsage = JSC::JSFunction::create(vm, globalObject, 0,
String("memoryUsage"_s), Process_functionMemoryUsage, ImplementationVisibility::Public);
JSC::JSFunction* rss = JSC::JSFunction::create(vm, globalObject, 0,
String("rss"_s), Process_functionMemoryUsageRSS, ImplementationVisibility::Public);
memoryUsage->putDirect(vm, JSC::Identifier::fromString(vm, "rss"_s), rss, JSC::PropertyAttribute::Function | 0);
return memoryUsage;
}
static JSValue constructFeatures(VM& vm, JSObject* processObject)
{
// {
@@ -1133,16 +1489,16 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionCwd,
browser constructBrowser PropertyCallback
chdir Process_functionChdir Function 1
config constructProcessConfigObject PropertyCallback
debugPort processDebugPort CustomAccessor
exitCode processExitCode CustomAccessor
title processTitle CustomAccessor
cpuUsage Process_functionCpuUsage Function 1
cwd Process_functionCwd Function 1
debugPort processDebugPort CustomAccessor
dlopen Process_functionDlopen Function 1
emitWarning Process_emitWarning Function 1
env constructEnv PropertyCallback
execArgv constructExecArgv PropertyCallback
execPath constructExecPath PropertyCallback
exit Process_functionExit Function 1
exitCode processExitCode CustomAccessor
features constructFeatures PropertyCallback
getActiveResourcesInfo Process_stubFunctionReturningArray Function 0
getegid Process_functiongetegid Function 0
@@ -1153,6 +1509,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionCwd,
hrtime constructProcessHrtimeObject PropertyCallback
isBun constructIsBun PropertyCallback
mainModule JSBuiltin ReadOnly|Builtin|Accessor|Function 0
memoryUsage constructMemoryUsage PropertyCallback
moduleLoadList Process_stubEmptyArray PropertyCallback
nextTick Process_functionNextTick Function 1
openStdin Process_functionOpenStdin Function 0
@@ -1166,6 +1523,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionCwd,
stderr constructStderr PropertyCallback
stdin constructStdin PropertyCallback
stdout constructStdout PropertyCallback
title processTitle CustomAccessor
umask Process_functionUmask Function 1
uptime Process_functionUptime Function 1
version constructVersion PropertyCallback
@@ -1192,6 +1550,14 @@ void Process::finishCreation(JSC::VM& vm)
{
Base::finishCreation(vm);
this->cpuUsageStructure.initLater([](const JSC::LazyProperty<JSC::JSObject, JSC::Structure>::Initializer& init) {
init.set(constructCPUUsageStructure(init.vm, init.owner->globalObject()));
});
this->memoryUsageStructure.initLater([](const JSC::LazyProperty<JSC::JSObject, JSC::Structure>::Initializer& init) {
init.set(constructMemoryUsageStructure(init.vm, init.owner->globalObject()));
});
this->putDirect(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, String("process"_s)), 0);
}

View File

@@ -45,6 +45,24 @@ public:
return accessor;
}
LazyProperty<JSObject, Structure> cpuUsageStructure;
LazyProperty<JSObject, Structure> memoryUsageStructure;
DECLARE_VISIT_CHILDREN;
template<typename, SubspaceAccess mode>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
return nullptr;
return WebCore::subspaceForImpl<Process, WebCore::UseCustomHeapCellType::No>(
vm,
[](auto& spaces) { return spaces.m_clientSubspaceForProcessObject.get(); },
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForProcessObject = std::forward<decltype(space)>(space); },
[](auto& spaces) { return spaces.m_subspaceForProcessObject.get(); },
[](auto& spaces, auto&& space) { spaces.m_subspaceForProcessObject = std::forward<decltype(space)>(space); });
}
void finishCreation(JSC::VM& vm);
};

View File

@@ -1,21 +1,5 @@
// File generated via `make generate-builtins`
static const struct CompactHashIndex processObjectTableIndex[141] = {
{ 42, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 16, 130 },
{ -1, -1 },
{ -1, -1 },
{ 19, 137 },
{ -1, -1 },
static const struct CompactHashIndex processObjectTableIndex[142] = {
{ 43, -1 },
{ -1, -1 },
{ -1, -1 },
@@ -27,43 +11,59 @@ static const struct CompactHashIndex processObjectTableIndex[141] = {
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 15, 129 },
{ -1, -1 },
{ -1, -1 },
{ 18, 138 },
{ -1, -1 },
{ 45, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 3, 140 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 3, 141 },
{ 1, 128 },
{ -1, -1 },
{ 57, -1 },
{ 59, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 30, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 50, -1 },
{ 27, -1 },
{ 10, -1 },
{ -1, -1 },
{ 11, -1 },
{ -1, -1 },
{ 15, 136 },
{ 31, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 52, -1 },
{ 27, -1 },
{ 12, -1 },
{ -1, -1 },
{ 19, -1 },
{ -1, -1 },
{ 14, 137 },
{ -1, -1 },
{ 36, -1 },
{ -1, -1 },
{ 38, -1 },
{ 55, -1 },
{ 35, -1 },
{ 6, 139 },
{ -1, -1 },
{ 37, -1 },
{ 53, -1 },
{ 34, -1 },
{ 6, 138 },
{ -1, -1 },
{ 49, -1 },
{ 51, -1 },
{ 4, -1 },
{ 45, -1 },
{ 47, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
@@ -75,23 +75,23 @@ static const struct CompactHashIndex processObjectTableIndex[141] = {
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 39, -1 },
{ 40, -1 },
{ -1, -1 },
{ 36, -1 },
{ 37, -1 },
{ -1, -1 },
{ 0, -1 },
{ 12, 129 },
{ 17, 131 },
{ 38, -1 },
{ 26, 135 },
{ 16, 130 },
{ 39, -1 },
{ -1, -1 },
{ 23, -1 },
{ 13, -1 },
{ 11, -1 },
{ -1, -1 },
{ -1, -1 },
{ 56, -1 },
{ 58, -1 },
{ -1, -1 },
{ -1, -1 },
{ 47, -1 },
{ 30, 136 },
{ -1, -1 },
{ 29, -1 },
{ 22, -1 },
@@ -105,45 +105,46 @@ static const struct CompactHashIndex processObjectTableIndex[141] = {
{ 5, -1 },
{ -1, -1 },
{ -1, -1 },
{ 46, -1 },
{ 48, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 14, 132 },
{ 13, 131 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 9, -1 },
{ 25, 134 },
{ 25, 133 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 21, 135 },
{ 21, 134 },
{ -1, -1 },
{ -1, -1 },
{ -1, -1 },
{ 44, 139 },
{ 46, 140 },
{ -1, -1 },
{ 18, -1 },
{ 17, -1 },
{ 8, -1 },
{ 26, -1 },
{ 28, -1 },
{ 31, 133 },
{ 32, -1 },
{ 32, 132 },
{ 33, -1 },
{ 40, -1 },
{ 34, -1 },
{ 41, -1 },
{ 48, -1 },
{ 51, -1 },
{ 52, -1 },
{ 42, -1 },
{ 44, -1 },
{ 49, -1 },
{ 50, -1 },
{ 53, -1 },
{ 54, -1 },
{ 55, -1 },
{ 56, -1 },
{ 57, -1 },
};
static const struct HashTableValue processObjectTableValues[58] = {
static const struct HashTableValue processObjectTableValues[60] = {
{ "abort"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionAbort, 1 } },
{ "allowedNodeEnvironmentFlags"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, Process_stubEmptySet } },
{ "arch"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructArch } },
@@ -154,16 +155,16 @@ static const struct HashTableValue processObjectTableValues[58] = {
{ "browser"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructBrowser } },
{ "chdir"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionChdir, 1 } },
{ "config"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructProcessConfigObject } },
{ "debugPort"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processDebugPort, setProcessDebugPort } },
{ "exitCode"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processExitCode, setProcessExitCode } },
{ "title"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processTitle, setProcessTitle } },
{ "cpuUsage"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionCpuUsage, 1 } },
{ "cwd"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionCwd, 1 } },
{ "debugPort"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processDebugPort, setProcessDebugPort } },
{ "dlopen"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionDlopen, 1 } },
{ "emitWarning"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_emitWarning, 1 } },
{ "env"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructEnv } },
{ "execArgv"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructExecArgv } },
{ "execPath"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructExecPath } },
{ "exit"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionExit, 1 } },
{ "exitCode"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processExitCode, setProcessExitCode } },
{ "features"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructFeatures } },
{ "getActiveResourcesInfo"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_stubFunctionReturningArray, 0 } },
{ "getegid"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functiongetegid, 0 } },
@@ -174,6 +175,7 @@ static const struct HashTableValue processObjectTableValues[58] = {
{ "hrtime"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructProcessHrtimeObject } },
{ "isBun"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructIsBun } },
{ "mainModule"_s, ((static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::Builtin|PropertyAttribute::Accessor|PropertyAttribute::Function)) & ~PropertyAttribute::Function) | PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinGeneratorType, processObjectMainModuleCodeGenerator, 0 } },
{ "memoryUsage"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructMemoryUsage } },
{ "moduleLoadList"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, Process_stubEmptyArray } },
{ "nextTick"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionNextTick, 1 } },
{ "openStdin"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionOpenStdin, 0 } },
@@ -187,6 +189,7 @@ static const struct HashTableValue processObjectTableValues[58] = {
{ "stderr"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructStderr } },
{ "stdin"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructStdin } },
{ "stdout"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructStdout } },
{ "title"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processTitle, setProcessTitle } },
{ "umask"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionUmask, 1 } },
{ "uptime"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionUptime, 1 } },
{ "version"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructVersion } },
@@ -205,4 +208,4 @@ static const struct HashTableValue processObjectTableValues[58] = {
};
static const struct HashTable processObjectTable =
{ 58, 127, true, nullptr, processObjectTableValues, processObjectTableIndex };
{ 60, 127, true, nullptr, processObjectTableValues, processObjectTableIndex };

View File

@@ -38,6 +38,7 @@ public:
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSMockImplementation;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSMockFunction;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMockWithImplementationCleanupData;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForProcessObject;
#include "ZigGeneratedClasses+DOMClientIsoSubspaces.h"
/* --- bun --- */

View File

@@ -38,6 +38,7 @@ public:
std::unique_ptr<IsoSubspace> m_subspaceForJSMockImplementation;
std::unique_ptr<IsoSubspace> m_subspaceForJSMockFunction;
std::unique_ptr<IsoSubspace> m_subspaceForMockWithImplementationCleanupData;
std::unique_ptr<IsoSubspace> m_subspaceForProcessObject;
#include "ZigGeneratedClasses+DOMIsoSubspaces.h"
/*-- BUN --*/

View File

@@ -162,7 +162,8 @@ it("process.umask()", () => {
expect(process.umask()).toBe(orig);
});
const versions = existsSync(import.meta.dir + "/../../src/generated_versions_list.zig");
const generated_versions_list = join(import.meta.dir, "../../../../src/generated_versions_list.zig");
const versions = existsSync(generated_versions_list);
(versions ? it : it.skip)("process.versions", () => {
// Generate a list of all the versions in the versions object
// example:
@@ -178,7 +179,7 @@ const versions = existsSync(import.meta.dir + "/../../src/generated_versions_lis
// pub const c_ares = "0e7a5dee0fbb04080750cf6eabbe89d8bae87faa";
// pub const usockets = "fafc241e8664243fc0c51d69684d5d02b9805134";
const versions = Object.fromEntries(
readFileSync(import.meta.dir + "/../../src/generated_versions_list.zig", "utf8")
readFileSync(generated_versions_list, "utf8")
.split("\n")
.filter(line => line.startsWith("pub const") && !line.includes("zig") && line.includes(' = "'))
.map(line => line.split(" = "))
@@ -291,3 +292,140 @@ describe("process.onBeforeExit", () => {
expect(stdout.toString().trim()).toBe("beforeExit: 0\nbeforeExit: 1\nexit: 2");
});
});
it("process.memoryUsage", () => {
expect(process.memoryUsage()).toEqual({
rss: expect.any(Number),
heapTotal: expect.any(Number),
heapUsed: expect.any(Number),
external: expect.any(Number),
arrayBuffers: expect.any(Number),
});
});
it("process.memoryUsage.rss", () => {
expect(process.memoryUsage.rss()).toEqual(expect.any(Number));
});
describe("process.cpuUsage", () => {
it("works", () => {
expect(process.cpuUsage()).toEqual({
user: expect.any(Number),
system: expect.any(Number),
});
});
it("works with diff", () => {
const init = process.cpuUsage();
for (let i = 0; i < 1000; i++) {}
const delta = process.cpuUsage(init);
expect(delta.user).toBeGreaterThan(0);
expect(delta.system).toBeGreaterThan(0);
});
it("works with diff of different structure", () => {
const init = {
user: 0,
system: 0,
};
for (let i = 0; i < 1000; i++) {}
const delta = process.cpuUsage(init);
expect(delta.user).toBeGreaterThan(0);
expect(delta.system).toBeGreaterThan(0);
});
it("throws on invalid property", () => {
const fixtures = [
{},
{ user: null },
{ user: {} },
{ user: "potato" },
{ user: 123 },
{ user: 123, system: null },
{ user: 123, system: "potato" },
];
for (const fixture of fixtures) {
expect(() => process.cpuUsage(fixture)).toThrow();
}
});
// Skipped on Linux because it seems to not change as often as on macOS
it.skipIf(process.platform === "linux")("increases monotonically", () => {
const init = process.cpuUsage();
for (let i = 0; i < 10000; i++) {}
const another = process.cpuUsage();
expect(another.user).toBeGreaterThan(init.user);
expect(another.system).toBeGreaterThan(init.system);
});
});
it("process.getegid", () => {
expect(typeof process.getegid()).toBe("number");
});
it("process.geteuid", () => {
expect(typeof process.geteuid()).toBe("number");
});
it("process.getgid", () => {
expect(typeof process.getgid()).toBe("number");
});
it("process.getgroups", () => {
expect(process.getgroups()).toBeInstanceOf(Array);
expect(process.getgroups().length).toBeGreaterThan(0);
});
it("process.getuid", () => {
expect(typeof process.getuid()).toBe("number");
});
it("process.getuid", () => {
expect(typeof process.getuid()).toBe("number");
});
const undefinedStubs = [
"_debugEnd",
"_debugProcess",
"_fatalException",
"_linkedBinding",
"_rawDebug",
"_startProfilerIdleNotifier",
"_stopProfilerIdleNotifier",
"_tickCallback",
];
for (const stub of undefinedStubs) {
it(`process.${stub}`, () => {
expect(process[stub]()).toBeUndefined();
});
}
const arrayStubs = ["getActiveResourcesInfo", "_getActiveRequests", "_getActiveHandles"];
for (const stub of arrayStubs) {
it(`process.${stub}`, () => {
expect(process[stub]()).toBeInstanceOf(Array);
});
}
const emptyObjectStubs = ["_preload_modules"];
const emptySetStubs = ["allowedNodeEnvironmentFlags"];
const emptyArrayStubs = ["moduleLoadList"];
for (const stub of emptyObjectStubs) {
it(`process.${stub}`, () => {
expect(process[stub]).toEqual({});
});
}
for (const stub of emptySetStubs) {
it(`process.${stub}`, () => {
expect(process[stub]).toBeInstanceOf(Set);
expect(process[stub].size).toBe(0);
});
}
for (const stub of emptyArrayStubs) {
it(`process.${stub}`, () => {
expect(process[stub]).toBeInstanceOf(Array);
expect(process[stub]).toHaveLength(0);
});
}