• 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 "trace_converter.h"
16 
17 #include <cinttypes>
18 #include <fcntl.h>
19 #include <sstream>
20 #include <tuple>
21 #include <unistd.h>
22 #include "common_types.pb.h"
23 #include "event_formatter.h"
24 
25 namespace {
26 constexpr unsigned TS_MIN_LEN = 9;
27 constexpr unsigned US_DIGITS = 6;
28 constexpr unsigned PID_STR_MAX = 6;
29 constexpr unsigned CPU_STR_MAX = 3;
30 constexpr unsigned COMM_STR_MAX = 16;
31 constexpr unsigned TGID_STR_MAX = 5;
32 constexpr unsigned TRACE_TXT_HEADER_MAX = 1024;
33 constexpr char HEX_CHARS[] = "0123456789abcdef";
34 constexpr int OUTPUT_FILE_MODE = 0644;
35 constexpr int INVALID_FD = -1;
36 using Clock = std::chrono::steady_clock;
37 using TimePoint = Clock::time_point;
38 using MilliSeconds = std::chrono::milliseconds;
39 using EventFormatter = FTRACE_NS::EventFormatter;
40 
41 /*
42  * trace_flag_type is an enumeration that holds different
43  * states when a trace occurs. These are:
44  *  IRQS_OFF		- interrupts were disabled
45  *  IRQS_NOSUPPORT	- arch does not support irqs_disabled_flags
46  *  NEED_RESCHED	- reschedule is requested
47  *  HARDIRQ		- inside an interrupt handler
48  *  SOFTIRQ		- inside a softirq handler
49  */
50 constexpr uint32_t TRACE_FLAG_IRQS_OFF = 0x01;
51 constexpr uint32_t TRACE_FLAG_IRQS_NOSUPPORT = 0x02;
52 constexpr uint32_t TRACE_FLAG_NEED_RESCHED = 0x04;
53 constexpr uint32_t TRACE_FLAG_HARDIRQ = 0x08;
54 constexpr uint32_t TRACE_FLAG_SOFTIRQ = 0x10;
55 constexpr uint32_t TRACE_FLAG_PREEMPT_RESCHED = 0x20;
56 constexpr uint32_t TRACE_FLAG_NMI = 0x40;
57 
58 std::string ENTIRES_STATS_PREFIX = "# entries-in-buffer/entries-written:";
59 std::string PROCESSOR_NUMBER_PREFIX = "#P:";
60 std::string TRACE_TXT_HEADER_FORMAT = R"(# tracer: nop
61 #
62 # entries-in-buffer/entries-written: %lu/%lu   #P:%d
63 #
64 #                                      _-----=> irqs-off
65 #                                     / _----=> need-resched
66 #                                    | / _---=> hardirq/softirq
67 #                                    || / _--=> preempt-depth
68 #                                    ||| /     delay
69 #           TASK-PID    TGID   CPU#  ||||    TIMESTAMP  FUNCTION
70 #              | |        |      |   ||||       |         |
71 )";
72 
TimeDeltaMs(const TimePoint & a,const TimePoint & b)73 long TimeDeltaMs(const TimePoint& a, const TimePoint& b)
74 {
75     auto delta = std::chrono::duration_cast<MilliSeconds>(a - b);
76     return static_cast<long>(delta.count());
77 }
78 
79 constexpr uint8_t HALF_BYTE_SIZE = 4;
80 constexpr uint8_t HALF_BYTE_MASK = 0x0F;
81 
GetHighHalfByte(uint8_t value)82 uint8_t GetHighHalfByte(uint8_t value)
83 {
84     return (value >> HALF_BYTE_SIZE) & HALF_BYTE_MASK;
85 }
86 
GetLowHalfByte(uint8_t value)87 uint8_t GetLowHalfByte(uint8_t value)
88 {
89     return value & HALF_BYTE_MASK;
90 }
91 } // namespace
92 
SetInput(const std::string & input)93 void TraceConverter::SetInput(const std::string& input)
94 {
95     input_ = input;
96 }
97 
SetOutput(const std::string & output)98 void TraceConverter::SetOutput(const std::string& output)
99 {
100     output_ = output;
101 }
102 
PrintTraceFileHeader(const TraceFileHeader & header)103 void TraceConverter::PrintTraceFileHeader(const TraceFileHeader& header)
104 {
105     HILOG_INFO(LOG_CORE, "======== TRACE FILE HEADER ========");
106     HILOG_INFO(LOG_CORE, "magic: %" PRIx64, header.data_.magic_);
107     HILOG_INFO(LOG_CORE, "length: %" PRIu64, header.data_.length_);
108     HILOG_INFO(LOG_CORE, "version: %" PRIx32, header.data_.version_);
109     HILOG_INFO(LOG_CORE, "segments: %" PRIx32, header.data_.segments_);
110     std::string sha = "";
111     for (size_t i = 0; i < std::size(header.data_.sha256_); i++) {
112         sha.push_back(HEX_CHARS[GetHighHalfByte(header.data_.sha256_[i])]);
113         sha.push_back(HEX_CHARS[GetLowHalfByte(header.data_.sha256_[i])]);
114     }
115     HILOG_INFO(LOG_CORE, "SHA256: %s", sha.c_str());
116     HILOG_INFO(LOG_CORE, "-------- -------- -------- --------");
117 }
118 
PrintCpuStats(const FtraceCpuStatsMsg & cpuStats)119 void TraceConverter::PrintCpuStats(const FtraceCpuStatsMsg& cpuStats)
120 {
121     auto status = cpuStats.status();
122     std::string statusName = "";
123     if (status == FtraceCpuStatsMsg::TRACE_START) {
124         statusName = "TRACE_START";
125     } else if (status == FtraceCpuStatsMsg::TRACE_END) {
126         statusName = "TRACE_END";
127     }
128     HILOG_INFO(LOG_CORE, "---- ---- ---- ---- ---- ----");
129     HILOG_INFO(LOG_CORE, "FtraceCpuStatsMsg:");
130     HILOG_INFO(LOG_CORE, "status: %s", statusName.c_str());
131     for (int i = 0; i < cpuStats.per_cpu_stats_size(); i++) {
132         HILOG_INFO(LOG_CORE, "per_cpu_stats[%d]: {", i);
133         HILOG_INFO(LOG_CORE, "  cpu: %" PRIu64, cpuStats.per_cpu_stats(i).cpu());
134         HILOG_INFO(LOG_CORE, "  entries: %" PRIu64, cpuStats.per_cpu_stats(i).entries());
135         HILOG_INFO(LOG_CORE, "  overrun: %" PRIu64, cpuStats.per_cpu_stats(i).overrun());
136         HILOG_INFO(LOG_CORE, "  commit_overrun: %" PRIu64, cpuStats.per_cpu_stats(i).commit_overrun());
137         HILOG_INFO(LOG_CORE, "  bytes: %" PRIu64, cpuStats.per_cpu_stats(i).bytes());
138         HILOG_INFO(LOG_CORE, "  oldest_event_ts: %.06f", cpuStats.per_cpu_stats(i).oldest_event_ts());
139         HILOG_INFO(LOG_CORE, "  now_ts: %.06f", cpuStats.per_cpu_stats(i).now_ts());
140         HILOG_INFO(LOG_CORE, "  dropped_events: %" PRIu64, cpuStats.per_cpu_stats(i).dropped_events());
141         HILOG_INFO(LOG_CORE, "  read_events: %" PRIu64, cpuStats.per_cpu_stats(i).read_events());
142         HILOG_INFO(LOG_CORE, "}");
143     }
144     HILOG_INFO(LOG_CORE, "---- ---- ---- ---- ---- ----");
145 }
146 
ParseCpuStats(const FtraceCpuStatsMsg & cpuStats)147 void TraceConverter::ParseCpuStats(const FtraceCpuStatsMsg& cpuStats)
148 {
149     auto status = cpuStats.status();
150     decltype(&startStats_) pStats = nullptr;
151     if (status == FtraceCpuStatsMsg::TRACE_START) {
152         startStats_.clear();
153         pStats = &startStats_;
154     } else if (status == FtraceCpuStatsMsg::TRACE_END) {
155         endStats_.clear();
156         pStats = &endStats_;
157     }
158     for (int i = 0; i < cpuStats.per_cpu_stats_size(); i++) {
159         if (pStats) {
160             pStats->push_back(cpuStats.per_cpu_stats(i));
161         }
162     }
163 }
164 
SummarizeStats()165 void TraceConverter::SummarizeStats()
166 {
167     auto sumCpuStats = [](const std::vector<PerCpuStatsMsg>& cpuStats) -> PerCpuStatsMsg {
168         PerCpuStatsMsg sum = {};
169         for (auto stats : cpuStats) {
170             sum.set_entries(sum.entries() + stats.entries());
171             sum.set_read_events(sum.read_events() + stats.read_events());
172         }
173         return sum;
174     };
175     endSum_ = sumCpuStats(endStats_);
176     startSum_ = sumCpuStats(startStats_);
177     entriesInBuffer_ = endSum_.read_events() - startSum_.read_events();
178 }
179 
180 // refers kernel function trace_print_lat_fmt
181 // https://github.com/torvalds/linux/blob/v4.19/kernel/trace/trace_output.c#L447
TraceFlagsToString(uint32_t flags,uint32_t preemptCount)182 std::string TraceConverter::TraceFlagsToString(uint32_t flags, uint32_t preemptCount)
183 {
184     std::string result = "";
185     char irqsOff = '.';
186     if (flags & TRACE_FLAG_IRQS_OFF) {
187         irqsOff = 'd';
188     } else if (flags & TRACE_FLAG_IRQS_NOSUPPORT) {
189         irqsOff = 'X';
190     }
191     result.push_back(irqsOff);
192 
193     char needResched = '.';
194     bool isNeedResched = flags & TRACE_FLAG_NEED_RESCHED;
195     bool isPreemptResched = flags & TRACE_FLAG_PREEMPT_RESCHED;
196     if (isNeedResched && isPreemptResched) {
197         needResched = 'N';
198     } else if (isNeedResched) {
199         needResched = 'n';
200     } else if (isPreemptResched) {
201         needResched = 'p';
202     }
203     result.push_back(needResched);
204 
205     bool nmiFlag = flags & TRACE_FLAG_NMI;
206     bool hardIrq = flags & TRACE_FLAG_HARDIRQ;
207     bool softIrq = flags & TRACE_FLAG_SOFTIRQ;
208     char irqChar = '.';
209     if (nmiFlag && hardIrq) {
210         irqChar = 'Z';
211     } else if (nmiFlag) {
212         irqChar = 'z';
213     } else if (hardIrq && softIrq) {
214         irqChar = 'H';
215     } else if (hardIrq) {
216         irqChar = 'h';
217     } else if (softIrq) {
218         irqChar = 's';
219     }
220     result.push_back(irqChar);
221 
222     if (preemptCount) {
223         result.push_back("0123456789abcdef"[preemptCount & 0x0F]);
224     } else {
225         result.push_back('.');
226     }
227     return result;
228 }
229 
FormatEvent(const FtraceEvent & event,int cpu)230 std::string TraceConverter::FormatEvent(const FtraceEvent& event, int cpu)
231 {
232     std::stringstream sout;
233     int pid = event.common_fields().pid();
234 
235     // TASK(comm) part, refers __trace_find_cmdline
236     // https://github.com/torvalds/linux/blob/v4.19/kernel/trace/trace.c#L1978
237     std::string comm = "<...>";
238     if (comm.size()) {
239         comm = event.comm();
240     }
241     if (pid == 0) {
242         comm = "<idle>";
243     }
244     if (comm.size() < COMM_STR_MAX) {
245         comm = std::string(COMM_STR_MAX - comm.size(), ' ') + comm;
246     }
247     sout << comm << '-';
248 
249     // PID part
250     std::string pidStr = std::to_string(pid);
251     if (pidStr.size() < PID_STR_MAX) {
252         pidStr.resize(PID_STR_MAX, ' ');
253     }
254     sout << pidStr;
255 
256     // TGID part
257     std::string tgidStr = "-----";
258     if (parseGid_ && event.tgid() != 0) {
259         tgidStr = std::to_string(event.tgid());
260         if (tgidStr.size() < TGID_STR_MAX) {
261             tgidStr.insert(tgidStr.begin(), TGID_STR_MAX - tgidStr.size(), ' ');
262         }
263     }
264     sout << '(' << tgidStr << ')';
265 
266     // CPU# part
267     std::string cpuStr = std::to_string(cpu);
268     if (cpuStr.size() < CPU_STR_MAX) {
269         cpuStr = std::string(CPU_STR_MAX - cpuStr.size(), '0') + cpuStr;
270     }
271     sout << " [" << cpuStr << "] ";
272 
273     // flags part
274     uint32_t flags = event.common_fields().flags();
275     uint32_t preeptCount = event.common_fields().preempt_count();
276     if (parseFlags_ && (flags | preeptCount)) {
277         sout << TraceFlagsToString(flags, preeptCount) << " ";
278     } else {
279         sout << ".... ";
280     }
281 
282     // TIMESTAMP part
283     std::string timestamp = "";
284     timestamp = std::to_string(event.timestamp());
285     auto tsLength = timestamp.length();
286     CHECK_TRUE(tsLength > TS_MIN_LEN, "", "invalid timestamp!");
287     std::string tsSecs = timestamp.substr(0, timestamp.size() - TS_MIN_LEN);
288     std::string tsMicroSecs = timestamp.substr(timestamp.size() - TS_MIN_LEN, US_DIGITS);
289     sout << tsSecs << '.' << tsMicroSecs << ": ";
290 
291     // FUNCTION part
292     sout << EventFormatter::GetInstance().FormatEvent(event) << "\n";
293     return sout.str();
294 }
295 
WriteEvent(const FtraceEvent & event,int cpu)296 bool TraceConverter::WriteEvent(const FtraceEvent& event, int cpu)
297 {
298     std::string line = FormatEvent(event, cpu);
299     CHECK_TRUE(line.size() > 0, false, "format event failed!");
300     auto nbytes = write(outputFd_, line.data(), line.size());
301     CHECK_TRUE(static_cast<size_t>(nbytes) == line.size(), false, "write event line FAILED!");
302     return true;
303 }
304 
PrintTextHeader(const std::string & msg)305 void TraceConverter::PrintTextHeader(const std::string& msg)
306 {
307     HILOG_INFO(LOG_CORE, "==== ==== ==== %s ==== ==== ====", msg.c_str());
308     HILOG_INFO(LOG_CORE, "%s", textHeader_.c_str());
309     HILOG_INFO(LOG_CORE, "---- ---- ---- %s ---- ---- ----", msg.c_str());
310 }
311 
WriteInitialHeader()312 bool TraceConverter::WriteInitialHeader()
313 {
314     std::vector<char> buffer(TRACE_TXT_HEADER_MAX);
315     int used = snprintf_s(buffer.data(), buffer.size(), buffer.size() - 1, TRACE_TXT_HEADER_FORMAT.c_str(), UINT32_MAX,
316                           UINT32_MAX, UINT8_MAX);
317     CHECK_TRUE(used > 0, false, "format initial header failed!");
318     textHeader_.assign(&buffer[0], &buffer[used]);
319 
320     auto nbytes = write(outputFd_, textHeader_.data(), textHeader_.size());
321     CHECK_TRUE(static_cast<size_t>(nbytes) == textHeader_.size(), false, "write inital header failed!");
322     PrintTextHeader("INITAL TRACE HEADER");
323     return true;
324 }
325 
GenerateNewEntriesValue(uint32_t orginLength)326 std::string TraceConverter::GenerateNewEntriesValue(uint32_t orginLength)
327 {
328     std::string newEntriesValue = std::to_string(entriesInBuffer_);
329     newEntriesValue += '/';
330     newEntriesValue += std::to_string(entriesWritten_);
331     if (newEntriesValue.size() < orginLength) {
332         size_t paddingSpaces = orginLength - newEntriesValue.size();
333         std::string leftPadding(paddingSpaces >> 1, ' ');
334         std::string rightPadding(paddingSpaces - leftPadding.size(), ' ');
335         newEntriesValue = leftPadding + newEntriesValue + rightPadding; // align center
336     }
337     return newEntriesValue;
338 }
339 
GenerateNewNumProcsValue(uint32_t originLength)340 std::string TraceConverter::GenerateNewNumProcsValue(uint32_t originLength)
341 {
342     std::string newNumProcsValue = std::to_string(numProcessors_);
343     if (newNumProcsValue.size() < originLength) {
344         size_t padding = originLength - newNumProcsValue.size();
345         std::string spaces(padding, ' ');
346         newNumProcsValue = spaces + newNumProcsValue; // align right
347     }
348     return newNumProcsValue;
349 }
350 
351 //                                                                      nppEnd
352 //                                                                         |
353 // format: # entries-in-buffer/entries-written: 4294967295/4294967295   #P:255\n
354 //         |                                   |                        |     |
355 //        esp                                espEnd                    npp   eol
WriteFinalHeader()356 bool TraceConverter::WriteFinalHeader()
357 {
358     HILOG_INFO(LOG_CORE, "WriteFinalHeader start!");
359     auto esp = textHeader_.find(ENTIRES_STATS_PREFIX); // entries stats prefix start
360     CHECK_TRUE(esp != std::string::npos, false, "entries stats prefix not found!");
361     auto espEnd = esp + ENTIRES_STATS_PREFIX.size(); // end of entries stats prefix
362 
363     auto npp = textHeader_.find(PROCESSOR_NUMBER_PREFIX, espEnd); // number processor prefix
364     CHECK_TRUE(npp, false, "number processors prefix not found!");
365     auto nppEnd = npp + PROCESSOR_NUMBER_PREFIX.size();
366 
367     auto eol = textHeader_.find('\n', nppEnd); // end of line
368     CHECK_TRUE(eol != std::string::npos, false, "entries stats line end not found!");
369 
370     size_t entriesValueLen = npp - espEnd;
371     std::string newEntriesValue = GenerateNewEntriesValue(entriesValueLen);
372     CHECK_TRUE(newEntriesValue.size() == entriesValueLen, false, "entries value length mismatch: %zu/%zu",
373                entriesValueLen, newEntriesValue.size());
374     HILOG_DEBUG(LOG_CORE, "newEntriesValue: '%s'", newEntriesValue.c_str());
375 
376     size_t numProcsValueLen = eol - nppEnd;
377     std::string newNumProcsValue = GenerateNewNumProcsValue(numProcsValueLen);
378     CHECK_TRUE(newNumProcsValue.size() == numProcsValueLen, false, "numProcs value length mismatch: %zu/%zu",
379                numProcsValueLen, newNumProcsValue.size());
380     HILOG_DEBUG(LOG_CORE, "newNumProcsValue: '%s'", newNumProcsValue.c_str());
381 
382     auto headerLength = textHeader_.size();
383     textHeader_.replace(nppEnd, newNumProcsValue.size(), newNumProcsValue);
384     textHeader_.replace(espEnd, newEntriesValue.size(), newEntriesValue);
385     CHECK_TRUE(textHeader_.size() == headerLength, false, "header size mismatch %zu => %zu!", headerLength,
386                textHeader_.size());
387 
388     lseek(outputFd_, 0, SEEK_SET); // move write postion to file head
389     auto nbytes = write(outputFd_, textHeader_.data(), textHeader_.size());
390     CHECK_TRUE(static_cast<size_t>(nbytes) == textHeader_.size(), false, "write final header failed!");
391     PrintTextHeader("FINAL TRACE HEADER");
392 
393     // flush file buffer
394     CHECK_TRUE(fsync(outputFd_) == 0, false, "fsync %s FAILED, %d", output_.c_str(), errno);
395     HILOG_INFO(LOG_CORE, "WriteFinalHeader done!");
396     return true;
397 }
398 
ReadAndParseEvents()399 bool TraceConverter::ReadAndParseEvents()
400 {
401     long resultCount = 0;
402     ProfilerPluginData pluginData;
403     auto parseStart = Clock::now();
404     while (reader_.Read(pluginData) > 0) {
405         resultCount++;
406         if (pluginData.name().find("trace") == std::string::npos) {
407             continue;
408         }
409 
410         TracePluginResult result;
411         auto& data = pluginData.data();
412         CHECK_TRUE(result.ParseFromArray(data.data(), data.size()), false, "parse result failed!");
413 
414         std::string progresss = "[" + std::to_string(resultCount) + "/" + std::to_string(numberResults_) + "]";
415         if (result.ftrace_cpu_stats_size() > 0) {
416             HILOG_INFO(LOG_CORE, "%s parse %d cpu stats result...", progresss.c_str(), result.ftrace_cpu_stats_size());
417             for (auto& cpuStats : result.ftrace_cpu_stats()) {
418                 PrintCpuStats(cpuStats);
419                 ParseCpuStats(cpuStats);
420                 numProcessors_ = std::max(numProcessors_, cpuStats.per_cpu_stats_size());
421             }
422             cpuEventQeueue_.resize(numProcessors_, {});
423         } else if (result.symbols_detail_size() > 0) {
424             HILOG_INFO(LOG_CORE, "%s parse %d kernel symbols ...", progresss.c_str(), result.symbols_detail_size());
425             for (auto& msg : result.symbols_detail()) {
426                 kernelSymbols_[msg.symbol_addr()] = msg.symbol_name();
427             }
428         } else if (result.ftrace_cpu_detail_size() > 0) {
429             HILOG_INFO(LOG_CORE, "%s parse %d cpu details...", progresss.c_str(), result.ftrace_cpu_detail_size());
430             for (auto& details : result.ftrace_cpu_detail()) {
431                 const int cpu = details.cpu();
432                 auto& eventQ = cpuEventQeueue_[cpu];
433                 eventQ.reserve(eventQ.size() + details.event_size());
434                 for (int i = 0; i < details.event_size(); i++) {
435                     eventQ.push_back(details.event(i));
436                 }
437             }
438         } else {
439             HILOG_WARN(LOG_CORE, "WARNING: other types of result...");
440         }
441     }
442     auto parseDone = Clock::now();
443     HILOG_INFO(LOG_CORE, "parsed done, results: %ld, time cost: %ld ms!", resultCount,
444                TimeDeltaMs(parseDone, parseStart));
445     HILOG_INFO(LOG_CORE, "CPU numbers: %d", numProcessors_);
446     return true;
447 }
448 
ConvertAndWriteEvents()449 bool TraceConverter::ConvertAndWriteEvents()
450 {
451     uint64_t totalEvents = 0;
452     HILOG_INFO(LOG_CORE, "summary of events on each CPU:");
453     for (size_t i = 0; i < cpuEventQeueue_.size(); i++) {
454         totalEvents += cpuEventQeueue_[i].size();
455         HILOG_INFO(LOG_CORE, "   events on CPU%zu: %zu", i, cpuEventQeueue_[i].size());
456     }
457     HILOG_INFO(LOG_CORE, "events on all CPU: %" PRIu64, totalEvents);
458 
459     // GOAL: sort all events, and write its to file.
460     // on each event list, the events are ordered by timestamp
461     // so we can used multi-way merge algorithm.
462     std::vector<uint32_t> queueFront(numProcessors_, 0);
463     HILOG_INFO(LOG_CORE, "start convert and write events to output...");
464     auto writeStart = Clock::now();
465     for (uint64_t i = 0; i < totalEvents; i++) {
466         // firstly, pick an event with smallest timestamp
467         uint32_t cpuId = 0;
468         uint64_t minTs = UINT64_MAX;
469         for (int c = 0; c < numProcessors_; c++) {
470             uint32_t cursor = queueFront[c];
471             if (cursor >= cpuEventQeueue_[c].size()) { // no more event on this queue
472                 continue;
473             }
474             uint64_t ts = cpuEventQeueue_[c][cursor].timestamp();
475             if (ts < minTs) {
476                 minTs = ts;
477                 cpuId = c;
478             }
479         }
480         // secondly, write picked event to file
481         CHECK_TRUE(WriteEvent(cpuEventQeueue_[cpuId][queueFront[cpuId]], cpuId), false,
482                    "write event [%" PRIu64 "] for CPU%u failed!", i, cpuId);
483         queueFront[cpuId]++;
484         float progress = i * 100.0f / totalEvents;
485         HILOG_INFO(LOG_CORE, "progress = % 3.2f, ts = %" PRIu64 ", cpu = %u", progress, minTs, cpuId);
486     }
487     printf("\n");
488     auto writeDone = Clock::now();
489     HILOG_INFO(LOG_CORE, "convert and write events done, time cost: %ld ms!", TimeDeltaMs(writeDone, writeStart));
490     entriesWritten_ = static_cast<decltype(entriesWritten_)>(totalEvents);
491     return totalEvents;
492 }
493 
Convert()494 bool TraceConverter::Convert()
495 {
496     char realPath[PATH_MAX + 1] = {0};
497 
498     auto startTime = Clock::now();
499     CHECK_TRUE(access(input_.c_str(), R_OK) == 0, false, "input %s not found!", input_.c_str());
500     CHECK_TRUE(reader_.Open(input_), false, "open %s failed!", input_.c_str());
501 
502     auto header = reader_.GetHeader();
503     PrintTraceFileHeader(header);
504     numberResults_ = (header.data_.segments_ >> 1); // pairs of (length segment, data segment)
505     HILOG_INFO(LOG_CORE, "number of results in trace file header: %u", numberResults_);
506 
507     if ((output_.length() > PATH_MAX) || (realpath(output_.c_str(), realPath) == nullptr)) {
508         return false;
509     }
510     outputFd_ = open(realPath, O_CREAT | O_RDWR, OUTPUT_FILE_MODE);
511     CHECK_TRUE(outputFd_ != -1, false, "open %s failed!", output_.c_str());
512     CHECK_TRUE(WriteInitialHeader(), false, "write initial header failed!");
513 
514     CHECK_TRUE(ReadAndParseEvents(), false, "read and parse events failed!");
515     CHECK_TRUE(ConvertAndWriteEvents(), false, "convert and write events failed!");
516     SummarizeStats();
517 
518     CHECK_TRUE(WriteFinalHeader(), false, "write final header failed!");
519     CHECK_TRUE(close(outputFd_) == 0, false, "close %s FAILED, %d", output_.c_str(), errno);
520     outputFd_ = INVALID_FD;
521 
522     auto endTime = Clock::now();
523     HILOG_INFO(LOG_CORE, "convert %s to %s done!", input_.c_str(), output_.c_str());
524     HILOG_INFO(LOG_CORE, "total time cost: %ld ms!", TimeDeltaMs(endTime, startTime));
525     return true;
526 }
527 
SetParseGid(bool parseGid)528 void TraceConverter::SetParseGid(bool parseGid)
529 {
530     parseGid_ = parseGid;
531 }
532 
SetParseFlags(bool parseFlags)533 void TraceConverter::SetParseFlags(bool parseFlags)
534 {
535     parseFlags_ = parseFlags;
536 }
537