• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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