• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef PANDA_PROFILING_DATA_H
17 #define PANDA_PROFILING_DATA_H
18 
19 #include "macros.h"
20 #include <array>
21 #include <atomic>
22 #include <numeric>
23 
24 #include <cstdint>
25 
26 #include "runtime/include/mem/panda_containers.h"
27 
28 namespace panda {
29 
30 class Class;
31 
32 class CallSiteInlineCache {
33 public:
34     static constexpr size_t CLASSES_COUNT = 4;
35     static constexpr uintptr_t MEGAMORPHIC_FLAG = static_cast<uintptr_t>(-1);
36 
From(void * mem,PandaVector<uint32_t> vcalls)37     static Span<CallSiteInlineCache> From(void *mem, PandaVector<uint32_t> vcalls)
38     {
39         auto inline_caches = reinterpret_cast<CallSiteInlineCache *>(mem);
40         auto ics = Span<CallSiteInlineCache>(inline_caches, vcalls.size());
41         for (size_t i = 0; i < vcalls.size(); i++) {
42             ics[i].Init(vcalls[i]);
43         }
44         return ics;
45     }
46 
Init(uintptr_t pc)47     void Init(uintptr_t pc)
48     {
49         SetBytecodePc(pc);
50         std::fill(classes_.begin(), classes_.end(), nullptr);
51     }
52 
UpdateInlineCaches(Class * cls)53     void UpdateInlineCaches(Class *cls)
54     {
55         for (uint32_t i = 0; i < classes_.size();) {
56             auto *class_atomic = reinterpret_cast<std::atomic<Class *> *>(&(classes_[i]));
57             // Atomic with acquire order reason: data race with classes_ with dependecies on reads after the load which
58             // should become visible
59             auto stored_class = class_atomic->load(std::memory_order_acquire);
60             // Check that the call is already megamorphic
61             if (i == 0 && stored_class == reinterpret_cast<Class *>(MEGAMORPHIC_FLAG)) {
62                 return;
63             }
64             if (stored_class == cls) {
65                 return;
66             }
67             if (stored_class == nullptr) {
68                 if (!class_atomic->compare_exchange_weak(stored_class, cls, std::memory_order_acq_rel)) {
69                     continue;
70                 }
71                 return;
72             }
73             i++;
74         }
75         // Megamorphic call, disable devirtualization for this call site.
76         auto *class_atomic = reinterpret_cast<std::atomic<Class *> *>(&(classes_[0]));
77         // Atomic with release order reason: data race with classes_ with dependecies on writes before the store which
78         // should become visible acquire
79         class_atomic->store(reinterpret_cast<Class *>(MEGAMORPHIC_FLAG), std::memory_order_release);
80     }
81 
GetBytecodePc()82     auto GetBytecodePc() const
83     {
84         // Atomic with acquire order reason: data race with bytecode_pc_ with dependecies on reads after the load which
85         // should become visible
86         return bytecode_pc_.load(std::memory_order_acquire);
87     }
88 
SetBytecodePc(uintptr_t pc)89     void SetBytecodePc(uintptr_t pc)
90     {
91         // Atomic with release order reason: data race with bytecode_pc_ with dependecies on writes before the store
92         // which should become visible acquire
93         bytecode_pc_.store(pc, std::memory_order_release);
94     }
95 
GetClassesCopy()96     std::vector<Class *> GetClassesCopy()
97     {
98         std::vector<Class *> result;
99         for (uint32_t i = 0; i < classes_.size();) {
100             auto *class_atomic = reinterpret_cast<std::atomic<Class *> const *>(&(classes_[i]));
101             // Atomic with acquire order reason: data race with classes_ with dependecies on reads after the load which
102             // should become visible
103             auto stored_class = class_atomic->load(std::memory_order_acquire);
104             if (stored_class != nullptr) {
105                 result.push_back(stored_class);
106             }
107             i++;
108         }
109         return result;
110     }
111 
GetClassesCount()112     size_t GetClassesCount() const
113     {
114         size_t classes_count = 0;
115         for (uint32_t i = 0; i < classes_.size();) {
116             auto *class_atomic = reinterpret_cast<std::atomic<Class *> const *>(&(classes_[i]));
117             // Atomic with acquire order reason: data race with classes_ with dependecies on reads after the load which
118             // should become visible
119             auto stored_class = class_atomic->load(std::memory_order_acquire);
120             if (stored_class != nullptr) {
121                 classes_count++;
122             }
123             i++;
124         }
125         return classes_count;
126     }
127 
IsMegamorphic(Class * cls)128     static bool IsMegamorphic(Class *cls)
129     {
130         auto *class_atomic = reinterpret_cast<std::atomic<Class *> *>(&cls);
131         // Atomic with acquire order reason: data race with classes_ with dependecies on reads after the load which
132         // should become visible
133         return class_atomic->load(std::memory_order_acquire) == reinterpret_cast<Class *>(MEGAMORPHIC_FLAG);
134     }
135 
136 private:
137     std::atomic_uintptr_t bytecode_pc_;
138     std::array<Class *, CLASSES_COUNT> classes_ {};
139 };
140 
141 class BranchData {
142 public:
From(void * mem,PandaVector<uint32_t> branches)143     static Span<BranchData> From(void *mem, PandaVector<uint32_t> branches)
144     {
145         auto branch_data = reinterpret_cast<BranchData *>(mem);
146         auto span = Span<BranchData>(branch_data, branches.size());
147         for (size_t i = 0; i < branches.size(); i++) {
148             span[i].Init(branches[i]);
149         }
150         return span;
151     }
152 
Init(uintptr_t pc)153     void Init(uintptr_t pc)
154     {
155         // Atomic with relaxed order reason: data race with pc_
156         pc_.store(pc, std::memory_order_relaxed);
157         // Atomic with relaxed order reason: data race with taken_counter_
158         taken_counter_.store(0, std::memory_order_relaxed);
159         // Atomic with relaxed order reason: data race with not_taken_counter_
160         not_taken_counter_.store(0, std::memory_order_relaxed);
161     }
162 
GetPc()163     uintptr_t GetPc() const
164     {
165         // Atomic with relaxed order reason: data race with pc_
166         return pc_.load(std::memory_order_relaxed);
167     }
168 
GetTakenCounter()169     int64_t GetTakenCounter() const
170     {
171         // Atomic with relaxed order reason: data race with taken_counter_
172         return taken_counter_.load(std::memory_order_relaxed);
173     }
174 
GetNotTakenCounter()175     int64_t GetNotTakenCounter() const
176     {
177         // Atomic with relaxed order reason: data race with not_taken_counter_
178         return not_taken_counter_.load(std::memory_order_relaxed);
179     }
180 
IncrementTaken()181     void IncrementTaken()
182     {
183         // Atomic with relaxed order reason: data race with taken_counter_
184         taken_counter_.fetch_add(1, std::memory_order_relaxed);
185     }
186 
IncrementNotTaken()187     void IncrementNotTaken()
188     {
189         // Atomic with relaxed order reason: data race with not_taken_counter_
190         not_taken_counter_.fetch_add(1, std::memory_order_relaxed);
191     }
192 
193 private:
194     std::atomic_uintptr_t pc_;
195     std::atomic_llong taken_counter_;
196     std::atomic_llong not_taken_counter_;
197 };
198 
199 class ProfilingData {
200 public:
ProfilingData(Span<CallSiteInlineCache> inline_caches,Span<BranchData> branch_data)201     explicit ProfilingData(Span<CallSiteInlineCache> inline_caches, Span<BranchData> branch_data)
202         : inline_caches_(inline_caches), branch_data_(branch_data)
203     {
204     }
205 
GetInlineCaches()206     Span<CallSiteInlineCache> GetInlineCaches()
207     {
208         return inline_caches_;
209     }
210 
FindInlineCache(uintptr_t pc)211     CallSiteInlineCache *FindInlineCache(uintptr_t pc)
212     {
213         auto ics = GetInlineCaches();
214         auto ic = std::lower_bound(ics.begin(), ics.end(), pc,
215                                    [](const auto &a, uintptr_t counter) { return a.GetBytecodePc() < counter; });
216         return (ic == ics.end() || ic->GetBytecodePc() != pc) ? nullptr : &*ic;
217     }
218 
UpdateInlineCaches(uintptr_t pc,Class * cls)219     void UpdateInlineCaches(uintptr_t pc, Class *cls)
220     {
221         auto ic = FindInlineCache(pc);
222         ASSERT(ic != nullptr);
223         if (ic != nullptr) {
224             ic->UpdateInlineCaches(cls);
225         }
226     }
227 
UpdateBranchTaken(uintptr_t pc)228     void UpdateBranchTaken(uintptr_t pc)
229     {
230         auto branch = FindBranchData(pc);
231         ASSERT(branch != nullptr);
232         branch->IncrementTaken();
233     }
234 
UpdateBranchNotTaken(uintptr_t pc)235     void UpdateBranchNotTaken(uintptr_t pc)
236     {
237         auto branch = FindBranchData(pc);
238         ASSERT(branch != nullptr);
239         branch->IncrementNotTaken();
240     }
241 
GetBranchTakenCounter(uintptr_t pc)242     int64_t GetBranchTakenCounter(uintptr_t pc)
243     {
244         auto branch = FindBranchData(pc);
245         ASSERT(branch != nullptr);
246         return branch->GetTakenCounter();
247     }
248 
GetBranchNotTakenCounter(uintptr_t pc)249     int64_t GetBranchNotTakenCounter(uintptr_t pc)
250     {
251         auto branch = FindBranchData(pc);
252         ASSERT(branch != nullptr);
253         return branch->GetNotTakenCounter();
254     }
255 
256 private:
FindBranchData(uintptr_t from_pc)257     BranchData *FindBranchData(uintptr_t from_pc)
258     {
259         auto it = std::lower_bound(branch_data_.begin(), branch_data_.end(), from_pc,
260                                    [](const auto &a, uintptr_t counter) { return a.GetPc() < counter; });
261         if (it == branch_data_.end() || it->GetPc() != from_pc) {
262             return nullptr;
263         }
264 
265         return &*it;
266     }
267 
268     Span<CallSiteInlineCache> inline_caches_;
269     Span<BranchData> branch_data_;
270 };
271 
272 }  // namespace panda
273 
274 #endif  // PANDA_PROFILING_DATA_H
275