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