• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 ark {
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 inlineCaches = reinterpret_cast<CallSiteInlineCache *>(mem);
40         auto ics = Span<CallSiteInlineCache>(inlineCaches, 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 *classAtomic = 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 storedClass = classAtomic->load(std::memory_order_acquire);
60             // Check that the call is already megamorphic
61             if (i == 0 && storedClass == reinterpret_cast<Class *>(MEGAMORPHIC_FLAG)) {
62                 return;
63             }
64             if (storedClass == cls) {
65                 return;
66             }
67             if (storedClass == nullptr) {
68                 if (!classAtomic->compare_exchange_weak(storedClass, 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 *classAtomic = 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         classAtomic->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 bytecodePc_.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         bytecodePc_.store(pc, std::memory_order_release);
94     }
95 
GetClassesCopy()96     std::vector<Class *> GetClassesCopy() const
97     {
98         std::vector<Class *> result;
99         for (uint32_t i = 0; i < classes_.size();) {
100             auto *classAtomic = 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 storedClass = classAtomic->load(std::memory_order_acquire);
104             if (storedClass != nullptr) {
105                 result.push_back(storedClass);
106             }
107             i++;
108         }
109         return result;
110     }
111 
GetClassesCount()112     size_t GetClassesCount() const
113     {
114         size_t classesCount = 0;
115         for (uint32_t i = 0; i < classes_.size();) {
116             auto *classAtomic = 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 storedClass = classAtomic->load(std::memory_order_acquire);
120             if (storedClass != nullptr) {
121                 classesCount++;
122             }
123             i++;
124         }
125         return classesCount;
126     }
127 
IsMegamorphic(Class * cls)128     static bool IsMegamorphic(Class *cls)
129     {
130         auto *classAtomic = 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 classAtomic->load(std::memory_order_acquire) == reinterpret_cast<Class *>(MEGAMORPHIC_FLAG);
134     }
135 
136 private:
137     std::atomic_uintptr_t bytecodePc_;
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 branchData = reinterpret_cast<BranchData *>(mem);
146         auto span = Span<BranchData>(branchData, branches.size());
147         for (size_t i = 0; i < branches.size(); i++) {
148             span[i].Init(branches[i]);
149         }
150         return span;
151     }
152 
153     void Init(uintptr_t pc, uint64_t taken = 0, uint64_t notTaken = 0)
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         takenCounter_.store(taken, std::memory_order_relaxed);
159         // Atomic with relaxed order reason: data race with not_taken_counter_
160         notTakenCounter_.store(notTaken, 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 takenCounter_.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 notTakenCounter_.load(std::memory_order_relaxed);
179     }
180 
IncrementTaken()181     void IncrementTaken()
182     {
183         // Atomic with relaxed order reason: data race with taken_counter_
184         takenCounter_.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         notTakenCounter_.fetch_add(1, std::memory_order_relaxed);
191     }
192 
193 private:
194     std::atomic_uintptr_t pc_;
195     std::atomic_llong takenCounter_;
196     std::atomic_llong notTakenCounter_;
197 };
198 
199 class ThrowData {
200 public:
From(void * mem,PandaVector<uint32_t> throws)201     static Span<ThrowData> From(void *mem, PandaVector<uint32_t> throws)
202     {
203         auto throwData = reinterpret_cast<ThrowData *>(mem);
204         auto span = Span<ThrowData>(throwData, throws.size());
205         for (size_t i = 0; i < throws.size(); i++) {
206             span[i].Init(throws[i]);
207         }
208         return span;
209     }
210 
211     void Init(uintptr_t pc, uint64_t taken = 0)
212     {
213         // Atomic with relaxed order reason: data race with pc_
214         pc_.store(pc, std::memory_order_relaxed);
215         // Atomic with relaxed order reason: data race with taken_counter_
216         takenCounter_.store(taken, std::memory_order_relaxed);
217     }
218 
GetPc()219     uintptr_t GetPc() const
220     {
221         // Atomic with relaxed order reason: data race with pc_
222         return pc_.load(std::memory_order_relaxed);
223     }
224 
GetTakenCounter()225     int64_t GetTakenCounter() const
226     {
227         // Atomic with relaxed order reason: data race with taken_counter_
228         return takenCounter_.load(std::memory_order_relaxed);
229     }
230 
IncrementTaken()231     void IncrementTaken()
232     {
233         // Atomic with relaxed order reason: data race with taken_counter_
234         takenCounter_.fetch_add(1, std::memory_order_relaxed);
235     }
236 
237 private:
238     std::atomic_uintptr_t pc_;
239     std::atomic_llong takenCounter_;
240 };
241 
242 class ProfilingData {
243 public:
ProfilingData(Span<CallSiteInlineCache> inlineCaches,Span<BranchData> branchData,Span<ThrowData> throwData)244     explicit ProfilingData(Span<CallSiteInlineCache> inlineCaches, Span<BranchData> branchData,
245                            Span<ThrowData> throwData)
246         : inlineCaches_(inlineCaches), branchData_(branchData), throwData_(throwData)
247     {
248     }
249 
GetInlineCaches()250     Span<CallSiteInlineCache> GetInlineCaches() const
251     {
252         return inlineCaches_;
253     }
254 
GetBranchData()255     Span<BranchData> GetBranchData() const
256     {
257         return branchData_;
258     }
259 
GetThrowData()260     Span<ThrowData> GetThrowData() const
261     {
262         return throwData_;
263     }
264 
FindInlineCache(uintptr_t pc)265     CallSiteInlineCache *FindInlineCache(uintptr_t pc)
266     {
267         auto ics = GetInlineCaches();
268         auto ic = std::lower_bound(ics.begin(), ics.end(), pc,
269                                    [](const auto &a, uintptr_t counter) { return a.GetBytecodePc() < counter; });
270         return (ic == ics.end() || ic->GetBytecodePc() != pc) ? nullptr : &*ic;
271     }
272 
UpdateInlineCaches(uintptr_t pc,Class * cls)273     void UpdateInlineCaches(uintptr_t pc, Class *cls)
274     {
275         auto ic = FindInlineCache(pc);
276         ASSERT(ic != nullptr);
277         if (ic != nullptr) {
278             ic->UpdateInlineCaches(cls);
279             isUpdated_ = true;
280         }
281     }
282 
UpdateBranchTaken(uintptr_t pc)283     void UpdateBranchTaken(uintptr_t pc)
284     {
285         auto branch = FindBranchData(pc);
286         ASSERT(branch != nullptr);
287         branch->IncrementTaken();
288         isUpdated_ = true;
289     }
290 
UpdateBranchNotTaken(uintptr_t pc)291     void UpdateBranchNotTaken(uintptr_t pc)
292     {
293         auto branch = FindBranchData(pc);
294         ASSERT(branch != nullptr);
295         branch->IncrementNotTaken();
296         isUpdated_ = true;
297     }
298 
GetBranchTakenCounter(uintptr_t pc)299     int64_t GetBranchTakenCounter(uintptr_t pc)
300     {
301         auto branch = FindBranchData(pc);
302         ASSERT(branch != nullptr);
303         return branch->GetTakenCounter();
304     }
305 
GetBranchNotTakenCounter(uintptr_t pc)306     int64_t GetBranchNotTakenCounter(uintptr_t pc)
307     {
308         auto branch = FindBranchData(pc);
309         ASSERT(branch != nullptr);
310         return branch->GetNotTakenCounter();
311     }
312 
UpdateThrowTaken(uintptr_t pc)313     void UpdateThrowTaken(uintptr_t pc)
314     {
315         auto thr0w = FindThrowData(pc);
316         ASSERT(thr0w != nullptr);
317         thr0w->IncrementTaken();
318         isUpdated_ = true;
319     }
320 
IsUpdateSinceLastSave()321     bool IsUpdateSinceLastSave() const
322     {
323         return isUpdated_;
324     }
325 
DataSaved()326     void DataSaved()
327     {
328         isUpdated_ = false;
329     }
330 
GetThrowTakenCounter(uintptr_t pc)331     int64_t GetThrowTakenCounter(uintptr_t pc)
332     {
333         auto thr0w = FindThrowData(pc);
334         ASSERT(thr0w != nullptr);
335         return thr0w->GetTakenCounter();
336     }
337 
338     template <typename Callback>
Make(mem::InternalAllocatorPtr allocator,size_t nInlineCaches,size_t nBranches,size_t nThrows,Callback && callback)339     static ProfilingData *Make(mem::InternalAllocatorPtr allocator, size_t nInlineCaches, size_t nBranches,
340                                size_t nThrows, Callback &&callback)
341     {
342         auto vcallDataOffset = RoundUp(sizeof(ProfilingData), alignof(CallSiteInlineCache));
343         auto branchesDataOffset =
344             RoundUp(vcallDataOffset + sizeof(CallSiteInlineCache) * nInlineCaches, alignof(BranchData));
345         auto throwsDataOffset = RoundUp(branchesDataOffset + sizeof(BranchData) * nBranches, alignof(ThrowData));
346         auto data = allocator->Alloc(throwsDataOffset + sizeof(ThrowData) * nThrows);
347 
348         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
349         auto vcallsMem = reinterpret_cast<uint8_t *>(data) + vcallDataOffset;
350         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
351         auto branchesMem = reinterpret_cast<uint8_t *>(data) + branchesDataOffset;
352         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
353         auto throwsMem = reinterpret_cast<uint8_t *>(data) + throwsDataOffset;
354         return callback(data, vcallsMem, branchesMem, throwsMem);
355     }
356 
357 private:
FindBranchData(uintptr_t fromPc)358     BranchData *FindBranchData(uintptr_t fromPc)
359     {
360         auto it = std::lower_bound(branchData_.begin(), branchData_.end(), fromPc,
361                                    [](const auto &a, uintptr_t counter) { return a.GetPc() < counter; });
362         if (it == branchData_.end() || it->GetPc() != fromPc) {
363             return nullptr;
364         }
365 
366         return &*it;
367     }
FindThrowData(uintptr_t fromPc)368     ThrowData *FindThrowData(uintptr_t fromPc)
369     {
370         if (throwData_.empty()) {
371             return nullptr;
372         }
373         auto it = std::lower_bound(throwData_.begin(), throwData_.end(), fromPc,
374                                    [](const auto &a, uintptr_t counter) { return a.GetPc() < counter; });
375         if (it == throwData_.end() || it->GetPc() != fromPc) {
376             return nullptr;
377         }
378 
379         return &*it;
380     }
381 
382     Span<CallSiteInlineCache> inlineCaches_;
383     Span<BranchData> branchData_;
384     Span<ThrowData> throwData_;
385     std::atomic_bool isUpdated_ {true};
386 };
387 
388 }  // namespace ark
389 
390 #endif  // PANDA_PROFILING_DATA_H
391