Coverage Report

Created: 2025-07-23 06:46

/src/perfetto/src/traced/probes/ftrace/cpu_reader.cc
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2017 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/traced/probes/ftrace/cpu_reader.h"
18
19
#include <dirent.h>
20
#include <fcntl.h>
21
22
#include <algorithm>
23
#include <optional>
24
#include <utility>
25
26
#include "perfetto/base/logging.h"
27
#include "perfetto/ext/base/metatrace.h"
28
#include "perfetto/ext/base/utils.h"
29
#include "perfetto/ext/tracing/core/trace_writer.h"
30
#include "src/kallsyms/kernel_symbol_map.h"
31
#include "src/kallsyms/lazy_kernel_symbolizer.h"
32
#include "src/traced/probes/ftrace/ftrace_config_muxer.h"
33
#include "src/traced/probes/ftrace/ftrace_controller.h"  // FtraceClockSnapshot
34
#include "src/traced/probes/ftrace/ftrace_data_source.h"
35
#include "src/traced/probes/ftrace/ftrace_print_filter.h"
36
#include "src/traced/probes/ftrace/proto_translation_table.h"
37
38
#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
39
#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
40
#include "protos/perfetto/trace/ftrace/ftrace_stats.pbzero.h"  // FtraceParseStatus
41
#include "protos/perfetto/trace/ftrace/generic.pbzero.h"
42
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
43
#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
44
#include "protos/perfetto/trace/trace_packet.pbzero.h"
45
46
namespace perfetto {
47
namespace {
48
49
using FtraceParseStatus = protos::pbzero::FtraceParseStatus;
50
using protos::pbzero::KprobeEvent;
51
52
// If the compact_sched buffer accumulates more unique strings, the reader will
53
// flush it to reset the interning state (and make it cheap again).
54
// This is not an exact cap, since we check only at tracing page boundaries.
55
constexpr size_t kCompactSchedInternerThreshold = 64;
56
57
// For further documentation of these constants see the kernel source:
58
//   linux/include/linux/ring_buffer.h
59
// Some of this is also available to userspace at runtime via:
60
//   /sys/kernel/tracing/events/header_event
61
constexpr uint32_t kTypePadding = 29;
62
constexpr uint32_t kTypeTimeExtend = 30;
63
constexpr uint32_t kTypeTimeStamp = 31;
64
65
struct EventHeader {
66
  // bottom 5 bits
67
  uint32_t type_or_length : 5;
68
  // top 27 bits
69
  uint32_t time_delta : 27;
70
};
71
72
// Reads a string from `start` until the first '\0' byte or until fixed_len
73
// characters have been read. Appends it to `*out` as field `field_id`.
74
void ReadIntoString(const uint8_t* start,
75
                    size_t fixed_len,
76
                    uint32_t field_id,
77
10.1k
                    protozero::Message* out) {
78
10.1k
  size_t len = strnlen(reinterpret_cast<const char*>(start), fixed_len);
79
10.1k
  out->AppendBytes(field_id, reinterpret_cast<const char*>(start), len);
80
10.1k
}
81
82
bool ReadDataLoc(const uint8_t* start,
83
                 const uint8_t* field_start,
84
                 const uint8_t* end,
85
                 const Field& field,
86
0
                 protozero::Message* message) {
87
0
  PERFETTO_DCHECK(field.ftrace_size == 4);
88
  // See kernel header include/trace/trace_events.h
89
0
  uint32_t data = 0;
90
0
  const uint8_t* ptr = field_start;
91
0
  if (!CpuReader::ReadAndAdvance(&ptr, end, &data)) {
92
0
    PERFETTO_DFATAL("couldn't read __data_loc value");
93
0
    return false;
94
0
  }
95
96
0
  const uint16_t offset = data & 0xffff;
97
0
  const uint16_t len = (data >> 16) & 0xffff;
98
0
  const uint8_t* const string_start = start + offset;
99
100
0
  if (PERFETTO_UNLIKELY(len == 0))
101
0
    return true;
102
0
  if (PERFETTO_UNLIKELY(string_start < start || string_start + len > end)) {
103
0
    PERFETTO_DFATAL("__data_loc points at invalid location");
104
0
    return false;
105
0
  }
106
0
  ReadIntoString(string_start, len, field.proto_field_id, message);
107
0
  return true;
108
0
}
109
110
template <typename T>
111
0
T ReadValue(const uint8_t* ptr) {
112
0
  T t;
113
0
  memcpy(&t, reinterpret_cast<const void*>(ptr), sizeof(T));
114
0
  return t;
115
0
}
Unexecuted instantiation: cpu_reader.cc:unsigned long perfetto::(anonymous namespace)::ReadValue<unsigned long>(unsigned char const*)
Unexecuted instantiation: cpu_reader.cc:unsigned int perfetto::(anonymous namespace)::ReadValue<unsigned int>(unsigned char const*)
Unexecuted instantiation: cpu_reader.cc:int perfetto::(anonymous namespace)::ReadValue<int>(unsigned char const*)
Unexecuted instantiation: cpu_reader.cc:unsigned char perfetto::(anonymous namespace)::ReadValue<unsigned char>(unsigned char const*)
116
117
// Reads a signed ftrace value as an int64_t, sign extending if necessary.
118
0
int64_t ReadSignedFtraceValue(const uint8_t* ptr, FtraceFieldType ftrace_type) {
119
0
  if (ftrace_type == kFtraceInt32) {
120
0
    int32_t value;
121
0
    memcpy(&value, reinterpret_cast<const void*>(ptr), sizeof(value));
122
0
    return int64_t(value);
123
0
  }
124
0
  if (ftrace_type == kFtraceInt64) {
125
0
    int64_t value;
126
0
    memcpy(&value, reinterpret_cast<const void*>(ptr), sizeof(value));
127
0
    return value;
128
0
  }
129
0
  PERFETTO_FATAL("unexpected ftrace type");
130
0
}
131
132
0
bool SetBlocking(int fd, bool is_blocking) {
133
0
  int flags = fcntl(fd, F_GETFL, 0);
134
0
  flags = (is_blocking) ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
135
0
  return fcntl(fd, F_SETFL, flags) == 0;
136
0
}
137
138
void SetParseError(const std::set<FtraceDataSource*>& started_data_sources,
139
                   size_t cpu,
140
0
                   FtraceParseStatus status) {
141
0
  PERFETTO_DPLOG("[cpu%zu]: unexpected ftrace read error: %s", cpu,
142
0
                 protos::pbzero::FtraceParseStatus_Name(status));
143
0
  for (FtraceDataSource* data_source : started_data_sources) {
144
0
    data_source->mutable_parse_errors()->insert(status);
145
0
  }
146
0
}
147
148
void WriteAndSetParseError(CpuReader::Bundler* bundler,
149
                           base::FlatSet<FtraceParseStatus>* stat,
150
                           uint64_t timestamp,
151
1.09k
                           FtraceParseStatus status) {
152
1.09k
  PERFETTO_DLOG("Error parsing ftrace page: %s",
153
1.09k
                protos::pbzero::FtraceParseStatus_Name(status));
154
1.09k
  stat->insert(status);
155
1.09k
  auto* proto = bundler->GetOrCreateBundle()->add_error();
156
1.09k
  if (timestamp)
157
1.07k
    proto->set_timestamp(timestamp);
158
1.09k
  proto->set_status(status);
159
1.09k
}
160
161
}  // namespace
162
163
using protos::pbzero::GenericFtraceEvent;
164
165
CpuReader::CpuReader(size_t cpu,
166
                     base::ScopedFile trace_fd,
167
                     const ProtoTranslationTable* table,
168
                     LazyKernelSymbolizer* symbolizer,
169
                     protos::pbzero::FtraceClock ftrace_clock,
170
                     const FtraceClockSnapshot* ftrace_clock_snapshot)
171
0
    : cpu_(cpu),
172
0
      table_(table),
173
0
      symbolizer_(symbolizer),
174
0
      trace_fd_(std::move(trace_fd)),
175
0
      ftrace_clock_(ftrace_clock),
176
0
      ftrace_clock_snapshot_(ftrace_clock_snapshot) {
177
0
  PERFETTO_CHECK(trace_fd_);
178
0
  PERFETTO_CHECK(SetBlocking(*trace_fd_, false));
179
0
}
180
181
0
CpuReader::~CpuReader() = default;
182
183
size_t CpuReader::ReadCycle(
184
    ParsingBuffers* parsing_bufs,
185
    size_t max_pages,
186
0
    const std::set<FtraceDataSource*>& started_data_sources) {
187
0
  PERFETTO_DCHECK(max_pages > 0 && parsing_bufs->ftrace_data_buf_pages() > 0);
188
0
  metatrace::ScopedEvent evt(metatrace::TAG_FTRACE,
189
0
                             metatrace::FTRACE_CPU_READ_CYCLE);
190
191
  // Work in batches to keep cache locality, and limit memory usage.
192
0
  size_t total_pages_read = 0;
193
0
  for (bool is_first_batch = true;; is_first_batch = false) {
194
0
    size_t batch_pages = std::min(parsing_bufs->ftrace_data_buf_pages(),
195
0
                                  max_pages - total_pages_read);
196
0
    size_t pages_read = ReadAndProcessBatch(
197
0
        parsing_bufs->ftrace_data_buf(), batch_pages, is_first_batch,
198
0
        parsing_bufs->compact_sched_buf(), started_data_sources);
199
200
0
    PERFETTO_DCHECK(pages_read <= batch_pages);
201
0
    total_pages_read += pages_read;
202
203
    // Check whether we've caught up to the writer, or possibly giving up on
204
    // this attempt due to some error.
205
0
    if (pages_read != batch_pages)
206
0
      break;
207
    // Check if we've hit the limit of work for this cycle.
208
0
    if (total_pages_read >= max_pages)
209
0
      break;
210
0
  }
211
0
  PERFETTO_METATRACE_COUNTER(TAG_FTRACE, FTRACE_PAGES_DRAINED,
212
0
                             total_pages_read);
213
0
  return total_pages_read;
214
0
}
215
216
// metatrace note: mark the reading phase as FTRACE_CPU_READ_BATCH, but let the
217
// parsing time be implied (by the difference between the caller's span, and
218
// this reading span). Makes it easier to estimate the read/parse ratio when
219
// looking at the trace in the UI.
220
size_t CpuReader::ReadAndProcessBatch(
221
    uint8_t* parsing_buf,
222
    size_t max_pages,
223
    bool first_batch_in_cycle,
224
    CompactSchedBuffer* compact_sched_buf,
225
0
    const std::set<FtraceDataSource*>& started_data_sources) {
226
0
  const uint32_t sys_page_size = base::GetSysPageSize();
227
0
  size_t pages_read = 0;
228
0
  {
229
0
    metatrace::ScopedEvent evt(metatrace::TAG_FTRACE,
230
0
                               metatrace::FTRACE_CPU_READ_BATCH);
231
0
    for (; pages_read < max_pages;) {
232
0
      uint8_t* curr_page = parsing_buf + (pages_read * sys_page_size);
233
0
      ssize_t res = PERFETTO_EINTR(read(*trace_fd_, curr_page, sys_page_size));
234
0
      if (res < 0) {
235
        // Expected errors:
236
        // EAGAIN: no data (since we're in non-blocking mode).
237
        // ENOMEM, EBUSY: temporary ftrace failures (they happen).
238
        // ENODEV: the cpu is offline (b/145583318).
239
0
        if (errno != EAGAIN && errno != ENOMEM && errno != EBUSY &&
240
0
            errno != ENODEV) {
241
0
          SetParseError(started_data_sources, cpu_,
242
0
                        FtraceParseStatus::FTRACE_STATUS_UNEXPECTED_READ_ERROR);
243
0
        }
244
0
        break;  // stop reading regardless of errno
245
0
      }
246
247
      // As long as all of our reads are for a single page, the kernel should
248
      // return exactly a well-formed raw ftrace page (if not in the steady
249
      // state of reading out fully-written pages, the kernel will construct
250
      // pages as necessary, copying over events and zero-filling at the end).
251
      // A sub-page read() is therefore not expected in practice. Kernel source
252
      // pointer: see usage of |info->read| within |tracing_buffers_read|.
253
0
      if (res == 0) {
254
        // Very rare, but possible. Stop for now, as this seems to occur when
255
        // we've caught up to the writer.
256
0
        PERFETTO_DLOG("[cpu%zu]: 0-sized read from ftrace pipe.", cpu_);
257
0
        break;
258
0
      }
259
0
      if (res != static_cast<ssize_t>(sys_page_size)) {
260
0
        SetParseError(started_data_sources, cpu_,
261
0
                      FtraceParseStatus::FTRACE_STATUS_PARTIAL_PAGE_READ);
262
0
        break;
263
0
      }
264
265
0
      pages_read += 1;
266
267
      // Heuristic for detecting whether we've caught up to the writer, based on
268
      // how much data is in this tracing page. To figure out the amount of
269
      // ftrace data, we need to parse the page header (since the read always
270
      // returns a page, zero-filled at the end). If we read fewer bytes than
271
      // the threshold, it means that we caught up with the write pointer and we
272
      // started consuming ftrace events in real-time. This cannot be just 4096
273
      // because it needs to account for fragmentation, i.e. for the fact that
274
      // the last trace event didn't fit in the current page and hence the
275
      // current page was terminated prematurely. This threshold is quite
276
      // permissive since Android userspace tracing can log >500 byte strings
277
      // via ftrace/print events.
278
      // It's still possible for false positives if events can be bigger than
279
      // half a page, but we don't have a robust way of checking buffer
280
      // occupancy with nonblocking reads. This can be revisited once all
281
      // kernels can be assumed to have bug-free poll() or reliable
282
      // tracefs/per_cpu/cpuX/stats values.
283
0
      static const size_t kPageFillThreshold = sys_page_size / 2;
284
0
      const uint8_t* scratch_ptr = curr_page;
285
0
      std::optional<PageHeader> hdr =
286
0
          ParsePageHeader(&scratch_ptr, table_->page_header_size_len());
287
0
      PERFETTO_DCHECK(hdr && hdr->size > 0 && hdr->size <= sys_page_size);
288
0
      if (!hdr.has_value()) {
289
        // The header error will be logged by ProcessPagesForDataSource.
290
0
        break;
291
0
      }
292
      // Note that the first read after starting the read cycle being small is
293
      // normal. It means that we're given the remainder of events from a
294
      // page that we've partially consumed during the last read of the previous
295
      // cycle (having caught up to the writer).
296
0
      if (hdr->size < kPageFillThreshold &&
297
0
          !(first_batch_in_cycle && pages_read == 1)) {
298
0
        break;
299
0
      }
300
0
    }
301
0
  }  // end of metatrace::FTRACE_CPU_READ_BATCH
302
303
  // Parse the pages and write to the trace for all relevant data sources.
304
0
  if (pages_read == 0)
305
0
    return pages_read;
306
307
0
  for (FtraceDataSource* data_source : started_data_sources) {
308
0
    ProcessPagesForDataSource(
309
0
        data_source->trace_writer(), data_source->mutable_metadata(), cpu_,
310
0
        data_source->parsing_config(), data_source->mutable_parse_errors(),
311
0
        data_source->mutable_bundle_end_timestamp(cpu_), parsing_buf,
312
0
        pages_read, compact_sched_buf, table_, symbolizer_,
313
0
        ftrace_clock_snapshot_, ftrace_clock_);
314
0
  }
315
0
  return pages_read;
316
0
}
317
318
void CpuReader::Bundler::StartNewPacket(
319
    bool lost_events,
320
1.23k
    uint64_t previous_bundle_end_timestamp) {
321
1.23k
  FinalizeAndRunSymbolizer();
322
1.23k
  packet_ = trace_writer_->NewTracePacket();
323
1.23k
  bundle_ = packet_->set_ftrace_events();
324
325
1.23k
  bundle_->set_cpu(static_cast<uint32_t>(cpu_));
326
1.23k
  if (lost_events) {
327
624
    bundle_->set_lost_events(true);
328
624
  }
329
330
  // note: set-to-zero is valid and expected for the first bundle per cpu
331
  // (outside of concurrent tracing), with the effective meaning of "all data is
332
  // valid since the data source was started".
333
1.23k
  bundle_->set_previous_bundle_end_timestamp(previous_bundle_end_timestamp);
334
335
1.23k
  if (ftrace_clock_) {
336
0
    bundle_->set_ftrace_clock(ftrace_clock_);
337
0
    if (ftrace_clock_snapshot_ && ftrace_clock_snapshot_->ftrace_clock_ts) {
338
0
      bundle_->set_ftrace_timestamp(ftrace_clock_snapshot_->ftrace_clock_ts);
339
0
      bundle_->set_boot_timestamp(ftrace_clock_snapshot_->boot_clock_ts);
340
0
    }
341
0
  }
342
1.23k
}
343
344
2.50k
void CpuReader::Bundler::FinalizeAndRunSymbolizer() {
345
2.50k
  if (!packet_) {
346
1.26k
    return;
347
1.26k
  }
348
349
1.23k
  if (compact_sched_enabled_) {
350
0
    compact_sched_buf_->WriteAndReset(bundle_);
351
0
  }
352
353
1.23k
  bundle_->Finalize();
354
1.23k
  bundle_ = nullptr;
355
  // Write the kernel symbol index (mangled address) -> name table.
356
  // |metadata| is shared across all cpus, is distinct per |data_source| (i.e.
357
  // tracing session) and is cleared after each FtraceController::ReadTick().
358
1.23k
  if (symbolizer_) {
359
    // Symbol indexes are assigned mononically as |kernel_addrs.size()|,
360
    // starting from index 1 (no symbol has index 0). Here we remember the
361
    // size() (which is also == the highest value in |kernel_addrs|) at the
362
    // beginning and only write newer indexes bigger than that.
363
0
    uint32_t max_index_at_start = metadata_->last_kernel_addr_index_written;
364
0
    PERFETTO_DCHECK(max_index_at_start <= metadata_->kernel_addrs.size());
365
0
    protos::pbzero::InternedData* interned_data = nullptr;
366
0
    auto* ksyms_map = symbolizer_->GetOrCreateKernelSymbolMap();
367
0
    bool wrote_at_least_one_symbol = false;
368
0
    for (const FtraceMetadata::KernelAddr& kaddr : metadata_->kernel_addrs) {
369
0
      if (kaddr.index <= max_index_at_start)
370
0
        continue;
371
0
      std::string sym_name = ksyms_map->Lookup(kaddr.addr);
372
0
      if (sym_name.empty()) {
373
        // Lookup failed. This can genuinely happen in many occasions. E.g.,
374
        // workqueue_execute_start has two pointers: one is a pointer to a
375
        // function (which we expect to be symbolized), the other (|work|) is
376
        // a pointer to a heap struct, which is unsymbolizable, even when
377
        // using the textual ftrace endpoint.
378
0
        continue;
379
0
      }
380
381
0
      if (!interned_data) {
382
        // If this is the very first write, clear the start of the sequence
383
        // so the trace processor knows that all previous indexes can be
384
        // discarded and that the mapping is restarting.
385
        // In most cases this occurs with cpu==0. But if cpu0 is idle, this
386
        // will happen with the first CPU that has any ftrace data.
387
0
        if (max_index_at_start == 0) {
388
0
          packet_->set_sequence_flags(
389
0
              protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
390
0
        }
391
0
        interned_data = packet_->set_interned_data();
392
0
      }
393
0
      auto* interned_sym = interned_data->add_kernel_symbols();
394
0
      interned_sym->set_iid(kaddr.index);
395
0
      interned_sym->set_str(sym_name);
396
0
      wrote_at_least_one_symbol = true;
397
0
    }
398
399
0
    auto max_it_at_end = static_cast<uint32_t>(metadata_->kernel_addrs.size());
400
401
    // Rationale for the if (wrote_at_least_one_symbol) check: in rare cases,
402
    // all symbols seen in a ProcessPagesForDataSource() call can fail the
403
    // ksyms_map->Lookup(). If that happens we don't want to bump the
404
    // last_kernel_addr_index_written watermark, as that would cause the next
405
    // call to NOT emit the SEQ_INCREMENTAL_STATE_CLEARED.
406
0
    if (wrote_at_least_one_symbol) {
407
0
      metadata_->last_kernel_addr_index_written = max_it_at_end;
408
0
    }
409
0
  }
410
1.23k
  packet_ = TraceWriter::TracePacketHandle(nullptr);
411
1.23k
}
412
413
// Error handling: will attempt parsing all pages even if there are errors in
414
// parsing the binary layout of the data. The error will be recorded in the
415
// event bundle proto with a timestamp, letting the trace processor decide
416
// whether to discard or keep the post-error data. Previously, we crashed as
417
// soon as we encountered such an error.
418
// static
419
bool CpuReader::ProcessPagesForDataSource(
420
    TraceWriter* trace_writer,
421
    FtraceMetadata* metadata,
422
    size_t cpu,
423
    const FtraceDataSourceConfig* ds_config,
424
    base::FlatSet<protos::pbzero::FtraceParseStatus>* parse_errors,
425
    uint64_t* bundle_end_timestamp,
426
    const uint8_t* parsing_buf,
427
    const size_t pages_read,
428
    CompactSchedBuffer* compact_sched_buf,
429
    const ProtoTranslationTable* table,
430
    LazyKernelSymbolizer* symbolizer,
431
    const FtraceClockSnapshot* ftrace_clock_snapshot,
432
1.26k
    protos::pbzero::FtraceClock ftrace_clock) {
433
1.26k
  const uint32_t sys_page_size = base::GetSysPageSize();
434
1.26k
  Bundler bundler(trace_writer, metadata,
435
1.26k
                  ds_config->symbolize_ksyms ? symbolizer : nullptr, cpu,
436
1.26k
                  ftrace_clock_snapshot, ftrace_clock, compact_sched_buf,
437
1.26k
                  ds_config->compact_sched.enabled, *bundle_end_timestamp);
438
439
1.26k
  bool success = true;
440
1.26k
  size_t pages_parsed = 0;
441
1.26k
  bool compact_sched_enabled = ds_config->compact_sched.enabled;
442
2.53k
  for (; pages_parsed < pages_read; pages_parsed++) {
443
1.26k
    const uint8_t* curr_page = parsing_buf + (pages_parsed * sys_page_size);
444
1.26k
    const uint8_t* curr_page_end = curr_page + sys_page_size;
445
1.26k
    const uint8_t* parse_pos = curr_page;
446
1.26k
    std::optional<PageHeader> page_header =
447
1.26k
        ParsePageHeader(&parse_pos, table->page_header_size_len());
448
449
1.26k
    if (!page_header.has_value() || page_header->size == 0 ||
450
1.26k
        parse_pos >= curr_page_end ||
451
1.26k
        parse_pos + page_header->size > curr_page_end) {
452
174
      WriteAndSetParseError(
453
174
          &bundler, parse_errors,
454
174
          page_header.has_value() ? page_header->timestamp : 0,
455
174
          FtraceParseStatus::FTRACE_STATUS_ABI_INVALID_PAGE_HEADER);
456
174
      success = false;
457
174
      continue;
458
174
    }
459
460
    // Start a new bundle if either:
461
    // * The page we're about to read indicates that there was a kernel ring
462
    //   buffer overrun since our last read from that per-cpu buffer. We have
463
    //   a single |lost_events| field per bundle, so start a new packet.
464
    // * The compact_sched buffer is holding more unique interned strings than
465
    //   a threshold. We need to flush the compact buffer to make the
466
    //   interning lookups cheap again.
467
1.09k
    bool interner_past_threshold =
468
1.09k
        compact_sched_enabled &&
469
1.09k
        bundler.compact_sched_buf()->interner().interned_comms_size() >
470
0
            kCompactSchedInternerThreshold;
471
472
1.09k
    if (page_header->lost_events || interner_past_threshold) {
473
      // pass in an updated bundle_end_timestamp since we're starting a new
474
      // bundle, which needs to reference the last timestamp from the prior one.
475
624
      bundler.StartNewPacket(page_header->lost_events, *bundle_end_timestamp);
476
624
    }
477
478
1.09k
    FtraceParseStatus status =
479
1.09k
        ParsePagePayload(parse_pos, &page_header.value(), table, ds_config,
480
1.09k
                         &bundler, metadata, bundle_end_timestamp);
481
482
1.09k
    if (status != FtraceParseStatus::FTRACE_STATUS_OK) {
483
920
      WriteAndSetParseError(&bundler, parse_errors, page_header->timestamp,
484
920
                            status);
485
920
      success = false;
486
920
      continue;
487
920
    }
488
1.09k
  }
489
  // bundler->FinalizeAndRunSymbolizer() will run as part of the destructor.
490
1.26k
  return success;
491
1.26k
}
492
493
// A page header consists of:
494
// * timestamp: 8 bytes
495
// * commit: 8 bytes on 64 bit, 4 bytes on 32 bit kernels
496
//
497
// The kernel reports this at /sys/kernel/debug/tracing/events/header_page.
498
//
499
// |commit|'s bottom bits represent the length of the payload following this
500
// header. The top bits have been repurposed as a bitset of flags pertaining to
501
// data loss. We look only at the "there has been some data lost" flag
502
// (RB_MISSED_EVENTS), and ignore the relatively tricky "appended the precise
503
// lost events count past the end of the valid data, as there was room to do so"
504
// flag (RB_MISSED_STORED).
505
//
506
// static
507
std::optional<CpuReader::PageHeader> CpuReader::ParsePageHeader(
508
    const uint8_t** ptr,
509
1.26k
    uint16_t page_header_size_len) {
510
  // Mask for the data length portion of the |commit| field. Note that the
511
  // kernel implementation never explicitly defines the boundary (beyond using
512
  // bits 30 and 31 as flags), but 27 bits are mentioned as sufficient in the
513
  // original commit message, and is the constant used by trace-cmd.
514
1.26k
  constexpr static uint64_t kDataSizeMask = (1ull << 27) - 1;
515
  // If set, indicates that the relevant cpu has lost events since the last read
516
  // (clearing the bit internally).
517
1.26k
  constexpr static uint64_t kMissedEventsFlag = (1ull << 31);
518
519
1.26k
  const uint8_t* end_of_page = *ptr + base::GetSysPageSize();
520
1.26k
  PageHeader page_header;
521
1.26k
  if (!CpuReader::ReadAndAdvance<uint64_t>(ptr, end_of_page,
522
1.26k
                                           &page_header.timestamp))
523
0
    return std::nullopt;
524
525
1.26k
  uint32_t size_and_flags;
526
527
  // On little endian, we can just read a uint32_t and reject the rest of the
528
  // number later.
529
1.26k
  if (!CpuReader::ReadAndAdvance<uint32_t>(
530
1.26k
          ptr, end_of_page, base::AssumeLittleEndian(&size_and_flags)))
531
0
    return std::nullopt;
532
533
1.26k
  page_header.size = size_and_flags & kDataSizeMask;
534
1.26k
  page_header.lost_events = bool(size_and_flags & kMissedEventsFlag);
535
1.26k
  PERFETTO_DCHECK(page_header.size <= base::GetSysPageSize());
536
537
  // Reject rest of the number, if applicable. On 32-bit, size_bytes - 4 will
538
  // evaluate to 0 and this will be a no-op. On 64-bit, this will advance by 4
539
  // bytes.
540
1.26k
  PERFETTO_DCHECK(page_header_size_len >= 4);
541
1.26k
  *ptr += page_header_size_len - 4;
542
543
1.26k
  return std::make_optional(page_header);
544
1.26k
}
545
546
// A raw ftrace buffer page consists of a header followed by a sequence of
547
// binary ftrace events. See |ParsePageHeader| for the format of the earlier.
548
//
549
// Error handling: if the binary data disagrees with our understanding of the
550
// ring buffer layout, returns an error and skips the rest of the page (but some
551
// events may have already been parsed and serialised).
552
//
553
// This method is deliberately static so it can be tested independently.
554
protos::pbzero::FtraceParseStatus CpuReader::ParsePagePayload(
555
    const uint8_t* start_of_payload,
556
    const PageHeader* page_header,
557
    const ProtoTranslationTable* table,
558
    const FtraceDataSourceConfig* ds_config,
559
    Bundler* bundler,
560
    FtraceMetadata* metadata,
561
1.09k
    uint64_t* bundle_end_timestamp) {
562
1.09k
  const uint8_t* ptr = start_of_payload;
563
1.09k
  const uint8_t* const end = ptr + page_header->size;
564
565
1.09k
  uint64_t timestamp = page_header->timestamp;
566
1.09k
  uint64_t last_written_event_ts = 0;
567
568
10.3k
  while (ptr < end) {
569
10.2k
    EventHeader event_header;
570
10.2k
    if (!ReadAndAdvance(&ptr, end, &event_header))
571
2
      return FtraceParseStatus::FTRACE_STATUS_ABI_SHORT_EVENT_HEADER;
572
573
10.2k
    timestamp += event_header.time_delta;
574
575
10.2k
    switch (event_header.type_or_length) {
576
489
      case kTypePadding: {
577
        // Left over page padding or discarded event.
578
489
        if (event_header.time_delta == 0) {
579
          // Should never happen: null padding event with unspecified size.
580
          // Only written beyond page_header->size.
581
1
          return FtraceParseStatus::FTRACE_STATUS_ABI_NULL_PADDING;
582
1
        }
583
488
        uint32_t length = 0;
584
488
        if (!ReadAndAdvance<uint32_t>(&ptr, end, &length))
585
6
          return FtraceParseStatus::FTRACE_STATUS_ABI_SHORT_PADDING_LENGTH;
586
        // Length includes itself (4 bytes).
587
482
        if (length < 4)
588
48
          return FtraceParseStatus::FTRACE_STATUS_ABI_INVALID_PADDING_LENGTH;
589
434
        ptr += length - 4;
590
434
        break;
591
482
      }
592
215
      case kTypeTimeExtend: {
593
        // Extend the time delta.
594
215
        uint32_t time_delta_ext = 0;
595
215
        if (!ReadAndAdvance<uint32_t>(&ptr, end, &time_delta_ext))
596
1
          return FtraceParseStatus::FTRACE_STATUS_ABI_SHORT_TIME_EXTEND;
597
214
        timestamp += (static_cast<uint64_t>(time_delta_ext)) << 27;
598
214
        break;
599
215
      }
600
406
      case kTypeTimeStamp: {
601
        // Absolute timestamp. This was historically partially implemented, but
602
        // not written. Kernels 4.17+ reimplemented this record, changing its
603
        // size in the process. We assume the newer layout. Parsed the same as
604
        // kTypeTimeExtend, except that the timestamp is interpreted as an
605
        // absolute, instead of a delta on top of the previous state.
606
406
        uint32_t time_delta_ext = 0;
607
406
        if (!ReadAndAdvance<uint32_t>(&ptr, end, &time_delta_ext))
608
1
          return FtraceParseStatus::FTRACE_STATUS_ABI_SHORT_TIME_STAMP;
609
405
        timestamp = event_header.time_delta +
610
405
                    (static_cast<uint64_t>(time_delta_ext) << 27);
611
405
        break;
612
406
      }
613
      // Data record:
614
9.09k
      default: {
615
        // If type_or_length <=28, the record length is 4x that value.
616
        // If type_or_length == 0, the length of the record is stored in the
617
        // first uint32_t word of the payload.
618
9.09k
        uint32_t event_size = 0;
619
9.09k
        if (event_header.type_or_length == 0) {
620
1.42k
          if (!ReadAndAdvance<uint32_t>(&ptr, end, &event_size))
621
2
            return FtraceParseStatus::FTRACE_STATUS_ABI_SHORT_DATA_LENGTH;
622
          // Size includes itself (4 bytes). However we've seen rare
623
          // contradictions on select Android 4.19+ kernels: the page header
624
          // says there's still valid data, but the rest of the page is full of
625
          // zeroes (which would not decode to a valid event). b/204564312.
626
1.41k
          if (event_size == 0)
627
777
            return FtraceParseStatus::FTRACE_STATUS_ABI_ZERO_DATA_LENGTH;
628
642
          else if (event_size < 4)
629
2
            return FtraceParseStatus::FTRACE_STATUS_ABI_INVALID_DATA_LENGTH;
630
640
          event_size -= 4;
631
7.67k
        } else {
632
7.67k
          event_size = 4 * event_header.type_or_length;
633
7.67k
        }
634
8.31k
        const uint8_t* start = ptr;
635
8.31k
        const uint8_t* next = ptr + event_size;
636
637
8.31k
        if (next > end)
638
73
          return FtraceParseStatus::FTRACE_STATUS_ABI_END_OVERFLOW;
639
640
8.24k
        uint16_t ftrace_event_id = 0;
641
8.24k
        if (!ReadAndAdvance<uint16_t>(&ptr, end, &ftrace_event_id))
642
1
          return FtraceParseStatus::FTRACE_STATUS_ABI_SHORT_EVENT_ID;
643
644
8.23k
        if (ds_config->event_filter.IsEventEnabled(ftrace_event_id)) {
645
          // Special-cased handling of some scheduler events when compact format
646
          // is enabled.
647
7.13k
          bool compact_sched_enabled = ds_config->compact_sched.enabled;
648
7.13k
          const CompactSchedSwitchFormat& sched_switch_format =
649
7.13k
              table->compact_sched_format().sched_switch;
650
7.13k
          const CompactSchedWakingFormat& sched_waking_format =
651
7.13k
              table->compact_sched_format().sched_waking;
652
653
          // Special-cased filtering of ftrace/print events to retain only the
654
          // matching events.
655
7.13k
          bool event_written = true;
656
7.13k
          bool ftrace_print_filter_enabled =
657
7.13k
              ds_config->print_filter.has_value();
658
659
7.13k
          if (compact_sched_enabled &&
660
7.13k
              ftrace_event_id == sched_switch_format.event_id) {
661
0
            if (event_size < sched_switch_format.size)
662
0
              return FtraceParseStatus::FTRACE_STATUS_SHORT_COMPACT_EVENT;
663
664
0
            ParseSchedSwitchCompact(start, timestamp, &sched_switch_format,
665
0
                                    bundler->compact_sched_buf(), metadata);
666
7.13k
          } else if (compact_sched_enabled &&
667
7.13k
                     ftrace_event_id == sched_waking_format.event_id) {
668
0
            if (event_size < sched_waking_format.size)
669
0
              return FtraceParseStatus::FTRACE_STATUS_SHORT_COMPACT_EVENT;
670
671
0
            ParseSchedWakingCompact(start, timestamp, &sched_waking_format,
672
0
                                    bundler->compact_sched_buf(), metadata);
673
7.13k
          } else if (ftrace_print_filter_enabled &&
674
7.13k
                     ftrace_event_id == ds_config->print_filter->event_id()) {
675
0
            if (ds_config->print_filter->IsEventInteresting(start, next)) {
676
0
              protos::pbzero::FtraceEvent* event =
677
0
                  bundler->GetOrCreateBundle()->add_event();
678
0
              event->set_timestamp(timestamp);
679
0
              if (!ParseEvent(ftrace_event_id, start, next, table, ds_config,
680
0
                              event, metadata)) {
681
0
                return FtraceParseStatus::FTRACE_STATUS_INVALID_EVENT;
682
0
              }
683
0
            } else {  // print event did NOT pass the filter
684
0
              event_written = false;
685
0
            }
686
7.13k
          } else {
687
            // Common case: parse all other types of enabled events.
688
7.13k
            protos::pbzero::FtraceEvent* event =
689
7.13k
                bundler->GetOrCreateBundle()->add_event();
690
7.13k
            event->set_timestamp(timestamp);
691
7.13k
            if (!ParseEvent(ftrace_event_id, start, next, table, ds_config,
692
7.13k
                            event, metadata)) {
693
6
              return FtraceParseStatus::FTRACE_STATUS_INVALID_EVENT;
694
6
            }
695
7.13k
          }
696
7.12k
          if (event_written) {
697
7.12k
            last_written_event_ts = timestamp;
698
7.12k
          }
699
7.12k
        }  // IsEventEnabled(id)
700
8.23k
        ptr = next;
701
8.23k
      }              // case (data_record)
702
10.2k
    }                // switch (event_header.type_or_length)
703
10.2k
  }                  // while (ptr < end)
704
705
175
  if (last_written_event_ts)
706
124
    *bundle_end_timestamp = last_written_event_ts;
707
175
  return FtraceParseStatus::FTRACE_STATUS_OK;
708
1.09k
}
709
710
// |start| is the start of the current event.
711
// |end| is the end of the buffer.
712
bool CpuReader::ParseEvent(uint16_t ftrace_event_id,
713
                           const uint8_t* start,
714
                           const uint8_t* end,
715
                           const ProtoTranslationTable* table,
716
                           const FtraceDataSourceConfig* ds_config,
717
                           protozero::Message* message,
718
7.13k
                           FtraceMetadata* metadata) {
719
7.13k
  PERFETTO_DCHECK(start < end);
720
721
  // The event must be enabled and known to reach here.
722
7.13k
  const Event& info = *table->GetEventById(ftrace_event_id);
723
724
7.13k
  if (info.size > static_cast<size_t>(end - start)) {
725
6
    PERFETTO_DLOG("Expected event length is beyond end of buffer.");
726
6
    return false;
727
6
  }
728
729
7.12k
  bool success = true;
730
7.12k
  const Field* common_pid_field = table->common_pid();
731
7.12k
  if (PERFETTO_LIKELY(common_pid_field))
732
7.12k
    success &=
733
7.12k
        ParseField(*common_pid_field, start, end, table, message, metadata);
734
735
7.12k
  protozero::Message* nested =
736
7.12k
      message->BeginNestedMessage<protozero::Message>(info.proto_field_id);
737
738
  // Parse generic (not known at compile time) event.
739
7.12k
  if (PERFETTO_UNLIKELY(info.proto_field_id ==
740
7.12k
                        protos::pbzero::FtraceEvent::kGenericFieldNumber)) {
741
0
    nested->AppendString(GenericFtraceEvent::kEventNameFieldNumber, info.name);
742
0
    for (const Field& field : info.fields) {
743
0
      auto* generic_field = nested->BeginNestedMessage<protozero::Message>(
744
0
          GenericFtraceEvent::kFieldFieldNumber);
745
0
      generic_field->AppendString(GenericFtraceEvent::Field::kNameFieldNumber,
746
0
                                  field.ftrace_name);
747
0
      success &= ParseField(field, start, end, table, generic_field, metadata);
748
0
    }
749
7.12k
  } else if (PERFETTO_UNLIKELY(
750
7.12k
                 info.proto_field_id ==
751
7.12k
                 protos::pbzero::FtraceEvent::kSysEnterFieldNumber)) {
752
0
    success &= ParseSysEnter(info, start, end, nested, metadata);
753
7.12k
  } else if (PERFETTO_UNLIKELY(
754
7.12k
                 info.proto_field_id ==
755
7.12k
                 protos::pbzero::FtraceEvent::kSysExitFieldNumber)) {
756
0
    success &= ParseSysExit(info, start, end, ds_config, nested, metadata);
757
7.12k
  } else if (PERFETTO_UNLIKELY(
758
7.12k
                 info.proto_field_id ==
759
7.12k
                 protos::pbzero::FtraceEvent::kKprobeEventFieldNumber)) {
760
0
    KprobeEvent::KprobeType* elem = ds_config->kprobes.Find(ftrace_event_id);
761
0
    nested->AppendString(KprobeEvent::kNameFieldNumber, info.name);
762
0
    if (elem) {
763
0
      nested->AppendVarInt(KprobeEvent::kTypeFieldNumber, *elem);
764
0
    }
765
7.12k
  } else {  // Parse all other events.
766
25.3k
    for (const Field& field : info.fields) {
767
25.3k
      success &= ParseField(field, start, end, table, nested, metadata);
768
25.3k
    }
769
7.12k
  }
770
771
7.12k
  if (PERFETTO_UNLIKELY(info.proto_field_id ==
772
7.12k
                        protos::pbzero::FtraceEvent::kTaskRenameFieldNumber)) {
773
    // For task renames, we want to store that the pid was renamed. We use the
774
    // common pid to reduce code complexity as in all the cases we care about,
775
    // the common pid is the same as the renamed pid (the pid inside the event).
776
0
    PERFETTO_DCHECK(metadata->last_seen_common_pid);
777
0
    metadata->AddRenamePid(metadata->last_seen_common_pid);
778
0
  }
779
780
  // This finalizes |nested| and |proto_field| automatically.
781
7.12k
  message->Finalize();
782
7.12k
  metadata->FinishEvent();
783
7.12k
  return success;
784
7.13k
}
785
786
// Caller must guarantee that the field fits in the range,
787
// explicitly: start + field.ftrace_offset + field.ftrace_size <= end
788
// The only exception is fields with strategy = kCStringToString
789
// where the total size isn't known up front. In this case ParseField
790
// will check the string terminates in the bounds and won't read past |end|.
791
bool CpuReader::ParseField(const Field& field,
792
                           const uint8_t* start,
793
                           const uint8_t* end,
794
                           const ProtoTranslationTable* table,
795
                           protozero::Message* message,
796
32.4k
                           FtraceMetadata* metadata) {
797
32.4k
  PERFETTO_DCHECK(start + field.ftrace_offset + field.ftrace_size <= end);
798
32.4k
  const uint8_t* field_start = start + field.ftrace_offset;
799
32.4k
  uint32_t field_id = field.proto_field_id;
800
801
32.4k
  switch (field.strategy) {
802
0
    case kUint8ToUint32:
803
0
    case kUint8ToUint64:
804
0
      ReadIntoVarInt<uint8_t>(field_start, field_id, message);
805
0
      return true;
806
0
    case kUint16ToUint32:
807
0
    case kUint16ToUint64:
808
0
      ReadIntoVarInt<uint16_t>(field_start, field_id, message);
809
0
      return true;
810
0
    case kUint32ToUint32:
811
0
    case kUint32ToUint64:
812
0
      ReadIntoVarInt<uint32_t>(field_start, field_id, message);
813
0
      return true;
814
0
    case kUint64ToUint64:
815
0
      ReadIntoVarInt<uint64_t>(field_start, field_id, message);
816
0
      return true;
817
0
    case kInt8ToInt32:
818
0
    case kInt8ToInt64:
819
0
      ReadIntoVarInt<int8_t>(field_start, field_id, message);
820
0
      return true;
821
0
    case kInt16ToInt32:
822
0
    case kInt16ToInt64:
823
0
      ReadIntoVarInt<int16_t>(field_start, field_id, message);
824
0
      return true;
825
6.06k
    case kInt32ToInt32:
826
6.06k
    case kInt32ToInt64:
827
6.06k
      ReadIntoVarInt<int32_t>(field_start, field_id, message);
828
6.06k
      return true;
829
3.03k
    case kInt64ToInt64:
830
3.03k
      ReadIntoVarInt<int64_t>(field_start, field_id, message);
831
3.03k
      return true;
832
6.06k
    case kFixedCStringToString:
833
      // TODO(hjd): Kernel-dive to check this how size:0 char fields work.
834
6.06k
      ReadIntoString(field_start, field.ftrace_size, field_id, message);
835
6.06k
      return true;
836
4.09k
    case kCStringToString:
837
      // TODO(hjd): Kernel-dive to check this how size:0 char fields work.
838
4.09k
      ReadIntoString(field_start, static_cast<size_t>(end - field_start),
839
4.09k
                     field_id, message);
840
4.09k
      return true;
841
0
    case kStringPtrToString: {
842
0
      uint64_t n = 0;
843
      // The ftrace field may be 8 or 4 bytes and we need to copy it into the
844
      // bottom of n. In the unlikely case where the field is >8 bytes we
845
      // should avoid making things worse by corrupting the stack but we
846
      // don't need to handle it correctly.
847
0
      size_t size = std::min<size_t>(field.ftrace_size, sizeof(n));
848
0
      memcpy(base::AssumeLittleEndian(&n),
849
0
             reinterpret_cast<const void*>(field_start), size);
850
      // Look up the adddress in the printk format map and write it into the
851
      // proto.
852
0
      base::StringView name = table->LookupTraceString(n);
853
0
      message->AppendBytes(field_id, name.begin(), name.size());
854
0
      return true;
855
6.06k
    }
856
0
    case kDataLocToString:
857
0
      return ReadDataLoc(start, field_start, end, field, message);
858
0
    case kBoolToUint32:
859
0
    case kBoolToUint64:
860
0
      ReadIntoVarInt<uint8_t>(field_start, field_id, message);
861
0
      return true;
862
0
    case kInode32ToUint64:
863
0
      ReadInode<uint32_t>(field_start, field_id, message, metadata);
864
0
      return true;
865
0
    case kInode64ToUint64:
866
0
      ReadInode<uint64_t>(field_start, field_id, message, metadata);
867
0
      return true;
868
6.06k
    case kPid32ToInt32:
869
6.06k
    case kPid32ToInt64:
870
6.06k
      ReadPid(field_start, field_id, message, metadata);
871
6.06k
      return true;
872
7.12k
    case kCommonPid32ToInt32:
873
7.12k
    case kCommonPid32ToInt64:
874
7.12k
      ReadCommonPid(field_start, field_id, message, metadata);
875
7.12k
      return true;
876
0
    case kDevId32ToUint64:
877
0
      ReadDevId<uint32_t>(field_start, field_id, message, metadata);
878
0
      return true;
879
0
    case kDevId64ToUint64:
880
0
      ReadDevId<uint64_t>(field_start, field_id, message, metadata);
881
0
      return true;
882
0
    case kFtraceSymAddr32ToUint64:
883
0
      ReadSymbolAddr<uint32_t>(field_start, field_id, message, metadata);
884
0
      return true;
885
0
    case kFtraceSymAddr64ToUint64:
886
0
      ReadSymbolAddr<uint64_t>(field_start, field_id, message, metadata);
887
0
      return true;
888
0
    case kInvalidTranslationStrategy:
889
0
      break;
890
32.4k
  }
891
  // Shouldn't reach this since we only attempt to parse fields that were
892
  // validated by the proto translation table earlier.
893
0
  return false;
894
32.4k
}
895
896
bool CpuReader::ParseSysEnter(const Event& info,
897
                              const uint8_t* start,
898
                              const uint8_t* end,
899
                              protozero::Message* message,
900
0
                              FtraceMetadata* /* metadata */) {
901
0
  if (info.fields.size() != 2) {
902
0
    PERFETTO_DLOG("Unexpected number of fields for sys_enter");
903
0
    return false;
904
0
  }
905
0
  const auto& id_field = info.fields[0];
906
0
  const auto& args_field = info.fields[1];
907
0
  if (start + id_field.ftrace_size + args_field.ftrace_size > end) {
908
0
    return false;
909
0
  }
910
  // field:long id;
911
0
  if (id_field.ftrace_type != kFtraceInt32 &&
912
0
      id_field.ftrace_type != kFtraceInt64) {
913
0
    return false;
914
0
  }
915
0
  const int64_t syscall_id = ReadSignedFtraceValue(
916
0
      start + id_field.ftrace_offset, id_field.ftrace_type);
917
0
  message->AppendVarInt(id_field.proto_field_id, syscall_id);
918
  // field:unsigned long args[6];
919
  // proto_translation_table will only allow exactly 6-element array, so we can
920
  // make the same hard assumption here.
921
0
  constexpr uint16_t arg_count = 6;
922
0
  size_t element_size = 0;
923
0
  if (args_field.ftrace_type == kFtraceUint32) {
924
0
    element_size = 4u;
925
0
  } else if (args_field.ftrace_type == kFtraceUint64) {
926
0
    element_size = 8u;
927
0
  } else {
928
0
    return false;
929
0
  }
930
0
  for (uint16_t i = 0; i < arg_count; ++i) {
931
0
    const uint8_t* element_ptr =
932
0
        start + args_field.ftrace_offset + i * element_size;
933
0
    uint64_t arg_value = 0;
934
0
    if (element_size == 8) {
935
0
      arg_value = ReadValue<uint64_t>(element_ptr);
936
0
    } else {
937
0
      arg_value = ReadValue<uint32_t>(element_ptr);
938
0
    }
939
0
    message->AppendVarInt(args_field.proto_field_id, arg_value);
940
0
  }
941
0
  return true;
942
0
}
943
944
bool CpuReader::ParseSysExit(const Event& info,
945
                             const uint8_t* start,
946
                             const uint8_t* end,
947
                             const FtraceDataSourceConfig* ds_config,
948
                             protozero::Message* message,
949
0
                             FtraceMetadata* metadata) {
950
0
  if (info.fields.size() != 2) {
951
0
    PERFETTO_DLOG("Unexpected number of fields for sys_exit");
952
0
    return false;
953
0
  }
954
0
  const auto& id_field = info.fields[0];
955
0
  const auto& ret_field = info.fields[1];
956
0
  if (start + id_field.ftrace_size + ret_field.ftrace_size > end) {
957
0
    return false;
958
0
  }
959
  //    field:long id;
960
0
  if (id_field.ftrace_type != kFtraceInt32 &&
961
0
      id_field.ftrace_type != kFtraceInt64) {
962
0
    return false;
963
0
  }
964
0
  const int64_t syscall_id = ReadSignedFtraceValue(
965
0
      start + id_field.ftrace_offset, id_field.ftrace_type);
966
0
  message->AppendVarInt(id_field.proto_field_id, syscall_id);
967
  //    field:long ret;
968
0
  if (ret_field.ftrace_type != kFtraceInt32 &&
969
0
      ret_field.ftrace_type != kFtraceInt64) {
970
0
    return false;
971
0
  }
972
0
  const int64_t syscall_ret = ReadSignedFtraceValue(
973
0
      start + ret_field.ftrace_offset, ret_field.ftrace_type);
974
0
  message->AppendVarInt(ret_field.proto_field_id, syscall_ret);
975
  // for any syscalls which return a new filedescriptor
976
  // we mark the fd as potential candidate for scraping
977
  // if the call succeeded and is within fd bounds
978
0
  if (ds_config->syscalls_returning_fd.count(syscall_id) && syscall_ret >= 0 &&
979
0
      syscall_ret <= std::numeric_limits<int>::max()) {
980
0
    const auto pid = metadata->last_seen_common_pid;
981
0
    const auto syscall_ret_u = static_cast<uint64_t>(syscall_ret);
982
0
    metadata->fds.insert(std::make_pair(pid, syscall_ret_u));
983
0
  }
984
0
  return true;
985
0
}
986
987
// Parse a sched_switch event according to pre-validated format, and buffer the
988
// individual fields in the current compact batch. See the code populating
989
// |CompactSchedSwitchFormat| for the assumptions made around the format, which
990
// this code is closely tied to.
991
// static
992
void CpuReader::ParseSchedSwitchCompact(const uint8_t* start,
993
                                        uint64_t timestamp,
994
                                        const CompactSchedSwitchFormat* format,
995
                                        CompactSchedBuffer* compact_buf,
996
0
                                        FtraceMetadata* metadata) {
997
0
  compact_buf->sched_switch().AppendTimestamp(timestamp);
998
999
0
  int32_t next_pid = ReadValue<int32_t>(start + format->next_pid_offset);
1000
0
  compact_buf->sched_switch().next_pid().Append(next_pid);
1001
0
  metadata->AddPid(next_pid);
1002
1003
0
  int32_t next_prio = ReadValue<int32_t>(start + format->next_prio_offset);
1004
0
  compact_buf->sched_switch().next_prio().Append(next_prio);
1005
1006
  // Varint encoding of int32 and int64 is the same, so treat the value as
1007
  // int64 after reading.
1008
0
  int64_t prev_state = ReadSignedFtraceValue(start + format->prev_state_offset,
1009
0
                                             format->prev_state_type);
1010
0
  compact_buf->sched_switch().prev_state().Append(prev_state);
1011
1012
  // next_comm
1013
0
  const char* comm_ptr =
1014
0
      reinterpret_cast<const char*>(start + format->next_comm_offset);
1015
0
  size_t iid = compact_buf->interner().InternComm(comm_ptr);
1016
0
  compact_buf->sched_switch().next_comm_index().Append(iid);
1017
0
}
1018
1019
// static
1020
void CpuReader::ParseSchedWakingCompact(const uint8_t* start,
1021
                                        uint64_t timestamp,
1022
                                        const CompactSchedWakingFormat* format,
1023
                                        CompactSchedBuffer* compact_buf,
1024
0
                                        FtraceMetadata* metadata) {
1025
0
  compact_buf->sched_waking().AppendTimestamp(timestamp);
1026
1027
0
  int32_t pid = ReadValue<int32_t>(start + format->pid_offset);
1028
0
  compact_buf->sched_waking().pid().Append(pid);
1029
0
  metadata->AddPid(pid);
1030
1031
0
  int32_t target_cpu = ReadValue<int32_t>(start + format->target_cpu_offset);
1032
0
  compact_buf->sched_waking().target_cpu().Append(target_cpu);
1033
1034
0
  int32_t prio = ReadValue<int32_t>(start + format->prio_offset);
1035
0
  compact_buf->sched_waking().prio().Append(prio);
1036
1037
  // comm
1038
0
  const char* comm_ptr =
1039
0
      reinterpret_cast<const char*>(start + format->comm_offset);
1040
0
  size_t iid = compact_buf->interner().InternComm(comm_ptr);
1041
0
  compact_buf->sched_waking().comm_index().Append(iid);
1042
1043
0
  uint32_t common_flags =
1044
0
      ReadValue<uint8_t>(start + format->common_flags_offset);
1045
0
  compact_buf->sched_waking().common_flags().Append(common_flags);
1046
0
}
1047
1048
}  // namespace perfetto