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