From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Sun, 12 Jun 2022 23:15:17 -0600 Subject: [PATCH] java.base: Enable java.lang.Process on serenity --- make/modules/java.base/Launcher.gmk | 2 +- make/modules/java.base/lib/CoreLibraries.gmk | 3 + .../libjava/ProcessHandleImpl_serenity.cpp | 164 ++++++++++++++++++ .../unix/classes/java/lang/ProcessImpl.java | 7 +- 4 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp diff --git a/make/modules/java.base/Launcher.gmk b/make/modules/java.base/Launcher.gmk index 700ddefda49e891ac1a2cfd8602fb8a9409ad1d4..78c884dae8271aea4431976823a0f18506cab4b4 100644 --- a/make/modules/java.base/Launcher.gmk +++ b/make/modules/java.base/Launcher.gmk @@ -73,7 +73,7 @@ endif ################################################################################ -ifeq ($(call isTargetOs, macosx aix linux), true) +ifeq ($(call isTargetOs, macosx aix linux serenity), true) $(eval $(call SetupJdkExecutable, BUILD_JSPAWNHELPER, \ NAME := jspawnhelper, \ SRC := $(TOPDIR)/src/$(MODULE)/unix/native/jspawnhelper, \ diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk index 0a61d009f34a4e73ace746d0cc6068fe2852e832..7867a3095dbe3d76c8db6d7d1948ae0f05d41c63 100644 --- a/make/modules/java.base/lib/CoreLibraries.gmk +++ b/make/modules/java.base/lib/CoreLibraries.gmk @@ -90,6 +90,8 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \ OPTIMIZATION := HIGH, \ CFLAGS := $(CFLAGS_JDKLIB) \ $(LIBJAVA_CFLAGS), \ + CXXFLAGS := $(CXXFLAGS_JDKLIB) \ + $(LIBJAVA_CXXFLAGS), \ jdk_util.c_CFLAGS := $(VERSION_CFLAGS), \ EXTRA_HEADER_DIRS := libfdlibm, \ WARNINGS_AS_ERRORS_xlc := false, \ @@ -102,6 +104,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \ LIBS_unix := -ljvm, \ LIBS_linux := $(LIBDL), \ LIBS_aix := $(LIBDL) $(LIBM),\ + LIBS_serenity := $(LIBDL) -lcore, \ LIBS_macosx := -framework CoreFoundation \ -framework Foundation \ -framework SystemConfiguration, \ diff --git a/src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp b/src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc0c08cb85a682d66a00f6b48ad2871f83b5e719 --- /dev/null +++ b/src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#define AK_DONT_REPLACE_STD + +#include "jvm.h" +#include "jni.h" +#include "jni_util.h" +#include "java_lang_String.h" + +extern "C" { +#include "ProcessHandleImpl_unix.h" +} + +#include +#include +#include +#include +#include +#include + +/* + * Implementation of native ProcessHandleImpl functions for SERENITY. + * See ProcessHandleImpl_unix.c for more details. + */ + +#define JAVA_TRY(expression, message) \ + ({ \ + auto _temporary_result = (expression); \ + if (_temporary_result.is_error()) [[unlikely]] \ + return throwSerenityError(env, _temporary_result.release_error(), (message)); \ + _temporary_result.release_value(); \ + }) + + +static RefPtr proc_all; + +extern "C" { +void os_initNative(JNIEnv *env, jclass clazz) { + proc_all = MUST(Core::File::open("/sys/kernel/processes", Core::OpenMode::ReadOnly)); +} + +jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, + jlongArray jparentArray, jlongArray jstimesArray) { + return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray); +} + +pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) { + auto maybe_stats = Core::ProcessStatisticsReader::get_all(proc_all); + if (maybe_stats.is_error()) { + JNU_ThrowByNameWithLastError(env, + "java/lang/RuntimeException", "ProcessStatisticsReader::get_all failed"); + return -1; + } + auto stats = maybe_stats.release_value(); + auto proc_it = find_if(stats.processes.begin(), stats.processes.end(), [pid](auto& proc_stats) { + return proc_stats.pid == pid; + }); + if (proc_it == stats.processes.end()) { + JNU_ThrowByNameWithLastError(env, + "java/lang/RuntimeException", "Selected pid does not exist"); + return -1; + } + auto& proc = *proc_it; + + for (auto& thread : proc.threads) { + *total += thread.time_user + thread.time_kernel; + } + + *start = 0; // FIXME: When did thread start? not reported in /sys/kernel/processes + + return proc.ppid; +} + + +static void throwSerenityError(JNIEnv* env, Error const& e, StringView msg) { + char err_buf[256]; + if (e.is_errno()) + getErrorString(e.code(), err_buf, sizeof(err_buf)); + else + strncpy(err_buf, e.string_literal().characters_without_null_termination(), sizeof(err_buf) - 1); + jstring s = JNU_NewStringPlatform(env, err_buf); + if (s != NULL) { + jobject x = JNU_NewObjectByName(env, "java/lang/RuntimeException", + "(Ljava/lang/String;)V", s); + if (x != NULL) { + env->Throw((jthrowable)x); + } + } + if (!env->ExceptionOccurred()) { + JNU_ThrowByName(env, "java/lang/RuntimeException", msg.characters_without_null_termination()); + } +} + +void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) { + auto maybe_stats = Core::ProcessStatisticsReader::get_all(proc_all); + if (maybe_stats.is_error()) { + JNU_ThrowByNameWithLastError(env, + "java/lang/RuntimeException", "ProcessStatisticsReader::get_all failed"); + return; + } + + auto stats = maybe_stats.release_value(); + auto proc_it = find_if(stats.processes.begin(), stats.processes.end(), [pid](auto& proc_stats) { + return proc_stats.pid == pid; + }); + if (proc_it == stats.processes.end()) { + JNU_ThrowByNameWithLastError(env, + "java/lang/RuntimeException", "Selected pid does not exist"); + return; + } + auto& proc = *proc_it; + + unix_getUserInfo(env, jinfo, proc.pid); + JNU_CHECK_EXCEPTION(env); + + auto cmdline_file = JAVA_TRY(Core::Stream::File::open(DeprecatedString::formatted("/proc/{}/cmdline", pid), Core::Stream::OpenMode::Read), "Unable to open /proc/pid/cmdline"sv); + auto contents = JAVA_TRY(cmdline_file->read_until_eof(), "Unable to read /proc/pid/cmdline"sv); + auto cmdline = JAVA_TRY(JsonValue::from_string(contents), "Invalid JSON in /proc/pid/cmdline"sv); + + if (!cmdline.is_array()) + return throwSerenityError(env, Error::from_string_literal("Not an array"), "Unexpected JSON in /proc/pid/cmdline"sv); + + jstring cmdexe = JNU_NewStringPlatform(env, cmdline.as_array()[0].as_string().characters()); + env->ExceptionClear(); // unconditionally clear any exception + env->SetObjectField(jinfo, ProcessHandleImpl_Info_commandID, cmdexe); + + int arr_size = cmdline.as_array().size(); + jclass string_clazz = JNU_ClassString(env); + CHECK_NULL(string_clazz); + jobjectArray java_cmdline = env->NewObjectArray(arr_size, string_clazz, NULL); + CHECK_NULL(java_cmdline); + jstring elem = NULL; + for (int i = 0; i < arr_size; ++i) { + elem = JNU_NewStringPlatform(env, cmdline.as_array()[i].as_string().characters()); + CHECK_NULL(elem); + env->SetObjectArrayElement(java_cmdline, i, elem); + JNU_CHECK_EXCEPTION(env); + } + env->SetObjectField(jinfo, ProcessHandleImpl_Info_argumentsID, java_cmdline); + JNU_CHECK_EXCEPTION(env); +} +} diff --git a/src/java.base/unix/classes/java/lang/ProcessImpl.java b/src/java.base/unix/classes/java/lang/ProcessImpl.java index 2bf36f8f136794af4030e12c64026ab217696959..317bbf158a2032cc23ef3b73528a9e9c801612cf 100644 --- a/src/java.base/unix/classes/java/lang/ProcessImpl.java +++ b/src/java.base/unix/classes/java/lang/ProcessImpl.java @@ -89,7 +89,9 @@ final class ProcessImpl extends Process { BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), - AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK); + AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), + + SERENITY(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK); final LaunchMechanism defaultLaunchMechanism; final Set validLaunchMechanisms; @@ -135,6 +137,7 @@ final class ProcessImpl extends Process { if (osName.equals("Linux")) { return LINUX; } if (osName.contains("OS X")) { return BSD; } if (osName.equals("AIX")) { return AIX; } + if (osName.equals("SerenityOS")) { return SERENITY; } throw new Error(osName + " is not a supported OS platform."); } @@ -348,6 +351,7 @@ final class ProcessImpl extends Process { switch (platform) { case LINUX: case BSD: + case SERENITY: stdin = (fds[0] == -1) ? ProcessBuilder.NullOutputStream.INSTANCE : new ProcessPipeOutputStream(fds[0]); @@ -467,6 +471,7 @@ final class ProcessImpl extends Process { case LINUX: case BSD: case AIX: + case SERENITY: // There is a risk that pid will be recycled, causing us to // kill the wrong process! So we only terminate processes // that appear to still be running. Even with this check,