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 
38 using WriterStructPtr = std::unique_ptr<WriterStruct>::pointer;
39 class StackPreprocess : public std::enable_shared_from_this<StackPreprocess> {
40 public:
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();
SetPid(int32_t pid)64     inline void SetPid(int32_t pid)
65     {
66         pid_ = pid;
67     }
InitStatisticsTime()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>&);
SetNmdFd(uint32_t fd)79     void SetNmdFd(uint32_t fd)
80     {
81         nmdFd_ = fd;
82     }
SetFlushSize(uint64_t size)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 
94 private:
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 {
ScopedLockFileStackPreprocess::ScopedLockFile112         ScopedLockFile(FILE* fpHook): fpHookData(fpHook)
113         {
114             flockfile(fpHookData);
115         }
~ScopedLockFileStackPreprocess::ScopedLockFile116         ~ScopedLockFile()
117         {
118             funlockfile(fpHookData);
119         }
120         FILE* fpHookData {nullptr};
121     };
122 
123 private:
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);
185 private:
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