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
31 namespace 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.
36 static constexpr const uint64_t kMinSavePeriodNs = MsToNs(20 * 1000); // 20 seconds
37 static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds
38 // Minimum number of JIT samples during launch to include a method into the profile.
39 static constexpr const size_t kStartupMethodSamples = 1;
40
41 static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10;
42 static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10;
43 static constexpr const uint32_t kMinimumNumberOfNotificationBeforeWake =
44 kMinimumNumberOfMethodsToSave;
45 static constexpr const uint32_t kMaximumNumberOfNotificationBeforeWake = 50;
46
47
48 ProfileSaver* ProfileSaver::instance_ = nullptr;
49 pthread_t ProfileSaver::profiler_pthread_ = 0U;
50
ProfileSaver(const std::string & output_filename,jit::JitCodeCache * jit_code_cache,const std::vector<std::string> & code_paths,const std::string & foreign_dex_profile_path,const std::string & app_data_dir)51 ProfileSaver::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
Run()79 void 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
NotifyJitActivity()153 void 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
WakeUpSaver()161 void ProfileSaver::WakeUpSaver() {
162 jit_activity_notifications_ = 0;
163 last_time_ns_saver_woke_up_ = NanoTime();
164 period_condition_.Signal(Thread::Current());
165 }
166
NotifyJitActivityInternal()167 void 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
GetCachedProfiledInfo(const std::string & filename)188 ProfileCompilationInfo* 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.
198 class GetMethodsVisitor : public ClassVisitor {
199 public:
GetMethodsVisitor(std::vector<MethodReference> * methods)200 explicit GetMethodsVisitor(std::vector<MethodReference>* methods) : methods_(methods) {}
201
operator ()(mirror::Class * klass)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
FetchAndCacheResolvedClassesAndMethods()223 void 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
ProcessProfilingInfo(uint16_t * new_methods)270 bool 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
RunProfileSaverThread(void * arg)348 void* 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
ShouldProfileLocation(const std::string & location)368 static 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
Start(const std::string & output_filename,jit::JitCodeCache * jit_code_cache,const std::vector<std::string> & code_paths,const std::string & foreign_dex_profile_path,const std::string & app_data_dir)388 void 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
Stop(bool dump_info)436 void 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
ShuttingDown(Thread * self)476 bool ProfileSaver::ShuttingDown(Thread* self) {
477 MutexLock mu(self, *Locks::profiler_lock_);
478 return shutting_down_;
479 }
480
IsStarted()481 bool ProfileSaver::IsStarted() {
482 MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
483 return instance_ != nullptr;
484 }
485
AddTrackedLocations(const std::string & output_filename,const std::string & app_data_dir,const std::vector<std::string> & code_paths)486 void 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).
NotifyDexUse(const std::string & dex_location)505 void 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
CheckContainsWithRealPath(const std::set<std::string> & paths_set,const std::string & path_to_check)537 static 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.
CreateForeignDexMarker(const std::string & foreign_dex_profile_path,std::string * dex_location_real_path)554 static 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
MaybeRecordDexUseInternal(const std::string & dex_location,const std::set<std::string> & app_code_paths,const std::string & foreign_dex_profile_path,const std::set<std::string> & app_data_dirs)584 bool 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
DumpInstanceInfo(std::ostream & os)635 void ProfileSaver::DumpInstanceInfo(std::ostream& os) {
636 MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
637 if (instance_ != nullptr) {
638 instance_->DumpInfo(os);
639 }
640 }
641
DumpInfo(std::ostream & os)642 void 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
ForceProcessProfiles()660 void 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
HasSeenMethod(const std::string & profile,const DexFile * dex_file,uint16_t method_idx)675 bool 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