Skip to content

Commit

Permalink
[EagerAppCDS] enable classpath change by replacing given env var
Browse files Browse the repository at this point in the history
Summary: If user give environment vars and use
`-Dcom.alibaba.cds.cp.reloc.envs`, cds will change the
classpath by using given environment vars to transform
some paths in classpath.

Test Plan: ci jtreg
jdk/test/com/alibaba/quickstart/TestEnvVariableReplay.java
jdk/test/com/alibaba/quickstart/TestEnvVariableDump.java

Reviewed-by: lingjun-cg, yuleil

Issue: dragonwell-project#595
  • Loading branch information
jia-wei-tang committed Sep 22, 2023
1 parent 029c216 commit 19b021c
Show file tree
Hide file tree
Showing 8 changed files with 362 additions and 5 deletions.
20 changes: 17 additions & 3 deletions hotspot/src/share/vm/classfile/sharedClassUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "runtime/arguments.hpp"
#include "runtime/java.hpp"
#include "runtime/os.hpp"
#include "runtime/quickStart.hpp"


class ManifestStream: public ResourceObj {
Expand Down Expand Up @@ -157,24 +158,34 @@ ClassPathSegment* SharedPathsMiscInfoExt::to_sorted_segments(const char* path, i
}


bool SharedPathsMiscInfoExt::check(jint type, const char* path) {

bool SharedPathsMiscInfoExt::check(jint type, const char* origin_path) {
switch (type) {
case APP:
{
int new_path_len = QuickStart::get_max_replaced_path_len(origin_path) + 1;
char* path = NEW_C_HEAP_ARRAY(char, new_path_len, mtInternal);
int origin_len = (int)strlen(origin_path);
if (TraceClassPaths || (TraceClassLoading && Verbose)) {
tty->print_cr("origin path len: %d; new len: %d", origin_len, new_path_len);
}
QuickStart::convert_path_by_env(origin_path, path);
ClassLoader::trace_class_path(tty, "new_path: ", path);
size_t len = strlen(path);
const char *appcp = Arguments::get_appclasspath();
assert(appcp != NULL, "NULL app classpath");
size_t appcp_len = strlen(appcp);
if (appcp_len < len) {
FREE_C_HEAP_ARRAY(char, path, mtInternal);
return fail("Run time APP classpath is shorter than the one at dump time: ", appcp);
}
// Prefix is OK: E.g., dump with -cp foo.jar, but run with -cp foo.jar:bar.jar
if (AppCDSVerifyClassPathOrder) {
if (strncmp(path, appcp, len) != 0) {
FREE_C_HEAP_ARRAY(char, path, mtInternal);
return fail("[APP classpath mismatch, actual: -Djava.class.path=", appcp);
}
if (appcp[len] != '\0' && appcp[len] != os::path_separator()[0]) {
FREE_C_HEAP_ARRAY(char, path, mtInternal);
return fail("Dump time APP classpath is not a proper prefix of run time APP classpath: ", appcp);
}
} else{
Expand All @@ -184,6 +195,7 @@ bool SharedPathsMiscInfoExt::check(jint type, const char* path) {
ClassPathSegment* app_cps = to_sorted_segments(appcp, app_seg_num);
ClassPathSegment* path_cps = to_sorted_segments(path, path_seg_num);
if (app_seg_num < path_seg_num) {
FREE_C_HEAP_ARRAY(char, path, mtInternal);
return fail("[APP classpath mismatch, actual: -Djava.class.path=", appcp);
} else {
int i = 0, j = 0;
Expand All @@ -203,14 +215,16 @@ bool SharedPathsMiscInfoExt::check(jint type, const char* path) {
}
}
if (i != path_seg_num) {
FREE_C_HEAP_ARRAY(char, path, mtInternal);
return fail("[APP classpath mismatch, actual: -Djava.class.path=", appcp);
}
}
}
FREE_C_HEAP_ARRAY(char, path, mtInternal);
}
break;
default:
return SharedPathsMiscInfo::check(type, path);
return SharedPathsMiscInfo::check(type, origin_path);
}

return true;
Expand Down
13 changes: 12 additions & 1 deletion hotspot/src/share/vm/memory/filemap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "oops/objArrayOop.hpp"
#include "runtime/arguments.hpp"
#include "runtime/java.hpp"
#include "runtime/quickStart.hpp"
#include "runtime/os.hpp"
#include "services/memTracker.hpp"
#include "utilities/defaultStream.hpp"
Expand Down Expand Up @@ -264,6 +265,14 @@ bool FileMapInfo::validate_classpath_entry_table() {
SharedClassPathEntry* ent = shared_classpath(i);
struct stat st;
const char* name = ent->_name;
bool replaced = false;
char* new_ent_name = QuickStart::replace_if_contains(name, &replaced);
if (replaced) {
if (TraceClassPaths || (TraceClassLoading && Verbose)) {
tty->print_cr("replace %s with %s", name, new_ent_name);
}
name = new_ent_name;
}
bool ok = true;
if (TraceClassPaths || (TraceClassLoading && Verbose)) {
tty->print_cr("[Checking shared classpath entry: %s]", name);
Expand All @@ -277,7 +286,7 @@ bool FileMapInfo::validate_classpath_entry_table() {
ok = false;
}
} else {
if (ent->_timestamp != st.st_mtime ||
if ((ent->_timestamp != st.st_mtime && !CDSIgnoreFileTimeCheck) ||
ent->_filesize != st.st_size) {
ok = false;
if (PrintSharedArchiveAndExit) {
Expand All @@ -290,6 +299,7 @@ bool FileMapInfo::validate_classpath_entry_table() {
}
}
}
FREE_C_HEAP_ARRAY(char, new_ent_name, mtInternal);
if (ok) {
if (TraceClassPaths || (TraceClassLoading && Verbose)) {
tty->print_cr("[ok]");
Expand All @@ -302,6 +312,7 @@ bool FileMapInfo::validate_classpath_entry_table() {

_classpath_entry_table_size = _header->_classpath_entry_table_size;
_validating_classpath_entry_table = false;
QuickStart::free_envs_array();
return true;
}

Expand Down
2 changes: 2 additions & 0 deletions hotspot/src/share/vm/runtime/globals_ext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@
"Ignore non-empty dir check in AppCDS") \
product(bool, CDSIgnoreBootClasspathDiff, false, \
"keep AppCDS on after appending boot classpath") \
product(bool, CDSIgnoreFileTimeCheck, false, \
"do not check timestamp of jar file when using CDS") \
\
product(bool, SuppressAppCDSErrors, false, \
"Suppress AppCDS errors during initialization; use warnings instead") \
Expand Down
154 changes: 153 additions & 1 deletion hotspot/src/share/vm/runtime/quickStart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ int QuickStart::_jvm_option_count = 0;
const char** QuickStart::_jvm_options = NULL;
const char* QuickStart::_cp_in_metadata_file = NULL;

GrowableArray<const char *>* QuickStart::old_envs = NULL;
GrowableArray<const char *>* QuickStart::new_envs = NULL;
int QuickStart::_max_env_diff_len = 0;

const char* QuickStart::_opt_name[] = {
#define OPT_TAG(name) #name,
OPT_TAG_LIST
Expand Down Expand Up @@ -105,6 +109,8 @@ int QuickStart::_lock_file_fd = 0;
#define JVM_CONF_FILE "jvm_conf"
#define CDS_DIFF_CLASSES "cds_diff_classes.lst"

#define ENV_MARK "ENV."

QuickStart::~QuickStart() {
if (_cache_path) {
os::free(const_cast<char*>(_cache_path));
Expand Down Expand Up @@ -450,7 +456,6 @@ bool QuickStart::load_and_validate(const JavaVMInitArgs* options_args) {
_vm_version = VM_Version::internal_vm_info_string();

while (fgets(line, sizeof(line), _metadata_file) != NULL) {

if (!feature_checked && match_option(line, _identifier_name[Features], &tail)) {
check_features(tail);
feature_checked = true;
Expand Down Expand Up @@ -527,6 +532,43 @@ bool QuickStart::load_and_validate(const JavaVMInitArgs* options_args) {
trim_tail_newline(cp_buff);
_cp_in_metadata_file = os::strdup(cp_buff);
FREE_C_HEAP_ARRAY(char, cp_buff, mtInternal);
} else if (match_option(line, ENV_MARK, &tail)) {
if (old_envs == NULL) {
old_envs = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<const char *>(5, true);
new_envs = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<const char *>(5, true);
}
// remove line_separator in line
char* pos = strstr(line, os::line_separator());
if (pos != NULL) {
*pos = '\0';
}
if (tail[0] != '\0') {
int p1 = strlen(ENV_MARK);
int p2 = strchr(line, '=') - line;
// e.g. from ENV.PWD=Path get Path
int old_env_len = strlen(line) - p2 -1;
char* old_env = NEW_C_HEAP_ARRAY(char, old_env_len + 1, mtInternal); // char + \0
strncpy(old_env, &line[p2+1], old_env_len);
old_env[old_env_len] = '\0';
old_envs->append(old_env);
// e.g. from ENV.PWD=PATH get PWD
char* env_name = NEW_C_HEAP_ARRAY(char, p2 - p1 + 1, mtInternal);
strncpy(env_name, &line[p1], p2-p1);
env_name[p2-p1] = '\0';
char* new_env_val = ::getenv(env_name);
FREE_C_HEAP_ARRAY(char, env_name, mtInternal);
// copy new_env
int new_env_len = strlen(new_env_val);
char* new_env = NEW_C_HEAP_ARRAY(char, new_env_len + 1, mtInternal);
strncpy(new_env, new_env_val, new_env_len);
new_env[new_env_len] = '\0';
new_envs->append(new_env);
tty->print_cr("old_env: %s", old_env);
tty->print_cr("new_env: %s", new_env);
if (new_env_len - old_env_len > _max_env_diff_len) {
_max_env_diff_len = new_env_len - old_env_len;
}
}
}
}
return true;
Expand Down Expand Up @@ -1006,7 +1048,117 @@ bool QuickStart::dump_cached_info(const JavaVMInitArgs* options_args) {
_temp_metadata_file->print_cr("%s", option->optionString);
}

const char* envs = Arguments::get_property("com.alibaba.cds.cp.reloc.envs");
if (envs != NULL) {
char env_i[JVM_MAXPATHLEN];
int p1 = 0, p2 = 0;
bool envs_process_end = false;
while(!envs_process_end) {
if (envs[p2] == '\0') {
envs_process_end = true;
}
if((envs[p2] == ',' || envs[p2] == '\0')) {
strncpy(env_i, &envs[p1], p2-p1);
env_i[p2-p1] = '\0';
p1 = p2 + 1;
const char* env_i_value = ::getenv(env_i);
if(env_i_value != NULL) {
_temp_metadata_file->print_cr("%s%s=%s", ENV_MARK, env_i, env_i_value);
}
}
p2++;
}
}

_temp_metadata_file->flush();
return true;
}

void QuickStart::convert_path_by_env(const char* origin_path, char* new_path) {
if (old_envs == NULL) {
strcpy(new_path, origin_path);
tty->print_cr("Don't need to convert path according to environment variables.");
return;
}

int p1 = 0, p2 = p1;
bool process_end = false;
while (!process_end) {
if (origin_path[p2] == '\0') {
process_end = true;
}
if((origin_path[p2] == *os::path_separator() || origin_path[p2] == '\0')) {
char split[JVM_MAXPATHLEN];
strncpy(split, &origin_path[p1], p2-p1);
split[p2-p1] = '\0';
bool replaced = false;
char* path = replace_if_contains(split, &replaced);
strcat(new_path, path);
FREE_C_HEAP_ARRAY(char, path, mtInternal);
if (origin_path[p2] == *os::path_separator()) {
strcat(new_path, os::path_separator());
} else {
strcat(new_path, "\0");
}
p1 = p2 + 1;
}
p2++;
}
}

void QuickStart::free_envs_array() {
if(old_envs != NULL) {
int len = old_envs->length();
for (int i = 0; i < len; i++){
FREE_C_HEAP_ARRAY(char, old_envs->at(i), mtInternal);
FREE_C_HEAP_ARRAY(char, new_envs->at(i), mtInternal);
}
old_envs->clear();
new_envs->clear();
delete old_envs;
old_envs = NULL;
delete new_envs;
new_envs = NULL;
}
}

char* QuickStart::replace_if_contains(const char* path, bool* replaced) {
if(old_envs != NULL) {
int array_cnt = old_envs->length();
for(int i = 0; i < array_cnt; i++) {
const char* target = old_envs->at(i);
if(strstr(path, target) != NULL) {
int target_len = strlen(target);
const char* new_target = new_envs->at(i);
int new_target_len = strlen(new_target);
char* new_path = NEW_C_HEAP_ARRAY(char, new_target_len + strlen(path) - target_len + 1, mtInternal);
new_path[0] = '\0'; // clear the buffer
strcat(new_path, new_target);
strcat(new_path, &path[target_len]);
strcat(new_path, "\0");
*replaced = true;
return new_path;
}
}
}

char* new_path = NEW_C_HEAP_ARRAY(char, strlen(path) + 1, mtInternal);
strcpy(new_path, path);
*replaced = false;
return new_path;
}

int QuickStart::get_max_replaced_path_len(const char* origin_path) {
int origin_path_len = (int)strlen(origin_path);
if (_max_env_diff_len <= 0) return origin_path_len;
int count = 1;
int p = 0;
while(origin_path[p] != '\0') {
if (origin_path[p] == *os::path_separator()) {
count++;
}
p++;
}

return origin_path_len + (count * _max_env_diff_len);
}
10 changes: 10 additions & 0 deletions hotspot/src/share/vm/runtime/quickStart.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ class QuickStart : AllStatic {
static const char** _jvm_options;
static const char* _cp_in_metadata_file;

// old env, new env
static GrowableArray<const char *>* old_envs;
static GrowableArray<const char *>* new_envs;
static int _max_env_diff_len; // len(new_env) - len(old_env)

static bool set_optimization(const char* option, bool enabled);
static bool determine_role(const JavaVMInitArgs* options_args);
static bool prepare_dump(const JavaVMInitArgs* options_args);
Expand All @@ -111,6 +116,11 @@ class QuickStart : AllStatic {
static void set_opt_passed(opt feature);
static void notify_dump();

static void convert_path_by_env(const char* origin_path, char* new_path);
static void free_envs_array();
static char* replace_if_contains(const char* path, bool* replaced);
static int get_max_replaced_path_len(const char* origin_path);

// cds stuff
private:
static const char* _origin_class_list;
Expand Down
10 changes: 10 additions & 0 deletions jdk/test/com/alibaba/quickstart/TestEnvClass.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
public class TestEnvClass {
public static void main(String[] args) {
System.out.println("==TestEnvClass2==");
TestEnvClass testEnvClass = new TestEnvClass();
testEnvClass.printClassName2();
}
public void printClassName2() {
System.out.println("==TestEnvClass2==");
}
}
Loading

0 comments on commit 19b021c

Please sign in to comment.