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
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<uint64_t> g_flushCount = 0;
37 std::atomic<bool> g_disableHook = false;
38 constexpr uint32_t MEMCHECK_DETAILINFO_MAXSIZE = 102400;
39
40 struct OptArg {
41 size_t pos;
42 char *buf;
43 };
44
45 } // namespace
46
47
GetRealTime()48 static std::string GetRealTime()
49 {
50 time_t now = time(nullptr);
51 tm tm;
52 const int timeLength = 64;
53 char stampStr[timeLength] = {0};
54
55 if (localtime_r(&now, &tm) == nullptr || strftime(stampStr, timeLength, "%Y/%m/%d %H:%M:%S", &tm) == 0) {
56 return "error time format!";
57 }
58 return std::string(stampStr);
59 }
60
NmdWriteStat(void * arg,const char * buf)61 static void NmdWriteStat(void *arg, const char *buf)
62 {
63 struct OptArg *opt = static_cast<struct OptArg*>(arg);
64 std::string getNmdTime = std::to_string(getpid()) + " " + GetRealTime() + "\n";
65 size_t nmdTimeLen = getNmdTime.size();
66 if (strncpy_s(opt->buf + opt->pos, MEMCHECK_DETAILINFO_MAXSIZE - opt->pos,
67 getNmdTime.c_str(), nmdTimeLen) != EOK) {
68 return;
69 }
70 opt->pos += nmdTimeLen;
71
72 size_t len = strlen(buf);
73 if (len + opt->pos + 1 > MEMCHECK_DETAILINFO_MAXSIZE) {
74 return;
75 }
76 if (strncpy_s(opt->buf + opt->pos, MEMCHECK_DETAILINFO_MAXSIZE - opt->pos, buf, len) != EOK) {
77 return;
78 }
79 opt->pos += len;
80 }
81
HookSocketClient(int pid,ClientConfig * config,Sampling * sampler,void (* disableHookCallback)())82 HookSocketClient::HookSocketClient(int pid, ClientConfig *config, Sampling *sampler, void (*disableHookCallback)())
83 : pid_(pid), config_(config), sampler_(sampler), disableHookCallback_(disableHookCallback)
84 {
85 smbFd_ = 0;
86 eventFd_ = 0;
87 unixSocketClient_ = nullptr;
88 serviceName_ = "HookService";
89 Connect(DEFAULT_UNIX_SOCKET_HOOK_FULL_PATH);
90 }
91
~HookSocketClient()92 HookSocketClient::~HookSocketClient()
93 {
94 if (stackWriter_) {
95 stackWriter_->Flush();
96 }
97 unixSocketClient_ = nullptr;
98 stackWriter_ = nullptr;
99 }
100
Connect(const std::string addrname)101 bool HookSocketClient::Connect(const std::string addrname)
102 {
103 if (unixSocketClient_ != nullptr) {
104 return false;
105 }
106 unixSocketClient_ = std::make_shared<UnixSocketClient>();
107 if (!unixSocketClient_->Connect(addrname, *this)) {
108 unixSocketClient_ = nullptr;
109 return false;
110 }
111
112 unixSocketClient_->SendHookConfig(reinterpret_cast<uint8_t *>(&pid_), sizeof(pid_));
113 return true;
114 }
115
ProtocolProc(SocketContext & context,uint32_t pnum,const int8_t * buf,const uint32_t size)116 bool HookSocketClient::ProtocolProc(SocketContext &context, uint32_t pnum, const int8_t *buf, const uint32_t size)
117 {
118 if (size != sizeof(ClientConfig)) {
119 return true;
120 }
121 *config_ = *reinterpret_cast<ClientConfig *>(const_cast<int8_t*>(buf));
122 config_->maxStackDepth = config_->maxStackDepth > MAX_UNWIND_DEPTH ? MAX_UNWIND_DEPTH : config_->maxStackDepth;
123 std::string configStr = config_->ToString();
124 sampler_->InitSampling(config_->sampleInterval);
125 smbFd_ = context.ReceiveFileDiscriptor();
126 eventFd_ = context.ReceiveFileDiscriptor();
127 std::string smbName = "hooknativesmb_" + std::to_string(pid_);
128 stackWriter_ = std::make_shared<StackWriter>(smbName, config_->shareMemorySize,
129 smbFd_, eventFd_, config_->isBlocked, config_->isSaMode);
130 nmdType_ = config_->nmdType;
131 g_disableHook = false;
132 largestSize_ = config_->largestSize;
133 secondLargestSize_ = config_->secondLargestSize;
134 maxGrowthSize_ = config_->maxGrowthSize;
135 sampleInterval_ = config_->sampleInterval;
136 PROFILER_LOG_INFO(LOG_CORE, "HookSocketClient::ProtocolProc, ts1.1 = %d, ts1.2 = %d, ts2 = %d, ts3 = %d",
137 largestSize_, secondLargestSize_, maxGrowthSize_, sampleInterval_);
138 if (nmdType_ == 0 || nmdType_ == ONLY_NMD_TYPE) {
139 SendNmdInfo();
140 } else if (nmdType_ == SIMP_NMD) {
141 SendSimplifiedNmdInfo();
142 }
143 return true;
144 }
145
SendStack(const void * data,size_t size)146 bool HookSocketClient::SendStack(const void* data, size_t size)
147 {
148 if (stackWriter_ == nullptr || unixSocketClient_ == nullptr) {
149 return false;
150 }
151
152 if (!unixSocketClient_->SendHeartBeat()) {
153 return false;
154 }
155
156 stackWriter_->WriteTimeout(data, size);
157 stackWriter_->Flush();
158
159 return true;
160 }
161
SendStackWithPayload(const void * data,size_t size,const void * payload,size_t payloadSize)162 bool HookSocketClient::SendStackWithPayload(const void* data, size_t size, const void* payload,
163 size_t payloadSize)
164 {
165 if (stackWriter_ == nullptr || unixSocketClient_ == nullptr || g_disableHook) {
166 return false;
167 } else if (unixSocketClient_->GetClientState() == CLIENT_STAT_THREAD_EXITED) {
168 DisableHook();
169 return false;
170 }
171
172 bool ret = stackWriter_->WriteWithPayloadTimeout(data, size, payload, payloadSize,
173 std::bind(&HookSocketClient::PeerIsConnected, this));
174 if (!ret && config_->isBlocked) {
175 DisableHook();
176 return false;
177 }
178 ++g_flushCount;
179 if (g_flushCount % FLUSH_FLAG == 0) {
180 stackWriter_->Flush();
181 }
182 return true;
183 }
184
Flush()185 void HookSocketClient::Flush()
186 {
187 if (stackWriter_ == nullptr || unixSocketClient_ == nullptr) {
188 return;
189 }
190 stackWriter_->Flush();
191 }
192
DisableHook()193 void HookSocketClient::DisableHook()
194 {
195 bool expected = false;
196 if (g_disableHook.compare_exchange_strong(expected, true, std::memory_order_release, std::memory_order_relaxed)) {
197 HILOG_BASE_INFO(LOG_CORE, "%s", __func__);
198 if (disableHookCallback_) {
199 disableHookCallback_();
200 }
201 }
202 }
203
PeerIsConnected()204 bool HookSocketClient::PeerIsConnected()
205 {
206 return !unixSocketClient_->IsConnected();
207 }
208
SendNmdInfo()209 bool HookSocketClient::SendNmdInfo()
210 {
211 if (!config_->printNmd) {
212 return false;
213 }
214 void* nmdBuf = malloc(MEMCHECK_DETAILINFO_MAXSIZE);
215 if (nmdBuf == nullptr) {
216 return false;
217 }
218 struct OptArg opt = {0, reinterpret_cast<char*>(nmdBuf) };
219 malloc_stats_print(NmdWriteStat, &opt, "a");
220 StackRawData rawdata = {{{{0}}}};
221 rawdata.type = NMD_MSG;
222 if (stackWriter_) {
223 stackWriter_->WriteWithPayloadTimeout(&rawdata, sizeof(BaseStackRawData),
224 reinterpret_cast<int8_t*>(opt.buf), strlen(opt.buf) + 1,
225 std::bind(&HookSocketClient::PeerIsConnected, this));
226 }
227 free(nmdBuf);
228 return true;
229 }
230
SendSimplifiedNmdInfo()231 bool HookSocketClient::SendSimplifiedNmdInfo()
232 {
233 if (!config_->printNmd) {
234 return false;
235 }
236 void* nmdBuf = malloc(MEMCHECK_DETAILINFO_MAXSIZE);
237 if (nmdBuf == nullptr) {
238 return false;
239 }
240 struct OptArg opt = {0, reinterpret_cast<char*>(nmdBuf) };
241 malloc_stats_print(NmdWriteStat, &opt, "s");
242 StackRawData rawdata = {{{{0}}}};
243 rawdata.type = NMD_MSG;
244 if (stackWriter_) {
245 stackWriter_->WriteWithPayloadTimeout(&rawdata, sizeof(BaseStackRawData),
246 reinterpret_cast<int8_t*>(opt.buf), strlen(opt.buf) + 1,
247 std::bind(&HookSocketClient::PeerIsConnected, this));
248 }
249 stackWriter_->Flush();
250 free(nmdBuf);
251 return true;
252 }
253
SendEndMsg()254 bool HookSocketClient::SendEndMsg()
255 {
256 StackRawData rawdata = {{{{0}}}};
257 rawdata.type = END_MSG;
258 if (stackWriter_) {
259 stackWriter_->WriteTimeout(&rawdata, sizeof(BaseStackRawData));
260 }
261 return true;
262 }