1/*
2 * Copyright (c) 2021-2022 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#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 "symbols_file.h"
27#include "utilities.h"
28#include "virtual_runtime.h"
29namespace OHOS {
30namespace Developtools {
31namespace HiPerf {
32
33static constexpr int MMAP_PROT_CHARS = 4;
34static constexpr int MAP_PROT_EXEC_INDEX = 2;
35
36#ifdef DEBUG_TIME
37
38bool VirtualThread::IsSorted() const
39{
40    for (std::size_t index = 1; index < memMaps_.size(); ++index) {
41        if (memMaps_[memMapsIndexs_[index - 1]]->end > memMaps_[memMapsIndexs_[index]]->begin) {
42            std::cout << "memMaps_ order error:\n"
43                      << "    " << memMaps_[memMapsIndexs_[index - 1]]->begin << "-"
44                      << memMaps_[memMapsIndexs_[index - 1]]->end
45                      << "    " << memMaps_[memMapsIndexs_[index]]->begin << "-"
46                      << memMaps_[memMapsIndexs_[index]]->end;
47            return false;
48        }
49    }
50    return true;
51}
52#endif
53
54int64_t VirtualThread::FindMapIndexByAddr(uint64_t addr) const
55{
56    HLOGM("try found vaddr 0x%" PRIx64 "in maps %zu", addr, memMaps_.size());
57    const int64_t illegal = -1;
58    if (memMaps_.size() == 0) {
59        return illegal;
60    }
61    if (memMaps_[memMapsIndexs_[0]]->begin > addr) {
62        return illegal;
63    }
64    if (memMaps_[memMapsIndexs_[memMapsIndexs_.size() >= 1 ? memMapsIndexs_.size() -  1 : 0]]->end <= addr) {
65        return illegal;
66    }
67    constexpr int divisorNum {2};
68    std::size_t left {0};
69    std::size_t right {memMapsIndexs_.size()};
70    std::size_t mid = (right - left) / divisorNum + left;
71    while (left < right) {
72        if (addr < memMaps_[memMapsIndexs_[mid]]->end) {
73            right = mid;
74            mid = (right - left) / divisorNum + left;
75            continue;
76        }
77        if (addr >= memMaps_[memMapsIndexs_[mid]]->end) {
78            left = mid + 1;
79            mid = (right - left) / divisorNum + left;
80            continue;
81        }
82    }
83    if (addr >= memMaps_[memMapsIndexs_[left]]->begin && addr < memMaps_[memMapsIndexs_[left]]->end) {
84        if (left > 0) {
85            memMaps_[memMapsIndexs_[left]]->prevMap = memMaps_[memMapsIndexs_[left - 1]];
86        }
87        return static_cast<int64_t>(memMapsIndexs_[left]);
88    }
89    return illegal;
90}
91
92std::shared_ptr<DfxMap> VirtualThread::FindMapByAddr(uint64_t addr) const
93{
94    HLOGM("try found vaddr 0x%" PRIx64 "in maps %zu", addr, memMaps_.size());
95    if (memMaps_.size() == 0) {
96        return nullptr;
97    }
98    if (memMaps_[memMapsIndexs_[0]]->begin > addr) {
99        return nullptr;
100    }
101    if (memMaps_[memMapsIndexs_[memMapsIndexs_.size() >= 1 ? memMapsIndexs_.size() - 1 : 0]]->end <= addr) {
102        return nullptr;
103    }
104    constexpr int divisorNum {2};
105    std::size_t left {0};
106    std::size_t right {memMapsIndexs_.size()};
107    std::size_t mid = (right - left) / divisorNum + left;
108    while (left < right) {
109        if (addr < memMaps_[memMapsIndexs_[mid]]->end) {
110            right = mid;
111            mid = (right - left) / divisorNum + left;
112            continue;
113        }
114        if (addr >= memMaps_[memMapsIndexs_[mid]]->end) {
115            left = mid + 1;
116            mid = (right - left) / divisorNum + left;
117            continue;
118        }
119    }
120    if (addr >= memMaps_[memMapsIndexs_[left]]->begin && addr < memMaps_[memMapsIndexs_[left]]->end) {
121        if (left > 0) {
122            memMaps_[memMapsIndexs_[left]]->prevMap = memMaps_[memMapsIndexs_[left - 1]];
123        }
124        return memMaps_[memMapsIndexs_[left]];
125    }
126    return nullptr;
127}
128
129std::shared_ptr<DfxMap> VirtualThread::FindMapByFileInfo(const std::string name, uint64_t offset) const
130{
131    for (auto &map : memMaps_) {
132        if (name != map->name) {
133            continue;
134        }
135        // check begin and length
136        if (offset >= map->offset && (offset - map->offset) < (map->end - map->begin)) {
137            HLOGMMM("found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64
138                    " pageoffset 0x%" PRIx64 ")  from %s",
139                    offset, map->begin, map->end, map->offset, map->name.c_str());
140            return map;
141        }
142    }
143    HLOGM("NOT found offset 0x%" PRIx64 " in maps %zu ", offset, memMaps_.size());
144    return nullptr;
145}
146
147SymbolsFile *VirtualThread::FindSymbolsFileByMap(std::shared_ptr<DfxMap> map) const
148{
149    if (map == nullptr) {
150        return nullptr;
151    }
152    if (map->symbolFileIndex != -1) {
153        // no need further operation
154        if (symbolsFiles_[map->symbolFileIndex]->LoadDebugInfo(map)) {
155            return symbolsFiles_[map->symbolFileIndex].get();
156        }
157    } else {
158        // add it to cache
159        for (size_t i = 0; i < symbolsFiles_.size(); ++i) {
160            if (symbolsFiles_[i]->filePath_ == map->name) {
161                HLOGD("found symbol for map '%s'", map->name.c_str());
162                if (symbolsFiles_[i]->LoadDebugInfo(map)) {
163                    HLOGD("found symbol for map '%s'", map->name.c_str());
164                    map->symbolFileIndex = static_cast<int32_t>(i);
165                    return symbolsFiles_[i].get();
166                }
167            }
168        }
169    }
170
171#ifdef DEBUG_MISS_SYMBOL
172    if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), inMap.name) ==
173        missedSymbolFile_.end()) {
174        missedSymbolFile_.emplace_back(inMap.name);
175        HLOGW("NOT found symbol for map '%s'", inMap.name.c_str());
176        for (const auto &file : symbolsFiles_) {
177            HLOGW(" we have '%s'", file->filePath_.c_str());
178        }
179    }
180#endif
181    return nullptr;
182}
183void VirtualThread::ReportVaddrMapMiss(uint64_t vaddr) const
184{
185#ifdef HIPERF_DEBUG
186    if (DebugLogger::logDisabled_) {
187        return;
188    }
189    if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
190        if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) {
191            missedRuntimeVaddr_.insert(vaddr);
192            HLOGV("vaddr %" PRIx64 " not found in any map", vaddr);
193            for (auto &map : memMaps_) {
194                if (map == nullptr) {
195                    return;
196                }
197                HLOGV("map %s ", map->ToString().c_str());
198            }
199        }
200    }
201#endif
202}
203
204bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, size_t size) const
205{
206    uint64_t pageIndex = vaddr >> 12;
207    uint64_t memMapIndex = -1;
208    const int64_t exceptRet = -1;
209    const uint64_t illegal = -1;
210    auto pageFile = vaddr4kPageCache_.find(pageIndex);
211    if (pageFile != vaddr4kPageCache_.end()) {
212        memMapIndex = pageFile->second;
213    } else {
214        int64_t retIndex = FindMapIndexByAddr(vaddr);
215        memMapIndex = static_cast<uint64_t>(retIndex);
216        // add to 4k page cache table
217        if (retIndex != exceptRet && memMapIndex < memMaps_.size()) {
218            const_cast<VirtualThread *>(this)->vaddr4kPageCache_[pageIndex] = memMapIndex;
219        }
220    }
221    if (memMapIndex != illegal) {
222        auto map = memMaps_[memMapIndex];
223        if (map != nullptr) {
224            if (map->elf == nullptr) {
225                SymbolsFile* symFile = FindSymbolsFileByMap(map);
226                if (symFile == nullptr) {
227                    return false;
228                }
229                map->elf = symFile->GetElfFile();
230            }
231            if (map->elf != nullptr) {
232                // default base offset is zero
233                uint64_t foff = vaddr - map->begin + map->offset - map->elf->GetBaseOffset();
234                if (map->elf->Read(foff, data, size)) {
235                    return true;
236                } else {
237                    return false;
238                }
239            } else {
240                HLOGW("find addr %" PRIx64 "in map but not loaded symbole %s", vaddr, map->name.c_str());
241            }
242        }
243    } else {
244        HLOGV("not found in any map");
245    }
246    return false;
247}
248
249#if defined(is_mingw) && is_mingw
250void VirtualThread::ParseMap()
251{
252    // only linux support read maps in runtime
253    return;
254}
255#else
256void VirtualThread::ParseMap()
257{
258    if (!(OHOS::HiviewDFX::DfxMaps::Create(pid_, memMaps_, memMapsIndexs_))) {
259        HLOGE("VirtualThread Failed to Parse Map.");
260    }
261    SortMemMaps();
262}
263#endif
264
265void VirtualThread::FixHMBundleMap()
266{
267    // fix bundle path in map
268    for (auto &map : memMaps_) {
269        NeedAdaptHMBundlePath(map->name, name_);
270    }
271}
272
273constexpr const int MMAP_LINE_TOKEN_INDEX_FLAG = 1;
274constexpr const int MMAP_LINE_TOKEN_INDEX_OFFSET = 2;
275constexpr const int MMAP_LINE_TOKEN_INDEX_NAME = 5;
276constexpr const int MMAP_LINE_MAX_TOKEN = 6;
277void VirtualThread::ParseServiceMap(const std::string &filename)
278{
279    std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
280    std::string mapContent = ReadFileToString(mapPath);
281    uint64_t begin = 0;
282    uint64_t end = 0;
283    if (mapContent.size() == 0) {
284        HLOGW("Parse %s failed, content empty", mapPath.c_str());
285        return;
286    }
287    std::istringstream s(mapContent);
288    std::string line;
289    while (std::getline(s, line)) {
290        std::vector<std::string> mapTokens = StringSplit(line, " ");
291        if (mapTokens.size() == MMAP_LINE_MAX_TOKEN &&
292            mapTokens[MMAP_LINE_TOKEN_INDEX_NAME] == name_) {
293            HLOGM("map line: %s", line.c_str());
294            std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
295            begin = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
296            end = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
297            break;
298        }
299    }
300    CreateMapItem(filename, begin, end - begin, 0);
301}
302
303void VirtualThread::ParseDevhostMap(pid_t devhost)
304{
305    std::string mapPath = StringPrintf("/proc/%d/maps", devhost);
306    std::string mapContent = ReadFileToString(mapPath);
307    std::string filename;
308    if (mapContent.size() > 0) {
309        std::istringstream s(mapContent);
310        std::string line;
311        while (std::getline(s, line)) {
312            // 2fe40000-311e1000 r-xp 00000000 00:01 217 /lib/libdh-linux.so.5.10.97-oh
313            // 0                 1    2        3     4   5
314            std::vector<std::string> mapTokens = StringSplit(line, " ");
315            if (mapTokens.size() < MMAP_LINE_MAX_TOKEN) {
316                continue;
317            }
318            HLOGM("map line: %s", line.c_str());
319
320            // 2fe40000-311e1000
321            constexpr const int mmapAddrRangeToken = 2;
322            std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
323            if (addrRanges.size() != mmapAddrRangeToken) {
324                continue;
325            }
326            uint64_t begin, end, offset;
327            // 2fe40000 / 311e1000
328            try {
329                begin = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
330                end = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
331                offset = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET],
332                                     nullptr, NUMBER_FORMAT_HEX_BASE);
333            } catch (...) {
334                continue;
335            }
336
337            // --x-
338            if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS ||
339                mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_PROT_EXEC_INDEX] != 'x') {
340                continue;
341            }
342
343            const std::string anonPrefix = "[anon:[";
344            const std::string anonPostfix = "]]";
345            filename = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME];
346            if (filename == "[shmm]") {
347                continue;
348            }
349            if (filename.find(anonPrefix) != std::string::npos) {
350                // '[anon:[liblinux/devhost.ko]]' to '/liblinux/devhost.ko'
351                filename = filename.substr(anonPrefix.size(),
352                                           filename.size() - anonPrefix.size() -
353                                           anonPostfix.size());
354                filename = "/" + filename;
355            } else if (filename.find(DEVHOST_LINUX_FILE_NAME) != std::string::npos) {
356                // '/lib/libdh-linux.so.5.10.97-oh' to '/lib/libdh-linux.so'
357                filename = DEVHOST_LINUX_FILE_NAME;
358            }
359            CreateMapItem(filename, begin, end - begin, offset);
360        }
361    }
362    SortMemMaps();
363}
364
365void VirtualThread::SortMemMaps()
366{
367    for (int currPos = 1; currPos < static_cast<int>(memMaps_.size()); ++currPos) {
368        int targetPos = currPos - 1;
369        while (targetPos >= 0 and memMaps_[memMapsIndexs_[currPos]]->end < memMaps_[memMapsIndexs_[targetPos]]->end) {
370            --targetPos;
371        }
372        if (targetPos < currPos - 1) {
373            auto target = memMapsIndexs_[currPos];
374            for (int k = currPos - 1; k > targetPos; --k) {
375                memMapsIndexs_[k + 1] = memMapsIndexs_[k];
376            }
377            memMapsIndexs_[targetPos + 1] = target;
378        }
379    }
380}
381
382std::shared_ptr<DfxMap> VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len,
383                                                     uint64_t offset, uint32_t prot)
384{
385    if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(filename)) {
386        return nullptr; // skip some memmap
387    }
388    std::shared_ptr<DfxMap> map = memMaps_.emplace_back(std::make_shared<DfxMap>(begin, begin + len, offset,
389        prot, filename));
390    memMapsIndexs_.emplace_back(memMaps_.size() >= 1 ? memMaps_.size() - 1 : 0);
391    HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ",
392          pid_, tid_, memMaps_.size(), map->name.c_str(), map->begin, map->end, map->offset);
393    SortMemMaps();
394    return map;
395}
396} // namespace HiPerf
397} // namespace Developtools
398} // namespace OHOS
399