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 "hook_standalone.h"
16 #include <functional>
17 #include <linux/types.h>
18 #include "utilities.h"
19 #include "hook_service.h"
20 #include "logging.h"
21 #include "share_memory_allocator.h"
22 #include "event_notifier.h"
23 #include "epoll_event_poller.h"
24 #include "virtual_runtime.h"
25
26 namespace OHOS {
27 namespace Developtools {
28 namespace Profiler {
29 namespace Hook {
30 const int DEFAULT_EVENT_POLLING_INTERVAL = 5000;
31 std::string g_smbName = "hooknativesmb";
32 std::shared_ptr<OHOS::Developtools::NativeDaemon::VirtualRuntime> g_runtimeInstance;
33 std::unique_ptr<EpollEventPoller> g_eventPoller_;
34 std::shared_ptr<ShareMemoryBlock> g_shareMemoryBlock;
35 std::shared_ptr<EventNotifier> g_eventNotifier;
36 std::shared_ptr<HookService> g_hookService;
37 uint32_t g_maxStackDepth;
38 std::unique_ptr<FILE, decltype(&fclose)> g_fpHookFile(nullptr, nullptr);
39
writeFrames(int type,const struct timespec & ts,void * addr,uint32_t mallocSize,const std::vector<OHOS::Developtools::NativeDaemon::CallFrame> & callsFrames)40 void writeFrames(int type, const struct timespec& ts, void* addr, uint32_t mallocSize,
41 const std::vector<OHOS::Developtools::NativeDaemon::CallFrame>& callsFrames)
42 {
43 if (type == 0) {
44 fprintf(g_fpHookFile.get(), "malloc;%" PRId64 ";%ld;0x%" PRIx64 ";%u\n",
45 (int64_t)ts.tv_sec, ts.tv_nsec, (uint64_t)addr, mallocSize);
46 } else if (type == 1) {
47 fprintf(g_fpHookFile.get(), "free;%" PRId64 ";%ld;0x%" PRIx64 "\n",
48 (int64_t)ts.tv_sec, ts.tv_nsec, (uint64_t)addr);
49 } else {
50 return;
51 }
52
53 for (size_t idx = 0, size = callsFrames.size(); idx < size; ++idx) {
54 (void)fprintf(g_fpHookFile.get(), "0x%" PRIx64 ";0x%" PRIx64 ";%s;%s;0x%" PRIx64 ";%" PRIu64 "\n",
55 callsFrames[idx].ip_, callsFrames[idx].sp_, callsFrames[idx].symbolName_.c_str(),
56 callsFrames[idx].filePath_.c_str(), callsFrames[idx].offset_, callsFrames[idx].symbolOffset_);
57 }
58 }
59
ReadShareMemory(uint64_t duration,const std::string & performance_filename)60 void ReadShareMemory(uint64_t duration, const std::string& performance_filename)
61 {
62 CHECK_NOTNULL(g_shareMemoryBlock, NO_RETVAL, "smb is null!");
63 uint64_t value = g_eventNotifier->Take();
64
65 static bool first_flag = true;
66 static bool end_flag = false;
67 static uint64_t times = 0;
68
69 struct timespec first_time;
70 struct timespec begin_time;
71 struct timespec end_time;
72 uint64_t total_time = 0;
73
74 while (true) {
75 bool ret = g_shareMemoryBlock->TakeData([&](const int8_t data[], uint32_t size) -> bool {
76 std::vector<u64> u64regs;
77 uint32_t *regAddr = nullptr;
78 uint32_t stackSize;
79 pid_t tid;
80 pid_t pid;
81 void *addr = nullptr;
82 int8_t* tmp = const_cast<int8_t *>(data);
83
84 struct timespec ts = {};
85 if (memcpy_s(&ts, sizeof(ts), data, sizeof(ts)) != EOK) {
86 HILOG_ERROR(LOG_CORE, "memcpy_s ts failed");
87 }
88 uint32_t type = *(reinterpret_cast<uint32_t *>(tmp + sizeof(ts)));
89 uint32_t mallocSize = *(reinterpret_cast<uint32_t *>(tmp + sizeof(ts) + sizeof(type)));
90 addr = *(reinterpret_cast<void **>(tmp + sizeof(ts) + sizeof(type) + sizeof(mallocSize)));
91 stackSize = *(reinterpret_cast<uint32_t *>(tmp + sizeof(ts)
92 + sizeof(type) + sizeof(mallocSize) + sizeof(void *)));
93 std::unique_ptr<uint8_t[]> stackData = std::make_unique<uint8_t[]>(stackSize);
94 if (memcpy_s(stackData.get(), stackSize, tmp + sizeof(stackSize) + sizeof(ts) + sizeof(type)
95 + sizeof(mallocSize) + sizeof(void *), stackSize) != EOK) {
96 HILOG_ERROR(LOG_CORE, "memcpy_s data failed");
97 }
98 tid = *(reinterpret_cast<pid_t *>(tmp + sizeof(stackSize) + stackSize + sizeof(ts)
99 + sizeof(type) + sizeof(mallocSize) + sizeof(void *)));
100 pid = *(reinterpret_cast<pid_t *>(tmp + sizeof(stackSize) + stackSize + sizeof(ts)
101 + sizeof(type) + sizeof(mallocSize) + sizeof(void *) + sizeof(tid)));
102 regAddr = reinterpret_cast<uint32_t *>(tmp + sizeof(tid) + sizeof(pid) + sizeof(stackSize)
103 + stackSize + sizeof(ts) + sizeof(type) + sizeof(mallocSize) + sizeof(void *));
104
105 int reg_count = (size - sizeof(tid) - sizeof(pid) - sizeof(stackSize) - stackSize
106 - sizeof(ts) - sizeof(type) - sizeof(mallocSize) - sizeof(void *))
107 / sizeof(uint32_t);
108 if (reg_count <= 0) {
109 HILOG_ERROR(LOG_CORE, "data error size = %u", size);
110 }
111 for (int idx = 0; idx < reg_count; ++idx) {
112 u64regs.push_back(*regAddr++);
113 }
114
115 if (!end_flag && duration != 0) {
116 clock_gettime(CLOCK_REALTIME, &begin_time);
117 if (first_flag) {
118 first_flag = false;
119 first_time = begin_time;
120 }
121 }
122
123 std::vector<OHOS::Developtools::NativeDaemon::CallFrame> callsFrames;
124 g_runtimeInstance->UnwindStack(u64regs, stackData.get(), stackSize, pid, tid, callsFrames,
125 (g_maxStackDepth > 0) ? g_maxStackDepth : OHOS::Developtools::NativeDaemon::MAX_CALL_FRAME_UNWIND_SIZE);
126
127 if (!end_flag && duration != 0) {
128 clock_gettime(CLOCK_REALTIME, &end_time);
129 total_time += (end_time.tv_sec - begin_time.tv_sec) * 1000000000LLU +
130 (end_time.tv_nsec - begin_time.tv_nsec);
131 ++times;
132 if (end_time.tv_sec - first_time.tv_sec >= duration) {
133 end_flag = true;
134 FILE *fp = fopen(performance_filename.c_str(), "a");
135 if (fp) {
136 time_t now = time(NULL);
137 struct tm now_tm;
138 localtime_r(&now, &now_tm);
139 fprintf(fp, "Current time: %04d-%02d-%02d %02d:%02d:%02d\n", now_tm.tm_year + 1900,
140 now_tm.tm_mon + 1, now_tm.tm_mday, now_tm.tm_hour, now_tm.tm_min, now_tm.tm_sec);
141 fprintf(fp, "Total durations: %" PRIu64 " nanoseconds\n", total_time);
142 fprintf(fp, "Total times: %" PRIu64 "\n", times);
143 fprintf(fp, "Average unwinding stack time: %.2f nanoseconds\n\n",
144 (double)(total_time) / times);
145 fclose(fp);
146 printf("Performance data has already been generated.\n");
147 }
148 }
149 }
150
151 writeFrames(type, ts, addr, mallocSize, callsFrames);
152 return true;
153 });
154 if (!ret) {
155 break;
156 }
157 }
158 }
159
StartHook(HookData & hookData)160 bool StartHook(HookData& hookData)
161 {
162 g_runtimeInstance = std::make_shared<OHOS::Developtools::NativeDaemon::VirtualRuntime>();
163 FILE *fp = fopen(hookData.fileName.c_str(), "wb+");
164 if (fp != nullptr) {
165 g_fpHookFile.reset();
166 g_fpHookFile = std::unique_ptr<FILE, decltype(&fclose)>(fp, fclose);
167 }
168 if (g_fpHookFile == nullptr) {
169 printf("create file failed!\n");
170 return false;
171 }
172 // create smb and eventNotifier
173 g_shareMemoryBlock = ShareMemoryAllocator::GetInstance().CreateMemoryBlockLocal(g_smbName, hookData.smbSize);
174
175 g_eventNotifier = EventNotifier::Create(0, EventNotifier::NONBLOCK);
176
177 // start event poller task
178 g_eventPoller_ = std::make_unique<EpollEventPoller>(DEFAULT_EVENT_POLLING_INTERVAL);
179 g_eventPoller_->Init();
180 g_eventPoller_->Start();
181
182 g_eventPoller_->AddFileDescriptor(g_eventNotifier->GetFd(),
183 std::bind(ReadShareMemory, hookData.duration, hookData.performance_filename));
184
185 HILOG_INFO(LOG_CORE, "hookservice smbFd = %d, eventFd = %d\n", g_shareMemoryBlock->GetfileDescriptor(),
186 g_eventNotifier->GetFd());
187
188 // start service init socket
189 g_hookService = std::make_shared<HookService>(g_shareMemoryBlock->GetfileDescriptor(),
190 g_eventNotifier->GetFd(), hookData.filterSize, hookData.smbSize, hookData.pid, hookData.processName);
191
192 g_maxStackDepth = hookData.maxStackDepth;
193 return true;
194 }
195 } // namespace Hook
196 } // namespace Profiler
197 } // namespace Developtools
198 } // namespace OHOS
199