• 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 "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