1 /*
2 * Copyright (c) 2025 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
16 #include "kernel_snapshot_parser.h"
17
18 #include "dfx_log.h"
19 #include "string_util.h"
20
21 #include "kernel_snapshot_kernel_frame.h"
22
23 namespace OHOS {
24 namespace HiviewDFX {
25 namespace {
26 constexpr int SEQUENCE_LENGTH = 7;
27
GetSnapshotMapCrashItem(SnapshotSection item)28 CrashSection GetSnapshotMapCrashItem(SnapshotSection item)
29 {
30 switch (item) {
31 case SnapshotSection::DUMP_REGISTERS:
32 return CrashSection::CREGISTERS;
33 case SnapshotSection::DATA_AROUND_REGS:
34 return CrashSection::MEMORY_NEAR_REGISTERS;
35 case SnapshotSection::CONTENT_OF_USER_STACK:
36 return CrashSection::FAULT_STACK;
37 case SnapshotSection::ELF_LOAD_INFO:
38 return CrashSection::MAPS;
39 case SnapshotSection::EXCEPTION_REGISTERS:
40 return CrashSection::EXCEPTION_REGISTERS;
41 default:
42 return CrashSection::INVALID_SECTION;
43 }
44 }
45 }
46
KernelSnapshotParser()47 KernelSnapshotParser::KernelSnapshotParser()
48 {
49 InitializeParseTable();
50 InitializeKeywordTrie();
51 }
52
PreProcessLine(std::string & line) const53 bool KernelSnapshotParser::PreProcessLine(std::string& line) const
54 {
55 if (line.size() <= SEQUENCE_LENGTH || line[0] == '\t') {
56 return false;
57 }
58 // move timestamp to end
59 if (isdigit(line[1])) {
60 auto pos = line.find('[', 1);
61 if (pos != std::string::npos) {
62 std::string tmp = line.substr(0, pos);
63 line = line.substr(pos) + tmp;
64 }
65 }
66 return true;
67 }
68
ConvertThreadInfoToPairs(const std::string & line)69 std::unordered_map<std::string, std::string> KernelSnapshotParser::ConvertThreadInfoToPairs(const std::string& line)
70 {
71 std::unordered_map<std::string, std::string> pairs;
72 size_t pos = 0;
73 while (pos < line.size()) {
74 pos = line.find_first_not_of(' ', pos);
75 if (pos == std::string::npos) {
76 break;
77 }
78 size_t keyStart = pos;
79
80 pos = line.find('=', pos);
81 if (pos == std::string::npos) {
82 break;
83 }
84 std::string key = line.substr(keyStart, pos - keyStart);
85
86 size_t valueStart = ++pos;
87 pos = line.find(',', pos);
88 if (pos == std::string::npos) {
89 pairs[key] = line.substr(valueStart);
90 break;
91 }
92 pairs[key] = line.substr(valueStart, pos - valueStart);
93 pos++;
94 }
95 return pairs;
96 }
97
ParseTransStart(const SnapshotCell & cell,CrashMap & output)98 void KernelSnapshotParser::ParseTransStart(const SnapshotCell& cell, CrashMap& output)
99 {
100 /**
101 * kernel crash snapshot transaction start format:
102 * [AB_00][transaction start] now mono_time is [45.006871][1733329272.590140]
103 */
104 const std::string& cont = cell.lines[cell.start];
105 if (cont.find("mono_time") == std::string::npos) {
106 return;
107 }
108 auto msPos = cont.rfind(".");
109 std::string millsecond;
110 const int millsecondLen = 3;
111 if (msPos != std::string::npos && msPos + 1 < cont.length()) {
112 millsecond = cont.substr(msPos + 1, millsecondLen);
113 }
114 millsecond = millsecond.length() != millsecondLen ? "000" : millsecond;
115 auto secondPos = cont.rfind("[");
116 if (secondPos != std::string::npos && secondPos + 1 < cont.length()) {
117 output[CrashSection::TIME_STAMP] = cont.substr(secondPos + 1, 10) + millsecond; // 10: second timestamp length
118 }
119 }
120
ParseThreadInfo(const SnapshotCell & cell,CrashMap & output)121 void KernelSnapshotParser::ParseThreadInfo(const SnapshotCell& cell, CrashMap& output)
122 {
123 if (cell.start + 1 > cell.end) {
124 return;
125 }
126 /**
127 * kernel crash snapshot thread info format:
128 * Thread info:
129 * name=ei.hmsapp.music, tid=5601, state=RUNNING, sctime=40.362389062, tcb_cref=502520008108a, pid=5601,
130 * ppid=656, pgid=1, uid=20020048, cpu=7, cur_rq=7
131 */
132 std::string info = cell.lines[cell.start + 1];
133 DFXLOGI("kernel snapshot thread info : %{public}s", info.c_str());
134 auto pairs = ConvertThreadInfoToPairs(info);
135 output[CrashSection::PROCESS_NAME] = pairs["name"]; // native process use this
136 output[CrashSection::FAULT_THREAD_INFO] = "Tid:" + pairs["tid"] + ", Name: " + pairs["name"] + "\n";
137 output[CrashSection::PID] = pairs["pid"];
138 output[CrashSection::UID] = pairs["uid"];
139 }
140
ParseStackBacktrace(const SnapshotCell & cell,CrashMap & output)141 void KernelSnapshotParser::ParseStackBacktrace(const SnapshotCell& cell, CrashMap& output)
142 {
143 KernelFrame frame;
144 for (size_t i = cell.start + 1; i <= cell.end; i++) {
145 frame.Parse(cell.lines[i]);
146 size_t pos = i - cell.start - 1;
147 output[CrashSection::FAULT_THREAD_INFO] += frame.ToString(pos) + "\n";
148 }
149 }
150
ParseProcessRealName(const SnapshotCell & cell,CrashMap & output)151 void KernelSnapshotParser::ParseProcessRealName(const SnapshotCell& cell, CrashMap& output)
152 {
153 /**
154 * kernel crash snapshot process statistics format:
155 * [ED_00]Process statistics:
156 * [ED_00] name tid state tcb_cref sched_cnt cpu_cur rq_cur cls rtprio ni pri pid ppid pgid
157 * [ED_00] SaInit1 1012 RUNNING 5022a0008106b 7 7 6 TS - 0 20 799 1 1
158 * [ED_00] audio_server 799 BLOCKED 5022a0008108a 325 4 6 TS - 0 20 799 1 1
159 */
160 for (size_t i = cell.start + 2; i <= cell.end; i++) { // 2 : skip header
161 const auto& item = cell.lines[i];
162 size_t nameStart = item.find_first_not_of(' ');
163 if (nameStart == std::string::npos) {
164 continue;
165 }
166 size_t nameEnd = item.find_first_of(' ', nameStart);
167 if (nameEnd == std::string::npos) {
168 continue;
169 }
170 std::string name = item.substr(nameStart, nameEnd - nameStart);
171
172 size_t tidStart = item.find_first_not_of(' ', nameEnd);
173 if (tidStart == std::string::npos) {
174 continue;
175 }
176 size_t tidEnd = item.find_first_of(' ', tidStart);
177 if (tidEnd == std::string::npos) {
178 continue;
179 }
180 std::string tid = item.substr(tidStart, tidEnd - tidStart);
181 if (tid == output[CrashSection::PID]) {
182 output[CrashSection::PROCESS_NAME] = name;
183 break;
184 }
185 }
186 }
187
ParseDefaultAction(const SnapshotCell & cell,CrashMap & output)188 void KernelSnapshotParser::ParseDefaultAction(const SnapshotCell& cell, CrashMap& output)
189 {
190 auto it = GetSnapshotMapCrashItem(cell.sectionKey);
191 if (it == CrashSection::INVALID_SECTION) {
192 return;
193 }
194 for (size_t i = cell.start + 1; i <= cell.end; i++) {
195 output[it] += cell.lines[i] + "\n";
196 }
197 }
198
InitializeParseTable()199 void KernelSnapshotParser::InitializeParseTable()
200 {
201 parseTable_ = {
202 {SnapshotSection::TRANSACTION_START, &KernelSnapshotParser::ParseTransStart},
203 {SnapshotSection::THREAD_INFO, &KernelSnapshotParser::ParseThreadInfo},
204 {SnapshotSection::STACK_BACKTRACE, &KernelSnapshotParser::ParseStackBacktrace},
205 {SnapshotSection::PROCESS_STATISTICS, &KernelSnapshotParser::ParseProcessRealName}
206 };
207 }
208
InitializeKeywordTrie()209 void KernelSnapshotParser::InitializeKeywordTrie()
210 {
211 for (const auto& item : SNAPSHOT_SECTION_KEYWORDS) {
212 snapshotTrie_.Insert(item.key, item.type);
213 }
214 }
215
ProcessSnapshotSection(const SnapshotCell & cell,CrashMap & output)216 void KernelSnapshotParser::ProcessSnapshotSection(const SnapshotCell& cell, CrashMap& output)
217 {
218 auto it = parseTable_.find(cell.sectionKey);
219 if (it != parseTable_.end()) {
220 ParseFunction func = it->second;
221 (this->*func)(cell, output);
222 } else {
223 ParseDefaultAction(cell, output);
224 }
225 }
226
ProcessTransStart(const std::vector<std::string> & lines,size_t & index,std::string keyword,CrashMap & output)227 bool KernelSnapshotParser::ProcessTransStart(const std::vector<std::string>& lines, size_t& index,
228 std::string keyword, CrashMap& output)
229 {
230 for (; index < lines.size(); index++) {
231 if (StartsWith(lines[index], keyword)) {
232 break;
233 }
234 }
235
236 if (index == lines.size()) {
237 return false;
238 }
239 SnapshotCell cell {SnapshotSection::TRANSACTION_START, lines, index, index};
240 ProcessSnapshotSection(cell, output);
241 index++;
242 return true;
243 }
244
ParseSnapshotUnit(const std::vector<std::string> & lines,size_t & index)245 CrashMap KernelSnapshotParser::ParseSnapshotUnit(const std::vector<std::string>& lines, size_t& index)
246 {
247 CrashMap output;
248 if (!ProcessTransStart(lines, index, SNAPSHOT_SECTION_KEYWORDS[0].key, output)) {
249 return output;
250 }
251
252 // process other snapshot sections
253 size_t curSectionLineNum = 0;
254 SnapshotSection lastSectionKey = SnapshotSection::INVALID_SECTION;
255 for (; index < lines.size(); index++) {
256 SnapshotSection curSectionKey;
257 if (!snapshotTrie_.MatchPrefix(lines[index], curSectionKey)) {
258 continue;
259 }
260 if (curSectionLineNum == 0) {
261 lastSectionKey = curSectionKey;
262 curSectionLineNum = index;
263 continue;
264 }
265
266 ProcessSnapshotSection({lastSectionKey, lines, curSectionLineNum, index - 1}, output);
267 lastSectionKey = curSectionKey;
268 curSectionLineNum = index;
269 if (lastSectionKey == SnapshotSection::TRANSACTION_END) {
270 break;
271 }
272 }
273 return output;
274 }
275
ParseSameSeqSnapshot(const std::vector<std::string> & lines,std::vector<CrashMap> & crashMaps)276 void KernelSnapshotParser::ParseSameSeqSnapshot(const std::vector<std::string>& lines, std::vector<CrashMap>& crashMaps)
277 {
278 size_t curLineNum = 0;
279
280 while (curLineNum < lines.size()) {
281 auto cm = ParseSnapshotUnit(lines, curLineNum);
282 if (!cm.empty()) {
283 crashMaps.emplace_back(cm);
284 }
285 }
286 }
287
ParseSnapshot(std::vector<std::string> & snapshotLines)288 std::vector<CrashMap> KernelSnapshotParser::ParseSnapshot(std::vector<std::string>& snapshotLines)
289 {
290 std::unordered_map<std::string, std::vector<std::string>> kernelSnapshotMap;
291 // devide snapshot info by sequence number
292 for (auto &line : snapshotLines) {
293 if (!PreProcessLine(line)) {
294 continue;
295 }
296
297 std::string seqNum = line.substr(0, SEQUENCE_LENGTH);
298 kernelSnapshotMap[seqNum].emplace_back(line.substr(SEQUENCE_LENGTH));
299 }
300 std::vector<CrashMap> crashMaps;
301
302 for (auto &item : kernelSnapshotMap) {
303 ParseSameSeqSnapshot(item.second, crashMaps);
304 }
305 return crashMaps;
306 }
307
SplitByNewLine(const std::string & str,std::vector<std::string> & lines)308 void KernelSnapshotParser::SplitByNewLine(const std::string& str, std::vector<std::string>& lines)
309 {
310 size_t start = 0;
311 while (start < str.size()) {
312 size_t end = str.find('\n', start);
313 if (end == std::string::npos) {
314 end = str.size();
315 }
316 lines.emplace_back(str.substr(start, end - start));
317 start = end + 1;
318 }
319 }
320
ParseAll(const std::string & cont)321 std::vector<CrashMap> KernelSnapshotParser::ParseAll(const std::string& cont)
322 {
323 std::vector<std::string> lines;
324 SplitByNewLine(cont, lines);
325 return ParseSnapshot(lines);
326 }
327 } // namespace HiviewDFX
328 } // namespace OHOS
329