[crac] RFR: 8368929: [CRaC] Move CPUFeatures check to C/R engine [v4]

Timofei Pushkin tpushkin at openjdk.org
Fri Oct 3 12:02:30 UTC 2025


On Thu, 2 Oct 2025 19:22:10 GMT, Radim Vansa <rvansa at openjdk.org> wrote:

>> Right now the logic checking if CPU features used before checkpoint match current CPU features is in VM code. VM stores and retrieves CPU features through C/R API's user_data extension. This is convenient when we have a single image that can be either accepted or rejected, but does not offer the flexibility for C/R engine to select the best image for current execution environment.
>> 
>> The goal of this issue is to move to a declarative API that will express the requirements using more abstract means, labels (for CPU architecture) and bitmaps (for CPU features).
>
> Radim Vansa has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Fix NoCPUFeaturesTest

Regarding `crexec` changes, I wonder if it would be easier for us to use the C++ stdlib fully (`std::vector`, `std::unordered_map`) catching all exceptions...

src/hotspot/cpu/x86/vm_version_x86.hpp line 617:

> 615:     static constexpr size_t print_buffer_length() {
> 616:       return MAX_CPU_FEATURES;
> 617:     }

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.

src/java.base/share/native/libcrexec/crexec.cpp line 142:

> 140:   &restore_data_extension.header,
> 141:   &user_data_extension.header,
> 142:   &image_constraints_extension.header,

Since we replace the direct use of user data with this new extension it should be a bit more efficient to put the new extension above the old one

src/java.base/share/native/libcrexec/crexec.cpp line 837:

> 835:   if (error) {
> 836:     return error;
> 837:   }

Nitpick: let's move this before initializing `restore_data_str` as this is a part of argument validation while `restore_data_str` and below is preparation for exec-ing

src/java.base/share/native/libcrexec/hashtable.hpp line 143:

> 141:   *value_ptr = std::forward<T>(value);
> 142:   return true;
> 143: }

I believe this usage of `std::forward` is incorrect as `T` is deduced from the hashtable class and not from the `value` parameter, so with this change it is now not possible to pass lvalues as `value`. I think the best would be to leave `T value` as it was and replace `std::forward` with `std::move` — lvalues will be copied then moved, rvalues will be just moved.

Same for `LinkedList::add`.

src/java.base/share/native/libcrexec/image_constraints.cpp line 39:

> 37: 
> 38: static FILE *open_tags(const char *image_location, const char *mode) {
> 39:   char fname[PATH_MAX];

In `crexec.cpp` we for some reason have our own conditional definition of `PATH_MAX`, I guess it caused problems at some point... Maybe create some `common.hpp` or `crexec.hpp` where `PATH_MAX` and `CREXEC` would be defined?

src/java.base/share/native/libcrexec/image_constraints.cpp line 46:

> 44:   FILE *f = fopen(fname, mode);
> 45:   if (f == nullptr) {
> 46:     fprintf(stderr, CREXEC "cannot open(%s) %s: %s\n", fname, mode, strerror(errno));

Suggestion:

    fprintf(stderr, CREXEC "cannot open %s in %s mode: %s\n", fname, mode, strerror(errno));

src/java.base/share/native/libcrexec/image_constraints.cpp line 142:

> 140:     return errno == ENOENT ? RESTORE_ERROR_NOT_FOUND : RESTORE_ERROR_NO_ACCESS;
> 141:   }
> 142:   char line[strlen(BITMAP_PREFIX) + _max_name_length + 1 + _max_value_length + 2];

AFAIR MSVC does not support arrays of non-constexpr size

src/java.base/share/native/libcrexec/image_constraints.cpp line 146:

> 144:   while (fgets(line, sizeof(line), f)) {
> 145:     char *eq = strchr(line, '=');
> 146:     char *nl = strchr(eq + 1, '\n');

I believe parsing will be broken if `=` is encountered in the tag name itself. Same for `\n` although that is less likely.

src/java.base/share/native/libcrexec/image_constraints.cpp line 160:

> 158:         .data = strdup(eq + 1),
> 159:         .data_length = (size_t) (nl - eq),
> 160:       });

Missing error checking here and below (`add`, `strdup`, `malloc`)

src/java.base/share/native/libcrexec/image_constraints.hpp line 36:

> 34: class ImageConstraints {
> 35: private:
> 36:   enum TagType {

It can have a smaller base type, and also making it a class is more idiomatic I believe
Suggestion:

  enum class TagType : std::uint8_t {

src/java.base/share/native/libcrexec/image_constraints.hpp line 93:

> 91: 
> 92:   const size_t _max_name_length = 256;
> 93:   const size_t _max_value_length = 256;

Suggestion:

  static constexpr size_t _MAX_NAME_LENGTH = 256;
  static constexpr size_t _MAX_VALUE_LENGTH = 256;

src/java.base/share/native/libcrexec/image_constraints.hpp line 95:

> 93:   const size_t _max_value_length = 256;
> 94: 
> 95:   bool check_tag(const char *name) {

"check" has too broad meaning IMO
Suggestion:

  bool has_tag(const char *name) const {

src/java.base/share/native/libcrexec/image_constraints.hpp line 119:

> 117:       .data = strdup(value),
> 118:       .data_length = value_length,
> 119:     });

`add` and `strdup`s can fail — this should be checked

src/java.base/share/native/libcrexec/linkedlist.hpp line 42:

> 40:   size_t _size;
> 41: public:
> 42:   LinkedList(): _head(nullptr), _tail(nullptr), _size(0) {}

Suggestion:

  Node *_head = nullptr;
  Node *_tail = nullptr;
  size_t _size = 0;
public:

src/java.base/share/native/libcrexec/linkedlist.hpp line 52:

> 50:   }
> 51: 
> 52:   T& add(T&& item) {

With the current style this should be:
Suggestion:

  T &add(T &&item) {

src/java.base/share/native/libcrexec/linkedlist.hpp line 53:

> 51: 
> 52:   T& add(T&& item) {
> 53:     Node *node = new(std::nothrow) Node();

Should do a null check after this

src/java.base/share/native/libcrexec/linkedlist.hpp line 74:

> 72:       node = node->_next;
> 73:     }
> 74:   }

I don't insist but implementing an `std::iterator` does not seem too cumbersome and would allow to use the normal for loop

src/java.base/share/native/libcrexec/linkedlist.hpp line 76:

> 74:   }
> 75: 
> 76:   size_t size() {

Suggestion:

  size_t size() const {

-------------

PR Review: https://git.openjdk.org/crac/pull/266#pullrequestreview-3298008166
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401210494
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401621379
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401643142
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401437652
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401543883
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401555404
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401594497
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401576791
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401598125
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401495490
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401506384
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401501558
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401523886
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401237419
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401444477
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401231987
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401266298
PR Review Comment: https://git.openjdk.org/crac/pull/266#discussion_r2401258129


More information about the crac-dev mailing list