顯示具有 Dalvik 標籤的文章。 顯示所有文章
顯示具有 Dalvik 標籤的文章。 顯示所有文章

2010年10月20日 星期三

Dalvik Interpreter (2)

After pushing the to-be-executed method frame on the top of stack thread, Dalvik will call the main interpreter to start the interpreting.
-dvmInterpret(Thread* self, const Method* method, JValue* pResult)

1. dvmInterpret()

-arguments:

1: Thread* self --> current thread
2: const Method* method --> current to-be-executed method
3: the returned value

-function work flow

1. Initialize working state
-source
interpState.method = method;
interpState.fp = (u4*) self->curFrame;
interpState.pc = method->insns;
interpState.entryPoint = kInterpEntryInstr;


if (dvmDebuggerOrProfilerActive())
    interpState.nextMode = INTERP_DBG;
else
    interpState.nextMode = INTERP_STD;


2. Determine execution mode. Typically, Dalvik has two modes of 
    interpreter to choose (C or Assembly for target platform).
-source
typedef bool (*Interpreter)(Thread*, InterpState*);
Interpreter stdInterp;
if (gDvm.executionMode == kExecutionModeInterpFast)
    stdInterp = dvmMterpStd;
#if defined(WITH_JIT)

else if (gDvm.executionMode == kExecutionModeJit)
    stdInterp = dvmMterpStd;
#endif
else
    stdInterp = dvmInterpretStd;


3. Call the real interpreter with the thread and interpState parameters
change = true;
while (change) {
    switch (interpState.nextMode) {
        case INTERP_STD:

            change = (*stdInterp)(self, &interpState);
            break;
#if defined(WITH_PROFILER) || defined(WITH_DEBUGGER) || defined(WITH_JIT)
        case INTERP_DBG:
            change = dvmInterpretDbg(self, &interpState);
            break;
#endif
        default:
            dvmAbort();

    }
}

2010年10月12日 星期二

Thread's Stack Management in Dalvik VM

Thread Setup in Dalvik:
1. dvmThreadStartup() (vm/Thread.c)
   -called by dvmStartup()
   -This function will setup the thread list and the main
    thread's environment
   -important codes
    thread = allocThread(gDvm.stackSize);

2. allocThread(int interpStackSize)
   -Default stack size per stack: 3 * 4k pages
   -Important codes
    stackBottom = (u1*) malloc(interpStackSize);
     thread->interpStackSize = interpStackSize;
     thread->interpStackStart = stackBottom + interpStackSize;
     thread->interpStackEnd = stackBottom + 
     STACK_OVERFLOW_RESERVE;(768 bytes)
   -The initialized stack in a thread

   
Stack Push/Pop in Dalvik:
1. At first, dalvik vm main function will invoke   
    dvmCallMethodV() to interpret the java main
    method
2. dvmCallMethodV() will invoke callPrep to push the
    stack frame.
   -callPrep will invoke dvmPushInterpFrame() to push
    the related method frame on the thread's stack.
   -The Dalvik vm thread stack frame

2010年9月9日 星期四

Dalvik Interpreter (1)

-Let's talk about how Dalvik generate interpreter for different platforms. All interpreter related files exist in dalvik/vm/mterp

1.
There exists a config file for every specific platform, e.g. config-armv5te or config-portstd. The config-portstd is a portable config file for all platforms.

2.
Every config file consists of the information needed by the gen-mterp.py. The gen-mterp.py is a python module which is used to read the platform-specific config file and generate the platform-specific interpreter.

3.
Following are currently supported platforms.
portstd
portdbg
allstubs
armv4t
armv5te
armv5te-vfp
armv7-a
x86

Every platform has its own .c and .S interpreter InterpC-$(TARGET_ARCH_EXT).c and InterpAsm-$(TARGET_ARCH_EXT).S. However, only .S or .c files has the interpreter entry point i.e. only one file is used to do interpreting.

The architecture-specific config files determine what goes into two generated out files (InterpC-$(TARGET_ARCH_EXT).c, InterpAsm-$(TARGET_ARCH_EXT).S).  The goal is to make it easy to swap C and assembly sources during initial development and testing, and to provide a way to use architecture-specific versions of some operations (e.g. making use of PLD instructions on ARMv6 or avoiding CLZ on ARMv4T).

(Config file example 1) - config-portstd

# C file header and basic definitions
import c/header.c

# simple def to specify the "standard" interp
import portable/portstd.c

# C pre-processor defines for stub C instructions
import portable/stubdefs.c

# common defs for the C opcodes
import c/opcommon.c

# entry point
# This entry point will tell the Dalvik VM where to enter the interpreter
import portable/entry.c --> InterpC-portstd is the main interpreter file

# opcode list; argument to op-start is default directory
op-start c
    # concatenate all C implementations
op-end

# "helper" code
import c/gotoTargets.c

# finish
import portable/enddefs.c

(Config file example 2) - config-armv5te
handler-size 64

# source for the instruction table stub
asm-stub armv5te/stub.S

# file header and basic definitions
import c/header.c                   --> This file will be paste in .c
import armv5te/header.S       --> This file will be paste in .S

# C pre-processor defines for stub C instructions
import cstubs/stubdefs.c        --> This file will be paste in .c

# highly-platform-specific defs
import armv5te/platform.S    --> This file will be paste in .S

# common defs for the C helpers; include this before the instruction handlers
import c/opcommon.c            --> This file will be paste in .c

# arch-specific entry point to interpreter
# This entry point will tell the Dalvik VM where to enter the interpreter
import armv5te/entry.S          --> InterpAsm-armv5te.S is the main interpreter file

# opcode list; argument to op-start is default directory
op-start armv5te
    #op OP_FILL_ARRAY_DATA c
op-end

# "helper" code for C; include if you use any of the C stubs (this generates
# object code, so it's normally excluded)
##import c/gotoTargets.c

# end of defs; include this when cstubs/stubdefs.c is included
import cstubs/enddefs.c

# common subroutines for asm
import armv5te/footer.S
import armv5te/debug.c

2010年8月18日 星期三

Dalvik Interpreting Workflow (1)

How Dalvik VM execute the .dex file

dalvikvm/Main.c

1. Jni fuction: JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)

JNI_CreateJavaVM will be called to construct a Dalvik virtual machine. JNI_CreateJavaVM is a function defined in Jni.h. The spec of Jni.h can be found on java site [1]. The JNI_CreateJavaVM will create everything needed to execute the .dex file, such as dynamic memory management, thread, bytecode verifier, etc. Then JavaVM and JNIEnv arguments will become the function interfaces to provide supported functions.

2. Jni function: FindClass(JNIEnv* env, const char* name)

This function will be called to find the class by name. Before executing a main method in java program, its class is needed to be found by a specified name. 

3. Jni function: GetStaticMethodID(JNIEnv* env, jclass jclazz,
    const char* name, const char* sig)

Because the java main function must be a static method, this jni function will be called to get the main method ID.

4. Jni function: CallStaticVoidMethod(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)

This function will start to interprete the .dex file. This function will call the Dalvik interpreter to interprete the .dex file. However, we can't find a direct definition of the function in the directory because the developer of Dalvik VM used a trick to define this function. Things will be explained in the next.

References:

2010年7月20日 星期二

Dalvik Memory Management (1)

Dalvik vm heap construction

Introduction:
The Dalvik vm has its own heap to allocate/de-allocate memory spaces for the newly created java classes. However, the posix doesn't provide interfaces to malloc/free memory spaces within an already set memory region. Therefore, the Dalvik vm uses the doug lea allocator (dlmalloc) to help it. The dlmalloc provide APIs to create a memory region and manage it. By using the dlmalloc, Dalvik vm has a heap to serve the dynamic memory allocation in Java.

1. At the construction step of Dalvik vm, callee: dvmHeapSourceStartup will be called
- file
-- dalvik/vm/alloc/HeapSource.c
- parameter
-- size_t startSize
= gDvm.heapSizeStart = 2 * 1024 * 1024 = 2MB
-- size_t absoluteMaxSize
= gDvm.heapSizeMax = 16 * 1024 * 1024 = 16MB
- return value
-- GcHeap *

2. Then callee: createMspace will be called by caller: dvmHeapSourceStartup
- file
-- dalvik/vm/alloc/HeapSource.c

- parameter
-- size_t startSize
= gDvm.heapSizeStart = 2 * 1024 * 1024 = 2MB
-- size_t absoluteMaxSize
= gDvm.heapSizeMax = 16 * 1024 * 1024 = 16MB

- return value
-- static mspace *

- This function will do
Make the name of the need-to-create heap, and then call the API to create a contiguous memory space as the heap.

3. Then callee: create_contiguous_mspace_with_name will be called by caller: createMspace
- file
-- system/core/libcutils/mspace.c
- parameter
-- size_t starting_capacity
= startSize / 2 = gDvm.heapSizeStart / 2 = 1MB
-- size_t max_capacity
= absoluteMaxSize = gDvm.heapSizeMax = 16MB
-- int locked
= false
-- char const * name
= name generated in createMspace
- return value
-- mspace

- This function will do:
(1). max_capacity = (size_t)ALIGN_UP(max_capacity, pagesize);
set the heap's max size align up to page boundary

#define PAGESIZE 4096 = 4KB
#define ALIGN_UP(p, alignment) (((uintptr_t)(p) + (alignment)-1) & ~((alignment)-1))


(2). fd = ashmem_create_region(buf, max_capacity);
Map the /dev/zero to fd
fd = open("/dev/zero", O_RDWR);

(3). base = mmap(NULL, max_capacity, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
Do mmap() to map the opened file /dev/zero to the memory with length = max_capacity. On Linux, the mapping will be created at a nearby page boundary.This step will create the needed heap for dynamic memory allocation usage.

(4). The following step will make some installation to the mapped memory in order to manage it as mspace.

local variable: struct mspace_contig_state *cs;

struct mspace_contig_state {
    unsigned int magic;
    char *brk;
    char *top;
    mspace m;
};


cs = base;
-This will make the cs point to the starting address of the created memory space, then the header of the memory space is cs.

m = create_mspace_with_base(base + sizeof(*cs), starting_capacity, locked);
-This will create the mspace from the mapped memory space.


cs->brk = m->seg.base + m->seg.size;
-This set the current usable size of the mspace, which is initialized to
m->footprint = m->max_footprint = starting_capacity = 1MB

cs->top = (char *)base + max_capacity;
This set the max usable size of the mspace.



if (cs->brk != cs->top) {
    /* mprotect() requires page-aligned arguments, but it's possible
     * for cs->brk not to be page-aligned at this point.
     */
    char *prot_brk = (char *)ALIGN_UP(cs->brk, pagesize);
    if (mprotect(prot_brk, cs->top - prot_brk, PROT_NONE) < 0)
      goto error;
}
-This step prevents access to the memory Dalvim VM haven't handed out yet.


(5). return (mspace)m;
This step return the mspace pointer to the caller: createMspace

4. Then callee: mspace_set_max_allowed_footprint will be called by caller: createMspace
- parameter
-- mspace msp
= newly created mspace
-- size_t bytes
= startSize = 1MB

- local variable ms = (mstate)msp

-This function will set the current max_allowed_footprint. ms->max_allowed_footprint will be set to msp->footprint. This function will be called later to increase or decrease the
ms->max_allowed_footprint according to the usage of the Dalvik vm heap. If the Dalvik VM needs to grow its heap size, the ms->max_allowed_footprint will be increased.