• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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