Dalvik vm make file config and source tree

Dalvik 虚拟机编译脚本 和 源代码树


本文简单的分析Dalvik虚拟机源码(dalvik/vm)的结构以及编译脚本(*.mk)

编译脚本

Dalvik源码目录结构并不复杂。其编译脚本也很简单。主要有以下几个文件组成:

dalvik/vm/Android.mk 

dalvik/vm/ReconfigureDvm.mk

dalvik/vm/Dvm.mk


和android系统里其他的模块类似,dalvik也是以Android.mk作为顶层编译配置文件或者入口。它的内容如下所示:

https://github.com/android/platform_dalvik/blob/master/vm/Android.mk


我们从头分析一下。首次按是注释说明:

#
# Android.mk for Dalvik VM.
#
# This makefile builds both for host and target, and so the very large
# swath of common definitions are factored out into a separate file to
# minimize duplication.
#
# If you enable or disable optional features here (or in Dvm.mk),
# rebuild the VM with:
#
#  make clean-libdvm clean-libdvm_assert clean-libdvm_sv clean-libdvm_interp
#  make -j4 libdvm
#

他告诉我们,该编译文件把dalvik编程成两部分,宿主机和目标机。对多数典型的配置下,宿主机就是我们的Linux编译服务器。而目标机就是我们的移动设备。 为了减少重复,宿主机和目标机都需要的编译配置被放到单独的文件里。单独的文件就是后面要讲道德(ReconfigureDvm.mk和Dvm.mk)。

同时,它也该出了在我们修改了dalvik源码后,怎么从头编译一个干净的实现出来。后面我们会动手实践。

LOCAL_PATH:= $(call my-dir)
和多数模块一样,接下来把dalvik源码路径赋值给LOCAL_PATH变量。以方便后面使用。这里LOCAL_PATH应该就是 dalvik/vm.

#
# Build for the target (device).
#

ifeq ($(TARGET_CPU_SMP),true)
    target_smp_flag := -DANDROID_SMP=1
else
    target_smp_flag := -DANDROID_SMP=0
endif
host_smp_flag := -DANDROID_SMP=1

# Build the installed version (libdvm.so) first
include $(LOCAL_PATH)/ReconfigureDvm.mk

# Overwrite default settings
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libdvm
LOCAL_CFLAGS += $(target_smp_flag)
include $(BUILD_SHARED_LIBRARY)
首先为目标机编译。首先是根据目标机是否支持SMP,设置变量target_smp_flag。后面我们会看到该变量被用来传给编译器选项。而宿主机上现在绝大多数都是支持SMP的,所以就直接复制是指为支持。

紧接着就调用脚本ReconfigureDvm.mk,从名字我们不难猜出改脚本是用来为dvm编译初始化编译环境的。后面我们会看到它的主要内容。

下面接着就是现实dvm在宿主机上最终会被编译的目标了。这是一个共享库。名字叫libdvm。我们可以在编译好的机器上找到他

out/target/xxx/libs/libdvm.so

# If WITH_JIT is configured, build multiple versions of libdvm.so to facilitate
# correctness/performance bugs triage
ifeq ($(WITH_JIT),true)

    # Derivation #1
    # Enable assert and JIT tuning
    include $(LOCAL_PATH)/ReconfigureDvm.mk

    # Enable assertions and JIT-tuning
    LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \
                    -DWITH_JIT_TUNING $(target_smp_flag)
    LOCAL_MODULE := libdvm_assert
    include $(BUILD_SHARED_LIBRARY)

    # Derivation #2
    # Enable assert and self-verification
    include $(LOCAL_PATH)/ReconfigureDvm.mk

    # Enable assertions and JIT self-verification
    LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \
                    -DWITH_SELF_VERIFICATION $(target_smp_flag)
    LOCAL_MODULE := libdvm_sv
    include $(BUILD_SHARED_LIBRARY)

    # Derivation #3
    # Compile out the JIT
    WITH_JIT := false
    include $(LOCAL_PATH)/ReconfigureDvm.mk

    LOCAL_CFLAGS += $(target_smp_flag)
    LOCAL_MODULE := libdvm_interp
    include $(BUILD_SHARED_LIBRARY)

endif
这一部分是为JIT特有 的。当系统支持JIT编译器是,dvm会编译三个额外的目标共享库,用于支持dvm的开发特性。他们分别是

libdvm_assert 用来打开dvm源码中的断言(assert)。我们在后面的分析文章中会看到,dvm实现中大量使用断言来增加运行时的检查。当打开这些断言是,任何断言检查失败都将直接导致dvm异常退出。我们从trace file就能够找到那个断言失败,从而检查出可能存在的bug。在最终发布版本中,断言会被关闭,哪些断言相当于空语句。因而不会再最终发布版本中是dvm异常退出。

libdvm_sv 除了代开断言外,还带开了自我检查能力,用来在某些重要时刻检查当前dvm状态没有异常。我们在后面的文章里会详细分析dvm到底做了哪些自我检查。

最后一个版本 libdvm_interp 用来关掉JIT,而编译出一个纯解释器实现的dvm实例。这样子我们就可以检查打开JIT后行为有没有出现和解释器实现的dvm有行为差异。

我们可以在如下目录里找到这三个为调试生成的dvm共享库:

out/target/xxx/symbol/libs/


最后的内容是用来为宿主机编译的。所有的内容包裹在如下代码里:

#
# Build for the host.
#

ifeq ($(WITH_HOST_DALVIK),true)

...
endif

首先是清空本地变量内容:

    include $(CLEAR_VARS)

然后是根据编译环境设置三个编译变量:

    # Variables used in the included Dvm.mk.
    dvm_os := $(HOST_OS)
    dvm_arch := $(HOST_ARCH)
    # Note: HOST_ARCH_VARIANT isn't defined.
    dvm_arch_variant := $(HOST_ARCH)

这三个变量最终会传给编译器。它们会引入平台特有的行为。 由于宿主机不必支持JIT,所以将它设置为false。

WITH_JIT := false
include $(LOCAL_PATH)/Dvm.mk
Dvm.mk我们后面会接着分析。

接下来根据目标平台,有选择性的引入编译需要的库:

    LOCAL_SHARED_LIBRARIES += libcrypto libssl libicuuc libicui18n

    LOCAL_LDLIBS := -lpthread -ldl
    ifeq ($(HOST_OS),linux)
      # need this for clock_gettime() in profiling
      LOCAL_LDLIBS += -lrt
    endif

    # Build as a WHOLE static library so dependencies are available at link
    # time. When building this target as a regular static library, certain
    # dependencies like expat are not found by the linker.
    LOCAL_WHOLE_STATIC_LIBRARIES += libexpat libcutils libdex liblog libnativehelper libz

    # The libffi from the source tree should never be used by host builds.
    # The recommendation is that host builds should always either
    # have sufficient custom code so that libffi isn't needed at all,
    # or they should use the platform's provided libffi. So, if the common
    # build rules decided to include it, axe it back out here.
    ifneq (,$(findstring libffi,$(LOCAL_SHARED_LIBRARIES)))
        LOCAL_SHARED_LIBRARIES := \
            $(patsubst libffi, ,$(LOCAL_SHARED_LIBRARIES))
    endif

最后告诉编译系统,dvm在宿主机上的编译结果有两个:

LOCAL_CFLAGS += $(host_smp_flag)
    LOCAL_MODULE_TAGS := optional
    LOCAL_MODULE := libdvm

    include $(BUILD_HOST_SHARED_LIBRARY)

    # Copy the dalvik shell script to the host's bin directory
    include $(CLEAR_VARS)
    LOCAL_IS_HOST_MODULE := true
    LOCAL_MODULE_TAGS := optional
    LOCAL_MODULE_CLASS := EXECUTABLES
    LOCAL_MODULE := dalvik
    include $(BUILD_SYSTEM)/base_rules.mk
$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/dalvik | $(ACP)
	@echo "Copy: $(PRIVATE_MODULE) ($@)"
	$(copy-file-to-new-target)
	$(hide) chmod 755 $@

它们分别是 dvm共享库libdvm.so 和可执行文件dalvikvm。我们编译后的目录里能找到它们:

out/host/xxx/libs/libdvm.so

out/host/xxx/bin/dalvikvm


前面我们看到,dvm在目标机上编译的结果可能有好几个,最终的共享库libdvm.so,以及几个开发使用的共享库libsdvm_xx.so。编译这些目标是我们需要首先清理当前的编译环境,以排除编译前一个目标多带来的副作用。这个工作单独放到编译脚本ReconfigureDvm.mk,以减少重复。ReconfigureDvm.mk内容如下:


include $(CLEAR_VARS)

# Variables used in the included Dvm.mk.
dvm_os := $(TARGET_OS)
dvm_arch := $(TARGET_ARCH)
dvm_arch_variant := $(TARGET_ARCH_VARIANT)

# for now, disable x86-atom variant
ifeq ($(dvm_arch_variant),x86-atom)
dvm_arch_variant := x86
endif

include $(LOCAL_PATH)/Dvm.mk

LOCAL_SHARED_LIBRARIES += liblog libcutils libnativehelper libz libdl

LOCAL_STATIC_LIBRARIES += libdex

LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
LOCAL_SHARED_LIBRARIES += libstlport

# Don't install on any build by default
LOCAL_MODULE_TAGS := optional

它的内容也不出意外。清除本地变量,设置dvm变量,引入dvm需要的库。最后设置LOCAL_MODULE_TAGS为optional来告诉编译系统除非显式指明(include dalvik/vm/android.mk),否则不会编译dvm。


最后一个文件dvm.mk设置了宿主机和目标机都需要的定义。其内容也很直观。完整的内容如下:

https://github.com/android/platform_dalvik/blob/master/vm/Dvm.mk

首先是设置编译器选项:

#
# Compiler defines.
#
LOCAL_CFLAGS += -fstrict-aliasing -Wstrict-aliasing=2 -fno-align-jumps
LOCAL_CFLAGS += -Wall -Wextra -Wno-unused-parameter
LOCAL_CFLAGS += -DARCH_VARIANT=\"$(dvm_arch_variant)\"

接着判断编译时有没有指定DEBUG_DALVIK_VM:

#
# Optional features.  These may impact the size or performance of the VM.
#

# Make a debugging version when building the simulator (if not told
# otherwise) and when explicitly asked.
dvm_make_debug_vm := false
ifneq ($(strip $(DEBUG_DALVIK_VM)),)
  dvm_make_debug_vm := $(DEBUG_DALVIK_VM)
endif

如果指定了该选项,则打开额外的编译选项,否则什么都不多做:

ifeq ($(dvm_make_debug_vm),true)
  #
  # "Debug" profile:
  # - debugger enabled
  # - profiling enabled
  # - tracked-reference verification enabled
  # - allocation limits enabled
  # - GDB helpers enabled
  # - LOGV
  # - assert()
  #
  LOCAL_CFLAGS += -DWITH_INSTR_CHECKS
  LOCAL_CFLAGS += -DWITH_EXTRA_OBJECT_VALIDATION
  LOCAL_CFLAGS += -DWITH_TRACKREF_CHECKS
  LOCAL_CFLAGS += -DWITH_EXTRA_GC_CHECKS=1
  #LOCAL_CFLAGS += -DCHECK_MUTEX
  LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=3
  # add some extra stuff to make it easier to examine with GDB
  LOCAL_CFLAGS += -DEASY_GDB
  # overall config may be for a "release" build, so reconfigure these
  LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT
else  # !dvm_make_debug_vm
  #
  # "Performance" profile:
  # - all development features disabled
  # - compiler optimizations enabled (redundant for "release" builds)
  # - (debugging and profiling still enabled)
  #
  #LOCAL_CFLAGS += -DNDEBUG -DLOG_NDEBUG=1
  # "-O2" is redundant for device (release) but useful for sim (debug)
  #LOCAL_CFLAGS += -O2 -Winline
  #LOCAL_CFLAGS += -DWITH_EXTRA_OBJECT_VALIDATION
  LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=1
  # if you want to try with assertions on the device, add:
  #LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT
endif  # !dvm_make_debug_vm

紧接着是该文件的大头,引入编译源文件。其中包括 所有平台都会使用到的源文件,如 AllocTracker.cpp等。为支持JIT特有的文件,如compiler/Compiler.cpp等。其中以下部分特别需要我们关注:

ifeq ($(WITH_COPYING_GC),true)
  LOCAL_CFLAGS += -DWITH_COPYING_GC
  LOCAL_SRC_FILES += \
	alloc/Copying.cpp.arm
else
  LOCAL_SRC_FILES += \
	alloc/HeapSource.cpp \
	alloc/MarkSweep.cpp.arm
endif

这是在指定垃圾收集器的类型。当在编译选项中指定WITH_COPYING_GC后就使用拷贝垃圾收集器,否则就是用标记-清除垃圾收集器。我们后面会在单独的章节里介绍dvm垃圾收集。

至此编译脚本的解析全部完成。dvm源代码结构简单,其编译脚本也很清晰。


源码目录树

下面是dvm源码目录树。后面章节会分别介绍其中最重要的部分:

https://github.com/android/platform_dalvik/tree/master/vm


你可能感兴趣的:(tree,File,Module,Build,include,profiling)