diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index 65f78ab67c..24331d5f9a 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -41,6 +41,7 @@ #include #include "wtf-bindings.h" #include "EventLoopTask.h" +#include #include #include "ProcessBindingTTYWrap.h" @@ -2586,6 +2587,7 @@ void Process::visitChildrenImpl(JSCell* cell, Visitor& visitor) visitor.append(thisObject->m_execArgv); thisObject->m_cpuUsageStructure.visit(visitor); + thisObject->m_resourceUsageStructure.visit(visitor); thisObject->m_memoryUsageStructure.visit(visitor); thisObject->m_bindingUV.visit(visitor); thisObject->m_bindingNatives.visit(visitor); @@ -2594,9 +2596,11 @@ void Process::visitChildrenImpl(JSCell* cell, Visitor& visitor) DEFINE_VISIT_CHILDREN(Process); +constexpr uint32_t cpuUsageStructureInlineCapacity = std::min(JSFinalObject::maxInlineCapacity, std::max(2, JSFinalObject::defaultInlineCapacity)); + static Structure* constructCPUUsageStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) { - JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, globalObject->objectPrototype(), 2); + JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, globalObject->objectPrototype(), cpuUsageStructureInlineCapacity); PropertyOffset offset; structure = structure->addPropertyTransition( vm, @@ -2612,9 +2616,37 @@ static Structure* constructCPUUsageStructure(JSC::VM& vm, JSC::JSGlobalObject* g offset); return structure; } + +constexpr uint32_t resourceUsageStructureInlineCapacity = std::min(JSFinalObject::maxInlineCapacity, std::max(16, JSFinalObject::defaultInlineCapacity)); + +static Structure* constructResourceUsageStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, globalObject->objectPrototype(), resourceUsageStructureInlineCapacity); + PropertyOffset offset; + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "userCPUTime"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "systemCPUTime"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "maxRSS"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "sharedMemorySize"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "unsharedDataSize"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "unsharedStackSize"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "minorPageFault"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "majorPageFault"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "swappedOut"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "fsRead"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "fsWrite"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "ipcSent"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "ipcReceived"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "signalsCount"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "voluntaryContextSwitches"_s), 0, offset); + structure = structure->addPropertyTransition(vm, structure, JSC::Identifier::fromString(vm, "involuntaryContextSwitches"_s), 0, offset); + return structure; +} + +constexpr uint32_t memoryUsageStructureInlineCapacity = std::min(JSFinalObject::maxInlineCapacity, std::max(5, JSFinalObject::defaultInlineCapacity)); + static Structure* constructMemoryUsageStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) { - JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, globalObject->objectPrototype(), 5); + JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, globalObject->objectPrototype(), memoryUsageStructureInlineCapacity); 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); @@ -2644,6 +2676,50 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionConstrainedMemory, (JSC::JSGlobalObject return JSValue::encode(jsDoubleNumber(static_cast(WTF::ramSize()))); } +JSC_DEFINE_HOST_FUNCTION(Process_functionResourceUsage, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + auto& vm = JSC::getVM(globalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + +#if !OS(WINDOWS) + struct rusage rusage; + if (getrusage(RUSAGE_SELF, &rusage) != 0) { + throwSystemError(throwScope, globalObject, "Failed to get resource usage"_s, "getrusage"_s, errno); + return {}; + } +#else + uv_rusage_t rusage; + int err = uv_getrusage(&rusage); + if (err) { + throwSystemError(throwScope, globalObject, "uv_getrusage"_s, err); + return {}; + } +#endif + Process* process = getProcessObject(globalObject, callFrame->thisValue()); + + Structure* resourceUsageStructure = process->resourceUsageStructure(); + JSObject* result = JSC::constructEmptyObject(vm, resourceUsageStructure); + + result->putDirectOffset(vm, 0, jsNumber(std::chrono::microseconds::period::den * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec)); + result->putDirectOffset(vm, 1, jsNumber(std::chrono::microseconds::period::den * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec)); + result->putDirectOffset(vm, 2, jsNumber(rusage.ru_maxrss)); + result->putDirectOffset(vm, 3, jsNumber(rusage.ru_ixrss)); + result->putDirectOffset(vm, 4, jsNumber(rusage.ru_idrss)); + result->putDirectOffset(vm, 5, jsNumber(rusage.ru_isrss)); + result->putDirectOffset(vm, 6, jsNumber(rusage.ru_minflt)); + result->putDirectOffset(vm, 7, jsNumber(rusage.ru_majflt)); + result->putDirectOffset(vm, 8, jsNumber(rusage.ru_nswap)); + result->putDirectOffset(vm, 9, jsNumber(rusage.ru_inblock)); + result->putDirectOffset(vm, 10, jsNumber(rusage.ru_oublock)); + result->putDirectOffset(vm, 11, jsNumber(rusage.ru_msgsnd)); + result->putDirectOffset(vm, 12, jsNumber(rusage.ru_msgrcv)); + result->putDirectOffset(vm, 13, jsNumber(rusage.ru_nsignals)); + result->putDirectOffset(vm, 14, jsNumber(rusage.ru_nvcsw)); + result->putDirectOffset(vm, 15, jsNumber(rusage.ru_nivcsw)); + + return JSValue::encode(result); +} + JSC_DEFINE_HOST_FUNCTION(Process_functionCpuUsage, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { auto& vm = JSC::getVM(globalObject); @@ -2656,8 +2732,9 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionCpuUsage, (JSC::JSGlobalObject * global } #else uv_rusage_t rusage; - if (uv_getrusage(&rusage) != 0) { - throwSystemError(throwScope, globalObject, "Failed to get CPU usage"_s, "uv_getrusage"_s, errno); + int err = uv_getrusage(&rusage); + if (err) { + throwSystemError(throwScope, globalObject, "Failed to get CPU usage"_s, "uv_getrusage"_s, err); return {}; } #endif @@ -2666,10 +2743,8 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionCpuUsage, (JSC::JSGlobalObject * global Structure* cpuUsageStructure = process->cpuUsageStructure(); - 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; + double user = std::chrono::microseconds::period::den * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec; + double system = std::chrono::microseconds::period::den * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec; if (callFrame->argumentCount() > 0) { JSValue comparatorValue = callFrame->argument(0); @@ -3429,6 +3504,18 @@ extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValu /* Source for Process.lut.h @begin processObjectTable + _debugEnd Process_stubEmptyFunction Function 0 + _debugProcess Process_stubEmptyFunction Function 0 + _fatalException Process_stubEmptyFunction Function 1 + _getActiveHandles Process_stubFunctionReturningArray Function 0 + _getActiveRequests Process_stubFunctionReturningArray Function 0 + _kill Process_functionReallyKill Function 2 + _linkedBinding Process_stubEmptyFunction Function 0 + _preload_modules Process_stubEmptyArray PropertyCallback + _rawDebug Process_stubEmptyFunction Function 0 + _startProfilerIdleNotifier Process_stubEmptyFunction Function 0 + _stopProfilerIdleNotifier Process_stubEmptyFunction Function 0 + _tickCallback Process_stubEmptyFunction Function 0 abort Process_functionAbort Function 1 allowedNodeEnvironmentFlags Process_stubEmptySet PropertyCallback arch constructArch PropertyCallback @@ -3438,8 +3525,8 @@ extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValu availableMemory Process_availableMemory Function 0 binding Process_functionBinding Function 1 browser constructBrowser PropertyCallback - chdir Process_functionChdir Function 1 channel constructProcessChannel PropertyCallback + chdir Process_functionChdir Function 1 config constructProcessConfigObject PropertyCallback connected processConnected CustomAccessor constrainedMemory Process_functionConstrainedMemory Function 0 @@ -3456,6 +3543,7 @@ extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValu exitCode processExitCode CustomAccessor|DontDelete features constructFeatures PropertyCallback getActiveResourcesInfo Process_stubFunctionReturningArray Function 0 + getBuiltinModule Process_functionLoadBuiltinModule Function 1 hasUncaughtExceptionCaptureCallback Process_hasUncaughtExceptionCaptureCallback Function 0 hrtime constructProcessHrtimeObject PropertyCallback isBun constructIsBun PropertyCallback @@ -3473,10 +3561,11 @@ extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValu ref Process_ref Function 1 release constructProcessReleaseObject PropertyCallback report constructProcessReportObject PropertyCallback + resourceUsage Process_functionResourceUsage Function 0 revision constructRevision PropertyCallback + send constructProcessSend PropertyCallback setSourceMapsEnabled Process_stubEmptyFunction Function 1 setUncaughtExceptionCaptureCallback Process_setUncaughtExceptionCaptureCallback Function 1 - send constructProcessSend PropertyCallback stderr constructStderr PropertyCallback stdin constructStdin PropertyCallback stdout constructStdout PropertyCallback @@ -3487,19 +3576,6 @@ extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValu uptime Process_functionUptime Function 1 version constructVersion PropertyCallback versions constructVersions PropertyCallback - _debugEnd Process_stubEmptyFunction Function 0 - _debugProcess Process_stubEmptyFunction Function 0 - _fatalException Process_stubEmptyFunction Function 1 - _getActiveRequests Process_stubFunctionReturningArray Function 0 - _getActiveHandles Process_stubFunctionReturningArray Function 0 - _linkedBinding Process_stubEmptyFunction Function 0 - _preload_modules Process_stubEmptyArray PropertyCallback - _rawDebug Process_stubEmptyFunction Function 0 - _startProfilerIdleNotifier Process_stubEmptyFunction Function 0 - _stopProfilerIdleNotifier Process_stubEmptyFunction Function 0 - _tickCallback Process_stubEmptyFunction Function 0 - _kill Process_functionReallyKill Function 2 - getBuiltinModule Process_functionLoadBuiltinModule Function 1 #if !OS(WINDOWS) getegid Process_functiongetegid Function 0 @@ -3532,6 +3608,10 @@ void Process::finishCreation(JSC::VM& vm) init.set(constructCPUUsageStructure(init.vm, init.owner->globalObject())); }); + m_resourceUsageStructure.initLater([](const JSC::LazyProperty::Initializer& init) { + init.set(constructResourceUsageStructure(init.vm, init.owner->globalObject())); + }); + m_memoryUsageStructure.initLater([](const JSC::LazyProperty::Initializer& init) { init.set(constructMemoryUsageStructure(init.vm, init.owner->globalObject())); }); diff --git a/src/bun.js/bindings/BunProcess.h b/src/bun.js/bindings/BunProcess.h index cf6c0c71da..2e5a3fb7e9 100644 --- a/src/bun.js/bindings/BunProcess.h +++ b/src/bun.js/bindings/BunProcess.h @@ -19,6 +19,7 @@ class Process : public WebCore::JSEventEmitter { using Base = WebCore::JSEventEmitter; LazyProperty m_cpuUsageStructure; + LazyProperty m_resourceUsageStructure; LazyProperty m_memoryUsageStructure; LazyProperty m_bindingUV; LazyProperty m_bindingNatives; @@ -118,6 +119,7 @@ public: } inline Structure* cpuUsageStructure() { return m_cpuUsageStructure.getInitializedOnMainThread(this); } + inline Structure* resourceUsageStructure() { return m_resourceUsageStructure.getInitializedOnMainThread(this); } inline Structure* memoryUsageStructure() { return m_memoryUsageStructure.getInitializedOnMainThread(this); } inline JSObject* bindingUV() { return m_bindingUV.getInitializedOnMainThread(this); } inline JSObject* bindingNatives() { return m_bindingNatives.getInitializedOnMainThread(this); } diff --git a/src/bun.js/bindings/helpers.cpp b/src/bun.js/bindings/helpers.cpp index c1179335d6..1f15a716ab 100644 --- a/src/bun.js/bindings/helpers.cpp +++ b/src/bun.js/bindings/helpers.cpp @@ -6,18 +6,20 @@ #include #endif -JSC::JSValue createSystemError(JSC::JSGlobalObject* global, ASCIILiteral message, ASCIILiteral syscall, int err) +using namespace JSC; + +JSValue createSystemError(JSGlobalObject* global, ASCIILiteral message, ASCIILiteral syscall, int err) { - auto* instance = JSC::createError(global, String(message)); + auto* instance = createError(global, String(message)); auto& vm = global->vm(); auto& builtinNames = WebCore::builtinNames(vm); instance->putDirect(vm, builtinNames.syscallPublicName(), jsString(vm, String(syscall)), 0); - instance->putDirect(vm, builtinNames.errnoPublicName(), JSC::jsNumber(err), 0); - instance->putDirect(vm, vm.propertyNames->name, jsString(vm, String("SystemError"_s)), JSC::PropertyAttribute::DontEnum | 0); + instance->putDirect(vm, builtinNames.errnoPublicName(), jsNumber(err), 0); + instance->putDirect(vm, vm.propertyNames->name, jsString(vm, String("SystemError"_s)), PropertyAttribute::DontEnum | 0); return instance; } -JSC::JSValue createSystemError(JSC::JSGlobalObject* global, ASCIILiteral syscall, int err) +JSValue createSystemError(JSGlobalObject* global, ASCIILiteral syscall, int err) { auto errstr = String::fromLatin1(Bun__errnoName(err)); #ifdef _WIN32 @@ -29,8 +31,8 @@ JSC::JSValue createSystemError(JSC::JSGlobalObject* global, ASCIILiteral syscall auto& vm = global->vm(); auto& builtinNames = WebCore::builtinNames(vm); instance->putDirect(vm, builtinNames.syscallPublicName(), jsString(vm, String(syscall)), 0); - instance->putDirect(vm, builtinNames.errnoPublicName(), JSC::jsNumber(err), 0); - instance->putDirect(vm, vm.propertyNames->name, jsString(vm, String("SystemError"_s)), JSC::PropertyAttribute::DontEnum | 0); + instance->putDirect(vm, builtinNames.errnoPublicName(), jsNumber(err), 0); + instance->putDirect(vm, vm.propertyNames->name, jsString(vm, String("SystemError"_s)), PropertyAttribute::DontEnum | 0); instance->putDirect(vm, builtinNames.codePublicName(), jsString(vm, errstr)); return instance; } diff --git a/test/js/node/test/parallel/test-resource-usage.js b/test/js/node/test/parallel/test-resource-usage.js new file mode 100644 index 0000000000..d46fe4aeb7 --- /dev/null +++ b/test/js/node/test/parallel/test-resource-usage.js @@ -0,0 +1,30 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +const rusage = process.resourceUsage(); +const fields = [ + 'userCPUTime', + 'systemCPUTime', + 'maxRSS', + 'sharedMemorySize', + 'unsharedDataSize', + 'unsharedStackSize', + 'minorPageFault', + 'majorPageFault', + 'swappedOut', + 'fsRead', + 'fsWrite', + 'ipcSent', + 'ipcReceived', + 'signalsCount', + 'voluntaryContextSwitches', + 'involuntaryContextSwitches', +]; + +assert.deepStrictEqual(Object.keys(rusage).sort(), fields.sort()); + +fields.forEach((n) => { + assert.strictEqual(typeof rusage[n], 'number', `${n} should be a number`); + assert(rusage[n] >= 0, `${n} should be above or equal 0`); +});