File: profile_saver.cc

package info (click to toggle)
android-platform-art 11.0.0%2Br48-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 78,932 kB
  • sloc: cpp: 459,858; java: 163,268; asm: 22,644; python: 9,815; sh: 6,330; ansic: 4,117; xml: 2,855; perl: 77; makefile: 73
file content (1003 lines) | stat: -rw-r--r-- 42,140 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "profile_saver.h"

#include <fcntl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "android-base/strings.h"

#include "art_method-inl.h"
#include "base/enums.h"
#include "base/logging.h"  // For VLOG.
#include "base/scoped_arena_containers.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
#include "class_table-inl.h"
#include "compiler_filter.h"
#include "dex/dex_file_loader.h"
#include "dex_reference_collection.h"
#include "gc/collector_type.h"
#include "gc/gc_cause.h"
#include "gc/scoped_gc_critical_section.h"
#include "jit/jit.h"
#include "jit/profiling_info.h"
#include "oat_file_manager.h"
#include "profile/profile_compilation_info.h"
#include "scoped_thread_state_change-inl.h"

namespace art {

using Hotness = ProfileCompilationInfo::MethodHotness;

ProfileSaver* ProfileSaver::instance_ = nullptr;
pthread_t ProfileSaver::profiler_pthread_ = 0U;

static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize ==
              InlineCache::kIndividualCacheSize,
              "InlineCache and ProfileCompilationInfo do not agree on kIndividualCacheSize");

// At what priority to schedule the saver threads. 9 is the lowest foreground priority on device.
static constexpr int kProfileSaverPthreadPriority = 9;

static void SetProfileSaverThreadPriority(pthread_t thread, int priority) {
#if defined(ART_TARGET_ANDROID)
  int result = setpriority(PRIO_PROCESS, pthread_gettid_np(thread), priority);
  if (result != 0) {
    LOG(ERROR) << "Failed to setpriority to :" << priority;
  }
#else
  UNUSED(thread);
  UNUSED(priority);
#endif
}

static int GetDefaultThreadPriority() {
#if defined(ART_TARGET_ANDROID)
  pthread_attr_t attr;
  sched_param param;
  pthread_attr_init(&attr);
  pthread_attr_getschedparam(&attr, &param);
  return param.sched_priority;
#else
  return 0;
#endif
}

ProfileSaver::ProfileSaver(const ProfileSaverOptions& options,
                           const std::string& output_filename,
                           jit::JitCodeCache* jit_code_cache,
                           const std::vector<std::string>& code_paths)
    : jit_code_cache_(jit_code_cache),
      shutting_down_(false),
      last_time_ns_saver_woke_up_(0),
      jit_activity_notifications_(0),
      wait_lock_("ProfileSaver wait lock"),
      period_condition_("ProfileSaver period condition", wait_lock_),
      total_bytes_written_(0),
      total_number_of_writes_(0),
      total_number_of_code_cache_queries_(0),
      total_number_of_skipped_writes_(0),
      total_number_of_failed_writes_(0),
      total_ms_of_sleep_(0),
      total_ns_of_work_(0),
      total_number_of_hot_spikes_(0),
      total_number_of_wake_ups_(0),
      options_(options) {
  DCHECK(options_.IsEnabled());
  AddTrackedLocations(output_filename, code_paths);
}

ProfileSaver::~ProfileSaver() {
  for (auto& it : profile_cache_) {
    delete it.second;
  }
}

void ProfileSaver::NotifyStartupCompleted() {
  Thread* self = Thread::Current();
  MutexLock mu(self, *Locks::profiler_lock_);
  if (instance_ == nullptr || instance_->shutting_down_) {
    return;
  }
  MutexLock mu2(self, instance_->wait_lock_);
  instance_->period_condition_.Signal(self);
}

void ProfileSaver::Run() {
  Thread* self = Thread::Current();

  // For thread annotalysis, the setup is more complicated than it should be. Run needs to start
  // under mutex, but should drop it.
  Locks::profiler_lock_->ExclusiveUnlock(self);

  // Fetch the resolved classes for the app images after sleeping for
  // options_.GetSaveResolvedClassesDelayMs().
  // TODO(calin) This only considers the case of the primary profile file.
  // Anything that gets loaded in the same VM will not have their resolved
  // classes save (unless they started before the initial saving was done).
  {
    MutexLock mu(self, wait_lock_);
    const uint64_t end_time = NanoTime() + MsToNs(options_.GetSaveResolvedClassesDelayMs());
    while (!Runtime::Current()->GetStartupCompleted()) {
      const uint64_t current_time = NanoTime();
      if (current_time >= end_time) {
        break;
      }
      period_condition_.TimedWait(self, NsToMs(end_time - current_time), 0);
    }
    total_ms_of_sleep_ += options_.GetSaveResolvedClassesDelayMs();
  }
  // Tell the runtime that startup is completed if it has not already been notified.
  // TODO: We should use another thread to do this in case the profile saver is not running.
  Runtime::Current()->NotifyStartupCompleted();

  FetchAndCacheResolvedClassesAndMethods(/*startup=*/ true);

  // When we save without waiting for JIT notifications we use a simple
  // exponential back off policy bounded by max_wait_without_jit.
  uint32_t max_wait_without_jit = options_.GetMinSavePeriodMs() * 16;
  uint64_t cur_wait_without_jit = options_.GetMinSavePeriodMs();
  // Loop for the profiled methods.
  while (!ShuttingDown(self)) {
    uint64_t sleep_start = NanoTime();
    {
      uint64_t sleep_time = 0;
      {
        MutexLock mu(self, wait_lock_);
        if (options_.GetWaitForJitNotificationsToSave()) {
          period_condition_.Wait(self);
        } else {
          period_condition_.TimedWait(self, cur_wait_without_jit, 0);
          if (cur_wait_without_jit < max_wait_without_jit) {
            cur_wait_without_jit *= 2;
          }
        }
        sleep_time = NanoTime() - sleep_start;
      }
      // Check if the thread was woken up for shutdown.
      if (ShuttingDown(self)) {
        break;
      }
      total_number_of_wake_ups_++;
      // We might have been woken up by a huge number of notifications to guarantee saving.
      // If we didn't meet the minimum saving period go back to sleep (only if missed by
      // a reasonable margin).
      uint64_t min_save_period_ns = MsToNs(options_.GetMinSavePeriodMs());
      while (min_save_period_ns * 0.9 > sleep_time) {
        {
          MutexLock mu(self, wait_lock_);
          period_condition_.TimedWait(self, NsToMs(min_save_period_ns - sleep_time), 0);
          sleep_time = NanoTime() - sleep_start;
        }
        // Check if the thread was woken up for shutdown.
        if (ShuttingDown(self)) {
          break;
        }
        total_number_of_wake_ups_++;
      }
    }
    total_ms_of_sleep_ += NsToMs(NanoTime() - sleep_start);

    if (ShuttingDown(self)) {
      break;
    }

    uint16_t number_of_new_methods = 0;
    uint64_t start_work = NanoTime();
    bool profile_saved_to_disk = ProcessProfilingInfo(/*force_save=*/false, &number_of_new_methods);
    // Update the notification counter based on result. Note that there might be contention on this
    // but we don't care about to be 100% precise.
    if (!profile_saved_to_disk) {
      // If we didn't save to disk it may be because we didn't have enough new methods.
      // Set the jit activity notifications to number_of_new_methods so we can wake up earlier
      // if needed.
      jit_activity_notifications_ = number_of_new_methods;
    }
    total_ns_of_work_ += NanoTime() - start_work;
  }
}

void ProfileSaver::NotifyJitActivity() {
  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
  if (instance_ == nullptr || instance_->shutting_down_) {
    return;
  }
  instance_->NotifyJitActivityInternal();
}

void ProfileSaver::WakeUpSaver() {
  jit_activity_notifications_ = 0;
  last_time_ns_saver_woke_up_ = NanoTime();
  period_condition_.Signal(Thread::Current());
}

void ProfileSaver::NotifyJitActivityInternal() {
  // Unlikely to overflow but if it happens,
  // we would have waken up the saver long before that.
  jit_activity_notifications_++;
  // Note that we are not as precise as we could be here but we don't want to wake the saver
  // every time we see a hot method.
  if (jit_activity_notifications_ > options_.GetMinNotificationBeforeWake()) {
    MutexLock wait_mutex(Thread::Current(), wait_lock_);
    if ((NanoTime() - last_time_ns_saver_woke_up_) > MsToNs(options_.GetMinSavePeriodMs())) {
      WakeUpSaver();
    } else if (jit_activity_notifications_ > options_.GetMaxNotificationBeforeWake()) {
      // Make sure to wake up the saver if we see a spike in the number of notifications.
      // This is a precaution to avoid losing a big number of methods in case
      // this is a spike with no jit after.
      total_number_of_hot_spikes_++;
      WakeUpSaver();
    }
  }
}

class ScopedDefaultPriority {
 public:
  explicit ScopedDefaultPriority(pthread_t thread) : thread_(thread) {
    SetProfileSaverThreadPriority(thread_, GetDefaultThreadPriority());
  }

  ~ScopedDefaultPriority() {
    SetProfileSaverThreadPriority(thread_, kProfileSaverPthreadPriority);
  }

 private:
  const pthread_t thread_;
};

// GetClassLoadersVisitor takes a snapshot of the class loaders and stores them in the out
// class_loaders argument. Not affected by class unloading since there are no suspend points in
// the caller.
class GetClassLoadersVisitor : public ClassLoaderVisitor {
 public:
  explicit GetClassLoadersVisitor(VariableSizedHandleScope* hs,
                                  std::vector<Handle<mirror::ClassLoader>>* class_loaders)
      : hs_(hs),
        class_loaders_(class_loaders) {}

  void Visit(ObjPtr<mirror::ClassLoader> class_loader)
      REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) override {
    class_loaders_->push_back(hs_->NewHandle(class_loader));
  }

 private:
  VariableSizedHandleScope* const hs_;
  std::vector<Handle<mirror::ClassLoader>>* const class_loaders_;
};

// GetClassesVisitor takes a snapshot of the loaded classes that we may want to visit and stores
// them in the out argument. Not affected by class unloading since there are no suspend points in
// the caller.
class GetClassesVisitor : public ClassVisitor {
 public:
  explicit GetClassesVisitor(bool profile_boot_class_path,
                             ScopedArenaVector<ObjPtr<mirror::Class>>* out)
      : profile_boot_class_path_(profile_boot_class_path),
        out_(out) {}

  bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES_SHARED(Locks::mutator_lock_) {
    if (klass->IsProxyClass() ||
        klass->IsArrayClass() ||
        klass->IsPrimitive() ||
        !klass->IsResolved() ||
        klass->IsErroneousResolved() ||
        (!profile_boot_class_path_ && klass->GetClassLoader() == nullptr)) {
      return true;
    }
    out_->push_back(klass);
    return true;
  }

 private:
  const bool profile_boot_class_path_;
  ScopedArenaVector<ObjPtr<mirror::Class>>* const out_;
};

using MethodReferenceCollection = DexReferenceCollection<uint16_t, ScopedArenaAllocatorAdapter>;
using TypeReferenceCollection = DexReferenceCollection<dex::TypeIndex,
                                                       ScopedArenaAllocatorAdapter>;

// Iterate over all of the loaded classes and visit each one. For each class, add it to the
// resolved_classes out argument if startup is true.
// Add methods to the hot_methods out argument if the number of samples is greater or equal to
// hot_method_sample_threshold, add it to sampled_methods if it has at least one sample.
static void SampleClassesAndExecutedMethods(pthread_t profiler_pthread,
                                            bool profile_boot_class_path,
                                            ScopedArenaAllocator* allocator,
                                            uint32_t hot_method_sample_threshold,
                                            bool startup,
                                            TypeReferenceCollection* resolved_classes,
                                            MethodReferenceCollection* hot_methods,
                                            MethodReferenceCollection* sampled_methods) {
  Thread* const self = Thread::Current();
  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
  // Restore profile saver thread priority during the GC critical section. This helps prevent
  // priority inversions blocking the GC for long periods of time.
  std::unique_ptr<ScopedDefaultPriority> sdp;
  // Only restore default priority if we are the profile saver thread. Other threads that call this
  // are threads calling Stop and the signal catcher (for SIGUSR1).
  if (pthread_self() == profiler_pthread) {
    sdp.reset(new ScopedDefaultPriority(profiler_pthread));
  }

  // Do ScopedGCCriticalSection before acquiring mutator lock to prevent the GC running and
  // blocking threads during thread root flipping. Since the GC is a background thread, blocking it
  // is not a problem.
  ScopedObjectAccess soa(self);
  gc::ScopedGCCriticalSection sgcs(self,
                                   gc::kGcCauseProfileSaver,
                                   gc::kCollectorTypeCriticalSection);
  VariableSizedHandleScope hs(soa.Self());
  std::vector<Handle<mirror::ClassLoader>> class_loaders;
  if (profile_boot_class_path) {
    // First add the boot class loader since visit classloaders doesn't visit it.
    class_loaders.push_back(hs.NewHandle<mirror::ClassLoader>(nullptr));
  }
  GetClassLoadersVisitor class_loader_visitor(&hs, &class_loaders);
  {
    // Read the class loaders into a temporary array to prevent contention problems on the
    // class_linker_classes_lock.
    ScopedTrace trace2("Get class loaders");
    ReaderMutexLock mu(soa.Self(), *Locks::classlinker_classes_lock_);
    class_linker->VisitClassLoaders(&class_loader_visitor);
  }
  ScopedArenaVector<ObjPtr<mirror::Class>> classes(allocator->Adapter());
  for (Handle<mirror::ClassLoader> class_loader : class_loaders) {
    ClassTable* table = class_linker->ClassTableForClassLoader(class_loader.Get());
    if (table == nullptr) {
      // If the class loader has not loaded any classes, it may have a null table.
      continue;
    }
    GetClassesVisitor get_classes_visitor(profile_boot_class_path, &classes);
    {
      // Collect the classes into a temporary array to prevent lock contention on the class
      // table lock. We want to avoid blocking class loading in other threads as much as
      // possible.
      ScopedTrace trace3("Visiting class table");
      table->Visit(get_classes_visitor);
    }
    for (ObjPtr<mirror::Class> klass : classes) {
      if (startup) {
        // We only record classes for the startup case. This may change in the future.
        resolved_classes->AddReference(&klass->GetDexFile(), klass->GetDexTypeIndex());
      }
      // Visit all of the methods in the class to see which ones were executed.
      for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
        if (!method.IsNative() && !method.IsAbstract()) {
          DCHECK(!method.IsProxyMethod());
          const uint16_t counter = method.GetCounter();
          // Mark startup methods as hot if they have more than hot_method_sample_threshold
          // samples. This means they will get compiled by the compiler driver.
          if (method.GetProfilingInfo(kRuntimePointerSize) != nullptr ||
              method.PreviouslyWarm() ||
              counter >= hot_method_sample_threshold) {
            hot_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex());
          } else if (counter != 0) {
            sampled_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex());
          }
        } else {
          // We do not record native methods. Once we AOT-compile the app, all native
          // methods shall have their thunks compiled.
        }
      }
    }
    classes.clear();
  }
}

void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) {
  ScopedTrace trace(__PRETTY_FUNCTION__);
  const uint64_t start_time = NanoTime();

  // Resolve any new registered locations.
  ResolveTrackedLocations();

  Thread* const self = Thread::Current();
  Runtime* const runtime = Runtime::Current();
  ArenaStack stack(runtime->GetArenaPool());
  ScopedArenaAllocator allocator(&stack);
  MethodReferenceCollection hot_methods(allocator.Adapter(), allocator.Adapter());
  MethodReferenceCollection sampled_methods(allocator.Adapter(), allocator.Adapter());
  TypeReferenceCollection resolved_classes(allocator.Adapter(), allocator.Adapter());
  const bool is_low_ram = Runtime::Current()->GetHeap()->IsLowMemoryMode();
  pthread_t profiler_pthread;
  {
    MutexLock mu(self, *Locks::profiler_lock_);
    profiler_pthread = profiler_pthread_;
  }
  uint32_t hot_method_sample_threshold = std::numeric_limits<uint32_t>::max();
  if (startup) {
    hot_method_sample_threshold = options_.GetHotStartupMethodSamples(is_low_ram);
  } else if (Runtime::Current()->GetJit() != nullptr) {
    hot_method_sample_threshold = Runtime::Current()->GetJit()->WarmMethodThreshold();
  }
  SampleClassesAndExecutedMethods(profiler_pthread,
                                  options_.GetProfileBootClassPath(),
                                  &allocator,
                                  hot_method_sample_threshold,
                                  startup,
                                  &resolved_classes,
                                  &hot_methods,
                                  &sampled_methods);
  MutexLock mu(self, *Locks::profiler_lock_);

  for (const auto& it : tracked_dex_base_locations_) {
    const std::string& filename = it.first;
    auto info_it = profile_cache_.find(filename);
    if (info_it == profile_cache_.end()) {
      info_it = profile_cache_.Put(
          filename,
          new ProfileCompilationInfo(
              Runtime::Current()->GetArenaPool(), options_.GetProfileBootClassPath()));
    }
    ProfileCompilationInfo* cached_info = info_it->second;

    const std::set<std::string>& locations = it.second;
    VLOG(profiler) << "Locations for " << it.first << " " << android::base::Join(locations, ':');

    for (const auto& pair : hot_methods.GetMap()) {
      const DexFile* const dex_file = pair.first;
      const std::string base_location = DexFileLoader::GetBaseLocation(dex_file->GetLocation());
      const MethodReferenceCollection::IndexVector& indices = pair.second;
      VLOG(profiler) << "Location " << dex_file->GetLocation()
                     << " base_location=" << base_location
                     << " found=" << (locations.find(base_location) != locations.end())
                     << " indices size=" << indices.size();
      if (locations.find(base_location) != locations.end()) {
        uint32_t flags = Hotness::kFlagHot;
        flags |= startup ? Hotness::kFlagStartup : Hotness::kFlagPostStartup;
        cached_info->AddMethodsForDex(
            AnnotateSampleFlags(flags),
            dex_file,
            indices.begin(),
            indices.end(),
            GetProfileSampleAnnotation());
      }
    }
    for (const auto& pair : sampled_methods.GetMap()) {
      const DexFile* const dex_file = pair.first;
      const std::string base_location = DexFileLoader::GetBaseLocation(dex_file->GetLocation());
      const MethodReferenceCollection::IndexVector& indices = pair.second;
      VLOG(profiler) << "Location " << base_location
                     << " found=" << (locations.find(base_location) != locations.end())
                     << " indices size=" << indices.size();
      if (locations.find(base_location) != locations.end()) {
        cached_info->AddMethodsForDex(
            AnnotateSampleFlags(startup ? Hotness::kFlagStartup : Hotness::kFlagPostStartup),
            dex_file,
            indices.begin(),
            indices.end(),
            GetProfileSampleAnnotation());
      }
    }
    for (const auto& pair : resolved_classes.GetMap()) {
      const DexFile* const dex_file = pair.first;
      const std::string base_location = DexFileLoader::GetBaseLocation(dex_file->GetLocation());
      if (locations.find(base_location) != locations.end()) {
        const TypeReferenceCollection::IndexVector& classes = pair.second;
        VLOG(profiler) << "Added " << classes.size() << " classes for location "
                       << base_location
                       << " (" << dex_file->GetLocation() << ")";
        cached_info->AddClassesForDex(dex_file,
                                      classes.begin(),
                                      classes.end(),
                                      GetProfileSampleAnnotation());
      } else {
        VLOG(profiler) << "Location not found " << base_location;
      }
    }
  }
  VLOG(profiler) << "Profile saver recorded " << hot_methods.NumReferences() << " hot methods and "
                 << sampled_methods.NumReferences() << " sampled methods with threshold "
                 << hot_method_sample_threshold << " in "
                 << PrettyDuration(NanoTime() - start_time);
}

bool ProfileSaver::ProcessProfilingInfo(bool force_save, /*out*/uint16_t* number_of_new_methods) {
  ScopedTrace trace(__PRETTY_FUNCTION__);

  // Resolve any new registered locations.
  ResolveTrackedLocations();

  SafeMap<std::string, std::set<std::string>> tracked_locations;
  {
    // Make a copy so that we don't hold the lock while doing I/O.
    MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
    tracked_locations = tracked_dex_base_locations_;
  }

  bool profile_file_saved = false;
  if (number_of_new_methods != nullptr) {
    *number_of_new_methods = 0;
  }

  // We only need to do this once, not once per dex location.
  // TODO: Figure out a way to only do it when stuff has changed? It takes 30-50ms.
  FetchAndCacheResolvedClassesAndMethods(/*startup=*/ false);

  for (const auto& it : tracked_locations) {
    if (!force_save && ShuttingDown(Thread::Current())) {
      // The ProfileSaver is in shutdown mode, meaning a stop request was made and
      // we need to exit cleanly (by waiting for the saver thread to finish). Unless
      // we have a request for a forced save, do not do any processing so that we
      // speed up the exit.
      return true;
    }
    const std::string& filename = it.first;
    const std::set<std::string>& locations = it.second;
    VLOG(profiler) << "Tracked filename " << filename << " locations "
                   << android::base::Join(locations, ":");

    std::vector<ProfileMethodInfo> profile_methods;
    {
      ScopedObjectAccess soa(Thread::Current());
      jit_code_cache_->GetProfiledMethods(locations, profile_methods);
      total_number_of_code_cache_queries_++;
    }
    {
      ProfileCompilationInfo info(Runtime::Current()->GetArenaPool());
      if (!info.Load(filename, /*clear_if_invalid=*/ true)) {
        LOG(WARNING) << "Could not forcefully load profile " << filename;
        continue;
      }
      if (options_.GetProfileBootClassPath() != info.IsForBootImage()) {
        // If we enabled boot class path profiling but the profile is a regular one,
        // (or the opposite), clear the profile. We do not support cross-version merges.
        LOG(WARNING) << "Adjust profile version: for_boot_classpath="
            << options_.GetProfileBootClassPath();
        info.ClearDataAndAdjustVersion(options_.GetProfileBootClassPath());
        // For saving to ensure we persist the new version.
        force_save = true;
      }
      uint64_t last_save_number_of_methods = info.GetNumberOfMethods();
      uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses();
      VLOG(profiler) << "last_save_number_of_methods=" << last_save_number_of_methods
                     << " last_save_number_of_classes=" << last_save_number_of_classes
                     << " number of profiled methods=" << profile_methods.size();

      // Try to add the method data. Note this may fail is the profile loaded from disk contains
      // outdated data (e.g. the previous profiled dex files might have been updated).
      // If this happens we clear the profile data and for the save to ensure the file is cleared.
      if (!info.AddMethods(
              profile_methods,
              AnnotateSampleFlags(Hotness::kFlagHot | Hotness::kFlagPostStartup),
              GetProfileSampleAnnotation())) {
        LOG(WARNING) << "Could not add methods to the existing profiler. "
            << "Clearing the profile data.";
        info.ClearData();
        force_save = true;
      }

      auto profile_cache_it = profile_cache_.find(filename);
      if (profile_cache_it != profile_cache_.end()) {
        if (!info.MergeWith(*(profile_cache_it->second))) {
          LOG(WARNING) << "Could not merge the profile. Clearing the profile data.";
          info.ClearData();
          force_save = true;
        }
      } else if (VLOG_IS_ON(profiler)) {
        LOG(INFO) << "Failed to find cached profile for " << filename;
        for (auto&& pair : profile_cache_) {
          LOG(INFO) << "Cached profile " << pair.first;
        }
      }

      int64_t delta_number_of_methods =
          info.GetNumberOfMethods() - last_save_number_of_methods;
      int64_t delta_number_of_classes =
          info.GetNumberOfResolvedClasses() - last_save_number_of_classes;

      if (!force_save &&
          delta_number_of_methods < options_.GetMinMethodsToSave() &&
          delta_number_of_classes < options_.GetMinClassesToSave()) {
        VLOG(profiler) << "Not enough information to save to: " << filename
                       << " Number of methods: " << delta_number_of_methods
                       << " Number of classes: " << delta_number_of_classes;
        total_number_of_skipped_writes_++;
        continue;
      }

      if (number_of_new_methods != nullptr) {
        *number_of_new_methods =
            std::max(static_cast<uint16_t>(delta_number_of_methods),
                     *number_of_new_methods);
      }
      uint64_t bytes_written;
      // Force the save. In case the profile data is corrupted or the the profile
      // has the wrong version this will "fix" the file to the correct format.
      if (info.Save(filename, &bytes_written)) {
        // We managed to save the profile. Clear the cache stored during startup.
        if (profile_cache_it != profile_cache_.end()) {
          ProfileCompilationInfo *cached_info = profile_cache_it->second;
          profile_cache_.erase(profile_cache_it);
          delete cached_info;
        }
        if (bytes_written > 0) {
          total_number_of_writes_++;
          total_bytes_written_ += bytes_written;
          profile_file_saved = true;
        } else {
          // At this point we could still have avoided the write.
          // We load and merge the data from the file lazily at its first ever
          // save attempt. So, whatever we are trying to save could already be
          // in the file.
          total_number_of_skipped_writes_++;
        }
      } else {
        LOG(WARNING) << "Could not save profiling info to " << filename;
        total_number_of_failed_writes_++;
      }
    }
  }

  // Trim the maps to madvise the pages used for profile info.
  // It is unlikely we will need them again in the near feature.
  Runtime::Current()->GetArenaPool()->TrimMaps();

  return profile_file_saved;
}

void* ProfileSaver::RunProfileSaverThread(void* arg) {
  Runtime* runtime = Runtime::Current();

  bool attached = runtime->AttachCurrentThread("Profile Saver",
                                               /*as_daemon=*/true,
                                               runtime->GetSystemThreadGroup(),
                                               /*create_peer=*/true);
  if (!attached) {
    CHECK(runtime->IsShuttingDown(Thread::Current()));
    return nullptr;
  }

  {
    Locks::profiler_lock_->ExclusiveLock(Thread::Current());
    CHECK_EQ(reinterpret_cast<ProfileSaver*>(arg), instance_);
    instance_->Run();
  }

  runtime->DetachCurrentThread();
  VLOG(profiler) << "Profile saver shutdown";
  return nullptr;
}

static bool ShouldProfileLocation(const std::string& location, bool profile_aot_code) {
  if (profile_aot_code) {
    // If we have to profile all the code, irrespective of its compilation state, return true
    // right away.
    return true;
  }

  OatFileManager& oat_manager = Runtime::Current()->GetOatFileManager();
  const OatFile* oat_file = oat_manager.FindOpenedOatFileFromDexLocation(location);
  if (oat_file == nullptr) {
    // This can happen if we fallback to run code directly from the APK.
    // Profile it with the hope that the background dexopt will get us back into
    // a good state.
    VLOG(profiler) << "Asked to profile a location without an oat file:" << location;
    return true;
  }
  CompilerFilter::Filter filter = oat_file->GetCompilerFilter();
  if ((filter == CompilerFilter::kSpeed) || (filter == CompilerFilter::kEverything)) {
    VLOG(profiler)
        << "Skip profiling oat file because it's already speed|everything compiled: "
        << location << " oat location: " << oat_file->GetLocation();
    return false;
  }
  return true;
}

void  ProfileSaver::Start(const ProfileSaverOptions& options,
                         const std::string& output_filename,
                         jit::JitCodeCache* jit_code_cache,
                         const std::vector<std::string>& code_paths) {
  Runtime* const runtime = Runtime::Current();
  DCHECK(options.IsEnabled());
  DCHECK(runtime->GetJit() != nullptr);
  DCHECK(!output_filename.empty());
  DCHECK(jit_code_cache != nullptr);

  std::vector<std::string> code_paths_to_profile;
  for (const std::string& location : code_paths) {
    if (ShouldProfileLocation(location, options.GetProfileAOTCode()))  {
      VLOG(profiler) << "Code path to profile " << location;
      code_paths_to_profile.push_back(location);
    }
  }

  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
  // Support getting profile samples for the boot class path. This will be used to generate the boot
  // image profile. The intention is to use this code to generate to boot image but not use it in
  // production. b/37966211
  if (options.GetProfileBootClassPath()) {
    std::set<std::string> code_paths_keys;
    for (const std::string& location : code_paths) {
      // Use the profile base key for checking file uniqueness (as it is constructed solely based
      // on the location and ignores other metadata like origin package).
      code_paths_keys.insert(ProfileCompilationInfo::GetProfileDexFileBaseKey(location));
    }
    for (const DexFile* dex_file : runtime->GetClassLinker()->GetBootClassPath()) {
      // Don't check ShouldProfileLocation since the boot class path may be speed compiled.
      const std::string& location = dex_file->GetLocation();
      const std::string key = ProfileCompilationInfo::GetProfileDexFileBaseKey(location);
      VLOG(profiler) << "Registering boot dex file " << location;
      if (code_paths_keys.find(key) != code_paths_keys.end()) {
        LOG(WARNING) << "Boot class path location key conflicts with code path " << location;
      } else if (instance_ == nullptr) {
        // Only add the boot class path once since Start may be called multiple times for secondary
        // dexes.
        // We still do the collision check above. This handles any secondary dexes that conflict
        // with the boot class path dex files.
        code_paths_to_profile.push_back(location);
      }
    }
  }
  if (code_paths_to_profile.empty()) {
    VLOG(profiler) << "No code paths should be profiled.";
    return;
  }

  if (instance_ != nullptr) {
    // If we already have an instance, make sure it uses the same jit_code_cache.
    // This may be called multiple times via Runtime::registerAppInfo (e.g. for
    // apps which share the same runtime).
    DCHECK_EQ(instance_->jit_code_cache_, jit_code_cache);
    // Add the code_paths to the tracked locations.
    instance_->AddTrackedLocations(output_filename, code_paths_to_profile);
    return;
  }

  VLOG(profiler) << "Starting profile saver using output file: " << output_filename
      << ". Tracking: " << android::base::Join(code_paths_to_profile, ':');

  instance_ = new ProfileSaver(options,
                               output_filename,
                               jit_code_cache,
                               code_paths_to_profile);

  // Create a new thread which does the saving.
  CHECK_PTHREAD_CALL(
      pthread_create,
      (&profiler_pthread_, nullptr, &RunProfileSaverThread, reinterpret_cast<void*>(instance_)),
      "Profile saver thread");

  SetProfileSaverThreadPriority(profiler_pthread_, kProfileSaverPthreadPriority);
}

void ProfileSaver::Stop(bool dump_info) {
  ProfileSaver* profile_saver = nullptr;
  pthread_t profiler_pthread = 0U;

  {
    MutexLock profiler_mutex(Thread::Current(), *Locks::profiler_lock_);
    VLOG(profiler) << "Stopping profile saver thread";
    profile_saver = instance_;
    profiler_pthread = profiler_pthread_;
    if (instance_ == nullptr) {
      DCHECK(false) << "Tried to stop a profile saver which was not started";
      return;
    }
    if (instance_->shutting_down_) {
      DCHECK(false) << "Tried to stop the profile saver twice";
      return;
    }
    instance_->shutting_down_ = true;
  }

  {
    // Wake up the saver thread if it is sleeping to allow for a clean exit.
    MutexLock wait_mutex(Thread::Current(), profile_saver->wait_lock_);
    profile_saver->period_condition_.Signal(Thread::Current());
  }

  // Force save everything before destroying the thread since we want profiler_pthread_ to remain
  // valid.
  profile_saver->ProcessProfilingInfo(/*force_save=*/true, /*number_of_new_methods=*/nullptr);

  // Wait for the saver thread to stop.
  CHECK_PTHREAD_CALL(pthread_join, (profiler_pthread, nullptr), "profile saver thread shutdown");

  {
    MutexLock profiler_mutex(Thread::Current(), *Locks::profiler_lock_);
    if (dump_info) {
      instance_->DumpInfo(LOG_STREAM(INFO));
    }
    instance_ = nullptr;
    profiler_pthread_ = 0U;
  }
  delete profile_saver;
}

bool ProfileSaver::ShuttingDown(Thread* self) {
  MutexLock mu(self, *Locks::profiler_lock_);
  return shutting_down_;
}

bool ProfileSaver::IsStarted() {
  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
  return instance_ != nullptr;
}

static void AddTrackedLocationsToMap(const std::string& output_filename,
                                     const std::vector<std::string>& code_paths,
                                     SafeMap<std::string, std::set<std::string>>* map) {
  std::vector<std::string> code_paths_and_filenames;
  // The dex locations are sometimes set to the filename instead of the full path.
  // So make sure we have both "locations" when tracking what needs to be profiled.
  //   - apps + system server have filenames
  //   - boot classpath elements have full paths

  // TODO(calin, ngeoffray, vmarko) This is an workaround for using filanames as
  // dex locations - needed to prebuilt with a partial boot image
  // (commit: c4a924d8c74241057d957d360bf31cd5cd0e4f9c).
  // We should find a better way which allows us to do the tracking based on full paths.
  for (const std::string& path : code_paths) {
    size_t last_sep_index = path.find_last_of('/');
    if (last_sep_index == path.size() - 1) {
      // Should not happen, but anyone can register code paths so better be prepared and ignore
      // such locations.
      continue;
    }
    std::string filename = last_sep_index == std::string::npos
        ? path
        : path.substr(last_sep_index + 1);

    code_paths_and_filenames.push_back(path);
    code_paths_and_filenames.push_back(filename);
  }

  auto it = map->find(output_filename);
  if (it == map->end()) {
    map->Put(
        output_filename,
        std::set<std::string>(code_paths_and_filenames.begin(), code_paths_and_filenames.end()));
  } else {
    it->second.insert(code_paths_and_filenames.begin(), code_paths_and_filenames.end());
  }
}

void ProfileSaver::AddTrackedLocations(const std::string& output_filename,
                                       const std::vector<std::string>& code_paths) {
  // Add the code paths to the list of tracked location.
  AddTrackedLocationsToMap(output_filename, code_paths, &tracked_dex_base_locations_);
  // The code paths may contain symlinks which could fool the profiler.
  // If the dex file is compiled with an absolute location but loaded with symlink
  // the profiler could skip the dex due to location mismatch.
  // To avoid this, we add the code paths to the temporary cache of 'to_be_resolved'
  // locations. When the profiler thread executes we will resolve the paths to their
  // real paths.
  // Note that we delay taking the realpath to avoid spending more time than needed
  // when registering location (as it is done during app launch).
  AddTrackedLocationsToMap(output_filename,
                           code_paths,
                           &tracked_dex_base_locations_to_be_resolved_);
}

void ProfileSaver::DumpInstanceInfo(std::ostream& os) {
  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
  if (instance_ != nullptr) {
    instance_->DumpInfo(os);
  }
}

void ProfileSaver::DumpInfo(std::ostream& os) {
  os << "ProfileSaver total_bytes_written=" << total_bytes_written_ << '\n'
     << "ProfileSaver total_number_of_writes=" << total_number_of_writes_ << '\n'
     << "ProfileSaver total_number_of_code_cache_queries="
     << total_number_of_code_cache_queries_ << '\n'
     << "ProfileSaver total_number_of_skipped_writes=" << total_number_of_skipped_writes_ << '\n'
     << "ProfileSaver total_number_of_failed_writes=" << total_number_of_failed_writes_ << '\n'
     << "ProfileSaver total_ms_of_sleep=" << total_ms_of_sleep_ << '\n'
     << "ProfileSaver total_ms_of_work=" << NsToMs(total_ns_of_work_) << '\n'
     << "ProfileSaver total_number_of_hot_spikes=" << total_number_of_hot_spikes_ << '\n'
     << "ProfileSaver total_number_of_wake_ups=" << total_number_of_wake_ups_ << '\n';
}


void ProfileSaver::ForceProcessProfiles() {
  ProfileSaver* saver = nullptr;
  {
    MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
    saver = instance_;
  }
  // TODO(calin): this is not actually thread safe as the instance_ may have been deleted,
  // but we only use this in testing when we now this won't happen.
  // Refactor the way we handle the instance so that we don't end up in this situation.
  if (saver != nullptr) {
    saver->ProcessProfilingInfo(/*force_save=*/true, /*number_of_new_methods=*/nullptr);
  }
}

bool ProfileSaver::HasSeenMethod(const std::string& profile, bool hot, MethodReference ref) {
  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
  if (instance_ != nullptr) {
    ProfileCompilationInfo info(Runtime::Current()->GetArenaPool());
    if (!info.Load(profile, /*clear_if_invalid=*/false)) {
      return false;
    }
    const ProfileCompilationInfo::MethodHotness hotness = info.GetMethodHotness(ref);
    return hot ? hotness.IsHot() : hotness.IsInProfile();
  }
  return false;
}

void ProfileSaver::ResolveTrackedLocations() {
  SafeMap<std::string, std::set<std::string>> locations_to_be_resolved;
  {
    // Make a copy so that we don't hold the lock while doing I/O.
    MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
    locations_to_be_resolved = tracked_dex_base_locations_to_be_resolved_;
    tracked_dex_base_locations_to_be_resolved_.clear();
  }

  // Resolve the locations.
  SafeMap<std::string, std::vector<std::string>> resolved_locations_map;
  for (const auto& it : locations_to_be_resolved) {
    const std::string& filename = it.first;
    const std::set<std::string>& locations = it.second;
    auto resolved_locations_it = resolved_locations_map.Put(
        filename,
        std::vector<std::string>(locations.size()));

    for (const auto& location : locations) {
      UniqueCPtr<const char[]> location_real(realpath(location.c_str(), nullptr));
      // Note that it's ok if we cannot get the real path.
      if (location_real != nullptr) {
        resolved_locations_it->second.emplace_back(location_real.get());
      }
    }
  }

  // Add the resolved locations to the tracked collection.
  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
  for (const auto& it : resolved_locations_map) {
    AddTrackedLocationsToMap(it.first, it.second, &tracked_dex_base_locations_);
  }
}

ProfileCompilationInfo::ProfileSampleAnnotation ProfileSaver::GetProfileSampleAnnotation() {
  // Ideally, this would be cached in the ProfileSaver class, when we start the thread.
  // However the profile is initialized before the process package name is set and fixing this
  // would require unnecessary complex synchronizations.
  std::string package_name = Runtime::Current()->GetProcessPackageName();
  if (package_name.empty()) {
    package_name = "unknown";
  }
  // We only use annotation for the boot image profiles. Regular apps do not use the extra
  // metadata and as such there is no need to pay the cost (storage and computational)
  // that comes with the annotations.
  return options_.GetProfileBootClassPath()
      ? ProfileCompilationInfo::ProfileSampleAnnotation(package_name)
      : ProfileCompilationInfo::ProfileSampleAnnotation::kNone;
}

Hotness::Flag ProfileSaver::AnnotateSampleFlags(uint32_t flags) {
  uint32_t extra_flags = 0;
  // We only add the extra flags for the boot image profile because individual apps do not use
  // this information.
  if (options_.GetProfileBootClassPath()) {
    extra_flags = Is64BitInstructionSet(Runtime::Current()->GetInstructionSet())
        ? Hotness::kFlag64bit
        : Hotness::kFlag32bit;
  }
  return static_cast<Hotness::Flag>(flags | extra_flags);
}

}   // namespace art