blob: 719ac3c85e9c350cca2755183b0c6face838bf3f [file] [log] [blame]
The Android Open Source Project0eec4642012-04-01 00:00:00 -07001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Bit of code to wrap DEX force-updating with a fork() call.
19 */
20
21#define LOG_TAG "TouchDex"
22#include "JNIHelp.h"
23
24#include "cutils/properties.h"
25
26#include <stdlib.h>
27#include <stdio.h>
28#include <string.h>
29#include <unistd.h>
30#include <signal.h>
31#include <sys/types.h>
32#include <sys/wait.h>
33#include <sys/time.h>
34#include <assert.h>
35#include <errno.h>
36
37#define JAVA_PACKAGE "dalvik/system"
38
39#ifndef HAVE_ANDROID_OS
40# define BASE_DIR "/work/device/out/linux-x86-debug-sim"
41#else
42# define BASE_DIR ""
43#endif
44
45namespace android {
46
47// fwd
48static void logProcStatus(pid_t pid);
49
50
51/*
52 * private static int trampoline(String dexFiles, String bcp)
53 */
54static jint dalvik_system_TouchDex_trampoline(JNIEnv* env,
55 jclass, jstring dexFilesStr, jstring bcpStr)
56{
57#ifndef HAVE_ANDROID_OS
58 /* don't do this on simulator -- gdb goes "funny" in goobuntu */
59 return 0;
60#endif
61
62 const int kMinTimeout = 900; // 90 seconds
63 const char* bcp;
64 const char* dexFiles;
65 static const char* kExecFile = BASE_DIR "/system/bin/dalvikvm";
66 //static const char* kDebugArg =
67 // "-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n";
68 static const char* kBcpArgName = "-Xbootclasspath:";
69 static const char* kClassName = "dalvik.system.TouchDex";
70 static const char* kExecMode = "-Xint";
71 static const int argc = 7;
72 const char* argv[argc+1];
73 const char* kVerifyArg;
74 const char* kDexOptArg;
75 int timeoutMult;
76 pid_t pid;
77 struct timeval startWhen, endWhen;
78 char propBuf[PROPERTY_VALUE_MAX];
79 char execModeBuf[PROPERTY_VALUE_MAX + sizeof("-X")];
80 bool verifyJava = true;
81
82 property_get("dalvik.vm.verify-bytecode", propBuf, "");
83 if (strcmp(propBuf, "true") == 0) {
84 verifyJava = true;
85 } else if (strcmp(propBuf, "false") == 0) {
86 verifyJava = false;
87 } else {
88 /* bad value or not defined; use default */
89 }
90
91 if (verifyJava) {
92 kVerifyArg = "-Xverify:all";
93 kDexOptArg = "-Xdexopt:verified";
94 timeoutMult = 11;
95 } else {
96 kVerifyArg = "-Xverify:none";
97 //kDexOptArg = "-Xdexopt:all";
98 kDexOptArg = "-Xdexopt:verified";
99 timeoutMult = 7;
100 }
101
102 property_get("dalvik.vm.execution-mode", propBuf, "");
103 if (strncmp(propBuf, "int:", 4) == 0) {
104 strcpy(execModeBuf, "-X");
105 strcat(execModeBuf, propBuf);
106 kExecMode = execModeBuf;
107 }
108
109 LOGV("TouchDex trampoline forking\n");
110 gettimeofday(&startWhen, NULL);
111
112 /*
113 * Retrieve strings. Note we want to do this *before* the fork() -- bad
114 * idea to perform Java operations in the child process (not all threads
115 * get carried over to the new process).
116 */
117 bcp = env->GetStringUTFChars(bcpStr, NULL);
118 dexFiles = env->GetStringUTFChars(dexFilesStr, NULL);
119 if (bcp == NULL || dexFiles == NULL) {
120 LOGE("Bad values for bcp=%p dexFiles=%p\n", bcp, dexFiles);
121 abort();
122 }
123
124 pid = fork();
125 if (pid < 0) {
126 LOGE("fork failed: %s", strerror(errno));
127 return -1;
128 }
129
130 if (pid == 0) {
131 /* child */
132 char* bcpArg;
133
134 LOGV("TouchDex trampoline in child\n");
135
136 bcpArg = (char*) malloc(strlen(bcp) + strlen(kBcpArgName) +1);
137 strcpy(bcpArg, kBcpArgName);
138 strcat(bcpArg, bcp);
139
140 argv[0] = kExecFile;
141 argv[1] = bcpArg;
142 argv[2] = kVerifyArg;
143 argv[3] = kDexOptArg;
144 argv[4] = kExecMode;
145 argv[5] = kClassName;
146 argv[6] = dexFiles;
147 argv[7] = NULL;
148
149 //LOGI("Calling execv with args:\n");
150 //for (int i = 0; i < argc; i++)
151 // LOGI(" %d: '%s'\n", i, argv[i]);
152
153 execv(kExecFile, (char* const*) argv);
154 free(bcpArg);
155
156 LOGE("execv '%s' failed: %s\n", kExecFile, strerror(errno));
157 exit(1);
158 } else {
159 int cc, count, dexCount, timeout;
160 int result = -1;
161 const char* cp;
162
163 /*
164 * Adjust the timeout based on how many DEX files we have to
165 * process. Larger DEX files take longer, so this is a crude
166 * approximation at best.
167 *
168 * We need this for http://b/issue?id=836771, which can leave us
169 * stuck waiting for a long time even if there is no work to be done.
170 *
171 * This is currently being (ab)used to convert single files, which
172 * sort of spoils the timeout calculation. We establish a minimum
173 * timeout for single apps.
174 *
175 * The timeout calculation doesn't work at all right when a
176 * directory is specified. So the minimum is now a minute. At
177 * this point it's probably safe to just remove the timeout.
178 *
179 * The timeout is in 1/10ths of a second.
180 */
181 dexCount = 1;
182 cp = dexFiles;
183 while (*++cp != '\0') {
184 if (*cp == ':')
185 dexCount++;
186 }
187 timeout = timeoutMult * dexCount;
188 if (timeout < kMinTimeout)
189 timeout = kMinTimeout;
190
191 env->ReleaseStringUTFChars(bcpStr, bcp);
192 env->ReleaseStringUTFChars(dexFilesStr, dexFiles);
193
194
195 LOGD("TouchDex parent waiting for pid=%d (timeout=%.1fs)\n",
196 (int) pid, timeout / 10.0);
197 for (count = 0; count < timeout; count++) {
198 /* waitpid doesn't take a timeout, so poll and sleep */
199 cc = waitpid(pid, &result, WNOHANG);
200 if (cc < 0) {
201 LOGE("waitpid(%d) failed: %s", (int) pid, strerror(errno));
202 return -1;
203 } else if (cc == 0) {
204 usleep(100000); /* 0.1 sec */
205 } else {
206 /* success! */
207 break;
208 }
209 }
210
211 if (count == timeout) {
212 /* note kill(0) returns 0 if the pid is a zombie */
213 LOGE("timed out waiting for %d; kill(0) returns %d\n",
214 (int) pid, kill(pid, 0));
215 logProcStatus(pid);
216 } else {
217 LOGV("TouchDex done after %d iterations (kill(0) returns %d)\n",
218 count, kill(pid, 0));
219 }
220
221 gettimeofday(&endWhen, NULL);
222 long long start = startWhen.tv_sec * 1000000 + startWhen.tv_usec;
223 long long end = endWhen.tv_sec * 1000000 + endWhen.tv_usec;
224
225 LOGI("Dalvik-cache prep: status=0x%04x, finished in %dms\n",
226 result, (int) ((end - start) / 1000));
227
228 if (WIFEXITED(result))
229 return WEXITSTATUS(result);
230 else
231 return result;
232 }
233}
234
235/*
236 * Dump the contents of /proc/<pid>/status to the log file.
237 */
238static void logProcStatus(pid_t pid)
239{
240 char localBuf[256];
241 FILE* fp;
242
243 sprintf(localBuf, "/proc/%d/status", (int) pid);
244 fp = fopen(localBuf, "r");
245 if (fp == NULL) {
246 LOGI("Unable to open '%s'\n", localBuf);
247 return;
248 }
249
250 LOGI("Contents of %s:\n", localBuf);
251 while (true) {
252 fgets(localBuf, sizeof(localBuf), fp);
253 if (ferror(fp) || feof(fp))
254 break;
255 LOGI(" %s", localBuf);
256 }
257
258 fclose(fp);
259}
260
261static JNINativeMethod gMethods[] = {
262 { "trampoline", "(Ljava/lang/String;Ljava/lang/String;)I",
263 (void*) dalvik_system_TouchDex_trampoline },
264};
265int register_dalvik_system_TouchDex(JNIEnv* env) {
266 return jniRegisterNativeMethods(env, JAVA_PACKAGE "/TouchDex", gMethods, NELEM(gMethods));
267}
268
269}; // namespace android