1/*
2 * Copyright (c) 2023-2024 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#include "allocation_inspector.h"
17
18#include "ecmascript/base/number_helper.h"
19#include "ecmascript/dfx/hprof/heap_sampling.h"
20
21namespace panda::ecmascript {
22void AllocationInspector::Step([[maybe_unused]] Address object, [[maybe_unused]] size_t size)
23{
24#ifdef ECMASCRIPT_SUPPORT_HEAPSAMPLING
25    ASSERT(!heap_->GetJSThread()->GetGcState());
26    if (object) {
27        profiler_->ImplementSampling(object, size);
28    }
29#endif
30}
31
32// Sampling follows a Poisson distribution.
33// According to the relationship between Poisson distribution and exponential probability distribution,
34// the stepSize follows the exponential probability distribution with parameter λ = 1/rate where rate
35// is average bytes between samples.
36// Let random be a uniformly distributed random number between 0 and 1, beacuse Inverse transform sampling
37// can generate exponential probability distribution with a uniform distribution, so nextSample = (- ln random) / λ.
38size_t AllocationInspector::GetNextStepSize()
39{
40    double random = base::RandomGenerator::NextDouble();
41    double stepSize = -std::log(random) * rate_;
42    return stepSize < TaggedObject::TaggedObjectSize()
43        ? TaggedObject::TaggedObjectSize()
44        : (stepSize > INT_MAX ? INT_MAX : static_cast<size_t>(stepSize));
45}
46
47void AllocationCounter::AddAllocationInspector(AllocationInspector *inspector)
48{
49    ASSERT(inspector != nullptr);
50    size_t stepSize = inspector->GetNextStepSize();
51    size_t nextCounter = currentCounter_ + stepSize;
52    inspector_ = inspector;
53
54    ASSERT(currentCounter_ == nextCounter_);
55    nextCounter_ = nextCounter;
56}
57
58void AllocationCounter::ClearAllocationInspector()
59{
60    inspector_ = nullptr;
61    currentCounter_ = 0;
62    nextCounter_ = 0;
63}
64
65void AllocationCounter::AdvanceAllocationInspector(size_t allocated)
66{
67    if (!IsActive()) {
68        return;
69    }
70    ASSERT(allocated < nextCounter_ - currentCounter_);
71    currentCounter_ += allocated;
72}
73
74void AllocationCounter::InvokeAllocationInspector(Address object, size_t objectSize, size_t alignedObjectSize)
75{
76    if (!IsActive()) {
77        return;
78    }
79
80    ASSERT(alignedObjectSize >= nextCounter_ - currentCounter_);
81    ASSERT(object != 0);
82    {
83        DISALLOW_GARBAGE_COLLECTION;
84        inspector_->Step(object, objectSize);
85    }
86    size_t nextStepSize = inspector_->GetNextStepSize();
87    // because next allocate or advance can add currentCounter_ to real allocated size,
88    // so need add alignedObjectSize here.
89    nextCounter_ = currentCounter_ + alignedObjectSize + nextStepSize;
90}
91} // namespace panda::ecmascript