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