1/*
2 * Copyright (C) 2015 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 "profile_saver.h"
18
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <fcntl.h>
22
23#include "art_method-inl.h"
24#include "base/systrace.h"
25#include "base/time_utils.h"
26#include "compiler_filter.h"
27#include "oat_file_manager.h"
28#include "scoped_thread_state_change.h"
29
30
31namespace art {
32
33// TODO: read the constants from ProfileOptions,
34// Add a random delay each time we go to sleep so that we don't hammer the CPU
35// with all profile savers running at the same time.
36static constexpr const uint64_t kMinSavePeriodNs = MsToNs(20 * 1000);  // 20 seconds
37static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000;  // 2 seconds
38// Minimum number of JIT samples during launch to include a method into the profile.
39static constexpr const size_t kStartupMethodSamples = 1;
40
41static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10;
42static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10;
43static constexpr const uint32_t kMinimumNumberOfNotificationBeforeWake =
44    kMinimumNumberOfMethodsToSave;
45static constexpr const uint32_t kMaximumNumberOfNotificationBeforeWake = 50;
46
47
48ProfileSaver* ProfileSaver::instance_ = nullptr;
49pthread_t ProfileSaver::profiler_pthread_ = 0U;
50
51ProfileSaver::ProfileSaver(const std::string& output_filename,
52                           jit::JitCodeCache* jit_code_cache,
53                           const std::vector<std::string>& code_paths,
54                           const std::string& foreign_dex_profile_path,
55                           const std::string& app_data_dir)
56    : jit_code_cache_(jit_code_cache),
57      foreign_dex_profile_path_(foreign_dex_profile_path),
58      shutting_down_(false),
59      last_save_number_of_methods_(0),
60      last_save_number_of_classes_(0),
61      last_time_ns_saver_woke_up_(0),
62      jit_activity_notifications_(0),
63      wait_lock_("ProfileSaver wait lock"),
64      period_condition_("ProfileSaver period condition", wait_lock_),
65      total_bytes_written_(0),
66      total_number_of_writes_(0),
67      total_number_of_code_cache_queries_(0),
68      total_number_of_skipped_writes_(0),
69      total_number_of_failed_writes_(0),
70      total_ms_of_sleep_(0),
71      total_ns_of_work_(0),
72      total_number_of_foreign_dex_marks_(0),
73      max_number_of_profile_entries_cached_(0),
74      total_number_of_hot_spikes_(0),
75      total_number_of_wake_ups_(0) {
76  AddTrackedLocations(output_filename, app_data_dir, code_paths);
77}
78
79void ProfileSaver::Run() {
80  Thread* self = Thread::Current();
81
82  // Fetch the resolved classes for the app images after sleeping for
83  // kSaveResolvedClassesDelayMs.
84  // TODO(calin) This only considers the case of the primary profile file.
85  // Anything that gets loaded in the same VM will not have their resolved
86  // classes save (unless they started before the initial saving was done).
87  {
88    MutexLock mu(self, wait_lock_);
89    constexpr uint64_t kSleepTime = kSaveResolvedClassesDelayMs;
90    const uint64_t end_time = NanoTime() + MsToNs(kSleepTime);
91    while (true) {
92      const uint64_t current_time = NanoTime();
93      if (current_time >= end_time) {
94        break;
95      }
96      period_condition_.TimedWait(self, NsToMs(end_time - current_time), 0);
97    }
98    total_ms_of_sleep_ += kSaveResolvedClassesDelayMs;
99  }
100  FetchAndCacheResolvedClassesAndMethods();
101
102  // Loop for the profiled methods.
103  while (!ShuttingDown(self)) {
104    uint64_t sleep_start = NanoTime();
105    {
106      uint64_t sleep_time = 0;
107      {
108        MutexLock mu(self, wait_lock_);
109        period_condition_.Wait(self);
110        sleep_time = NanoTime() - sleep_start;
111      }
112      // Check if the thread was woken up for shutdown.
113      if (ShuttingDown(self)) {
114        break;
115      }
116      total_number_of_wake_ups_++;
117      // We might have been woken up by a huge number of notifications to guarantee saving.
118      // If we didn't meet the minimum saving period go back to sleep (only if missed by
119      // a reasonable margin).
120      while (kMinSavePeriodNs * 0.9 > sleep_time) {
121        {
122          MutexLock mu(self, wait_lock_);
123          period_condition_.TimedWait(self, NsToMs(kMinSavePeriodNs - sleep_time), 0);
124          sleep_time = NanoTime() - sleep_start;
125        }
126        // Check if the thread was woken up for shutdown.
127        if (ShuttingDown(self)) {
128          break;
129        }
130        total_number_of_wake_ups_++;
131      }
132    }
133    total_ms_of_sleep_ += NsToMs(NanoTime() - sleep_start);
134
135    if (ShuttingDown(self)) {
136      break;
137    }
138
139    uint16_t new_methods = 0;
140    uint64_t start_work = NanoTime();
141    bool profile_saved_to_disk = ProcessProfilingInfo(&new_methods);
142    // Update the notification counter based on result. Note that there might be contention on this
143    // but we don't care about to be 100% precise.
144    if (!profile_saved_to_disk) {
145      // If we didn't save to disk it may be because we didn't have enough new methods.
146      // Set the jit activity notifications to new_methods so we can wake up earlier if needed.
147      jit_activity_notifications_ = new_methods;
148    }
149    total_ns_of_work_ += NanoTime() - start_work;
150  }
151}
152
153void ProfileSaver::NotifyJitActivity() {
154  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
155  if (instance_ == nullptr || instance_->shutting_down_) {
156    return;
157  }
158  instance_->NotifyJitActivityInternal();
159}
160
161void ProfileSaver::WakeUpSaver() {
162  jit_activity_notifications_ = 0;
163  last_time_ns_saver_woke_up_ = NanoTime();
164  period_condition_.Signal(Thread::Current());
165}
166
167void ProfileSaver::NotifyJitActivityInternal() {
168  // Unlikely to overflow but if it happens,
169  // we would have waken up the saver long before that.
170  jit_activity_notifications_++;
171  // Note that we are not as precise as we could be here but we don't want to wake the saver
172  // every time we see a hot method.
173  if (jit_activity_notifications_ > kMinimumNumberOfNotificationBeforeWake) {
174    MutexLock wait_mutex(Thread::Current(), wait_lock_);
175    if ((NanoTime() - last_time_ns_saver_woke_up_) > kMinSavePeriodNs) {
176      WakeUpSaver();
177    }
178  } else if (jit_activity_notifications_ > kMaximumNumberOfNotificationBeforeWake) {
179    // Make sure to wake up the saver if we see a spike in the number of notifications.
180    // This is a precaution to avoid "loosing" a big number of methods in case
181    // this is a spike with no jit after.
182    total_number_of_hot_spikes_++;
183    MutexLock wait_mutex(Thread::Current(), wait_lock_);
184    WakeUpSaver();
185  }
186}
187
188ProfileCompilationInfo* ProfileSaver::GetCachedProfiledInfo(const std::string& filename) {
189  auto info_it = profile_cache_.find(filename);
190  if (info_it == profile_cache_.end()) {
191    info_it = profile_cache_.Put(filename, ProfileCompilationInfo());
192  }
193  return &info_it->second;
194}
195
196// Get resolved methods that have a profile info or more than kStartupMethodSamples samples.
197// Excludes native methods and classes in the boot image.
198class GetMethodsVisitor : public ClassVisitor {
199 public:
200  explicit GetMethodsVisitor(std::vector<MethodReference>* methods) : methods_(methods) {}
201
202  virtual bool operator()(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) {
203    if (Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
204      return true;
205    }
206    for (ArtMethod& method : klass->GetMethods(sizeof(void*))) {
207      if (!method.IsNative()) {
208        if (method.GetCounter() >= kStartupMethodSamples ||
209            method.GetProfilingInfo(sizeof(void*)) != nullptr) {
210          // Have samples, add to profile.
211          const DexFile* dex_file = method.GetInterfaceMethodIfProxy(sizeof(void*))->GetDexFile();
212          methods_->push_back(MethodReference(dex_file, method.GetDexMethodIndex()));
213        }
214      }
215    }
216    return true;
217  }
218
219 private:
220  std::vector<MethodReference>* const methods_;
221};
222
223void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
224  ScopedTrace trace(__PRETTY_FUNCTION__);
225  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
226  std::set<DexCacheResolvedClasses> resolved_classes =
227      class_linker->GetResolvedClasses(/*ignore boot classes*/ true);
228
229  std::vector<MethodReference> methods;
230  {
231    ScopedTrace trace2("Get hot methods");
232    GetMethodsVisitor visitor(&methods);
233    ScopedObjectAccess soa(Thread::Current());
234    class_linker->VisitClasses(&visitor);
235    VLOG(profiler) << "Methods with samples greater than "
236                   << kStartupMethodSamples << " = " << methods.size();
237  }
238  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
239  uint64_t total_number_of_profile_entries_cached = 0;
240
241  for (const auto& it : tracked_dex_base_locations_) {
242    std::set<DexCacheResolvedClasses> resolved_classes_for_location;
243    const std::string& filename = it.first;
244    const std::set<std::string>& locations = it.second;
245    std::vector<MethodReference> methods_for_location;
246    for (const MethodReference& ref : methods) {
247      if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) {
248        methods_for_location.push_back(ref);
249      }
250    }
251    for (const DexCacheResolvedClasses& classes : resolved_classes) {
252      if (locations.find(classes.GetBaseLocation()) != locations.end()) {
253        VLOG(profiler) << "Added " << classes.GetClasses().size() << " classes for location "
254                       << classes.GetBaseLocation() << " (" << classes.GetDexLocation() << ")";
255        resolved_classes_for_location.insert(classes);
256      } else {
257        VLOG(profiler) << "Location not found " << classes.GetBaseLocation()
258                       << " (" << classes.GetDexLocation() << ")";
259      }
260    }
261    ProfileCompilationInfo* info = GetCachedProfiledInfo(filename);
262    info->AddMethodsAndClasses(methods_for_location, resolved_classes_for_location);
263    total_number_of_profile_entries_cached += resolved_classes_for_location.size();
264  }
265  max_number_of_profile_entries_cached_ = std::max(
266      max_number_of_profile_entries_cached_,
267      total_number_of_profile_entries_cached);
268}
269
270bool ProfileSaver::ProcessProfilingInfo(uint16_t* new_methods) {
271  ScopedTrace trace(__PRETTY_FUNCTION__);
272  SafeMap<std::string, std::set<std::string>> tracked_locations;
273  {
274    // Make a copy so that we don't hold the lock while doing I/O.
275    MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
276    tracked_locations = tracked_dex_base_locations_;
277  }
278
279  bool profile_file_saved = false;
280  uint64_t total_number_of_profile_entries_cached = 0;
281  *new_methods = 0;
282
283  for (const auto& it : tracked_locations) {
284    if (ShuttingDown(Thread::Current())) {
285      return true;
286    }
287    const std::string& filename = it.first;
288    const std::set<std::string>& locations = it.second;
289    std::vector<MethodReference> methods;
290    {
291      ScopedObjectAccess soa(Thread::Current());
292      jit_code_cache_->GetProfiledMethods(locations, methods);
293      total_number_of_code_cache_queries_++;
294    }
295
296    ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename);
297    cached_info->AddMethodsAndClasses(methods, std::set<DexCacheResolvedClasses>());
298    int64_t delta_number_of_methods =
299        cached_info->GetNumberOfMethods() -
300        static_cast<int64_t>(last_save_number_of_methods_);
301    int64_t delta_number_of_classes =
302        cached_info->GetNumberOfResolvedClasses() -
303        static_cast<int64_t>(last_save_number_of_classes_);
304
305    if (delta_number_of_methods < kMinimumNumberOfMethodsToSave &&
306        delta_number_of_classes < kMinimumNumberOfClassesToSave) {
307      VLOG(profiler) << "Not enough information to save to: " << filename
308          << " Nr of methods: " << delta_number_of_methods
309          << " Nr of classes: " << delta_number_of_classes;
310      total_number_of_skipped_writes_++;
311      continue;
312    }
313    *new_methods = std::max(static_cast<uint16_t>(delta_number_of_methods), *new_methods);
314    uint64_t bytes_written;
315    // Force the save. In case the profile data is corrupted or the the profile
316    // has the wrong version this will "fix" the file to the correct format.
317    if (cached_info->MergeAndSave(filename, &bytes_written, /*force*/ true)) {
318      last_save_number_of_methods_ = cached_info->GetNumberOfMethods();
319      last_save_number_of_classes_ = cached_info->GetNumberOfResolvedClasses();
320      // Clear resolved classes. No need to store them around as
321      // they don't change after the first write.
322      cached_info->ClearResolvedClasses();
323      if (bytes_written > 0) {
324        total_number_of_writes_++;
325        total_bytes_written_ += bytes_written;
326        profile_file_saved = true;
327      } else {
328        // At this point we could still have avoided the write.
329        // We load and merge the data from the file lazily at its first ever
330        // save attempt. So, whatever we are trying to save could already be
331        // in the file.
332        total_number_of_skipped_writes_++;
333      }
334    } else {
335      LOG(WARNING) << "Could not save profiling info to " << filename;
336      total_number_of_failed_writes_++;
337    }
338    total_number_of_profile_entries_cached +=
339        cached_info->GetNumberOfMethods() +
340        cached_info->GetNumberOfResolvedClasses();
341  }
342  max_number_of_profile_entries_cached_ = std::max(
343      max_number_of_profile_entries_cached_,
344      total_number_of_profile_entries_cached);
345  return profile_file_saved;
346}
347
348void* ProfileSaver::RunProfileSaverThread(void* arg) {
349  Runtime* runtime = Runtime::Current();
350
351  bool attached = runtime->AttachCurrentThread("Profile Saver",
352                                               /*as_daemon*/true,
353                                               runtime->GetSystemThreadGroup(),
354                                               /*create_peer*/true);
355  if (!attached) {
356    CHECK(runtime->IsShuttingDown(Thread::Current()));
357    return nullptr;
358  }
359
360  ProfileSaver* profile_saver = reinterpret_cast<ProfileSaver*>(arg);
361  profile_saver->Run();
362
363  runtime->DetachCurrentThread();
364  VLOG(profiler) << "Profile saver shutdown";
365  return nullptr;
366}
367
368static bool ShouldProfileLocation(const std::string& location) {
369  OatFileManager& oat_manager = Runtime::Current()->GetOatFileManager();
370  const OatFile* oat_file = oat_manager.FindOpenedOatFileFromDexLocation(location);
371  if (oat_file == nullptr) {
372    // This can happen if we fallback to run code directly from the APK.
373    // Profile it with the hope that the background dexopt will get us back into
374    // a good state.
375    VLOG(profiler) << "Asked to profile a location without an oat file:" << location;
376    return true;
377  }
378  CompilerFilter::Filter filter = oat_file->GetCompilerFilter();
379  if ((filter == CompilerFilter::kSpeed) || (filter == CompilerFilter::kEverything)) {
380    VLOG(profiler)
381        << "Skip profiling oat file because it's already speed|everything compiled: "
382        << location << " oat location: " << oat_file->GetLocation();
383    return false;
384  }
385  return true;
386}
387
388void ProfileSaver::Start(const std::string& output_filename,
389                         jit::JitCodeCache* jit_code_cache,
390                         const std::vector<std::string>& code_paths,
391                         const std::string& foreign_dex_profile_path,
392                         const std::string& app_data_dir) {
393  DCHECK(Runtime::Current()->SaveProfileInfo());
394  DCHECK(!output_filename.empty());
395  DCHECK(jit_code_cache != nullptr);
396
397  std::vector<std::string> code_paths_to_profile;
398
399  for (const std::string& location : code_paths) {
400    if (ShouldProfileLocation(location))  {
401      code_paths_to_profile.push_back(location);
402    }
403  }
404  if (code_paths_to_profile.empty()) {
405    VLOG(profiler) << "No code paths should be profiled.";
406    return;
407  }
408
409  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
410  if (instance_ != nullptr) {
411    // If we already have an instance, make sure it uses the same jit_code_cache.
412    // This may be called multiple times via Runtime::registerAppInfo (e.g. for
413    // apps which share the same runtime).
414    DCHECK_EQ(instance_->jit_code_cache_, jit_code_cache);
415    // Add the code_paths to the tracked locations.
416    instance_->AddTrackedLocations(output_filename, app_data_dir, code_paths_to_profile);
417    return;
418  }
419
420  VLOG(profiler) << "Starting profile saver using output file: " << output_filename
421      << ". Tracking: " << Join(code_paths_to_profile, ':');
422
423  instance_ = new ProfileSaver(output_filename,
424                               jit_code_cache,
425                               code_paths_to_profile,
426                               foreign_dex_profile_path,
427                               app_data_dir);
428
429  // Create a new thread which does the saving.
430  CHECK_PTHREAD_CALL(
431      pthread_create,
432      (&profiler_pthread_, nullptr, &RunProfileSaverThread, reinterpret_cast<void*>(instance_)),
433      "Profile saver thread");
434}
435
436void ProfileSaver::Stop(bool dump_info) {
437  ProfileSaver* profile_saver = nullptr;
438  pthread_t profiler_pthread = 0U;
439
440  {
441    MutexLock profiler_mutex(Thread::Current(), *Locks::profiler_lock_);
442    VLOG(profiler) << "Stopping profile saver thread";
443    profile_saver = instance_;
444    profiler_pthread = profiler_pthread_;
445    if (instance_ == nullptr) {
446      DCHECK(false) << "Tried to stop a profile saver which was not started";
447      return;
448    }
449    if (instance_->shutting_down_) {
450      DCHECK(false) << "Tried to stop the profile saver twice";
451      return;
452    }
453    instance_->shutting_down_ = true;
454    if (dump_info) {
455      instance_->DumpInfo(LOG(INFO));
456    }
457  }
458
459  {
460    // Wake up the saver thread if it is sleeping to allow for a clean exit.
461    MutexLock wait_mutex(Thread::Current(), profile_saver->wait_lock_);
462    profile_saver->period_condition_.Signal(Thread::Current());
463  }
464
465  // Wait for the saver thread to stop.
466  CHECK_PTHREAD_CALL(pthread_join, (profiler_pthread, nullptr), "profile saver thread shutdown");
467
468  {
469    MutexLock profiler_mutex(Thread::Current(), *Locks::profiler_lock_);
470    instance_ = nullptr;
471    profiler_pthread_ = 0U;
472  }
473  delete profile_saver;
474}
475
476bool ProfileSaver::ShuttingDown(Thread* self) {
477  MutexLock mu(self, *Locks::profiler_lock_);
478  return shutting_down_;
479}
480
481bool ProfileSaver::IsStarted() {
482  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
483  return instance_ != nullptr;
484}
485
486void ProfileSaver::AddTrackedLocations(const std::string& output_filename,
487                                       const std::string& app_data_dir,
488                                       const std::vector<std::string>& code_paths) {
489  auto it = tracked_dex_base_locations_.find(output_filename);
490  if (it == tracked_dex_base_locations_.end()) {
491    tracked_dex_base_locations_.Put(output_filename,
492                                    std::set<std::string>(code_paths.begin(), code_paths.end()));
493    if (!app_data_dir.empty()) {
494      app_data_dirs_.insert(app_data_dir);
495    }
496  } else {
497    it->second.insert(code_paths.begin(), code_paths.end());
498  }
499}
500
501// TODO(calin): This may lead to several calls to realpath.
502// Consider moving the logic to the saver thread (i.e. when notified,
503// only cache the location, and then wake up the saver thread to do the
504// comparisons with the real file paths and to create the markers).
505void ProfileSaver::NotifyDexUse(const std::string& dex_location) {
506  if (!ShouldProfileLocation(dex_location)) {
507    return;
508  }
509  std::set<std::string> app_code_paths;
510  std::string foreign_dex_profile_path;
511  std::set<std::string> app_data_dirs;
512  {
513    MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
514    if (instance_ == nullptr) {
515      return;
516    }
517    // Make a copy so that we don't hold the lock while doing I/O.
518    for (const auto& it : instance_->tracked_dex_base_locations_) {
519      app_code_paths.insert(it.second.begin(), it.second.end());
520    }
521    foreign_dex_profile_path = instance_->foreign_dex_profile_path_;
522    app_data_dirs.insert(instance_->app_data_dirs_.begin(), instance_->app_data_dirs_.end());
523  }
524
525  bool mark_created = MaybeRecordDexUseInternal(dex_location,
526                                                app_code_paths,
527                                                foreign_dex_profile_path,
528                                                app_data_dirs);
529  if (mark_created) {
530    MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
531    if (instance_ != nullptr) {
532      instance_->total_number_of_foreign_dex_marks_++;
533    }
534  }
535}
536
537static bool CheckContainsWithRealPath(const std::set<std::string>& paths_set,
538                                      const std::string& path_to_check) {
539  for (const auto& path : paths_set) {
540    UniqueCPtr<const char[]> real_path(realpath(path.c_str(), nullptr));
541    if (real_path == nullptr) {
542      PLOG(WARNING) << "Could not get realpath for " << path;
543      continue;
544    }
545    std::string real_path_str(real_path.get());
546    if (real_path_str == path_to_check) {
547      return true;
548    }
549  }
550  return false;
551}
552
553// After the call, dex_location_real_path will contain the marker's name.
554static bool CreateForeignDexMarker(const std::string& foreign_dex_profile_path,
555                                   /*in-out*/ std::string* dex_location_real_path) {
556  // For foreign dex files we record a flag on disk. PackageManager will (potentially) take this
557  // into account when deciding how to optimize the loaded dex file.
558  // The expected flag name is the canonical path of the apk where '/' is substituted to '@'.
559  // (it needs to be kept in sync with
560  // frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java)
561  std::replace(dex_location_real_path->begin(), dex_location_real_path->end(), '/', '@');
562  std::string flag_path = foreign_dex_profile_path + "/" + *dex_location_real_path;
563  // We use O_RDONLY as the access mode because we must supply some access
564  // mode, and there is no access mode that means 'create but do not read' the
565  // file. We will not not actually read from the file.
566  int fd = TEMP_FAILURE_RETRY(open(flag_path.c_str(),
567        O_CREAT | O_RDONLY | O_EXCL | O_CLOEXEC | O_NOFOLLOW, 0));
568  if (fd != -1) {
569    if (close(fd) != 0) {
570      PLOG(WARNING) << "Could not close file after flagging foreign dex use " << flag_path;
571    }
572    return true;
573  } else {
574    if (errno != EEXIST && errno != EACCES) {
575      // Another app could have already created the file, and selinux may not
576      // allow the read access to the file implied by the call to open.
577      PLOG(WARNING) << "Could not create foreign dex use mark " << flag_path;
578      return false;
579    }
580    return true;
581  }
582}
583
584bool ProfileSaver::MaybeRecordDexUseInternal(
585      const std::string& dex_location,
586      const std::set<std::string>& app_code_paths,
587      const std::string& foreign_dex_profile_path,
588      const std::set<std::string>& app_data_dirs) {
589  if (dex_location.empty()) {
590    LOG(WARNING) << "Asked to record foreign dex use with an empty dex location.";
591    return false;
592  }
593  if (foreign_dex_profile_path.empty()) {
594    LOG(WARNING) << "Asked to record foreign dex use without a valid profile path ";
595    return false;
596  }
597
598  if (app_code_paths.find(dex_location) != app_code_paths.end()) {
599    // The dex location belongs to the application code paths. Nothing to record.
600    return false;
601  }
602
603  if (app_data_dirs.find(dex_location) != app_data_dirs.end()) {
604    // The dex location is under the application folder. Nothing to record.
605    return false;
606  }
607
608  // Do another round of checks with the real paths.
609  // Application directory could be a symlink (e.g. /data/data instead of /data/user/0), and we
610  // don't have control over how the dex files are actually loaded (symlink or canonical path),
611
612  // Note that we could cache all the real locations in the saver (since it's an expensive
613  // operation). However we expect that app_code_paths is small (usually 1 element), and
614  // NotifyDexUse is called just a few times in the app lifetime. So we make the compromise
615  // to save some bytes of memory usage.
616
617  UniqueCPtr<const char[]> dex_location_real_path(realpath(dex_location.c_str(), nullptr));
618  if (dex_location_real_path == nullptr) {
619    PLOG(WARNING) << "Could not get realpath for " << dex_location;
620    return false;
621  }
622  std::string dex_location_real_path_str(dex_location_real_path.get());
623
624  if (CheckContainsWithRealPath(app_code_paths, dex_location_real_path_str)) {
625    return false;
626  }
627
628  if (CheckContainsWithRealPath(app_data_dirs, dex_location_real_path_str)) {
629    return false;
630  }
631
632  return CreateForeignDexMarker(foreign_dex_profile_path, &dex_location_real_path_str);
633}
634
635void ProfileSaver::DumpInstanceInfo(std::ostream& os) {
636  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
637  if (instance_ != nullptr) {
638    instance_->DumpInfo(os);
639  }
640}
641
642void ProfileSaver::DumpInfo(std::ostream& os) {
643  os << "ProfileSaver total_bytes_written=" << total_bytes_written_ << '\n'
644     << "ProfileSaver total_number_of_writes=" << total_number_of_writes_ << '\n'
645     << "ProfileSaver total_number_of_code_cache_queries="
646     << total_number_of_code_cache_queries_ << '\n'
647     << "ProfileSaver total_number_of_skipped_writes=" << total_number_of_skipped_writes_ << '\n'
648     << "ProfileSaver total_number_of_failed_writes=" << total_number_of_failed_writes_ << '\n'
649     << "ProfileSaver total_ms_of_sleep=" << total_ms_of_sleep_ << '\n'
650     << "ProfileSaver total_ms_of_work=" << NsToMs(total_ns_of_work_) << '\n'
651     << "ProfileSaver total_number_of_foreign_dex_marks="
652     << total_number_of_foreign_dex_marks_ << '\n'
653     << "ProfileSaver max_number_profile_entries_cached="
654     << max_number_of_profile_entries_cached_ << '\n'
655     << "ProfileSaver total_number_of_hot_spikes=" << total_number_of_hot_spikes_ << '\n'
656     << "ProfileSaver total_number_of_wake_ups=" << total_number_of_wake_ups_ << '\n';
657}
658
659
660void ProfileSaver::ForceProcessProfiles() {
661  ProfileSaver* saver = nullptr;
662  {
663    MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
664    saver = instance_;
665  }
666  // TODO(calin): this is not actually thread safe as the instance_ may have been deleted,
667  // but we only use this in testing when we now this won't happen.
668  // Refactor the way we handle the instance so that we don't end up in this situation.
669  if (saver != nullptr) {
670    uint16_t new_methods;
671    saver->ProcessProfilingInfo(&new_methods);
672  }
673}
674
675bool ProfileSaver::HasSeenMethod(const std::string& profile,
676                                 const DexFile* dex_file,
677                                 uint16_t method_idx) {
678  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
679  if (instance_ != nullptr) {
680    ProfileCompilationInfo* info = instance_->GetCachedProfiledInfo(profile);
681    if (info != nullptr) {
682      return info->ContainsMethod(MethodReference(dex_file, method_idx));
683    }
684  }
685  return false;
686}
687
688}   // namespace art
689