dalvik_system_DexFile.cc revision e1ff199b3bbcf58ed9462e1b7aa47027294f4e4b
1/*
2 * Copyright (C) 2008 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#include <algorithm>
18#include <set>
19#include <fcntl.h>
20#include <unistd.h>
21
22#include "base/logging.h"
23#include "class_linker.h"
24#include "common_throws.h"
25#include "dex_file-inl.h"
26#include "gc/space/image_space.h"
27#include "gc/space/space-inl.h"
28#include "image.h"
29#include "jni_internal.h"
30#include "mirror/class_loader.h"
31#include "mirror/object-inl.h"
32#include "mirror/string.h"
33#include "oat.h"
34#include "os.h"
35#include "profiler.h"
36#include "runtime.h"
37#include "scoped_thread_state_change.h"
38#include "ScopedLocalRef.h"
39#include "ScopedUtfChars.h"
40#include "well_known_classes.h"
41#include "zip_archive.h"
42
43#ifdef HAVE_ANDROID_OS
44#include "cutils/properties.h"
45#endif
46
47namespace art {
48
49// A smart pointer that provides read-only access to a Java string's UTF chars.
50// Unlike libcore's NullableScopedUtfChars, this will *not* throw NullPointerException if
51// passed a null jstring. The correct idiom is:
52//
53//   NullableScopedUtfChars name(env, javaName);
54//   if (env->ExceptionCheck()) {
55//       return NULL;
56//   }
57//   // ... use name.c_str()
58//
59// TODO: rewrite to get rid of this, or change ScopedUtfChars to offer this option.
60class NullableScopedUtfChars {
61 public:
62  NullableScopedUtfChars(JNIEnv* env, jstring s) : mEnv(env), mString(s) {
63    mUtfChars = (s != NULL) ? env->GetStringUTFChars(s, NULL) : NULL;
64  }
65
66  ~NullableScopedUtfChars() {
67    if (mUtfChars) {
68      mEnv->ReleaseStringUTFChars(mString, mUtfChars);
69    }
70  }
71
72  const char* c_str() const {
73    return mUtfChars;
74  }
75
76  size_t size() const {
77    return strlen(mUtfChars);
78  }
79
80  // Element access.
81  const char& operator[](size_t n) const {
82    return mUtfChars[n];
83  }
84
85 private:
86  JNIEnv* mEnv;
87  jstring mString;
88  const char* mUtfChars;
89
90  // Disallow copy and assignment.
91  NullableScopedUtfChars(const NullableScopedUtfChars&);
92  void operator=(const NullableScopedUtfChars&);
93};
94
95static jlong DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) {
96  ScopedUtfChars sourceName(env, javaSourceName);
97  if (sourceName.c_str() == NULL) {
98    return 0;
99  }
100  NullableScopedUtfChars outputName(env, javaOutputName);
101  if (env->ExceptionCheck()) {
102    return 0;
103  }
104
105  uint32_t dex_location_checksum;
106  uint32_t* dex_location_checksum_pointer = &dex_location_checksum;
107  std::vector<std::string> error_msgs;
108  std::string error_msg;
109  if (!DexFile::GetChecksum(sourceName.c_str(), dex_location_checksum_pointer, &error_msg)) {
110    dex_location_checksum_pointer = NULL;
111  }
112
113  ClassLinker* linker = Runtime::Current()->GetClassLinker();
114  const DexFile* dex_file;
115  if (outputName.c_str() == nullptr) {
116    // FindOrCreateOatFileForDexLocation can tolerate a missing dex_location_checksum
117    dex_file = linker->FindDexFileInOatFileFromDexLocation(sourceName.c_str(),
118                                                           dex_location_checksum_pointer,
119                                                           kRuntimeISA,
120                                                           &error_msgs);
121  } else {
122    // FindOrCreateOatFileForDexLocation requires the dex_location_checksum
123    if (dex_location_checksum_pointer == NULL) {
124      ScopedObjectAccess soa(env);
125      DCHECK(!error_msg.empty());
126      ThrowIOException("%s", error_msg.c_str());
127      return 0;
128    }
129    dex_file = linker->FindOrCreateOatFileForDexLocation(sourceName.c_str(), dex_location_checksum,
130                                                         outputName.c_str(), &error_msgs);
131  }
132  if (dex_file == nullptr) {
133    ScopedObjectAccess soa(env);
134    CHECK(!error_msgs.empty());
135    // The most important message is at the end. So set up nesting by going forward, which will
136    // wrap the existing exception as a cause for the following one.
137    auto it = error_msgs.begin();
138    auto itEnd = error_msgs.end();
139    for ( ; it != itEnd; ++it) {
140      ThrowWrappedIOException("%s", it->c_str());
141    }
142
143    return 0;
144  }
145  return static_cast<jlong>(reinterpret_cast<uintptr_t>(dex_file));
146}
147
148static const DexFile* toDexFile(jlong dex_file_address, JNIEnv* env) {
149  const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address));
150  if (UNLIKELY(dex_file == nullptr)) {
151    ScopedObjectAccess soa(env);
152    ThrowNullPointerException(NULL, "dex_file == null");
153  }
154  return dex_file;
155}
156
157static void DexFile_closeDexFile(JNIEnv* env, jclass, jlong cookie) {
158  const DexFile* dex_file;
159  dex_file = toDexFile(cookie, env);
160  if (dex_file == nullptr) {
161    return;
162  }
163  ScopedObjectAccess soa(env);
164  if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) {
165    return;
166  }
167  delete dex_file;
168}
169
170static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
171                                        jlong cookie) {
172  const DexFile* dex_file = toDexFile(cookie, env);
173  if (dex_file == NULL) {
174    VLOG(class_linker) << "Failed to find dex_file";
175    return NULL;
176  }
177  ScopedUtfChars class_name(env, javaName);
178  if (class_name.c_str() == NULL) {
179    VLOG(class_linker) << "Failed to find class_name";
180    return NULL;
181  }
182  const std::string descriptor(DotToDescriptor(class_name.c_str()));
183  const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str());
184  if (dex_class_def == NULL) {
185    VLOG(class_linker) << "Failed to find dex_class_def";
186    return NULL;
187  }
188  ScopedObjectAccess soa(env);
189  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
190  class_linker->RegisterDexFile(*dex_file);
191  StackHandleScope<1> hs(soa.Self());
192  Handle<mirror::ClassLoader> class_loader(
193      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
194  mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file,
195                                                    *dex_class_def);
196  VLOG(class_linker) << "DexFile_defineClassNative returning " << result;
197  return soa.AddLocalReference<jclass>(result);
198}
199
200static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jlong cookie) {
201  jobjectArray result = nullptr;
202  const DexFile* dex_file = toDexFile(cookie, env);
203  if (dex_file != nullptr) {
204    result = env->NewObjectArray(dex_file->NumClassDefs(), WellKnownClasses::java_lang_String,
205                                 nullptr);
206    if (result != nullptr) {
207      for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
208        const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
209        const char* descriptor = dex_file->GetClassDescriptor(class_def);
210        ScopedLocalRef<jstring> jdescriptor(env, env->NewStringUTF(descriptor));
211        if (jdescriptor.get() == nullptr) {
212          return nullptr;
213        }
214        env->SetObjectArrayElement(result, i, jdescriptor.get());
215      }
216    }
217  }
218  return result;
219}
220
221// Copy a profile file
222static void CopyProfileFile(const char* oldfile, const char* newfile) {
223  int fd = open(oldfile, O_RDONLY);
224  if (fd < 0) {
225    // If we can't open the file show the uid:gid of the this process to allow
226    // diagnosis of the problem.
227    LOG(ERROR) << "Failed to open profile file " << oldfile<< ".  My uid:gid is "
228      << getuid() << ":" << getgid();
229    return;
230  }
231
232  // Create the copy with rw------- (only accessible by system)
233  int fd2 = open(newfile, O_WRONLY|O_CREAT|O_TRUNC, 0600);
234  if (fd2 < 0) {
235    // If we can't open the file show the uid:gid of the this process to allow
236    // diagnosis of the problem.
237    LOG(ERROR) << "Failed to create/write prev profile file " << newfile << ".  My uid:gid is "
238      << getuid() << ":" << getgid();
239    return;
240  }
241  char buf[4096];
242  while (true) {
243    int n = read(fd, buf, sizeof(buf));
244    if (n <= 0) {
245      break;
246    }
247    write(fd2, buf, n);
248  }
249  close(fd);
250  close(fd2);
251}
252
253static double GetDoubleProperty(const char* property, double minValue, double maxValue, double defaultValue) {
254#ifndef HAVE_ANDROID_OS
255  return defaultValue;
256#else
257  char buf[PROP_VALUE_MAX];
258  char* endptr;
259
260  property_get(property, buf, "");
261  double value = strtod(buf, &endptr);
262
263  if (value == 0 && endptr == buf) {
264    value = defaultValue;
265  } else if (value < minValue || value > maxValue) {
266    value = defaultValue;
267  }
268  return value;
269#endif
270}
271
272static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename,
273    const char* pkgname, const char* instruction_set, const jboolean defer) {
274  const bool kVerboseLogging = false;  // Spammy logging.
275  const bool kReasonLogging = true;  // Logging of reason for returning JNI_TRUE.
276
277  if ((filename == nullptr) || !OS::FileExists(filename)) {
278    LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename << "' does not exist";
279    ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
280    const char* message = (filename == nullptr) ? "<empty file name>" : filename;
281    env->ThrowNew(fnfe.get(), message);
282    return JNI_FALSE;
283  }
284
285  // Always treat elements of the bootclasspath as up-to-date.  The
286  // fact that code is running at all means that this should be true.
287  Runtime* runtime = Runtime::Current();
288  ClassLinker* class_linker = runtime->GetClassLinker();
289  // TODO: We're assuming that the 64 and 32 bit runtimes have identical
290  // class paths. isDexOptNeeded will not necessarily be called on a runtime
291  // that has the same instruction set as the file being dexopted.
292  const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath();
293  for (size_t i = 0; i < boot_class_path.size(); i++) {
294    if (boot_class_path[i]->GetLocation() == filename) {
295      if (kVerboseLogging) {
296        LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename;
297      }
298      return JNI_FALSE;
299    }
300  }
301
302  const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
303
304  // Check if we have an odex file next to the dex file.
305  std::string odex_filename(DexFilenameToOdexFilename(filename, kRuntimeISA));
306  std::string error_msg;
307  std::unique_ptr<const OatFile> oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false,
308                                                        &error_msg));
309  if (oat_file.get() == nullptr) {
310    if (kVerboseLogging) {
311      LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << filename
312          << "': " << error_msg;
313    }
314    error_msg.clear();
315  } else {
316    const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, NULL,
317                                                                           kReasonLogging);
318    if (oat_dex_file != nullptr) {
319      uint32_t location_checksum;
320      // If its not possible to read the classes.dex assume up-to-date as we won't be able to
321      // compile it anyway.
322      if (!DexFile::GetChecksum(filename, &location_checksum, &error_msg)) {
323        if (kVerboseLogging) {
324          LOG(INFO) << "DexFile_isDexOptNeeded ignoring precompiled stripped file: "
325              << filename << ": " << error_msg;
326        }
327        return JNI_FALSE;
328      }
329      if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename, location_checksum,
330                                              target_instruction_set,
331                                              &error_msg)) {
332        if (kVerboseLogging) {
333          LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << odex_filename
334              << " has an up-to-date checksum compared to " << filename;
335        }
336        return JNI_FALSE;
337      } else {
338        if (kVerboseLogging) {
339          LOG(INFO) << "DexFile_isDexOptNeeded found precompiled file " << odex_filename
340              << " with an out-of-date checksum compared to " << filename
341              << ": " << error_msg;
342        }
343        error_msg.clear();
344      }
345    }
346  }
347
348  // Check the profile file.  We need to rerun dex2oat if the profile has changed significantly
349  // since the last time, or it's new.
350  // If the 'defer' argument is true then this will be retried later.  In this case we
351  // need to make sure that the profile file copy is not made so that we will get the
352  // same result second time.
353  if (pkgname != nullptr) {
354    const std::string profile_file = GetDalvikCacheOrDie("profiles", false /* create_if_absent */)
355        + std::string("/") + pkgname;
356    const std::string profile_cache_dir = GetDalvikCacheOrDie("profile-cache",
357                                                              false /* create_if_absent */);
358
359    // Make the profile cache if it doesn't exist.
360    mkdir(profile_cache_dir.c_str(), 0700);
361
362    // The previous profile file (a copy of the profile the last time this was run) is
363    // in the dalvik-cache directory because this is owned by system.  The profiles
364    // directory is owned by install so system cannot write files in there.
365    std::string prev_profile_file = profile_cache_dir + std::string("/") + pkgname;
366
367    struct stat profstat, prevstat;
368    int e1 = stat(profile_file.c_str(), &profstat);
369    int e2 = stat(prev_profile_file.c_str(), &prevstat);
370    if (e1 < 0) {
371      // No profile file, need to run dex2oat
372      if (kReasonLogging) {
373        LOG(INFO) << "DexFile_isDexOptNeeded profile file " << profile_file << " doesn't exist";
374      }
375      return JNI_TRUE;
376    }
377
378    if (e2 == 0) {
379      // There is a previous profile file.  Check if the profile has changed significantly.
380      // A change in profile is considered significant if X% (change_thr property) of the top K%
381      // (compile_thr property) samples has changed.
382
383      double topKThreshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.compile_thr", 10.0, 90.0, 90.0);
384      double changeThreshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.change_thr", 1.0, 90.0, 10.0);
385      double changePercent = 0.0;
386      std::set<std::string> newTopK, oldTopK;
387      bool newOk = ProfileHelper::LoadTopKSamples(newTopK, profile_file, topKThreshold);
388      bool oldOk = ProfileHelper::LoadTopKSamples(oldTopK, prev_profile_file, topKThreshold);
389      if (!newOk || !oldOk) {
390        if (kVerboseLogging) {
391          LOG(INFO) << "DexFile_isDexOptNeeded Ignoring invalid profiles: "
392                    << (newOk ?  "" : profile_file) << " " << (oldOk ? "" : prev_profile_file);
393        }
394      } else if (newTopK.empty()) {
395        if (kVerboseLogging) {
396          LOG(INFO) << "DexFile_isDexOptNeeded empty profile: " << profile_file;
397        }
398        // If the new topK is empty we shouldn't optimize so we leave the changePercent at 0.0.
399      } else {
400        std::set<std::string> diff;
401        std::set_difference(newTopK.begin(), newTopK.end(), oldTopK.begin(), oldTopK.end(),
402          std::inserter(diff, diff.end()));
403        // TODO: consider using the usedPercentage instead of the plain diff count.
404        changePercent = 100.0 * static_cast<double>(diff.size()) / static_cast<double>(newTopK.size());
405        if (kVerboseLogging) {
406          std::set<std::string>::iterator end = diff.end();
407          for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
408            LOG(INFO) << "DexFile_isDexOptNeeded new in topK: " << *it;
409          }
410        }
411      }
412
413      if (changePercent > changeThreshold) {
414        if (kReasonLogging) {
415          LOG(INFO) << "DexFile_isDexOptNeeded size of new profile file " << profile_file <<
416          " is significantly different from old profile file " << prev_profile_file << " (top "
417          << topKThreshold << "% samples changed in proportion of " << changePercent << "%)";
418        }
419        if (!defer) {
420          CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
421        }
422        return JNI_TRUE;
423      }
424    } else {
425      // Previous profile does not exist.  Make a copy of the current one.
426      if (kVerboseLogging) {
427        LOG(INFO) << "DexFile_isDexOptNeeded previous profile doesn't exist: " << prev_profile_file;
428      }
429      if (!defer) {
430        CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
431      }
432    }
433  }
434
435  // Check if we have an oat file in the cache
436  const std::string cache_dir(GetDalvikCacheOrDie(instruction_set));
437  const std::string cache_location(
438      GetDalvikCacheFilenameOrDie(filename, cache_dir.c_str()));
439  oat_file.reset(OatFile::Open(cache_location, filename, NULL, false, &error_msg));
440  if (oat_file.get() == nullptr) {
441    if (kReasonLogging) {
442      LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
443          << " does not exist for " << filename << ": " << error_msg;
444    }
445    return JNI_TRUE;
446  }
447
448  uint32_t location_checksum;
449  if (!DexFile::GetChecksum(filename, &location_checksum, &error_msg)) {
450    if (kReasonLogging) {
451      LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename
452            << " (error " << error_msg << ")";
453    }
454    return JNI_TRUE;
455  }
456
457  if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename, location_checksum,
458                                           target_instruction_set, &error_msg)) {
459    if (kReasonLogging) {
460      LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
461          << " has out-of-date checksum compared to " << filename
462          << " (error " << error_msg << ")";
463    }
464    return JNI_TRUE;
465  }
466
467  if (kVerboseLogging) {
468    LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
469              << " is up-to-date for " << filename;
470  }
471  CHECK(error_msg.empty()) << error_msg;
472  return JNI_FALSE;
473}
474
475static jboolean DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring javaFilename,
476    jstring javaPkgname, jstring javaInstructionSet, jboolean defer) {
477  ScopedUtfChars filename(env, javaFilename);
478  NullableScopedUtfChars pkgname(env, javaPkgname);
479  ScopedUtfChars instruction_set(env, javaInstructionSet);
480
481  return IsDexOptNeededInternal(env, filename.c_str(), pkgname.c_str(),
482                                instruction_set.c_str(), defer);
483}
484
485// public API, NULL pkgname
486static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
487  const char* instruction_set = GetInstructionSetString(kRuntimeISA);
488  ScopedUtfChars filename(env, javaFilename);
489  return IsDexOptNeededInternal(env, filename.c_str(), nullptr /* pkgname */,
490                                instruction_set, false /* defer */);
491}
492
493
494static JNINativeMethod gMethods[] = {
495  NATIVE_METHOD(DexFile, closeDexFile, "(J)V"),
496  NATIVE_METHOD(DexFile, defineClassNative, "(Ljava/lang/String;Ljava/lang/ClassLoader;J)Ljava/lang/Class;"),
497  NATIVE_METHOD(DexFile, getClassNameList, "(J)[Ljava/lang/String;"),
498  NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
499  NATIVE_METHOD(DexFile, isDexOptNeededInternal, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Z"),
500  NATIVE_METHOD(DexFile, openDexFileNative, "(Ljava/lang/String;Ljava/lang/String;I)J"),
501};
502
503void register_dalvik_system_DexFile(JNIEnv* env) {
504  REGISTER_NATIVE_METHODS("dalvik/system/DexFile");
505}
506
507}  // namespace art
508