Windows编译OpenJDK踩坑(持续更新).

编译

下载源码

AdoptOpenJDK/openjdk-jdk11u

jdk-updates

准备boot JDK

JDK release

C:\Program Files (x86)\Tencent\微信web开发者工具\dll

安装vs2017

  • 使用C++的桌面开发

  • 通用Windows平台开发

  • 语言包选择English

cygwin

安装必要软件包

<path to Cygwin setup>/setup-x86_64 -q -P autoconf -P make -P unzip -P zip

configu & compile

配置

cd path/openjdk17

bash configure --with-debug-level=slowdebug --with-jvm-variants=server --disable-zip-debug-info --disable-warnings-as-errors --with-boot-jdk=/mnt/d/Aproject/openjdk/

编译

make LOG=info

生成compile-database

make compile-commands

使用wsl编译

安装必要软件包

下载好jdk源码后,解压到d:\openjdk\jdk11u

打开wsl shell,进入jdk源码目录

cd /mnt/d/openjdk/jdk11u

配置

bash configure --with-debug-level=slowdebug --with-jvm-variants=server --disable-zip-debug-info --disable-warnings-as-errors --with-boot-jdk=/mnt/d/Aproject/openjdk/

编译

make

生成 compile database

make compile-commands

编译特定的模块

make CONF=config java.base-lib

Clion Debug

Run/Debug Configurations > new Custom Build Application

name: jdk11-spring-boot-debug

Executeble:build\jdk\bin\java.exe

Program arguments:

-Xdebug -XX:+UseSerialGC -Xrunjdwp:server=y,transport=dt_socket,address=8099,suspend=n -Xlog:gc -Xms1000M -Xmn1000M -XX:-DoEscapeAnalysis -XX:+UseTLAB -XX:-ResizeTLAB -jar "D:\AProject\edian\backend\etc\api\certification-service\target\cs1.jar" -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintCompilation -XX:+PrintInlining -XX:CompileCommand=quiet

Environment variables:

_JAVA_LAUNCHER_DEBUG =1 

ref code: src/java.base/share/native/launcher/main.c:146

#ifdef _WIN32
{
int i = 0;
if (getenv(JLDEBUG_ENV_ENTRY) != NULL) {
printf("Windows original main args:\n");
for (i = 0 ; i < __argc ; i++) {
printf("wwwd_args[%d] = %s\n", i, __argv[i]);
}
}
}

问题

assert(strcmp(value, system_value) == 0) failed: property value mustn’t differ from System.getProperty

出现这个问题,通常日志如下

# To suppress the following error report, specify this argument
# after -XX: or in .hotspotrc: SuppressErrorAt=\statSampler.cpp:204
#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (d:\aproject\openjdk\jdk17\src\hotspot\share\runtime\statSampler.cpp:204), pid=4488, tid=21828
# assert(strcmp(value, system_value) == 0) failed: property value mustn't differ from System.getProperty
#
# JRE version: OpenJDK Runtime Environment (17.0) (slowdebug build 17-internal+0-adhoc.edian.jdk17)
# Java VM: OpenJDK 64-Bit Server VM (slowdebug 17-internal+0-adhoc.edian.jdk17, mixed mode, tiered, compressed oops, com
pressed class ptrs, g1 gc, windows-amd64)
# Core dump will be written. Default location: D:\AProject\openjdk\jdk17\build\windows-x86_64-server-slowdebug\jdk\bin\h
s_err_pid4488.mdmp

该问题是由于在bash configure阶段,启用了调试,如--with-debug-level=*debug (*指的是 slow,fast)
相关定义可在如下文件找到

make/hotspot/lib/JvmFlags.gmk:81


ifeq ($(DEBUG_LEVEL), release)
# For hotspot, release builds differ internally between "optimized" and "product"
# in that "optimize" does not define PRODUCT.
ifneq ($(HOTSPOT_DEBUG_LEVEL), optimized)
JVM_CFLAGS_DEBUGLEVEL := -DPRODUCT
endif
else ifeq ($(DEBUG_LEVEL), fastdebug)
JVM_CFLAGS_DEBUGLEVEL := -DASSERT
ifeq ($(call isTargetOs, windows aix), false)
# NOTE: Old build did not define CHECK_UNHANDLED_OOPS on Windows and AIX.
JVM_CFLAGS_DEBUGLEVEL += -DCHECK_UNHANDLED_OOPS
endif
else ifeq ($(DEBUG_LEVEL), slowdebug)
# _NMT_NOINLINE_ informs NMT that no inlining is done by the compiler
JVM_CFLAGS_DEBUGLEVEL := -DASSERT -D_NMT_NOINLINE_
endif

定位到相关源码则是

statSampler.cpp:180

void StatSampler::assert_system_property(const char* name, const char* value, TRAPS) {
#ifdef ASSERT
ResourceMark rm(THREAD);

// setup the arguments to getProperty
Handle key_str = java_lang_String::create_from_str(name, CHECK);

// return value
JavaValue result(T_OBJECT);

// public static String getProperty(String key, String def);
JavaCalls::call_static(&result,
vmClasses::System_klass(),
vmSymbols::getProperty_name(),
vmSymbols::string_string_signature(),
key_str,
CHECK);

oop value_oop = result.get_oop();
assert(value_oop != NULL, "property must have a value");

// convert Java String to utf8 string
char* system_value = java_lang_String::as_utf8_string(value_oop);
printf("\tA system value: %s -> \n%s\n\n",name,system_value);
assert(strcmp(value, system_value) == 0, "property value mustn't differ from System.getProperty");
#endif // ASSERT
}

当比较到valuesystem_value不相等时,会抛出异常,

通过断点调试

此时的char* name值为java.library.path

是由于环境变量PATH中包含了带有中文的路径,导致system_value转换为utf8时,中文字符转换为乱码,导致strcmp不相等,从而抛出异常。

解决办法:把PATH中的中文路径去掉,或者bash configure阶段不要启用调试

补充1

如果是Windows平台 此时value中存储的是GBK编码后的path值,value_oop存储的是UTF-16(LE)编码后的path值,经过

char* system_value = java_lang_String::as_utf8_string(value_oop); 后,system_value存储的是utf8编码后的path值,这个时候strcmp比较字符串的结果是不相等的(因为有中文)

补充2

假如path含有中文字符 eg:c:\Hello\Abc-ZH-中文-Abc;

则有如下

字符 GBK UTF16 UTF8
D6D0 4E2D E4B8AD
CEC4 6587 E69687

value内存中的值(GBK编码)

6d 33 32 3b   43 3a 5c 57   49 4e 44 4f   57 53 3b 63   │ m32;C:\WINDOWS;c │
3a 5c 48 65 6c 6c 6f 5c 41 62 63 2d 5a 48 2d d6 │ :\Hello\Abc-ZH-· │
d0 ce c4 2d 41 62 63 3b 43 3a 5c 47 6e 75 57 69 │ ···-Abc;C:\GnuWi │

value_oop 在内存中的值(因为使用UTF16LE编码,所以字节序和大端模式相反)

57 00 53 00   3b 00 63 00   3a 00 5c 00   48 00 65 00   │ W·S·;·c·:·\·H·e· │
6c 00 6c 00 6f 00 5c 00 41 00 62 00 63 00 2d 00 │ l·l·o·\·A·b·c·-· │
5a 00 48 00 2d 00 2d 4e 87 65 2d 00 41 00 62 00 │ Z·H·-·-N·e-·A·b· │
63 00 3b 00 43 00 3a 00 5c 00 47 00 6e 00 75 00 │ c·;·C·:·\·G·n·u· │
57 00 69 00 6e 00 33 00 32 00 5c 00 62 00 69 00 │ W·i·n·3·2·\·b·i· │

system_value在内存中的值(UTF8编码)

6d 33 32 3b   43 3a 5c 57   49 4e 44 4f   57 53 3b 63   │ m32;C:\WINDOWS;c │
3a 5c 48 65 6c 6c 6f 5c 41 62 63 2d 5a 48 2d e4 │ :\Hello\Abc-ZH-· │
b8 ad e6 96 87 2d 41 62 63 3b 43 3a 5c 47 6e 75 │ ·····-Abc;C:\Gnu │

补充3

utf8 最小代码单元是一个字节

utf16 的最小代码单元是2个字节

complication-database.json 导入clion无法正确编译

使用make compile-commands 生成 compilation-database.json

将其导入到clion中,出现编译错误,原因是因为json中的编译命令路径问题,需要把
\\\\\\ 替换为/,然后重新导入即可。

arguments.cpp 969 解析+-参数选项
1162 解析JVM -XX参数

ref

  1. jvm char*在内存中的存储方式为小端模式

  2. 查看字符编码

Unicode和UTF编码转换

  1. jstring 转char*
    jstring value_oop;
    java_lang_String::as_utf8_string(value_oop)

Can not determine compiler

Cannot determine compiler type by executable file: 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\bin\Hostx86\x64\ml64.exe'

Clion plugins

cidr-base-plugin.jar

定义了各种编译器匹配实现,以及编译器调用逻辑

OCCompilerResolver 编译器解析工具

OCBuiltInCompilerResolver 默认实现类

主要匹配以下编译器


private static final Map<String, OCCompilerKind> compilerCache = MapsKt.mapOf(
new Pair[]{
TuplesKt.to("cl", MSVCCompilerKind.INSTANCE),
TuplesKt.to("gcc", GCCCompilerKind.INSTANCE),
TuplesKt.to("g++", GCCCompilerKind.INSTANCE),
TuplesKt.to("xgcc", GCCCompilerKind.INSTANCE),
TuplesKt.to("xg++", GCCCompilerKind.INSTANCE),
TuplesKt.to("colorgcc", GCCCompilerKind.INSTANCE),
TuplesKt.to("cc", GCCCompilerKind.INSTANCE),
TuplesKt.to("c++", GCCCompilerKind.INSTANCE),
TuplesKt.to("cpp", GCCCompilerKind.INSTANCE),
TuplesKt.to("clang", ClangCompilerKind.INSTANCE),
TuplesKt.to("clang++", ClangCompilerKind.INSTANCE),
TuplesKt.to("emcc", ClangCompilerKind.INSTANCE),
TuplesKt.to("em++", ClangCompilerKind.INSTANCE),
TuplesKt.to("tiarmclang", ClangCompilerKind.INSTANCE),
TuplesKt.to("nvcc", NVCCCompilerKind.INSTANCE),
TuplesKt.to(OCBuiltInCompilerKindProvider.CLANG_CL_ID, ClangClCompilerKind.INSTANCE),
TuplesKt.to("icl", MSVCCompilerKind.INSTANCE),
TuplesKt.to("icc", GCCCompilerKind.INSTANCE),
TuplesKt.to("icpc", GCCCompilerKind.INSTANCE),
TuplesKt.to("dpcpp-cl", ClangClCompilerKind.INSTANCE)
}
);

clion-compdb.jar

主要用于解析compile-commands.json

结构如下

[
{ "directory": "/home/user/llvm/build",
"arguments": ["/usr/bin/clang++", "-Irelative", "-DSOMEDEF=With spaces, quotes and \\-es.", "-c", "-o", "file.o", "file.cc"],
"file": "file.cc" },

{ "directory": "/home/user/llvm/build",
"command": "/usr/bin/clang++ -Irelative -DSOMEDEF=\"With spaces, quotes and \\-es.\" -c -o file.o file.cc",
"file": "file2.cc" },

...
]

当通过Clion打开compile-commands.json后,会进行Sync操作,触发CompDBProjectResolver#resolveProjectInfo

通过遍历compile-commands.json中的command值,校验command是否符合 cidr-base-plugin.jar中定义的编译器。

如果command中的命令不符合上述的编译器,那么会出现 Cannot determine compiler type by executable file: '/path/xxx' 这个错误,导致导入失败

private final void reportUnresolvedCompilers(ExternalSystemTaskId resolveTaskId, Set<? extends File> set, ExternalSystemTaskNotificationListener listener, String parentEventId) {
if (set.isEmpty()) {
return;
}
for (File compiler : set) {
String message = CompDBBundle.message("project.resolver.cannot.resolve.compiler.error", compiler);
Intrinsics.checkNotNullExpressionValue(message, "CompDBBundle.message(\"pr…ompiler.error\", compiler)");
listener.onTaskOutput(resolveTaskId, message + "\n", false);
CompDBLog.INSTANCE.getLOG().debug(message);
String compilerEventId = "unresolved-compiler:" + UUID.randomUUID();
OperationDescriptor taskOperationDescriptorImpl = new TaskOperationDescriptorImpl(message, System.currentTimeMillis(), "unresolved-compiler");
ExternalSystemProgressEvent externalSystemStartEventImpl = new ExternalSystemStartEventImpl(compilerEventId, parentEventId, taskOperationDescriptorImpl);
ExternalSystemProgressEvent externalSystemFinishEventImpl = new ExternalSystemFinishEventImpl(compilerEventId, parentEventId, taskOperationDescriptorImpl, new FailureResultImpl(externalSystemStartEventImpl.getEventTime(), externalSystemStartEventImpl.getEventTime(), CollectionsKt.listOf(new FailureImpl(message, message, CollectionsKt.emptyList()))));
listener.onStatusChange(new ExternalSystemTaskExecutionEvent(resolveTaskId, externalSystemStartEventImpl));
listener.onStatusChange(new ExternalSystemTaskExecutionEvent(resolveTaskId, externalSystemFinishEventImpl));
}
}

解决方法

找到compile_commands.json 把所有含ml64.exe 相关的commands删掉(注意删掉的是一个整体)

样例

[{ "directory": "d:/aproject/openjdk/jdk17/make", "file": "d:/aproject/openjdk/jdk17/src/jdk.incubator.vector/windows/native/libsvml/svml_d_acos_windows_x86.S", "command": "c:/progra~2/micros~2/2017/commun~1/vc/tools/msvc/1416~1.270/bin/hostx86/x64/ml64.exe -nologo -c -Fod:/aproject/openjdk/jdk17/build/windows-x86_64-server-slowdebug/support/native/jdk.incubator.vector/libsvml/svml_d_acos_windows_x86.obj d:/aproject/openjdk/jdk17/src/jdk.incubator.vector/windows/native/libsvml/svml_d_acos_windows_x86.S" }]

再重新导入一下即可

Menu > Tools > Compilation Database > Reload Compilation Database Project

reference

Tips & Tricks: Develop OpenJDK in CLion with Pleasure

GC垃圾回收器 调整参数

不同版本 OpenJDK 源码调试方案

WSL

在Ubuntu中编译和调试OpenJDK

编译 openJDK

Ubuntu下使用rpm

windows 平台编译openjdk12

tar教程

OpenJDK 编译指南(Ubuntu 16.04 + MacOS 10.15)

Cygwin

OpenJDK 编译指南(Ubuntu 16.04 + MacOS 10.15)