1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. 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 #include "hook_socket_client.h"
16 #include "runtime_stack_range.h"
17 #include "common.h"
18 #include "hook_common.h"
19 #include "unix_socket_client.h"
20 #include "share_memory_allocator.h"
21 #include "logging.h"
22 #include "sampling.h"
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <fcntl.h>
27 #include <cstdio>
28 #include <cstdlib>
29 #include <cstring>
30 #include <iostream>
31
32 namespace {
33 constexpr int FLUSH_FLAG = 20;
34 constexpr int ONLY_NMD_TYPE = 2;
35 constexpr int SIMP_NMD = 3;
36 std::atomic<bool> g_disableHook = true;
37 constexpr uint32_t MEMCHECK_DETAILINFO_MAXSIZE = 102400;
38
39 struct OptArg {
40 size_t pos;
41 char *buf;
42 };
43
44 } // namespace
45
46 using namespace OHOS::Developtools::NativeDaemon;
GetRealTime()47 static std::string GetRealTime()
48 {
49 time_t now = time(nullptr);
50 tm tm;
51 const int timeLength = 64;
52 char stampStr[timeLength] = {0};
53
54 if (localtime_r(&now, &tm) == nullptr || strftime(stampStr, timeLength, "%Y/%m/%d %H:%M:%S", &tm) == 0) {
55 return "error time format!";
56 }
57 return std::string(stampStr);
58 }
59
NmdWriteStat(void * arg,const char * buf)60 static void NmdWriteStat(void *arg, const char *buf)
61 {
62 struct OptArg *opt = static_cast<struct OptArg*>(arg);
63 std::string getNmdTime = std::to_string(getpid()) + " " + GetRealTime() + "\n";
64 size_t nmdTimeLen = getNmdTime.size();
65 if (strncpy_s(opt->buf + opt->pos, MEMCHECK_DETAILINFO_MAXSIZE - opt->pos,
66 getNmdTime.c_str(), nmdTimeLen) != EOK) {
67 return;
68 }
69 opt->pos += nmdTimeLen;
70
71 size_t len = strlen(buf);
72 if (len + opt->pos + 1 > MEMCHECK_DETAILINFO_MAXSIZE) {
73 return;
74 }
75 if (strncpy_s(opt->buf + opt->pos, MEMCHECK_DETAILINFO_MAXSIZE - opt->pos, buf, len) != EOK) {
76 return;
77 }
78 opt->pos += len;
79 }
80
HookSocketClient(int pid,ClientConfig * config,Sampling * sampler,std::atomic<Range> * targetedRange,std::atomic<int> * memCount,void (* disableHookCallback)())81 HookSocketClient::HookSocketClient(int pid, ClientConfig *config, Sampling *sampler,
82 std::atomic<Range>* targetedRange, std::atomic<int>* memCount,
83 void (*disableHookCallback)())
84 : pid_(pid), config_(config), sampler_(sampler), targetedRange_(targetedRange),
85 sharedMemCount_(memCount), disableHookCallback_(disableHookCallback)
86 {
87 g_disableHook = true;
88 int sharedMemCount = (config_->offlineSymbolization) ? SHARED_MEMORY_NUM : 1;
89 smbFds_.reserve(sharedMemCount);
90 eventFds_.reserve(sharedMemCount);
91 stackWriterList_.reserve(sharedMemCount);
92 unixSocketClient_ = nullptr;
93 serviceName_ = "HookService";
94 Connect(DEFAULT_UNIX_SOCKET_HOOK_FULL_PATH);
95 }
96
~HookSocketClient()97 HookSocketClient::~HookSocketClient()
98 {
99 for (size_t i = 0; i < stackWriterList_.size(); ++i) {
100 if (stackWriterList_[i]) {
101 stackWriterList_[i]->Flush();
102 }
103 stackWriterList_[i] = nullptr;
104 }
105 unixSocketClient_ = nullptr;
106 g_disableHook = true;
107 }
108
Connect(const std::string addrname)109 bool HookSocketClient::Connect(const std::string addrname)
110 {
111 if (unixSocketClient_ != nullptr) {
112 return false;
113 }
114 unixSocketClient_ = std::make_shared<UnixSocketClient>();
115 if (!unixSocketClient_->Connect(addrname, *this, disableHookCallback_)) {
116 unixSocketClient_ = nullptr;
117 return false;
118 }
119
120 unixSocketClient_->SendHookConfig(reinterpret_cast<uint8_t *>(&pid_), sizeof(pid_));
121 return true;
122 }
123
ProtocolProc(SocketContext & context,uint32_t pnum,const int8_t * buf,const uint32_t size)124 bool HookSocketClient::ProtocolProc(SocketContext &context, uint32_t pnum, const int8_t *buf, const uint32_t size)
125 {
126 if (size != sizeof(ClientConfig)) {
127 return true;
128 }
129 *config_ = *reinterpret_cast<ClientConfig *>(const_cast<int8_t*>(buf));
130 config_->maxStackDepth = config_->maxStackDepth > MAX_UNWIND_DEPTH ? MAX_UNWIND_DEPTH : config_->maxStackDepth;
131 std::string configStr = config_->ToString();
132 sampler_->InitSampling(config_->sampleInterval);
133 int sharedMemCount = (config_->offlineSymbolization) ? SHARED_MEMORY_NUM : 1;
134 for (int i = 0; i < sharedMemCount; ++i) {
135 int smbfd = context.ReceiveFileDiscriptor();
136 int eventfd = context.ReceiveFileDiscriptor();
137 smbFds_.push_back(smbfd);
138 eventFds_.push_back(eventfd);
139 std::string smbName = "hooknativesmb_" + std::to_string(pid_) + ":" + std::to_string(i);
140 std::shared_ptr<StackWriter> stackWriter = std::make_shared<StackWriter>(smbName, config_->shareMemorySize,
141 smbfd, eventfd, config_->isBlocked, config_->isSaMode);
142 stackWriterList_.push_back(stackWriter);
143 }
144 nmdType_ = config_->nmdType;
145 g_disableHook = false;
146 if (sharedMemCount_ != nullptr) {
147 *sharedMemCount_ = sharedMemCount;
148 }
149 largestSize_ = config_->largestSize;
150 secondLargestSize_ = config_->secondLargestSize;
151 maxGrowthSize_ = config_->maxGrowthSize;
152 sampleInterval_ = config_->sampleInterval;
153 PROFILER_LOG_INFO(LOG_CORE, "HookSocketClient::ProtocolProc, ts1.1 = %d, ts1.2 = %d, ts2 = %d, ts3 = %d",
154 largestSize_, secondLargestSize_, maxGrowthSize_, sampleInterval_);
155 if (nmdType_ == 0 || nmdType_ == ONLY_NMD_TYPE) {
156 SendNmdInfo();
157 } else if (nmdType_ == SIMP_NMD) {
158 SendSimplifiedNmdInfo();
159 }
160 if (!config_->targetSoName.empty() && !ParseTargetedMaps(*targetedRange_, config_->targetSoName)) {
161 PROFILER_LOG_ERROR(LOG_CORE, "HookSocketClient::ProtocolProc ParseTargetedMaps failed!");
162 return false;
163 }
164 return true;
165 }
166
SendStack(const void * data,size_t size)167 bool HookSocketClient::SendStack(const void* data, size_t size)
168 {
169 if (stackWriterList_.size() == 0 || stackWriterList_[0] == nullptr || unixSocketClient_ == nullptr) {
170 return false;
171 }
172
173 if (!unixSocketClient_->SendHeartBeat()) {
174 return false;
175 }
176
177 stackWriterList_[0]->WriteTimeout(data, size);
178 stackWriterList_[0]->Flush();
179
180 return true;
181 }
182
SendStackWithPayload(const void * data,size_t size,const void * payload,size_t payloadSize,int smbIndex)183 bool HookSocketClient::SendStackWithPayload(const void* data, size_t size, const void* payload,
184 size_t payloadSize, int smbIndex)
185 {
186 if (smbIndex + 1 > static_cast<int>(stackWriterList_.size())) {
187 return false;
188 }
189 std::shared_ptr<StackWriter> stackWriter = stackWriterList_[smbIndex];
190 if (stackWriter == nullptr || unixSocketClient_ == nullptr || g_disableHook) {
191 return false;
192 } else if (unixSocketClient_->GetClientState() == CLIENT_STAT_THREAD_EXITED) {
193 DisableHook();
194 return false;
195 }
196
197 bool ret = stackWriter->WriteWithPayloadTimeout(data, size, payload, payloadSize,
198 std::bind(&HookSocketClient::PeerIsConnected, this));
199 if (!ret && config_->isBlocked) {
200 DisableHook();
201 return false;
202 }
203 if (stackWriter->PrepareFlush()) {
204 stackWriter->Flush();
205 }
206 return true;
207 }
208
Flush()209 void HookSocketClient::Flush()
210 {
211 for (size_t i = 0; i < stackWriterList_.size(); ++i) {
212 if (stackWriterList_[i] == nullptr || unixSocketClient_ == nullptr) {
213 return;
214 }
215 stackWriterList_[i]->Flush();
216 }
217 }
218
DisableHook()219 void HookSocketClient::DisableHook()
220 {
221 bool expected = false;
222 if (g_disableHook.compare_exchange_strong(expected, true, std::memory_order_release, std::memory_order_relaxed)) {
223 HILOG_BASE_INFO(LOG_CORE, "%s", __func__);
224 if (disableHookCallback_) {
225 disableHookCallback_();
226 }
227 }
228 }
229
PeerIsConnected()230 bool HookSocketClient::PeerIsConnected()
231 {
232 return !unixSocketClient_->IsConnected();
233 }
234
SendNmdInfo()235 bool HookSocketClient::SendNmdInfo()
236 {
237 if ((!config_->printNmd) || (stackWriterList_.size() == 0)) {
238 return false;
239 }
240 void* nmdBuf = malloc(MEMCHECK_DETAILINFO_MAXSIZE);
241 if (nmdBuf == nullptr) {
242 return false;
243 }
244 struct OptArg opt = {0, reinterpret_cast<char*>(nmdBuf) };
245 malloc_stats_print(NmdWriteStat, &opt, "a");
246 StackRawData rawdata = {{{{0}}}};
247 rawdata.type = NMD_MSG;
248 if (stackWriterList_[0]) {
249 stackWriterList_[0]->WriteWithPayloadTimeout(&rawdata, sizeof(BaseStackRawData),
250 reinterpret_cast<int8_t*>(opt.buf), strlen(opt.buf) + 1,
251 std::bind(&HookSocketClient::PeerIsConnected, this));
252 }
253 stackWriterList_[0]->Flush();
254 free(nmdBuf);
255 return true;
256 }
257
SendSimplifiedNmdInfo()258 bool HookSocketClient::SendSimplifiedNmdInfo()
259 {
260 if ((!config_->printNmd) || (stackWriterList_.size() == 0)) {
261 return false;
262 }
263 void* nmdBuf = malloc(MEMCHECK_DETAILINFO_MAXSIZE);
264 if (nmdBuf == nullptr) {
265 return false;
266 }
267 struct OptArg opt = {0, reinterpret_cast<char*>(nmdBuf) };
268 malloc_stats_print(NmdWriteStat, &opt, "s");
269 StackRawData rawdata = {{{{0}}}};
270 rawdata.type = NMD_MSG;
271 if (stackWriterList_[0]) {
272 stackWriterList_[0]->WriteWithPayloadTimeout(&rawdata, sizeof(BaseStackRawData),
273 reinterpret_cast<int8_t*>(opt.buf), strlen(opt.buf) + 1,
274 std::bind(&HookSocketClient::PeerIsConnected, this));
275 }
276 stackWriterList_[0]->Flush();
277 free(nmdBuf);
278 return true;
279 }
280
SendEndMsg()281 bool HookSocketClient::SendEndMsg()
282 {
283 StackRawData rawdata = {{{{0}}}};
284 rawdata.type = END_MSG;
285 std::for_each(stackWriterList_.begin(), stackWriterList_.end(),
286 [&rawdata](std::shared_ptr<StackWriter>& writer) {
287 if (writer) {
288 writer->WriteTimeout(&rawdata, sizeof(BaseStackRawData));
289 }
290 });
291 return true;
292 }