1 /*
2 * Copyright (c) 2021 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 #include "stack_preprocess.h"
16
17 #include <unistd.h>
18
19 #include "logging.h"
20 #include "plugin_service_types.pb.h"
21
22 static std::atomic<uint64_t> timeCost = 0;
23 static std::atomic<uint64_t> unwindTimes = 0;
24
25 constexpr static uint32_t MAX_BUFFER_SIZE = 50 * 1024;
26 constexpr static uint32_t MAX_MATCH_CNT = 1000;
27 constexpr static uint32_t MAX_MATCH_INTERVAL = 2000;
28 constexpr static uint32_t LOG_PRINT_TIMES = 10000;
29 constexpr static uint32_t FUNCTION_MAP_LOG_PRINT = 100;
30 constexpr static uint32_t FILE_MAP_LOG_PRINT = 10;
31 constexpr static uint32_t MAX_BATCH_CNT = 5;
32
33 using namespace OHOS::Developtools::NativeDaemon;
34
StackPreprocess(const StackDataRepeaterPtr & dataRepeater,NativeHookConfig hookConfig)35 StackPreprocess::StackPreprocess(const StackDataRepeaterPtr& dataRepeater, NativeHookConfig hookConfig)
36 : dataRepeater_(dataRepeater), buffer_(new (std::nothrow) uint8_t[MAX_BUFFER_SIZE]),
37 hookConfig_(hookConfig), fpHookData_(nullptr, nullptr)
38 {
39 runtime_instance = std::make_shared<VirtualRuntime>();
40
41 if (hookConfig_.malloc_free_matching_interval() > MAX_MATCH_INTERVAL) {
42 HILOG_INFO(LOG_CORE, "Not support set %d", hookConfig_.malloc_free_matching_interval());
43 hookConfig_.set_malloc_free_matching_interval(MAX_MATCH_INTERVAL);
44 }
45
46 if (hookConfig_.malloc_free_matching_cnt() > MAX_MATCH_CNT) {
47 HILOG_INFO(LOG_CORE, "Not support set %d", hookConfig_.malloc_free_matching_cnt());
48 hookConfig_.set_malloc_free_matching_cnt(MAX_MATCH_CNT);
49 }
50 HILOG_INFO(LOG_CORE, "malloc_free_matching_interval = %d malloc_free_matching_cnt = %d\n",
51 hookConfig_.malloc_free_matching_interval(), hookConfig_.malloc_free_matching_cnt());
52 // create file
53 if (hookConfig_.save_file()) {
54 if (hookConfig_.file_name() != "") {
55 HILOG_DEBUG(LOG_CORE, "save file name = %s", hookConfig_.file_name().c_str());
56 FILE *fp = fopen(hookConfig_.file_name().c_str(), "wb+");
57 if (fp) {
58 fpHookData_.reset();
59 fpHookData_ = std::unique_ptr<FILE, decltype(&fclose)>(fp, fclose);
60 } else {
61 fpHookData_.reset();
62 }
63 } else {
64 HILOG_WARN(LOG_CORE, "If you need to save the file, please set the file_name");
65 }
66 }
67 }
68
~StackPreprocess()69 StackPreprocess::~StackPreprocess()
70 {
71 isStopTakeData_ = true;
72 if (dataRepeater_) {
73 dataRepeater_->Close();
74 }
75 if (thread_.joinable()) {
76 thread_.join();
77 }
78 runtime_instance = nullptr;
79 fpHookData_ = nullptr;
80 }
81
SetWriter(const std::shared_ptr<BufferWriter> & writer)82 void StackPreprocess::SetWriter(const std::shared_ptr<BufferWriter>& writer)
83 {
84 writer_ = writer;
85 }
86
StartTakeResults()87 bool StackPreprocess::StartTakeResults()
88 {
89 CHECK_NOTNULL(dataRepeater_, false, "data repeater null");
90
91 std::thread demuxer(&StackPreprocess::TakeResults, this);
92 CHECK_TRUE(demuxer.get_id() != std::thread::id(), false, "demuxer thread invalid");
93
94 thread_ = std::move(demuxer);
95 isStopTakeData_ = false;
96 return true;
97 }
98
StopTakeResults()99 bool StackPreprocess::StopTakeResults()
100 {
101 HILOG_INFO(LOG_CORE, "start StopTakeResults");
102 CHECK_NOTNULL(dataRepeater_, false, "data repeater null");
103 CHECK_TRUE(thread_.get_id() != std::thread::id(), false, "thread invalid");
104
105 isStopTakeData_ = true;
106 dataRepeater_->PutRawStack(nullptr);
107 HILOG_INFO(LOG_CORE, "Wait thread join");
108
109 if (thread_.joinable()) {
110 thread_.join();
111 }
112 HILOG_INFO(LOG_CORE, "Wait thread join success");
113 return true;
114 }
115
TakeResults()116 void StackPreprocess::TakeResults()
117 {
118 if (!dataRepeater_) {
119 return;
120 }
121
122 size_t minStackDepth = hookConfig_.max_stack_depth() > MIN_STACK_DEPTH
123 ? MIN_STACK_DEPTH : hookConfig_.max_stack_depth();
124 if (hookConfig_.blocked()) {
125 minStackDepth = static_cast<size_t>(hookConfig_.max_stack_depth());
126 }
127 minStackDepth += FILTER_STACK_DEPTH;
128 HILOG_INFO(LOG_CORE, "TakeResults thread %d, start!", gettid());
129 while (1) {
130 BatchNativeHookData stackData;
131 RawStackPtr batchRawStack[MAX_BATCH_CNT] = {nullptr};
132 auto result = dataRepeater_->TakeRawData(hookConfig_.malloc_free_matching_interval(),
133 MAX_BATCH_CNT, batchRawStack);
134 if (!result || isStopTakeData_) {
135 break;
136 }
137 for (unsigned int i = 0; i < MAX_BATCH_CNT; i++) {
138 auto rawData = batchRawStack[i];
139 if (!rawData || isStopTakeData_) {
140 break;
141 }
142
143 if (!rawData->reportFlag) {
144 ignoreCnts_++;
145 if (ignoreCnts_ % LOG_PRINT_TIMES == 0) {
146 HILOG_INFO(LOG_CORE, "ignoreCnts_ = %d quene size = %zu\n", ignoreCnts_, dataRepeater_->Size());
147 }
148 continue;
149 }
150 eventCnts_++;
151 if (eventCnts_ % LOG_PRINT_TIMES == 0) {
152 HILOG_INFO(LOG_CORE, "eventCnts_ = %d quene size = %zu\n", eventCnts_, dataRepeater_->Size());
153 }
154
155 std::vector<u64> u64regs;
156 std::vector<CallFrame> callsFrames;
157
158 if (hookConfig_.fp_unwind()) {
159 for (int idx = 1; idx < MAX_UNWIND_DEPTH + 1; ++idx) {
160 if (rawData->stackConext.ip[idx] == 0) {
161 break;
162 }
163 OHOS::Developtools::NativeDaemon::CallFrame frame(0);
164 frame.ip_ = rawData->stackConext.ip[idx];
165 callsFrames.push_back(frame);
166 }
167 } else {
168 int regCount = OHOS::Developtools::NativeDaemon::RegisterGetCount();
169 #if defined(__arm__)
170 uint32_t *regAddrArm = reinterpret_cast<uint32_t *>(rawData->stackConext.regs);
171 for (int idx = 0; idx < regCount; ++idx) {
172 u64regs.push_back(*regAddrArm++);
173 }
174 #else
175 uint64_t *regAddrAarch64 = reinterpret_cast<uint64_t *>(rawData->stackConext.regs);
176 for (int idx = 0; idx < regCount; ++idx) {
177 u64regs.push_back(*regAddrAarch64++);
178 }
179 #endif
180 }
181 #ifdef PERFORMANCE_DEBUG
182 struct timespec start = {};
183 clock_gettime(CLOCK_REALTIME, &start);
184 #endif
185 size_t stackDepth = (hookConfig_.max_stack_depth() > 0)
186 ? hookConfig_.max_stack_depth() + FILTER_STACK_DEPTH
187 : MAX_CALL_FRAME_UNWIND_SIZE;
188 if (rawData->reduceStackFlag) {
189 stackDepth = minStackDepth;
190 }
191 bool ret = runtime_instance->UnwindStack(u64regs, rawData->stackData.get(), rawData->stackSize,
192 rawData->stackConext.pid, rawData->stackConext.tid, callsFrames,
193 stackDepth);
194 if (!ret) {
195 HILOG_ERROR(LOG_CORE, "unwind fatal error");
196 continue;
197 }
198 if (rawData->stackConext.type == MMAP_MSG) {
199 // if mmap msg trigger by dlopen, update maps voluntarily
200 for (auto &callsFrame : callsFrames) {
201 if (callsFrame.symbolName_ == "dlopen") {
202 HILOG_INFO(LOG_CORE, "mmap msg trigger by dlopen, update maps voluntarily");
203 runtime_instance->UpdateMaps(rawData->stackConext.pid, rawData->stackConext.tid);
204 break;
205 }
206 }
207 }
208 #ifdef PERFORMANCE_DEBUG
209 struct timespec end = {};
210 clock_gettime(CLOCK_REALTIME, &end);
211 timeCost += (end.tv_sec - start.tv_sec) * MAX_MATCH_CNT * MAX_MATCH_CNT * MAX_MATCH_CNT +
212 (end.tv_nsec - start.tv_nsec);
213 unwindTimes++;
214 if (unwindTimes % LOG_PRINT_TIMES == 0) {
215 HILOG_ERROR(LOG_CORE, "unwindTimes %" PRIu64" cost time = %" PRIu64" mean cost = %" PRIu64"\n",
216 unwindTimes.load(), timeCost.load(), timeCost.load() / unwindTimes.load());
217 }
218 #endif
219 if (hookConfig_.save_file() && hookConfig_.file_name() != "") {
220 writeFrames(rawData, callsFrames);
221 } else if (!hookConfig_.save_file()) {
222 SetHookData(rawData, callsFrames, stackData);
223 }
224 }
225 if (stackData.events().size() > 0) {
226 size_t length = stackData.ByteSizeLong();
227 if (length < MAX_BUFFER_SIZE) {
228 stackData.SerializeToArray(buffer_.get(), length);
229 ProfilerPluginData pluginData;
230 pluginData.set_name("nativehook");
231 pluginData.set_version("1.01");
232 pluginData.set_status(0);
233 pluginData.set_data(buffer_.get(), length);
234 struct timespec ts;
235 clock_gettime(CLOCK_REALTIME, &ts);
236 pluginData.set_clock_id(ProfilerPluginData::CLOCKID_REALTIME);
237 pluginData.set_tv_sec(ts.tv_sec);
238 pluginData.set_tv_nsec(ts.tv_nsec);
239 writer_->WriteMessage(pluginData, "nativehook");
240 writer_->Flush();
241 }
242 }
243 }
244 HILOG_INFO(LOG_CORE, "TakeResults thread %d, exit!", gettid());
245 }
246
SetHookData(RawStackPtr rawStack,std::vector<CallFrame> & callsFrames,BatchNativeHookData & batchNativeHookData)247 void StackPreprocess::SetHookData(RawStackPtr rawStack,
248 std::vector<CallFrame>& callsFrames, BatchNativeHookData& batchNativeHookData)
249 {
250 NativeHookData* hookData = batchNativeHookData.add_events();
251 hookData->set_tv_sec(rawStack->stackConext.ts.tv_sec);
252 hookData->set_tv_nsec(rawStack->stackConext.ts.tv_nsec);
253 // ignore the first two frame if dwarf unwind
254 size_t idx = hookConfig_.fp_unwind() ? 0 : FILTER_STACK_DEPTH;
255
256 if (rawStack->stackConext.type == MALLOC_MSG) {
257 AllocEvent* allocEvent = hookData->mutable_alloc_event();
258 allocEvent->set_pid(rawStack->stackConext.pid);
259 allocEvent->set_tid(rawStack->stackConext.tid);
260 allocEvent->set_addr((uint64_t)rawStack->stackConext.addr);
261 allocEvent->set_size(static_cast<uint64_t>(rawStack->stackConext.mallocSize));
262 std::string name = rawStack->stackConext.tname;
263 if (!name.empty()) {
264 allocEvent->set_thread_name_id(GetThreadIdx(name, batchNativeHookData));
265 }
266 for (; idx < callsFrames.size(); ++idx) {
267 Frame* frame = allocEvent->add_frame_info();
268 SetFrameInfo(*frame, callsFrames[idx], batchNativeHookData);
269 }
270 } else if (rawStack->stackConext.type == FREE_MSG) {
271 FreeEvent* freeEvent = hookData->mutable_free_event();
272 freeEvent->set_pid(rawStack->stackConext.pid);
273 freeEvent->set_tid(rawStack->stackConext.tid);
274 freeEvent->set_addr((uint64_t)rawStack->stackConext.addr);
275 std::string name = rawStack->stackConext.tname;
276 if (!name.empty()) {
277 freeEvent->set_thread_name_id(GetThreadIdx(name, batchNativeHookData));
278 }
279 for (; idx < callsFrames.size(); ++idx) {
280 Frame* frame = freeEvent->add_frame_info();
281 SetFrameInfo(*frame, callsFrames[idx], batchNativeHookData);
282 }
283 } else if (rawStack->stackConext.type == MMAP_MSG) {
284 MmapEvent* mmapEvent = hookData->mutable_mmap_event();
285 mmapEvent->set_pid(rawStack->stackConext.pid);
286 mmapEvent->set_tid(rawStack->stackConext.tid);
287 mmapEvent->set_addr((uint64_t)rawStack->stackConext.addr);
288 mmapEvent->set_size(static_cast<uint64_t>(rawStack->stackConext.mallocSize));
289 std::string name = rawStack->stackConext.tname;
290 if (!name.empty()) {
291 mmapEvent->set_thread_name_id(GetThreadIdx(name, batchNativeHookData));
292 }
293 for (; idx < callsFrames.size(); ++idx) {
294 Frame* frame = mmapEvent->add_frame_info();
295 SetFrameInfo(*frame, callsFrames[idx], batchNativeHookData);
296 }
297 } else if (rawStack->stackConext.type == MUNMAP_MSG) {
298 MunmapEvent* munmapEvent = hookData->mutable_munmap_event();
299 munmapEvent->set_pid(rawStack->stackConext.pid);
300 munmapEvent->set_tid(rawStack->stackConext.tid);
301 munmapEvent->set_addr((uint64_t)rawStack->stackConext.addr);
302 munmapEvent->set_size(static_cast<uint64_t>(rawStack->stackConext.mallocSize));
303 std::string name = rawStack->stackConext.tname;
304 if (!name.empty()) {
305 munmapEvent->set_thread_name_id(GetThreadIdx(name, batchNativeHookData));
306 }
307 for (; idx < callsFrames.size(); ++idx) {
308 Frame* frame = munmapEvent->add_frame_info();
309 SetFrameInfo(*frame, callsFrames[idx], batchNativeHookData);
310 }
311 } else if (rawStack->stackConext.type == MEMORY_TAG) {
312 MemTagEvent* tagEvent = hookData->mutable_tag_event();
313 tagEvent->set_tag(rawStack->stackConext.tname);
314 tagEvent->set_size(rawStack->stackConext.mallocSize);
315 tagEvent->set_addr((uint64_t)rawStack->stackConext.addr);
316 }
317 }
318
GetThreadIdx(std::string threadName,BatchNativeHookData & batchNativeHookData)319 uint32_t StackPreprocess::GetThreadIdx(std::string threadName, BatchNativeHookData& batchNativeHookData)
320 {
321 auto it = threadMap_.find(threadName);
322 if (it != threadMap_.end()) {
323 return it->second;
324 } else {
325 auto hookData = batchNativeHookData.add_events();
326 auto* thread = hookData->mutable_thread_name_map();
327 thread->set_id(threadMap_.size() + 1);
328 thread->set_name(threadName);
329 threadMap_.insert(std::pair<std::string, uint32_t>(threadName, threadMap_.size() + 1));
330
331 HILOG_INFO(LOG_CORE, "threadName = %s, functionMap_.size() = %zu\n", threadName.c_str(), threadMap_.size());
332 return threadMap_.size();
333 }
334 }
335
writeFrames(RawStackPtr rawStack,const std::vector<CallFrame> & callsFrames)336 void StackPreprocess::writeFrames(RawStackPtr rawStack, const std::vector<CallFrame>& callsFrames)
337 {
338 CHECK_TRUE(fpHookData_.get() != nullptr, NO_RETVAL, "fpHookData_ is nullptr, please check file_name(%s)",
339 hookConfig_.file_name().c_str());
340 if (rawStack->stackConext.type == MEMORY_TAG) {
341 fprintf(fpHookData_.get(), "memtag;%u;%u;%" PRId64 ";%ld;0x%" PRIx64 ":tag:%s\n",
342 rawStack->stackConext.pid, rawStack->stackConext.tid,
343 (int64_t)rawStack->stackConext.ts.tv_sec, rawStack->stackConext.ts.tv_nsec,
344 (uint64_t)rawStack->stackConext.addr, rawStack->stackConext.tname);
345 return;
346 }
347 std::string tag = "";
348 switch (rawStack->stackConext.type) {
349 case FREE_MSG:
350 tag = "free";
351 break;
352 case MALLOC_MSG:
353 tag = "malloc";
354 break;
355 case MMAP_MSG:
356 tag = "mmap";
357 break;
358 case MUNMAP_MSG:
359 tag = "munmap";
360 break;
361 default:
362 break;
363 }
364
365 fprintf(fpHookData_.get(), "%s;%u;%u;%" PRId64 ";%ld;0x%" PRIx64 ";%zu\n", tag.c_str(),
366 rawStack->stackConext.pid, rawStack->stackConext.tid, (int64_t)rawStack->stackConext.ts.tv_sec,
367 rawStack->stackConext.ts.tv_nsec, (uint64_t)rawStack->stackConext.addr, rawStack->stackConext.mallocSize);
368
369 for (size_t idx = 0; idx < callsFrames.size(); ++idx) {
370 (void)fprintf(fpHookData_.get(), "0x%" PRIx64 ";0x%" PRIx64 ";%s;%s;0x%" PRIx64 ";%" PRIu64 "\n",
371 callsFrames[idx].ip_, callsFrames[idx].sp_, std::string(callsFrames[idx].symbolName_).c_str(),
372 std::string(callsFrames[idx].filePath_).c_str(), callsFrames[idx].offset_, callsFrames[idx].symbolOffset_);
373 }
374 }
375
SetFrameInfo(Frame & frame,CallFrame & callsFrame,BatchNativeHookData & batchNativeHookData)376 void StackPreprocess::SetFrameInfo(Frame& frame, CallFrame& callsFrame, BatchNativeHookData& batchNativeHookData)
377 {
378 frame.set_ip(callsFrame.ip_);
379 frame.set_sp(callsFrame.sp_);
380 frame.set_offset(callsFrame.offset_);
381 frame.set_symbol_offset(callsFrame.symbolOffset_);
382 if (hookConfig_.string_compressed()) {
383 auto itFuntion = functionMap_.find(std::string(callsFrame.symbolName_));
384 if (itFuntion != functionMap_.end()) {
385 frame.set_symbol_name_id(itFuntion->second);
386 } else {
387 frame.set_symbol_name_id(functionMap_.size() + 1);
388 auto hookData = batchNativeHookData.add_events();
389 SymbolMap* symbolMap = hookData->mutable_symbol_name();
390 symbolMap->set_id(functionMap_.size() + 1);
391 symbolMap->set_name(std::string(callsFrame.symbolName_));
392 functionMap_.insert(std::pair<std::string, uint32_t>(callsFrame.symbolName_, functionMap_.size() + 1));
393 if (functionMap_.size() % FUNCTION_MAP_LOG_PRINT == 0) {
394 HILOG_INFO(LOG_CORE, "functionMap_.size() = %zu\n", functionMap_.size());
395 }
396 }
397
398 auto itFile = fileMap_.find(std::string(callsFrame.filePath_));
399 if (itFile != fileMap_.end()) {
400 frame.set_file_path_id(itFile->second);
401 } else {
402 frame.set_file_path_id(fileMap_.size() + 1);
403 auto hookData = batchNativeHookData.add_events();
404 FilePathMap* filepathMap = hookData->mutable_file_path();
405 filepathMap->set_id(fileMap_.size() + 1);
406 filepathMap->set_name(std::string(callsFrame.filePath_));
407 fileMap_.insert(std::pair<std::string, uint32_t>(callsFrame.filePath_, fileMap_.size() + 1));
408 if (fileMap_.size() % FILE_MAP_LOG_PRINT == 0) {
409 HILOG_INFO(LOG_CORE, "fileMap.size() = %zu\n", fileMap_.size());
410 }
411 }
412 } else {
413 frame.set_symbol_name(std::string(callsFrame.symbolName_));
414 frame.set_file_path(std::string(callsFrame.filePath_));
415 }
416 }
417