1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
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#define HILOG_TAG "RuntimeThread"
16
17#include "virtual_thread.h"
18
19#include <cinttypes>
20#include <iostream>
21#include <sstream>
22#if !is_mingw
23#include <sys/mman.h>
24#endif
25
26#include "common.h"
27#include "symbols_file.h"
28#include "utilities.h"
29#include "virtual_runtime.h"
30namespace OHOS {
31namespace Developtools {
32namespace NativeDaemon {
33#ifdef DEBUG_TIME
34bool VirtualThread::IsSorted() const
35{
36    for (std::size_t index = 1; index < maps_->size(); ++index) {
37        if ((*maps_)[index - 1].end > (*maps_)[index].begin) {
38            std::cout << "maps_ order error:\n"
39                      << "    " << (*maps_)[index - 1].begin << "-" << (*maps_)[index - 1].end
40                      << "    " << (*maps_)[index].begin << "-" << (*maps_)[index].end;
41            return false;
42        }
43    }
44    return true;
45}
46#endif
47
48const std::pair<std::shared_ptr<MemMaps>, uint32_t> VirtualThread::FindMemMapsByAddr(uint64_t addr) const
49{
50    return virtualruntime_->FindMap(addr);
51}
52
53const std::shared_ptr<DfxMap> VirtualThread::FindMapByAddr(uint64_t addr) const
54{
55    HLOGM("try found vaddr 0x%" PRIx64 " in maps %zu ", addr, maps_->size());
56    if (maps_->size() == 0) {
57        return nullptr;
58    }
59    if (maps_->front()->begin > addr) {
60        return nullptr;
61    }
62    if (maps_->back()->end <= addr) {
63        return nullptr;
64    }
65    constexpr int two {2};
66    std::size_t left {0};
67    std::size_t right {maps_->size()};
68    std::size_t mid = (right - left) / two + left;
69    while (left < right) {
70        if (addr < (*maps_)[mid]->end) {
71            right = mid;
72            mid = (right - left) / two + left;
73            continue;
74        }
75        if (addr >= (*maps_)[mid]->end) {
76            left = mid + 1;
77            mid = (right - left) / two + left;
78            continue;
79        }
80    }
81    if (addr >= (*maps_)[left]->begin and addr < (*maps_)[left]->end) {
82        if (left > 0) {
83            (*maps_)[left]->prevMap = (*maps_)[left - 1];
84        }
85        return (*maps_)[left];
86    }
87    return nullptr;
88}
89VirtualThread::VirtualThread(pid_t pid,
90                             pid_t tid,
91                             const std::unordered_map<std::string, std::unique_ptr<SymbolsFile>>& symbolsFiles,
92                             VirtualRuntime* runtime,
93                             bool parseFlag)
94    : pid_(pid), tid_(tid), symbolsFiles_(symbolsFiles), virtualruntime_(runtime)
95{
96    maps_ = &virtualruntime_->processMaps_;
97    if (parseFlag) {
98        if (virtualruntime_->processMaps_.size() == 0) {
99            this->ParseMap(virtualruntime_->processMaps_);
100        }
101    }
102
103    this->name_ = ReadThreadName(pid);
104    HLOGM("%d %d map from parent size is %zu", pid, tid, maps_->size());
105}
106
107std::string VirtualThread::ReadThreadName(pid_t tid)
108{
109    std::string comm = ReadFileToString(StringPrintf("/proc/%d/comm", tid)).c_str();
110    comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end());
111    comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end());
112    return comm;
113}
114
115const std::shared_ptr<DfxMap> VirtualThread::FindMapByFileInfo(const std::string name, uint64_t offset) const
116{
117    for (auto map : *maps_) {
118        if (name != map->name) {
119            continue;
120        }
121        // check begin and length
122        if (offset >= map->offset && (offset - map->offset) < (map->end - map->begin)) {
123            HLOGMMM("found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64
124                    " pageoffset 0x%" PRIx64 ")  from %s",
125                    offset, map->begin, map->end, map->offset, map->name.c_str());
126            return map;
127        }
128    }
129    HLOGM("NOT found offset 0x%" PRIx64 " in maps %zu ", offset, maps_->size());
130    return nullptr;
131}
132
133SymbolsFile *VirtualThread::FindSymbolsFileByMap(std::shared_ptr<DfxMap> inMap) const
134{
135    auto search = symbolsFiles_.find(inMap->name);
136    if (search != symbolsFiles_.end()) {
137        auto& symbolsFile = search->second;
138        HLOGM("found symbol for map '%s'", inMap->name.c_str());
139        symbolsFile->LoadDebugInfo(inMap);
140        return symbolsFile.get();
141    }
142#ifdef DEBUG_MISS_SYMBOL
143    if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), inMap->name) ==
144        missedSymbolFile_.end()) {
145        missedSymbolFile_.emplace_back(inMap->name);
146        HLOGW("NOT found symbol for map '%s'", inMap->name.c_str());
147        for (const auto &file : symbolsFiles_) {
148            HLOGW(" we have '%s'", file->filePath_.c_str());
149        }
150    }
151#endif
152    return nullptr;
153}
154
155SymbolsFile *VirtualThread::FindSymbolsFileByName(const std::string &name) const
156{
157    auto search = symbolsFiles_.find(name);
158    if (search != symbolsFiles_.end()) {
159        auto& symbolsFile = search->second;
160        HLOGM("found symbol for map '%s'", name.c_str());
161        symbolsFile->LoadDebugInfo();
162        return symbolsFile.get();
163    }
164#ifdef DEBUG_MISS_SYMBOL
165    if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), name) ==
166        missedSymbolFile_.end()) {
167        missedSymbolFile_.emplace_back(name);
168        HLOGW("NOT found symbol for map '%s'", name.c_str());
169        for (const auto &file : symbolsFiles_) {
170            HLOGW(" we have '%s'", file->filePath_.c_str());
171        }
172    }
173#endif
174    return nullptr;
175}
176
177void VirtualThread::ReportVaddrMapMiss(uint64_t vaddr) const
178{
179#ifdef HIPERF_DEBUG
180    if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
181        if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) {
182            missedRuntimeVaddr_.insert(vaddr);
183            HLOGV("vaddr %" PRIx64 " not found in any map", vaddr);
184            for (auto &map : *maps_) {
185                HLOGV("map %s ", map->ToString().c_str());
186            }
187        }
188    }
189#endif
190}
191
192bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, size_t size) const
193{
194    auto [curMemMaps, itemIndex] = virtualruntime_->FindMap(vaddr);
195    if (curMemMaps != nullptr) {
196        // found symbols by file name
197        SymbolsFile *symbolsFile = FindSymbolsFileByMap((curMemMaps->GetMaps())[itemIndex]);
198        if (symbolsFile != nullptr) {
199            std::shared_ptr<DfxMap> map = (curMemMaps->GetMaps())[itemIndex];
200            HLOGM("read vaddr from addr is 0x%" PRIx64 "  mapStart :0x%" PRIx64 " mapOffset :0x%" PRIx64 " at '%s'",
201                  vaddr - map->begin, map->begin, map->offset, map->name.c_str());
202            map->elf = symbolsFile->GetElfFile();
203            if (map->elf != nullptr) {
204                auto fileOffset = map->FileOffsetFromAddr(vaddr);
205                fileOffset -= map->elf->GetBaseOffset();
206                map->elf->Read(fileOffset, data, size);
207                return true;
208            }
209            HLOGE("ElfFile(%s) is null or read file offset from addr fail", curMemMaps->name_.c_str());
210            return false;
211        } else {
212            HLOGE("found addr %" PRIx64 " in map but not loaded symbole %s", vaddr, curMemMaps->name_.c_str());
213        }
214    } else {
215#ifdef HIPERF_DEBUG
216        ReportVaddrMapMiss(vaddr);
217#endif
218    }
219    return false;
220}
221
222bool VirtualThread::ParseMap(std::vector<std::shared_ptr<DfxMap>>& memMaps, bool update)
223{
224    std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
225    std::shared_ptr<DfxMaps> dfxMaps = OHOS::HiviewDFX::DfxMaps::Create(pid_, mapPath);
226    if (dfxMaps == nullptr) {
227        HLOGE("VirtualThread Failed to Parse Map.");
228        return false;
229    }
230    memMaps = dfxMaps->GetMaps();
231    bool mapsAdded = !update;
232    std::vector<std::shared_ptr<DfxMap>> tempMap;
233    std::string tempMapName;
234    std::shared_ptr<DfxMap> prevMap = nullptr;
235    for (auto memMapItem : memMaps) {
236        if (!update) {
237            virtualruntime_->FillMapsCache(tempMapName, memMapItem);
238            bool updateNormalSymbol = true;
239            if (memMapItem->name.find(".hap") != std::string::npos && (memMapItem->prots & PROT_EXEC)) {
240                memMapItem->prevMap = prevMap;
241                HLOGD("update hap(%s) symbols", memMapItem->name.c_str());
242                updateNormalSymbol = !virtualruntime_->UpdateHapSymbols(memMapItem);
243            }
244            if (updateNormalSymbol) {
245                virtualruntime_->UpdateSymbols(memMapItem->name, memMapItem);
246            }
247            prevMap = memMapItem;
248        } else if (!virtualruntime_->IsSymbolExist(memMapItem->name)) {
249            virtualruntime_->FillMapsCache(tempMapName, memMapItem);
250            mapsAdded = true;
251            tempMap.push_back(memMapItem);
252            bool updateNormalSymbol = true;
253            if (memMapItem->name.find(".hap") != std::string::npos && (memMapItem->prots & PROT_EXEC)) {
254                memMapItem->prevMap = prevMap;
255                HLOGD("update hap(%s) symbols", memMapItem->name.c_str());
256                updateNormalSymbol = !virtualruntime_->UpdateHapSymbols(memMapItem);
257            }
258            if (updateNormalSymbol) {
259                virtualruntime_->UpdateSymbols(memMapItem->name, memMapItem);
260            }
261            prevMap = memMapItem;
262        }
263    }
264
265    // Find if there are duplicate mapping intervals, and if there are, overwrite the old data with the new data.
266    for (auto tempMapIter = tempMap.begin(); tempMapIter != tempMap.end(); ++tempMapIter) {
267        auto memMapIter = std::find_if(memMaps.begin(), memMaps.end(), [&](const std::shared_ptr<DfxMap>& map) {
268            if ((*tempMapIter)->begin == map->begin && (*tempMapIter)->end == map->end) {
269                return true;
270            }
271            return false;
272        });
273        if (memMapIter != memMaps.end()) {
274            virtualruntime_->DelSymbolFile((*memMapIter)->name);
275            memMaps.erase(memMapIter);
276        }
277    }
278    memMaps.insert(memMaps.end(), tempMap.begin(), tempMap.end());
279
280    if (mapsAdded) {
281        PROFILER_LOG_DEBUG(LOG_CORE, "maps changed and need sort");
282        SortMaps();
283    } else {
284        PROFILER_LOG_DEBUG(LOG_CORE, "maps no change");
285        return false;
286    }
287    virtualruntime_->soBegin_ = 0;
288    return true;
289}
290
291void VirtualThread::SortMaps()
292{
293    for (size_t currPos = 1; currPos < maps_->size(); ++currPos) {
294        int targetPos = static_cast<int>(currPos - 1);
295        while (targetPos >= 0 && (*maps_)[currPos]->end < (*maps_)[targetPos]->end) {
296            --targetPos;
297        }
298        if (targetPos < static_cast<int>(currPos - 1)) {
299            auto target = (*maps_)[currPos];
300            for (size_t k = currPos - 1; k > static_cast<size_t>(targetPos); --k) {
301                (*maps_)[k + 1] = (*maps_)[k];
302            }
303            (*maps_)[targetPos + 1] = target;
304        }
305    }
306    return;
307}
308
309void VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len,
310                                  uint64_t offset)
311{
312    if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(filename)) {
313        return; // skip some memmap
314    }
315    uint32_t prots =  PROT_EXEC;
316
317    std::shared_ptr<DfxMap> map = std::make_shared<DfxMap>(begin, begin + len, offset, prots, filename);
318    maps_->emplace_back(map);
319    std::string tempMapName{" "};
320    virtualruntime_->FillMapsCache(tempMapName, map);
321    SortMaps();
322}
323} // namespace NativeDaemon
324} // namespace Developtools
325} // namespace OHOS