1800b99b8Sopenharmony_ci/*
2800b99b8Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
3800b99b8Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4800b99b8Sopenharmony_ci * you may not use this file except in compliance with the License.
5800b99b8Sopenharmony_ci * You may obtain a copy of the License at
6800b99b8Sopenharmony_ci *
7800b99b8Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0
8800b99b8Sopenharmony_ci *
9800b99b8Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10800b99b8Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11800b99b8Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12800b99b8Sopenharmony_ci * See the License for the specific language governing permissions and
13800b99b8Sopenharmony_ci * limitations under the License.
14800b99b8Sopenharmony_ci */
15800b99b8Sopenharmony_ci#include "unique_stack_table.h"
16800b99b8Sopenharmony_ci
17800b99b8Sopenharmony_ci#include <sys/mman.h>
18800b99b8Sopenharmony_ci#include <sys/prctl.h>
19800b99b8Sopenharmony_ci#include <securec.h>
20800b99b8Sopenharmony_ci#include "dfx_log.h"
21800b99b8Sopenharmony_cinamespace OHOS {
22800b99b8Sopenharmony_cinamespace HiviewDFX {
23800b99b8Sopenharmony_ciUniqueStackTable* UniqueStackTable::Instance()
24800b99b8Sopenharmony_ci{
25800b99b8Sopenharmony_ci    static UniqueStackTable* instance = new UniqueStackTable();
26800b99b8Sopenharmony_ci    return instance;
27800b99b8Sopenharmony_ci}
28800b99b8Sopenharmony_ci
29800b99b8Sopenharmony_cibool UniqueStackTable::Init()
30800b99b8Sopenharmony_ci{
31800b99b8Sopenharmony_ci    std::lock_guard<std::mutex> guard(stackTableMutex_);
32800b99b8Sopenharmony_ci    // index 0 for reserved
33800b99b8Sopenharmony_ci    if (tableBufMMap_ != nullptr) {
34800b99b8Sopenharmony_ci        return true;
35800b99b8Sopenharmony_ci    }
36800b99b8Sopenharmony_ci    availableIndex_ = 1;
37800b99b8Sopenharmony_ci    totalNodes_ = ((tableSize_ / sizeof(Node)) >> 1) << 1; // make it even.
38800b99b8Sopenharmony_ci    if (totalNodes_ > MAX_NODES_CNT) {
39800b99b8Sopenharmony_ci        DFXLOGW("Hashtable size limit, initial value too large!\n");
40800b99b8Sopenharmony_ci        return false;
41800b99b8Sopenharmony_ci    }
42800b99b8Sopenharmony_ci
43800b99b8Sopenharmony_ci    availableNodes_ = totalNodes_;
44800b99b8Sopenharmony_ci    if (availableNodes_ == 0) {
45800b99b8Sopenharmony_ci        return false;
46800b99b8Sopenharmony_ci    }
47800b99b8Sopenharmony_ci    hashModulus_ = availableNodes_ - 1;
48800b99b8Sopenharmony_ci    hashStep_ = (totalNodes_ / (deconflictTimes_ * 2 + 1)); // 2 : double times
49800b99b8Sopenharmony_ci    auto retBufMMap = mmap(NULL, tableSize_, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
50800b99b8Sopenharmony_ci    if (retBufMMap == MAP_FAILED) {
51800b99b8Sopenharmony_ci        DFXLOGW("Failed to mmap!\n");
52800b99b8Sopenharmony_ci        return false;
53800b99b8Sopenharmony_ci    }
54800b99b8Sopenharmony_ci    tableBufMMap_ = retBufMMap;
55800b99b8Sopenharmony_ci    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, tableBufMMap_, tableSize_, "async_stack_table");
56800b99b8Sopenharmony_ci    DFXLOGD(
57800b99b8Sopenharmony_ci        "Init totalNodes_: %{public}u, availableNodes_: %{public}u, availableIndex_: %{public}u \
58800b99b8Sopenharmony_ci        hashStep_: %{public}" PRIu64 ", hashModulus_: %{public}u",
59800b99b8Sopenharmony_ci        totalNodes_, availableNodes_, availableIndex_, hashStep_, hashModulus_);
60800b99b8Sopenharmony_ci    return true;
61800b99b8Sopenharmony_ci}
62800b99b8Sopenharmony_ci
63800b99b8Sopenharmony_cibool UniqueStackTable::Resize()
64800b99b8Sopenharmony_ci{
65800b99b8Sopenharmony_ci    std::lock_guard<std::mutex> guard(stackTableMutex_);
66800b99b8Sopenharmony_ci    if (tableBufMMap_ == nullptr) {
67800b99b8Sopenharmony_ci        DFXLOGW("[%{public}d]: Hashtable not exist, fatal error!", __LINE__);
68800b99b8Sopenharmony_ci        return false;
69800b99b8Sopenharmony_ci    }
70800b99b8Sopenharmony_ci
71800b99b8Sopenharmony_ci    uint32_t oldNumNodes = totalNodes_;
72800b99b8Sopenharmony_ci    DFXLOGW("Before resize, totalNodes_: %{public}u, availableNodes_: %{public}u, " \
73800b99b8Sopenharmony_ci        "availableIndex_: %{public}u  hashStep_: %{public}" PRIu64 "",
74800b99b8Sopenharmony_ci        totalNodes_, availableNodes_, availableIndex_, hashStep_);
75800b99b8Sopenharmony_ci
76800b99b8Sopenharmony_ci    if ((totalNodes_ << RESIZE_MULTIPLE) > MAX_NODES_CNT) {
77800b99b8Sopenharmony_ci        DFXLOGW("Hashtable size limit, resize failed current cnt: %{public}u, max cnt: %{public}u",
78800b99b8Sopenharmony_ci            totalNodes_,
79800b99b8Sopenharmony_ci            MAX_NODES_CNT);
80800b99b8Sopenharmony_ci        return false;
81800b99b8Sopenharmony_ci    }
82800b99b8Sopenharmony_ci
83800b99b8Sopenharmony_ci    uint32_t newtableSize = tableSize_ << RESIZE_MULTIPLE;
84800b99b8Sopenharmony_ci    void* newTableBuf = mmap(NULL, newtableSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
85800b99b8Sopenharmony_ci    if (newTableBuf == MAP_FAILED) {
86800b99b8Sopenharmony_ci        return false;
87800b99b8Sopenharmony_ci    }
88800b99b8Sopenharmony_ci    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, newTableBuf, newtableSize, "async_stack_table");
89800b99b8Sopenharmony_ci    if (memcpy_s(newTableBuf, newtableSize, tableBufMMap_, tableSize_) != 0) {
90800b99b8Sopenharmony_ci        DFXLOGE("Failed to memcpy table buffer");
91800b99b8Sopenharmony_ci    }
92800b99b8Sopenharmony_ci    munmap(tableBufMMap_, tableSize_);
93800b99b8Sopenharmony_ci    tableBufMMap_ = newTableBuf;
94800b99b8Sopenharmony_ci    tableSize_ = newtableSize;
95800b99b8Sopenharmony_ci    deconflictTimes_ += DECONFLICT_INCREASE_STEP;
96800b99b8Sopenharmony_ci    availableIndex_ += availableNodes_;
97800b99b8Sopenharmony_ci    totalNodes_ = ((newtableSize / sizeof(Node)) >> 1) << 1; // make it even.
98800b99b8Sopenharmony_ci    availableNodes_ = totalNodes_ - oldNumNodes;
99800b99b8Sopenharmony_ci    if (availableNodes_ == 0) {
100800b99b8Sopenharmony_ci        return false;
101800b99b8Sopenharmony_ci    }
102800b99b8Sopenharmony_ci    hashModulus_ = availableNodes_ - 1;
103800b99b8Sopenharmony_ci    hashStep_ = availableNodes_ / (deconflictTimes_ * 2 + 1); // 2: double times
104800b99b8Sopenharmony_ci    DFXLOGW("After resize, totalNodes_: %{public}u, availableNodes_: %{public}u, " \
105800b99b8Sopenharmony_ci        "availableIndex_: %{public}u hashStep_: %{public}" PRIu64 "",
106800b99b8Sopenharmony_ci        totalNodes_, availableNodes_, availableIndex_, hashStep_);
107800b99b8Sopenharmony_ci    return true;
108800b99b8Sopenharmony_ci}
109800b99b8Sopenharmony_ci
110800b99b8Sopenharmony_ciuint64_t UniqueStackTable::PutPcInSlot(uint64_t thisPc, uint64_t prevIdx)
111800b99b8Sopenharmony_ci{
112800b99b8Sopenharmony_ci    Node *tableHead = reinterpret_cast<Node *>(tableBufMMap_);
113800b99b8Sopenharmony_ci    if (hashModulus_ == 0) {
114800b99b8Sopenharmony_ci        DFXLOGW("The value of the hashModulus_ is zero\n");
115800b99b8Sopenharmony_ci        return 0;
116800b99b8Sopenharmony_ci    }
117800b99b8Sopenharmony_ci    uint64_t curPcIdx = (((thisPc >> 2) ^ (prevIdx << 4)) % hashModulus_) + availableIndex_;
118800b99b8Sopenharmony_ci    uint8_t currentDeconflictTimes_ = deconflictTimes_;
119800b99b8Sopenharmony_ci
120800b99b8Sopenharmony_ci    Node node;
121800b99b8Sopenharmony_ci    node.section.pc = thisPc;
122800b99b8Sopenharmony_ci    node.section.prevIdx = prevIdx;
123800b99b8Sopenharmony_ci    node.section.inKernel = !!(thisPc & PC_IN_KERNEL);
124800b99b8Sopenharmony_ci    while (currentDeconflictTimes_--) {
125800b99b8Sopenharmony_ci        Node* tableNode = (Node*)tableHead + curPcIdx;
126800b99b8Sopenharmony_ci
127800b99b8Sopenharmony_ci        // empty case
128800b99b8Sopenharmony_ci        if (tableNode->value == 0) {
129800b99b8Sopenharmony_ci            tableNode->value = node.value;
130800b99b8Sopenharmony_ci            usedSlots_.emplace_back(uint32_t(curPcIdx));
131800b99b8Sopenharmony_ci            return curPcIdx;
132800b99b8Sopenharmony_ci        }
133800b99b8Sopenharmony_ci        // already inserted
134800b99b8Sopenharmony_ci        if (tableNode->value == node.value) {
135800b99b8Sopenharmony_ci            return curPcIdx;
136800b99b8Sopenharmony_ci        }
137800b99b8Sopenharmony_ci
138800b99b8Sopenharmony_ci        curPcIdx += currentDeconflictTimes_ * hashStep_ + 1;
139800b99b8Sopenharmony_ci        if (availableNodes_ == 0) {
140800b99b8Sopenharmony_ci            return 0;
141800b99b8Sopenharmony_ci        }
142800b99b8Sopenharmony_ci        if (curPcIdx >= totalNodes_) {
143800b99b8Sopenharmony_ci            // make sure index 0 do not occupy
144800b99b8Sopenharmony_ci            curPcIdx -= (availableNodes_ - 1);
145800b99b8Sopenharmony_ci        }
146800b99b8Sopenharmony_ci    }
147800b99b8Sopenharmony_ci
148800b99b8Sopenharmony_ci    DFXLOGW("Collison unresolved, need resize, usedSlots_.size(): %{public}zu, curPcIdx: %{public}" PRIu64 "",
149800b99b8Sopenharmony_ci        usedSlots_.size(), curPcIdx);
150800b99b8Sopenharmony_ci    return 0;
151800b99b8Sopenharmony_ci}
152800b99b8Sopenharmony_ci
153800b99b8Sopenharmony_ci// todo add lock
154800b99b8Sopenharmony_ciuint64_t UniqueStackTable::PutPcsInTable(StackId *stackId, uintptr_t* pcs, size_t nr)
155800b99b8Sopenharmony_ci{
156800b99b8Sopenharmony_ci    if (!Init()) {
157800b99b8Sopenharmony_ci        DFXLOGW("init Hashtable failed, fatal error!");
158800b99b8Sopenharmony_ci        return 0;
159800b99b8Sopenharmony_ci    }
160800b99b8Sopenharmony_ci    std::lock_guard<std::mutex> guard(stackTableMutex_);
161800b99b8Sopenharmony_ci    int64_t reverseIndex = static_cast<int64_t>(nr);
162800b99b8Sopenharmony_ci    uint64_t prev = 0;
163800b99b8Sopenharmony_ci    while (--reverseIndex >= 0) {
164800b99b8Sopenharmony_ci        uint64_t pc = pcs[reverseIndex];
165800b99b8Sopenharmony_ci
166800b99b8Sopenharmony_ci        if (pc == 0) {
167800b99b8Sopenharmony_ci            continue;
168800b99b8Sopenharmony_ci        }
169800b99b8Sopenharmony_ci        prev = PutPcInSlot(pc, prev);
170800b99b8Sopenharmony_ci        if (prev == 0) {
171800b99b8Sopenharmony_ci            return 0;
172800b99b8Sopenharmony_ci        }
173800b99b8Sopenharmony_ci    }
174800b99b8Sopenharmony_ci
175800b99b8Sopenharmony_ci    stackId->section.id = prev;
176800b99b8Sopenharmony_ci    stackId->section.nr = static_cast<uint64_t>(nr);
177800b99b8Sopenharmony_ci    return prev;
178800b99b8Sopenharmony_ci}
179800b99b8Sopenharmony_ci
180800b99b8Sopenharmony_cisize_t UniqueStackTable::GetWriteSize()
181800b99b8Sopenharmony_ci{
182800b99b8Sopenharmony_ci    std::lock_guard<std::mutex> guard(stackTableMutex_);
183800b99b8Sopenharmony_ci    if (tableBufMMap_ == nullptr) {
184800b99b8Sopenharmony_ci        DFXLOGW("[%{public}d]: Hashtable not exist, fatal error!", __LINE__);
185800b99b8Sopenharmony_ci        return 0;
186800b99b8Sopenharmony_ci    }
187800b99b8Sopenharmony_ci    size_t size = 0;
188800b99b8Sopenharmony_ci    size += sizeof(pid_);
189800b99b8Sopenharmony_ci    size += sizeof(tableSize_);
190800b99b8Sopenharmony_ci    uint32_t usedNodes = usedSlots_.size();
191800b99b8Sopenharmony_ci    size += sizeof(usedNodes);
192800b99b8Sopenharmony_ci    size += usedNodes * sizeof(uint32_t); // key index
193800b99b8Sopenharmony_ci    size += usedNodes * sizeof(uint64_t); // node value
194800b99b8Sopenharmony_ci    return size;
195800b99b8Sopenharmony_ci}
196800b99b8Sopenharmony_ci
197800b99b8Sopenharmony_ciNode* UniqueStackTable::GetFrame(uint64_t stackId)
198800b99b8Sopenharmony_ci{
199800b99b8Sopenharmony_ci    Node *tableHead = reinterpret_cast<Node *>(tableBufMMap_);
200800b99b8Sopenharmony_ci    if (stackId >= totalNodes_) {
201800b99b8Sopenharmony_ci        // should not occur
202800b99b8Sopenharmony_ci        DFXLOGW("Failed to find frame by index: %{public}" PRIu64 "", stackId);
203800b99b8Sopenharmony_ci        return nullptr;
204800b99b8Sopenharmony_ci    }
205800b99b8Sopenharmony_ci
206800b99b8Sopenharmony_ci    return (Node *)&tableHead[stackId];
207800b99b8Sopenharmony_ci}
208800b99b8Sopenharmony_ci
209800b99b8Sopenharmony_cibool UniqueStackTable::GetPcsByStackId(StackId stackId, std::vector<uintptr_t>& pcs)
210800b99b8Sopenharmony_ci{
211800b99b8Sopenharmony_ci    std::lock_guard<std::mutex> guard(stackTableMutex_);
212800b99b8Sopenharmony_ci    if (tableBufMMap_ == nullptr) {
213800b99b8Sopenharmony_ci        DFXLOGW("Hashtable not exist, failed to find frame!");
214800b99b8Sopenharmony_ci        return false;
215800b99b8Sopenharmony_ci    }
216800b99b8Sopenharmony_ci    uint64_t nr = stackId.section.nr;
217800b99b8Sopenharmony_ci    uint64_t tailIdx = stackId.section.id;
218800b99b8Sopenharmony_ci
219800b99b8Sopenharmony_ci    Node *node = GetFrame(tailIdx);
220800b99b8Sopenharmony_ci    while (nr-- && node != nullptr) {
221800b99b8Sopenharmony_ci        pcs.push_back(
222800b99b8Sopenharmony_ci            node->section.inKernel ? (node->section.pc | KERNEL_PREFIX) : node->section.pc);
223800b99b8Sopenharmony_ci        if (node->section.prevIdx == HEAD_NODE_INDEX) {
224800b99b8Sopenharmony_ci            break;
225800b99b8Sopenharmony_ci        }
226800b99b8Sopenharmony_ci        node = GetFrame(node->section.prevIdx);
227800b99b8Sopenharmony_ci    }
228800b99b8Sopenharmony_ci    return true;
229800b99b8Sopenharmony_ci}
230800b99b8Sopenharmony_ci
231800b99b8Sopenharmony_cibool UniqueStackTable::ImportNode(uint32_t index, const Node& node)
232800b99b8Sopenharmony_ci{
233800b99b8Sopenharmony_ci    std::lock_guard<std::mutex> guard(stackTableMutex_);
234800b99b8Sopenharmony_ci    Node *tableHead = reinterpret_cast<Node *>(tableBufMMap_);
235800b99b8Sopenharmony_ci    if (index >= tableSize_) {
236800b99b8Sopenharmony_ci        return false;
237800b99b8Sopenharmony_ci    }
238800b99b8Sopenharmony_ci    tableHead[index].value = node.value;
239800b99b8Sopenharmony_ci    return true;
240800b99b8Sopenharmony_ci}
241800b99b8Sopenharmony_ci}
242800b99b8Sopenharmony_ci} // namespace OHOS
243