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#ifndef STACK_PREPROCESS_H
16#define STACK_PREPROCESS_H
17
18#include <chrono>
19#include <thread>
20#include <unordered_map>
21#include <list>
22#include <algorithm>
23#include <mutex>
24#include <variant>
25
26#include "logging.h"
27#include "nocopyable.h"
28#include "stack_data_repeater.h"
29#include "buffer_writer.h"
30#include "virtual_runtime.h"
31#include "hook_common.h"
32#include "native_hook_config.pb.h"
33#include "native_hook_result.pb.h"
34#include "native_hook_result.pbencoder.h"
35#include "safe_map.h"
36#include "schedule_task_manager.h"
37
38using WriterStructPtr = std::unique_ptr<WriterStruct>::pointer;
39class StackPreprocess : public std::enable_shared_from_this<StackPreprocess> {
40public:
41    struct RecordStatistic {
42        uint32_t pid {0};
43        uint32_t callstackId {0};
44        uint32_t tagId {0};
45        RecordStatisticsEvent::MemoryType type {RecordStatisticsEvent::MALLOC};
46        uint64_t applyCount {0};
47        uint64_t releaseCount {0};
48        uint64_t applySize {0};
49        uint64_t releaseSize {0};
50    };
51
52    explicit StackPreprocess(const StackDataRepeaterPtr& dataRepeater, const NativeHookConfig& hookConfig,
53        clockid_t pluginDataClockId, FILE* fpHookData = nullptr, bool isHookStandalone = false,
54        bool isSaService = false, bool isProtobufSerialize = true);
55    ~StackPreprocess();
56    void SetWriter(const std::shared_ptr<Writer>& writer);
57    void SetWriter(const WriterStructPtr& writer);
58    bool StartTakeResults();
59    bool StopTakeResults();
60    void FinishTraceFile();
61    bool FlushRecordStatistics();
62    void FlushRecordApplyAndReleaseMatchData();
63    void ForceStop();
64    inline void SetPid(int32_t pid)
65    {
66        pid_ = pid;
67    }
68    inline void InitStatisticsTime()
69    {
70        lastStatisticsTime_ = std::chrono::steady_clock::now();
71    }
72    void SaveMemTag(uint32_t tagId, const std::string& tagName);
73    bool GetMemTag(uint32_t tagId, std::string& tagName);
74    void SaveJsRawStack(uint64_t jsChainId, const char* jsRawStack);
75    const char* GetJsRawStack(uint64_t jsChainId);
76    void ReportBasicData();
77    void WriteHookConfig();
78    void TakeResultsFromShmem(const std::shared_ptr<EventNotifier>&, const std::shared_ptr<ShareMemoryBlock>&);
79    void SetNmdFd(uint32_t fd)
80    {
81        nmdFd_ = fd;
82    }
83    void SetFlushSize(uint64_t size)
84    {
85        double tenth = static_cast<double>(size) / 10.0;
86        flushSize_ = static_cast<uint64_t>(std::ceil(tenth));
87        PROFILER_LOG_INFO(LOG_CORE, "SetFlushSize size: %" PRIu64 ", flushSize_: %" PRIu64 "", size, flushSize_);
88        if (isProtobufSerialize_) {
89            bufferSize_ = flushSize_ << 1;
90            buffer_ = std::make_unique<uint8_t[]>(bufferSize_);
91        }
92    }
93
94private:
95    using CallFrame = OHOS::Developtools::NativeDaemon::CallFrame;
96    struct ElfSymbolTable {
97        uint64_t textVaddr;
98        uint32_t textOffset;
99        uint32_t symEntSize;
100        std::vector<uint8_t> strTable;
101        std::vector<uint8_t> symTable;
102    };
103
104    enum RecordStatisticsLimit : std::size_t {
105        STATISTICS_MAP_SZIE = 100000,
106        STATISTICS_PERIOD_DATA_SIZE = 100000,
107        ALLOC_ADDRMAMP_SIZE = 100000,
108        MATCH_ADDRMAMP_SIZE = 100000,
109    };
110
111    struct ScopedLockFile {
112        ScopedLockFile(FILE* fpHook): fpHookData(fpHook)
113        {
114            flockfile(fpHookData);
115        }
116        ~ScopedLockFile()
117        {
118            funlockfile(fpHookData);
119        }
120        FILE* fpHookData {nullptr};
121    };
122
123private:
124    void TakeResults();
125    template <typename T>
126    void SetHookData(RawStackPtr rawStack, T& stackData);
127    template <typename T>
128    void SetHookData(RawStackPtr rawStack, std::vector<CallFrame>& callFrames, T& stackData);
129    void WriteFrames(RawStackPtr RawStack, const std::vector<CallFrame>& callFrames);
130    template <typename T>
131    void SetFrameInfo(T& frame, CallFrame& callFrame);
132    template <typename T>
133    void ReportSymbolNameMap(CallFrame& callFrame, T& stackData);
134    template <typename T>
135    void ReportFilePathMap(CallFrame& callFrame, T& stackData);
136    template <typename T>
137    void ReportFrameMap(CallFrame& callFrame, T& stackData);
138    void ReportThreadNameMap(uint32_t tid, const std::string& tname);
139    void SetMapsInfo();
140    template <typename T>
141    void SetSymbolInfo(uint32_t filePathId, ElfSymbolTable& symbolInfo, T& batchNativeHookData);
142    template <typename T>
143    void FlushCheck(T& stackData);
144    void FlushData(BatchNativeHookData& stackData);
145    void FlushData(OHOS::Developtools::Profiler::ProtoEncoder::BatchNativeHookData& stackData);
146    void Flush(const uint8_t* src, size_t size);
147    void GetSymbols(const std::string& filePath, ElfSymbolTable& symbols);
148    template <typename T>
149    void FillOfflineCallStack(std::vector<CallFrame>& callFrames, size_t idx, T& stackData);
150    template <typename T>
151    void FillCallStack(std::vector<CallFrame>& callFrames, size_t idx, T& stackData);
152    template <typename T>
153    uint32_t SetCallStackMap(T& stackData);
154    template <typename T>
155    uint32_t GetCallStackId(const RawStackPtr& rawStack, std::vector<CallFrame>& callFrames, T& stackData);
156    uint32_t FindCallStackId(std::vector<uint64_t>& callStack);
157    template <typename T>
158    void SetEventFrame(const RawStackPtr& rawStack, std::vector<CallFrame>& callFrames,
159        T* event, uint32_t stackId, const std::string& type = "");
160    template <typename T>
161    void SetEventFrame(const ReportEventBaseData& rawStack, T* event, uint32_t stackMapId,
162                       const std::string& type = "");
163    template <typename T>
164    void SetAllocStatisticsFrame(const RawStackPtr& rawStack, std::vector<CallFrame>& callFrames, T& stackData);
165    template <typename T>
166    void SetAllocStatisticsFrame(const RawStackPtr& rawStack, T& stackData);
167    template <typename T>
168    void SetApplyAndReleaseMatchFrame(RawStackPtr rawStack, std::vector<CallFrame>& callFrames, T& stackData);
169    void IntervalFlushRecordStatistics();
170    void IntervalFlushApplyAndReleaseMatchData();
171    bool HandleNoStackEvent(RawStackPtr& rawStack);
172    bool SetFreeStatisticsData(uint64_t addr);
173    void SetAllocStatisticsData(const RawStackPtr& rawStack, size_t stackId, bool isExists = false);
174    unsigned LgFloor(unsigned long x);
175    uint64_t PowCeil(uint64_t x);
176    size_t ComputeAlign(size_t size);
177    void ReportOfflineSymbolizationData();
178
179    RandomWriteCtx* StartReport();
180    void FinishReport();
181    void FillFpNativeIp(RawStackPtr& rawData);
182    void FillFpJsData(RawStackPtr& rawData);
183    void FillDwarfErrorStack();
184    void FillNapiStack(std::string& tagName, std::vector<CallFrame>& callFrames, uint64_t napiIndex);
185private:
186    std::chrono::steady_clock::time_point lastStatisticsTime_ = std::chrono::steady_clock::now();
187    std::shared_ptr<Writer> writer_ = nullptr;
188    StackDataRepeaterPtr dataRepeater_ = nullptr;
189    std::thread thread_ {};
190    std::unique_ptr<uint8_t[]> buffer_;
191    std::atomic_bool isStopTakeData_ = false;
192    std::shared_ptr<OHOS::Developtools::NativeDaemon::VirtualRuntime> runtime_instance;
193    DISALLOW_COPY_AND_MOVE(StackPreprocess);
194    OHOS::SafeMap<uint32_t, std::string> memTagMap_ = {};
195    std::unordered_map<uint32_t, std::string> threadNameMap_ = {};
196    NativeHookConfig hookConfig_;
197    uint32_t ignoreCnts_ = 0;
198    uint32_t eventCnts_ = 0;
199    bool flushBasicData_ {true};
200    std::vector<u64> u64regs_;
201    std::vector<CallFrame> callFrames_;
202    std::vector<uint64_t> callStack_;
203    // Key is callStack_, value is call stack id
204    std::map<std::vector<uint64_t>, uint32_t> callStackMap_;
205    // Key is ip , response_library_mode used
206    std::unordered_map<uint64_t, uint32_t> responseLibraryMap_;
207    std::chrono::seconds statisticsInterval_ {0};
208    // Key is call stack id, value is recordstatistic data
209    std::unordered_map<uint32_t, RecordStatistic> recordStatisticsMap_;
210    // Key is call stack id, value is recordstatistic data pointer
211    std::unordered_map<uint32_t, RecordStatistic*> statisticsPeriodData_;
212    // Key is alloc or mmap address, value first is mallocsize, second is recordstatistic data pointer
213    std::unordered_map<uint64_t, std::pair<uint64_t, RecordStatistic*>> allocAddrMap_;
214    // Key is alloc or mmap address, value is ReportEventBaseData list iterator
215    std::unordered_map<uint64_t, std::list<ReportEventBaseData>::iterator> applyAndReleaseMatchIntervallMap_;
216    std::list<ReportEventBaseData> applyAndReleaseMatchPeriodListData_;
217    std::chrono::seconds applyAndReleaseMatchInterval_{0};
218    // used for plugin data
219    clockid_t pluginDataClockId_ = CLOCK_REALTIME;
220    // used for clac wait time in StackDataRepeater::TakeRawData() or statistics HookData
221    clockid_t hookDataClockId_ = CLOCK_REALTIME;
222    FILE* fpHookData_ {nullptr};
223    bool isHookStandaloneSerialize_ {false};
224    int32_t pid_ {-1};
225    std::mutex mtx_;
226    bool isSaService_{false};
227    std::mutex allocAddrMapMtx_;
228    bool isProtobufSerialize_{true};
229    WriterStructPtr resultWriter_{nullptr};
230    std::variant<BatchNativeHookData, OHOS::Developtools::Profiler::ProtoEncoder::BatchNativeHookData> stackData_;
231    uint64_t flushSize_{0};
232    uint64_t bufferSize_{0};
233    bool statisticsModelFlushCallstack_{false};
234    OHOS::Developtools::Profiler::ProtoEncoder::ProfilerPluginData profilerPluginData_;
235    // Key is js stack id , value is js raw stack pointer
236    std::map<uint64_t, const char*> jsStackMap_ = {};
237    std::set<std::string> jsStackSet_ = {};
238    bool unwindFailReport_ = true;
239    std::vector<std::string> prctlPeriodTags_; // applyAndReleaseMatchInterval mode used
240    std::vector<std::string> fpJsCallStacks_;
241    std::atomic<uint64_t> napiIndex_{1};
242    ScheduleTaskManager scheduleTaskManager_;
243    uint32_t nmdFd_ = 0;
244};
245
246#endif // STACK_PREPROCESS_H