• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/trace_processor/sqlite/sqlite_raw_table.h"
18 
19 #include <inttypes.h>
20 
21 #include "perfetto/base/compiler.h"
22 #include "perfetto/ext/base/string_utils.h"
23 #include "src/trace_processor/importers/common/system_info_tracker.h"
24 #include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
25 #include "src/trace_processor/sqlite/sqlite_utils.h"
26 #include "src/trace_processor/types/gfp_flags.h"
27 #include "src/trace_processor/types/task_state.h"
28 #include "src/trace_processor/types/variadic.h"
29 
30 #include "protos/perfetto/trace/ftrace/binder.pbzero.h"
31 #include "protos/perfetto/trace/ftrace/clk.pbzero.h"
32 #include "protos/perfetto/trace/ftrace/filemap.pbzero.h"
33 #include "protos/perfetto/trace/ftrace/ftrace.pbzero.h"
34 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
35 #include "protos/perfetto/trace/ftrace/power.pbzero.h"
36 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
37 #include "protos/perfetto/trace/ftrace/workqueue.pbzero.h"
38 
39 namespace perfetto {
40 namespace trace_processor {
41 
42 namespace {
43 
44 struct FtraceTime {
FtraceTimeperfetto::trace_processor::__anon17f1eb3f0111::FtraceTime45   FtraceTime(int64_t ns)
46       : secs(ns / 1000000000LL), micros((ns - secs * 1000000000LL) / 1000) {}
47 
48   const int64_t secs;
49   const int64_t micros;
50 };
51 
52 class ArgsSerializer {
53  public:
54   ArgsSerializer(TraceProcessorContext*,
55                  ArgSetId arg_set_id,
56                  NullTermStringView event_name,
57                  std::vector<uint32_t>* field_id_to_arg_index,
58                  base::StringWriter*);
59 
60   void SerializeArgs();
61 
62  private:
63   using ValueWriter = std::function<void(const Variadic&)>;
64 
WriteArgForField(uint32_t field_id)65   void WriteArgForField(uint32_t field_id) {
66     WriteArgForField(field_id,
67                      [this](const Variadic& v) { return WriteValue(v); });
68   }
69 
WriteArgForField(uint32_t field_id,ValueWriter writer)70   void WriteArgForField(uint32_t field_id, ValueWriter writer) {
71     WriteArgAtRow(FieldIdToRow(field_id), writer);
72   }
73 
WriteValueForField(uint32_t field_id)74   void WriteValueForField(uint32_t field_id) {
75     WriteValueForField(field_id,
76                        [this](const Variadic& v) { return WriteValue(v); });
77   }
78 
WriteValueForField(uint32_t field_id,ValueWriter writer)79   void WriteValueForField(uint32_t field_id, ValueWriter writer) {
80     writer(storage_->GetArgValue(FieldIdToRow(field_id)));
81   }
82 
WriteArgAtRow(uint32_t arg_index)83   void WriteArgAtRow(uint32_t arg_index) {
84     WriteArgAtRow(arg_index,
85                   [this](const Variadic& v) { return WriteValue(v); });
86   }
87 
88   void WriteArgAtRow(uint32_t arg_index, ValueWriter writer);
89 
90   void WriteValue(const Variadic& variadic);
91 
FieldIdToRow(uint32_t field_id)92   uint32_t FieldIdToRow(uint32_t field_id) {
93     PERFETTO_DCHECK(field_id > 0);
94     PERFETTO_DCHECK(field_id < field_id_to_arg_index_->size());
95     uint32_t index_in_arg_set = (*field_id_to_arg_index_)[field_id];
96     return start_row_ + index_in_arg_set;
97   }
98 
99   const TraceStorage* storage_ = nullptr;
100   TraceProcessorContext* context_ = nullptr;
101   ArgSetId arg_set_id_ = kInvalidArgSetId;
102   NullTermStringView event_name_;
103   std::vector<uint32_t>* field_id_to_arg_index_;
104 
105   RowMap row_map_;
106   uint32_t start_row_ = 0;
107 
108   base::StringWriter* writer_ = nullptr;
109 };
110 
ArgsSerializer(TraceProcessorContext * context,ArgSetId arg_set_id,NullTermStringView event_name,std::vector<uint32_t> * field_id_to_arg_index,base::StringWriter * writer)111 ArgsSerializer::ArgsSerializer(TraceProcessorContext* context,
112                                ArgSetId arg_set_id,
113                                NullTermStringView event_name,
114                                std::vector<uint32_t>* field_id_to_arg_index,
115                                base::StringWriter* writer)
116     : context_(context),
117       arg_set_id_(arg_set_id),
118       event_name_(event_name),
119       field_id_to_arg_index_(field_id_to_arg_index),
120       writer_(writer) {
121   storage_ = context_->storage.get();
122   const auto& args = storage_->arg_table();
123   const auto& set_ids = args.arg_set_id();
124 
125   // We assume that the row map is a contiguous range (which is always the case
126   // because arg_set_ids are contiguous by definition).
127   row_map_ = args.FilterToRowMap({set_ids.eq(arg_set_id_)});
128   start_row_ = row_map_.empty() ? 0 : row_map_.Get(0);
129 
130   // If the vector already has entries, we've previously cached the mapping
131   // from field id to arg index.
132   if (!field_id_to_arg_index->empty())
133     return;
134 
135   auto* descriptor = GetMessageDescriptorForName(event_name);
136   if (!descriptor) {
137     // If we don't have a descriptor, this event must be a generic ftrace event.
138     // As we can't possibly have any special handling for generic events, just
139     // add a row to the vector (for the invalid field id 0) to remove future
140     // lookups for this event name.
141     field_id_to_arg_index->resize(1);
142     return;
143   }
144 
145   // If we have a descriptor, try and create the mapping from proto field id
146   // to the index in the arg set.
147   size_t max = descriptor->max_field_id;
148 
149   // We need to reserve an index for the invalid field id 0.
150   field_id_to_arg_index_->resize(max + 1);
151 
152   // Go through each field id and find the entry in the args table for that
153   for (uint32_t i = 1; i <= max; ++i) {
154     for (auto it = row_map_.IterateRows(); it; it.Next()) {
155       base::StringView key = args.key().GetString(it.row());
156       if (key == descriptor->fields[i].name) {
157         (*field_id_to_arg_index)[i] = it.index();
158         break;
159       }
160     }
161   }
162 }
163 
SerializeArgs()164 void ArgsSerializer::SerializeArgs() {
165   if (row_map_.empty())
166     return;
167 
168   if (event_name_ == "sched_switch") {
169     using SS = protos::pbzero::SchedSwitchFtraceEvent;
170 
171     WriteArgForField(SS::kPrevCommFieldNumber);
172     WriteArgForField(SS::kPrevPidFieldNumber);
173     WriteArgForField(SS::kPrevPrioFieldNumber);
174     WriteArgForField(SS::kPrevStateFieldNumber, [this](const Variadic& value) {
175       PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
176       auto state = static_cast<uint16_t>(value.int_value);
177       auto kernel_version =
178           SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
179       writer_->AppendString(
180           ftrace_utils::TaskState(state, kernel_version).ToString('|').data());
181     });
182     writer_->AppendLiteral(" ==>");
183     WriteArgForField(SS::kNextCommFieldNumber);
184     WriteArgForField(SS::kNextPidFieldNumber);
185     WriteArgForField(SS::kNextPrioFieldNumber);
186     return;
187   } else if (event_name_ == "sched_wakeup") {
188     using SW = protos::pbzero::SchedWakeupFtraceEvent;
189     WriteArgForField(SW::kCommFieldNumber);
190     WriteArgForField(SW::kPidFieldNumber);
191     WriteArgForField(SW::kPrioFieldNumber);
192     WriteArgForField(SW::kTargetCpuFieldNumber, [this](const Variadic& value) {
193       PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
194       writer_->AppendPaddedInt<'0', 3>(value.int_value);
195     });
196     return;
197   } else if (event_name_ == "clock_set_rate") {
198     using CSR = protos::pbzero::ClockSetRateFtraceEvent;
199 
200     // We use the string "todo" as the name to stay consistent with old
201     // trace_to_text print code.
202     writer_->AppendString(" todo");
203     WriteArgForField(CSR::kStateFieldNumber);
204     WriteArgForField(CSR::kCpuIdFieldNumber);
205     return;
206   } else if (event_name_ == "clk_set_rate") {
207     using CSR = protos::pbzero::ClkSetRateFtraceEvent;
208     writer_->AppendLiteral(" ");
209     WriteValueForField(CSR::kNameFieldNumber);
210     writer_->AppendLiteral(" ");
211     WriteValueForField(CSR::kRateFieldNumber);
212     return;
213   } else if (event_name_ == "clock_enable") {
214     using CE = protos::pbzero::ClockEnableFtraceEvent;
215     WriteValueForField(CE::kNameFieldNumber);
216     WriteArgForField(CE::kStateFieldNumber);
217     WriteArgForField(CE::kCpuIdFieldNumber);
218     return;
219   } else if (event_name_ == "clock_disable") {
220     using CD = protos::pbzero::ClockDisableFtraceEvent;
221     WriteValueForField(CD::kNameFieldNumber);
222     WriteArgForField(CD::kStateFieldNumber);
223     WriteArgForField(CD::kCpuIdFieldNumber);
224     return;
225   } else if (event_name_ == "binder_transaction") {
226     using BT = protos::pbzero::BinderTransactionFtraceEvent;
227     writer_->AppendString(" transaction=");
228     WriteValueForField(BT::kDebugIdFieldNumber, [this](const Variadic& value) {
229       PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
230       writer_->AppendUnsignedInt(static_cast<uint32_t>(value.int_value));
231     });
232 
233     writer_->AppendString(" dest_node=");
234     WriteValueForField(
235         BT::kTargetNodeFieldNumber, [this](const Variadic& value) {
236           PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
237           writer_->AppendUnsignedInt(static_cast<uint32_t>(value.int_value));
238         });
239 
240     writer_->AppendString(" dest_proc=");
241     WriteValueForField(BT::kToProcFieldNumber);
242 
243     writer_->AppendString(" dest_thread=");
244     WriteValueForField(BT::kToThreadFieldNumber);
245 
246     writer_->AppendString(" reply=");
247     WriteValueForField(BT::kReplyFieldNumber);
248 
249     writer_->AppendString(" flags=0x");
250     WriteValueForField(BT::kFlagsFieldNumber, [this](const Variadic& value) {
251       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
252       writer_->AppendHexInt(value.uint_value);
253     });
254 
255     writer_->AppendString(" code=0x");
256     WriteValueForField(BT::kCodeFieldNumber, [this](const Variadic& value) {
257       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
258       writer_->AppendHexInt(value.uint_value);
259     });
260     return;
261   } else if (event_name_ == "binder_transaction_alloc_buf") {
262     using BTAB = protos::pbzero::BinderTransactionAllocBufFtraceEvent;
263     writer_->AppendString(" transaction=");
264     WriteValueForField(
265         BTAB::kDebugIdFieldNumber, [this](const Variadic& value) {
266           PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
267           writer_->AppendUnsignedInt(static_cast<uint32_t>(value.int_value));
268         });
269     WriteArgForField(BTAB::kDataSizeFieldNumber);
270     WriteArgForField(BTAB::kOffsetsSizeFieldNumber);
271     return;
272   } else if (event_name_ == "binder_transaction_received") {
273     using BTR = protos::pbzero::BinderTransactionReceivedFtraceEvent;
274     writer_->AppendString(" transaction=");
275     WriteValueForField(BTR::kDebugIdFieldNumber, [this](const Variadic& value) {
276       PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
277       writer_->AppendUnsignedInt(static_cast<uint32_t>(value.int_value));
278     });
279     return;
280   } else if (event_name_ == "mm_filemap_add_to_page_cache") {
281     using MFA = protos::pbzero::MmFilemapAddToPageCacheFtraceEvent;
282     writer_->AppendString(" dev ");
283     WriteValueForField(MFA::kSDevFieldNumber, [this](const Variadic& value) {
284       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
285       writer_->AppendUnsignedInt(value.uint_value >> 20);
286     });
287     writer_->AppendString(":");
288     WriteValueForField(MFA::kSDevFieldNumber, [this](const Variadic& value) {
289       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
290       writer_->AppendUnsignedInt(value.uint_value & ((1 << 20) - 1));
291     });
292     writer_->AppendString(" ino ");
293     WriteValueForField(MFA::kIInoFieldNumber, [this](const Variadic& value) {
294       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
295       writer_->AppendHexInt(value.uint_value);
296     });
297     writer_->AppendString(" page=0000000000000000");
298     writer_->AppendString(" pfn=");
299     WriteValueForField(MFA::kPfnFieldNumber);
300     writer_->AppendString(" ofs=");
301     WriteValueForField(MFA::kIndexFieldNumber, [this](const Variadic& value) {
302       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
303       writer_->AppendUnsignedInt(value.uint_value << 12);
304     });
305     return;
306   } else if (event_name_ == "print") {
307     using P = protos::pbzero::PrintFtraceEvent;
308 
309     writer_->AppendChar(' ');
310     WriteValueForField(P::kBufFieldNumber, [this](const Variadic& value) {
311       PERFETTO_DCHECK(value.type == Variadic::Type::kString);
312 
313       NullTermStringView str = storage_->GetString(value.string_value);
314       // If the last character is a newline in a print, just drop it.
315       auto chars_to_print = !str.empty() && str.c_str()[str.size() - 1] == '\n'
316                                 ? str.size() - 1
317                                 : str.size();
318       writer_->AppendString(str.c_str(), chars_to_print);
319     });
320     return;
321   } else if (event_name_ == "sched_blocked_reason") {
322     using SBR = protos::pbzero::SchedBlockedReasonFtraceEvent;
323     WriteArgForField(SBR::kPidFieldNumber);
324     WriteArgForField(SBR::kIoWaitFieldNumber);
325     WriteArgForField(SBR::kCallerFieldNumber, [this](const Variadic& value) {
326       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
327       writer_->AppendHexInt(value.uint_value);
328     });
329     return;
330   } else if (event_name_ == "workqueue_activate_work") {
331     using WAW = protos::pbzero::WorkqueueActivateWorkFtraceEvent;
332     writer_->AppendString(" work struct ");
333     WriteValueForField(WAW::kWorkFieldNumber, [this](const Variadic& value) {
334       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
335       writer_->AppendHexInt(value.uint_value);
336     });
337     return;
338   } else if (event_name_ == "workqueue_execute_start") {
339     using WES = protos::pbzero::WorkqueueExecuteStartFtraceEvent;
340     writer_->AppendString(" work struct ");
341     WriteValueForField(WES::kWorkFieldNumber, [this](const Variadic& value) {
342       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
343       writer_->AppendHexInt(value.uint_value);
344     });
345     writer_->AppendString(": function ");
346     WriteValueForField(WES::kFunctionFieldNumber,
347                        [this](const Variadic& value) {
348                          PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
349                          writer_->AppendHexInt(value.uint_value);
350                        });
351     return;
352   } else if (event_name_ == "workqueue_execute_end") {
353     using WE = protos::pbzero::WorkqueueExecuteEndFtraceEvent;
354     writer_->AppendString(" work struct ");
355     WriteValueForField(WE::kWorkFieldNumber, [this](const Variadic& value) {
356       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
357       writer_->AppendHexInt(value.uint_value);
358     });
359     return;
360   } else if (event_name_ == "workqueue_queue_work") {
361     using WQW = protos::pbzero::WorkqueueQueueWorkFtraceEvent;
362     writer_->AppendString(" work struct=");
363     WriteValueForField(WQW::kWorkFieldNumber, [this](const Variadic& value) {
364       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
365       writer_->AppendHexInt(value.uint_value);
366     });
367     WriteArgForField(WQW::kFunctionFieldNumber, [this](const Variadic& value) {
368       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
369       writer_->AppendHexInt(value.uint_value);
370     });
371     WriteArgForField(WQW::kWorkqueueFieldNumber, [this](const Variadic& value) {
372       PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
373       writer_->AppendHexInt(value.uint_value);
374     });
375     WriteValueForField(WQW::kReqCpuFieldNumber);
376     WriteValueForField(WQW::kCpuFieldNumber);
377     return;
378   }
379 
380   for (auto it = row_map_.IterateRows(); it; it.Next()) {
381     WriteArgAtRow(it.row());
382   }
383 }
384 
WriteArgAtRow(uint32_t arg_row,ValueWriter writer)385 void ArgsSerializer::WriteArgAtRow(uint32_t arg_row, ValueWriter writer) {
386   const auto& args = storage_->arg_table();
387   const auto& key = storage_->GetString(args.key()[arg_row]);
388   auto value = storage_->GetArgValue(arg_row);
389 
390   writer_->AppendChar(' ');
391   writer_->AppendString(key.c_str(), key.size());
392   writer_->AppendChar('=');
393 
394   if (key == "gfp_flags") {
395     auto kernel_version =
396         SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
397     WriteGfpFlag(value.uint_value, kernel_version, writer_);
398     return;
399   }
400   writer(value);
401 }
402 
WriteValue(const Variadic & value)403 void ArgsSerializer::WriteValue(const Variadic& value) {
404   switch (value.type) {
405     case Variadic::kInt:
406       writer_->AppendInt(value.int_value);
407       break;
408     case Variadic::kUint:
409       writer_->AppendUnsignedInt(value.uint_value);
410       break;
411     case Variadic::kString: {
412       const auto& str = storage_->GetString(value.string_value);
413       writer_->AppendString(str.c_str(), str.size());
414       break;
415     }
416     case Variadic::kReal:
417       writer_->AppendDouble(value.real_value);
418       break;
419     case Variadic::kPointer:
420       writer_->AppendUnsignedInt(value.pointer_value);
421       break;
422     case Variadic::kBool:
423       writer_->AppendBool(value.bool_value);
424       break;
425     case Variadic::kJson: {
426       const auto& str = storage_->GetString(value.json_value);
427       writer_->AppendString(str.c_str(), str.size());
428       break;
429     }
430   }
431 }
432 
433 }  // namespace
434 
SqliteRawTable(sqlite3 * db,Context context)435 SqliteRawTable::SqliteRawTable(sqlite3* db, Context context)
436     : DbSqliteTable(
437           db,
438           {context.cache, tables::RawTable::Schema(), TableComputation::kStatic,
439            &context.context->storage->raw_table(), nullptr}),
440       serializer_(context.context) {
__anon17f1eb3f1a02(sqlite3_context* ctx, int argc, sqlite3_value** argv) 441   auto fn = [](sqlite3_context* ctx, int argc, sqlite3_value** argv) {
442     auto* thiz = static_cast<SqliteRawTable*>(sqlite3_user_data(ctx));
443     thiz->ToSystrace(ctx, argc, argv);
444   };
445   sqlite3_create_function(db, "to_ftrace", 1,
446                           SQLITE_UTF8 | SQLITE_DETERMINISTIC, this, fn, nullptr,
447                           nullptr);
448 }
449 
450 SqliteRawTable::~SqliteRawTable() = default;
451 
RegisterTable(sqlite3 * db,QueryCache * cache,TraceProcessorContext * context)452 void SqliteRawTable::RegisterTable(sqlite3* db,
453                                    QueryCache* cache,
454                                    TraceProcessorContext* context) {
455   SqliteTable::Register<SqliteRawTable, Context>(db, Context{cache, context},
456                                                  "raw");
457 }
458 
ToSystrace(sqlite3_context * ctx,int argc,sqlite3_value ** argv)459 void SqliteRawTable::ToSystrace(sqlite3_context* ctx,
460                                 int argc,
461                                 sqlite3_value** argv) {
462   if (argc != 1 || sqlite3_value_type(argv[0]) != SQLITE_INTEGER) {
463     sqlite3_result_error(ctx, "Usage: to_ftrace(id)", -1);
464     return;
465   }
466   uint32_t row = static_cast<uint32_t>(sqlite3_value_int64(argv[0]));
467 
468   auto str = serializer_.SerializeToString(row);
469   sqlite3_result_text(ctx, str.release(), -1, free);
470 }
471 
SystraceSerializer(TraceProcessorContext * context)472 SystraceSerializer::SystraceSerializer(TraceProcessorContext* context)
473     : context_(context) {
474   storage_ = context_->storage.get();
475 }
476 
SerializeToString(uint32_t raw_row)477 SystraceSerializer::ScopedCString SystraceSerializer::SerializeToString(
478     uint32_t raw_row) {
479   const auto& raw = storage_->raw_table();
480 
481   char line[4096];
482   base::StringWriter writer(line, sizeof(line));
483 
484   SerializePrefix(raw_row, &writer);
485 
486   StringId event_name_id = raw.name()[raw_row];
487   NullTermStringView event_name = storage_->GetString(event_name_id);
488   writer.AppendChar(' ');
489   if (event_name == "print") {
490     writer.AppendString("tracing_mark_write");
491   } else {
492     writer.AppendString(event_name.c_str(), event_name.size());
493   }
494   writer.AppendChar(':');
495 
496   ArgsSerializer serializer(context_, raw.arg_set_id()[raw_row], event_name,
497                             &proto_id_to_arg_index_by_event_[event_name_id],
498                             &writer);
499   serializer.SerializeArgs();
500 
501   return ScopedCString(writer.CreateStringCopy(), free);
502 }
503 
SerializePrefix(uint32_t raw_row,base::StringWriter * writer)504 void SystraceSerializer::SerializePrefix(uint32_t raw_row,
505                                          base::StringWriter* writer) {
506   const auto& raw = storage_->raw_table();
507 
508   int64_t ts = raw.ts()[raw_row];
509   uint32_t cpu = raw.cpu()[raw_row];
510 
511   UniqueTid utid = raw.utid()[raw_row];
512   uint32_t tid = storage_->thread_table().tid()[utid];
513 
514   uint32_t tgid = 0;
515   auto opt_upid = storage_->thread_table().upid()[utid];
516   if (opt_upid.has_value()) {
517     tgid = storage_->process_table().pid()[*opt_upid];
518   }
519   auto name = storage_->GetString(storage_->thread_table().name()[utid]);
520 
521   FtraceTime ftrace_time(ts);
522   if (tid == 0) {
523     name = "<idle>";
524   } else if (name == "") {
525     name = "<unknown>";
526   } else if (name == "CrRendererMain") {
527     // TODO(taylori): Remove this when crbug.com/978093 is fixed or
528     // when a better solution is found.
529     name = "CrRendererMainThread";
530   }
531 
532   int64_t padding = 16 - static_cast<int64_t>(name.size());
533   if (padding > 0) {
534     writer->AppendChar(' ', static_cast<size_t>(padding));
535   }
536   for (size_t i = 0; i < name.size(); ++i) {
537     char c = name.data()[i];
538     writer->AppendChar(c == '-' ? '_' : c);
539   }
540   writer->AppendChar('-');
541 
542   size_t pre_pid_pos = writer->pos();
543   writer->AppendInt(tid);
544   size_t pid_chars = writer->pos() - pre_pid_pos;
545   if (PERFETTO_LIKELY(pid_chars < 5)) {
546     writer->AppendChar(' ', 5 - pid_chars);
547   }
548 
549   writer->AppendLiteral(" (");
550   if (tgid == 0) {
551     writer->AppendLiteral("-----");
552   } else {
553     writer->AppendPaddedInt<' ', 5>(tgid);
554   }
555   writer->AppendLiteral(") [");
556   writer->AppendPaddedInt<'0', 3>(cpu);
557   writer->AppendLiteral("] .... ");
558 
559   writer->AppendInt(ftrace_time.secs);
560   writer->AppendChar('.');
561   writer->AppendPaddedInt<'0', 6>(ftrace_time.micros);
562   writer->AppendChar(':');
563 }
564 
565 }  // namespace trace_processor
566 }  // namespace perfetto
567