1 /*
2  * Copyright (C) 2020 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 "base/atomic.h"
18 #include "base/locks.h"
19 #include "gc/heap.h"
20 #include "javaheapprof/javaheapsampler.h"
21 #ifdef ART_TARGET_ANDROID
22 #include "perfetto/heap_profile.h"
23 #endif
24 #include "runtime.h"
25 
26 namespace art {
27 
NextGeoDistRandSample()28 size_t HeapSampler::NextGeoDistRandSample() {
29   // Make sure that rng_ and geo_dist are thread safe by acquiring a lock to access.
30   art::MutexLock mu(art::Thread::Current(), geo_dist_rng_lock_);
31   size_t nsample = geo_dist_(rng_);
32   if (nsample == 0) {
33     // Geometric distribution results in +ve values but could have zero.
34     // In the zero case, return 1.
35     nsample = 1;
36   }
37   return nsample;
38 }
39 
PickAndAdjustNextSample(size_t sample_adjust_bytes)40 size_t HeapSampler::PickAndAdjustNextSample(size_t sample_adjust_bytes) {
41   size_t bytes_until_sample;
42   if (GetSamplingInterval() == 1) {
43     bytes_until_sample = 1;
44     return bytes_until_sample;
45   }
46   bytes_until_sample = NextGeoDistRandSample();
47   VLOG(heap) << "JHP:PickAndAdjustNextSample, sample_adjust_bytes: "
48              << sample_adjust_bytes
49              << " bytes_until_sample: " << bytes_until_sample;
50   // Adjust the sample bytes
51   if (sample_adjust_bytes > 0 && bytes_until_sample > sample_adjust_bytes) {
52     bytes_until_sample -= sample_adjust_bytes;
53     VLOG(heap) << "JHP:PickAndAdjustNextSample, final bytes_until_sample: "
54                << bytes_until_sample;
55   }
56   return bytes_until_sample;
57 }
58 
59 // Report to Perfetto an allocation sample.
60 // Samples can only be reported after the allocation is done.
61 // Also bytes_until_sample can only be updated after the allocation and reporting is done.
62 // Thus next bytes_until_sample is previously calculated (before allocation) to be able to
63 // get the next tlab_size, but only saved/updated here.
ReportSample(art::mirror::Object * obj,size_t allocation_size)64 void HeapSampler::ReportSample(art::mirror::Object* obj, size_t allocation_size) {
65   VLOG(heap) << "JHP:***Report Perfetto Allocation: alloc_size: " << allocation_size;
66   uint64_t perf_alloc_id = reinterpret_cast<uint64_t>(obj);
67   VLOG(heap) << "JHP:***Report Perfetto Allocation: obj: " << perf_alloc_id;
68 #ifdef ART_TARGET_ANDROID
69   AHeapProfile_reportSample(perfetto_heap_id_, perf_alloc_id, allocation_size);
70 #endif
71 }
72 
73 // Check whether we should take a sample or not at this allocation and calculate the sample
74 // offset to use in the expand Tlab calculation. Thus the offset from current pos to the next
75 // sample.
76 // tlab_used = pos - start
GetSampleOffset(size_t alloc_size,size_t tlab_used,bool * take_sample,size_t * temp_bytes_until_sample)77 size_t HeapSampler::GetSampleOffset(size_t alloc_size,
78                                     size_t tlab_used,
79                                     bool* take_sample,
80                                     size_t* temp_bytes_until_sample) {
81   size_t exhausted_size = alloc_size + tlab_used;
82   VLOG(heap) << "JHP:GetSampleOffset: exhausted_size = " << exhausted_size;
83   // Note bytes_until_sample is used as an offset from the start point
84   size_t bytes_until_sample = *GetBytesUntilSample();
85   ssize_t diff = bytes_until_sample - exhausted_size;
86   VLOG(heap) << "JHP:GetSampleOffset: diff = " << diff << " bytes_until_sample = "
87              << bytes_until_sample;
88   if (diff <= 0) {
89     *take_sample = true;
90     // Compute a new bytes_until_sample
91     size_t sample_adj_bytes = -diff;
92     size_t next_bytes_until_sample = PickAndAdjustNextSample(sample_adj_bytes);
93     VLOG(heap) << "JHP:GetSampleOffset: Take sample, next_bytes_until_sample = "
94                << next_bytes_until_sample;
95     next_bytes_until_sample += tlab_used;
96     VLOG(heap) << "JHP:GetSampleOffset:Next sample offset = "
97                << (next_bytes_until_sample - tlab_used);
98     // This function is called before the actual allocation happens so we cannot update
99     // the bytes_until_sample till after the allocation happens, save it to temp which
100     // will be saved after the allocation by the calling function.
101     *temp_bytes_until_sample = next_bytes_until_sample;
102     return (next_bytes_until_sample - tlab_used);
103     // original bytes_until_sample, not offseted
104   } else {
105     *take_sample = false;
106     // The following 2 lines are used in the NonTlab case but have no effect on the
107     // Tlab case, because we will only use the temp_bytes_until_sample if the
108     // take_sample was true (after returning from this function in Tlab case in the
109     // SetBytesUntilSample).
110     size_t next_bytes_until_sample = bytes_until_sample - alloc_size;
111     *temp_bytes_until_sample = next_bytes_until_sample;
112     VLOG(heap) << "JHP:GetSampleOffset: No sample, next_bytes_until_sample= "
113                << next_bytes_until_sample << " alloc= " << alloc_size;
114     return diff;
115   }
116 }
117 
118 // We are tracking the location of samples from the start location of the Tlab
119 // We need to adjust how to calculate the sample position in cases where ResetTlab.
120 // Adjustment is the new reference position adjustment, usually the new pos-start.
AdjustSampleOffset(size_t adjustment)121 void HeapSampler::AdjustSampleOffset(size_t adjustment) {
122   size_t* bytes_until_sample = GetBytesUntilSample();
123   size_t cur_bytes_until_sample = *bytes_until_sample;
124   if (cur_bytes_until_sample < adjustment) {
125     VLOG(heap) << "JHP:AdjustSampleOffset:No Adjustment";
126     return;
127   }
128   size_t next_bytes_until_sample = cur_bytes_until_sample - adjustment;
129   *bytes_until_sample = next_bytes_until_sample;
130   VLOG(heap) << "JHP:AdjustSampleOffset: adjustment = " << adjustment
131              << " next_bytes_until_sample = " << next_bytes_until_sample;
132 }
133 
IsEnabled()134 bool HeapSampler::IsEnabled() {
135   return enabled_.load(std::memory_order_acquire);
136 }
137 
GetSamplingInterval()138 int HeapSampler::GetSamplingInterval() {
139   return p_sampling_interval_.load(std::memory_order_acquire);
140 }
141 
SetSamplingInterval(int sampling_interval)142 void HeapSampler::SetSamplingInterval(int sampling_interval) {
143   // Make sure that rng_ and geo_dist are thread safe by acquiring a lock to access.
144   art::MutexLock mu(art::Thread::Current(), geo_dist_rng_lock_);
145   p_sampling_interval_.store(sampling_interval, std::memory_order_release);
146   geo_dist_.param(std::geometric_distribution<size_t>::param_type(1.0/p_sampling_interval_));
147 }
148 
149 }  // namespace art
150