106f6ba60Sopenharmony_ci/*
206f6ba60Sopenharmony_ci * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
306f6ba60Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
406f6ba60Sopenharmony_ci * you may not use this file except in compliance with the License.
506f6ba60Sopenharmony_ci * You may obtain a copy of the License at
606f6ba60Sopenharmony_ci *
706f6ba60Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
806f6ba60Sopenharmony_ci *
906f6ba60Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1006f6ba60Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1106f6ba60Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1206f6ba60Sopenharmony_ci * See the License for the specific language governing permissions and
1306f6ba60Sopenharmony_ci * limitations under the License.
1406f6ba60Sopenharmony_ci */
1506f6ba60Sopenharmony_ci#ifndef HIPERF_VIRTUAL_RUNTIME_H
1606f6ba60Sopenharmony_ci#define HIPERF_VIRTUAL_RUNTIME_H
1706f6ba60Sopenharmony_ci#include <unistd.h>
1806f6ba60Sopenharmony_ci#include <sys/types.h>
1906f6ba60Sopenharmony_ci#include <pthread.h>
2006f6ba60Sopenharmony_ci#include <functional>
2106f6ba60Sopenharmony_ci#include <map>
2206f6ba60Sopenharmony_ci#if defined(is_ohos) && is_ohos
2306f6ba60Sopenharmony_ci#include "call_stack.h"
2406f6ba60Sopenharmony_ci#endif
2506f6ba60Sopenharmony_ci#include "hashlistpp.h"
2606f6ba60Sopenharmony_ci#include "perf_event_record.h"
2706f6ba60Sopenharmony_ci#include "symbols_file.h"
2806f6ba60Sopenharmony_ci#include "virtual_thread.h"
2906f6ba60Sopenharmony_ci#include "native_hook_config.pb.h"
3006f6ba60Sopenharmony_ci
3106f6ba60Sopenharmony_cinamespace OHOS {
3206f6ba60Sopenharmony_cinamespace Developtools {
3306f6ba60Sopenharmony_cinamespace NativeDaemon {
3406f6ba60Sopenharmony_ci/*
3506f6ba60Sopenharmony_ciThis Class contains userspace thread objects. and kernel space objects
3606f6ba60Sopenharmony_ciIt represents a virtual operating environment, mainly referring to the relationship between pid,
3706f6ba60Sopenharmony_cimmaps, and symbols.
3806f6ba60Sopenharmony_ci
3906f6ba60Sopenharmony_ciIt mainly receives data is ip pointer (virtual address), pid
4006f6ba60Sopenharmony_ciAccording to these data, it will find the corresponding mmap and its corresponding elf (also called
4106f6ba60Sopenharmony_ciDSO)
4206f6ba60Sopenharmony_ci
4306f6ba60Sopenharmony_ciThen find the corresponding symbol in the corresponding elf symbol file according to the offset
4406f6ba60Sopenharmony_cirecorded in the corresponding mmap.
4506f6ba60Sopenharmony_ci*/
4606f6ba60Sopenharmony_ci
4706f6ba60Sopenharmony_ciclass VirtualRuntime {
4806f6ba60Sopenharmony_cipublic:
4906f6ba60Sopenharmony_ci    VirtualRuntime() = default;
5006f6ba60Sopenharmony_ci    VirtualRuntime(const NativeHookConfig& hookConfig);
5106f6ba60Sopenharmony_ci    virtual ~VirtualRuntime();
5206f6ba60Sopenharmony_ci    // thread need hook the record
5306f6ba60Sopenharmony_ci    // from the record , it will call back to write some Simulated Record
5406f6ba60Sopenharmony_ci    // case 1. some mmap will be create when it read mmaps for each new process (from record sample)
5506f6ba60Sopenharmony_ci
5606f6ba60Sopenharmony_ci    // set symbols path , it will send to every symobile file for search
5706f6ba60Sopenharmony_ci    bool SetSymbolsPaths(const std::vector<std::string> &symbolsPaths);
5806f6ba60Sopenharmony_ci
5906f6ba60Sopenharmony_ci    // any mode
6006f6ba60Sopenharmony_ci    static_assert(sizeof(pid_t) == sizeof(int));
6106f6ba60Sopenharmony_ci
6206f6ba60Sopenharmony_ci    const std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> &GetSymbolsFiles() const
6306f6ba60Sopenharmony_ci    {
6406f6ba60Sopenharmony_ci        return symbolsFiles_;
6506f6ba60Sopenharmony_ci    }
6606f6ba60Sopenharmony_ci
6706f6ba60Sopenharmony_ci    const DfxSymbol GetSymbol(CallFrame& callFrame, pid_t pid, pid_t tid,
6806f6ba60Sopenharmony_ci                           const perf_callchain_context &context = PERF_CONTEXT_MAX);
6906f6ba60Sopenharmony_ci
7006f6ba60Sopenharmony_ci    VirtualThread &GetThread(pid_t pid, pid_t tid);
7106f6ba60Sopenharmony_ci    const std::map<pid_t, VirtualThread> &GetThreads() const
7206f6ba60Sopenharmony_ci    {
7306f6ba60Sopenharmony_ci        return userSpaceThreadMap_;
7406f6ba60Sopenharmony_ci    }
7506f6ba60Sopenharmony_ci
7606f6ba60Sopenharmony_ci    bool UnwindStack(std::vector<u64>& regs,
7706f6ba60Sopenharmony_ci                     const u8* stack_addr,
7806f6ba60Sopenharmony_ci                     int stack_size,
7906f6ba60Sopenharmony_ci                     pid_t pid,
8006f6ba60Sopenharmony_ci                     pid_t tid,
8106f6ba60Sopenharmony_ci                     std::vector<CallFrame>& callFrames,
8206f6ba60Sopenharmony_ci                     size_t maxStackLevel);
8306f6ba60Sopenharmony_ci    bool GetSymbolName(pid_t pid, pid_t tid, std::vector<CallFrame>& callFrames, int offset, bool first,
8406f6ba60Sopenharmony_ci                       bool onlyjs = false);
8506f6ba60Sopenharmony_ci    void ClearMaps();
8606f6ba60Sopenharmony_ci    void FillMapsCache(std::string& currentFileName, std::shared_ptr<DfxMap> mapItem);
8706f6ba60Sopenharmony_ci    void HandleMapInfo(std::vector<uint64_t> info, const std::string& filePath, pid_t pid, pid_t tid);
8806f6ba60Sopenharmony_ci    void RemoveMaps(uint64_t addr);
8906f6ba60Sopenharmony_ci      // threads
9006f6ba60Sopenharmony_ci    VirtualThread &UpdateThread(pid_t pid, pid_t tid, const std::string name = "");
9106f6ba60Sopenharmony_ci    void FillSymbolNameId(CallFrame& callFrame, DfxSymbol& symbol);
9206f6ba60Sopenharmony_ci    void FillFileSet(CallFrame& callFrame, const DfxSymbol& symbol);
9306f6ba60Sopenharmony_ci    uint32_t FillArkTsFilePath(std::string_view& jstr);
9406f6ba60Sopenharmony_ci    uint32_t FindArkTsFilePath(std::string_view& jstr);
9506f6ba60Sopenharmony_ci    bool ArktsGetSymbolCache(CallFrame& callFrame, DfxSymbol &symbol);
9606f6ba60Sopenharmony_ci    uint32_t GetJsSymbolCacheSize();
9706f6ba60Sopenharmony_ci    void FillJsSymbolCache(CallFrame& callFrame, const DfxSymbol& symbol);
9806f6ba60Sopenharmony_ci    std::vector<uint64_t>& GetOfflineMaps()
9906f6ba60Sopenharmony_ci    {
10006f6ba60Sopenharmony_ci        return offlineMapAddr_;
10106f6ba60Sopenharmony_ci    }
10206f6ba60Sopenharmony_ci
10306f6ba60Sopenharmony_ci    void ClearOfflineMaps()
10406f6ba60Sopenharmony_ci    {
10506f6ba60Sopenharmony_ci        offlineMapAddr_.clear();
10606f6ba60Sopenharmony_ci    }
10706f6ba60Sopenharmony_ci
10806f6ba60Sopenharmony_ci    std::map<uint64_t, std::shared_ptr<MemMaps>>& GetMapsCache()
10906f6ba60Sopenharmony_ci    {
11006f6ba60Sopenharmony_ci        return mapsCache_;
11106f6ba60Sopenharmony_ci    }
11206f6ba60Sopenharmony_ci
11306f6ba60Sopenharmony_ci    std::pair<std::shared_ptr<MemMaps>, uint32_t> FindMap(uint64_t addr);
11406f6ba60Sopenharmony_ci    uint64_t soBegin_ {0};
11506f6ba60Sopenharmony_ci    // debug time
11606f6ba60Sopenharmony_ci#ifdef HIPERF_DEBUG_TIME
11706f6ba60Sopenharmony_ci    std::chrono::microseconds updateSymbolsTimes_ = std::chrono::microseconds::zero();
11806f6ba60Sopenharmony_ci    std::chrono::microseconds unwindFromRecordTimes_ = std::chrono::microseconds::zero();
11906f6ba60Sopenharmony_ci    std::chrono::microseconds unwindCallStackTimes_ = std::chrono::microseconds::zero();
12006f6ba60Sopenharmony_ci    std::chrono::microseconds symbolicRecordTimes_ = std::chrono::microseconds::zero();
12106f6ba60Sopenharmony_ci    std::chrono::microseconds updateThreadTimes_ = std::chrono::microseconds::zero();
12206f6ba60Sopenharmony_ci#endif
12306f6ba60Sopenharmony_ci    const bool loadSymboleWhenNeeded_ = true; // thie is a feature config
12406f6ba60Sopenharmony_ci    void UpdateSymbols(std::string filename, std::shared_ptr<DfxMap> map);
12506f6ba60Sopenharmony_ci    // we don't know whether hap vma mapping is stand for a so
12606f6ba60Sopenharmony_ci    // thus we need try to parse it first
12706f6ba60Sopenharmony_ci    bool UpdateHapSymbols(std::shared_ptr<DfxMap> map);
12806f6ba60Sopenharmony_ci    bool IsSymbolExist(const std::string& fileName);
12906f6ba60Sopenharmony_ci    void DelSymbolFile(const std::string& fileName);
13006f6ba60Sopenharmony_ci    void UpdateMaps(pid_t pid, pid_t tid);
13106f6ba60Sopenharmony_ci    std::vector<std::shared_ptr<DfxMap>>& GetProcessMaps()
13206f6ba60Sopenharmony_ci    {
13306f6ba60Sopenharmony_ci        return processMaps_;
13406f6ba60Sopenharmony_ci    }
13506f6ba60Sopenharmony_ci
13606f6ba60Sopenharmony_cipublic:
13706f6ba60Sopenharmony_ci    enum SymbolCacheLimit : std::size_t {
13806f6ba60Sopenharmony_ci        USER_SYMBOL_CACHE_LIMIT = 10000,
13906f6ba60Sopenharmony_ci    };
14006f6ba60Sopenharmony_ci
14106f6ba60Sopenharmony_ciprivate:
14206f6ba60Sopenharmony_ci    struct SymbolCacheKey : public std::pair<uint64_t, uint32_t> {
14306f6ba60Sopenharmony_ci        uint64_t& ip = first;
14406f6ba60Sopenharmony_ci        uint32_t& filePathId = second;
14506f6ba60Sopenharmony_ci        explicit SymbolCacheKey() = default;
14606f6ba60Sopenharmony_ci        virtual ~SymbolCacheKey() = default;
14706f6ba60Sopenharmony_ci        SymbolCacheKey(const SymbolCacheKey &) = default;
14806f6ba60Sopenharmony_ci        SymbolCacheKey& operator=(const SymbolCacheKey& sym)
14906f6ba60Sopenharmony_ci        {
15006f6ba60Sopenharmony_ci            ip = sym.ip;
15106f6ba60Sopenharmony_ci            filePathId = sym.filePathId;
15206f6ba60Sopenharmony_ci            return *this;
15306f6ba60Sopenharmony_ci        }
15406f6ba60Sopenharmony_ci        SymbolCacheKey(const std::pair<uint64_t, uint32_t>& arg) : pair(arg), ip(first), filePathId(second) {}
15506f6ba60Sopenharmony_ci        SymbolCacheKey(uint64_t ip, uint32_t filePathId) : pair(ip, filePathId), ip(first), filePathId(second) {}
15606f6ba60Sopenharmony_ci    };
15706f6ba60Sopenharmony_ci
15806f6ba60Sopenharmony_ci    // boost library recommendation algorithm to reduce hash collisions.
15906f6ba60Sopenharmony_ci    struct HashPair {
16006f6ba60Sopenharmony_ci        size_t operator() (const SymbolCacheKey& key) const
16106f6ba60Sopenharmony_ci        {
16206f6ba60Sopenharmony_ci            std::hash<uint64_t> hasher;
16306f6ba60Sopenharmony_ci            size_t seed = 0;
16406f6ba60Sopenharmony_ci            // 6 and 2 is the number of displacements
16506f6ba60Sopenharmony_ci            seed ^= hasher(key.ip) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
16606f6ba60Sopenharmony_ci            seed ^= hasher(key.filePathId) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
16706f6ba60Sopenharmony_ci            return seed;
16806f6ba60Sopenharmony_ci        }
16906f6ba60Sopenharmony_ci    };
17006f6ba60Sopenharmony_ci#if defined(is_ohos) && is_ohos
17106f6ba60Sopenharmony_ci    CallStack callstack_;
17206f6ba60Sopenharmony_ci#endif
17306f6ba60Sopenharmony_ci    // pid map with user space thread
17406f6ba60Sopenharmony_ci    pid_t pid_ = 0;
17506f6ba60Sopenharmony_ci    pthread_mutex_t threadMapsLock_;
17606f6ba60Sopenharmony_ci    std::map<pid_t, VirtualThread> userSpaceThreadMap_;
17706f6ba60Sopenharmony_ci    // not pid , just map
17806f6ba60Sopenharmony_ci    std::vector<DfxMap> kernelSpaceMaps_;
17906f6ba60Sopenharmony_ci    pthread_mutex_t processSymbolsFileLock_;
18006f6ba60Sopenharmony_ci    std::unordered_set<uint32_t> fileSet_; // for mapItem filePathId_
18106f6ba60Sopenharmony_ci    std::unordered_map<std::string, uint32_t> functionMap_;
18206f6ba60Sopenharmony_ci    std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> symbolsFiles_;
18306f6ba60Sopenharmony_ci    std::unordered_map<SymbolCacheKey, DfxSymbol, HashPair> userSymbolCache_;
18406f6ba60Sopenharmony_ci    bool GetSymbolCache(uint64_t ip, DfxSymbol &symbol, const VirtualThread &thread);
18506f6ba60Sopenharmony_ci    void UpdateSymbolCache(uint64_t ip, DfxSymbol &symbol, HashList<uint64_t, DfxSymbol> &cache);
18606f6ba60Sopenharmony_ci
18706f6ba60Sopenharmony_ci    // find synbols function name
18806f6ba60Sopenharmony_ci    void MakeCallFrame(DfxSymbol &symbol, CallFrame &callFrame);
18906f6ba60Sopenharmony_ci
19006f6ba60Sopenharmony_ci    std::string ReadThreadName(pid_t tid);
19106f6ba60Sopenharmony_ci    VirtualThread &CreateThread(pid_t pid, pid_t tid);
19206f6ba60Sopenharmony_ci
19306f6ba60Sopenharmony_ci    const DfxSymbol GetKernelSymbol(uint64_t ip, const std::vector<std::shared_ptr<DfxMap>> &maps,
19406f6ba60Sopenharmony_ci                                 const VirtualThread &thread);
19506f6ba60Sopenharmony_ci    const DfxSymbol GetUserSymbol(uint64_t ip, const VirtualThread &thread);
19606f6ba60Sopenharmony_ci
19706f6ba60Sopenharmony_ci    std::vector<std::string> symbolsPaths_;
19806f6ba60Sopenharmony_ci
19906f6ba60Sopenharmony_ci    friend class VirtualRuntimeTest;
20006f6ba60Sopenharmony_ci    friend class VirtualThread;
20106f6ba60Sopenharmony_ci    std::vector<std::shared_ptr<DfxMap>> processMaps_;
20206f6ba60Sopenharmony_ci    std::unordered_set<uint64_t> failedIPs_;
20306f6ba60Sopenharmony_ci    const NativeHookConfig hookConfig_;
20406f6ba60Sopenharmony_ci    uint32_t memMapFilePathId_ = 0;
20506f6ba60Sopenharmony_ci    std::map<uint64_t, std::shared_ptr<MemMaps>> mapsCache_; // key is memMap soBegin, value is MemMaps
20606f6ba60Sopenharmony_ci    std::vector<uint64_t> offlineMapAddr_; // element is memMap soBegin
20706f6ba60Sopenharmony_ci    std::unordered_map<std::string_view, uint32_t> jsUrlMap_; // Key is js url , value is filePathId
20806f6ba60Sopenharmony_ci};
20906f6ba60Sopenharmony_ci} // namespace NativeDaemon
21006f6ba60Sopenharmony_ci} // namespace Developtools
21106f6ba60Sopenharmony_ci} // namespace OHOS
21206f6ba60Sopenharmony_ci#endif