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 "log_util.h"
16
17 #include <cstring>
18 #include <fcntl.h>
19 #include <fstream>
20 #include <sstream>
21 #include <unistd.h>
22
23 #include "file_util.h"
24 #include "logger.h"
25 #include "string_util.h"
26
27 using namespace std;
28 namespace OHOS {
29 namespace HiviewDFX {
30 DEFINE_LOG_TAG("LogUtil");
31
32 namespace {
33 const string ARROW = "->";
34 const string CODE = "code";
35 const string WAIT = "wait";
36 const int BUF_LEN_2048 = 2048;
37 const int TOTAL_SKIP_NUM = 4;
38 }
39
40 const std::string LogUtil::SPLIT_PATTERN = "\n";
41 const std::string LogUtil::SMART_PARSER_TEST_DIR = "/data/test/test_data/SmartParser";
42 const int LogUtil::TOTAL_LINE_NUM = 200;
43
44 /* ParseIpcInfo function:
45 * INPUT :
46 * buffer : log buffer
47 * offset : buffer seekg
48 * pidTid : pid:tid input in Ipc transactions
49 * OUTPUT :
50 * pair<int, int> : it is pid:tid which input pid:tid is waiting for
51 * vector<string> : it is Ipc string line which show Ipc transactions between input and output
52 * string : it is simplify Ipc list of output for vector<string>
53 */
ParseIpcInfo(stringstream & buffer,const int offset,const pair<int,int> & pidTid)54 tuple<pair<int, int>, vector<string>> LogUtil::ParseIpcInfo(stringstream& buffer,
55 const int offset, const pair<int, int>& pidTid)
56 {
57 ReadIpcInfo(buffer, offset);
58 if (IpcInfo_.empty()) {
59 return tuple<pair<int, int>, vector<string>> {};
60 }
61
62 vector<IpcTrans> IpcNode;
63 string clientPidPid = to_string(pidTid.first) + ":" + to_string(pidTid.second);
64 // situation 1.tid of IpcTrans is zero && situation 2.multiIpc
65 ParseIpcList(clientPidPid, IpcNode);
66
67 if (IpcNode.empty()) {
68 return tuple<pair<int, int>, vector<string>> {};
69 }
70
71 // assigneValue to output
72 vector<string> resultIpcLine;
73 for (const auto& Ipc : IpcNode) {
74 resultIpcLine.push_back(Ipc.line);
75 }
76 size_t size = resultIpcLine.size();
77 if (size >= 2 && resultIpcLine.back() == resultIpcLine[size - 2]) { // 2 : last two
78 resultIpcLine[size - 1] = "Can't find which process holdes Ipc mostly";
79 }
80
81 string serverPidTid = IpcNode.back().sIpcItem.pidTid;
82 int pid = StringUtil::StrToInt(StringUtil::GetLeftSubstr(serverPidTid, ":"));
83 int tid = StringUtil::StrToInt(StringUtil::GetRightSubstr(serverPidTid, ":"));
84 pair<int, int> pidTidResult = make_pair(pid, tid);
85
86 return tuple<pair<int, int>, vector<string>>(pidTidResult, resultIpcLine);
87 }
88
ReadIpcInfo(stringstream & buffer,const int offset)89 void LogUtil::ReadIpcInfo(stringstream& buffer, const int offset)
90 {
91 buffer.seekg(offset, ios::beg);
92 string line;
93 while (getline(buffer, line)) {
94 if (line.find("async") == string::npos && line.find(ARROW) != string::npos) {
95 GetIpcInfo(line);
96 }
97 if (line.empty() || line.find("end Ipc transactions") != string::npos) {
98 break;
99 }
100 }
101 }
102
ParseIpcStr(string IpcStr) const103 IpcItem LogUtil::ParseIpcStr(string IpcStr) const
104 {
105 IpcItem item;
106 IpcStr = StringUtil::EraseString(IpcStr, " ");
107 string csIpc = StringUtil::EraseString(IpcStr, "\t");
108 item.pidTid = StringUtil::GetLeftSubstr(csIpc, "(");
109 string rest = StringUtil::GetRightSubstr(csIpc, "(");
110 item.packageName = StringUtil::GetLeftSubstr(rest, ":");
111 item.threadName = StringUtil::GetMidSubstr(rest, ":", ")");
112 return item;
113 }
114
115 /*
116 * example of Ipc transactions:
117 * 26920:26920(m.Ipc.phone:m.Ipc.phone) -> 1477:2146(system_server:Ipc:1477_A) code 2b wait:7.200053127 s
118 * [async] 1477:1973(system_server:Ipc:1477_7) -> 2148:0(awei.HwOPServer:unknown) code 1 wait:37.375423970 s
119 * ......
120 * return fail show Ipc transaction doesn't conform to the format below; parse fail.
121 * return true show parse correctly. ignore [async] Ipc transactions
122 */
GetIpcInfo(const string & line)123 void LogUtil::GetIpcInfo(const string& line)
124 {
125 string serverIpc = StringUtil::GetMidSubstr(line, ARROW, CODE);
126 string codeStr = StringUtil::GetMidSubstr(line, CODE, WAIT);
127 if (!serverIpc.empty() && !codeStr.empty()) {
128 string clientIpc = StringUtil::GetLeftSubstr(line, ARROW);
129 string timeStr = StringUtil::GetRightSubstr(line, WAIT);
130 IpcTrans bindStr;
131 bindStr.cIpcItem = ParseIpcStr(clientIpc);
132 bindStr.sIpcItem = ParseIpcStr(serverIpc);
133 bindStr.code = StringUtil::EraseString(codeStr, " ");
134 timeStr = StringUtil::GetMidSubstr(timeStr, ":", " ");
135 bindStr.waitTime = StringUtil::StringToDouble(timeStr);
136 bindStr.line = line;
137 pair<string, IpcTrans> IpcPair(bindStr.sIpcItem.pidTid, bindStr);
138 IpcInfo_.insert(make_pair(bindStr.cIpcItem.pidTid, IpcPair));
139 }
140 }
141
142 // situation 1:thread pool have exhausted, serverTid is zero
ParseZeroIpc(const IpcTrans & beginIpc) const143 IpcTrans LogUtil::ParseZeroIpc(const IpcTrans& beginIpc) const
144 {
145 string serverPidMatch = StringUtil::GetLeftSubstr(beginIpc.sIpcItem.pidTid, ":");
146 map<double, IpcTrans> timeIpc;
147 for (auto it = IpcInfo_.begin(); it != IpcInfo_.end(); it++) {
148 string serverPidTid = it->second.first;
149 string serverTid = StringUtil::GetRightSubstr(serverPidTid, ":");
150 string serverPid = StringUtil::GetLeftSubstr(serverPidTid, ":");
151 if (serverPid == serverPidMatch && serverTid != "0") {
152 IpcTrans exhaustIpc = it->second.second;
153 timeIpc.insert(make_pair(exhaustIpc.waitTime, exhaustIpc));
154 }
155 }
156 // all tid is 0
157 if (timeIpc.empty()) {
158 HIVIEW_LOGD("all tid is 0");
159 return beginIpc;
160 }
161 // get IpcTrans of max wait time
162 auto iter = timeIpc.rbegin();
163 return iter->second;
164 }
165
166 // situation 2: there are IpcTrans list in IpcTrans transactions
ParseIpcList(const string & clientPidTid,vector<IpcTrans> & IpcNode) const167 void LogUtil::ParseIpcList(const string& clientPidTid, vector<IpcTrans>& IpcNode) const
168 {
169 int times = 0;
170 // loop lookup Ipclist
171 auto iterInfo = IpcInfo_.find(clientPidTid);
172 while (iterInfo != IpcInfo_.end() && times++ < 20) { // mostly 20 list
173 string serverPidTid = iterInfo->second.first;
174 string serverTid = StringUtil::GetRightSubstr(serverPidTid, ":");
175 if (serverTid == "0") {
176 IpcNode.push_back(iterInfo->second.second); // push IpcItem which tid is 0
177 IpcNode.push_back(ParseZeroIpc(iterInfo->second.second)); // (Ipc exhaust) show maxwait time
178 break;
179 }
180 IpcNode.push_back(iterInfo->second.second);
181 iterInfo = IpcInfo_.find(serverPidTid);
182 }
183 }
184
185 /* GetTrace function:
186 * buffer : log buffer
187 * cursor : buffer seekg
188 * reg : regex which is used to get trace line
189 * result : all trace line will be spliced by "\n"
190 * startReg : start place when regex is match, default empty string
191 */
GetTrace(stringstream & buffer,int cursor,const string & reg,string & result,string startReg)192 void LogUtil::GetTrace(stringstream& buffer, int cursor, const string& reg, string& result, string startReg)
193 {
194 buffer.seekg(cursor, ios::beg);
195 string line;
196 bool start = false;
197 int num = 0;
198 int skipNum = 0;
199 startReg = startReg.empty() ? reg : startReg;
200
201 while (getline(buffer, line) && num++ < TOTAL_LINE_NUM) {
202 if (line.length() > BUF_LEN_2048) {
203 continue;
204 }
205 if (line.size() == 0 || skipNum >= TOTAL_SKIP_NUM) {
206 break; // blank line
207 }
208 if (!start) {
209 start = regex_search(line, regex(startReg));
210 if (!start) {
211 continue;
212 }
213 }
214
215 smatch matches;
216 if (regex_search(line, matches, regex(reg))) {
217 skipNum = 0;
218 } else {
219 skipNum++;
220 continue;
221 }
222 result += matches.str(0) + LogUtil::SPLIT_PATTERN;
223 }
224 }
225
ReadFileBuff(const string & file,stringstream & buffer)226 bool LogUtil::ReadFileBuff(const string& file, stringstream& buffer)
227 {
228 int fd = LogUtil::GetFileFd(file);
229 if (fd < 0) {
230 HIVIEW_LOGE("%{public}s get fd fail, fd is %{public}d.", file.c_str(), fd);
231 return false;
232 }
233
234 std::string content;
235 if (!FileUtil::LoadStringFromFd(fd, content)) {
236 HIVIEW_LOGE("read file: %s failed, fd is %d\n", file.c_str(), fd);
237 close(fd);
238 return false;
239 }
240 buffer.str(content);
241 close(fd);
242 return true;
243 }
244
GetFileFd(const string & file)245 int LogUtil::GetFileFd(const string& file)
246 {
247 if (file.empty() || !FileUtil::IsLegalPath(file)) {
248 HIVIEW_LOGE("the system file (%{public}s) is illegal.", file.c_str());
249 return -1;
250 }
251 std::string realFileName;
252 if (!FileUtil::PathToRealPath(file, realFileName) || realFileName.empty() ||
253 !FileUtil::FileExists(realFileName)) {
254 HIVIEW_LOGE("the system file (%{public}s) is not found.", realFileName.c_str());
255 return -1;
256 }
257 return open(realFileName.c_str(), O_RDONLY);
258 }
259
FileExist(const string & file)260 bool LogUtil::FileExist(const string& file)
261 {
262 return FileUtil::FileExists(file);
263 }
264
IsTestModel(const string & sourceFile,const string & name,const string & pattern,string & desPath)265 bool LogUtil::IsTestModel(const string& sourceFile, const string& name,
266 const string& pattern, string& desPath)
267 {
268 if (FileUtil::IsDirectory(LogUtil::SMART_PARSER_TEST_DIR)) {
269 HIVIEW_LOGI("test dir exist.");
270 std::string sourceFileName = StringUtil::GetRrightSubstr(sourceFile, "/");
271 std::string dirOrFileName = StringUtil::GetRrightSubstr(name, "/");
272 std::string fileName = pattern.find("/") != std::string::npos ? StringUtil::GetRrightSubstr(pattern, "/") : pattern;
273 smatch result;
274 if (regex_match(sourceFileName, result, regex(dirOrFileName)) ||
275 regex_match(sourceFileName, result, regex(fileName))) {
276 return LogUtil::FileExist(desPath);
277 }
278 return false;
279 }
280 return false;
281 }
282 } // namespace HiviewDFX
283 } // namespace OHOS