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