Skip to content
Open
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
3 changes: 3 additions & 0 deletions make/autoconf/flags-cflags.m4
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,9 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_CPU_DEP],
$1_DEFINES_CPU_JDK="${$1_DEFINES_CPU_JDK} -DARCH='\"$FLAGS_CPU_LEGACY\"' \
-D$FLAGS_CPU_LEGACY"

# setup arch name (the same as 'os.arch' system property)
$1_DEFINES_CPU_JVM="${$1_DEFINES_CPU_JVM} -DARCHPROPNAME='\"$OPENJDK_TARGET_CPU_OSARCH\"'"

if test "x$FLAGS_CPU_BITS" = x64; then
$1_DEFINES_CPU_JDK="${$1_DEFINES_CPU_JDK} -D_LP64=1"
$1_DEFINES_CPU_JVM="${$1_DEFINES_CPU_JVM} -D_LP64=1"
Expand Down
8 changes: 5 additions & 3 deletions src/hotspot/cpu/aarch64/vm_version_aarch64.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@ class VM_Version : public Abstract_VM_Version {
public:
// Initialization
static void initialize();
struct VM_Features {};
struct VM_Features {
int print_numbers(char *buf_orig, size_t buflen) const { return 0; }
static constexpr size_t print_buffer_length() { return 0; }
};
static bool cpu_features_binary(VM_Features *data) { return false; }
static bool cpu_features_binary_check(const VM_Features *data) { return data == nullptr; }
static bool ignore_cpu_features() { return true; }
static bool ignore_cpu_features(bool is_checkpoint) { return true; }
static void check_virtualizations();

static void print_platform_virtualization_info(outputStream*);
Expand Down
8 changes: 5 additions & 3 deletions src/hotspot/cpu/arm/vm_version_arm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ class VM_Version: public Abstract_VM_Version {
public:
static void initialize();
static bool is_initialized() { return _is_initialized; }
struct VM_Features {};
struct VM_Features {
int print_numbers(char *buf_orig, size_t buflen) const { return 0; }
static constexpr size_t print_buffer_length() { return 0; }
};
static bool cpu_features_binary(VM_Features *data) { return false; }
static bool cpu_features_binary_check(const VM_Features *data) { return data == nullptr; }
static bool ignore_cpu_features() { return true; }
static bool ignore_cpu_features(bool is_checkpoint) { return true; }


protected:
Expand Down
8 changes: 5 additions & 3 deletions src/hotspot/cpu/ppc/vm_version_ppc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ class VM_Version: public Abstract_VM_Version {
// Initialization
static void initialize();
static void check_virtualizations();
struct VM_Features {};
struct VM_Features {
int print_numbers(char *buf_orig, size_t buflen) const { return 0; }
static constexpr size_t print_buffer_length() { return 0; }
};
static bool cpu_features_binary(VM_Features *data) { return false; }
static bool cpu_features_binary_check(const VM_Features *data) { return data == nullptr; }
static bool ignore_cpu_features() { return true; }
static bool ignore_cpu_features(bool is_checkpoint) { return true; }

// Override Abstract_VM_Version implementation
static void print_platform_virtualization_info(outputStream*);
Expand Down
8 changes: 5 additions & 3 deletions src/hotspot/cpu/riscv/vm_version_riscv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,12 @@ class VM_Version : public Abstract_VM_Version {
// Initialization
static void initialize();
static void initialize_cpu_information();
struct VM_Features {};
struct VM_Features {
int print_numbers(char *buf_orig, size_t buflen) const { return 0; }
static constexpr size_t print_buffer_length() { return 0; }
};
static bool cpu_features_binary(VM_Features *data) { return false; }
static bool cpu_features_binary_check(const VM_Features *data) { return data == nullptr; }
static bool ignore_cpu_features() { return true; }
static bool ignore_cpu_features(bool is_checkpoint) { return true; }

constexpr static bool supports_stack_watermark_barrier() { return true; }

Expand Down
8 changes: 5 additions & 3 deletions src/hotspot/cpu/s390/vm_version_s390.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,10 +414,12 @@ class VM_Version: public Abstract_VM_Version {
static void initialize();
static void print_features();
static bool is_determine_features_test_running() { return _is_determine_features_test_running; }
struct VM_Features {};
struct VM_Features {
int print_numbers(char *buf_orig, size_t buflen) const { return 0; }
static constexpr size_t print_buffer_length() { return 0; }
};
static bool cpu_features_binary(VM_Features *data) { return false; }
static bool cpu_features_binary_check(const VM_Features *data) { return data == nullptr; }
static bool ignore_cpu_features() { return true; }
static bool ignore_cpu_features(bool is_checkpoint) { return true; }

// Override Abstract_VM_Version implementation
static void print_platform_virtualization_info(outputStream*);
Expand Down
49 changes: 1 addition & 48 deletions src/hotspot/cpu/x86/vm_version_x86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2557,55 +2557,8 @@ void VM_Version::VM_Features::print_missing_features() const {

bool VM_Version::cpu_features_binary(VM_Version::VM_Features *data) {
*data = _features;
return true;
}

bool VM_Version::cpu_features_binary_check(const VM_Version::VM_Features *data_ptr) {
assert(CPUFeatures == nullptr, "This should only be called on restore and CPUFeatures is not restore-settable");

if (!data_ptr) {
return false;
}
VM_Version::VM_Features data = *data_ptr;

if (ShowCPUFeatures) {
char buf[MAX_CPU_FEATURES * 16];
data.print_numbers_and_names(buf, sizeof(buf));
tty->print_cr("This snapshot's stored CPU features are: -XX:CPUFeatures=%s", buf);
}

VM_Version::VM_Features features_missing = data & ~_features;

// Workaround JDK-8311164: CPU_HT is set randomly on hybrid CPUs like Alder Lake.
features_missing.clear_feature(CPU_HT);

if (!features_missing.empty()) {
char buf_use[MAX_CPU_FEATURES];
(data & _features).print_numbers(buf_use, sizeof(buf_use));
char buf_have[MAX_CPU_FEATURES];
data.print_numbers(buf_have, sizeof(buf_have));
tty->print("You have to specify -XX:CPUFeatures=%s together with -XX:CRaCCheckpointTo when making a checkpoint file"
"; specified -XX:CRaCRestoreFrom file contains CPU features %s",
buf_use, buf_have);
features_missing.print_missing_features();
if (!IgnoreCPUFeatures) {
return false;
}
}

_features_saved = _features;
_features = data;

if (ShowCPUFeatures && !CRaCRestoreFrom) {
print_using_features_cr();
}

#ifdef LINUX
// glibc_not_using() has done setenv(TUNABLES_NAME) and it expects us to re-exec ourselves.
// But we were only checking the cpufeatures file before restoring the process so we ignore the result.
glibc_not_using();
#endif

data->clear_feature(CPU_HT);
return true;
}

Expand Down
12 changes: 10 additions & 2 deletions src/hotspot/cpu/x86/vm_version_x86.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,10 @@ class VM_Version : public Abstract_VM_Version {
return buf - buf_orig;
}

static constexpr size_t print_buffer_length() {
return MAX_CPU_FEATURES;
}
Comment on lines +615 to +617
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe by the time the corresponding printing function is used the resource area is already initialized. It thus should be possible to make it allocate a resource array internally instead of making the callers use this to allocate on stack. Or there can be two functions: one that uses a provided buffer and its wrapper that allocates a resource array.


void print_numbers_and_names(char *buf, size_t buflen) const {
int res = print_numbers(buf, buflen);
buf += res;
Expand Down Expand Up @@ -908,8 +912,12 @@ class VM_Version : public Abstract_VM_Version {
// Initialization
static void initialize();
static bool cpu_features_binary(VM_Features *data);
static bool cpu_features_binary_check(const VM_Features *data);
static bool ignore_cpu_features() { return _ignore_glibc_not_using; }
static bool ignore_cpu_features(bool is_checkpoint) {
// This gets triggered by -XX:CPUFeatures=ignore, not writing the features & arch
// on checkpoint into the image at all, and skipping the check on restore.
// IgnoreCPUFeatures is ignored on checkpoint
return _ignore_glibc_not_using || (!is_checkpoint && IgnoreCPUFeatures);
}
static void restore_check(const char* str, const char* msg_prefix);

// Override Abstract_VM_Version implementation
Expand Down
8 changes: 5 additions & 3 deletions src/hotspot/cpu/zero/vm_version_zero.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@
class VM_Version : public Abstract_VM_Version {
public:
static void initialize();
struct VM_Features {};
struct VM_Features {
int print_numbers(char *buf_orig, size_t buflen) const { return 0; }
static constexpr size_t print_buffer_length() { return 0; }
};
static bool cpu_features_binary(VM_Features *data) { return false; }
static bool cpu_features_binary_check(const VM_Features *data) { return data == nullptr; }
static bool ignore_cpu_features() { return true; }
static bool ignore_cpu_features(bool is_checkpoint) { return true; }

constexpr static bool supports_stack_watermark_barrier() { return true; }

Expand Down
8 changes: 8 additions & 0 deletions src/hotspot/share/include/crlib/crlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ struct crlib_api {
};
typedef const struct crlib_api crlib_api_t;

// Error codes returned from crlib_api.restore():
#define RESTORE_ERROR_UNKNOWN -1 // generic error
#define RESTORE_ERROR_NOT_FOUND -2 // image location does not contain an image
#define RESTORE_ERROR_NO_ACCESS -3 // image cannot be accessed/retrieved (permissions or I/O issue)
#define RESTORE_ERROR_INVALID -4 // image is not suitable (e.g. corruption or wrong architecture)
#define RESTORE_ERROR_MEMORY -5 // memory allocation failure during restore
#define RESTORE_ERROR_PROCINFO -6 // the process cannot fetch information about itself
Comment on lines +80 to +85
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cannot it be an enum?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API already defines return value as int, we don't want to change that part.


#define CRLIB_API crlib_api
#define CRLIB_API_FUNC "crlib_api"

Expand Down
63 changes: 63 additions & 0 deletions src/hotspot/share/include/crlib/crlib_image_constraints.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2025, Azul Systems, Inc. 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.
*
* 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.
*/
#ifndef CRLIB_IMAGE_CONSTRAINTS_H
#define CRLIB_IMAGE_CONSTRAINTS_H

#include "crlib.h"

#ifdef __cplusplus
extern "C" {
#endif

#define CRLIB_EXTENSION_IMAGE_CONSTRAINTS_NAME "image constraints"
#define CRLIB_EXTENSION_IMAGE_CONSTRAINTS(api) \
CRLIB_EXTENSION(api, crlib_image_constraints_t, CRLIB_EXTENSION_IMAGE_CONSTRAINTS_NAME)

typedef enum {
/* Natural zero-extensions of the bitmaps are equal */
EQUALS,
/* Bitmap in image must be subset or equal to bitmap in constraint */
SUBSET,
/* Bitmap in image must be superset or equal to bitmap in constraint */
SUPERSET,
} bitmap_comparison_t;

// API for storing & verifying application-defined labels and bitmaps
typedef struct crlib_image_constraints {
crlib_extension_t header;

// Invoked before checkpoint. Return false if name or value exceed limits, or if the name was already used.
bool (*set_label)(crlib_conf_t *, const char *name, const char *value);
bool (*set_bitmap)(crlib_conf_t *, const char *name, const unsigned char *value, size_t length_bytes);

// Invoked before restore. The conditions are not evaluated immediately; the restore will fail
// with RESTORE_ERROR_INVALID if these constraints are not matched.
void (*require_label)(crlib_conf_t *, const char *name, const char *value);
void (*require_bitmap)(crlib_conf_t *, const char *name, const unsigned char *value, size_t length_bytes, bitmap_comparison_t comparison);
} crlib_image_constraints_t;

#ifdef __cplusplus
} // extern "C"
#endif

#endif // CRLIB_USER_DATA_H
61 changes: 46 additions & 15 deletions src/hotspot/share/runtime/crac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ static jlong _restore_start_time;
static jlong _restore_start_nanos;

CracEngine *crac::_engine = nullptr;
unsigned int crac::_generation = 1;
char crac::_checkpoint_bootid[UUID_LENGTH];
jlong crac::_checkpoint_wallclock_seconds;
jlong crac::_checkpoint_wallclock_nanos;
Expand Down Expand Up @@ -102,23 +103,27 @@ static char * strchrnul(char * str, char c) {
int crac::checkpoint_restore(int *shmid) {
guarantee(_engine != nullptr, "CRaC engine is not initialized");

// If this is a second checkpoint we should use a clear configuration
if (_generation != 0 && !_engine->reset_conf()) {
return JVM_CHECKPOINT_ERROR;
}

crac::record_time_before_checkpoint();

// CRaCCheckpointTo can be changed on restore so we need to update the conf
// to account for that.
// Note that CRaCEngine and CRaCEngineOptions are not updated (as documented)
// so we don't need to re-init the whole engine handle.
if (restore_start_time() != -1 && // A way to detect we've restored at least once
!_engine->configure_image_location(CRaCCheckpointTo)) {
if (_generation != 0 && !_engine->configure_image_location(CRaCCheckpointTo)) {
return JVM_CHECKPOINT_ERROR;
}

if (!VM_Version::ignore_cpu_features()) {
if (!VM_Version::ignore_cpu_features(true)) {
VM_Version::VM_Features data;
if (VM_Version::cpu_features_binary(&data)) {
switch (_engine->prepare_user_data_api()) {
switch (_engine->prepare_image_constraints_api()) {
case CracEngine::ApiStatus::OK:
if (!_engine->cpufeatures_store(&data)) {
if (!_engine->store_cpuinfo(&data)) {
return JVM_CHECKPOINT_ERROR;
}
break;
Expand Down Expand Up @@ -275,6 +280,7 @@ void VM_Crac::doit() {
}
}

crac::_generation++;
Arguments::reset_for_crac_restore();
os::reset_cached_process_id();

Expand Down Expand Up @@ -520,17 +526,16 @@ void crac::restore(crac_restore_data& restore_data) {
return;
}

if (!VM_Version::ignore_cpu_features()) {
switch (engine.prepare_user_data_api()) {
// Previously IgnoreCPUFeatures didn't disable the check completely; the difference
// was printed out but continued even despite features not being satisfied.
// Since the check itself is delegated to the C/R Engine we will simply
// skip the check here.
if (!VM_Version::ignore_cpu_features(false)) {
switch (engine.prepare_image_constraints_api()) {
case CracEngine::ApiStatus::OK: {
VM_Version::VM_Features data;
bool present;
if (!engine.cpufeatures_load(&data, &present)) {
return;
}
if (!VM_Version::cpu_features_binary_check(present ? &data : nullptr)) {
log_error(crac)("Image %s has incompatible CPU features in its user data", CRaCRestoreFrom);
return;
if (VM_Version::cpu_features_binary(&data)) {
engine.require_cpuinfo(&data);
}
} break;
case CracEngine::ApiStatus::ERR:
Expand Down Expand Up @@ -580,7 +585,33 @@ void crac::restore(crac_restore_data& restore_data) {

const int ret = engine.restore();
if (ret != 0) {
log_error(crac)("CRaC engine failed to restore from %s: error %i", CRaCRestoreFrom, ret);
const char *msg = "";
switch (ret) {
case RESTORE_ERROR_NOT_FOUND:
msg = "the image or its part cannot be found";
break;
case RESTORE_ERROR_NO_ACCESS:
msg = "the image cannot be accessed/retrieved (permissions or I/O issue)";
break;
case RESTORE_ERROR_INVALID:
msg = "the image does not match current CPU or is corrupted";
break;
case RESTORE_ERROR_MEMORY:
msg = "memory allocation failure during restore";
break;
case RESTORE_ERROR_PROCINFO:
msg = "the process cannot fetch information about itself";
break;
}
log_error(crac)("CRaC engine failed to restore from %s: %s (error %d)", CRaCRestoreFrom, msg, ret);
}
if (ret == RESTORE_ERROR_INVALID) {
VM_Version::VM_Features data;
if (VM_Version::cpu_features_binary(&data)) {
char buf[VM_Version::VM_Features::print_buffer_length()];
data.print_numbers(buf, sizeof(buf));
log_error(crac)("\tIf the restore failed due to a wrong CPU features, try using -XX:CPUFeatures=%s on checkpoint.", buf);
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/runtime/crac.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class crac: AllStatic {

private:
static CracEngine *_engine;
static unsigned int _generation;

static char _checkpoint_bootid[UUID_LENGTH];
// Timestamps recorded before checkpoint.
Expand Down
Loading