/src/perfetto/src/tracing/service/tracing_service_impl.cc
Line | Count | Source |
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/tracing/service/tracing_service_impl.h" |
18 | | |
19 | | #include <limits.h> |
20 | | #include <string.h> |
21 | | |
22 | | #include <algorithm> |
23 | | #include <cinttypes> |
24 | | #include <cstdint> |
25 | | #include <limits> |
26 | | #include <optional> |
27 | | #include <string> |
28 | | #include <unordered_set> |
29 | | |
30 | | #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \ |
31 | | !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL) |
32 | | #include <sys/uio.h> |
33 | | #include <sys/utsname.h> |
34 | | #include <unistd.h> |
35 | | #endif |
36 | | |
37 | | #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \ |
38 | | PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) |
39 | | #include "src/android_internal/lazy_library_loader.h" // nogncheck |
40 | | #include "src/android_internal/tracing_service_proxy.h" // nogncheck |
41 | | #endif |
42 | | |
43 | | #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ |
44 | | PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ |
45 | | PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) |
46 | | #define PERFETTO_HAS_CHMOD |
47 | | #include <sys/stat.h> |
48 | | #endif |
49 | | |
50 | | #include "perfetto/base/build_config.h" |
51 | | #include "perfetto/base/status.h" |
52 | | #include "perfetto/base/task_runner.h" |
53 | | #include "perfetto/ext/base/android_utils.h" |
54 | | #include "perfetto/ext/base/clock_snapshots.h" |
55 | | #include "perfetto/ext/base/file_utils.h" |
56 | | #include "perfetto/ext/base/metatrace.h" |
57 | | #include "perfetto/ext/base/string_utils.h" |
58 | | #include "perfetto/ext/base/string_view.h" |
59 | | #include "perfetto/ext/base/sys_types.h" |
60 | | #include "perfetto/ext/base/utils.h" |
61 | | #include "perfetto/ext/base/uuid.h" |
62 | | #include "perfetto/ext/base/version.h" |
63 | | #include "perfetto/ext/base/watchdog.h" |
64 | | #include "perfetto/ext/tracing/core/basic_types.h" |
65 | | #include "perfetto/ext/tracing/core/client_identity.h" |
66 | | #include "perfetto/ext/tracing/core/consumer.h" |
67 | | #include "perfetto/ext/tracing/core/observable_events.h" |
68 | | #include "perfetto/ext/tracing/core/producer.h" |
69 | | #include "perfetto/ext/tracing/core/shared_memory.h" |
70 | | #include "perfetto/ext/tracing/core/shared_memory_abi.h" |
71 | | #include "perfetto/ext/tracing/core/trace_packet.h" |
72 | | #include "perfetto/ext/tracing/core/trace_writer.h" |
73 | | #include "perfetto/protozero/scattered_heap_buffer.h" |
74 | | #include "perfetto/protozero/static_buffer.h" |
75 | | #include "perfetto/tracing/core/data_source_descriptor.h" |
76 | | #include "perfetto/tracing/core/tracing_service_capabilities.h" |
77 | | #include "perfetto/tracing/core/tracing_service_state.h" |
78 | | #include "src/android_stats/statsd_logging_helper.h" |
79 | | #include "src/protozero/filtering/message_filter.h" |
80 | | #include "src/protozero/filtering/string_filter.h" |
81 | | #include "src/tracing/core/shared_memory_arbiter_impl.h" |
82 | | #include "src/tracing/service/packet_stream_validator.h" |
83 | | #include "src/tracing/service/trace_buffer.h" |
84 | | |
85 | | #include "protos/perfetto/common/builtin_clock.gen.h" |
86 | | #include "protos/perfetto/common/builtin_clock.pbzero.h" |
87 | | #include "protos/perfetto/common/trace_stats.pbzero.h" |
88 | | #include "protos/perfetto/config/trace_config.pbzero.h" |
89 | | #include "protos/perfetto/trace/clock_snapshot.pbzero.h" |
90 | | #include "protos/perfetto/trace/perfetto/tracing_service_event.pbzero.h" |
91 | | #include "protos/perfetto/trace/remote_clock_sync.pbzero.h" |
92 | | #include "protos/perfetto/trace/system_info.pbzero.h" |
93 | | #include "protos/perfetto/trace/trace_packet.pbzero.h" |
94 | | #include "protos/perfetto/trace/trace_uuid.pbzero.h" |
95 | | #include "protos/perfetto/trace/trigger.pbzero.h" |
96 | | |
97 | | // General note: this class must assume that Producers are malicious and will |
98 | | // try to crash / exploit this class. We can trust pointers because they come |
99 | | // from the IPC layer, but we should never assume that that the producer calls |
100 | | // come in the right order or their arguments are sane / within bounds. |
101 | | |
102 | | // This is a macro because we want the call-site line number for the ELOG. |
103 | | #define PERFETTO_SVC_ERR(...) \ |
104 | 0 | (PERFETTO_ELOG(__VA_ARGS__), ::perfetto::base::ErrStatus(__VA_ARGS__)) |
105 | | |
106 | | namespace perfetto { |
107 | | |
108 | | namespace { |
109 | | constexpr int kMaxBuffersPerConsumer = 128; |
110 | | constexpr uint32_t kDefaultSnapshotsIntervalMs = 10 * 1000; |
111 | | constexpr int kDefaultWriteIntoFilePeriodMs = 5000; |
112 | | constexpr int kMinWriteIntoFilePeriodMs = 100; |
113 | | constexpr uint32_t kAllDataSourceStartedTimeout = 20000; |
114 | | constexpr int kMaxConcurrentTracingSessions = 15; |
115 | | constexpr int kMaxConcurrentTracingSessionsPerUid = 5; |
116 | | constexpr int kMaxConcurrentTracingSessionsForStatsdUid = 10; |
117 | | constexpr int64_t kMinSecondsBetweenTracesGuardrail = 5 * 60; |
118 | | |
119 | | constexpr uint32_t kMillisPerHour = 3600000; |
120 | | constexpr uint32_t kMillisPerDay = kMillisPerHour * 24; |
121 | | constexpr uint32_t kMaxTracingDurationMillis = 7 * 24 * kMillisPerHour; |
122 | | |
123 | | // These apply only if enable_extra_guardrails is true. |
124 | | constexpr uint32_t kGuardrailsMaxTracingBufferSizeKb = 128 * 1024; |
125 | | constexpr uint32_t kGuardrailsMaxTracingDurationMillis = 24 * kMillisPerHour; |
126 | | |
127 | | constexpr size_t kMaxLifecycleEventsListedDataSources = 32; |
128 | | |
129 | | #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || PERFETTO_BUILDFLAG(PERFETTO_OS_NACL) |
130 | | struct iovec { |
131 | | void* iov_base; // Address |
132 | | size_t iov_len; // Block size |
133 | | }; |
134 | | |
135 | | // Simple implementation of writev. Note that this does not give the atomicity |
136 | | // guarantees of a real writev, but we don't depend on these (we aren't writing |
137 | | // to the same file from another thread). |
138 | | ssize_t writev(int fd, const struct iovec* iov, int iovcnt) { |
139 | | ssize_t total_size = 0; |
140 | | for (int i = 0; i < iovcnt; ++i) { |
141 | | ssize_t current_size = base::WriteAll(fd, iov[i].iov_base, iov[i].iov_len); |
142 | | if (current_size != static_cast<ssize_t>(iov[i].iov_len)) |
143 | | return -1; |
144 | | total_size += current_size; |
145 | | } |
146 | | return total_size; |
147 | | } |
148 | | |
149 | | #define IOV_MAX 1024 // Linux compatible limit. |
150 | | |
151 | | #elif PERFETTO_BUILDFLAG(PERFETTO_OS_QNX) |
152 | | #define IOV_MAX 1024 // Linux compatible limit. |
153 | | #endif |
154 | | |
155 | | // Partially encodes a CommitDataRequest in an int32 for the purposes of |
156 | | // metatracing. Note that it encodes only the bottom 10 bits of the producer id |
157 | | // (which is technically 16 bits wide). |
158 | | // |
159 | | // Format (by bit range): |
160 | | // [ 31 ][ 30 ][ 29:20 ][ 19:10 ][ 9:0] |
161 | | // [unused][has flush id][num chunks to patch][num chunks to move][producer id] |
162 | | int32_t EncodeCommitDataRequest(ProducerID producer_id, |
163 | 0 | const CommitDataRequest& req_untrusted) { |
164 | 0 | uint32_t cmov = static_cast<uint32_t>(req_untrusted.chunks_to_move_size()); |
165 | 0 | uint32_t cpatch = static_cast<uint32_t>(req_untrusted.chunks_to_patch_size()); |
166 | 0 | uint32_t has_flush_id = req_untrusted.flush_request_id() != 0; |
167 | |
|
168 | 0 | uint32_t mask = (1 << 10) - 1; |
169 | 0 | uint32_t acc = 0; |
170 | 0 | acc |= has_flush_id << 30; |
171 | 0 | acc |= (cpatch & mask) << 20; |
172 | 0 | acc |= (cmov & mask) << 10; |
173 | 0 | acc |= (producer_id & mask); |
174 | 0 | return static_cast<int32_t>(acc); |
175 | 0 | } |
176 | | |
177 | | void SerializeAndAppendPacket(std::vector<TracePacket>* packets, |
178 | 2.70k | std::vector<uint8_t> packet) { |
179 | 2.70k | Slice slice = Slice::Allocate(packet.size()); |
180 | 2.70k | memcpy(slice.own_data(), packet.data(), packet.size()); |
181 | 2.70k | packets->emplace_back(); |
182 | 2.70k | packets->back().AddSlice(std::move(slice)); |
183 | 2.70k | } |
184 | | |
185 | | std::tuple<size_t /*shm_size*/, size_t /*page_size*/> EnsureValidShmSizes( |
186 | | size_t shm_size, |
187 | 300 | size_t page_size) { |
188 | | // Theoretically the max page size supported by the ABI is 64KB. |
189 | | // However, the current implementation of TraceBuffer (the non-shared |
190 | | // userspace buffer where the service copies data) supports at most |
191 | | // 32K. Setting 64K "works" from the producer<>consumer viewpoint |
192 | | // but then causes the data to be discarded when copying it into |
193 | | // TraceBuffer. |
194 | 300 | constexpr size_t kMaxPageSize = 32 * 1024; |
195 | 300 | static_assert(kMaxPageSize <= SharedMemoryABI::kMaxPageSize, ""); |
196 | | |
197 | 300 | if (page_size == 0) |
198 | 300 | page_size = TracingServiceImpl::kDefaultShmPageSize; |
199 | 300 | if (shm_size == 0) |
200 | 300 | shm_size = TracingServiceImpl::kDefaultShmSize; |
201 | | |
202 | 300 | page_size = std::min<size_t>(page_size, kMaxPageSize); |
203 | 300 | shm_size = std::min<size_t>(shm_size, TracingServiceImpl::kMaxShmSize); |
204 | | |
205 | | // The tracing page size has to be multiple of 4K. On some systems (e.g. Mac |
206 | | // on Arm64) the system page size can be larger (e.g., 16K). That doesn't |
207 | | // matter here, because the tracing page size is just a logical partitioning |
208 | | // and does not have any dependencies on kernel mm syscalls (read: it's fine |
209 | | // to have trace page sizes of 4K on a system where the kernel page size is |
210 | | // 16K). |
211 | 300 | bool page_size_is_valid = page_size >= SharedMemoryABI::kMinPageSize; |
212 | 300 | page_size_is_valid &= page_size % SharedMemoryABI::kMinPageSize == 0; |
213 | | |
214 | | // Only allow power of two numbers of pages, i.e. 1, 2, 4, 8 pages. |
215 | 300 | size_t num_pages = page_size / SharedMemoryABI::kMinPageSize; |
216 | 300 | page_size_is_valid &= (num_pages & (num_pages - 1)) == 0; |
217 | | |
218 | 300 | if (!page_size_is_valid || shm_size < page_size || |
219 | 300 | shm_size % page_size != 0) { |
220 | 0 | return std::make_tuple(TracingServiceImpl::kDefaultShmSize, |
221 | 0 | TracingServiceImpl::kDefaultShmPageSize); |
222 | 0 | } |
223 | 300 | return std::make_tuple(shm_size, page_size); |
224 | 300 | } |
225 | | |
226 | | bool NameMatchesFilter(const std::string& name, |
227 | | const std::vector<std::string>& name_filter, |
228 | 300 | const std::vector<std::string>& name_regex_filter) { |
229 | 300 | bool filter_is_set = !name_filter.empty() || !name_regex_filter.empty(); |
230 | 300 | if (!filter_is_set) |
231 | 300 | return true; |
232 | 0 | bool filter_matches = std::find(name_filter.begin(), name_filter.end(), |
233 | 0 | name) != name_filter.end(); |
234 | 0 | bool filter_regex_matches = |
235 | 0 | std::find_if(name_regex_filter.begin(), name_regex_filter.end(), |
236 | 0 | [&](const std::string& regex) { |
237 | 0 | return std::regex_match( |
238 | 0 | name, std::regex(regex, std::regex::extended)); |
239 | 0 | }) != name_regex_filter.end(); |
240 | 0 | return filter_matches || filter_regex_matches; |
241 | 300 | } |
242 | | |
243 | | // Used when TraceConfig.write_into_file == true and output_path is not empty. |
244 | 0 | base::ScopedFile CreateTraceFile(const std::string& path, bool overwrite) { |
245 | | #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \ |
246 | | PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) |
247 | | // This is NOT trying to preserve any security property, SELinux does that. |
248 | | // It just improves the actionability of the error when people try to save the |
249 | | // trace in a location that is not SELinux-allowed (a generic "permission |
250 | | // denied" vs "don't put it here, put it there"). |
251 | | // These are the only SELinux approved dir for trace files that are created |
252 | | // directly by traced. |
253 | | static const char* kTraceDirBasePath = "/data/misc/perfetto-traces/"; |
254 | | if (!base::StartsWith(path, kTraceDirBasePath)) { |
255 | | PERFETTO_ELOG("Invalid output_path %s. On Android it must be within %s.", |
256 | | path.c_str(), kTraceDirBasePath); |
257 | | return base::ScopedFile(); |
258 | | } |
259 | | #endif |
260 | | // O_CREAT | O_EXCL will fail if the file exists already. |
261 | 0 | const int flags = O_RDWR | O_CREAT | (overwrite ? O_TRUNC : O_EXCL); |
262 | 0 | auto fd = base::OpenFile(path, flags, 0600); |
263 | 0 | if (fd) { |
264 | 0 | #if defined(PERFETTO_HAS_CHMOD) |
265 | | // Passing 0644 directly above won't work because of umask. |
266 | 0 | PERFETTO_CHECK(fchmod(*fd, 0644) == 0); |
267 | 0 | #endif |
268 | 0 | } else { |
269 | 0 | PERFETTO_PLOG("Failed to create %s", path.c_str()); |
270 | 0 | } |
271 | 0 | return fd; |
272 | 0 | } |
273 | | |
274 | 1.20k | bool ShouldLogEvent(const TraceConfig& cfg) { |
275 | 1.20k | switch (cfg.statsd_logging()) { |
276 | 0 | case TraceConfig::STATSD_LOGGING_ENABLED: |
277 | 0 | return true; |
278 | 0 | case TraceConfig::STATSD_LOGGING_DISABLED: |
279 | 0 | return false; |
280 | 1.20k | case TraceConfig::STATSD_LOGGING_UNSPECIFIED: |
281 | 1.20k | break; |
282 | 1.20k | } |
283 | | // For backward compatibility with older versions of perfetto_cmd. |
284 | 1.20k | return cfg.enable_extra_guardrails(); |
285 | 1.20k | } |
286 | | |
287 | | // Appends `data` (which has `size` bytes), to `*packet`. Splits the data in |
288 | | // slices no larger than `max_slice_size`. |
289 | | void AppendOwnedSlicesToPacket(std::unique_ptr<uint8_t[]> data, |
290 | | size_t size, |
291 | | size_t max_slice_size, |
292 | 0 | perfetto::TracePacket* packet) { |
293 | 0 | if (size <= max_slice_size) { |
294 | 0 | packet->AddSlice(Slice::TakeOwnership(std::move(data), size)); |
295 | 0 | return; |
296 | 0 | } |
297 | 0 | uint8_t* src_ptr = data.get(); |
298 | 0 | for (size_t size_left = size; size_left > 0;) { |
299 | 0 | const size_t slice_size = std::min(size_left, max_slice_size); |
300 | |
|
301 | 0 | Slice slice = Slice::Allocate(slice_size); |
302 | 0 | memcpy(slice.own_data(), src_ptr, slice_size); |
303 | 0 | packet->AddSlice(std::move(slice)); |
304 | |
|
305 | 0 | src_ptr += slice_size; |
306 | 0 | size_left -= slice_size; |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | | using TraceFilter = protos::gen::TraceConfig::TraceFilter; |
311 | | std::optional<protozero::StringFilter::Policy> ConvertPolicy( |
312 | 0 | TraceFilter::StringFilterPolicy policy) { |
313 | 0 | switch (policy) { |
314 | 0 | case TraceFilter::SFP_UNSPECIFIED: |
315 | 0 | return std::nullopt; |
316 | 0 | case TraceFilter::SFP_MATCH_REDACT_GROUPS: |
317 | 0 | return protozero::StringFilter::Policy::kMatchRedactGroups; |
318 | 0 | case TraceFilter::SFP_ATRACE_MATCH_REDACT_GROUPS: |
319 | 0 | return protozero::StringFilter::Policy::kAtraceMatchRedactGroups; |
320 | 0 | case TraceFilter::SFP_MATCH_BREAK: |
321 | 0 | return protozero::StringFilter::Policy::kMatchBreak; |
322 | 0 | case TraceFilter::SFP_ATRACE_MATCH_BREAK: |
323 | 0 | return protozero::StringFilter::Policy::kAtraceMatchBreak; |
324 | 0 | case TraceFilter::SFP_ATRACE_REPEATED_SEARCH_REDACT_GROUPS: |
325 | 0 | return protozero::StringFilter::Policy::kAtraceRepeatedSearchRedactGroups; |
326 | 0 | } |
327 | 0 | return std::nullopt; |
328 | 0 | } |
329 | | |
330 | | } // namespace |
331 | | |
332 | | // static |
333 | | std::unique_ptr<TracingService> TracingService::CreateInstance( |
334 | | std::unique_ptr<SharedMemory::Factory> shm_factory, |
335 | | base::TaskRunner* task_runner, |
336 | 311 | InitOpts init_opts) { |
337 | 311 | tracing_service::Dependencies deps; |
338 | 311 | deps.clock = std::make_unique<tracing_service::ClockImpl>(); |
339 | 311 | uint32_t seed = static_cast<uint32_t>(deps.clock->GetWallTimeMs().count()); |
340 | 311 | deps.random = std::make_unique<tracing_service::RandomImpl>(seed); |
341 | 311 | return std::unique_ptr<TracingService>(new TracingServiceImpl( |
342 | 311 | std::move(shm_factory), task_runner, std::move(deps), init_opts)); |
343 | 311 | } |
344 | | |
345 | | TracingServiceImpl::TracingServiceImpl( |
346 | | std::unique_ptr<SharedMemory::Factory> shm_factory, |
347 | | base::TaskRunner* task_runner, |
348 | | tracing_service::Dependencies deps, |
349 | | InitOpts init_opts) |
350 | 311 | : clock_(std::move(deps.clock)), |
351 | 311 | random_(std::move(deps.random)), |
352 | 311 | init_opts_(init_opts), |
353 | 311 | shm_factory_(std::move(shm_factory)), |
354 | 311 | uid_(base::GetCurrentUserId()), |
355 | 311 | buffer_ids_(kMaxTraceBufferID), |
356 | 311 | weak_runner_(task_runner) { |
357 | 311 | PERFETTO_DCHECK(task_runner); |
358 | 311 | } |
359 | | |
360 | 311 | TracingServiceImpl::~TracingServiceImpl() { |
361 | | // TODO(fmayer): handle teardown of all Producer. |
362 | 311 | } |
363 | | |
364 | | std::unique_ptr<TracingService::ProducerEndpoint> |
365 | | TracingServiceImpl::ConnectProducer(Producer* producer, |
366 | | const ClientIdentity& client_identity, |
367 | | const std::string& producer_name, |
368 | | size_t shared_memory_size_hint_bytes, |
369 | | bool in_process, |
370 | | ProducerSMBScrapingMode smb_scraping_mode, |
371 | | size_t shared_memory_page_size_hint_bytes, |
372 | | std::unique_ptr<SharedMemory> shm, |
373 | 300 | const std::string& sdk_version) { |
374 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
375 | | |
376 | 300 | auto uid = client_identity.uid(); |
377 | 300 | if (lockdown_mode_ && uid != base::GetCurrentUserId()) { |
378 | 0 | PERFETTO_DLOG("Lockdown mode. Rejecting producer with UID %ld", |
379 | 0 | static_cast<unsigned long>(uid)); |
380 | 0 | return nullptr; |
381 | 0 | } |
382 | | |
383 | 300 | if (producers_.size() >= kMaxProducerID) { |
384 | 0 | PERFETTO_DFATAL("Too many producers."); |
385 | 0 | return nullptr; |
386 | 0 | } |
387 | 300 | const ProducerID id = GetNextProducerID(); |
388 | 300 | PERFETTO_DLOG("Producer %" PRIu16 " connected, uid=%d", id, |
389 | 300 | static_cast<int>(uid)); |
390 | 300 | bool smb_scraping_enabled = smb_scraping_enabled_; |
391 | 300 | switch (smb_scraping_mode) { |
392 | 300 | case ProducerSMBScrapingMode::kDefault: |
393 | 300 | break; |
394 | 0 | case ProducerSMBScrapingMode::kEnabled: |
395 | 0 | smb_scraping_enabled = true; |
396 | 0 | break; |
397 | 0 | case ProducerSMBScrapingMode::kDisabled: |
398 | 0 | smb_scraping_enabled = false; |
399 | 0 | break; |
400 | 300 | } |
401 | | |
402 | 300 | std::unique_ptr<ProducerEndpointImpl> endpoint(new ProducerEndpointImpl( |
403 | 300 | id, client_identity, this, weak_runner_.task_runner(), producer, |
404 | 300 | producer_name, sdk_version, in_process, smb_scraping_enabled)); |
405 | 300 | auto it_and_inserted = producers_.emplace(id, endpoint.get()); |
406 | 300 | PERFETTO_DCHECK(it_and_inserted.second); |
407 | 300 | endpoint->shmem_size_hint_bytes_ = shared_memory_size_hint_bytes; |
408 | 300 | endpoint->shmem_page_size_hint_bytes_ = shared_memory_page_size_hint_bytes; |
409 | | |
410 | | // Producer::OnConnect() should run before Producer::OnTracingSetup(). The |
411 | | // latter may be posted by SetupSharedMemory() below, so post OnConnect() now. |
412 | 300 | endpoint->weak_runner_.PostTask( |
413 | 300 | [endpoint = endpoint.get()] { endpoint->producer_->OnConnect(); }); |
414 | | |
415 | 300 | if (shm) { |
416 | | // The producer supplied an SMB. This is used only by Chrome; in the most |
417 | | // common cases the SMB is created by the service and passed via |
418 | | // OnTracingSetup(). Verify that it is correctly sized before we attempt to |
419 | | // use it. The transport layer has to verify the integrity of the SMB (e.g. |
420 | | // ensure that the producer can't resize if after the fact). |
421 | 0 | size_t shm_size, page_size; |
422 | 0 | std::tie(shm_size, page_size) = |
423 | 0 | EnsureValidShmSizes(shm->size(), endpoint->shmem_page_size_hint_bytes_); |
424 | 0 | if (shm_size == shm->size() && |
425 | 0 | page_size == endpoint->shmem_page_size_hint_bytes_) { |
426 | 0 | PERFETTO_DLOG( |
427 | 0 | "Adopting producer-provided SMB of %zu kB for producer \"%s\"", |
428 | 0 | shm_size / 1024, endpoint->name_.c_str()); |
429 | 0 | endpoint->SetupSharedMemory(std::move(shm), page_size, |
430 | 0 | /*provided_by_producer=*/true); |
431 | 0 | } else { |
432 | 0 | PERFETTO_LOG( |
433 | 0 | "Discarding incorrectly sized producer-provided SMB for producer " |
434 | 0 | "\"%s\", falling back to service-provided SMB. Requested sizes: %zu " |
435 | 0 | "B total, %zu B page size; suggested corrected sizes: %zu B total, " |
436 | 0 | "%zu B page size", |
437 | 0 | endpoint->name_.c_str(), shm->size(), |
438 | 0 | endpoint->shmem_page_size_hint_bytes_, shm_size, page_size); |
439 | 0 | shm.reset(); |
440 | 0 | } |
441 | 0 | } |
442 | | |
443 | 300 | return std::unique_ptr<ProducerEndpoint>(std::move(endpoint)); |
444 | 300 | } |
445 | | |
446 | 300 | void TracingServiceImpl::DisconnectProducer(ProducerID id) { |
447 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
448 | 300 | PERFETTO_DLOG("Producer %" PRIu16 " disconnected", id); |
449 | 300 | PERFETTO_DCHECK(producers_.count(id)); |
450 | | |
451 | | // Scrape remaining chunks for this producer to ensure we don't lose data. |
452 | 300 | if (auto* producer = GetProducer(id)) { |
453 | 300 | for (auto& session_id_and_session : tracing_sessions_) |
454 | 300 | ScrapeSharedMemoryBuffers(&session_id_and_session.second, producer); |
455 | 300 | } |
456 | | |
457 | 600 | for (auto it = data_sources_.begin(); it != data_sources_.end();) { |
458 | 300 | auto next = it; |
459 | 300 | next++; |
460 | 300 | if (it->second.producer_id == id) |
461 | 300 | UnregisterDataSource(id, it->second.descriptor.name()); |
462 | 300 | it = next; |
463 | 300 | } |
464 | | |
465 | 300 | producers_.erase(id); |
466 | 300 | UpdateMemoryGuardrail(); |
467 | 300 | } |
468 | | |
469 | | TracingServiceImpl::ProducerEndpointImpl* TracingServiceImpl::GetProducer( |
470 | 45.9k | ProducerID id) const { |
471 | 45.9k | PERFETTO_DCHECK_THREAD(thread_checker_); |
472 | 45.9k | auto it = producers_.find(id); |
473 | 45.9k | if (it == producers_.end()) |
474 | 0 | return nullptr; |
475 | 45.9k | return it->second; |
476 | 45.9k | } |
477 | | |
478 | | std::unique_ptr<TracingService::ConsumerEndpoint> |
479 | 300 | TracingServiceImpl::ConnectConsumer(Consumer* consumer, uid_t uid) { |
480 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
481 | 300 | PERFETTO_DLOG("Consumer %p connected from UID %" PRIu64, |
482 | 300 | reinterpret_cast<void*>(consumer), static_cast<uint64_t>(uid)); |
483 | 300 | std::unique_ptr<ConsumerEndpointImpl> endpoint(new ConsumerEndpointImpl( |
484 | 300 | this, weak_runner_.task_runner(), consumer, uid)); |
485 | | // Consumer might go away before we're able to send the connect notification, |
486 | | // if that is the case just bail out. |
487 | 300 | auto weak_ptr = endpoint->weak_ptr_factory_.GetWeakPtr(); |
488 | 300 | weak_runner_.task_runner()->PostTask([weak_ptr = std::move(weak_ptr)] { |
489 | 300 | if (weak_ptr) |
490 | 300 | weak_ptr->consumer_->OnConnect(); |
491 | 300 | }); |
492 | 300 | return std::unique_ptr<ConsumerEndpoint>(std::move(endpoint)); |
493 | 300 | } |
494 | | |
495 | 300 | void TracingServiceImpl::DisconnectConsumer(ConsumerEndpointImpl* consumer) { |
496 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
497 | 300 | PERFETTO_DLOG("Consumer %p disconnected", reinterpret_cast<void*>(consumer)); |
498 | | |
499 | 300 | if (consumer->tracing_session_id_) |
500 | 300 | FreeBuffers(consumer->tracing_session_id_); // Will also DisableTracing(). |
501 | | |
502 | | // At this point no more pointers to |consumer| should be around. |
503 | 300 | PERFETTO_DCHECK(!std::any_of( |
504 | 300 | tracing_sessions_.begin(), tracing_sessions_.end(), |
505 | 300 | [consumer](const std::pair<const TracingSessionID, TracingSession>& kv) { |
506 | 300 | return kv.second.consumer_maybe_null == consumer; |
507 | 300 | })); |
508 | 300 | } |
509 | | |
510 | | bool TracingServiceImpl::DetachConsumer(ConsumerEndpointImpl* consumer, |
511 | 0 | const std::string& key) { |
512 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
513 | 0 | PERFETTO_DLOG("Consumer %p detached", reinterpret_cast<void*>(consumer)); |
514 | |
|
515 | 0 | TracingSessionID tsid = consumer->tracing_session_id_; |
516 | 0 | TracingSession* tracing_session; |
517 | 0 | if (!tsid || !(tracing_session = GetTracingSession(tsid))) |
518 | 0 | return false; |
519 | | |
520 | 0 | if (GetDetachedSession(consumer->uid_, key)) { |
521 | 0 | PERFETTO_ELOG("Another session has been detached with the same key \"%s\"", |
522 | 0 | key.c_str()); |
523 | 0 | return false; |
524 | 0 | } |
525 | | |
526 | 0 | PERFETTO_DCHECK(tracing_session->consumer_maybe_null == consumer); |
527 | 0 | tracing_session->consumer_maybe_null = nullptr; |
528 | 0 | tracing_session->detach_key = key; |
529 | 0 | consumer->tracing_session_id_ = 0; |
530 | 0 | return true; |
531 | 0 | } |
532 | | |
533 | | std::unique_ptr<TracingService::RelayEndpoint> |
534 | 0 | TracingServiceImpl::ConnectRelayClient(RelayClientID relay_client_id) { |
535 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
536 | |
|
537 | 0 | auto endpoint = std::make_unique<RelayEndpointImpl>(relay_client_id, this); |
538 | 0 | relay_clients_[relay_client_id] = endpoint.get(); |
539 | |
|
540 | 0 | return std::move(endpoint); |
541 | 0 | } |
542 | | |
543 | 0 | void TracingServiceImpl::DisconnectRelayClient(RelayClientID relay_client_id) { |
544 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
545 | |
|
546 | 0 | if (relay_clients_.find(relay_client_id) == relay_clients_.end()) |
547 | 0 | return; |
548 | 0 | relay_clients_.erase(relay_client_id); |
549 | 0 | } |
550 | | |
551 | | bool TracingServiceImpl::AttachConsumer(ConsumerEndpointImpl* consumer, |
552 | 0 | const std::string& key) { |
553 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
554 | 0 | PERFETTO_DLOG("Consumer %p attaching to session %s", |
555 | 0 | reinterpret_cast<void*>(consumer), key.c_str()); |
556 | |
|
557 | 0 | if (consumer->tracing_session_id_) { |
558 | 0 | PERFETTO_ELOG( |
559 | 0 | "Cannot reattach consumer to session %s" |
560 | 0 | " while it already attached tracing session ID %" PRIu64, |
561 | 0 | key.c_str(), consumer->tracing_session_id_); |
562 | 0 | return false; |
563 | 0 | } |
564 | | |
565 | 0 | auto* tracing_session = GetDetachedSession(consumer->uid_, key); |
566 | 0 | if (!tracing_session) { |
567 | 0 | PERFETTO_ELOG( |
568 | 0 | "Failed to attach consumer, session '%s' not found for uid %d", |
569 | 0 | key.c_str(), static_cast<int>(consumer->uid_)); |
570 | 0 | return false; |
571 | 0 | } |
572 | | |
573 | 0 | consumer->tracing_session_id_ = tracing_session->id; |
574 | 0 | tracing_session->consumer_maybe_null = consumer; |
575 | 0 | tracing_session->detach_key.clear(); |
576 | 0 | return true; |
577 | 0 | } |
578 | | |
579 | | base::Status TracingServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer, |
580 | | const TraceConfig& cfg, |
581 | 300 | base::ScopedFile fd) { |
582 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
583 | | |
584 | | // If the producer is specifying a UUID, respect that (at least for the first |
585 | | // snapshot). Otherwise generate a new UUID. |
586 | 300 | base::Uuid uuid(cfg.trace_uuid_lsb(), cfg.trace_uuid_msb()); |
587 | 300 | if (!uuid) |
588 | 300 | uuid = base::Uuidv4(); |
589 | | |
590 | 300 | PERFETTO_DLOG("Enabling tracing for consumer %p, UUID: %s", |
591 | 300 | reinterpret_cast<void*>(consumer), |
592 | 300 | uuid.ToPrettyString().c_str()); |
593 | 300 | MaybeLogUploadEvent(cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracing); |
594 | 300 | if (cfg.lockdown_mode() == TraceConfig::LOCKDOWN_SET) |
595 | 0 | lockdown_mode_ = true; |
596 | 300 | if (cfg.lockdown_mode() == TraceConfig::LOCKDOWN_CLEAR) |
597 | 0 | lockdown_mode_ = false; |
598 | | |
599 | | // Scope |tracing_session| to this block to prevent accidental use of a null |
600 | | // pointer later in this function. |
601 | 300 | { |
602 | 300 | TracingSession* tracing_session = |
603 | 300 | GetTracingSession(consumer->tracing_session_id_); |
604 | 300 | if (tracing_session) { |
605 | 0 | MaybeLogUploadEvent( |
606 | 0 | cfg, uuid, |
607 | 0 | PerfettoStatsdAtom::kTracedEnableTracingExistingTraceSession); |
608 | 0 | return PERFETTO_SVC_ERR( |
609 | 0 | "A Consumer is trying to EnableTracing() but another tracing " |
610 | 0 | "session is already active (forgot a call to FreeBuffers() ?)"); |
611 | 0 | } |
612 | 300 | } |
613 | | |
614 | 300 | const uint32_t max_duration_ms = cfg.enable_extra_guardrails() |
615 | 300 | ? kGuardrailsMaxTracingDurationMillis |
616 | 300 | : kMaxTracingDurationMillis; |
617 | 300 | if (cfg.duration_ms() > max_duration_ms) { |
618 | 0 | MaybeLogUploadEvent(cfg, uuid, |
619 | 0 | PerfettoStatsdAtom::kTracedEnableTracingTooLongTrace); |
620 | 0 | return PERFETTO_SVC_ERR("Requested too long trace (%" PRIu32 |
621 | 0 | "ms > %" PRIu32 " ms)", |
622 | 0 | cfg.duration_ms(), max_duration_ms); |
623 | 0 | } |
624 | | |
625 | 300 | const bool has_trigger_config = |
626 | 300 | GetTriggerMode(cfg) != TraceConfig::TriggerConfig::UNSPECIFIED; |
627 | 300 | if (has_trigger_config && |
628 | 0 | (cfg.trigger_config().trigger_timeout_ms() == 0 || |
629 | 0 | cfg.trigger_config().trigger_timeout_ms() > max_duration_ms)) { |
630 | 0 | MaybeLogUploadEvent( |
631 | 0 | cfg, uuid, |
632 | 0 | PerfettoStatsdAtom::kTracedEnableTracingInvalidTriggerTimeout); |
633 | 0 | return PERFETTO_SVC_ERR( |
634 | 0 | "Traces with START_TRACING triggers must provide a positive " |
635 | 0 | "trigger_timeout_ms < 7 days (received %" PRIu32 "ms)", |
636 | 0 | cfg.trigger_config().trigger_timeout_ms()); |
637 | 0 | } |
638 | | |
639 | | // This check has been introduced in May 2023 after finding b/274931668. |
640 | 300 | if (static_cast<int>(cfg.trigger_config().trigger_mode()) > |
641 | 300 | TraceConfig::TriggerConfig::TriggerMode_MAX) { |
642 | 0 | MaybeLogUploadEvent( |
643 | 0 | cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingInvalidTriggerMode); |
644 | 0 | return PERFETTO_SVC_ERR( |
645 | 0 | "The trace config specified an invalid trigger_mode"); |
646 | 0 | } |
647 | | |
648 | 300 | if (cfg.trigger_config().use_clone_snapshot_if_available() && |
649 | 0 | cfg.trigger_config().trigger_mode() != |
650 | 0 | TraceConfig::TriggerConfig::STOP_TRACING) { |
651 | 0 | MaybeLogUploadEvent( |
652 | 0 | cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingInvalidTriggerMode); |
653 | 0 | return PERFETTO_SVC_ERR( |
654 | 0 | "trigger_mode must be STOP_TRACING when " |
655 | 0 | "use_clone_snapshot_if_available=true"); |
656 | 0 | } |
657 | | |
658 | 300 | if (has_trigger_config && cfg.duration_ms() != 0) { |
659 | 0 | MaybeLogUploadEvent( |
660 | 0 | cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingDurationWithTrigger); |
661 | 0 | return PERFETTO_SVC_ERR( |
662 | 0 | "duration_ms was set, this must not be set for traces with triggers."); |
663 | 0 | } |
664 | | |
665 | 300 | for (char c : cfg.bugreport_filename()) { |
666 | 0 | if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || |
667 | 0 | (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.')) { |
668 | 0 | MaybeLogUploadEvent( |
669 | 0 | cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingInvalidBrFilename); |
670 | 0 | return PERFETTO_SVC_ERR( |
671 | 0 | "bugreport_filename contains invalid chars. Use [a-zA-Z0-9-_.]+"); |
672 | 0 | } |
673 | 0 | } |
674 | | |
675 | 300 | if ((GetTriggerMode(cfg) == TraceConfig::TriggerConfig::STOP_TRACING || |
676 | 300 | GetTriggerMode(cfg) == TraceConfig::TriggerConfig::CLONE_SNAPSHOT) && |
677 | 0 | cfg.write_into_file()) { |
678 | | // We don't support this usecase because there are subtle assumptions which |
679 | | // break around TracingServiceEvents and windowed sorting (i.e. if we don't |
680 | | // drain the events in ReadBuffersIntoFile because we are waiting for |
681 | | // STOP_TRACING, we can end up queueing up a lot of TracingServiceEvents and |
682 | | // emitting them wildy out of order breaking windowed sorting in trace |
683 | | // processor). |
684 | 0 | MaybeLogUploadEvent( |
685 | 0 | cfg, uuid, |
686 | 0 | PerfettoStatsdAtom::kTracedEnableTracingStopTracingWriteIntoFile); |
687 | 0 | return PERFETTO_SVC_ERR( |
688 | 0 | "Specifying trigger mode STOP_TRACING/CLONE_SNAPSHOT and " |
689 | 0 | "write_into_file together is unsupported"); |
690 | 0 | } |
691 | | |
692 | 300 | std::unordered_set<std::string> triggers; |
693 | 300 | for (const auto& trigger : cfg.trigger_config().triggers()) { |
694 | 0 | if (!triggers.insert(trigger.name()).second) { |
695 | 0 | MaybeLogUploadEvent( |
696 | 0 | cfg, uuid, |
697 | 0 | PerfettoStatsdAtom::kTracedEnableTracingDuplicateTriggerName); |
698 | 0 | return PERFETTO_SVC_ERR("Duplicate trigger name: %s", |
699 | 0 | trigger.name().c_str()); |
700 | 0 | } |
701 | 0 | } |
702 | | |
703 | 300 | if (cfg.enable_extra_guardrails()) { |
704 | 0 | if (cfg.deferred_start()) { |
705 | 0 | MaybeLogUploadEvent( |
706 | 0 | cfg, uuid, |
707 | 0 | PerfettoStatsdAtom::kTracedEnableTracingInvalidDeferredStart); |
708 | 0 | return PERFETTO_SVC_ERR( |
709 | 0 | "deferred_start=true is not supported in unsupervised traces"); |
710 | 0 | } |
711 | 0 | uint64_t buf_size_sum = 0; |
712 | 0 | for (const auto& buf : cfg.buffers()) { |
713 | 0 | if (buf.size_kb() % 4 != 0) { |
714 | 0 | MaybeLogUploadEvent( |
715 | 0 | cfg, uuid, |
716 | 0 | PerfettoStatsdAtom::kTracedEnableTracingInvalidBufferSize); |
717 | 0 | return PERFETTO_SVC_ERR( |
718 | 0 | "buffers.size_kb must be a multiple of 4, got %" PRIu32, |
719 | 0 | buf.size_kb()); |
720 | 0 | } |
721 | 0 | buf_size_sum += buf.size_kb(); |
722 | 0 | } |
723 | | |
724 | 0 | uint32_t max_tracing_buffer_size_kb = |
725 | 0 | std::max(kGuardrailsMaxTracingBufferSizeKb, |
726 | 0 | cfg.guardrail_overrides().max_tracing_buffer_size_kb()); |
727 | 0 | if (buf_size_sum > max_tracing_buffer_size_kb) { |
728 | 0 | MaybeLogUploadEvent( |
729 | 0 | cfg, uuid, |
730 | 0 | PerfettoStatsdAtom::kTracedEnableTracingBufferSizeTooLarge); |
731 | 0 | return PERFETTO_SVC_ERR("Requested too large trace buffer (%" PRIu64 |
732 | 0 | "kB > %" PRIu32 " kB)", |
733 | 0 | buf_size_sum, max_tracing_buffer_size_kb); |
734 | 0 | } |
735 | 0 | } |
736 | | |
737 | 300 | if (cfg.buffers_size() > kMaxBuffersPerConsumer) { |
738 | 0 | MaybeLogUploadEvent(cfg, uuid, |
739 | 0 | PerfettoStatsdAtom::kTracedEnableTracingTooManyBuffers); |
740 | 0 | return PERFETTO_SVC_ERR("Too many buffers configured (%d)", |
741 | 0 | cfg.buffers_size()); |
742 | 0 | } |
743 | | // Check that the config specifies all buffers for its data sources. This |
744 | | // is also checked in SetupDataSource, but it is simpler to return a proper |
745 | | // error to the consumer from here (and there will be less state to undo). |
746 | 300 | for (const TraceConfig::DataSource& cfg_data_source : cfg.data_sources()) { |
747 | 300 | size_t num_buffers = static_cast<size_t>(cfg.buffers_size()); |
748 | 300 | size_t target_buffer = cfg_data_source.config().target_buffer(); |
749 | 300 | if (target_buffer >= num_buffers) { |
750 | 0 | MaybeLogUploadEvent( |
751 | 0 | cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingOobTargetBuffer); |
752 | 0 | return PERFETTO_SVC_ERR( |
753 | 0 | "Data source \"%s\" specified an out of bounds target_buffer (%zu >= " |
754 | 0 | "%zu)", |
755 | 0 | cfg_data_source.config().name().c_str(), target_buffer, num_buffers); |
756 | 0 | } |
757 | 300 | } |
758 | | |
759 | 300 | if (!cfg.unique_session_name().empty()) { |
760 | 0 | const std::string& name = cfg.unique_session_name(); |
761 | 0 | for (auto& kv : tracing_sessions_) { |
762 | 0 | if (kv.second.state == TracingSession::CLONED_READ_ONLY) |
763 | 0 | continue; // Don't consider cloned sessions in uniqueness checks. |
764 | 0 | if (kv.second.config.unique_session_name() == name) { |
765 | 0 | MaybeLogUploadEvent( |
766 | 0 | cfg, uuid, |
767 | 0 | PerfettoStatsdAtom::kTracedEnableTracingDuplicateSessionName); |
768 | 0 | static const char fmt[] = |
769 | 0 | "A trace with this unique session name (%s) already exists"; |
770 | | // This happens frequently, don't make it an "E"LOG. |
771 | 0 | PERFETTO_LOG(fmt, name.c_str()); |
772 | 0 | return base::ErrStatus(fmt, name.c_str()); |
773 | 0 | } |
774 | 0 | } |
775 | 0 | } |
776 | | |
777 | 300 | if (!cfg.session_semaphores().empty()) { |
778 | 0 | struct SemaphoreSessionsState { |
779 | 0 | uint64_t smallest_max_other_session_count = |
780 | 0 | std::numeric_limits<uint64_t>::max(); |
781 | 0 | uint64_t session_count = 0; |
782 | 0 | }; |
783 | | // For each semaphore, compute the number of active sessions and the |
784 | | // MIN(limit). |
785 | 0 | std::unordered_map<std::string, SemaphoreSessionsState> |
786 | 0 | sem_to_sessions_state; |
787 | 0 | for (const auto& id_and_session : tracing_sessions_) { |
788 | 0 | const auto& session = id_and_session.second; |
789 | 0 | if (session.state == TracingSession::CLONED_READ_ONLY || |
790 | 0 | session.state == TracingSession::DISABLED) { |
791 | | // Don't consider cloned or disabled sessions in checks. |
792 | 0 | continue; |
793 | 0 | } |
794 | 0 | for (const auto& sem : session.config.session_semaphores()) { |
795 | 0 | auto& sessions_state = sem_to_sessions_state[sem.name()]; |
796 | 0 | sessions_state.smallest_max_other_session_count = |
797 | 0 | std::min(sessions_state.smallest_max_other_session_count, |
798 | 0 | sem.max_other_session_count()); |
799 | 0 | sessions_state.session_count++; |
800 | 0 | } |
801 | 0 | } |
802 | | |
803 | | // Check if any of the semaphores declared by the config clashes with any of |
804 | | // the currently active semaphores. |
805 | 0 | for (const auto& semaphore : cfg.session_semaphores()) { |
806 | 0 | auto it = sem_to_sessions_state.find(semaphore.name()); |
807 | 0 | if (it == sem_to_sessions_state.end()) { |
808 | 0 | continue; |
809 | 0 | } |
810 | 0 | uint64_t max_other_session_count = |
811 | 0 | std::min(semaphore.max_other_session_count(), |
812 | 0 | it->second.smallest_max_other_session_count); |
813 | 0 | if (it->second.session_count > max_other_session_count) { |
814 | 0 | MaybeLogUploadEvent( |
815 | 0 | cfg, uuid, |
816 | 0 | PerfettoStatsdAtom:: |
817 | 0 | kTracedEnableTracingFailedSessionSemaphoreCheck); |
818 | 0 | return PERFETTO_SVC_ERR( |
819 | 0 | "Semaphore \"%s\" exceeds maximum allowed other session count " |
820 | 0 | "(%" PRIu64 " > min(%" PRIu64 ", %" PRIu64 "))", |
821 | 0 | semaphore.name().c_str(), it->second.session_count, |
822 | 0 | semaphore.max_other_session_count(), |
823 | 0 | it->second.smallest_max_other_session_count); |
824 | 0 | } |
825 | 0 | } |
826 | 0 | } |
827 | | |
828 | 300 | if (cfg.enable_extra_guardrails()) { |
829 | | // unique_session_name can be empty |
830 | 0 | const std::string& name = cfg.unique_session_name(); |
831 | 0 | int64_t now_s = clock_->GetBootTimeS().count(); |
832 | | |
833 | | // Remove any entries where the time limit has passed so this map doesn't |
834 | | // grow indefinitely: |
835 | 0 | std::map<std::string, int64_t>& sessions = session_to_last_trace_s_; |
836 | 0 | for (auto it = sessions.cbegin(); it != sessions.cend();) { |
837 | 0 | if (now_s - it->second > kMinSecondsBetweenTracesGuardrail) { |
838 | 0 | it = sessions.erase(it); |
839 | 0 | } else { |
840 | 0 | ++it; |
841 | 0 | } |
842 | 0 | } |
843 | |
|
844 | 0 | int64_t& previous_s = session_to_last_trace_s_[name]; |
845 | 0 | if (previous_s == 0) { |
846 | 0 | previous_s = now_s; |
847 | 0 | } else { |
848 | 0 | MaybeLogUploadEvent( |
849 | 0 | cfg, uuid, |
850 | 0 | PerfettoStatsdAtom::kTracedEnableTracingSessionNameTooRecent); |
851 | 0 | return PERFETTO_SVC_ERR( |
852 | 0 | "A trace with unique session name \"%s\" began less than %" PRId64 |
853 | 0 | "s ago (%" PRId64 "s)", |
854 | 0 | name.c_str(), kMinSecondsBetweenTracesGuardrail, now_s - previous_s); |
855 | 0 | } |
856 | 0 | } |
857 | | |
858 | 300 | const int sessions_for_uid = static_cast<int>(std::count_if( |
859 | 300 | tracing_sessions_.begin(), tracing_sessions_.end(), |
860 | 300 | [consumer](const decltype(tracing_sessions_)::value_type& s) { |
861 | 0 | return s.second.consumer_uid == consumer->uid_; |
862 | 0 | })); |
863 | | |
864 | 300 | int per_uid_limit = kMaxConcurrentTracingSessionsPerUid; |
865 | 300 | if (consumer->uid_ == 1066 /* AID_STATSD*/) { |
866 | 0 | per_uid_limit = kMaxConcurrentTracingSessionsForStatsdUid; |
867 | 0 | } |
868 | 300 | if (sessions_for_uid >= per_uid_limit) { |
869 | 0 | MaybeLogUploadEvent( |
870 | 0 | cfg, uuid, |
871 | 0 | PerfettoStatsdAtom::kTracedEnableTracingTooManySessionsForUid); |
872 | 0 | return PERFETTO_SVC_ERR( |
873 | 0 | "Too many concurrent tracing sesions (%d) for uid %d limit is %d", |
874 | 0 | sessions_for_uid, static_cast<int>(consumer->uid_), per_uid_limit); |
875 | 0 | } |
876 | | |
877 | | // TODO(primiano): This is a workaround to prevent that a producer gets stuck |
878 | | // in a state where it stalls by design by having more TraceWriterImpl |
879 | | // instances than free pages in the buffer. This is really a bug in |
880 | | // trace_probes and the way it handles stalls in the shmem buffer. |
881 | 300 | if (tracing_sessions_.size() >= kMaxConcurrentTracingSessions) { |
882 | 0 | MaybeLogUploadEvent( |
883 | 0 | cfg, uuid, |
884 | 0 | PerfettoStatsdAtom::kTracedEnableTracingTooManyConcurrentSessions); |
885 | 0 | return PERFETTO_SVC_ERR("Too many concurrent tracing sesions (%zu)", |
886 | 0 | tracing_sessions_.size()); |
887 | 0 | } |
888 | | |
889 | | // If the trace config provides a filter bytecode, setup the filter now. |
890 | | // If the filter loading fails, abort the tracing session rather than running |
891 | | // unfiltered. |
892 | 300 | std::unique_ptr<protozero::MessageFilter> trace_filter; |
893 | 300 | if (cfg.has_trace_filter()) { |
894 | 0 | const auto& filt = cfg.trace_filter(); |
895 | 0 | trace_filter.reset(new protozero::MessageFilter()); |
896 | |
|
897 | 0 | protozero::StringFilter& string_filter = trace_filter->string_filter(); |
898 | 0 | for (const auto& rule : filt.string_filter_chain().rules()) { |
899 | 0 | auto opt_policy = ConvertPolicy(rule.policy()); |
900 | 0 | if (!opt_policy.has_value()) { |
901 | 0 | MaybeLogUploadEvent( |
902 | 0 | cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingInvalidFilter); |
903 | 0 | return PERFETTO_SVC_ERR( |
904 | 0 | "Trace filter has invalid string filtering rules, aborting"); |
905 | 0 | } |
906 | 0 | string_filter.AddRule(*opt_policy, rule.regex_pattern(), |
907 | 0 | rule.atrace_payload_starts_with()); |
908 | 0 | } |
909 | | |
910 | 0 | const std::string& bytecode_v1 = filt.bytecode(); |
911 | 0 | const std::string& bytecode_v2 = filt.bytecode_v2(); |
912 | 0 | const std::string& bytecode = |
913 | 0 | bytecode_v2.empty() ? bytecode_v1 : bytecode_v2; |
914 | 0 | if (!trace_filter->LoadFilterBytecode(bytecode.data(), bytecode.size())) { |
915 | 0 | MaybeLogUploadEvent( |
916 | 0 | cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingInvalidFilter); |
917 | 0 | return PERFETTO_SVC_ERR("Trace filter bytecode invalid, aborting"); |
918 | 0 | } |
919 | | |
920 | | // The filter is created using perfetto.protos.Trace as root message |
921 | | // (because that makes it possible to play around with the `proto_filter` |
922 | | // tool on actual traces). Here in the service, however, we deal with |
923 | | // perfetto.protos.TracePacket(s), which are one level down (Trace.packet). |
924 | | // The IPC client (or the write_into_filte logic in here) are responsible |
925 | | // for pre-pending the packet preamble (See GetProtoPreamble() calls), but |
926 | | // the preamble is not there at ReadBuffer time. Hence we change the root of |
927 | | // the filtering to start at the Trace.packet level. |
928 | 0 | if (!trace_filter->SetFilterRoot({TracePacket::kPacketFieldNumber})) { |
929 | 0 | MaybeLogUploadEvent( |
930 | 0 | cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingInvalidFilter); |
931 | 0 | return PERFETTO_SVC_ERR("Failed to set filter root."); |
932 | 0 | } |
933 | 0 | } |
934 | | |
935 | 300 | const TracingSessionID tsid = ++last_tracing_session_id_; |
936 | 300 | TracingSession* tracing_session = |
937 | 300 | &tracing_sessions_ |
938 | 300 | .emplace(std::piecewise_construct, std::forward_as_tuple(tsid), |
939 | 300 | std::forward_as_tuple(tsid, consumer, cfg, |
940 | 300 | weak_runner_.task_runner())) |
941 | 300 | .first->second; |
942 | | |
943 | 300 | tracing_session->trace_uuid = uuid; |
944 | | |
945 | 300 | if (trace_filter) |
946 | 0 | tracing_session->trace_filter = std::move(trace_filter); |
947 | | |
948 | 300 | if (cfg.write_into_file()) { |
949 | 0 | if (!fd ^ !cfg.output_path().empty()) { |
950 | 0 | MaybeLogUploadEvent( |
951 | 0 | tracing_session->config, uuid, |
952 | 0 | PerfettoStatsdAtom::kTracedEnableTracingInvalidFdOutputFile); |
953 | 0 | tracing_sessions_.erase(tsid); |
954 | 0 | return PERFETTO_SVC_ERR( |
955 | 0 | "When write_into_file==true either a FD needs to be passed or " |
956 | 0 | "output_path must be populated (but not both)"); |
957 | 0 | } |
958 | 0 | if (!cfg.output_path().empty()) { |
959 | 0 | fd = CreateTraceFile(cfg.output_path(), /*overwrite=*/false); |
960 | 0 | if (!fd) { |
961 | 0 | MaybeLogUploadEvent( |
962 | 0 | tracing_session->config, uuid, |
963 | 0 | PerfettoStatsdAtom::kTracedEnableTracingFailedToCreateFile); |
964 | 0 | tracing_sessions_.erase(tsid); |
965 | 0 | return PERFETTO_SVC_ERR("Failed to create the trace file %s", |
966 | 0 | cfg.output_path().c_str()); |
967 | 0 | } |
968 | 0 | } |
969 | 0 | tracing_session->write_into_file = std::move(fd); |
970 | 0 | uint32_t write_period_ms = cfg.file_write_period_ms(); |
971 | 0 | if (write_period_ms == 0) |
972 | 0 | write_period_ms = kDefaultWriteIntoFilePeriodMs; |
973 | 0 | if (write_period_ms < kMinWriteIntoFilePeriodMs) |
974 | 0 | write_period_ms = kMinWriteIntoFilePeriodMs; |
975 | 0 | tracing_session->write_period_ms = write_period_ms; |
976 | 0 | tracing_session->max_file_size_bytes = cfg.max_file_size_bytes(); |
977 | 0 | tracing_session->bytes_written_into_file = 0; |
978 | 0 | } |
979 | | |
980 | 300 | if (cfg.compression_type() == TraceConfig::COMPRESSION_TYPE_DEFLATE) { |
981 | 0 | if (init_opts_.compressor_fn) { |
982 | 0 | tracing_session->compress_deflate = true; |
983 | 0 | } else { |
984 | 0 | PERFETTO_LOG( |
985 | 0 | "COMPRESSION_TYPE_DEFLATE is not supported in the current build " |
986 | 0 | "configuration. Skipping compression"); |
987 | 0 | } |
988 | 0 | } |
989 | | |
990 | | // Initialize the log buffers. |
991 | 300 | bool did_allocate_all_buffers = true; |
992 | 300 | bool invalid_buffer_config = false; |
993 | | |
994 | | // Allocate the trace buffers. Also create a map to translate a consumer |
995 | | // relative index (TraceConfig.DataSourceConfig.target_buffer) into the |
996 | | // corresponding BufferID, which is a global ID namespace for the service and |
997 | | // all producers. |
998 | 300 | size_t total_buf_size_kb = 0; |
999 | 300 | const size_t num_buffers = static_cast<size_t>(cfg.buffers_size()); |
1000 | 300 | tracing_session->buffers_index.reserve(num_buffers); |
1001 | 600 | for (size_t i = 0; i < num_buffers; i++) { |
1002 | 300 | const TraceConfig::BufferConfig& buffer_cfg = cfg.buffers()[i]; |
1003 | 300 | BufferID global_id = buffer_ids_.Allocate(); |
1004 | 300 | if (!global_id) { |
1005 | 0 | did_allocate_all_buffers = false; // We ran out of IDs. |
1006 | 0 | break; |
1007 | 0 | } |
1008 | 300 | tracing_session->buffers_index.push_back(global_id); |
1009 | | // TraceBuffer size is limited to 32-bit. |
1010 | 300 | const uint32_t buf_size_kb = buffer_cfg.size_kb(); |
1011 | 300 | const uint64_t buf_size_bytes = buf_size_kb * static_cast<uint64_t>(1024); |
1012 | 300 | const size_t buf_size = static_cast<size_t>(buf_size_bytes); |
1013 | 300 | if (buf_size_bytes == 0 || |
1014 | 300 | buf_size_bytes > std::numeric_limits<uint32_t>::max() || |
1015 | 300 | buf_size != buf_size_bytes) { |
1016 | 0 | invalid_buffer_config = true; |
1017 | 0 | did_allocate_all_buffers = false; |
1018 | 0 | break; |
1019 | 0 | } |
1020 | 300 | total_buf_size_kb += buf_size_kb; |
1021 | 300 | TraceBuffer::OverwritePolicy policy = |
1022 | 300 | buffer_cfg.fill_policy() == TraceConfig::BufferConfig::DISCARD |
1023 | 300 | ? TraceBuffer::kDiscard |
1024 | 300 | : TraceBuffer::kOverwrite; |
1025 | 300 | auto it_and_inserted = |
1026 | 300 | buffers_.emplace(global_id, TraceBuffer::Create(buf_size, policy)); |
1027 | 300 | PERFETTO_DCHECK(it_and_inserted.second); // buffers_.count(global_id) == 0. |
1028 | 300 | std::unique_ptr<TraceBuffer>& trace_buffer = it_and_inserted.first->second; |
1029 | 300 | if (!trace_buffer) { |
1030 | 0 | did_allocate_all_buffers = false; |
1031 | 0 | break; |
1032 | 0 | } |
1033 | 300 | } |
1034 | | |
1035 | | // This can happen if either: |
1036 | | // - All the kMaxTraceBufferID slots are taken. |
1037 | | // - OOM, or, more realistically, we exhausted virtual memory. |
1038 | | // - The buffer size in the config is invalid. |
1039 | | // In any case, free all the previously allocated buffers and abort. |
1040 | 300 | if (!did_allocate_all_buffers) { |
1041 | 0 | for (BufferID global_id : tracing_session->buffers_index) { |
1042 | 0 | buffer_ids_.Free(global_id); |
1043 | 0 | buffers_.erase(global_id); |
1044 | 0 | } |
1045 | 0 | MaybeLogUploadEvent(tracing_session->config, uuid, |
1046 | 0 | PerfettoStatsdAtom::kTracedEnableTracingOom); |
1047 | 0 | tracing_sessions_.erase(tsid); |
1048 | 0 | if (invalid_buffer_config) { |
1049 | 0 | return PERFETTO_SVC_ERR( |
1050 | 0 | "Failed to allocate tracing buffers: Invalid buffer sizes"); |
1051 | 0 | } |
1052 | 0 | return PERFETTO_SVC_ERR( |
1053 | 0 | "Failed to allocate tracing buffers: OOM or too many buffers"); |
1054 | 0 | } |
1055 | | |
1056 | 300 | UpdateMemoryGuardrail(); |
1057 | | |
1058 | 300 | consumer->tracing_session_id_ = tsid; |
1059 | | |
1060 | | // Setup the data sources on the producers without starting them. |
1061 | 300 | for (const TraceConfig::DataSource& cfg_data_source : cfg.data_sources()) { |
1062 | | // Scan all the registered data sources with a matching name. |
1063 | 300 | auto range = data_sources_.equal_range(cfg_data_source.config().name()); |
1064 | 387 | for (auto it = range.first; it != range.second; it++) { |
1065 | 87 | TraceConfig::ProducerConfig producer_config; |
1066 | 87 | for (const auto& config : cfg.producers()) { |
1067 | 0 | if (GetProducer(it->second.producer_id)->name_ == |
1068 | 0 | config.producer_name()) { |
1069 | 0 | producer_config = config; |
1070 | 0 | break; |
1071 | 0 | } |
1072 | 0 | } |
1073 | 87 | SetupDataSource(cfg_data_source, producer_config, it->second, |
1074 | 87 | tracing_session); |
1075 | 87 | } |
1076 | 300 | } |
1077 | | |
1078 | 300 | bool has_start_trigger = false; |
1079 | 300 | switch (GetTriggerMode(cfg)) { |
1080 | 300 | case TraceConfig::TriggerConfig::UNSPECIFIED: |
1081 | | // no triggers are specified so this isn't a trace that is using triggers. |
1082 | 300 | PERFETTO_DCHECK(!has_trigger_config); |
1083 | 300 | break; |
1084 | 0 | case TraceConfig::TriggerConfig::START_TRACING: |
1085 | | // For traces which use START_TRACE triggers we need to ensure that the |
1086 | | // tracing session will be cleaned up when it times out. |
1087 | 0 | has_start_trigger = true; |
1088 | 0 | weak_runner_.PostDelayedTask( |
1089 | 0 | [tsid, this]() { OnStartTriggersTimeout(tsid); }, |
1090 | 0 | cfg.trigger_config().trigger_timeout_ms()); |
1091 | 0 | break; |
1092 | 0 | case TraceConfig::TriggerConfig::STOP_TRACING: |
1093 | 0 | case TraceConfig::TriggerConfig::CLONE_SNAPSHOT: |
1094 | | // Update the tracing_session's duration_ms to ensure that if no trigger |
1095 | | // is received the session will end and be cleaned up equal to the |
1096 | | // timeout. |
1097 | | // |
1098 | | // TODO(nuskos): Refactor this so that rather then modifying the config we |
1099 | | // have a field we look at on the tracing_session. |
1100 | 0 | tracing_session->config.set_duration_ms( |
1101 | 0 | cfg.trigger_config().trigger_timeout_ms()); |
1102 | 0 | break; |
1103 | | |
1104 | | // The case of unknown modes (coming from future versions of the service) |
1105 | | // is handled few lines above (search for TriggerMode_MAX). |
1106 | 300 | } |
1107 | | |
1108 | 300 | tracing_session->state = TracingSession::CONFIGURED; |
1109 | 300 | PERFETTO_LOG( |
1110 | 300 | "Configured tracing session %" PRIu64 |
1111 | 300 | ", #sources:%zu, duration:%u ms%s, #buffers:%d, total " |
1112 | 300 | "buffer size:%zu KB, total sessions:%zu, uid:%u session name: \"%s\"", |
1113 | 300 | tsid, cfg.data_sources().size(), tracing_session->config.duration_ms(), |
1114 | 300 | tracing_session->config.prefer_suspend_clock_for_duration() |
1115 | 300 | ? " (suspend_clock)" |
1116 | 300 | : "", |
1117 | 300 | cfg.buffers_size(), total_buf_size_kb, tracing_sessions_.size(), |
1118 | 300 | static_cast<unsigned int>(consumer->uid_), |
1119 | 300 | cfg.unique_session_name().c_str()); |
1120 | | |
1121 | | // Start the data sources, unless this is a case of early setup + fast |
1122 | | // triggering, either through TraceConfig.deferred_start or |
1123 | | // TraceConfig.trigger_config(). If both are specified which ever one occurs |
1124 | | // first will initiate the trace. |
1125 | 300 | if (!cfg.deferred_start() && !has_start_trigger) |
1126 | 300 | StartTracing(tsid); |
1127 | | |
1128 | 300 | return base::OkStatus(); |
1129 | 300 | } |
1130 | | |
1131 | | void TracingServiceImpl::ChangeTraceConfig(ConsumerEndpointImpl* consumer, |
1132 | 0 | const TraceConfig& updated_cfg) { |
1133 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
1134 | 0 | TracingSession* tracing_session = |
1135 | 0 | GetTracingSession(consumer->tracing_session_id_); |
1136 | 0 | PERFETTO_DCHECK(tracing_session); |
1137 | |
|
1138 | 0 | if ((tracing_session->state != TracingSession::STARTED) && |
1139 | 0 | (tracing_session->state != TracingSession::CONFIGURED)) { |
1140 | 0 | PERFETTO_ELOG( |
1141 | 0 | "ChangeTraceConfig() was called for a tracing session which isn't " |
1142 | 0 | "running."); |
1143 | 0 | return; |
1144 | 0 | } |
1145 | | |
1146 | | // We only support updating producer_name_{,regex}_filter (and pass-through |
1147 | | // configs) for now; null out any changeable fields and make sure the rest are |
1148 | | // identical. |
1149 | 0 | TraceConfig new_config_copy(updated_cfg); |
1150 | 0 | for (auto& ds_cfg : *new_config_copy.mutable_data_sources()) { |
1151 | 0 | ds_cfg.clear_producer_name_filter(); |
1152 | 0 | ds_cfg.clear_producer_name_regex_filter(); |
1153 | 0 | } |
1154 | |
|
1155 | 0 | TraceConfig current_config_copy(tracing_session->config); |
1156 | 0 | for (auto& ds_cfg : *current_config_copy.mutable_data_sources()) { |
1157 | 0 | ds_cfg.clear_producer_name_filter(); |
1158 | 0 | ds_cfg.clear_producer_name_regex_filter(); |
1159 | 0 | } |
1160 | |
|
1161 | 0 | if (new_config_copy != current_config_copy) { |
1162 | 0 | PERFETTO_LOG( |
1163 | 0 | "ChangeTraceConfig() was called with a config containing unsupported " |
1164 | 0 | "changes; only adding to the producer_name_{,regex}_filter is " |
1165 | 0 | "currently supported and will have an effect."); |
1166 | 0 | } |
1167 | |
|
1168 | 0 | for (TraceConfig::DataSource& cfg_data_source : |
1169 | 0 | *tracing_session->config.mutable_data_sources()) { |
1170 | | // Find the updated producer_filter in the new config. |
1171 | 0 | std::vector<std::string> new_producer_name_filter; |
1172 | 0 | std::vector<std::string> new_producer_name_regex_filter; |
1173 | 0 | bool found_data_source = false; |
1174 | 0 | for (const auto& it : updated_cfg.data_sources()) { |
1175 | 0 | if (cfg_data_source.config().name() == it.config().name()) { |
1176 | 0 | new_producer_name_filter = it.producer_name_filter(); |
1177 | 0 | new_producer_name_regex_filter = it.producer_name_regex_filter(); |
1178 | 0 | found_data_source = true; |
1179 | 0 | break; |
1180 | 0 | } |
1181 | 0 | } |
1182 | | |
1183 | | // Bail out if data source not present in the new config. |
1184 | 0 | if (!found_data_source) { |
1185 | 0 | PERFETTO_ELOG( |
1186 | 0 | "ChangeTraceConfig() called without a current data source also " |
1187 | 0 | "present in the new config: %s", |
1188 | 0 | cfg_data_source.config().name().c_str()); |
1189 | 0 | continue; |
1190 | 0 | } |
1191 | | |
1192 | | // TODO(oysteine): Just replacing the filter means that if |
1193 | | // there are any filter entries which were present in the original config, |
1194 | | // but removed from the config passed to ChangeTraceConfig, any matching |
1195 | | // producers will keep producing but newly added producers after this |
1196 | | // point will never start. |
1197 | 0 | *cfg_data_source.mutable_producer_name_filter() = new_producer_name_filter; |
1198 | 0 | *cfg_data_source.mutable_producer_name_regex_filter() = |
1199 | 0 | new_producer_name_regex_filter; |
1200 | | |
1201 | | // Get the list of producers that are already set up. |
1202 | 0 | std::unordered_set<uint16_t> set_up_producers; |
1203 | 0 | auto& ds_instances = tracing_session->data_source_instances; |
1204 | 0 | for (auto instance_it = ds_instances.begin(); |
1205 | 0 | instance_it != ds_instances.end(); ++instance_it) { |
1206 | 0 | set_up_producers.insert(instance_it->first); |
1207 | 0 | } |
1208 | | |
1209 | | // Scan all the registered data sources with a matching name. |
1210 | 0 | auto range = data_sources_.equal_range(cfg_data_source.config().name()); |
1211 | 0 | for (auto it = range.first; it != range.second; it++) { |
1212 | 0 | ProducerEndpointImpl* producer = GetProducer(it->second.producer_id); |
1213 | 0 | PERFETTO_DCHECK(producer); |
1214 | | |
1215 | | // Check if the producer name of this data source is present |
1216 | | // in the name filters. We currently only support new filters, not |
1217 | | // removing old ones. |
1218 | 0 | if (!NameMatchesFilter(producer->name_, new_producer_name_filter, |
1219 | 0 | new_producer_name_regex_filter)) { |
1220 | 0 | continue; |
1221 | 0 | } |
1222 | | |
1223 | | // If this producer is already set up, we assume that all datasources |
1224 | | // in it started already. |
1225 | 0 | if (set_up_producers.count(it->second.producer_id)) |
1226 | 0 | continue; |
1227 | | |
1228 | | // If it wasn't previously setup, set it up now. |
1229 | | // (The per-producer config is optional). |
1230 | 0 | TraceConfig::ProducerConfig producer_config; |
1231 | 0 | for (const auto& config : tracing_session->config.producers()) { |
1232 | 0 | if (producer->name_ == config.producer_name()) { |
1233 | 0 | producer_config = config; |
1234 | 0 | break; |
1235 | 0 | } |
1236 | 0 | } |
1237 | |
|
1238 | 0 | DataSourceInstance* ds_inst = SetupDataSource( |
1239 | 0 | cfg_data_source, producer_config, it->second, tracing_session); |
1240 | |
|
1241 | 0 | if (ds_inst && tracing_session->state == TracingSession::STARTED) |
1242 | 0 | StartDataSourceInstance(producer, tracing_session, ds_inst); |
1243 | 0 | } |
1244 | 0 | } |
1245 | 0 | } |
1246 | | |
1247 | | uint32_t TracingServiceImpl::DelayToNextWritePeriodMs( |
1248 | 0 | const TracingSession& session) { |
1249 | 0 | PERFETTO_DCHECK(session.write_period_ms > 0); |
1250 | 0 | return session.write_period_ms - |
1251 | 0 | static_cast<uint32_t>(clock_->GetWallTimeMs().count() % |
1252 | 0 | session.write_period_ms); |
1253 | 0 | } |
1254 | | |
1255 | 300 | void TracingServiceImpl::StartTracing(TracingSessionID tsid) { |
1256 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
1257 | | |
1258 | 300 | TracingSession* tracing_session = GetTracingSession(tsid); |
1259 | 300 | if (!tracing_session) { |
1260 | 0 | PERFETTO_ELOG("StartTracing() failed, invalid session ID %" PRIu64, tsid); |
1261 | 0 | return; |
1262 | 0 | } |
1263 | | |
1264 | 300 | MaybeLogUploadEvent(tracing_session->config, tracing_session->trace_uuid, |
1265 | 300 | PerfettoStatsdAtom::kTracedStartTracing); |
1266 | | |
1267 | 300 | if (tracing_session->state != TracingSession::CONFIGURED) { |
1268 | 0 | MaybeLogUploadEvent( |
1269 | 0 | tracing_session->config, tracing_session->trace_uuid, |
1270 | 0 | PerfettoStatsdAtom::kTracedStartTracingInvalidSessionState); |
1271 | 0 | PERFETTO_ELOG("StartTracing() failed, invalid session state: %d", |
1272 | 0 | tracing_session->state); |
1273 | 0 | return; |
1274 | 0 | } |
1275 | | |
1276 | 300 | tracing_session->state = TracingSession::STARTED; |
1277 | | |
1278 | | // We store the start of trace snapshot separately as it's important to make |
1279 | | // sure we can interpret all the data in the trace and storing it in the ring |
1280 | | // buffer means it could be overwritten by a later snapshot. |
1281 | 300 | if (!tracing_session->config.builtin_data_sources() |
1282 | 300 | .disable_clock_snapshotting()) { |
1283 | 300 | SnapshotClocks(&tracing_session->initial_clock_snapshot); |
1284 | 300 | } |
1285 | | |
1286 | | // We don't snapshot the clocks here because we just did this above. |
1287 | 300 | SnapshotLifecycleEvent( |
1288 | 300 | tracing_session, |
1289 | 300 | protos::pbzero::TracingServiceEvent::kTracingStartedFieldNumber, |
1290 | 300 | false /* snapshot_clocks */); |
1291 | | |
1292 | | // Periodically snapshot clocks, stats, sync markers while the trace is |
1293 | | // active. The snapshots are emitted on the future ReadBuffers() calls, which |
1294 | | // means that: |
1295 | | // (a) If we're streaming to a file (or to a consumer) while tracing, we |
1296 | | // write snapshots periodically into the trace. |
1297 | | // (b) If ReadBuffers() is only called after tracing ends, we emit the latest |
1298 | | // snapshot into the trace. For clock snapshots, we keep track of the |
1299 | | // snapshot recorded at the beginning of the session |
1300 | | // (initial_clock_snapshot above), as well as the most recent sampled |
1301 | | // snapshots that showed significant new drift between different clocks. |
1302 | | // The latter clock snapshots are sampled periodically and at lifecycle |
1303 | | // events. |
1304 | 300 | base::PeriodicTask::Args snapshot_task_args; |
1305 | 300 | snapshot_task_args.start_first_task_immediately = true; |
1306 | 300 | snapshot_task_args.use_suspend_aware_timer = |
1307 | 300 | tracing_session->config.builtin_data_sources() |
1308 | 300 | .prefer_suspend_clock_for_snapshot(); |
1309 | 300 | snapshot_task_args.task = [this, tsid] { PeriodicSnapshotTask(tsid); }; |
1310 | 300 | snapshot_task_args.period_ms = |
1311 | 300 | tracing_session->config.builtin_data_sources().snapshot_interval_ms(); |
1312 | 300 | if (!snapshot_task_args.period_ms) |
1313 | 300 | snapshot_task_args.period_ms = kDefaultSnapshotsIntervalMs; |
1314 | 300 | tracing_session->snapshot_periodic_task.Start(snapshot_task_args); |
1315 | | |
1316 | | // Trigger delayed task if the trace is time limited. |
1317 | 300 | const uint32_t trace_duration_ms = tracing_session->config.duration_ms(); |
1318 | 300 | if (trace_duration_ms > 0) { |
1319 | 0 | auto stop_task = |
1320 | 0 | std::bind(&TracingServiceImpl::StopOnDurationMsExpiry, this, tsid); |
1321 | 0 | if (tracing_session->config.prefer_suspend_clock_for_duration()) { |
1322 | 0 | base::PeriodicTask::Args stop_args; |
1323 | 0 | stop_args.use_suspend_aware_timer = true; |
1324 | 0 | stop_args.period_ms = trace_duration_ms; |
1325 | 0 | stop_args.one_shot = true; |
1326 | 0 | stop_args.task = std::move(stop_task); |
1327 | 0 | tracing_session->timed_stop_task.Start(stop_args); |
1328 | 0 | } else { |
1329 | 0 | weak_runner_.PostDelayedTask(std::move(stop_task), trace_duration_ms); |
1330 | 0 | } |
1331 | 0 | } // if (trace_duration_ms > 0). |
1332 | | |
1333 | | // Start the periodic drain tasks if we should to save the trace into a file. |
1334 | 300 | if (tracing_session->config.write_into_file()) { |
1335 | 0 | weak_runner_.PostDelayedTask([this, tsid] { ReadBuffersIntoFile(tsid); }, |
1336 | 0 | DelayToNextWritePeriodMs(*tracing_session)); |
1337 | 0 | } |
1338 | | |
1339 | | // Start the periodic flush tasks if the config specified a flush period. |
1340 | 300 | if (tracing_session->config.flush_period_ms()) |
1341 | 0 | PeriodicFlushTask(tsid, /*post_next_only=*/true); |
1342 | | |
1343 | | // Start the periodic incremental state clear tasks if the config specified a |
1344 | | // period. |
1345 | 300 | if (tracing_session->config.incremental_state_config().clear_period_ms()) { |
1346 | 0 | PeriodicClearIncrementalStateTask(tsid, /*post_next_only=*/true); |
1347 | 0 | } |
1348 | | |
1349 | 300 | for (auto& [prod_id, data_source] : tracing_session->data_source_instances) { |
1350 | 87 | ProducerEndpointImpl* producer = GetProducer(prod_id); |
1351 | 87 | if (!producer) { |
1352 | 0 | PERFETTO_DFATAL("Producer does not exist."); |
1353 | 0 | continue; |
1354 | 0 | } |
1355 | 87 | StartDataSourceInstance(producer, tracing_session, &data_source); |
1356 | 87 | } |
1357 | | |
1358 | 300 | MaybeNotifyAllDataSourcesStarted(tracing_session); |
1359 | | |
1360 | | // `did_notify_all_data_source_started` is only set if a consumer is |
1361 | | // connected. |
1362 | 300 | if (tracing_session->consumer_maybe_null) { |
1363 | 300 | weak_runner_.PostDelayedTask( |
1364 | 300 | [this, tsid] { OnAllDataSourceStartedTimeout(tsid); }, |
1365 | 300 | kAllDataSourceStartedTimeout); |
1366 | 300 | } |
1367 | 300 | } |
1368 | | |
1369 | 0 | void TracingServiceImpl::StopOnDurationMsExpiry(TracingSessionID tsid) { |
1370 | 0 | auto* tracing_session_ptr = GetTracingSession(tsid); |
1371 | 0 | if (!tracing_session_ptr) |
1372 | 0 | return; |
1373 | | // If this trace was using STOP_TRACING triggers and we've seen |
1374 | | // one, then the trigger overrides the normal timeout. In this |
1375 | | // case we just return and let the other task clean up this trace. |
1376 | 0 | if (GetTriggerMode(tracing_session_ptr->config) == |
1377 | 0 | TraceConfig::TriggerConfig::STOP_TRACING && |
1378 | 0 | !tracing_session_ptr->received_triggers.empty()) |
1379 | 0 | return; |
1380 | | // In all other cases (START_TRACING or no triggers) we flush |
1381 | | // after |trace_duration_ms| unconditionally. |
1382 | 0 | FlushAndDisableTracing(tsid); |
1383 | 0 | } |
1384 | | |
1385 | | void TracingServiceImpl::StartDataSourceInstance( |
1386 | | ProducerEndpointImpl* producer, |
1387 | | TracingSession* tracing_session, |
1388 | 300 | TracingServiceImpl::DataSourceInstance* instance) { |
1389 | 300 | PERFETTO_DCHECK(instance->state == DataSourceInstance::CONFIGURED); |
1390 | | |
1391 | 300 | bool start_immediately = !instance->will_notify_on_start; |
1392 | | |
1393 | 300 | if (producer->IsAndroidProcessFrozen()) { |
1394 | 0 | PERFETTO_DLOG( |
1395 | 0 | "skipping waiting of data source \"%s\" on producer \"%s\" (pid=%u) " |
1396 | 0 | "because it is frozen", |
1397 | 0 | instance->data_source_name.c_str(), producer->name_.c_str(), |
1398 | 0 | producer->pid()); |
1399 | 0 | start_immediately = true; |
1400 | 0 | } |
1401 | | |
1402 | 300 | if (!start_immediately) { |
1403 | 0 | instance->state = DataSourceInstance::STARTING; |
1404 | 300 | } else { |
1405 | 300 | instance->state = DataSourceInstance::STARTED; |
1406 | 300 | } |
1407 | 300 | if (tracing_session->consumer_maybe_null) { |
1408 | 300 | tracing_session->consumer_maybe_null->OnDataSourceInstanceStateChange( |
1409 | 300 | *producer, *instance); |
1410 | 300 | } |
1411 | 300 | producer->StartDataSource(instance->instance_id, instance->config); |
1412 | | |
1413 | | // If all data sources are started, notify the consumer. |
1414 | 300 | if (instance->state == DataSourceInstance::STARTED) |
1415 | 300 | MaybeNotifyAllDataSourcesStarted(tracing_session); |
1416 | 300 | } |
1417 | | |
1418 | | // DisableTracing just stops the data sources but doesn't free up any buffer. |
1419 | | // This is to allow the consumer to freeze the buffers (by stopping the trace) |
1420 | | // and then drain the buffers. The actual teardown of the TracingSession happens |
1421 | | // in FreeBuffers(). |
1422 | | void TracingServiceImpl::DisableTracing(TracingSessionID tsid, |
1423 | 300 | bool disable_immediately) { |
1424 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
1425 | 300 | TracingSession* tracing_session = GetTracingSession(tsid); |
1426 | 300 | if (!tracing_session) { |
1427 | | // Can happen if the consumer calls this before EnableTracing() or after |
1428 | | // FreeBuffers(). |
1429 | 0 | PERFETTO_DLOG("DisableTracing() failed, invalid session ID %" PRIu64, tsid); |
1430 | 0 | return; |
1431 | 0 | } |
1432 | | |
1433 | 300 | MaybeLogUploadEvent(tracing_session->config, tracing_session->trace_uuid, |
1434 | 300 | PerfettoStatsdAtom::kTracedDisableTracing); |
1435 | | |
1436 | 300 | switch (tracing_session->state) { |
1437 | | // Spurious call to DisableTracing() while already disabled, nothing to do. |
1438 | 0 | case TracingSession::DISABLED: |
1439 | 0 | PERFETTO_DCHECK(tracing_session->AllDataSourceInstancesStopped()); |
1440 | 0 | return; |
1441 | | |
1442 | 0 | case TracingSession::CLONED_READ_ONLY: |
1443 | 0 | return; |
1444 | | |
1445 | | // This is either: |
1446 | | // A) The case of a graceful DisableTracing() call followed by a call to |
1447 | | // FreeBuffers(), iff |disable_immediately| == true. In this case we want |
1448 | | // to forcefully transition in the disabled state without waiting for the |
1449 | | // outstanding acks because the buffers are going to be destroyed soon. |
1450 | | // B) A spurious call, iff |disable_immediately| == false, in which case |
1451 | | // there is nothing to do. |
1452 | 0 | case TracingSession::DISABLING_WAITING_STOP_ACKS: |
1453 | 0 | PERFETTO_DCHECK(!tracing_session->AllDataSourceInstancesStopped()); |
1454 | 0 | if (disable_immediately) |
1455 | 0 | DisableTracingNotifyConsumerAndFlushFile(tracing_session); |
1456 | 0 | return; |
1457 | | |
1458 | | // Continues below. |
1459 | 0 | case TracingSession::CONFIGURED: |
1460 | | // If the session didn't even start there is no need to orchestrate a |
1461 | | // graceful stop of data sources. |
1462 | 0 | disable_immediately = true; |
1463 | 0 | break; |
1464 | | |
1465 | | // This is the nominal case, continues below. |
1466 | 300 | case TracingSession::STARTED: |
1467 | 300 | break; |
1468 | 300 | } |
1469 | | |
1470 | 300 | for (auto& data_source_inst : tracing_session->data_source_instances) { |
1471 | 0 | const ProducerID producer_id = data_source_inst.first; |
1472 | 0 | DataSourceInstance& instance = data_source_inst.second; |
1473 | 0 | ProducerEndpointImpl* producer = GetProducer(producer_id); |
1474 | 0 | PERFETTO_DCHECK(producer); |
1475 | 0 | PERFETTO_DCHECK(instance.state == DataSourceInstance::CONFIGURED || |
1476 | 0 | instance.state == DataSourceInstance::STARTING || |
1477 | 0 | instance.state == DataSourceInstance::STARTED); |
1478 | 0 | StopDataSourceInstance(producer, tracing_session, &instance, |
1479 | 0 | disable_immediately); |
1480 | 0 | } |
1481 | | |
1482 | | // If the periodic task is running, we can stop the periodic snapshot timer |
1483 | | // here instead of waiting until FreeBuffers to prevent useless snapshots |
1484 | | // which won't be read. |
1485 | 300 | tracing_session->snapshot_periodic_task.Reset(); |
1486 | | |
1487 | | // Either this request is flagged with |disable_immediately| or there are no |
1488 | | // data sources that are requesting a final handshake. In both cases just mark |
1489 | | // the session as disabled immediately, notify the consumer and flush the |
1490 | | // trace file (if used). |
1491 | 300 | if (tracing_session->AllDataSourceInstancesStopped()) |
1492 | 300 | return DisableTracingNotifyConsumerAndFlushFile(tracing_session); |
1493 | | |
1494 | 0 | tracing_session->state = TracingSession::DISABLING_WAITING_STOP_ACKS; |
1495 | 0 | weak_runner_.PostDelayedTask([this, tsid] { OnDisableTracingTimeout(tsid); }, |
1496 | 0 | tracing_session->data_source_stop_timeout_ms()); |
1497 | | |
1498 | | // Deliberately NOT removing the session from |tracing_session_|, it's still |
1499 | | // needed to call ReadBuffers(). FreeBuffers() will erase() the session. |
1500 | 0 | } |
1501 | | |
1502 | | void TracingServiceImpl::NotifyDataSourceStarted( |
1503 | | ProducerID producer_id, |
1504 | 0 | DataSourceInstanceID instance_id) { |
1505 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
1506 | 0 | for (auto& kv : tracing_sessions_) { |
1507 | 0 | TracingSession& tracing_session = kv.second; |
1508 | 0 | DataSourceInstance* instance = |
1509 | 0 | tracing_session.GetDataSourceInstance(producer_id, instance_id); |
1510 | |
|
1511 | 0 | if (!instance) |
1512 | 0 | continue; |
1513 | | |
1514 | | // If the tracing session was already stopped, ignore this notification. |
1515 | 0 | if (tracing_session.state != TracingSession::STARTED) |
1516 | 0 | continue; |
1517 | | |
1518 | 0 | if (instance->state != DataSourceInstance::STARTING) { |
1519 | 0 | PERFETTO_ELOG("Started data source instance in incorrect state: %d", |
1520 | 0 | instance->state); |
1521 | 0 | continue; |
1522 | 0 | } |
1523 | | |
1524 | 0 | instance->state = DataSourceInstance::STARTED; |
1525 | |
|
1526 | 0 | ProducerEndpointImpl* producer = GetProducer(producer_id); |
1527 | 0 | PERFETTO_DCHECK(producer); |
1528 | 0 | if (tracing_session.consumer_maybe_null) { |
1529 | 0 | tracing_session.consumer_maybe_null->OnDataSourceInstanceStateChange( |
1530 | 0 | *producer, *instance); |
1531 | 0 | } |
1532 | | |
1533 | | // If all data sources are started, notify the consumer. |
1534 | 0 | MaybeNotifyAllDataSourcesStarted(&tracing_session); |
1535 | 0 | } // for (tracing_session) |
1536 | 0 | } |
1537 | | |
1538 | 0 | void TracingServiceImpl::OnAllDataSourceStartedTimeout(TracingSessionID tsid) { |
1539 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
1540 | 0 | TracingSession* tracing_session = GetTracingSession(tsid); |
1541 | | // It would be possible to check for `AllDataSourceInstancesStarted()` here, |
1542 | | // but it doesn't make much sense, because a data source can be registered |
1543 | | // after the session has started. Therefore this is tied to |
1544 | | // `did_notify_all_data_source_started`: if that notification happened, do not |
1545 | | // record slow data sources. |
1546 | 0 | if (!tracing_session || !tracing_session->consumer_maybe_null || |
1547 | 0 | tracing_session->did_notify_all_data_source_started) { |
1548 | 0 | return; |
1549 | 0 | } |
1550 | | |
1551 | 0 | int64_t timestamp = clock_->GetBootTimeNs().count(); |
1552 | |
|
1553 | 0 | protozero::HeapBuffered<protos::pbzero::TracePacket> packet; |
1554 | 0 | packet->set_timestamp(static_cast<uint64_t>(timestamp)); |
1555 | 0 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
1556 | 0 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
1557 | |
|
1558 | 0 | size_t i = 0; |
1559 | 0 | protos::pbzero::TracingServiceEvent::DataSources* slow_data_sources = |
1560 | 0 | packet->set_service_event()->set_slow_starting_data_sources(); |
1561 | 0 | for (const auto& [producer_id, ds_instance] : |
1562 | 0 | tracing_session->data_source_instances) { |
1563 | 0 | if (ds_instance.state == DataSourceInstance::STARTED) { |
1564 | 0 | continue; |
1565 | 0 | } |
1566 | 0 | ProducerEndpointImpl* producer = GetProducer(producer_id); |
1567 | 0 | if (!producer) { |
1568 | 0 | continue; |
1569 | 0 | } |
1570 | 0 | if (++i > kMaxLifecycleEventsListedDataSources) { |
1571 | 0 | break; |
1572 | 0 | } |
1573 | 0 | auto* ds = slow_data_sources->add_data_source(); |
1574 | 0 | ds->set_producer_name(producer->name_); |
1575 | 0 | ds->set_data_source_name(ds_instance.data_source_name); |
1576 | 0 | PERFETTO_LOG( |
1577 | 0 | "Data source failed to start within 20s data_source=\"%s\", " |
1578 | 0 | "producer=\"%s\", tsid=%" PRIu64, |
1579 | 0 | ds_instance.data_source_name.c_str(), producer->name_.c_str(), tsid); |
1580 | 0 | } |
1581 | |
|
1582 | 0 | tracing_session->slow_start_event = TracingSession::ArbitraryLifecycleEvent{ |
1583 | 0 | timestamp, packet.SerializeAsArray()}; |
1584 | 0 | } |
1585 | | |
1586 | | void TracingServiceImpl::MaybeNotifyAllDataSourcesStarted( |
1587 | 900 | TracingSession* tracing_session) { |
1588 | 900 | if (!tracing_session->consumer_maybe_null) |
1589 | 0 | return; |
1590 | | |
1591 | 900 | if (!tracing_session->AllDataSourceInstancesStarted()) |
1592 | 0 | return; |
1593 | | |
1594 | | // In some rare cases, we can get in this state more than once. Consider the |
1595 | | // following scenario: 3 data sources are registered -> trace starts -> |
1596 | | // all 3 data sources ack -> OnAllDataSourcesStarted() is called. |
1597 | | // Imagine now that a 4th data source registers while the trace is ongoing. |
1598 | | // This would hit the AllDataSourceInstancesStarted() condition again. |
1599 | | // In this case, however, we don't want to re-notify the consumer again. |
1600 | | // That would be unexpected (even if, perhaps, technically correct) and |
1601 | | // trigger bugs in the consumer. |
1602 | 900 | if (tracing_session->did_notify_all_data_source_started) |
1603 | 600 | return; |
1604 | | |
1605 | 900 | PERFETTO_DLOG("All data sources started"); |
1606 | | |
1607 | 300 | SnapshotLifecycleEvent( |
1608 | 300 | tracing_session, |
1609 | 300 | protos::pbzero::TracingServiceEvent::kAllDataSourcesStartedFieldNumber, |
1610 | 300 | true /* snapshot_clocks */); |
1611 | | |
1612 | 300 | tracing_session->did_notify_all_data_source_started = true; |
1613 | 300 | tracing_session->consumer_maybe_null->OnAllDataSourcesStarted(); |
1614 | 300 | } |
1615 | | |
1616 | | void TracingServiceImpl::NotifyDataSourceStopped( |
1617 | | ProducerID producer_id, |
1618 | 0 | DataSourceInstanceID instance_id) { |
1619 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
1620 | 0 | for (auto& kv : tracing_sessions_) { |
1621 | 0 | TracingSession& tracing_session = kv.second; |
1622 | 0 | DataSourceInstance* instance = |
1623 | 0 | tracing_session.GetDataSourceInstance(producer_id, instance_id); |
1624 | |
|
1625 | 0 | if (!instance) |
1626 | 0 | continue; |
1627 | | |
1628 | 0 | if (instance->state != DataSourceInstance::STOPPING) { |
1629 | 0 | PERFETTO_ELOG("Stopped data source instance in incorrect state: %d", |
1630 | 0 | instance->state); |
1631 | 0 | continue; |
1632 | 0 | } |
1633 | | |
1634 | 0 | instance->state = DataSourceInstance::STOPPED; |
1635 | |
|
1636 | 0 | ProducerEndpointImpl* producer = GetProducer(producer_id); |
1637 | 0 | PERFETTO_DCHECK(producer); |
1638 | 0 | if (tracing_session.consumer_maybe_null) { |
1639 | 0 | tracing_session.consumer_maybe_null->OnDataSourceInstanceStateChange( |
1640 | 0 | *producer, *instance); |
1641 | 0 | } |
1642 | |
|
1643 | 0 | if (!tracing_session.AllDataSourceInstancesStopped()) |
1644 | 0 | continue; |
1645 | | |
1646 | 0 | if (tracing_session.state != TracingSession::DISABLING_WAITING_STOP_ACKS) |
1647 | 0 | continue; |
1648 | | |
1649 | | // All data sources acked the termination. |
1650 | 0 | DisableTracingNotifyConsumerAndFlushFile(&tracing_session); |
1651 | 0 | } // for (tracing_session) |
1652 | 0 | } |
1653 | | |
1654 | | void TracingServiceImpl::ActivateTriggers( |
1655 | | ProducerID producer_id, |
1656 | 0 | const std::vector<std::string>& triggers) { |
1657 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
1658 | 0 | auto* producer = GetProducer(producer_id); |
1659 | 0 | PERFETTO_DCHECK(producer); |
1660 | |
|
1661 | 0 | int64_t now_ns = clock_->GetBootTimeNs().count(); |
1662 | 0 | for (const auto& trigger_name : triggers) { |
1663 | 0 | PERFETTO_DLOG("Received ActivateTriggers request for \"%s\"", |
1664 | 0 | trigger_name.c_str()); |
1665 | 0 | android_stats::MaybeLogTriggerEvent(PerfettoTriggerAtom::kTracedTrigger, |
1666 | 0 | trigger_name); |
1667 | |
|
1668 | 0 | base::Hasher hash; |
1669 | 0 | hash.Update(trigger_name.c_str(), trigger_name.size()); |
1670 | 0 | std::string triggered_session_name; |
1671 | 0 | base::Uuid triggered_session_uuid; |
1672 | 0 | TracingSessionID triggered_session_id = 0; |
1673 | 0 | auto trigger_mode = TraceConfig::TriggerConfig::UNSPECIFIED; |
1674 | |
|
1675 | 0 | uint64_t trigger_name_hash = hash.digest(); |
1676 | 0 | size_t count_in_window = |
1677 | 0 | PurgeExpiredAndCountTriggerInWindow(now_ns, trigger_name_hash); |
1678 | |
|
1679 | 0 | bool trigger_matched = false; |
1680 | 0 | bool trigger_activated = false; |
1681 | 0 | for (auto& id_and_tracing_session : tracing_sessions_) { |
1682 | 0 | auto& tracing_session = id_and_tracing_session.second; |
1683 | 0 | TracingSessionID tsid = id_and_tracing_session.first; |
1684 | 0 | auto iter = std::find_if( |
1685 | 0 | tracing_session.config.trigger_config().triggers().begin(), |
1686 | 0 | tracing_session.config.trigger_config().triggers().end(), |
1687 | 0 | [&trigger_name](const TraceConfig::TriggerConfig::Trigger& trigger) { |
1688 | 0 | return trigger.name() == trigger_name; |
1689 | 0 | }); |
1690 | 0 | if (iter == tracing_session.config.trigger_config().triggers().end()) |
1691 | 0 | continue; |
1692 | 0 | if (tracing_session.state == TracingSession::CLONED_READ_ONLY) |
1693 | 0 | continue; |
1694 | | |
1695 | | // If this trigger requires a certain producer to have sent it |
1696 | | // (non-empty producer_name()) ensure the producer who sent this trigger |
1697 | | // matches. |
1698 | 0 | if (!iter->producer_name_regex().empty() && |
1699 | 0 | !std::regex_match( |
1700 | 0 | producer->name_, |
1701 | 0 | std::regex(iter->producer_name_regex(), std::regex::extended))) { |
1702 | 0 | continue; |
1703 | 0 | } |
1704 | | |
1705 | | // Use a random number between 0 and 1 to check if we should allow this |
1706 | | // trigger through or not. |
1707 | 0 | double trigger_rnd = random_->GetValue(); |
1708 | 0 | PERFETTO_DCHECK(trigger_rnd >= 0 && trigger_rnd < 1); |
1709 | 0 | if (trigger_rnd < iter->skip_probability()) { |
1710 | 0 | MaybeLogTriggerEvent(tracing_session.config, |
1711 | 0 | PerfettoTriggerAtom::kTracedLimitProbability, |
1712 | 0 | trigger_name); |
1713 | 0 | continue; |
1714 | 0 | } |
1715 | | |
1716 | | // If we already triggered more times than the limit, silently ignore |
1717 | | // this trigger. |
1718 | 0 | if (iter->max_per_24_h() > 0 && count_in_window >= iter->max_per_24_h()) { |
1719 | 0 | MaybeLogTriggerEvent(tracing_session.config, |
1720 | 0 | PerfettoTriggerAtom::kTracedLimitMaxPer24h, |
1721 | 0 | trigger_name); |
1722 | 0 | continue; |
1723 | 0 | } |
1724 | 0 | trigger_matched = true; |
1725 | 0 | triggered_session_id = tracing_session.id; |
1726 | 0 | triggered_session_name = tracing_session.config.unique_session_name(); |
1727 | 0 | triggered_session_uuid.set_lsb_msb(tracing_session.trace_uuid.lsb(), |
1728 | 0 | tracing_session.trace_uuid.msb()); |
1729 | 0 | trigger_mode = GetTriggerMode(tracing_session.config); |
1730 | |
|
1731 | 0 | const bool triggers_already_received = |
1732 | 0 | !tracing_session.received_triggers.empty(); |
1733 | 0 | const TriggerInfo trigger = {static_cast<uint64_t>(now_ns), iter->name(), |
1734 | 0 | producer->name_, producer->uid()}; |
1735 | 0 | tracing_session.received_triggers.push_back(trigger); |
1736 | 0 | switch (trigger_mode) { |
1737 | 0 | case TraceConfig::TriggerConfig::START_TRACING: |
1738 | | // If the session has already been triggered and moved past |
1739 | | // CONFIGURED then we don't need to repeat StartTracing. This would |
1740 | | // work fine (StartTracing would return false) but would add error |
1741 | | // logs. |
1742 | 0 | if (tracing_session.state != TracingSession::CONFIGURED) |
1743 | 0 | break; |
1744 | | |
1745 | 0 | trigger_activated = true; |
1746 | 0 | MaybeLogUploadEvent( |
1747 | 0 | tracing_session.config, tracing_session.trace_uuid, |
1748 | 0 | PerfettoStatsdAtom::kTracedTriggerStartTracing, iter->name()); |
1749 | | |
1750 | | // We override the trace duration to be the trigger's requested |
1751 | | // value, this ensures that the trace will end after this amount |
1752 | | // of time has passed. |
1753 | 0 | tracing_session.config.set_duration_ms(iter->stop_delay_ms()); |
1754 | 0 | StartTracing(tsid); |
1755 | 0 | break; |
1756 | 0 | case TraceConfig::TriggerConfig::STOP_TRACING: |
1757 | | // Only stop the trace once to avoid confusing log messages. I.E. |
1758 | | // when we've already hit the first trigger we've already Posted the |
1759 | | // task to FlushAndDisable. So all future triggers will just break |
1760 | | // out. |
1761 | 0 | if (triggers_already_received) |
1762 | 0 | break; |
1763 | | |
1764 | 0 | trigger_activated = true; |
1765 | 0 | MaybeLogUploadEvent( |
1766 | 0 | tracing_session.config, tracing_session.trace_uuid, |
1767 | 0 | PerfettoStatsdAtom::kTracedTriggerStopTracing, iter->name()); |
1768 | | |
1769 | | // Now that we've seen a trigger we need to stop, flush, and disable |
1770 | | // this session after the configured |stop_delay_ms|. |
1771 | 0 | weak_runner_.PostDelayedTask( |
1772 | 0 | [this, tsid] { |
1773 | | // Skip entirely the flush if the trace session doesn't exist |
1774 | | // anymore. This is to prevent misleading error messages to be |
1775 | | // logged. |
1776 | 0 | if (GetTracingSession(tsid)) |
1777 | 0 | FlushAndDisableTracing(tsid); |
1778 | 0 | }, |
1779 | | // If this trigger is zero this will immediately executable and |
1780 | | // will happen shortly. |
1781 | 0 | iter->stop_delay_ms()); |
1782 | 0 | break; |
1783 | | |
1784 | 0 | case TraceConfig::TriggerConfig::CLONE_SNAPSHOT: |
1785 | 0 | trigger_activated = true; |
1786 | 0 | MaybeLogUploadEvent( |
1787 | 0 | tracing_session.config, tracing_session.trace_uuid, |
1788 | 0 | PerfettoStatsdAtom::kTracedTriggerCloneSnapshot, iter->name()); |
1789 | 0 | weak_runner_.PostDelayedTask( |
1790 | 0 | [this, tsid, trigger] { |
1791 | 0 | auto* tsess = GetTracingSession(tsid); |
1792 | 0 | if (!tsess || !tsess->consumer_maybe_null) |
1793 | 0 | return; |
1794 | 0 | tsess->consumer_maybe_null->NotifyCloneSnapshotTrigger(trigger); |
1795 | 0 | }, |
1796 | 0 | iter->stop_delay_ms()); |
1797 | 0 | break; |
1798 | | |
1799 | 0 | case TraceConfig::TriggerConfig::UNSPECIFIED: |
1800 | 0 | PERFETTO_ELOG("Trigger activated but trigger mode unspecified."); |
1801 | 0 | break; |
1802 | 0 | } |
1803 | 0 | } // for (.. : tracing_sessions_) |
1804 | | |
1805 | 0 | if (trigger_matched) { |
1806 | 0 | trigger_history_.emplace_back(TriggerHistory{now_ns, trigger_name_hash}); |
1807 | 0 | } |
1808 | |
|
1809 | 0 | if (trigger_activated) { |
1810 | | // Log only the trigger that actually caused a trace stop/start, don't log |
1811 | | // the follow-up ones, even if they matched. |
1812 | 0 | PERFETTO_LOG( |
1813 | 0 | "Trace trigger activated: trigger_name=\"%s\" trigger_mode=%d " |
1814 | 0 | "trace_name=\"%s\" trace_uuid=\"%s\" tsid=%" PRIu64, |
1815 | 0 | trigger_name.c_str(), trigger_mode, triggered_session_name.c_str(), |
1816 | 0 | triggered_session_uuid.ToPrettyString().c_str(), |
1817 | 0 | triggered_session_id); |
1818 | 0 | } |
1819 | 0 | } // for (trigger_name : triggers) |
1820 | 0 | } |
1821 | | |
1822 | | // Always invoked TraceConfig.data_source_stop_timeout_ms (by default |
1823 | | // kDataSourceStopTimeoutMs) after DisableTracing(). In nominal conditions all |
1824 | | // data sources should have acked the stop and this will early out. |
1825 | 0 | void TracingServiceImpl::OnDisableTracingTimeout(TracingSessionID tsid) { |
1826 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
1827 | 0 | TracingSession* tracing_session = GetTracingSession(tsid); |
1828 | 0 | if (!tracing_session || |
1829 | 0 | tracing_session->state != TracingSession::DISABLING_WAITING_STOP_ACKS) { |
1830 | 0 | return; // Tracing session was successfully disabled. |
1831 | 0 | } |
1832 | | |
1833 | 0 | PERFETTO_ILOG("Timeout while waiting for ACKs for tracing session %" PRIu64, |
1834 | 0 | tsid); |
1835 | 0 | PERFETTO_DCHECK(!tracing_session->AllDataSourceInstancesStopped()); |
1836 | 0 | DisableTracingNotifyConsumerAndFlushFile(tracing_session); |
1837 | 0 | } |
1838 | | |
1839 | | void TracingServiceImpl::DisableTracingNotifyConsumerAndFlushFile( |
1840 | 300 | TracingSession* tracing_session) { |
1841 | 300 | PERFETTO_DCHECK(tracing_session->state != TracingSession::DISABLED); |
1842 | 300 | for (auto& inst_kv : tracing_session->data_source_instances) { |
1843 | 0 | if (inst_kv.second.state == DataSourceInstance::STOPPED) |
1844 | 0 | continue; |
1845 | 0 | inst_kv.second.state = DataSourceInstance::STOPPED; |
1846 | 0 | ProducerEndpointImpl* producer = GetProducer(inst_kv.first); |
1847 | 0 | PERFETTO_DCHECK(producer); |
1848 | 0 | if (tracing_session->consumer_maybe_null) { |
1849 | 0 | tracing_session->consumer_maybe_null->OnDataSourceInstanceStateChange( |
1850 | 0 | *producer, inst_kv.second); |
1851 | 0 | } |
1852 | 0 | } |
1853 | 300 | tracing_session->state = TracingSession::DISABLED; |
1854 | | |
1855 | | // Scrape any remaining chunks that weren't flushed by the producers. |
1856 | 300 | for (auto& producer_id_and_producer : producers_) |
1857 | 0 | ScrapeSharedMemoryBuffers(tracing_session, producer_id_and_producer.second); |
1858 | | |
1859 | 300 | SnapshotLifecycleEvent( |
1860 | 300 | tracing_session, |
1861 | 300 | protos::pbzero::TracingServiceEvent::kTracingDisabledFieldNumber, |
1862 | 300 | true /* snapshot_clocks */); |
1863 | | |
1864 | 300 | if (tracing_session->write_into_file) { |
1865 | 0 | tracing_session->write_period_ms = 0; |
1866 | 0 | ReadBuffersIntoFile(tracing_session->id); |
1867 | 0 | } |
1868 | | |
1869 | 300 | MaybeLogUploadEvent(tracing_session->config, tracing_session->trace_uuid, |
1870 | 300 | PerfettoStatsdAtom::kTracedNotifyTracingDisabled); |
1871 | | |
1872 | 300 | if (tracing_session->consumer_maybe_null) |
1873 | 300 | tracing_session->consumer_maybe_null->NotifyOnTracingDisabled(""); |
1874 | 300 | } |
1875 | | |
1876 | | void TracingServiceImpl::Flush(TracingSessionID tsid, |
1877 | | uint32_t timeout_ms, |
1878 | | ConsumerEndpoint::FlushCallback callback, |
1879 | 0 | FlushFlags flush_flags) { |
1880 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
1881 | 0 | TracingSession* tracing_session = GetTracingSession(tsid); |
1882 | 0 | if (!tracing_session) { |
1883 | 0 | PERFETTO_DLOG("Flush() failed, invalid session ID %" PRIu64, tsid); |
1884 | 0 | return; |
1885 | 0 | } |
1886 | | |
1887 | 0 | SnapshotLifecycleEvent( |
1888 | 0 | tracing_session, |
1889 | 0 | protos::pbzero::TracingServiceEvent::kFlushStartedFieldNumber, |
1890 | 0 | false /* snapshot_clocks */); |
1891 | |
|
1892 | 0 | std::map<ProducerID, std::vector<DataSourceInstanceID>> data_source_instances; |
1893 | 0 | for (const auto& [producer_id, ds_inst] : |
1894 | 0 | tracing_session->data_source_instances) { |
1895 | 0 | if (ds_inst.no_flush) |
1896 | 0 | continue; |
1897 | 0 | data_source_instances[producer_id].push_back(ds_inst.instance_id); |
1898 | 0 | } |
1899 | 0 | FlushDataSourceInstances(tracing_session, timeout_ms, data_source_instances, |
1900 | 0 | std::move(callback), flush_flags); |
1901 | 0 | } |
1902 | | |
1903 | | void TracingServiceImpl::FlushDataSourceInstances( |
1904 | | TracingSession* tracing_session, |
1905 | | uint32_t timeout_ms, |
1906 | | const std::map<ProducerID, std::vector<DataSourceInstanceID>>& |
1907 | | data_source_instances, |
1908 | | ConsumerEndpoint::FlushCallback callback, |
1909 | 0 | FlushFlags flush_flags) { |
1910 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
1911 | 0 | if (!timeout_ms) |
1912 | 0 | timeout_ms = tracing_session->flush_timeout_ms(); |
1913 | |
|
1914 | 0 | if (tracing_session->pending_flushes.size() > 1000) { |
1915 | 0 | PERFETTO_ELOG("Too many flushes (%zu) pending for the tracing session", |
1916 | 0 | tracing_session->pending_flushes.size()); |
1917 | 0 | callback(false); |
1918 | 0 | return; |
1919 | 0 | } |
1920 | | |
1921 | 0 | if (tracing_session->state != TracingSession::STARTED) { |
1922 | 0 | PERFETTO_LOG("Flush() called, but tracing has not been started"); |
1923 | 0 | callback(false); |
1924 | 0 | return; |
1925 | 0 | } |
1926 | | |
1927 | 0 | tracing_session->last_flush_events.clear(); |
1928 | |
|
1929 | 0 | ++tracing_session->flushes_requested; |
1930 | 0 | FlushRequestID flush_request_id = ++last_flush_request_id_; |
1931 | 0 | PendingFlush& pending_flush = |
1932 | 0 | tracing_session->pending_flushes |
1933 | 0 | .emplace_hint(tracing_session->pending_flushes.end(), |
1934 | 0 | flush_request_id, PendingFlush(std::move(callback))) |
1935 | 0 | ->second; |
1936 | | |
1937 | | // Send a flush request to each producer involved in the tracing session. In |
1938 | | // order to issue a flush request we have to build a map of all data source |
1939 | | // instance ids enabled for each producer. |
1940 | |
|
1941 | 0 | for (const auto& [producer_id, data_sources] : data_source_instances) { |
1942 | 0 | ProducerEndpointImpl* producer = GetProducer(producer_id); |
1943 | 0 | producer->Flush(flush_request_id, data_sources, flush_flags); |
1944 | 0 | if (!producer->IsAndroidProcessFrozen()) { |
1945 | 0 | pending_flush.producers.insert(producer_id); |
1946 | 0 | } else { |
1947 | 0 | PERFETTO_DLOG( |
1948 | 0 | "skipping waiting flush for on producer \"%s\" (pid=%" PRIu32 |
1949 | 0 | ") because it is frozen", |
1950 | 0 | producer->name_.c_str(), static_cast<uint32_t>(producer->pid())); |
1951 | 0 | } |
1952 | 0 | } |
1953 | | |
1954 | | // If there are no producers to flush (realistically this happens only in |
1955 | | // some tests) fire OnFlushTimeout() straight away, without waiting. |
1956 | 0 | if (data_source_instances.empty()) |
1957 | 0 | timeout_ms = 0; |
1958 | |
|
1959 | 0 | weak_runner_.PostDelayedTask( |
1960 | 0 | [this, tsid = tracing_session->id, flush_request_id, flush_flags] { |
1961 | 0 | OnFlushTimeout(tsid, flush_request_id, flush_flags); |
1962 | 0 | }, |
1963 | 0 | timeout_ms); |
1964 | 0 | } |
1965 | | |
1966 | | void TracingServiceImpl::NotifyFlushDoneForProducer( |
1967 | | ProducerID producer_id, |
1968 | 0 | FlushRequestID flush_request_id) { |
1969 | 0 | for (auto& kv : tracing_sessions_) { |
1970 | | // Remove all pending flushes <= |flush_request_id| for |producer_id|. |
1971 | 0 | auto& pending_flushes = kv.second.pending_flushes; |
1972 | 0 | auto end_it = pending_flushes.upper_bound(flush_request_id); |
1973 | 0 | for (auto it = pending_flushes.begin(); it != end_it;) { |
1974 | 0 | PendingFlush& pending_flush = it->second; |
1975 | 0 | pending_flush.producers.erase(producer_id); |
1976 | 0 | if (pending_flush.producers.empty()) { |
1977 | 0 | TracingSessionID tsid = kv.first; |
1978 | 0 | auto callback = std::move(pending_flush.callback); |
1979 | 0 | weak_runner_.PostTask([this, tsid, callback = std::move(callback)]() { |
1980 | 0 | CompleteFlush(tsid, std::move(callback), |
1981 | 0 | /*success=*/true); |
1982 | 0 | }); |
1983 | 0 | it = pending_flushes.erase(it); |
1984 | 0 | } else { |
1985 | 0 | it++; |
1986 | 0 | } |
1987 | 0 | } // for (pending_flushes) |
1988 | 0 | } // for (tracing_session) |
1989 | 0 | } |
1990 | | |
1991 | | void TracingServiceImpl::OnFlushTimeout(TracingSessionID tsid, |
1992 | | FlushRequestID flush_request_id, |
1993 | 0 | FlushFlags flush_flags) { |
1994 | 0 | TracingSession* tracing_session = GetTracingSession(tsid); |
1995 | 0 | if (!tracing_session) |
1996 | 0 | return; |
1997 | 0 | auto it = tracing_session->pending_flushes.find(flush_request_id); |
1998 | 0 | if (it == tracing_session->pending_flushes.end()) |
1999 | 0 | return; // Nominal case: flush was completed and acked on time. |
2000 | | |
2001 | 0 | PendingFlush& pending_flush = it->second; |
2002 | | |
2003 | | // If there were no producers to flush, consider it a success. |
2004 | 0 | bool success = pending_flush.producers.empty(); |
2005 | 0 | auto callback = std::move(pending_flush.callback); |
2006 | | // If flush failed and this is a "final" flush, log which data sources were |
2007 | | // slow. |
2008 | 0 | if ((flush_flags.reason() == FlushFlags::Reason::kTraceClone || |
2009 | 0 | flush_flags.reason() == FlushFlags::Reason::kTraceStop) && |
2010 | 0 | !success) { |
2011 | 0 | int64_t timestamp = clock_->GetBootTimeNs().count(); |
2012 | |
|
2013 | 0 | protozero::HeapBuffered<protos::pbzero::TracePacket> packet; |
2014 | 0 | packet->set_timestamp(static_cast<uint64_t>(timestamp)); |
2015 | 0 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
2016 | 0 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
2017 | |
|
2018 | 0 | size_t i = 0; |
2019 | 0 | protos::pbzero::TracingServiceEvent::DataSources* event = |
2020 | 0 | packet->set_service_event()->set_last_flush_slow_data_sources(); |
2021 | 0 | for (const auto& producer_id : pending_flush.producers) { |
2022 | 0 | ProducerEndpointImpl* producer = GetProducer(producer_id); |
2023 | 0 | if (!producer) { |
2024 | 0 | continue; |
2025 | 0 | } |
2026 | 0 | if (++i > kMaxLifecycleEventsListedDataSources) { |
2027 | 0 | break; |
2028 | 0 | } |
2029 | | |
2030 | 0 | auto ds_id_range = |
2031 | 0 | tracing_session->data_source_instances.equal_range(producer_id); |
2032 | 0 | for (auto ds_it = ds_id_range.first; ds_it != ds_id_range.second; |
2033 | 0 | ds_it++) { |
2034 | 0 | auto* ds = event->add_data_source(); |
2035 | 0 | ds->set_producer_name(producer->name_); |
2036 | 0 | ds->set_data_source_name(ds_it->second.data_source_name); |
2037 | 0 | if (++i > kMaxLifecycleEventsListedDataSources) { |
2038 | 0 | break; |
2039 | 0 | } |
2040 | 0 | } |
2041 | 0 | } |
2042 | |
|
2043 | 0 | tracing_session->last_flush_events.push_back( |
2044 | 0 | {timestamp, packet.SerializeAsArray()}); |
2045 | 0 | } |
2046 | 0 | tracing_session->pending_flushes.erase(it); |
2047 | 0 | CompleteFlush(tsid, std::move(callback), success); |
2048 | 0 | } |
2049 | | |
2050 | | void TracingServiceImpl::CompleteFlush(TracingSessionID tsid, |
2051 | | ConsumerEndpoint::FlushCallback callback, |
2052 | 0 | bool success) { |
2053 | 0 | TracingSession* tracing_session = GetTracingSession(tsid); |
2054 | 0 | if (!tracing_session) { |
2055 | 0 | callback(false); |
2056 | 0 | return; |
2057 | 0 | } |
2058 | | // Producers may not have been able to flush all their data, even if they |
2059 | | // indicated flush completion. If possible, also collect uncommitted chunks |
2060 | | // to make sure we have everything they wrote so far. |
2061 | 0 | for (auto& producer_id_and_producer : producers_) { |
2062 | 0 | ScrapeSharedMemoryBuffers(tracing_session, producer_id_and_producer.second); |
2063 | 0 | } |
2064 | 0 | SnapshotLifecycleEvent( |
2065 | 0 | tracing_session, |
2066 | 0 | protos::pbzero::TracingServiceEvent::kAllDataSourcesFlushedFieldNumber, |
2067 | 0 | true /* snapshot_clocks */); |
2068 | |
|
2069 | 0 | tracing_session->flushes_succeeded += success ? 1 : 0; |
2070 | 0 | tracing_session->flushes_failed += success ? 0 : 1; |
2071 | 0 | callback(success); |
2072 | 0 | } |
2073 | | |
2074 | | void TracingServiceImpl::ScrapeSharedMemoryBuffers( |
2075 | | TracingSession* tracing_session, |
2076 | 300 | ProducerEndpointImpl* producer) { |
2077 | 300 | if (!producer->smb_scraping_enabled_) |
2078 | 300 | return; |
2079 | | |
2080 | | // Can't copy chunks if we don't know about any trace writers. |
2081 | 0 | if (producer->writers_.empty()) |
2082 | 0 | return; |
2083 | | |
2084 | | // Performance optimization: On flush or session disconnect, this method is |
2085 | | // called for each producer. If the producer doesn't participate in the |
2086 | | // session, there's no need to scape its chunks right now. We can tell if a |
2087 | | // producer participates in the session by checking if the producer is allowed |
2088 | | // to write into the session's log buffers. |
2089 | 0 | const auto& session_buffers = tracing_session->buffers_index; |
2090 | 0 | bool producer_in_session = |
2091 | 0 | std::any_of(session_buffers.begin(), session_buffers.end(), |
2092 | 0 | [producer](BufferID buffer_id) { |
2093 | 0 | return producer->allowed_target_buffers_.count(buffer_id); |
2094 | 0 | }); |
2095 | 0 | if (!producer_in_session) |
2096 | 0 | return; |
2097 | | |
2098 | 0 | PERFETTO_DLOG("Scraping SMB for producer %" PRIu16, producer->id_); |
2099 | | |
2100 | | // Find and copy any uncommitted chunks from the SMB. |
2101 | | // |
2102 | | // In nominal conditions, the page header bitmap of the used SMB pages should |
2103 | | // never change because the service is the only one who is supposed to modify |
2104 | | // used pages (to make them free again). |
2105 | | // |
2106 | | // However, the code here needs to deal with the case of a malicious producer |
2107 | | // altering the SMB in unpredictable ways. Thankfully the SMB size is |
2108 | | // immutable, so a chunk will always point to some valid memory, even if the |
2109 | | // producer alters the intended layout and chunk header concurrently. |
2110 | | // Ultimately a malicious producer altering the SMB's chunk header bitamp |
2111 | | // while we are iterating in this function is not any different from the case |
2112 | | // of a malicious producer asking to commit a chunk made of random data, |
2113 | | // which is something this class has to deal with regardless. |
2114 | | // |
2115 | | // The only legitimate mutations that can happen from sane producers, |
2116 | | // concurrently to this function, are: |
2117 | | // A. free pages being partitioned, |
2118 | | // B. free chunks being migrated to kChunkBeingWritten, |
2119 | | // C. kChunkBeingWritten chunks being migrated to kChunkCompleted. |
2120 | |
|
2121 | 0 | SharedMemoryABI* abi = &producer->shmem_abi_; |
2122 | | // num_pages() is immutable after the SMB is initialized and cannot be changed |
2123 | | // even by a producer even if malicious. |
2124 | 0 | for (size_t page_idx = 0; page_idx < abi->num_pages(); page_idx++) { |
2125 | 0 | uint32_t header_bitmap = abi->GetPageHeaderBitmap(page_idx); |
2126 | |
|
2127 | 0 | uint32_t used_chunks = |
2128 | 0 | abi->GetUsedChunks(header_bitmap); // Returns a bitmap. |
2129 | | // Skip empty pages. |
2130 | 0 | if (used_chunks == 0) |
2131 | 0 | continue; |
2132 | | |
2133 | | // Scrape the chunks that are currently used. These should be either in |
2134 | | // state kChunkBeingWritten or kChunkComplete. |
2135 | 0 | for (uint32_t chunk_idx = 0; used_chunks; chunk_idx++, used_chunks >>= 1) { |
2136 | 0 | if (!(used_chunks & 1)) |
2137 | 0 | continue; |
2138 | | |
2139 | 0 | SharedMemoryABI::ChunkState state = |
2140 | 0 | SharedMemoryABI::GetChunkStateFromHeaderBitmap(header_bitmap, |
2141 | 0 | chunk_idx); |
2142 | 0 | PERFETTO_DCHECK(state == SharedMemoryABI::kChunkBeingWritten || |
2143 | 0 | state == SharedMemoryABI::kChunkComplete); |
2144 | 0 | bool chunk_complete = state == SharedMemoryABI::kChunkComplete; |
2145 | |
|
2146 | 0 | SharedMemoryABI::Chunk chunk = |
2147 | 0 | abi->GetChunkUnchecked(page_idx, header_bitmap, chunk_idx); |
2148 | |
|
2149 | 0 | uint16_t packet_count; |
2150 | 0 | uint8_t flags; |
2151 | | // GetPacketCountAndFlags has acquire_load semantics. |
2152 | 0 | std::tie(packet_count, flags) = chunk.GetPacketCountAndFlags(); |
2153 | | |
2154 | | // It only makes sense to copy an incomplete chunk if there's at least |
2155 | | // one full packet available. (The producer may not have completed the |
2156 | | // last packet in it yet, so we need at least 2.) |
2157 | 0 | if (!chunk_complete && packet_count < 2) |
2158 | 0 | continue; |
2159 | | |
2160 | | // At this point, it is safe to access the remaining header fields of |
2161 | | // the chunk. Even if the chunk was only just transferred from |
2162 | | // kChunkFree into kChunkBeingWritten state, the header should be |
2163 | | // written completely once the packet count increased above 1 (it was |
2164 | | // reset to 0 by the service when the chunk was freed). |
2165 | | |
2166 | 0 | WriterID writer_id = chunk.writer_id(); |
2167 | 0 | std::optional<BufferID> target_buffer_id = |
2168 | 0 | producer->buffer_id_for_writer(writer_id); |
2169 | | |
2170 | | // We can only scrape this chunk if we know which log buffer to copy it |
2171 | | // into. |
2172 | 0 | if (!target_buffer_id) |
2173 | 0 | continue; |
2174 | | |
2175 | | // Skip chunks that don't belong to the requested tracing session. |
2176 | 0 | bool target_buffer_belongs_to_session = |
2177 | 0 | std::find(session_buffers.begin(), session_buffers.end(), |
2178 | 0 | *target_buffer_id) != session_buffers.end(); |
2179 | 0 | if (!target_buffer_belongs_to_session) |
2180 | 0 | continue; |
2181 | | |
2182 | 0 | uint32_t chunk_id = |
2183 | 0 | chunk.header()->chunk_id.load(std::memory_order_relaxed); |
2184 | |
|
2185 | 0 | CopyProducerPageIntoLogBuffer( |
2186 | 0 | producer->id_, producer->client_identity_, writer_id, chunk_id, |
2187 | 0 | *target_buffer_id, packet_count, flags, chunk_complete, |
2188 | 0 | chunk.payload_begin(), chunk.payload_size()); |
2189 | 0 | } |
2190 | 0 | } |
2191 | 0 | } |
2192 | | |
2193 | 0 | void TracingServiceImpl::FlushAndDisableTracing(TracingSessionID tsid) { |
2194 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
2195 | 0 | PERFETTO_DLOG("Triggering final flush for %" PRIu64, tsid); |
2196 | 0 | Flush( |
2197 | 0 | tsid, 0, |
2198 | 0 | [this, tsid](bool success) { |
2199 | | // This was a DLOG up to Jun 2021 (v16, Android S). |
2200 | 0 | PERFETTO_LOG("FlushAndDisableTracing(%" PRIu64 ") done, success=%d", |
2201 | 0 | tsid, success); |
2202 | 0 | TracingSession* session = GetTracingSession(tsid); |
2203 | 0 | if (!session) { |
2204 | 0 | return; |
2205 | 0 | } |
2206 | 0 | session->final_flush_outcome = success |
2207 | 0 | ? TraceStats::FINAL_FLUSH_SUCCEEDED |
2208 | 0 | : TraceStats::FINAL_FLUSH_FAILED; |
2209 | 0 | if (session->consumer_maybe_null) { |
2210 | | // If the consumer is still attached, just disable the session but |
2211 | | // give it a chance to read the contents. |
2212 | 0 | DisableTracing(tsid); |
2213 | 0 | } else { |
2214 | | // If the consumer detached, destroy the session. If the consumer did |
2215 | | // start the session in long-tracing mode, the service will have saved |
2216 | | // the contents to the passed file. If not, the contents will be |
2217 | | // destroyed. |
2218 | 0 | FreeBuffers(tsid); |
2219 | 0 | } |
2220 | 0 | }, |
2221 | 0 | FlushFlags(FlushFlags::Initiator::kTraced, |
2222 | 0 | FlushFlags::Reason::kTraceStop)); |
2223 | 0 | } |
2224 | | |
2225 | | void TracingServiceImpl::PeriodicFlushTask(TracingSessionID tsid, |
2226 | 0 | bool post_next_only) { |
2227 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
2228 | 0 | TracingSession* tracing_session = GetTracingSession(tsid); |
2229 | 0 | if (!tracing_session || tracing_session->state != TracingSession::STARTED) |
2230 | 0 | return; |
2231 | | |
2232 | 0 | uint32_t flush_period_ms = tracing_session->config.flush_period_ms(); |
2233 | 0 | weak_runner_.PostDelayedTask( |
2234 | 0 | [this, tsid] { PeriodicFlushTask(tsid, /*post_next_only=*/false); }, |
2235 | 0 | flush_period_ms - static_cast<uint32_t>(clock_->GetWallTimeMs().count() % |
2236 | 0 | flush_period_ms)); |
2237 | |
|
2238 | 0 | if (post_next_only) |
2239 | 0 | return; |
2240 | | |
2241 | 0 | PERFETTO_DLOG("Triggering periodic flush for trace session %" PRIu64, tsid); |
2242 | 0 | Flush( |
2243 | 0 | tsid, 0, |
2244 | 0 | [](bool success) { |
2245 | 0 | if (!success) |
2246 | 0 | PERFETTO_ELOG("Periodic flush timed out"); |
2247 | 0 | }, |
2248 | 0 | FlushFlags(FlushFlags::Initiator::kTraced, |
2249 | 0 | FlushFlags::Reason::kPeriodic)); |
2250 | 0 | } |
2251 | | |
2252 | | void TracingServiceImpl::PeriodicClearIncrementalStateTask( |
2253 | | TracingSessionID tsid, |
2254 | 0 | bool post_next_only) { |
2255 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
2256 | 0 | TracingSession* tracing_session = GetTracingSession(tsid); |
2257 | 0 | if (!tracing_session || tracing_session->state != TracingSession::STARTED) |
2258 | 0 | return; |
2259 | | |
2260 | 0 | uint32_t clear_period_ms = |
2261 | 0 | tracing_session->config.incremental_state_config().clear_period_ms(); |
2262 | 0 | weak_runner_.PostDelayedTask( |
2263 | 0 | [this, tsid] { |
2264 | 0 | PeriodicClearIncrementalStateTask(tsid, /*post_next_only=*/false); |
2265 | 0 | }, |
2266 | 0 | clear_period_ms - static_cast<uint32_t>(clock_->GetWallTimeMs().count() % |
2267 | 0 | clear_period_ms)); |
2268 | |
|
2269 | 0 | if (post_next_only) |
2270 | 0 | return; |
2271 | | |
2272 | 0 | PERFETTO_DLOG( |
2273 | 0 | "Performing periodic incremental state clear for trace session %" PRIu64, |
2274 | 0 | tsid); |
2275 | | |
2276 | | // Queue the IPCs to producers with active data sources that opted in. |
2277 | 0 | std::map<ProducerID, std::vector<DataSourceInstanceID>> clear_map; |
2278 | 0 | for (const auto& kv : tracing_session->data_source_instances) { |
2279 | 0 | ProducerID producer_id = kv.first; |
2280 | 0 | const DataSourceInstance& data_source = kv.second; |
2281 | 0 | if (data_source.handles_incremental_state_clear) { |
2282 | 0 | clear_map[producer_id].push_back(data_source.instance_id); |
2283 | 0 | } |
2284 | 0 | } |
2285 | |
|
2286 | 0 | for (const auto& kv : clear_map) { |
2287 | 0 | ProducerID producer_id = kv.first; |
2288 | 0 | const std::vector<DataSourceInstanceID>& data_sources = kv.second; |
2289 | 0 | ProducerEndpointImpl* producer = GetProducer(producer_id); |
2290 | 0 | if (!producer) { |
2291 | 0 | PERFETTO_DFATAL("Producer does not exist."); |
2292 | 0 | continue; |
2293 | 0 | } |
2294 | 0 | producer->ClearIncrementalState(data_sources); |
2295 | 0 | } |
2296 | 0 | } |
2297 | | |
2298 | | bool TracingServiceImpl::ReadBuffersIntoConsumer( |
2299 | | TracingSessionID tsid, |
2300 | 300 | ConsumerEndpointImpl* consumer) { |
2301 | 300 | PERFETTO_DCHECK(consumer); |
2302 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
2303 | 300 | TracingSession* tracing_session = GetTracingSession(tsid); |
2304 | 300 | if (!tracing_session) { |
2305 | 0 | PERFETTO_DLOG( |
2306 | 0 | "Cannot ReadBuffersIntoConsumer(): no tracing session is active"); |
2307 | 0 | return false; |
2308 | 0 | } |
2309 | | |
2310 | 300 | if (tracing_session->write_into_file) { |
2311 | | // If the consumer enabled tracing and asked to save the contents into the |
2312 | | // passed file makes little sense to also try to read the buffers over IPC, |
2313 | | // as that would just steal data from the periodic draining task. |
2314 | 0 | PERFETTO_ELOG("Consumer trying to read from write_into_file session."); |
2315 | 0 | return false; |
2316 | 0 | } |
2317 | | |
2318 | 300 | if (IsWaitingForTrigger(tracing_session)) |
2319 | 0 | return false; |
2320 | | |
2321 | | // This is a rough threshold to determine how much to read from the buffer in |
2322 | | // each task. This is to avoid executing a single huge sending task for too |
2323 | | // long and risk to hit the watchdog. This is *not* an upper bound: we just |
2324 | | // stop accumulating new packets and PostTask *after* we cross this threshold. |
2325 | | // This constant essentially balances the PostTask and IPC overhead vs the |
2326 | | // responsiveness of the service. An extremely small value will cause one IPC |
2327 | | // and one PostTask for each slice but will keep the service extremely |
2328 | | // responsive. An extremely large value will batch the send for the full |
2329 | | // buffer in one large task, will hit the blocking send() once the socket |
2330 | | // buffers are full and hang the service for a bit (until the consumer |
2331 | | // catches up). |
2332 | 300 | static constexpr size_t kApproxBytesPerTask = 32768; |
2333 | 300 | bool has_more; |
2334 | 300 | std::vector<TracePacket> packets = |
2335 | 300 | ReadBuffers(tracing_session, kApproxBytesPerTask, &has_more); |
2336 | | |
2337 | 300 | if (has_more) { |
2338 | 0 | auto weak_consumer = consumer->weak_ptr_factory_.GetWeakPtr(); |
2339 | 0 | weak_runner_.PostTask( |
2340 | 0 | [this, weak_consumer = std::move(weak_consumer), tsid] { |
2341 | 0 | if (!weak_consumer) |
2342 | 0 | return; |
2343 | 0 | ReadBuffersIntoConsumer(tsid, weak_consumer.get()); |
2344 | 0 | }); |
2345 | 0 | } |
2346 | | |
2347 | | // Keep this as tail call, just in case the consumer re-enters. |
2348 | 300 | consumer->consumer_->OnTraceData(std::move(packets), has_more); |
2349 | 300 | return true; |
2350 | 300 | } |
2351 | | |
2352 | 0 | bool TracingServiceImpl::ReadBuffersIntoFile(TracingSessionID tsid) { |
2353 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
2354 | 0 | TracingSession* tracing_session = GetTracingSession(tsid); |
2355 | 0 | if (!tracing_session) { |
2356 | | // This will be hit systematically from the PostDelayedTask. Avoid logging, |
2357 | | // it would be just spam. |
2358 | 0 | return false; |
2359 | 0 | } |
2360 | | |
2361 | | // This can happen if the file is closed by a previous task because it reaches |
2362 | | // |max_file_size_bytes|. |
2363 | 0 | if (!tracing_session->write_into_file) |
2364 | 0 | return false; |
2365 | | |
2366 | 0 | if (IsWaitingForTrigger(tracing_session)) |
2367 | 0 | return false; |
2368 | | |
2369 | | // ReadBuffers() can allocate memory internally, for filtering. By limiting |
2370 | | // the data that ReadBuffers() reads to kWriteIntoChunksSize per iteration, |
2371 | | // we limit the amount of memory used on each iteration. |
2372 | | // |
2373 | | // It would be tempting to split this into multiple tasks like in |
2374 | | // ReadBuffersIntoConsumer, but that's not currently possible. |
2375 | | // ReadBuffersIntoFile has to read the whole available data before returning, |
2376 | | // to support the disable_immediately=true code paths. |
2377 | 0 | bool has_more = true; |
2378 | 0 | bool stop_writing_into_file = false; |
2379 | 0 | do { |
2380 | 0 | std::vector<TracePacket> packets = |
2381 | 0 | ReadBuffers(tracing_session, kWriteIntoFileChunkSize, &has_more); |
2382 | |
|
2383 | 0 | stop_writing_into_file = WriteIntoFile(tracing_session, std::move(packets)); |
2384 | 0 | } while (has_more && !stop_writing_into_file); |
2385 | |
|
2386 | 0 | if (stop_writing_into_file || tracing_session->write_period_ms == 0) { |
2387 | | // Ensure all data was written to the file before we close it. |
2388 | 0 | base::FlushFile(tracing_session->write_into_file.get()); |
2389 | 0 | tracing_session->write_into_file.reset(); |
2390 | 0 | tracing_session->write_period_ms = 0; |
2391 | 0 | if (tracing_session->state == TracingSession::STARTED) |
2392 | 0 | DisableTracing(tsid); |
2393 | 0 | return true; |
2394 | 0 | } |
2395 | | |
2396 | 0 | weak_runner_.PostDelayedTask([this, tsid] { ReadBuffersIntoFile(tsid); }, |
2397 | 0 | DelayToNextWritePeriodMs(*tracing_session)); |
2398 | 0 | return true; |
2399 | 0 | } |
2400 | | |
2401 | 300 | bool TracingServiceImpl::IsWaitingForTrigger(TracingSession* tracing_session) { |
2402 | | // Ignore the logic below for cloned tracing sessions. In this case we |
2403 | | // actually want to read the (cloned) trace buffers even if no trigger was |
2404 | | // hit. |
2405 | 300 | if (tracing_session->state == TracingSession::CLONED_READ_ONLY) { |
2406 | 0 | return false; |
2407 | 0 | } |
2408 | | |
2409 | | // When a tracing session is waiting for a trigger, it is considered empty. If |
2410 | | // a tracing session finishes and moves into DISABLED without ever receiving a |
2411 | | // trigger, the trace should never return any data. This includes the |
2412 | | // synthetic packets like TraceConfig and Clock snapshots. So we bail out |
2413 | | // early and let the consumer know there is no data. |
2414 | 300 | if (!tracing_session->config.trigger_config().triggers().empty() && |
2415 | 0 | tracing_session->received_triggers.empty()) { |
2416 | 0 | PERFETTO_DLOG( |
2417 | 0 | "ReadBuffers(): tracing session has not received a trigger yet."); |
2418 | 0 | return true; |
2419 | 0 | } |
2420 | | |
2421 | | // Traces with CLONE_SNAPSHOT triggers are a special case of the above. They |
2422 | | // can be read only via a CloneSession() request. This is to keep the |
2423 | | // behavior consistent with the STOP_TRACING+triggers case and avoid periodic |
2424 | | // finalizations and uploads of the main CLONE_SNAPSHOT triggers. |
2425 | 300 | if (GetTriggerMode(tracing_session->config) == |
2426 | 300 | TraceConfig::TriggerConfig::CLONE_SNAPSHOT) { |
2427 | 0 | PERFETTO_DLOG( |
2428 | 0 | "ReadBuffers(): skipping because the tracing session has " |
2429 | 0 | "CLONE_SNAPSHOT triggers defined"); |
2430 | 0 | return true; |
2431 | 0 | } |
2432 | | |
2433 | 300 | return false; |
2434 | 300 | } |
2435 | | |
2436 | | std::vector<TracePacket> TracingServiceImpl::ReadBuffers( |
2437 | | TracingSession* tracing_session, |
2438 | | size_t threshold, |
2439 | 300 | bool* has_more) { |
2440 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
2441 | 300 | PERFETTO_DCHECK(tracing_session); |
2442 | 300 | *has_more = false; |
2443 | | |
2444 | 300 | std::vector<TracePacket> packets; |
2445 | 300 | packets.reserve(1024); // Just an educated guess to avoid trivial expansions. |
2446 | | |
2447 | 300 | if (!tracing_session->initial_clock_snapshot.empty()) { |
2448 | 300 | EmitClockSnapshot(tracing_session, |
2449 | 300 | std::move(tracing_session->initial_clock_snapshot), |
2450 | 300 | &packets); |
2451 | 300 | } |
2452 | | |
2453 | 300 | for (auto& snapshot : tracing_session->clock_snapshot_ring_buffer) { |
2454 | 300 | PERFETTO_DCHECK(!snapshot.empty()); |
2455 | 300 | EmitClockSnapshot(tracing_session, std::move(snapshot), &packets); |
2456 | 300 | } |
2457 | 300 | tracing_session->clock_snapshot_ring_buffer.clear(); |
2458 | | |
2459 | 300 | if (tracing_session->should_emit_sync_marker) { |
2460 | 300 | EmitSyncMarker(&packets); |
2461 | 300 | tracing_session->should_emit_sync_marker = false; |
2462 | 300 | } |
2463 | | |
2464 | 300 | if (!tracing_session->config.builtin_data_sources().disable_trace_config()) { |
2465 | 300 | MaybeEmitTraceConfig(tracing_session, &packets); |
2466 | 300 | MaybeEmitCloneTrigger(tracing_session, &packets); |
2467 | 300 | MaybeEmitReceivedTriggers(tracing_session, &packets); |
2468 | 300 | } |
2469 | 300 | if (!tracing_session->did_emit_initial_packets) { |
2470 | 300 | EmitUuid(tracing_session, &packets); |
2471 | 300 | if (!tracing_session->config.builtin_data_sources().disable_system_info()) |
2472 | 300 | EmitSystemInfo(&packets); |
2473 | 300 | } |
2474 | 300 | tracing_session->did_emit_initial_packets = true; |
2475 | | |
2476 | | // Note that in the proto comment, we guarantee that the tracing_started |
2477 | | // lifecycle event will be emitted before any data packets so make sure to |
2478 | | // keep this before reading the tracing buffers. |
2479 | 300 | if (!tracing_session->config.builtin_data_sources().disable_service_events()) |
2480 | 300 | EmitLifecycleEvents(tracing_session, &packets); |
2481 | | |
2482 | | // In a multi-machine tracing session, emit clock synchronization messages for |
2483 | | // remote machines. |
2484 | 300 | if (!relay_clients_.empty()) |
2485 | 0 | MaybeEmitRemoteClockSync(tracing_session, &packets); |
2486 | | |
2487 | 300 | size_t packets_bytes = 0; // SUM(slice.size() for each slice in |packets|). |
2488 | | |
2489 | | // Add up size for packets added by the Maybe* calls above. |
2490 | 2.40k | for (const TracePacket& packet : packets) { |
2491 | 2.40k | packets_bytes += packet.size(); |
2492 | 2.40k | } |
2493 | | |
2494 | 300 | bool did_hit_threshold = false; |
2495 | | |
2496 | 300 | for (size_t buf_idx = 0; |
2497 | 600 | buf_idx < tracing_session->num_buffers() && !did_hit_threshold; |
2498 | 300 | buf_idx++) { |
2499 | 300 | auto tbuf_iter = buffers_.find(tracing_session->buffers_index[buf_idx]); |
2500 | 300 | if (tbuf_iter == buffers_.end()) { |
2501 | 0 | PERFETTO_DFATAL("Buffer not found."); |
2502 | 0 | continue; |
2503 | 0 | } |
2504 | 300 | TraceBuffer& tbuf = *tbuf_iter->second; |
2505 | 300 | tbuf.BeginRead(); |
2506 | 600 | while (!did_hit_threshold) { |
2507 | 600 | TracePacket packet; |
2508 | 600 | TraceBuffer::PacketSequenceProperties sequence_properties{}; |
2509 | 600 | bool previous_packet_dropped; |
2510 | 600 | if (!tbuf.ReadNextTracePacket(&packet, &sequence_properties, |
2511 | 600 | &previous_packet_dropped)) { |
2512 | 300 | break; |
2513 | 300 | } |
2514 | 300 | packet.set_buffer_index_for_stats(static_cast<uint32_t>(buf_idx)); |
2515 | 300 | PERFETTO_DCHECK(sequence_properties.producer_id_trusted != 0); |
2516 | 300 | PERFETTO_DCHECK(sequence_properties.writer_id != 0); |
2517 | 300 | PERFETTO_DCHECK(sequence_properties.client_identity_trusted.has_uid()); |
2518 | | // Not checking sequence_properties.client_identity_trusted.has_pid(): |
2519 | | // it is false if the platform doesn't support it. |
2520 | | |
2521 | 300 | PERFETTO_DCHECK(packet.size() > 0); |
2522 | 300 | if (!PacketStreamValidator::Validate(packet.slices())) { |
2523 | 0 | tracing_session->invalid_packets++; |
2524 | 0 | PERFETTO_DLOG("Dropping invalid packet"); |
2525 | 0 | continue; |
2526 | 0 | } |
2527 | | |
2528 | | // Append a slice with the trusted field data. This can't be spoofed |
2529 | | // because above we validated that the existing slices don't contain any |
2530 | | // trusted fields. For added safety we append instead of prepending |
2531 | | // because according to protobuf semantics, if the same field is |
2532 | | // encountered multiple times the last instance takes priority. Note that |
2533 | | // truncated packets are also rejected, so the producer can't give us a |
2534 | | // partial packet (e.g., a truncated string) which only becomes valid when |
2535 | | // the trusted data is appended here. |
2536 | 300 | Slice slice = Slice::Allocate(32); |
2537 | 300 | protozero::StaticBuffered<protos::pbzero::TracePacket> trusted_packet( |
2538 | 300 | slice.own_data(), slice.size); |
2539 | 300 | const auto& client_identity_trusted = |
2540 | 300 | sequence_properties.client_identity_trusted; |
2541 | 300 | trusted_packet->set_trusted_uid( |
2542 | 300 | static_cast<int32_t>(client_identity_trusted.uid())); |
2543 | 300 | trusted_packet->set_trusted_packet_sequence_id( |
2544 | 300 | tracing_session->GetPacketSequenceID( |
2545 | 300 | client_identity_trusted.machine_id(), |
2546 | 300 | sequence_properties.producer_id_trusted, |
2547 | 300 | sequence_properties.writer_id)); |
2548 | 300 | if (client_identity_trusted.has_pid()) { |
2549 | | // Not supported on all platforms. |
2550 | 300 | trusted_packet->set_trusted_pid( |
2551 | 300 | static_cast<int32_t>(client_identity_trusted.pid())); |
2552 | 300 | } |
2553 | 300 | if (client_identity_trusted.has_non_default_machine_id()) { |
2554 | 0 | trusted_packet->set_machine_id(client_identity_trusted.machine_id()); |
2555 | 0 | } |
2556 | 300 | if (previous_packet_dropped) |
2557 | 300 | trusted_packet->set_previous_packet_dropped(previous_packet_dropped); |
2558 | 300 | slice.size = trusted_packet.Finalize(); |
2559 | 300 | packet.AddSlice(std::move(slice)); |
2560 | | |
2561 | | // Append the packet (inclusive of the trusted uid) to |packets|. |
2562 | 300 | packets_bytes += packet.size(); |
2563 | 300 | did_hit_threshold = packets_bytes >= threshold; |
2564 | 300 | packets.emplace_back(std::move(packet)); |
2565 | 300 | } // for(packets...) |
2566 | 300 | } // for(buffers...) |
2567 | | |
2568 | 300 | *has_more = did_hit_threshold; |
2569 | | |
2570 | | // Only emit the "read complete" lifetime event when there is no more trace |
2571 | | // data available to read. These events are used as safe points to limit |
2572 | | // sorting in trace processor: the code shouldn't emit the event unless the |
2573 | | // buffers are empty. |
2574 | 300 | if (!*has_more && !tracing_session->config.builtin_data_sources() |
2575 | 300 | .disable_service_events()) { |
2576 | | // We don't bother snapshotting clocks here because we wouldn't be able to |
2577 | | // emit it and we shouldn't have significant drift from the last snapshot in |
2578 | | // any case. |
2579 | 300 | SnapshotLifecycleEvent(tracing_session, |
2580 | 300 | protos::pbzero::TracingServiceEvent:: |
2581 | 300 | kReadTracingBuffersCompletedFieldNumber, |
2582 | 300 | false /* snapshot_clocks */); |
2583 | 300 | EmitLifecycleEvents(tracing_session, &packets); |
2584 | 300 | } |
2585 | | |
2586 | | // Only emit the stats when there is no more trace data is available to read. |
2587 | | // That way, any problems that occur while reading from the buffers are |
2588 | | // reflected in the emitted stats. This is particularly important for use |
2589 | | // cases where ReadBuffers is only ever called after the tracing session is |
2590 | | // stopped. |
2591 | 300 | if (!*has_more && tracing_session->should_emit_stats) { |
2592 | 300 | EmitStats(tracing_session, &packets); |
2593 | 300 | tracing_session->should_emit_stats = false; |
2594 | 300 | } |
2595 | | |
2596 | 300 | MaybeFilterPackets(tracing_session, &packets); |
2597 | | |
2598 | 300 | MaybeCompressPackets(tracing_session, &packets); |
2599 | | |
2600 | 300 | if (!*has_more) { |
2601 | | // We've observed some extremely high memory usage by scudo after |
2602 | | // MaybeFilterPackets in the past. The original bug (b/195145848) is fixed |
2603 | | // now, but this code asks scudo to release memory just in case. |
2604 | 300 | base::MaybeReleaseAllocatorMemToOS(); |
2605 | 300 | } |
2606 | | |
2607 | 300 | return packets; |
2608 | 300 | } |
2609 | | |
2610 | | void TracingServiceImpl::MaybeFilterPackets(TracingSession* tracing_session, |
2611 | 300 | std::vector<TracePacket>* packets) { |
2612 | | // If the tracing session specified a filter, run all packets through the |
2613 | | // filter and replace them with the filter results. |
2614 | | // The process below mantains the cardinality of input packets. Even if an |
2615 | | // entire packet is filtered out, we emit a zero-sized TracePacket proto. That |
2616 | | // makes debugging and reasoning about the trace stats easier. |
2617 | | // This place swaps the contents of each |packets| entry in place. |
2618 | 300 | if (!tracing_session->trace_filter) { |
2619 | 300 | return; |
2620 | 300 | } |
2621 | 0 | protozero::MessageFilter& trace_filter = *tracing_session->trace_filter; |
2622 | | // The filter root should be reset from protos.Trace to protos.TracePacket |
2623 | | // by the earlier call to SetFilterRoot() in EnableTracing(). |
2624 | 0 | PERFETTO_DCHECK(trace_filter.config().root_msg_index() != 0); |
2625 | 0 | std::vector<protozero::MessageFilter::InputSlice> filter_input; |
2626 | 0 | auto start = clock_->GetWallTimeNs(); |
2627 | 0 | for (TracePacket& packet : *packets) { |
2628 | 0 | const auto& packet_slices = packet.slices(); |
2629 | 0 | const size_t input_packet_size = packet.size(); |
2630 | 0 | filter_input.clear(); |
2631 | 0 | filter_input.resize(packet_slices.size()); |
2632 | 0 | ++tracing_session->filter_input_packets; |
2633 | 0 | tracing_session->filter_input_bytes += input_packet_size; |
2634 | 0 | for (size_t i = 0; i < packet_slices.size(); ++i) |
2635 | 0 | filter_input[i] = {packet_slices[i].start, packet_slices[i].size}; |
2636 | 0 | auto filtered_packet = trace_filter.FilterMessageFragments( |
2637 | 0 | &filter_input[0], filter_input.size()); |
2638 | | |
2639 | | // Replace the packet in-place with the filtered one (unless failed). |
2640 | 0 | std::optional<uint32_t> maybe_buffer_idx = packet.buffer_index_for_stats(); |
2641 | 0 | packet = TracePacket(); |
2642 | 0 | if (filtered_packet.error) { |
2643 | 0 | ++tracing_session->filter_errors; |
2644 | 0 | PERFETTO_DLOG("Trace packet filtering failed @ packet %" PRIu64, |
2645 | 0 | tracing_session->filter_input_packets); |
2646 | 0 | continue; |
2647 | 0 | } |
2648 | 0 | tracing_session->filter_output_bytes += filtered_packet.size; |
2649 | 0 | if (maybe_buffer_idx.has_value()) { |
2650 | | // Keep the per-buffer stats updated. Also propagate the |
2651 | | // buffer_index_for_stats in the output packet to allow accounting by |
2652 | | // other parts of the ReadBuffer pipeline. |
2653 | 0 | uint32_t buffer_idx = maybe_buffer_idx.value(); |
2654 | 0 | packet.set_buffer_index_for_stats(buffer_idx); |
2655 | 0 | auto& vec = tracing_session->filter_bytes_discarded_per_buffer; |
2656 | 0 | if (static_cast<size_t>(buffer_idx) >= vec.size()) |
2657 | 0 | vec.resize(buffer_idx + 1); |
2658 | 0 | PERFETTO_DCHECK(input_packet_size >= filtered_packet.size); |
2659 | 0 | size_t bytes_filtered_out = input_packet_size - filtered_packet.size; |
2660 | 0 | vec[buffer_idx] += bytes_filtered_out; |
2661 | 0 | } |
2662 | 0 | AppendOwnedSlicesToPacket(std::move(filtered_packet.data), |
2663 | 0 | filtered_packet.size, kMaxTracePacketSliceSize, |
2664 | 0 | &packet); |
2665 | 0 | } |
2666 | 0 | auto end = clock_->GetWallTimeNs(); |
2667 | 0 | tracing_session->filter_time_taken_ns += |
2668 | 0 | static_cast<uint64_t>((end - start).count()); |
2669 | 0 | } |
2670 | | |
2671 | | void TracingServiceImpl::MaybeCompressPackets( |
2672 | | TracingSession* tracing_session, |
2673 | 300 | std::vector<TracePacket>* packets) { |
2674 | 300 | if (!tracing_session->compress_deflate) { |
2675 | 300 | return; |
2676 | 300 | } |
2677 | | |
2678 | 0 | init_opts_.compressor_fn(packets); |
2679 | 0 | } |
2680 | | |
2681 | | bool TracingServiceImpl::WriteIntoFile(TracingSession* tracing_session, |
2682 | 0 | std::vector<TracePacket> packets) { |
2683 | 0 | if (!tracing_session->write_into_file) { |
2684 | 0 | return false; |
2685 | 0 | } |
2686 | 0 | const uint64_t max_size = tracing_session->max_file_size_bytes |
2687 | 0 | ? tracing_session->max_file_size_bytes |
2688 | 0 | : std::numeric_limits<size_t>::max(); |
2689 | |
|
2690 | 0 | size_t total_slices = 0; |
2691 | 0 | for (const TracePacket& packet : packets) { |
2692 | 0 | total_slices += packet.slices().size(); |
2693 | 0 | } |
2694 | | // When writing into a file, the file should look like a root trace.proto |
2695 | | // message. Each packet should be prepended with a proto preamble stating |
2696 | | // its field id (within trace.proto) and size. Hence the addition below. |
2697 | 0 | const size_t max_iovecs = total_slices + packets.size(); |
2698 | |
|
2699 | 0 | size_t num_iovecs = 0; |
2700 | 0 | bool stop_writing_into_file = false; |
2701 | 0 | std::unique_ptr<struct iovec[]> iovecs(new struct iovec[max_iovecs]); |
2702 | 0 | size_t num_iovecs_at_last_packet = 0; |
2703 | 0 | uint64_t bytes_about_to_be_written = 0; |
2704 | 0 | for (TracePacket& packet : packets) { |
2705 | 0 | std::tie(iovecs[num_iovecs].iov_base, iovecs[num_iovecs].iov_len) = |
2706 | 0 | packet.GetProtoPreamble(); |
2707 | 0 | bytes_about_to_be_written += iovecs[num_iovecs].iov_len; |
2708 | 0 | num_iovecs++; |
2709 | 0 | for (const Slice& slice : packet.slices()) { |
2710 | | // writev() doesn't change the passed pointer. However, struct iovec |
2711 | | // take a non-const ptr because it's the same struct used by readv(). |
2712 | | // Hence the const_cast here. |
2713 | 0 | char* start = static_cast<char*>(const_cast<void*>(slice.start)); |
2714 | 0 | bytes_about_to_be_written += slice.size; |
2715 | 0 | iovecs[num_iovecs++] = {start, slice.size}; |
2716 | 0 | } |
2717 | |
|
2718 | 0 | if (tracing_session->bytes_written_into_file + bytes_about_to_be_written >= |
2719 | 0 | max_size) { |
2720 | 0 | stop_writing_into_file = true; |
2721 | 0 | num_iovecs = num_iovecs_at_last_packet; |
2722 | 0 | break; |
2723 | 0 | } |
2724 | | |
2725 | 0 | num_iovecs_at_last_packet = num_iovecs; |
2726 | 0 | } |
2727 | 0 | PERFETTO_DCHECK(num_iovecs <= max_iovecs); |
2728 | 0 | int fd = *tracing_session->write_into_file; |
2729 | |
|
2730 | 0 | uint64_t total_wr_size = 0; |
2731 | | |
2732 | | // writev() can take at most IOV_MAX entries per call. Batch them. |
2733 | 0 | constexpr size_t kIOVMax = IOV_MAX; |
2734 | 0 | for (size_t i = 0; i < num_iovecs; i += kIOVMax) { |
2735 | 0 | int iov_batch_size = static_cast<int>(std::min(num_iovecs - i, kIOVMax)); |
2736 | 0 | ssize_t wr_size = PERFETTO_EINTR(writev(fd, &iovecs[i], iov_batch_size)); |
2737 | 0 | if (wr_size <= 0) { |
2738 | 0 | PERFETTO_PLOG("writev() failed"); |
2739 | 0 | stop_writing_into_file = true; |
2740 | 0 | break; |
2741 | 0 | } |
2742 | 0 | total_wr_size += static_cast<size_t>(wr_size); |
2743 | 0 | } |
2744 | |
|
2745 | 0 | tracing_session->bytes_written_into_file += total_wr_size; |
2746 | |
|
2747 | 0 | PERFETTO_DLOG("Draining into file, written: %" PRIu64 " KB, stop: %d", |
2748 | 0 | (total_wr_size + 1023) / 1024, stop_writing_into_file); |
2749 | 0 | return stop_writing_into_file; |
2750 | 0 | } |
2751 | | |
2752 | 300 | void TracingServiceImpl::FreeBuffers(TracingSessionID tsid) { |
2753 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
2754 | 300 | PERFETTO_DLOG("Freeing buffers for session %" PRIu64, tsid); |
2755 | 300 | TracingSession* tracing_session = GetTracingSession(tsid); |
2756 | 300 | if (!tracing_session) { |
2757 | 0 | PERFETTO_DLOG("FreeBuffers() failed, invalid session ID %" PRIu64, tsid); |
2758 | 0 | return; // TODO(primiano): signal failure? |
2759 | 0 | } |
2760 | 300 | DisableTracing(tsid, /*disable_immediately=*/true); |
2761 | | |
2762 | 300 | PERFETTO_DCHECK(tracing_session->AllDataSourceInstancesStopped()); |
2763 | 300 | tracing_session->data_source_instances.clear(); |
2764 | | |
2765 | 300 | for (auto& producer_entry : producers_) { |
2766 | 0 | ProducerEndpointImpl* producer = producer_entry.second; |
2767 | 0 | producer->OnFreeBuffers(tracing_session->buffers_index); |
2768 | 0 | } |
2769 | | |
2770 | 300 | for (BufferID buffer_id : tracing_session->buffers_index) { |
2771 | 300 | buffer_ids_.Free(buffer_id); |
2772 | 300 | PERFETTO_DCHECK(buffers_.count(buffer_id) == 1); |
2773 | 300 | buffers_.erase(buffer_id); |
2774 | 300 | } |
2775 | 300 | bool notify_traceur = |
2776 | 300 | tracing_session->config.notify_traceur() && |
2777 | 0 | tracing_session->state != TracingSession::CLONED_READ_ONLY; |
2778 | 300 | bool is_long_trace = |
2779 | 300 | (tracing_session->config.write_into_file() && |
2780 | 0 | tracing_session->config.file_write_period_ms() < kMillisPerDay); |
2781 | 300 | auto pending_clones = std::move(tracing_session->pending_clones); |
2782 | 300 | tracing_sessions_.erase(tsid); |
2783 | 300 | tracing_session = nullptr; |
2784 | 300 | UpdateMemoryGuardrail(); |
2785 | | |
2786 | 300 | for (const auto& id_to_clone_op : pending_clones) { |
2787 | 0 | const PendingClone& clone_op = id_to_clone_op.second; |
2788 | 0 | if (clone_op.weak_consumer) { |
2789 | 0 | weak_runner_.task_runner()->PostTask( |
2790 | 0 | [weak_consumer = clone_op.weak_consumer] { |
2791 | 0 | if (weak_consumer) { |
2792 | 0 | weak_consumer->consumer_->OnSessionCloned( |
2793 | 0 | {false, "Original session ended", {}}); |
2794 | 0 | } |
2795 | 0 | }); |
2796 | 0 | } |
2797 | 0 | } |
2798 | | |
2799 | 300 | PERFETTO_LOG("Tracing session %" PRIu64 " ended, total sessions:%zu", tsid, |
2800 | 300 | tracing_sessions_.size()); |
2801 | | #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) && \ |
2802 | | PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) |
2803 | | if (notify_traceur && is_long_trace) { |
2804 | | PERFETTO_LAZY_LOAD(android_internal::NotifyTraceSessionEnded, notify_fn); |
2805 | | if (!notify_fn || !notify_fn(/*session_stolen=*/false)) |
2806 | | PERFETTO_ELOG("Failed to notify Traceur long tracing has ended"); |
2807 | | } |
2808 | | #else |
2809 | 300 | base::ignore_result(notify_traceur); |
2810 | 300 | base::ignore_result(is_long_trace); |
2811 | 300 | #endif |
2812 | 300 | } |
2813 | | |
2814 | | void TracingServiceImpl::RegisterDataSource(ProducerID producer_id, |
2815 | 300 | const DataSourceDescriptor& desc) { |
2816 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
2817 | 300 | if (desc.name().empty()) { |
2818 | 0 | PERFETTO_DLOG("Received RegisterDataSource() with empty name"); |
2819 | 0 | return; |
2820 | 0 | } |
2821 | | |
2822 | 300 | ProducerEndpointImpl* producer = GetProducer(producer_id); |
2823 | 300 | if (!producer) { |
2824 | 0 | PERFETTO_DFATAL("Producer not found."); |
2825 | 0 | return; |
2826 | 0 | } |
2827 | | |
2828 | | // Check that the producer doesn't register two data sources with the same ID. |
2829 | | // Note that we tolerate |id| == 0 because until Android T / v22 the |id| |
2830 | | // field didn't exist. |
2831 | 300 | for (const auto& kv : data_sources_) { |
2832 | 0 | if (desc.id() && kv.second.producer_id == producer_id && |
2833 | 0 | kv.second.descriptor.id() == desc.id()) { |
2834 | 0 | PERFETTO_ELOG( |
2835 | 0 | "Failed to register data source \"%s\". A data source with the same " |
2836 | 0 | "id %" PRIu64 " (name=\"%s\") is already registered for producer %d", |
2837 | 0 | desc.name().c_str(), desc.id(), kv.second.descriptor.name().c_str(), |
2838 | 0 | producer_id); |
2839 | 0 | return; |
2840 | 0 | } |
2841 | 0 | } |
2842 | | |
2843 | 300 | PERFETTO_DLOG("Producer %" PRIu16 " registered data source \"%s\"", |
2844 | 300 | producer_id, desc.name().c_str()); |
2845 | | |
2846 | 300 | auto reg_ds = data_sources_.emplace(desc.name(), |
2847 | 300 | RegisteredDataSource{producer_id, desc}); |
2848 | | |
2849 | | // If there are existing tracing sessions, we need to check if the new |
2850 | | // data source is enabled by any of them. |
2851 | 300 | for (auto& iter : tracing_sessions_) { |
2852 | 213 | TracingSession& tracing_session = iter.second; |
2853 | 213 | if (tracing_session.state != TracingSession::STARTED && |
2854 | 0 | tracing_session.state != TracingSession::CONFIGURED) { |
2855 | 0 | continue; |
2856 | 0 | } |
2857 | | |
2858 | 213 | TraceConfig::ProducerConfig producer_config; |
2859 | 213 | for (const auto& config : tracing_session.config.producers()) { |
2860 | 0 | if (producer->name_ == config.producer_name()) { |
2861 | 0 | producer_config = config; |
2862 | 0 | break; |
2863 | 0 | } |
2864 | 0 | } |
2865 | 213 | for (const TraceConfig::DataSource& cfg_data_source : |
2866 | 213 | tracing_session.config.data_sources()) { |
2867 | 213 | if (cfg_data_source.config().name() != desc.name()) |
2868 | 0 | continue; |
2869 | 213 | DataSourceInstance* ds_inst = SetupDataSource( |
2870 | 213 | cfg_data_source, producer_config, reg_ds->second, &tracing_session); |
2871 | 213 | if (ds_inst && tracing_session.state == TracingSession::STARTED) |
2872 | 213 | StartDataSourceInstance(producer, &tracing_session, ds_inst); |
2873 | 213 | } |
2874 | 213 | } // for(iter : tracing_sessions_) |
2875 | 300 | } |
2876 | | |
2877 | | void TracingServiceImpl::UpdateDataSource( |
2878 | | ProducerID producer_id, |
2879 | 0 | const DataSourceDescriptor& new_desc) { |
2880 | 0 | if (new_desc.id() == 0) { |
2881 | 0 | PERFETTO_ELOG("UpdateDataSource() must have a non-zero id"); |
2882 | 0 | return; |
2883 | 0 | } |
2884 | | |
2885 | | // If this producer has already registered a matching descriptor name and id, |
2886 | | // just update the descriptor. |
2887 | 0 | RegisteredDataSource* data_source = nullptr; |
2888 | 0 | auto range = data_sources_.equal_range(new_desc.name()); |
2889 | 0 | for (auto it = range.first; it != range.second; ++it) { |
2890 | 0 | if (it->second.producer_id == producer_id && |
2891 | 0 | it->second.descriptor.id() == new_desc.id()) { |
2892 | 0 | data_source = &it->second; |
2893 | 0 | break; |
2894 | 0 | } |
2895 | 0 | } |
2896 | |
|
2897 | 0 | if (!data_source) { |
2898 | 0 | PERFETTO_ELOG( |
2899 | 0 | "UpdateDataSource() failed, could not find an existing data source " |
2900 | 0 | "with name=\"%s\" id=%" PRIu64, |
2901 | 0 | new_desc.name().c_str(), new_desc.id()); |
2902 | 0 | return; |
2903 | 0 | } |
2904 | | |
2905 | 0 | data_source->descriptor = new_desc; |
2906 | 0 | } |
2907 | | |
2908 | | void TracingServiceImpl::StopDataSourceInstance(ProducerEndpointImpl* producer, |
2909 | | TracingSession* tracing_session, |
2910 | | DataSourceInstance* instance, |
2911 | 300 | bool disable_immediately) { |
2912 | 300 | const DataSourceInstanceID ds_inst_id = instance->instance_id; |
2913 | 300 | if (producer->IsAndroidProcessFrozen()) { |
2914 | 0 | PERFETTO_DLOG( |
2915 | 0 | "skipping waiting of data source \"%s\" on producer \"%s\" (pid=%u) " |
2916 | 0 | "because it is frozen", |
2917 | 0 | instance->data_source_name.c_str(), producer->name_.c_str(), |
2918 | 0 | producer->pid()); |
2919 | 0 | disable_immediately = true; |
2920 | 0 | } |
2921 | 300 | if (instance->will_notify_on_stop && !disable_immediately) { |
2922 | 0 | instance->state = DataSourceInstance::STOPPING; |
2923 | 300 | } else { |
2924 | 300 | instance->state = DataSourceInstance::STOPPED; |
2925 | 300 | } |
2926 | 300 | if (tracing_session->consumer_maybe_null) { |
2927 | 300 | tracing_session->consumer_maybe_null->OnDataSourceInstanceStateChange( |
2928 | 300 | *producer, *instance); |
2929 | 300 | } |
2930 | 300 | producer->StopDataSource(ds_inst_id); |
2931 | 300 | } |
2932 | | |
2933 | | void TracingServiceImpl::UnregisterDataSource(ProducerID producer_id, |
2934 | 300 | const std::string& name) { |
2935 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
2936 | 300 | PERFETTO_DLOG("Producer %" PRIu16 " unregistered data source \"%s\"", |
2937 | 300 | producer_id, name.c_str()); |
2938 | 300 | PERFETTO_CHECK(producer_id); |
2939 | 300 | ProducerEndpointImpl* producer = GetProducer(producer_id); |
2940 | 300 | PERFETTO_DCHECK(producer); |
2941 | 300 | for (auto& kv : tracing_sessions_) { |
2942 | 300 | auto& ds_instances = kv.second.data_source_instances; |
2943 | 300 | bool removed = false; |
2944 | 600 | for (auto it = ds_instances.begin(); it != ds_instances.end();) { |
2945 | 300 | if (it->first == producer_id && it->second.data_source_name == name) { |
2946 | 300 | DataSourceInstanceID ds_inst_id = it->second.instance_id; |
2947 | 300 | if (it->second.state != DataSourceInstance::STOPPED) { |
2948 | 300 | if (it->second.state != DataSourceInstance::STOPPING) { |
2949 | 300 | StopDataSourceInstance(producer, &kv.second, &it->second, |
2950 | 300 | /* disable_immediately = */ false); |
2951 | 300 | } |
2952 | | |
2953 | | // Mark the instance as stopped immediately, since we are |
2954 | | // unregistering it below. |
2955 | | // |
2956 | | // The StopDataSourceInstance above might have set the state to |
2957 | | // STOPPING so this condition isn't an else. |
2958 | 300 | if (it->second.state == DataSourceInstance::STOPPING) |
2959 | 0 | NotifyDataSourceStopped(producer_id, ds_inst_id); |
2960 | 300 | } |
2961 | 300 | it = ds_instances.erase(it); |
2962 | 300 | removed = true; |
2963 | 300 | } else { |
2964 | 0 | ++it; |
2965 | 0 | } |
2966 | 300 | } // for (data_source_instances) |
2967 | 300 | if (removed) |
2968 | 300 | MaybeNotifyAllDataSourcesStarted(&kv.second); |
2969 | 300 | } // for (tracing_session) |
2970 | | |
2971 | 300 | for (auto it = data_sources_.begin(); it != data_sources_.end(); ++it) { |
2972 | 300 | if (it->second.producer_id == producer_id && |
2973 | 300 | it->second.descriptor.name() == name) { |
2974 | 300 | data_sources_.erase(it); |
2975 | 300 | return; |
2976 | 300 | } |
2977 | 300 | } |
2978 | | |
2979 | 300 | PERFETTO_DFATAL( |
2980 | 0 | "Tried to unregister a non-existent data source \"%s\" for " |
2981 | 0 | "producer %" PRIu16, |
2982 | 0 | name.c_str(), producer_id); |
2983 | 0 | } |
2984 | | |
2985 | | bool TracingServiceImpl::IsInitiatorPrivileged( |
2986 | 300 | const TracingSession& tracing_session) { |
2987 | | #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) |
2988 | | if (tracing_session.consumer_uid == 1066 /* AID_STATSD */ && |
2989 | | tracing_session.config.statsd_metadata().triggering_config_uid() != |
2990 | | 2000 /* AID_SHELL */ |
2991 | | && tracing_session.config.statsd_metadata().triggering_config_uid() != |
2992 | | 0 /* AID_ROOT */) { |
2993 | | // StatsD can be triggered either by shell, root or an app that has DUMP and |
2994 | | // USAGE_STATS permission. When triggered by shell or root, we do not want |
2995 | | // to consider the trace a trusted system trace, as it was initiated by the |
2996 | | // user. Otherwise, it has to come from an app with DUMP and |
2997 | | // PACKAGE_USAGE_STATS, which has to be preinstalled and trusted by the |
2998 | | // system. |
2999 | | // Check for shell / root: https://bit.ly/3b7oZNi |
3000 | | // Check for DUMP or PACKAGE_USAGE_STATS: https://bit.ly/3ep0NrR |
3001 | | return true; |
3002 | | } |
3003 | | if (tracing_session.consumer_uid == 1000 /* AID_SYSTEM */) { |
3004 | | // AID_SYSTEM is considered a privileged initiator so that system_server can |
3005 | | // profile apps that are not profileable by shell. Other AID_SYSTEM |
3006 | | // processes are not allowed by SELinux to connect to the consumer socket or |
3007 | | // to exec perfetto. |
3008 | | return true; |
3009 | | } |
3010 | | #else |
3011 | 300 | base::ignore_result(tracing_session); |
3012 | 300 | #endif |
3013 | 300 | return false; |
3014 | 300 | } |
3015 | | |
3016 | | TracingServiceImpl::DataSourceInstance* TracingServiceImpl::SetupDataSource( |
3017 | | const TraceConfig::DataSource& cfg_data_source, |
3018 | | const TraceConfig::ProducerConfig& producer_config, |
3019 | | const RegisteredDataSource& data_source, |
3020 | 300 | TracingSession* tracing_session) { |
3021 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
3022 | 300 | ProducerEndpointImpl* producer = GetProducer(data_source.producer_id); |
3023 | 300 | PERFETTO_DCHECK(producer); |
3024 | | // An existing producer that is not ftrace could have registered itself as |
3025 | | // ftrace, we must not enable it in that case. |
3026 | 300 | if (lockdown_mode_ && producer->uid() != uid_) { |
3027 | 0 | PERFETTO_DLOG("Lockdown mode: not enabling producer %hu", producer->id_); |
3028 | 0 | return nullptr; |
3029 | 0 | } |
3030 | | // TODO(primiano): Add tests for registration ordering (data sources vs |
3031 | | // consumers). |
3032 | 300 | if (!NameMatchesFilter(producer->name_, |
3033 | 300 | cfg_data_source.producer_name_filter(), |
3034 | 300 | cfg_data_source.producer_name_regex_filter())) { |
3035 | 0 | PERFETTO_DLOG("Data source: %s is filtered out for producer: %s", |
3036 | 0 | cfg_data_source.config().name().c_str(), |
3037 | 0 | producer->name_.c_str()); |
3038 | 0 | return nullptr; |
3039 | 0 | } |
3040 | | |
3041 | 300 | auto relative_buffer_id = cfg_data_source.config().target_buffer(); |
3042 | 300 | if (relative_buffer_id >= tracing_session->num_buffers()) { |
3043 | 0 | PERFETTO_LOG( |
3044 | 0 | "The TraceConfig for DataSource %s specified a target_buffer out of " |
3045 | 0 | "bound (%u). Skipping it.", |
3046 | 0 | cfg_data_source.config().name().c_str(), relative_buffer_id); |
3047 | 0 | return nullptr; |
3048 | 0 | } |
3049 | | |
3050 | | // Create a copy of the DataSourceConfig specified in the trace config. This |
3051 | | // will be passed to the producer after translating the |target_buffer| id. |
3052 | | // The |target_buffer| parameter passed by the consumer in the trace config is |
3053 | | // relative to the buffers declared in the same trace config. This has to be |
3054 | | // translated to the global BufferID before passing it to the producers, which |
3055 | | // don't know anything about tracing sessions and consumers. |
3056 | | |
3057 | 300 | DataSourceInstanceID inst_id = ++last_data_source_instance_id_; |
3058 | 300 | auto insert_iter = tracing_session->data_source_instances.emplace( |
3059 | 300 | std::piecewise_construct, // |
3060 | 300 | std::forward_as_tuple(producer->id_), |
3061 | 300 | std::forward_as_tuple( |
3062 | 300 | inst_id, |
3063 | 300 | cfg_data_source.config(), // Deliberate copy. |
3064 | 300 | data_source.descriptor.name(), |
3065 | 300 | data_source.descriptor.will_notify_on_start(), |
3066 | 300 | data_source.descriptor.will_notify_on_stop(), |
3067 | 300 | data_source.descriptor.handles_incremental_state_clear(), |
3068 | 300 | data_source.descriptor.no_flush())); |
3069 | 300 | DataSourceInstance* ds_instance = &insert_iter->second; |
3070 | | |
3071 | | // New data source instance starts out in CONFIGURED state. |
3072 | 300 | if (tracing_session->consumer_maybe_null) { |
3073 | 300 | tracing_session->consumer_maybe_null->OnDataSourceInstanceStateChange( |
3074 | 300 | *producer, *ds_instance); |
3075 | 300 | } |
3076 | | |
3077 | 300 | DataSourceConfig& ds_config = ds_instance->config; |
3078 | 300 | ds_config.set_trace_duration_ms(tracing_session->config.duration_ms()); |
3079 | | |
3080 | | // Rationale for `if (prefer) set_prefer(true)`, rather than `set(prefer)`: |
3081 | | // ComputeStartupConfigHash() in tracing_muxer_impl.cc compares hashes of the |
3082 | | // DataSourceConfig and expects to know (and clear) the fields generated by |
3083 | | // the tracing service. Unconditionally adding a new field breaks backward |
3084 | | // compatibility of startup tracing with older SDKs, because the serialization |
3085 | | // also propagates unkonwn fields, breaking the hash matching check. |
3086 | 300 | if (tracing_session->config.prefer_suspend_clock_for_duration()) |
3087 | 0 | ds_config.set_prefer_suspend_clock_for_duration(true); |
3088 | | |
3089 | 300 | ds_config.set_stop_timeout_ms(tracing_session->data_source_stop_timeout_ms()); |
3090 | 300 | ds_config.set_enable_extra_guardrails( |
3091 | 300 | tracing_session->config.enable_extra_guardrails()); |
3092 | 300 | if (IsInitiatorPrivileged(*tracing_session)) { |
3093 | 0 | ds_config.set_session_initiator( |
3094 | 0 | DataSourceConfig::SESSION_INITIATOR_TRUSTED_SYSTEM); |
3095 | 300 | } else { |
3096 | | // Unset in case the consumer set it. |
3097 | | // We need to be able to trust this field. |
3098 | 300 | ds_config.set_session_initiator( |
3099 | 300 | DataSourceConfig::SESSION_INITIATOR_UNSPECIFIED); |
3100 | 300 | } |
3101 | 300 | ds_config.set_tracing_session_id(tracing_session->id); |
3102 | 300 | BufferID global_id = tracing_session->buffers_index[relative_buffer_id]; |
3103 | 300 | PERFETTO_DCHECK(global_id); |
3104 | 300 | ds_config.set_target_buffer(global_id); |
3105 | | |
3106 | 300 | PERFETTO_DLOG("Setting up data source %s with target buffer %" PRIu16, |
3107 | 300 | ds_config.name().c_str(), global_id); |
3108 | 300 | if (!producer->shared_memory()) { |
3109 | | // Determine the SMB page size. Must be an integer multiple of 4k. |
3110 | | // As for the SMB size below, the decision tree is as follows: |
3111 | | // 1. Give priority to what is defined in the trace config. |
3112 | | // 2. If unset give priority to the hint passed by the producer. |
3113 | | // 3. Keep within bounds and ensure it's a multiple of 4k. |
3114 | 300 | size_t page_size = producer_config.page_size_kb() * 1024; |
3115 | 300 | if (page_size == 0) |
3116 | 300 | page_size = producer->shmem_page_size_hint_bytes_; |
3117 | | |
3118 | | // Determine the SMB size. Must be an integer multiple of the SMB page size. |
3119 | | // The decision tree is as follows: |
3120 | | // 1. Give priority to what defined in the trace config. |
3121 | | // 2. If unset give priority to the hint passed by the producer. |
3122 | | // 3. Keep within bounds and ensure it's a multiple of the page size. |
3123 | 300 | size_t shm_size = producer_config.shm_size_kb() * 1024; |
3124 | 300 | if (shm_size == 0) |
3125 | 300 | shm_size = producer->shmem_size_hint_bytes_; |
3126 | | |
3127 | 300 | auto valid_sizes = EnsureValidShmSizes(shm_size, page_size); |
3128 | 300 | if (valid_sizes != std::tie(shm_size, page_size)) { |
3129 | 300 | PERFETTO_DLOG( |
3130 | 300 | "Invalid configured SMB sizes: shm_size %zu page_size %zu. Falling " |
3131 | 300 | "back to shm_size %zu page_size %zu.", |
3132 | 300 | shm_size, page_size, std::get<0>(valid_sizes), |
3133 | 300 | std::get<1>(valid_sizes)); |
3134 | 300 | } |
3135 | 300 | std::tie(shm_size, page_size) = valid_sizes; |
3136 | | |
3137 | | // TODO(primiano): right now Create() will suicide in case of OOM if the |
3138 | | // mmap fails. We should instead gracefully fail the request and tell the |
3139 | | // client to go away. |
3140 | 300 | PERFETTO_DLOG("Creating SMB of %zu KB for producer \"%s\"", shm_size / 1024, |
3141 | 300 | producer->name_.c_str()); |
3142 | 300 | auto shared_memory = shm_factory_->CreateSharedMemory(shm_size); |
3143 | 300 | producer->SetupSharedMemory(std::move(shared_memory), page_size, |
3144 | 300 | /*provided_by_producer=*/false); |
3145 | 300 | } |
3146 | 300 | producer->SetupDataSource(inst_id, ds_config); |
3147 | 300 | return ds_instance; |
3148 | 300 | } |
3149 | | |
3150 | | // Note: all the fields % *_trusted ones are untrusted, as in, the Producer |
3151 | | // might be lying / returning garbage contents. |src| and |size| can be trusted |
3152 | | // in terms of being a valid pointer, but not the contents. |
3153 | | void TracingServiceImpl::CopyProducerPageIntoLogBuffer( |
3154 | | ProducerID producer_id_trusted, |
3155 | | const ClientIdentity& client_identity_trusted, |
3156 | | WriterID writer_id, |
3157 | | ChunkID chunk_id, |
3158 | | BufferID buffer_id, |
3159 | | uint16_t num_fragments, |
3160 | | uint8_t chunk_flags, |
3161 | | bool chunk_complete, |
3162 | | const uint8_t* src, |
3163 | 44.6k | size_t size) { |
3164 | 44.6k | PERFETTO_DCHECK_THREAD(thread_checker_); |
3165 | | |
3166 | 44.6k | ProducerEndpointImpl* producer = GetProducer(producer_id_trusted); |
3167 | 44.6k | if (!producer) { |
3168 | 0 | PERFETTO_DFATAL("Producer not found."); |
3169 | 0 | chunks_discarded_++; |
3170 | 0 | return; |
3171 | 0 | } |
3172 | | |
3173 | 44.6k | TraceBuffer* buf = GetBufferByID(buffer_id); |
3174 | 44.6k | if (!buf) { |
3175 | 0 | PERFETTO_DLOG("Could not find target buffer %" PRIu16 |
3176 | 0 | " for producer %" PRIu16, |
3177 | 0 | buffer_id, producer_id_trusted); |
3178 | 0 | chunks_discarded_++; |
3179 | 0 | return; |
3180 | 0 | } |
3181 | | |
3182 | | // Verify that the producer is actually allowed to write into the target |
3183 | | // buffer specified in the request. This prevents a malicious producer from |
3184 | | // injecting data into a log buffer that belongs to a tracing session the |
3185 | | // producer is not part of. |
3186 | 44.6k | if (!producer->is_allowed_target_buffer(buffer_id)) { |
3187 | 0 | PERFETTO_ELOG("Producer %" PRIu16 |
3188 | 0 | " tried to write into forbidden target buffer %" PRIu16, |
3189 | 0 | producer_id_trusted, buffer_id); |
3190 | 0 | PERFETTO_DFATAL("Forbidden target buffer"); |
3191 | 0 | chunks_discarded_++; |
3192 | 0 | return; |
3193 | 0 | } |
3194 | | |
3195 | | // If the writer was registered by the producer, it should only write into the |
3196 | | // buffer it was registered with. |
3197 | 44.6k | std::optional<BufferID> associated_buffer = |
3198 | 44.6k | producer->buffer_id_for_writer(writer_id); |
3199 | 44.6k | if (associated_buffer && *associated_buffer != buffer_id) { |
3200 | 0 | PERFETTO_ELOG("Writer %" PRIu16 " of producer %" PRIu16 |
3201 | 0 | " was registered to write into target buffer %" PRIu16 |
3202 | 0 | ", but tried to write into buffer %" PRIu16, |
3203 | 0 | writer_id, producer_id_trusted, *associated_buffer, |
3204 | 0 | buffer_id); |
3205 | 0 | PERFETTO_DFATAL("Wrong target buffer"); |
3206 | 0 | chunks_discarded_++; |
3207 | 0 | return; |
3208 | 0 | } |
3209 | | |
3210 | 44.6k | buf->CopyChunkUntrusted(producer_id_trusted, client_identity_trusted, |
3211 | 44.6k | writer_id, chunk_id, num_fragments, chunk_flags, |
3212 | 44.6k | chunk_complete, src, size); |
3213 | 44.6k | } |
3214 | | |
3215 | | void TracingServiceImpl::ApplyChunkPatches( |
3216 | | ProducerID producer_id_trusted, |
3217 | 1.75k | const std::vector<CommitDataRequest::ChunkToPatch>& chunks_to_patch) { |
3218 | 1.75k | PERFETTO_DCHECK_THREAD(thread_checker_); |
3219 | | |
3220 | 1.75k | for (const auto& chunk : chunks_to_patch) { |
3221 | 0 | const ChunkID chunk_id = static_cast<ChunkID>(chunk.chunk_id()); |
3222 | 0 | const WriterID writer_id = static_cast<WriterID>(chunk.writer_id()); |
3223 | 0 | TraceBuffer* buf = |
3224 | 0 | GetBufferByID(static_cast<BufferID>(chunk.target_buffer())); |
3225 | 0 | static_assert(std::numeric_limits<ChunkID>::max() == kMaxChunkID, |
3226 | 0 | "Add a '|| chunk_id > kMaxChunkID' below if this fails"); |
3227 | 0 | if (!writer_id || writer_id > kMaxWriterID || !buf) { |
3228 | | // This can genuinely happen when the trace is stopped. The producers |
3229 | | // might see the stop signal with some delay and try to keep sending |
3230 | | // patches left soon after. |
3231 | 0 | PERFETTO_DLOG( |
3232 | 0 | "Received invalid chunks_to_patch request from Producer: %" PRIu16 |
3233 | 0 | ", BufferID: %" PRIu32 " ChunkdID: %" PRIu32 " WriterID: %" PRIu16, |
3234 | 0 | producer_id_trusted, chunk.target_buffer(), chunk_id, writer_id); |
3235 | 0 | patches_discarded_ += static_cast<uint64_t>(chunk.patches_size()); |
3236 | 0 | continue; |
3237 | 0 | } |
3238 | | |
3239 | | // Note, there's no need to validate that the producer is allowed to write |
3240 | | // to the specified buffer ID (or that it's the correct buffer ID for a |
3241 | | // registered TraceWriter). That's because TraceBuffer uses the producer ID |
3242 | | // and writer ID to look up the chunk to patch. If the producer specifies an |
3243 | | // incorrect buffer, this lookup will fail and TraceBuffer will ignore the |
3244 | | // patches. Because the producer ID is trusted, there's also no way for a |
3245 | | // malicious producer to patch another producer's data. |
3246 | | |
3247 | | // Speculate on the fact that there are going to be a limited amount of |
3248 | | // patches per request, so we can allocate the |patches| array on the stack. |
3249 | 0 | std::array<TraceBuffer::Patch, 1024> patches; // Uninitialized. |
3250 | 0 | if (chunk.patches().size() > patches.size()) { |
3251 | 0 | PERFETTO_ELOG("Too many patches (%zu) batched in the same request", |
3252 | 0 | patches.size()); |
3253 | 0 | PERFETTO_DFATAL("Too many patches"); |
3254 | 0 | patches_discarded_ += static_cast<uint64_t>(chunk.patches_size()); |
3255 | 0 | continue; |
3256 | 0 | } |
3257 | | |
3258 | 0 | size_t i = 0; |
3259 | 0 | for (const auto& patch : chunk.patches()) { |
3260 | 0 | const std::string& patch_data = patch.data(); |
3261 | 0 | if (patch_data.size() != patches[i].data.size()) { |
3262 | 0 | PERFETTO_ELOG("Received patch from producer: %" PRIu16 |
3263 | 0 | " of unexpected size %zu", |
3264 | 0 | producer_id_trusted, patch_data.size()); |
3265 | 0 | patches_discarded_++; |
3266 | 0 | continue; |
3267 | 0 | } |
3268 | 0 | patches[i].offset_untrusted = patch.offset(); |
3269 | 0 | memcpy(&patches[i].data[0], patch_data.data(), patches[i].data.size()); |
3270 | 0 | i++; |
3271 | 0 | } |
3272 | 0 | buf->TryPatchChunkContents(producer_id_trusted, writer_id, chunk_id, |
3273 | 0 | &patches[0], i, chunk.has_more_patches()); |
3274 | 0 | } |
3275 | 1.75k | } |
3276 | | |
3277 | | TracingServiceImpl::TracingSession* TracingServiceImpl::GetDetachedSession( |
3278 | | uid_t uid, |
3279 | 0 | const std::string& key) { |
3280 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
3281 | 0 | for (auto& kv : tracing_sessions_) { |
3282 | 0 | TracingSession* session = &kv.second; |
3283 | 0 | if (session->consumer_uid == uid && session->detach_key == key) { |
3284 | 0 | PERFETTO_DCHECK(session->consumer_maybe_null == nullptr); |
3285 | 0 | return session; |
3286 | 0 | } |
3287 | 0 | } |
3288 | 0 | return nullptr; |
3289 | 0 | } |
3290 | | |
3291 | | TracingServiceImpl::TracingSession* TracingServiceImpl::GetTracingSession( |
3292 | 2.10k | TracingSessionID tsid) { |
3293 | 2.10k | PERFETTO_DCHECK_THREAD(thread_checker_); |
3294 | 2.10k | auto it = tsid ? tracing_sessions_.find(tsid) : tracing_sessions_.end(); |
3295 | 2.10k | if (it == tracing_sessions_.end()) |
3296 | 600 | return nullptr; |
3297 | 1.50k | return &it->second; |
3298 | 2.10k | } |
3299 | | |
3300 | | TracingServiceImpl::TracingSession* |
3301 | | TracingServiceImpl::GetTracingSessionByUniqueName( |
3302 | 0 | const std::string& unique_session_name) { |
3303 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
3304 | 0 | if (unique_session_name.empty()) { |
3305 | 0 | return nullptr; |
3306 | 0 | } |
3307 | 0 | for (auto& session_id_and_session : tracing_sessions_) { |
3308 | 0 | TracingSession& session = session_id_and_session.second; |
3309 | 0 | if (session.state == TracingSession::CLONED_READ_ONLY) { |
3310 | 0 | continue; |
3311 | 0 | } |
3312 | 0 | if (session.config.unique_session_name() == unique_session_name) { |
3313 | 0 | return &session; |
3314 | 0 | } |
3315 | 0 | } |
3316 | 0 | return nullptr; |
3317 | 0 | } |
3318 | | |
3319 | | TracingServiceImpl::TracingSession* |
3320 | 0 | TracingServiceImpl::FindTracingSessionWithMaxBugreportScore() { |
3321 | 0 | TracingSession* max_session = nullptr; |
3322 | 0 | for (auto& session_id_and_session : tracing_sessions_) { |
3323 | 0 | auto& session = session_id_and_session.second; |
3324 | 0 | const int32_t score = session.config.bugreport_score(); |
3325 | | // Exclude sessions with 0 (or below) score. By default tracing sessions |
3326 | | // should NOT be eligible to be attached to bugreports. |
3327 | 0 | if (score <= 0 || session.state != TracingSession::STARTED) |
3328 | 0 | continue; |
3329 | | |
3330 | 0 | if (!max_session || score > max_session->config.bugreport_score()) |
3331 | 0 | max_session = &session; |
3332 | 0 | } |
3333 | 0 | return max_session; |
3334 | 0 | } |
3335 | | |
3336 | 300 | ProducerID TracingServiceImpl::GetNextProducerID() { |
3337 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
3338 | 300 | PERFETTO_CHECK(producers_.size() < kMaxProducerID); |
3339 | 300 | do { |
3340 | 300 | ++last_producer_id_; |
3341 | 300 | } while (producers_.count(last_producer_id_) || last_producer_id_ == 0); |
3342 | 300 | PERFETTO_DCHECK(last_producer_id_ > 0 && last_producer_id_ <= kMaxProducerID); |
3343 | 300 | return last_producer_id_; |
3344 | 300 | } |
3345 | | |
3346 | 45.2k | TraceBuffer* TracingServiceImpl::GetBufferByID(BufferID buffer_id) { |
3347 | 45.2k | auto buf_iter = buffers_.find(buffer_id); |
3348 | 45.2k | if (buf_iter == buffers_.end()) |
3349 | 0 | return nullptr; |
3350 | 45.2k | return &*buf_iter->second; |
3351 | 45.2k | } |
3352 | | |
3353 | 0 | void TracingServiceImpl::OnStartTriggersTimeout(TracingSessionID tsid) { |
3354 | | // Skip entirely the flush if the trace session doesn't exist anymore. |
3355 | | // This is to prevent misleading error messages to be logged. |
3356 | | // |
3357 | | // if the trace has started from the trigger we rely on |
3358 | | // the |stop_delay_ms| from the trigger so don't flush and |
3359 | | // disable if we've moved beyond a CONFIGURED state |
3360 | 0 | auto* tracing_session_ptr = GetTracingSession(tsid); |
3361 | 0 | if (tracing_session_ptr && |
3362 | 0 | tracing_session_ptr->state == TracingSession::CONFIGURED) { |
3363 | 0 | PERFETTO_DLOG("Disabling TracingSession %" PRIu64 |
3364 | 0 | " since no triggers activated.", |
3365 | 0 | tsid); |
3366 | | // No data should be returned from ReadBuffers() regardless of if we |
3367 | | // call FreeBuffers() or DisableTracing(). This is because in |
3368 | | // STOP_TRACING we need this promise in either case, and using |
3369 | | // DisableTracing() allows a graceful shutdown. Consumers can follow |
3370 | | // their normal path and check the buffers through ReadBuffers() and |
3371 | | // the code won't hang because the tracing session will still be |
3372 | | // alive just disabled. |
3373 | 0 | DisableTracing(tsid); |
3374 | 0 | } |
3375 | 0 | } |
3376 | | |
3377 | 1.20k | void TracingServiceImpl::UpdateMemoryGuardrail() { |
3378 | 1.20k | #if PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG) |
3379 | 1.20k | uint64_t total_buffer_bytes = 0; |
3380 | | |
3381 | | // Sum up all the shared memory buffers. |
3382 | 1.20k | for (const auto& id_to_producer : producers_) { |
3383 | 599 | if (id_to_producer.second->shared_memory()) |
3384 | 300 | total_buffer_bytes += id_to_producer.second->shared_memory()->size(); |
3385 | 599 | } |
3386 | | |
3387 | | // Sum up all the trace buffers. |
3388 | 1.20k | for (const auto& id_to_buffer : buffers_) { |
3389 | 900 | total_buffer_bytes += id_to_buffer.second->size(); |
3390 | 900 | } |
3391 | | |
3392 | | // Sum up all the cloned traced buffers. |
3393 | 1.20k | for (const auto& id_to_ts : tracing_sessions_) { |
3394 | 900 | const TracingSession& ts = id_to_ts.second; |
3395 | 900 | for (const auto& id_to_clone_op : ts.pending_clones) { |
3396 | 0 | const PendingClone& clone_op = id_to_clone_op.second; |
3397 | 0 | for (const std::unique_ptr<TraceBuffer>& buf : clone_op.buffers) { |
3398 | 0 | if (buf) { |
3399 | 0 | total_buffer_bytes += buf->size(); |
3400 | 0 | } |
3401 | 0 | } |
3402 | 0 | } |
3403 | 900 | } |
3404 | | |
3405 | | // Set the guard rail to 32MB + the sum of all the buffers over a 30 second |
3406 | | // interval. |
3407 | 1.20k | uint64_t guardrail = base::kWatchdogDefaultMemorySlack + total_buffer_bytes; |
3408 | 1.20k | base::Watchdog::GetInstance()->SetMemoryLimit(guardrail, 30 * 1000); |
3409 | 1.20k | #endif |
3410 | 1.20k | } |
3411 | | |
3412 | 300 | void TracingServiceImpl::PeriodicSnapshotTask(TracingSessionID tsid) { |
3413 | 300 | auto* tracing_session = GetTracingSession(tsid); |
3414 | 300 | if (!tracing_session) |
3415 | 0 | return; |
3416 | 300 | if (tracing_session->state != TracingSession::STARTED) |
3417 | 0 | return; |
3418 | 300 | tracing_session->should_emit_sync_marker = true; |
3419 | 300 | tracing_session->should_emit_stats = true; |
3420 | 300 | MaybeSnapshotClocksIntoRingBuffer(tracing_session); |
3421 | 300 | } |
3422 | | |
3423 | | void TracingServiceImpl::SnapshotLifecycleEvent(TracingSession* tracing_session, |
3424 | | uint32_t field_id, |
3425 | 1.20k | bool snapshot_clocks) { |
3426 | | // field_id should be an id of a field in TracingServiceEvent. |
3427 | 1.20k | auto& lifecycle_events = tracing_session->lifecycle_events; |
3428 | 1.20k | auto event_it = |
3429 | 1.20k | std::find_if(lifecycle_events.begin(), lifecycle_events.end(), |
3430 | 4.20k | [field_id](const TracingSession::LifecycleEvent& event) { |
3431 | 4.20k | return event.field_id == field_id; |
3432 | 4.20k | }); |
3433 | | |
3434 | 1.20k | TracingSession::LifecycleEvent* event; |
3435 | 1.20k | if (event_it == lifecycle_events.end()) { |
3436 | 1.20k | lifecycle_events.emplace_back(field_id); |
3437 | 1.20k | event = &lifecycle_events.back(); |
3438 | 1.20k | } else { |
3439 | 0 | event = &*event_it; |
3440 | 0 | } |
3441 | | |
3442 | | // Snapshot the clocks before capturing the timestamp for the event so we can |
3443 | | // use this snapshot to resolve the event timestamp if necessary. |
3444 | 1.20k | if (snapshot_clocks) |
3445 | 600 | MaybeSnapshotClocksIntoRingBuffer(tracing_session); |
3446 | | |
3447 | | // Erase before emplacing to prevent a unncessary doubling of memory if |
3448 | | // not needed. |
3449 | 1.20k | if (event->timestamps.size() >= event->max_size) { |
3450 | 0 | event->timestamps.erase_front(1 + event->timestamps.size() - |
3451 | 0 | event->max_size); |
3452 | 0 | } |
3453 | 1.20k | event->timestamps.emplace_back(clock_->GetBootTimeNs().count()); |
3454 | 1.20k | } |
3455 | | |
3456 | | void TracingServiceImpl::SetSingleLifecycleEvent( |
3457 | | TracingSession* tracing_session, |
3458 | | uint32_t field_id, |
3459 | 0 | int64_t boot_timestamp_ns) { |
3460 | | // field_id should be an id of a field in TracingServiceEvent. |
3461 | 0 | auto& lifecycle_events = tracing_session->lifecycle_events; |
3462 | 0 | auto event_it = |
3463 | 0 | std::find_if(lifecycle_events.begin(), lifecycle_events.end(), |
3464 | 0 | [field_id](const TracingSession::LifecycleEvent& event) { |
3465 | 0 | return event.field_id == field_id; |
3466 | 0 | }); |
3467 | |
|
3468 | 0 | TracingSession::LifecycleEvent* event; |
3469 | 0 | if (event_it == lifecycle_events.end()) { |
3470 | 0 | lifecycle_events.emplace_back(field_id); |
3471 | 0 | event = &lifecycle_events.back(); |
3472 | 0 | } else { |
3473 | 0 | event = &*event_it; |
3474 | 0 | } |
3475 | |
|
3476 | 0 | event->timestamps.clear(); |
3477 | 0 | event->timestamps.emplace_back(boot_timestamp_ns); |
3478 | 0 | } |
3479 | | |
3480 | | void TracingServiceImpl::MaybeSnapshotClocksIntoRingBuffer( |
3481 | 900 | TracingSession* tracing_session) { |
3482 | 900 | if (tracing_session->config.builtin_data_sources() |
3483 | 900 | .disable_clock_snapshotting()) { |
3484 | 0 | return; |
3485 | 0 | } |
3486 | | |
3487 | | // We are making an explicit copy of the latest snapshot (if it exists) |
3488 | | // because SnapshotClocks reads this data and computes the drift based on its |
3489 | | // content. If the clock drift is high enough, it will update the contents of |
3490 | | // |snapshot| and return true. Otherwise, it will return false. |
3491 | 900 | TracingSession::ClockSnapshotData snapshot = |
3492 | 900 | tracing_session->clock_snapshot_ring_buffer.empty() |
3493 | 900 | ? TracingSession::ClockSnapshotData() |
3494 | 900 | : tracing_session->clock_snapshot_ring_buffer.back(); |
3495 | 900 | bool did_update = SnapshotClocks(&snapshot); |
3496 | 900 | if (did_update) { |
3497 | | // This means clocks drifted enough since last snapshot. See the comment |
3498 | | // in SnapshotClocks. |
3499 | 600 | auto* snapshot_buffer = &tracing_session->clock_snapshot_ring_buffer; |
3500 | | |
3501 | | // Erase before emplacing to prevent a unncessary doubling of memory if |
3502 | | // not needed. |
3503 | 600 | static constexpr uint32_t kClockSnapshotRingBufferSize = 16; |
3504 | 600 | if (snapshot_buffer->size() >= kClockSnapshotRingBufferSize) { |
3505 | 0 | snapshot_buffer->erase_front(1 + snapshot_buffer->size() - |
3506 | 0 | kClockSnapshotRingBufferSize); |
3507 | 0 | } |
3508 | 600 | snapshot_buffer->emplace_back(std::move(snapshot)); |
3509 | 600 | } |
3510 | 900 | } |
3511 | | |
3512 | | // Returns true when the data in |snapshot_data| is updated with the new state |
3513 | | // of the clocks and false otherwise. |
3514 | | bool TracingServiceImpl::SnapshotClocks( |
3515 | 1.20k | TracingSession::ClockSnapshotData* snapshot_data) { |
3516 | | // Minimum drift that justifies replacing a prior clock snapshot that hasn't |
3517 | | // been emitted into the trace yet (see comment below). |
3518 | 1.20k | static constexpr int64_t kSignificantDriftNs = 10 * 1000 * 1000; // 10 ms |
3519 | | |
3520 | 1.20k | TracingSession::ClockSnapshotData new_snapshot_data = |
3521 | 1.20k | base::CaptureClockSnapshots(); |
3522 | | // If we're about to update a session's latest clock snapshot that hasn't been |
3523 | | // emitted into the trace yet, check whether the clocks have drifted enough to |
3524 | | // warrant overriding the current snapshot values. The older snapshot would be |
3525 | | // valid for a larger part of the currently buffered trace data because the |
3526 | | // clock sync protocol in trace processor uses the latest clock <= timestamp |
3527 | | // to translate times (see https://perfetto.dev/docs/concepts/clock-sync), so |
3528 | | // we try to keep it if we can. |
3529 | 1.20k | if (!snapshot_data->empty()) { |
3530 | 300 | PERFETTO_DCHECK(snapshot_data->size() == new_snapshot_data.size()); |
3531 | 300 | PERFETTO_DCHECK((*snapshot_data)[0].clock_id == |
3532 | 300 | protos::gen::BUILTIN_CLOCK_BOOTTIME); |
3533 | | |
3534 | 300 | bool update_snapshot = false; |
3535 | 300 | uint64_t old_boot_ns = (*snapshot_data)[0].timestamp; |
3536 | 300 | uint64_t new_boot_ns = new_snapshot_data[0].timestamp; |
3537 | 300 | int64_t boot_diff = |
3538 | 300 | static_cast<int64_t>(new_boot_ns) - static_cast<int64_t>(old_boot_ns); |
3539 | | |
3540 | 2.10k | for (size_t i = 1; i < snapshot_data->size(); i++) { |
3541 | 1.80k | uint64_t old_ns = (*snapshot_data)[i].timestamp; |
3542 | 1.80k | uint64_t new_ns = new_snapshot_data[i].timestamp; |
3543 | | |
3544 | 1.80k | int64_t diff = |
3545 | 1.80k | static_cast<int64_t>(new_ns) - static_cast<int64_t>(old_ns); |
3546 | | |
3547 | | // Compare the boottime delta against the delta of this clock. |
3548 | 1.80k | if (std::abs(boot_diff - diff) >= kSignificantDriftNs) { |
3549 | 0 | update_snapshot = true; |
3550 | 0 | break; |
3551 | 0 | } |
3552 | 1.80k | } |
3553 | 300 | if (!update_snapshot) |
3554 | 300 | return false; |
3555 | 0 | snapshot_data->clear(); |
3556 | 0 | } |
3557 | | |
3558 | 900 | *snapshot_data = std::move(new_snapshot_data); |
3559 | 900 | return true; |
3560 | 1.20k | } |
3561 | | |
3562 | | void TracingServiceImpl::EmitClockSnapshot( |
3563 | | TracingSession* tracing_session, |
3564 | | TracingSession::ClockSnapshotData snapshot_data, |
3565 | 600 | std::vector<TracePacket>* packets) { |
3566 | 600 | PERFETTO_DCHECK(!tracing_session->config.builtin_data_sources() |
3567 | 600 | .disable_clock_snapshotting()); |
3568 | | |
3569 | 600 | protozero::HeapBuffered<protos::pbzero::TracePacket> packet; |
3570 | 600 | auto* snapshot = packet->set_clock_snapshot(); |
3571 | | |
3572 | 600 | protos::gen::BuiltinClock trace_clock = |
3573 | 600 | tracing_session->config.builtin_data_sources().primary_trace_clock(); |
3574 | 600 | if (!trace_clock) |
3575 | 600 | trace_clock = protos::gen::BUILTIN_CLOCK_BOOTTIME; |
3576 | 600 | snapshot->set_primary_trace_clock( |
3577 | 600 | static_cast<protos::pbzero::BuiltinClock>(trace_clock)); |
3578 | | |
3579 | 4.20k | for (auto& clock_id_and_ts : snapshot_data) { |
3580 | 4.20k | auto* c = snapshot->add_clocks(); |
3581 | 4.20k | c->set_clock_id(clock_id_and_ts.clock_id); |
3582 | 4.20k | c->set_timestamp(clock_id_and_ts.timestamp); |
3583 | 4.20k | } |
3584 | | |
3585 | 600 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
3586 | 600 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
3587 | 600 | SerializeAndAppendPacket(packets, packet.SerializeAsArray()); |
3588 | 600 | } |
3589 | | |
3590 | 300 | void TracingServiceImpl::EmitSyncMarker(std::vector<TracePacket>* packets) { |
3591 | | // The sync marks are used to tokenize large traces efficiently. |
3592 | | // See description in trace_packet.proto. |
3593 | 300 | if (sync_marker_packet_size_ == 0) { |
3594 | | // The marker ABI expects that the marker is written after the uid. |
3595 | | // Protozero guarantees that fields are written in the same order of the |
3596 | | // calls. The ResynchronizeTraceStreamUsingSyncMarker test verifies the ABI. |
3597 | 300 | protozero::StaticBuffered<protos::pbzero::TracePacket> packet( |
3598 | 300 | &sync_marker_packet_[0], sizeof(sync_marker_packet_)); |
3599 | 300 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
3600 | 300 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
3601 | | |
3602 | | // Keep this last. |
3603 | 300 | packet->set_synchronization_marker(kSyncMarker, sizeof(kSyncMarker)); |
3604 | 300 | sync_marker_packet_size_ = packet.Finalize(); |
3605 | 300 | } |
3606 | 300 | packets->emplace_back(); |
3607 | 300 | packets->back().AddSlice(&sync_marker_packet_[0], sync_marker_packet_size_); |
3608 | 300 | } |
3609 | | |
3610 | | void TracingServiceImpl::EmitStats(TracingSession* tracing_session, |
3611 | 300 | std::vector<TracePacket>* packets) { |
3612 | 300 | protozero::HeapBuffered<protos::pbzero::TracePacket> packet; |
3613 | 300 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
3614 | 300 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
3615 | 300 | GetTraceStats(tracing_session).Serialize(packet->set_trace_stats()); |
3616 | 300 | SerializeAndAppendPacket(packets, packet.SerializeAsArray()); |
3617 | 300 | } |
3618 | | |
3619 | 300 | TraceStats TracingServiceImpl::GetTraceStats(TracingSession* tracing_session) { |
3620 | 300 | TraceStats trace_stats; |
3621 | 300 | trace_stats.set_producers_connected(static_cast<uint32_t>(producers_.size())); |
3622 | 300 | trace_stats.set_producers_seen(last_producer_id_); |
3623 | 300 | trace_stats.set_data_sources_registered( |
3624 | 300 | static_cast<uint32_t>(data_sources_.size())); |
3625 | 300 | trace_stats.set_data_sources_seen(last_data_source_instance_id_); |
3626 | 300 | trace_stats.set_tracing_sessions( |
3627 | 300 | static_cast<uint32_t>(tracing_sessions_.size())); |
3628 | 300 | trace_stats.set_total_buffers(static_cast<uint32_t>(buffers_.size())); |
3629 | 300 | trace_stats.set_chunks_discarded(chunks_discarded_); |
3630 | 300 | trace_stats.set_patches_discarded(patches_discarded_); |
3631 | 300 | trace_stats.set_invalid_packets(tracing_session->invalid_packets); |
3632 | 300 | trace_stats.set_flushes_requested(tracing_session->flushes_requested); |
3633 | 300 | trace_stats.set_flushes_succeeded(tracing_session->flushes_succeeded); |
3634 | 300 | trace_stats.set_flushes_failed(tracing_session->flushes_failed); |
3635 | 300 | trace_stats.set_final_flush_outcome(tracing_session->final_flush_outcome); |
3636 | | |
3637 | 300 | if (tracing_session->trace_filter) { |
3638 | 0 | auto* filt_stats = trace_stats.mutable_filter_stats(); |
3639 | 0 | filt_stats->set_input_packets(tracing_session->filter_input_packets); |
3640 | 0 | filt_stats->set_input_bytes(tracing_session->filter_input_bytes); |
3641 | 0 | filt_stats->set_output_bytes(tracing_session->filter_output_bytes); |
3642 | 0 | filt_stats->set_errors(tracing_session->filter_errors); |
3643 | 0 | filt_stats->set_time_taken_ns(tracing_session->filter_time_taken_ns); |
3644 | 0 | for (uint64_t value : tracing_session->filter_bytes_discarded_per_buffer) |
3645 | 0 | filt_stats->add_bytes_discarded_per_buffer(value); |
3646 | 0 | } |
3647 | | |
3648 | 300 | for (BufferID buf_id : tracing_session->buffers_index) { |
3649 | 300 | TraceBuffer* buf = GetBufferByID(buf_id); |
3650 | 300 | if (!buf) { |
3651 | 0 | PERFETTO_DFATAL("Buffer not found."); |
3652 | 0 | continue; |
3653 | 0 | } |
3654 | 300 | *trace_stats.add_buffer_stats() = buf->stats(); |
3655 | 300 | } // for (buf in session). |
3656 | | |
3657 | 300 | if (!tracing_session->config.builtin_data_sources() |
3658 | 300 | .disable_chunk_usage_histograms()) { |
3659 | | // Emit chunk usage stats broken down by sequence ID (i.e. by trace-writer). |
3660 | | // Writer stats are updated by each TraceBuffer object at ReadBuffers time, |
3661 | | // and there can be >1 buffer per session. A trace writer never writes to |
3662 | | // more than one buffer (it's technically allowed but doesn't happen in the |
3663 | | // current impl of the tracing SDK). |
3664 | | |
3665 | 300 | bool has_written_bucket_definition = false; |
3666 | 300 | uint32_t buf_idx = static_cast<uint32_t>(-1); |
3667 | 300 | for (const BufferID buf_id : tracing_session->buffers_index) { |
3668 | 300 | ++buf_idx; |
3669 | 300 | const TraceBuffer* buf = GetBufferByID(buf_id); |
3670 | 300 | if (!buf) |
3671 | 0 | continue; |
3672 | 600 | for (auto it = buf->writer_stats().GetIterator(); it; ++it) { |
3673 | 300 | const auto& hist = it.value().used_chunk_hist; |
3674 | 300 | ProducerID p; |
3675 | 300 | WriterID w; |
3676 | 300 | GetProducerAndWriterID(it.key(), &p, &w); |
3677 | 300 | if (!has_written_bucket_definition) { |
3678 | | // Serialize one-off the histogram bucket definition, which is the |
3679 | | // same for all entries in the map. |
3680 | 300 | has_written_bucket_definition = true; |
3681 | | // The -1 in the loop below is to skip the implicit overflow bucket. |
3682 | 3.30k | for (size_t i = 0; i < hist.num_buckets() - 1; ++i) { |
3683 | 3.00k | trace_stats.add_chunk_payload_histogram_def(hist.GetBucketThres(i)); |
3684 | 3.00k | } |
3685 | 300 | } // if(!has_written_bucket_definition) |
3686 | 300 | auto* wri_stats = trace_stats.add_writer_stats(); |
3687 | 300 | wri_stats->set_sequence_id( |
3688 | 300 | tracing_session->GetPacketSequenceID(kDefaultMachineID, p, w)); |
3689 | 300 | wri_stats->set_buffer(buf_idx); |
3690 | 3.60k | for (size_t i = 0; i < hist.num_buckets(); ++i) { |
3691 | 3.30k | wri_stats->add_chunk_payload_histogram_counts(hist.GetBucketCount(i)); |
3692 | 3.30k | wri_stats->add_chunk_payload_histogram_sum(hist.GetBucketSum(i)); |
3693 | 3.30k | } |
3694 | 300 | } // for each sequence (writer). |
3695 | 300 | } // for each buffer. |
3696 | 300 | } // if (!disable_chunk_usage_histograms) |
3697 | | |
3698 | 300 | return trace_stats; |
3699 | 300 | } |
3700 | | |
3701 | | void TracingServiceImpl::EmitUuid(TracingSession* tracing_session, |
3702 | 300 | std::vector<TracePacket>* packets) { |
3703 | 300 | protozero::HeapBuffered<protos::pbzero::TracePacket> packet; |
3704 | 300 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
3705 | 300 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
3706 | 300 | auto* uuid = packet->set_trace_uuid(); |
3707 | 300 | uuid->set_lsb(tracing_session->trace_uuid.lsb()); |
3708 | 300 | uuid->set_msb(tracing_session->trace_uuid.msb()); |
3709 | 300 | SerializeAndAppendPacket(packets, packet.SerializeAsArray()); |
3710 | 300 | } |
3711 | | |
3712 | | void TracingServiceImpl::MaybeEmitTraceConfig( |
3713 | | TracingSession* tracing_session, |
3714 | 300 | std::vector<TracePacket>* packets) { |
3715 | 300 | if (tracing_session->did_emit_initial_packets) |
3716 | 0 | return; |
3717 | 300 | protozero::HeapBuffered<protos::pbzero::TracePacket> packet; |
3718 | 300 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
3719 | 300 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
3720 | 300 | tracing_session->config.Serialize(packet->set_trace_config()); |
3721 | 300 | SerializeAndAppendPacket(packets, packet.SerializeAsArray()); |
3722 | 300 | } |
3723 | | |
3724 | 300 | void TracingServiceImpl::EmitSystemInfo(std::vector<TracePacket>* packets) { |
3725 | 300 | protozero::HeapBuffered<protos::pbzero::TracePacket> packet; |
3726 | 300 | auto* info = packet->set_system_info(); |
3727 | 300 | info->set_tracing_service_version(base::GetVersionString()); |
3728 | | |
3729 | 300 | std::optional<int32_t> tzoff = base::GetTimezoneOffsetMins(); |
3730 | 300 | if (tzoff.has_value()) |
3731 | 300 | info->set_timezone_off_mins(*tzoff); |
3732 | | |
3733 | 300 | #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \ |
3734 | 300 | !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL) |
3735 | 300 | struct utsname uname_info; |
3736 | 300 | if (uname(&uname_info) == 0) { |
3737 | 300 | auto* utsname_info = info->set_utsname(); |
3738 | 300 | utsname_info->set_sysname(uname_info.sysname); |
3739 | 300 | utsname_info->set_version(uname_info.version); |
3740 | 300 | utsname_info->set_machine(uname_info.machine); |
3741 | 300 | utsname_info->set_release(uname_info.release); |
3742 | 300 | } |
3743 | 300 | info->set_page_size(static_cast<uint32_t>(sysconf(_SC_PAGESIZE))); |
3744 | 300 | info->set_num_cpus(static_cast<uint32_t>(sysconf(_SC_NPROCESSORS_CONF))); |
3745 | 300 | #endif // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) |
3746 | | #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) |
3747 | | std::string fingerprint_value = base::GetAndroidProp("ro.build.fingerprint"); |
3748 | | if (!fingerprint_value.empty()) { |
3749 | | info->set_android_build_fingerprint(fingerprint_value); |
3750 | | } else { |
3751 | | PERFETTO_ELOG("Unable to read ro.build.fingerprint"); |
3752 | | } |
3753 | | |
3754 | | std::string device_manufacturer_value = |
3755 | | base::GetAndroidProp("ro.product.manufacturer"); |
3756 | | if (!device_manufacturer_value.empty()) { |
3757 | | info->set_android_device_manufacturer(device_manufacturer_value); |
3758 | | } else { |
3759 | | PERFETTO_ELOG("Unable to read ro.product.manufacturer"); |
3760 | | } |
3761 | | |
3762 | | std::string sdk_str_value = base::GetAndroidProp("ro.build.version.sdk"); |
3763 | | std::optional<uint64_t> sdk_value = base::StringToUInt64(sdk_str_value); |
3764 | | if (sdk_value.has_value()) { |
3765 | | info->set_android_sdk_version(*sdk_value); |
3766 | | } else { |
3767 | | PERFETTO_ELOG("Unable to read ro.build.version.sdk"); |
3768 | | } |
3769 | | |
3770 | | std::string soc_model_value = base::GetAndroidProp("ro.soc.model"); |
3771 | | if (!soc_model_value.empty()) { |
3772 | | info->set_android_soc_model(soc_model_value); |
3773 | | } else { |
3774 | | PERFETTO_ELOG("Unable to read ro.soc.model"); |
3775 | | } |
3776 | | |
3777 | | // guest_soc model is not always present |
3778 | | std::string guest_soc_model_value = |
3779 | | base::GetAndroidProp("ro.boot.guest_soc.model"); |
3780 | | if (!guest_soc_model_value.empty()) { |
3781 | | info->set_android_guest_soc_model(guest_soc_model_value); |
3782 | | } |
3783 | | |
3784 | | std::string hw_rev_value = base::GetAndroidProp("ro.boot.hardware.revision"); |
3785 | | if (!hw_rev_value.empty()) { |
3786 | | info->set_android_hardware_revision(hw_rev_value); |
3787 | | } else { |
3788 | | PERFETTO_ELOG("Unable to read ro.boot.hardware.revision"); |
3789 | | } |
3790 | | |
3791 | | std::string hw_ufs_value = base::GetAndroidProp("ro.boot.hardware.ufs"); |
3792 | | if (!hw_ufs_value.empty()) { |
3793 | | info->set_android_storage_model(hw_ufs_value); |
3794 | | } else { |
3795 | | PERFETTO_ELOG("Unable to read ro.boot.hardware.ufs"); |
3796 | | } |
3797 | | |
3798 | | std::string hw_ddr_value = base::GetAndroidProp("ro.boot.hardware.ddr"); |
3799 | | if (!hw_ddr_value.empty()) { |
3800 | | info->set_android_ram_model(hw_ddr_value); |
3801 | | } else { |
3802 | | PERFETTO_ELOG("Unable to read ro.boot.hardware.ddr"); |
3803 | | } |
3804 | | |
3805 | | #endif // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) |
3806 | 300 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
3807 | 300 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
3808 | 300 | SerializeAndAppendPacket(packets, packet.SerializeAsArray()); |
3809 | 300 | } |
3810 | | |
3811 | | void TracingServiceImpl::EmitLifecycleEvents( |
3812 | | TracingSession* tracing_session, |
3813 | 600 | std::vector<TracePacket>* packets) { |
3814 | 600 | using TimestampedPacket = |
3815 | 600 | std::pair<int64_t /* ts */, std::vector<uint8_t> /* serialized packet */>; |
3816 | | |
3817 | 600 | std::vector<TimestampedPacket> timestamped_packets; |
3818 | 2.70k | for (auto& event : tracing_session->lifecycle_events) { |
3819 | 2.70k | for (int64_t ts : event.timestamps) { |
3820 | 900 | protozero::HeapBuffered<protos::pbzero::TracePacket> packet; |
3821 | 900 | packet->set_timestamp(static_cast<uint64_t>(ts)); |
3822 | 900 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
3823 | 900 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
3824 | | |
3825 | 900 | auto* service_event = packet->set_service_event(); |
3826 | 900 | service_event->AppendVarInt(event.field_id, 1); |
3827 | 900 | timestamped_packets.emplace_back(ts, packet.SerializeAsArray()); |
3828 | 900 | } |
3829 | 2.70k | event.timestamps.clear(); |
3830 | 2.70k | } |
3831 | | |
3832 | 600 | if (tracing_session->slow_start_event.has_value()) { |
3833 | 0 | const TracingSession::ArbitraryLifecycleEvent& event = |
3834 | 0 | *tracing_session->slow_start_event; |
3835 | 0 | timestamped_packets.emplace_back(event.timestamp, std::move(event.data)); |
3836 | 0 | } |
3837 | 600 | tracing_session->slow_start_event.reset(); |
3838 | | |
3839 | 600 | for (auto& event : tracing_session->last_flush_events) { |
3840 | 0 | timestamped_packets.emplace_back(event.timestamp, std::move(event.data)); |
3841 | 0 | } |
3842 | 600 | tracing_session->last_flush_events.clear(); |
3843 | | |
3844 | 600 | for (size_t i = 0; i < tracing_session->buffer_cloned_timestamps.size(); |
3845 | 600 | i++) { |
3846 | 0 | protozero::HeapBuffered<protos::pbzero::TracePacket> packet; |
3847 | 0 | int64_t ts = tracing_session->buffer_cloned_timestamps[i]; |
3848 | 0 | packet->set_timestamp(static_cast<uint64_t>(ts)); |
3849 | 0 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
3850 | 0 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
3851 | |
|
3852 | 0 | auto* service_event = packet->set_service_event(); |
3853 | 0 | service_event->set_buffer_cloned(static_cast<uint32_t>(i)); |
3854 | |
|
3855 | 0 | timestamped_packets.emplace_back(ts, packet.SerializeAsArray()); |
3856 | 0 | } |
3857 | 600 | tracing_session->buffer_cloned_timestamps.clear(); |
3858 | | |
3859 | | // We sort by timestamp here to ensure that the "sequence" of lifecycle |
3860 | | // packets has monotonic timestamps like other sequences in the trace. |
3861 | | // Note that these events could still be out of order with respect to other |
3862 | | // events on the service packet sequence (e.g. trigger received packets). |
3863 | 600 | std::sort(timestamped_packets.begin(), timestamped_packets.end(), |
3864 | 600 | [](const TimestampedPacket& a, const TimestampedPacket& b) { |
3865 | 300 | return a.first < b.first; |
3866 | 300 | }); |
3867 | | |
3868 | 600 | for (auto& pair : timestamped_packets) |
3869 | 900 | SerializeAndAppendPacket(packets, std::move(pair.second)); |
3870 | 600 | } |
3871 | | |
3872 | | void TracingServiceImpl::MaybeEmitRemoteClockSync( |
3873 | | TracingSession* tracing_session, |
3874 | 0 | std::vector<TracePacket>* packets) { |
3875 | 0 | if (tracing_session->did_emit_remote_clock_sync_) |
3876 | 0 | return; |
3877 | | |
3878 | 0 | std::unordered_set<MachineID> did_emit_machines; |
3879 | 0 | for (const auto& id_and_relay_client : relay_clients_) { |
3880 | 0 | const auto& relay_client = id_and_relay_client.second; |
3881 | 0 | auto machine_id = relay_client->machine_id(); |
3882 | 0 | if (did_emit_machines.find(machine_id) != did_emit_machines.end()) |
3883 | 0 | continue; // Already emitted for the machine (e.g. multiple clients). |
3884 | | |
3885 | 0 | auto& sync_clock_snapshots = relay_client->synced_clocks(); |
3886 | 0 | if (sync_clock_snapshots.empty()) { |
3887 | 0 | PERFETTO_DLOG("Clock not synchronized for machine ID = %" PRIu32, |
3888 | 0 | machine_id); |
3889 | 0 | continue; |
3890 | 0 | } |
3891 | | |
3892 | | // Don't emit twice for the same machine. |
3893 | 0 | did_emit_machines.insert(machine_id); |
3894 | |
|
3895 | 0 | protozero::HeapBuffered<protos::pbzero::TracePacket> sync_packet; |
3896 | 0 | sync_packet->set_machine_id(machine_id); |
3897 | 0 | sync_packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
3898 | 0 | auto* remote_clock_sync = sync_packet->set_remote_clock_sync(); |
3899 | 0 | for (const auto& sync_exchange : relay_client->synced_clocks()) { |
3900 | 0 | auto* sync_exchange_msg = remote_clock_sync->add_synced_clocks(); |
3901 | |
|
3902 | 0 | auto* client_snapshots = sync_exchange_msg->set_client_clocks(); |
3903 | 0 | for (const auto& client_clock : sync_exchange.client_clocks) { |
3904 | 0 | auto* clock = client_snapshots->add_clocks(); |
3905 | 0 | clock->set_clock_id(client_clock.clock_id); |
3906 | 0 | clock->set_timestamp(client_clock.timestamp); |
3907 | 0 | } |
3908 | |
|
3909 | 0 | auto* host_snapshots = sync_exchange_msg->set_host_clocks(); |
3910 | 0 | for (const auto& host_clock : sync_exchange.host_clocks) { |
3911 | 0 | auto* clock = host_snapshots->add_clocks(); |
3912 | 0 | clock->set_clock_id(host_clock.clock_id); |
3913 | 0 | clock->set_timestamp(host_clock.timestamp); |
3914 | 0 | } |
3915 | 0 | } |
3916 | |
|
3917 | 0 | SerializeAndAppendPacket(packets, sync_packet.SerializeAsArray()); |
3918 | 0 | } |
3919 | |
|
3920 | 0 | tracing_session->did_emit_remote_clock_sync_ = true; |
3921 | 0 | } |
3922 | | |
3923 | | void TracingServiceImpl::MaybeEmitCloneTrigger( |
3924 | | TracingSession* tracing_session, |
3925 | 300 | std::vector<TracePacket>* packets) { |
3926 | 300 | if (tracing_session->clone_trigger.has_value()) { |
3927 | 0 | protozero::HeapBuffered<protos::pbzero::TracePacket> packet; |
3928 | 0 | auto* trigger = packet->set_clone_snapshot_trigger(); |
3929 | 0 | const auto& info = tracing_session->clone_trigger.value(); |
3930 | 0 | trigger->set_trigger_name(info.trigger_name); |
3931 | 0 | trigger->set_producer_name(info.producer_name); |
3932 | 0 | trigger->set_trusted_producer_uid(static_cast<int32_t>(info.producer_uid)); |
3933 | |
|
3934 | 0 | packet->set_timestamp(info.boot_time_ns); |
3935 | 0 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
3936 | 0 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
3937 | 0 | SerializeAndAppendPacket(packets, packet.SerializeAsArray()); |
3938 | 0 | } |
3939 | 300 | } |
3940 | | |
3941 | | void TracingServiceImpl::MaybeEmitReceivedTriggers( |
3942 | | TracingSession* tracing_session, |
3943 | 300 | std::vector<TracePacket>* packets) { |
3944 | 300 | PERFETTO_DCHECK(tracing_session->num_triggers_emitted_into_trace <= |
3945 | 300 | tracing_session->received_triggers.size()); |
3946 | 300 | for (size_t i = tracing_session->num_triggers_emitted_into_trace; |
3947 | 300 | i < tracing_session->received_triggers.size(); ++i) { |
3948 | 0 | const auto& info = tracing_session->received_triggers[i]; |
3949 | 0 | protozero::HeapBuffered<protos::pbzero::TracePacket> packet; |
3950 | 0 | auto* trigger = packet->set_trigger(); |
3951 | 0 | trigger->set_trigger_name(info.trigger_name); |
3952 | 0 | trigger->set_producer_name(info.producer_name); |
3953 | 0 | trigger->set_trusted_producer_uid(static_cast<int32_t>(info.producer_uid)); |
3954 | |
|
3955 | 0 | packet->set_timestamp(info.boot_time_ns); |
3956 | 0 | packet->set_trusted_uid(static_cast<int32_t>(uid_)); |
3957 | 0 | packet->set_trusted_packet_sequence_id(kServicePacketSequenceID); |
3958 | 0 | SerializeAndAppendPacket(packets, packet.SerializeAsArray()); |
3959 | 0 | ++tracing_session->num_triggers_emitted_into_trace; |
3960 | 0 | } |
3961 | 300 | } |
3962 | | |
3963 | | void TracingServiceImpl::MaybeLogUploadEvent(const TraceConfig& cfg, |
3964 | | const base::Uuid& uuid, |
3965 | | PerfettoStatsdAtom atom, |
3966 | 1.20k | const std::string& trigger_name) { |
3967 | 1.20k | if (!ShouldLogEvent(cfg)) |
3968 | 1.20k | return; |
3969 | | |
3970 | 1.20k | PERFETTO_DCHECK(uuid); // The UUID must be set at this point. |
3971 | 0 | android_stats::MaybeLogUploadEvent(atom, uuid.lsb(), uuid.msb(), |
3972 | 0 | trigger_name); |
3973 | 0 | } |
3974 | | |
3975 | | void TracingServiceImpl::MaybeLogTriggerEvent(const TraceConfig& cfg, |
3976 | | PerfettoTriggerAtom atom, |
3977 | 0 | const std::string& trigger_name) { |
3978 | 0 | if (!ShouldLogEvent(cfg)) |
3979 | 0 | return; |
3980 | 0 | android_stats::MaybeLogTriggerEvent(atom, trigger_name); |
3981 | 0 | } |
3982 | | |
3983 | | size_t TracingServiceImpl::PurgeExpiredAndCountTriggerInWindow( |
3984 | | int64_t now_ns, |
3985 | 0 | uint64_t trigger_name_hash) { |
3986 | 0 | constexpr int64_t kOneDayInNs = 24ll * 60 * 60 * 1000 * 1000 * 1000; |
3987 | 0 | PERFETTO_DCHECK( |
3988 | 0 | std::is_sorted(trigger_history_.begin(), trigger_history_.end())); |
3989 | 0 | size_t remove_count = 0; |
3990 | 0 | size_t trigger_count = 0; |
3991 | 0 | for (const TriggerHistory& h : trigger_history_) { |
3992 | 0 | if (h.timestamp_ns < now_ns - kOneDayInNs) { |
3993 | 0 | remove_count++; |
3994 | 0 | } else if (h.name_hash == trigger_name_hash) { |
3995 | 0 | trigger_count++; |
3996 | 0 | } |
3997 | 0 | } |
3998 | 0 | trigger_history_.erase_front(remove_count); |
3999 | 0 | return trigger_count; |
4000 | 0 | } |
4001 | | |
4002 | | base::Status TracingServiceImpl::FlushAndCloneSession( |
4003 | | ConsumerEndpointImpl* consumer, |
4004 | 0 | ConsumerEndpoint::CloneSessionArgs args) { |
4005 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4006 | 0 | auto clone_target = FlushFlags::CloneTarget::kUnknown; |
4007 | |
|
4008 | 0 | TracingSession* session = nullptr; |
4009 | 0 | if (args.for_bugreport) { |
4010 | 0 | clone_target = FlushFlags::CloneTarget::kBugreport; |
4011 | 0 | } |
4012 | 0 | if (args.tsid != 0) { |
4013 | 0 | if (args.tsid == kBugreportSessionId) { |
4014 | | // This branch is only here to support the legacy protocol where we could |
4015 | | // clone only a single session using the magic ID kBugreportSessionId. |
4016 | | // The newer perfetto --clone-all-for-bugreport first queries the existing |
4017 | | // sessions and then issues individual clone requests specifying real |
4018 | | // session IDs, setting args.{for_bugreport,skip_trace_filter}=true. |
4019 | 0 | PERFETTO_LOG("Looking for sessions for bugreport"); |
4020 | 0 | session = FindTracingSessionWithMaxBugreportScore(); |
4021 | 0 | if (!session) { |
4022 | 0 | return base::ErrStatus( |
4023 | 0 | "No tracing sessions eligible for bugreport found"); |
4024 | 0 | } |
4025 | 0 | args.tsid = session->id; |
4026 | 0 | clone_target = FlushFlags::CloneTarget::kBugreport; |
4027 | 0 | args.skip_trace_filter = true; |
4028 | 0 | } else { |
4029 | 0 | session = GetTracingSession(args.tsid); |
4030 | 0 | } |
4031 | 0 | } else if (!args.unique_session_name.empty()) { |
4032 | 0 | session = GetTracingSessionByUniqueName(args.unique_session_name); |
4033 | 0 | } |
4034 | | |
4035 | 0 | if (!session) { |
4036 | 0 | return base::ErrStatus("Tracing session not found"); |
4037 | 0 | } |
4038 | | |
4039 | | // Skip the UID check for sessions marked with a bugreport_score > 0. |
4040 | | // Those sessions, by design, can be stolen by any other consumer for the |
4041 | | // sake of creating snapshots for bugreports. |
4042 | 0 | if (!session->IsCloneAllowed(consumer->uid_)) { |
4043 | 0 | return PERFETTO_SVC_ERR("Not allowed to clone a session from another UID"); |
4044 | 0 | } |
4045 | | |
4046 | | // If any of the buffers are marked as clear_before_clone, reset them before |
4047 | | // issuing the Flush(kCloneReason). |
4048 | 0 | size_t buf_idx = 0; |
4049 | 0 | for (BufferID src_buf_id : session->buffers_index) { |
4050 | 0 | if (!session->config.buffers()[buf_idx++].clear_before_clone()) |
4051 | 0 | continue; |
4052 | 0 | auto buf_iter = buffers_.find(src_buf_id); |
4053 | 0 | PERFETTO_CHECK(buf_iter != buffers_.end()); |
4054 | 0 | std::unique_ptr<TraceBuffer>& buf = buf_iter->second; |
4055 | | |
4056 | | // No need to reset the buffer if nothing has been written into it yet. |
4057 | | // This is the canonical case if producers behive nicely and don't timeout |
4058 | | // the handling of writes during the flush. |
4059 | | // This check avoids a useless re-mmap upon every Clone() if the buffer is |
4060 | | // already empty (when used in combination with `transfer_on_clone`). |
4061 | 0 | if (!buf->has_data()) |
4062 | 0 | continue; |
4063 | | |
4064 | | // Some leftover data was left in the buffer. Recreate it to empty it. |
4065 | 0 | const auto buf_policy = buf->overwrite_policy(); |
4066 | 0 | const auto buf_size = buf->size(); |
4067 | 0 | std::unique_ptr<TraceBuffer> old_buf = std::move(buf); |
4068 | 0 | buf = TraceBuffer::Create(buf_size, buf_policy); |
4069 | 0 | if (!buf) { |
4070 | | // This is extremely rare but could happen on 32-bit. If the new buffer |
4071 | | // allocation failed, put back the buffer where it was and fail the clone. |
4072 | | // We cannot leave the original tracing session buffer-less as it would |
4073 | | // cause crashes when data sources commit new data. |
4074 | 0 | buf = std::move(old_buf); |
4075 | 0 | return base::ErrStatus( |
4076 | 0 | "Buffer allocation failed while attempting to clone"); |
4077 | 0 | } |
4078 | 0 | } |
4079 | | |
4080 | 0 | auto weak_consumer = consumer->GetWeakPtr(); |
4081 | |
|
4082 | 0 | const PendingCloneID clone_id = session->last_pending_clone_id_++; |
4083 | |
|
4084 | 0 | auto& clone_op = session->pending_clones[clone_id]; |
4085 | 0 | clone_op.pending_flush_cnt = 0; |
4086 | | // Pre-initialize these vectors just as an optimization to avoid reallocations |
4087 | | // in DoCloneBuffers(). |
4088 | 0 | clone_op.buffers.reserve(session->buffers_index.size()); |
4089 | 0 | clone_op.buffer_cloned_timestamps.reserve(session->buffers_index.size()); |
4090 | 0 | clone_op.weak_consumer = weak_consumer; |
4091 | 0 | clone_op.skip_trace_filter = args.skip_trace_filter; |
4092 | 0 | if (!args.clone_trigger_name.empty()) { |
4093 | 0 | clone_op.clone_trigger = {args.clone_trigger_boot_time_ns, |
4094 | 0 | args.clone_trigger_name, |
4095 | 0 | args.clone_trigger_producer_name, |
4096 | 0 | args.clone_trigger_trusted_producer_uid}; |
4097 | 0 | } |
4098 | | |
4099 | | // Issue separate flush requests for separate buffer groups. The buffer marked |
4100 | | // as transfer_on_clone will be flushed and cloned separately: even if they're |
4101 | | // slower (like in the case of Winscope tracing), they will not delay the |
4102 | | // snapshot of the other buffers. |
4103 | | // |
4104 | | // In the future we might want to split the buffer into more groups and maybe |
4105 | | // allow this to be configurable. |
4106 | 0 | std::array<std::set<BufferID>, 2> bufs_groups; |
4107 | 0 | for (size_t i = 0; i < session->buffers_index.size(); i++) { |
4108 | 0 | if (session->config.buffers()[i].transfer_on_clone()) { |
4109 | 0 | bufs_groups[0].insert(session->buffers_index[i]); |
4110 | 0 | } else { |
4111 | 0 | bufs_groups[1].insert(session->buffers_index[i]); |
4112 | 0 | } |
4113 | 0 | } |
4114 | |
|
4115 | 0 | SnapshotLifecycleEvent( |
4116 | 0 | session, protos::pbzero::TracingServiceEvent::kFlushStartedFieldNumber, |
4117 | 0 | false /* snapshot_clocks */); |
4118 | 0 | clone_op.pending_flush_cnt = bufs_groups.size(); |
4119 | 0 | clone_op.clone_started_timestamp_ns = clock_->GetBootTimeNs().count(); |
4120 | 0 | for (const std::set<BufferID>& buf_group : bufs_groups) { |
4121 | 0 | FlushDataSourceInstances( |
4122 | 0 | session, 0, |
4123 | 0 | GetFlushableDataSourceInstancesForBuffers(session, buf_group), |
4124 | 0 | [tsid = session->id, clone_id, buf_group, this](bool final_flush) { |
4125 | 0 | OnFlushDoneForClone(tsid, clone_id, buf_group, final_flush); |
4126 | 0 | }, |
4127 | 0 | FlushFlags(FlushFlags::Initiator::kTraced, |
4128 | 0 | FlushFlags::Reason::kTraceClone, clone_target)); |
4129 | 0 | } |
4130 | |
|
4131 | 0 | return base::OkStatus(); |
4132 | 0 | } |
4133 | | |
4134 | | std::map<ProducerID, std::vector<DataSourceInstanceID>> |
4135 | | TracingServiceImpl::GetFlushableDataSourceInstancesForBuffers( |
4136 | | TracingSession* session, |
4137 | 0 | const std::set<BufferID>& bufs) { |
4138 | 0 | std::map<ProducerID, std::vector<DataSourceInstanceID>> data_source_instances; |
4139 | |
|
4140 | 0 | for (const auto& [producer_id, ds_inst] : session->data_source_instances) { |
4141 | | // TODO(ddiproietto): Consider if we should skip instances if ds_inst.state |
4142 | | // != DataSourceInstance::STARTED |
4143 | 0 | if (ds_inst.no_flush) { |
4144 | 0 | continue; |
4145 | 0 | } |
4146 | 0 | if (!bufs.count(static_cast<BufferID>(ds_inst.config.target_buffer()))) { |
4147 | 0 | continue; |
4148 | 0 | } |
4149 | 0 | data_source_instances[producer_id].push_back(ds_inst.instance_id); |
4150 | 0 | } |
4151 | |
|
4152 | 0 | return data_source_instances; |
4153 | 0 | } |
4154 | | |
4155 | | void TracingServiceImpl::OnFlushDoneForClone(TracingSessionID tsid, |
4156 | | PendingCloneID clone_id, |
4157 | | const std::set<BufferID>& buf_ids, |
4158 | 0 | bool final_flush_outcome) { |
4159 | 0 | TracingSession* src = GetTracingSession(tsid); |
4160 | | // The session might be gone by the time we try to clone it. |
4161 | 0 | if (!src) { |
4162 | 0 | return; |
4163 | 0 | } |
4164 | | |
4165 | 0 | auto it = src->pending_clones.find(clone_id); |
4166 | 0 | if (it == src->pending_clones.end()) { |
4167 | 0 | return; |
4168 | 0 | } |
4169 | 0 | auto& clone_op = it->second; |
4170 | |
|
4171 | 0 | if (final_flush_outcome == false) { |
4172 | 0 | clone_op.flush_failed = true; |
4173 | 0 | } |
4174 | |
|
4175 | 0 | base::Status result; |
4176 | 0 | base::Uuid uuid; |
4177 | | |
4178 | | // First clone the flushed TraceBuffer(s). This can fail because of ENOMEM. If |
4179 | | // it happens bail out early before creating any session. |
4180 | 0 | if (!DoCloneBuffers(*src, buf_ids, &clone_op)) { |
4181 | 0 | result = PERFETTO_SVC_ERR("Buffer allocation failed"); |
4182 | 0 | } |
4183 | |
|
4184 | 0 | if (result.ok()) { |
4185 | 0 | UpdateMemoryGuardrail(); |
4186 | |
|
4187 | 0 | if (--clone_op.pending_flush_cnt != 0) { |
4188 | | // Wait for more pending flushes. |
4189 | 0 | return; |
4190 | 0 | } |
4191 | | |
4192 | 0 | PERFETTO_LOG("FlushAndCloneSession(%" PRIu64 ") started, success=%d", tsid, |
4193 | 0 | final_flush_outcome); |
4194 | |
|
4195 | 0 | if (clone_op.weak_consumer) { |
4196 | 0 | result = FinishCloneSession( |
4197 | 0 | &*clone_op.weak_consumer, tsid, std::move(clone_op.buffers), |
4198 | 0 | std::move(clone_op.buffer_cloned_timestamps), |
4199 | 0 | clone_op.skip_trace_filter, !clone_op.flush_failed, |
4200 | 0 | clone_op.clone_trigger, &uuid, clone_op.clone_started_timestamp_ns); |
4201 | 0 | } |
4202 | 0 | } // if (result.ok()) |
4203 | | |
4204 | 0 | if (clone_op.weak_consumer) { |
4205 | 0 | clone_op.weak_consumer->consumer_->OnSessionCloned( |
4206 | 0 | {result.ok(), result.message(), uuid}); |
4207 | 0 | } |
4208 | |
|
4209 | 0 | src->pending_clones.erase(it); |
4210 | 0 | UpdateMemoryGuardrail(); |
4211 | 0 | } |
4212 | | |
4213 | | bool TracingServiceImpl::DoCloneBuffers(const TracingSession& src, |
4214 | | const std::set<BufferID>& buf_ids, |
4215 | 0 | PendingClone* clone_op) { |
4216 | 0 | PERFETTO_DCHECK(src.num_buffers() == src.config.buffers().size()); |
4217 | 0 | clone_op->buffers.resize(src.buffers_index.size()); |
4218 | 0 | clone_op->buffer_cloned_timestamps.resize(src.buffers_index.size()); |
4219 | |
|
4220 | 0 | int64_t now = clock_->GetBootTimeNs().count(); |
4221 | |
|
4222 | 0 | for (size_t buf_idx = 0; buf_idx < src.buffers_index.size(); buf_idx++) { |
4223 | 0 | BufferID src_buf_id = src.buffers_index[buf_idx]; |
4224 | 0 | if (buf_ids.count(src_buf_id) == 0) |
4225 | 0 | continue; |
4226 | 0 | auto buf_iter = buffers_.find(src_buf_id); |
4227 | 0 | PERFETTO_CHECK(buf_iter != buffers_.end()); |
4228 | 0 | std::unique_ptr<TraceBuffer>& src_buf = buf_iter->second; |
4229 | 0 | std::unique_ptr<TraceBuffer> new_buf; |
4230 | 0 | if (src.config.buffers()[buf_idx].transfer_on_clone()) { |
4231 | 0 | const auto buf_policy = src_buf->overwrite_policy(); |
4232 | 0 | const auto buf_size = src_buf->size(); |
4233 | 0 | new_buf = std::move(src_buf); |
4234 | 0 | src_buf = TraceBuffer::Create(buf_size, buf_policy); |
4235 | 0 | if (!src_buf) { |
4236 | | // If the allocation fails put the buffer back and let the code below |
4237 | | // handle the failure gracefully. |
4238 | 0 | src_buf = std::move(new_buf); |
4239 | 0 | } |
4240 | 0 | } else { |
4241 | 0 | new_buf = src_buf->CloneReadOnly(); |
4242 | 0 | } |
4243 | 0 | if (!new_buf.get()) { |
4244 | 0 | return false; |
4245 | 0 | } |
4246 | 0 | clone_op->buffers[buf_idx] = std::move(new_buf); |
4247 | 0 | clone_op->buffer_cloned_timestamps[buf_idx] = now; |
4248 | 0 | } |
4249 | 0 | return true; |
4250 | 0 | } |
4251 | | |
4252 | | base::Status TracingServiceImpl::FinishCloneSession( |
4253 | | ConsumerEndpointImpl* consumer, |
4254 | | TracingSessionID src_tsid, |
4255 | | std::vector<std::unique_ptr<TraceBuffer>> buf_snaps, |
4256 | | std::vector<int64_t> buf_cloned_timestamps, |
4257 | | bool skip_trace_filter, |
4258 | | bool final_flush_outcome, |
4259 | | std::optional<TriggerInfo> clone_trigger, |
4260 | | base::Uuid* new_uuid, |
4261 | 0 | int64_t clone_started_timestamp_ns) { |
4262 | 0 | PERFETTO_DLOG("CloneSession(%" PRIu64 |
4263 | 0 | ", skip_trace_filter=%d) started, consumer uid: %d", |
4264 | 0 | src_tsid, skip_trace_filter, static_cast<int>(consumer->uid_)); |
4265 | |
|
4266 | 0 | TracingSession* src = GetTracingSession(src_tsid); |
4267 | | |
4268 | | // The session might be gone by the time we try to clone it. |
4269 | 0 | if (!src) |
4270 | 0 | return PERFETTO_SVC_ERR("session not found"); |
4271 | | |
4272 | 0 | if (consumer->tracing_session_id_) { |
4273 | 0 | return PERFETTO_SVC_ERR( |
4274 | 0 | "The consumer is already attached to another tracing session"); |
4275 | 0 | } |
4276 | | |
4277 | 0 | std::vector<BufferID> buf_ids = |
4278 | 0 | buffer_ids_.AllocateMultiple(buf_snaps.size()); |
4279 | 0 | if (buf_ids.size() != buf_snaps.size()) { |
4280 | 0 | return PERFETTO_SVC_ERR("Buffer id allocation failed"); |
4281 | 0 | } |
4282 | | |
4283 | 0 | PERFETTO_CHECK(std::none_of( |
4284 | 0 | buf_snaps.begin(), buf_snaps.end(), |
4285 | 0 | [](const std::unique_ptr<TraceBuffer>& buf) { return buf == nullptr; })); |
4286 | | |
4287 | 0 | const TracingSessionID tsid = ++last_tracing_session_id_; |
4288 | 0 | TracingSession* cloned_session = |
4289 | 0 | &tracing_sessions_ |
4290 | 0 | .emplace(std::piecewise_construct, std::forward_as_tuple(tsid), |
4291 | 0 | std::forward_as_tuple(tsid, consumer, src->config, |
4292 | 0 | weak_runner_.task_runner())) |
4293 | 0 | .first->second; |
4294 | | |
4295 | | // Generate a new UUID for the cloned session, but preserve the LSB. In some |
4296 | | // contexts the LSB is used to tie the trace back to the statsd subscription |
4297 | | // that triggered it. See the corresponding code in perfetto_cmd.cc which |
4298 | | // reads at triggering_subscription_id(). |
4299 | 0 | const int64_t orig_uuid_lsb = src->trace_uuid.lsb(); |
4300 | 0 | cloned_session->state = TracingSession::CLONED_READ_ONLY; |
4301 | 0 | cloned_session->trace_uuid = base::Uuidv4(); |
4302 | 0 | cloned_session->trace_uuid.set_lsb(orig_uuid_lsb); |
4303 | 0 | *new_uuid = cloned_session->trace_uuid; |
4304 | |
|
4305 | 0 | for (size_t i = 0; i < buf_snaps.size(); i++) { |
4306 | 0 | BufferID buf_global_id = buf_ids[i]; |
4307 | 0 | std::unique_ptr<TraceBuffer>& buf = buf_snaps[i]; |
4308 | | // This is only needed for transfer_on_clone. Other buffers are already |
4309 | | // marked as read-only by CloneReadOnly(). We cannot do this early because |
4310 | | // in case of an allocation failure we will put std::move() the original |
4311 | | // buffer back in its place and in that case should not be made read-only. |
4312 | 0 | buf->set_read_only(); |
4313 | 0 | buffers_.emplace(buf_global_id, std::move(buf)); |
4314 | 0 | cloned_session->buffers_index.emplace_back(buf_global_id); |
4315 | 0 | } |
4316 | 0 | UpdateMemoryGuardrail(); |
4317 | | |
4318 | | // Copy over relevant state that we want to persist in the cloned session. |
4319 | | // Mostly stats and metadata that is emitted in the trace file by the service. |
4320 | | // Also clear the received trigger list in the main tracing session. A |
4321 | | // CLONE_SNAPSHOT session can go in ring buffer mode for several hours and get |
4322 | | // snapshotted several times. This causes two issues with `received_triggers`: |
4323 | | // 1. Adding noise in the cloned trace emitting triggers that happened too |
4324 | | // far back (see b/290799105). |
4325 | | // 2. Bloating memory (see b/290798988). |
4326 | 0 | cloned_session->should_emit_stats = true; |
4327 | 0 | cloned_session->clone_trigger = clone_trigger; |
4328 | 0 | cloned_session->received_triggers = std::move(src->received_triggers); |
4329 | 0 | src->received_triggers.clear(); |
4330 | 0 | src->num_triggers_emitted_into_trace = 0; |
4331 | 0 | cloned_session->lifecycle_events = |
4332 | 0 | std::vector<TracingSession::LifecycleEvent>(src->lifecycle_events); |
4333 | 0 | cloned_session->slow_start_event = src->slow_start_event; |
4334 | 0 | cloned_session->last_flush_events = src->last_flush_events; |
4335 | 0 | cloned_session->initial_clock_snapshot = src->initial_clock_snapshot; |
4336 | 0 | cloned_session->clock_snapshot_ring_buffer = src->clock_snapshot_ring_buffer; |
4337 | 0 | cloned_session->invalid_packets = src->invalid_packets; |
4338 | 0 | cloned_session->flushes_requested = src->flushes_requested; |
4339 | 0 | cloned_session->flushes_succeeded = src->flushes_succeeded; |
4340 | 0 | cloned_session->flushes_failed = src->flushes_failed; |
4341 | 0 | cloned_session->compress_deflate = src->compress_deflate; |
4342 | 0 | if (src->trace_filter && !skip_trace_filter) { |
4343 | | // Copy the trace filter, unless it's a clone-for-bugreport (b/317065412). |
4344 | 0 | cloned_session->trace_filter.reset( |
4345 | 0 | new protozero::MessageFilter(src->trace_filter->config())); |
4346 | 0 | } |
4347 | |
|
4348 | 0 | cloned_session->buffer_cloned_timestamps = std::move(buf_cloned_timestamps); |
4349 | |
|
4350 | 0 | SetSingleLifecycleEvent( |
4351 | 0 | cloned_session, |
4352 | 0 | protos::pbzero::TracingServiceEvent::kCloneStartedFieldNumber, |
4353 | 0 | clone_started_timestamp_ns); |
4354 | |
|
4355 | 0 | SnapshotLifecycleEvent( |
4356 | 0 | cloned_session, |
4357 | 0 | protos::pbzero::TracingServiceEvent::kTracingDisabledFieldNumber, |
4358 | 0 | true /* snapshot_clocks */); |
4359 | |
|
4360 | 0 | PERFETTO_DLOG("Consumer (uid:%d) cloned tracing session %" PRIu64 |
4361 | 0 | " -> %" PRIu64, |
4362 | 0 | static_cast<int>(consumer->uid_), src_tsid, tsid); |
4363 | |
|
4364 | 0 | consumer->tracing_session_id_ = tsid; |
4365 | 0 | cloned_session->final_flush_outcome = final_flush_outcome |
4366 | 0 | ? TraceStats::FINAL_FLUSH_SUCCEEDED |
4367 | 0 | : TraceStats::FINAL_FLUSH_FAILED; |
4368 | 0 | return base::OkStatus(); |
4369 | 0 | } |
4370 | | |
4371 | 0 | bool TracingServiceImpl::TracingSession::IsCloneAllowed(uid_t clone_uid) const { |
4372 | 0 | if (clone_uid == 0) |
4373 | 0 | return true; // Root is always allowed to clone everything. |
4374 | 0 | if (clone_uid == this->consumer_uid) |
4375 | 0 | return true; // Allow cloning if the uids match. |
4376 | | #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) |
4377 | | // On Android allow shell to clone sessions marked as exported for bugreport. |
4378 | | // Dumpstate (invoked by adb bugreport) invokes commands as shell. |
4379 | | if (clone_uid == AID_SHELL && this->config.bugreport_score() > 0) |
4380 | | return true; |
4381 | | #endif |
4382 | 0 | return false; |
4383 | 0 | } |
4384 | | |
4385 | | //////////////////////////////////////////////////////////////////////////////// |
4386 | | // TracingServiceImpl::ConsumerEndpointImpl implementation |
4387 | | //////////////////////////////////////////////////////////////////////////////// |
4388 | | |
4389 | | TracingServiceImpl::ConsumerEndpointImpl::ConsumerEndpointImpl( |
4390 | | TracingServiceImpl* service, |
4391 | | base::TaskRunner* task_runner, |
4392 | | Consumer* consumer, |
4393 | | uid_t uid) |
4394 | 300 | : task_runner_(task_runner), |
4395 | 300 | service_(service), |
4396 | 300 | consumer_(consumer), |
4397 | 300 | uid_(uid), |
4398 | 300 | weak_ptr_factory_(this) {} |
4399 | | |
4400 | 300 | TracingServiceImpl::ConsumerEndpointImpl::~ConsumerEndpointImpl() { |
4401 | 300 | service_->DisconnectConsumer(this); |
4402 | 300 | consumer_->OnDisconnect(); |
4403 | 300 | } |
4404 | | |
4405 | | void TracingServiceImpl::ConsumerEndpointImpl::NotifyOnTracingDisabled( |
4406 | 300 | const std::string& error) { |
4407 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4408 | 300 | task_runner_->PostTask([weak_this = weak_ptr_factory_.GetWeakPtr(), |
4409 | 300 | error /* deliberate copy */] { |
4410 | 300 | if (weak_this) |
4411 | 0 | weak_this->consumer_->OnTracingDisabled(error); |
4412 | 300 | }); |
4413 | 300 | } |
4414 | | |
4415 | | void TracingServiceImpl::ConsumerEndpointImpl::EnableTracing( |
4416 | | const TraceConfig& cfg, |
4417 | 300 | base::ScopedFile fd) { |
4418 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4419 | 300 | auto status = service_->EnableTracing(this, cfg, std::move(fd)); |
4420 | 300 | if (!status.ok()) |
4421 | 0 | NotifyOnTracingDisabled(status.message()); |
4422 | 300 | } |
4423 | | |
4424 | | void TracingServiceImpl::ConsumerEndpointImpl::ChangeTraceConfig( |
4425 | 0 | const TraceConfig& cfg) { |
4426 | 0 | if (!tracing_session_id_) { |
4427 | 0 | PERFETTO_LOG( |
4428 | 0 | "Consumer called ChangeTraceConfig() but tracing was " |
4429 | 0 | "not active"); |
4430 | 0 | return; |
4431 | 0 | } |
4432 | 0 | service_->ChangeTraceConfig(this, cfg); |
4433 | 0 | } |
4434 | | |
4435 | 0 | void TracingServiceImpl::ConsumerEndpointImpl::StartTracing() { |
4436 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4437 | 0 | if (!tracing_session_id_) { |
4438 | 0 | PERFETTO_LOG("Consumer called StartTracing() but tracing was not active"); |
4439 | 0 | return; |
4440 | 0 | } |
4441 | 0 | service_->StartTracing(tracing_session_id_); |
4442 | 0 | } |
4443 | | |
4444 | 0 | void TracingServiceImpl::ConsumerEndpointImpl::DisableTracing() { |
4445 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4446 | 0 | if (!tracing_session_id_) { |
4447 | 0 | PERFETTO_LOG("Consumer called DisableTracing() but tracing was not active"); |
4448 | 0 | return; |
4449 | 0 | } |
4450 | 0 | service_->DisableTracing(tracing_session_id_); |
4451 | 0 | } |
4452 | | |
4453 | 300 | void TracingServiceImpl::ConsumerEndpointImpl::ReadBuffers() { |
4454 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4455 | 300 | if (!tracing_session_id_) { |
4456 | 0 | PERFETTO_LOG("Consumer called ReadBuffers() but tracing was not active"); |
4457 | 0 | consumer_->OnTraceData({}, /* has_more = */ false); |
4458 | 0 | return; |
4459 | 0 | } |
4460 | 300 | if (!service_->ReadBuffersIntoConsumer(tracing_session_id_, this)) { |
4461 | 0 | consumer_->OnTraceData({}, /* has_more = */ false); |
4462 | 0 | } |
4463 | 300 | } |
4464 | | |
4465 | 0 | void TracingServiceImpl::ConsumerEndpointImpl::FreeBuffers() { |
4466 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4467 | 0 | if (!tracing_session_id_) { |
4468 | 0 | PERFETTO_LOG("Consumer called FreeBuffers() but tracing was not active"); |
4469 | 0 | return; |
4470 | 0 | } |
4471 | 0 | service_->FreeBuffers(tracing_session_id_); |
4472 | 0 | tracing_session_id_ = 0; |
4473 | 0 | } |
4474 | | |
4475 | | void TracingServiceImpl::ConsumerEndpointImpl::Flush(uint32_t timeout_ms, |
4476 | | FlushCallback callback, |
4477 | 0 | FlushFlags flush_flags) { |
4478 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4479 | 0 | if (!tracing_session_id_) { |
4480 | 0 | PERFETTO_LOG("Consumer called Flush() but tracing was not active"); |
4481 | 0 | return; |
4482 | 0 | } |
4483 | 0 | service_->Flush(tracing_session_id_, timeout_ms, callback, flush_flags); |
4484 | 0 | } |
4485 | | |
4486 | 0 | void TracingServiceImpl::ConsumerEndpointImpl::Detach(const std::string& key) { |
4487 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4488 | 0 | bool success = service_->DetachConsumer(this, key); |
4489 | 0 | auto weak_this = weak_ptr_factory_.GetWeakPtr(); |
4490 | 0 | task_runner_->PostTask([weak_this = std::move(weak_this), success] { |
4491 | 0 | if (weak_this) |
4492 | 0 | weak_this->consumer_->OnDetach(success); |
4493 | 0 | }); |
4494 | 0 | } |
4495 | | |
4496 | 0 | void TracingServiceImpl::ConsumerEndpointImpl::Attach(const std::string& key) { |
4497 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4498 | 0 | bool success = service_->AttachConsumer(this, key); |
4499 | 0 | task_runner_->PostTask([weak_this = weak_ptr_factory_.GetWeakPtr(), success] { |
4500 | 0 | if (!weak_this) |
4501 | 0 | return; |
4502 | 0 | Consumer* consumer = weak_this->consumer_; |
4503 | 0 | TracingSession* session = |
4504 | 0 | weak_this->service_->GetTracingSession(weak_this->tracing_session_id_); |
4505 | 0 | if (!session) { |
4506 | 0 | consumer->OnAttach(false, TraceConfig()); |
4507 | 0 | return; |
4508 | 0 | } |
4509 | 0 | consumer->OnAttach(success, session->config); |
4510 | 0 | }); |
4511 | 0 | } |
4512 | | |
4513 | 0 | void TracingServiceImpl::ConsumerEndpointImpl::GetTraceStats() { |
4514 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4515 | 0 | bool success = false; |
4516 | 0 | TraceStats stats; |
4517 | 0 | TracingSession* session = service_->GetTracingSession(tracing_session_id_); |
4518 | 0 | if (session) { |
4519 | 0 | success = true; |
4520 | 0 | stats = service_->GetTraceStats(session); |
4521 | 0 | } |
4522 | 0 | auto weak_this = weak_ptr_factory_.GetWeakPtr(); |
4523 | 0 | task_runner_->PostTask( |
4524 | 0 | [weak_this = std::move(weak_this), success, stats = std::move(stats)] { |
4525 | 0 | if (weak_this) |
4526 | 0 | weak_this->consumer_->OnTraceStats(success, stats); |
4527 | 0 | }); |
4528 | 0 | } |
4529 | | |
4530 | | void TracingServiceImpl::ConsumerEndpointImpl::ObserveEvents( |
4531 | 300 | uint32_t events_mask) { |
4532 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4533 | 300 | observable_events_mask_ = events_mask; |
4534 | 300 | TracingSession* session = service_->GetTracingSession(tracing_session_id_); |
4535 | 300 | if (!session) |
4536 | 300 | return; |
4537 | | |
4538 | 0 | if (observable_events_mask_ & ObservableEvents::TYPE_DATA_SOURCES_INSTANCES) { |
4539 | | // Issue initial states. |
4540 | 0 | for (const auto& kv : session->data_source_instances) { |
4541 | 0 | ProducerEndpointImpl* producer = service_->GetProducer(kv.first); |
4542 | 0 | PERFETTO_DCHECK(producer); |
4543 | 0 | OnDataSourceInstanceStateChange(*producer, kv.second); |
4544 | 0 | } |
4545 | 0 | } |
4546 | | |
4547 | | // If the ObserveEvents() call happens after data sources have acked already |
4548 | | // notify immediately. |
4549 | 0 | if (observable_events_mask_ & |
4550 | 0 | ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED) { |
4551 | 0 | service_->MaybeNotifyAllDataSourcesStarted(session); |
4552 | 0 | } |
4553 | 0 | } |
4554 | | |
4555 | | void TracingServiceImpl::ConsumerEndpointImpl::OnDataSourceInstanceStateChange( |
4556 | | const ProducerEndpointImpl& producer, |
4557 | 900 | const DataSourceInstance& instance) { |
4558 | 900 | if (!(observable_events_mask_ & |
4559 | 900 | ObservableEvents::TYPE_DATA_SOURCES_INSTANCES)) { |
4560 | 900 | return; |
4561 | 900 | } |
4562 | | |
4563 | 0 | if (instance.state != DataSourceInstance::CONFIGURED && |
4564 | 0 | instance.state != DataSourceInstance::STARTED && |
4565 | 0 | instance.state != DataSourceInstance::STOPPED) { |
4566 | 0 | return; |
4567 | 0 | } |
4568 | | |
4569 | 0 | auto* observable_events = AddObservableEvents(); |
4570 | 0 | auto* change = observable_events->add_instance_state_changes(); |
4571 | 0 | change->set_producer_name(producer.name_); |
4572 | 0 | change->set_data_source_name(instance.data_source_name); |
4573 | 0 | if (instance.state == DataSourceInstance::STARTED) { |
4574 | 0 | change->set_state(ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STARTED); |
4575 | 0 | } else { |
4576 | 0 | change->set_state(ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STOPPED); |
4577 | 0 | } |
4578 | 0 | } |
4579 | | |
4580 | 300 | void TracingServiceImpl::ConsumerEndpointImpl::OnAllDataSourcesStarted() { |
4581 | 300 | if (!(observable_events_mask_ & |
4582 | 300 | ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED)) { |
4583 | 0 | return; |
4584 | 0 | } |
4585 | 300 | auto* observable_events = AddObservableEvents(); |
4586 | 300 | observable_events->set_all_data_sources_started(true); |
4587 | 300 | } |
4588 | | |
4589 | | void TracingServiceImpl::ConsumerEndpointImpl::NotifyCloneSnapshotTrigger( |
4590 | 0 | const TriggerInfo& trigger) { |
4591 | 0 | if (!(observable_events_mask_ & ObservableEvents::TYPE_CLONE_TRIGGER_HIT)) { |
4592 | 0 | return; |
4593 | 0 | } |
4594 | 0 | auto* observable_events = AddObservableEvents(); |
4595 | 0 | auto* clone_trig = observable_events->mutable_clone_trigger_hit(); |
4596 | 0 | clone_trig->set_tracing_session_id(static_cast<int64_t>(tracing_session_id_)); |
4597 | 0 | clone_trig->set_trigger_name(trigger.trigger_name); |
4598 | 0 | clone_trig->set_producer_name(trigger.producer_name); |
4599 | 0 | clone_trig->set_producer_uid(static_cast<uint32_t>(trigger.producer_uid)); |
4600 | 0 | clone_trig->set_boot_time_ns(trigger.boot_time_ns); |
4601 | 0 | } |
4602 | | |
4603 | | ObservableEvents* |
4604 | 300 | TracingServiceImpl::ConsumerEndpointImpl::AddObservableEvents() { |
4605 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4606 | 300 | if (!observable_events_) { |
4607 | 300 | observable_events_.reset(new ObservableEvents()); |
4608 | 300 | task_runner_->PostTask([weak_this = weak_ptr_factory_.GetWeakPtr()] { |
4609 | 300 | if (!weak_this) |
4610 | 0 | return; |
4611 | | |
4612 | | // Move into a temporary to allow reentrancy in OnObservableEvents. |
4613 | 300 | auto observable_events = std::move(weak_this->observable_events_); |
4614 | 300 | weak_this->consumer_->OnObservableEvents(*observable_events); |
4615 | 300 | }); |
4616 | 300 | } |
4617 | 300 | return observable_events_.get(); |
4618 | 300 | } |
4619 | | |
4620 | | void TracingServiceImpl::ConsumerEndpointImpl::QueryServiceState( |
4621 | | QueryServiceStateArgs args, |
4622 | 0 | QueryServiceStateCallback callback) { |
4623 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4624 | 0 | TracingServiceState svc_state; |
4625 | |
|
4626 | 0 | const auto& sessions = service_->tracing_sessions_; |
4627 | 0 | svc_state.set_tracing_service_version(base::GetVersionString()); |
4628 | 0 | svc_state.set_num_sessions(static_cast<int>(sessions.size())); |
4629 | |
|
4630 | 0 | int num_started = 0; |
4631 | 0 | for (const auto& kv : sessions) |
4632 | 0 | num_started += kv.second.state == TracingSession::State::STARTED ? 1 : 0; |
4633 | 0 | svc_state.set_num_sessions_started(num_started); |
4634 | |
|
4635 | 0 | for (const auto& kv : service_->producers_) { |
4636 | 0 | if (args.sessions_only) |
4637 | 0 | break; |
4638 | 0 | auto* producer = svc_state.add_producers(); |
4639 | 0 | producer->set_id(static_cast<int>(kv.first)); |
4640 | 0 | producer->set_name(kv.second->name_); |
4641 | 0 | producer->set_sdk_version(kv.second->sdk_version_); |
4642 | 0 | producer->set_uid(static_cast<int32_t>(kv.second->uid())); |
4643 | 0 | producer->set_pid(static_cast<int32_t>(kv.second->pid())); |
4644 | 0 | producer->set_frozen(kv.second->IsAndroidProcessFrozen()); |
4645 | 0 | } |
4646 | |
|
4647 | 0 | for (const auto& kv : service_->data_sources_) { |
4648 | 0 | if (args.sessions_only) |
4649 | 0 | break; |
4650 | 0 | const auto& registered_data_source = kv.second; |
4651 | 0 | auto* data_source = svc_state.add_data_sources(); |
4652 | 0 | *data_source->mutable_ds_descriptor() = registered_data_source.descriptor; |
4653 | 0 | data_source->set_producer_id( |
4654 | 0 | static_cast<int>(registered_data_source.producer_id)); |
4655 | 0 | } |
4656 | |
|
4657 | 0 | svc_state.set_supports_tracing_sessions(true); |
4658 | 0 | for (const auto& kv : service_->tracing_sessions_) { |
4659 | 0 | const TracingSession& s = kv.second; |
4660 | 0 | if (!s.IsCloneAllowed(uid_)) |
4661 | 0 | continue; |
4662 | 0 | auto* session = svc_state.add_tracing_sessions(); |
4663 | 0 | session->set_id(s.id); |
4664 | 0 | session->set_consumer_uid(static_cast<int>(s.consumer_uid)); |
4665 | 0 | session->set_duration_ms(s.config.duration_ms()); |
4666 | 0 | session->set_num_data_sources( |
4667 | 0 | static_cast<uint32_t>(s.data_source_instances.size())); |
4668 | 0 | session->set_unique_session_name(s.config.unique_session_name()); |
4669 | 0 | if (s.config.has_bugreport_score()) |
4670 | 0 | session->set_bugreport_score(s.config.bugreport_score()); |
4671 | 0 | if (s.config.has_bugreport_filename()) |
4672 | 0 | session->set_bugreport_filename(s.config.bugreport_filename()); |
4673 | 0 | for (const auto& snap_kv : s.initial_clock_snapshot) { |
4674 | 0 | if (snap_kv.clock_id == protos::pbzero::BUILTIN_CLOCK_REALTIME) |
4675 | 0 | session->set_start_realtime_ns(static_cast<int64_t>(snap_kv.timestamp)); |
4676 | 0 | } |
4677 | 0 | for (const auto& buf : s.config.buffers()) |
4678 | 0 | session->add_buffer_size_kb(buf.size_kb()); |
4679 | |
|
4680 | 0 | switch (s.state) { |
4681 | 0 | case TracingSession::State::DISABLED: |
4682 | 0 | session->set_state("DISABLED"); |
4683 | 0 | break; |
4684 | 0 | case TracingSession::State::CONFIGURED: |
4685 | 0 | session->set_state("CONFIGURED"); |
4686 | 0 | break; |
4687 | 0 | case TracingSession::State::STARTED: |
4688 | 0 | session->set_is_started(true); |
4689 | 0 | session->set_state("STARTED"); |
4690 | 0 | break; |
4691 | 0 | case TracingSession::State::DISABLING_WAITING_STOP_ACKS: |
4692 | 0 | session->set_state("STOP_WAIT"); |
4693 | 0 | break; |
4694 | 0 | case TracingSession::State::CLONED_READ_ONLY: |
4695 | 0 | session->set_state("CLONED_READ_ONLY"); |
4696 | 0 | break; |
4697 | 0 | } |
4698 | 0 | } |
4699 | 0 | callback(/*success=*/true, svc_state); |
4700 | 0 | } |
4701 | | |
4702 | | void TracingServiceImpl::ConsumerEndpointImpl::QueryCapabilities( |
4703 | 0 | QueryCapabilitiesCallback callback) { |
4704 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4705 | 0 | TracingServiceCapabilities caps; |
4706 | 0 | caps.set_has_query_capabilities(true); |
4707 | 0 | caps.set_has_trace_config_output_path(true); |
4708 | 0 | caps.set_has_clone_session(true); |
4709 | 0 | caps.add_observable_events(ObservableEvents::TYPE_DATA_SOURCES_INSTANCES); |
4710 | 0 | caps.add_observable_events(ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED); |
4711 | 0 | caps.add_observable_events(ObservableEvents::TYPE_CLONE_TRIGGER_HIT); |
4712 | 0 | static_assert( |
4713 | 0 | ObservableEvents::Type_MAX == ObservableEvents::TYPE_CLONE_TRIGGER_HIT, |
4714 | 0 | ""); |
4715 | 0 | callback(caps); |
4716 | 0 | } |
4717 | | |
4718 | | void TracingServiceImpl::ConsumerEndpointImpl::SaveTraceForBugreport( |
4719 | 0 | SaveTraceForBugreportCallback consumer_callback) { |
4720 | 0 | consumer_callback(false, |
4721 | 0 | "SaveTraceForBugreport is deprecated. Use " |
4722 | 0 | "CloneSession(kBugreportSessionId) instead."); |
4723 | 0 | } |
4724 | | |
4725 | | void TracingServiceImpl::ConsumerEndpointImpl::CloneSession( |
4726 | 0 | CloneSessionArgs args) { |
4727 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4728 | | // FlushAndCloneSession will call OnSessionCloned after the async flush. |
4729 | 0 | base::Status result = service_->FlushAndCloneSession(this, std::move(args)); |
4730 | |
|
4731 | 0 | if (!result.ok()) { |
4732 | 0 | consumer_->OnSessionCloned({false, result.message(), {}}); |
4733 | 0 | } |
4734 | 0 | } |
4735 | | |
4736 | | //////////////////////////////////////////////////////////////////////////////// |
4737 | | // TracingServiceImpl::ProducerEndpointImpl implementation |
4738 | | //////////////////////////////////////////////////////////////////////////////// |
4739 | | |
4740 | | TracingServiceImpl::ProducerEndpointImpl::ProducerEndpointImpl( |
4741 | | ProducerID id, |
4742 | | const ClientIdentity& client_identity, |
4743 | | TracingServiceImpl* service, |
4744 | | base::TaskRunner* task_runner, |
4745 | | Producer* producer, |
4746 | | const std::string& producer_name, |
4747 | | const std::string& sdk_version, |
4748 | | bool in_process, |
4749 | | bool smb_scraping_enabled) |
4750 | 300 | : id_(id), |
4751 | 300 | client_identity_(client_identity), |
4752 | 300 | service_(service), |
4753 | 300 | producer_(producer), |
4754 | 300 | name_(producer_name), |
4755 | 300 | sdk_version_(sdk_version), |
4756 | 300 | in_process_(in_process), |
4757 | 300 | smb_scraping_enabled_(smb_scraping_enabled), |
4758 | 300 | weak_runner_(task_runner) {} |
4759 | | |
4760 | 300 | TracingServiceImpl::ProducerEndpointImpl::~ProducerEndpointImpl() { |
4761 | 300 | service_->DisconnectProducer(id_); |
4762 | 300 | producer_->OnDisconnect(); |
4763 | 300 | } |
4764 | | |
4765 | 0 | void TracingServiceImpl::ProducerEndpointImpl::Disconnect() { |
4766 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4767 | | // Disconnection is only supported via destroying the ProducerEndpoint. |
4768 | 0 | PERFETTO_FATAL("Not supported"); |
4769 | 0 | } |
4770 | | |
4771 | | void TracingServiceImpl::ProducerEndpointImpl::RegisterDataSource( |
4772 | 300 | const DataSourceDescriptor& desc) { |
4773 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4774 | 300 | service_->RegisterDataSource(id_, desc); |
4775 | 300 | } |
4776 | | |
4777 | | void TracingServiceImpl::ProducerEndpointImpl::UpdateDataSource( |
4778 | 0 | const DataSourceDescriptor& desc) { |
4779 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4780 | 0 | service_->UpdateDataSource(id_, desc); |
4781 | 0 | } |
4782 | | |
4783 | | void TracingServiceImpl::ProducerEndpointImpl::UnregisterDataSource( |
4784 | 0 | const std::string& name) { |
4785 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4786 | 0 | service_->UnregisterDataSource(id_, name); |
4787 | 0 | } |
4788 | | |
4789 | | void TracingServiceImpl::ProducerEndpointImpl::RegisterTraceWriter( |
4790 | | uint32_t writer_id, |
4791 | 300 | uint32_t target_buffer) { |
4792 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4793 | 300 | writers_[static_cast<WriterID>(writer_id)] = |
4794 | 300 | static_cast<BufferID>(target_buffer); |
4795 | 300 | } |
4796 | | |
4797 | | void TracingServiceImpl::ProducerEndpointImpl::UnregisterTraceWriter( |
4798 | 300 | uint32_t writer_id) { |
4799 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4800 | 300 | writers_.erase(static_cast<WriterID>(writer_id)); |
4801 | 300 | } |
4802 | | |
4803 | | void TracingServiceImpl::ProducerEndpointImpl::CommitData( |
4804 | | const CommitDataRequest& req_untrusted, |
4805 | 1.75k | CommitDataCallback callback) { |
4806 | 1.75k | PERFETTO_DCHECK_THREAD(thread_checker_); |
4807 | | |
4808 | 1.75k | if (metatrace::IsEnabled(metatrace::TAG_TRACE_SERVICE)) { |
4809 | 0 | PERFETTO_METATRACE_COUNTER(TAG_TRACE_SERVICE, TRACE_SERVICE_COMMIT_DATA, |
4810 | 0 | EncodeCommitDataRequest(id_, req_untrusted)); |
4811 | 0 | } |
4812 | | |
4813 | 1.75k | if (!shared_memory_) { |
4814 | 0 | PERFETTO_DLOG( |
4815 | 0 | "Attempted to commit data before the shared memory was allocated."); |
4816 | 0 | return; |
4817 | 0 | } |
4818 | 1.75k | PERFETTO_DCHECK(shmem_abi_.is_valid()); |
4819 | 44.6k | for (const auto& entry : req_untrusted.chunks_to_move()) { |
4820 | 44.6k | const uint32_t page_idx = entry.page(); |
4821 | 44.6k | if (page_idx >= shmem_abi_.num_pages()) |
4822 | 0 | continue; // A buggy or malicious producer. |
4823 | | |
4824 | 44.6k | SharedMemoryABI::Chunk chunk; |
4825 | 44.6k | bool commit_data_over_ipc = entry.has_data(); |
4826 | 44.6k | if (PERFETTO_UNLIKELY(commit_data_over_ipc)) { |
4827 | | // Chunk data is passed over the wire. Create a chunk using the serialized |
4828 | | // protobuf message. |
4829 | 0 | const std::string& data = entry.data(); |
4830 | 0 | if (data.size() > SharedMemoryABI::Chunk::kMaxSize) { |
4831 | 0 | PERFETTO_DFATAL("IPC data commit too large: %zu", data.size()); |
4832 | 0 | continue; // A malicious or buggy producer |
4833 | 0 | } |
4834 | | // |data| is not altered, but we need to const_cast becasue Chunk data |
4835 | | // members are non-const. |
4836 | 0 | chunk = SharedMemoryABI::MakeChunkFromSerializedData( |
4837 | 0 | reinterpret_cast<uint8_t*>(const_cast<char*>(data.data())), |
4838 | 0 | static_cast<uint16_t>(entry.data().size()), |
4839 | 0 | static_cast<uint8_t>(entry.chunk())); |
4840 | 0 | } else |
4841 | 44.6k | chunk = shmem_abi_.TryAcquireChunkForReading(page_idx, entry.chunk()); |
4842 | 44.6k | if (!chunk.is_valid()) { |
4843 | 0 | PERFETTO_DLOG("Asked to move chunk %d:%d, but it's not complete", |
4844 | 0 | entry.page(), entry.chunk()); |
4845 | 0 | continue; |
4846 | 0 | } |
4847 | | |
4848 | | // TryAcquireChunkForReading() has load-acquire semantics. Once acquired, |
4849 | | // the ABI contract expects the producer to not touch the chunk anymore |
4850 | | // (until the service marks that as free). This is why all the reads below |
4851 | | // are just memory_order_relaxed. Also, the code here assumes that all this |
4852 | | // data can be malicious and just gives up if anything is malformed. |
4853 | 44.6k | BufferID buffer_id = static_cast<BufferID>(entry.target_buffer()); |
4854 | 44.6k | const SharedMemoryABI::ChunkHeader& chunk_header = *chunk.header(); |
4855 | 44.6k | WriterID writer_id = chunk_header.writer_id.load(std::memory_order_relaxed); |
4856 | 44.6k | ChunkID chunk_id = chunk_header.chunk_id.load(std::memory_order_relaxed); |
4857 | 44.6k | auto packets = chunk_header.packets.load(std::memory_order_relaxed); |
4858 | 44.6k | uint16_t num_fragments = packets.count; |
4859 | 44.6k | uint8_t chunk_flags = packets.flags; |
4860 | | |
4861 | 44.6k | service_->CopyProducerPageIntoLogBuffer( |
4862 | 44.6k | id_, client_identity_, writer_id, chunk_id, buffer_id, num_fragments, |
4863 | 44.6k | chunk_flags, |
4864 | 44.6k | /*chunk_complete=*/true, chunk.payload_begin(), chunk.payload_size()); |
4865 | | |
4866 | 44.6k | if (!commit_data_over_ipc) { |
4867 | | // This one has release-store semantics. |
4868 | 44.6k | shmem_abi_.ReleaseChunkAsFree(std::move(chunk)); |
4869 | 44.6k | } |
4870 | 44.6k | } // for(chunks_to_move) |
4871 | | |
4872 | 1.75k | service_->ApplyChunkPatches(id_, req_untrusted.chunks_to_patch()); |
4873 | | |
4874 | 1.75k | if (req_untrusted.flush_request_id()) { |
4875 | 0 | service_->NotifyFlushDoneForProducer(id_, req_untrusted.flush_request_id()); |
4876 | 0 | } |
4877 | | |
4878 | | // Keep this invocation last. ProducerIPCService::CommitData() relies on this |
4879 | | // callback being invoked within the same callstack and not posted. If this |
4880 | | // changes, the code there needs to be changed accordingly. |
4881 | 1.75k | if (callback) |
4882 | 300 | callback(); |
4883 | 1.75k | } |
4884 | | |
4885 | | void TracingServiceImpl::ProducerEndpointImpl::SetupSharedMemory( |
4886 | | std::unique_ptr<SharedMemory> shared_memory, |
4887 | | size_t page_size_bytes, |
4888 | 300 | bool provided_by_producer) { |
4889 | 300 | PERFETTO_DCHECK(!shared_memory_ && !shmem_abi_.is_valid()); |
4890 | 300 | PERFETTO_DCHECK(page_size_bytes % 1024 == 0); |
4891 | | |
4892 | 300 | shared_memory_ = std::move(shared_memory); |
4893 | 300 | shared_buffer_page_size_kb_ = page_size_bytes / 1024; |
4894 | 300 | is_shmem_provided_by_producer_ = provided_by_producer; |
4895 | | |
4896 | 300 | shmem_abi_.Initialize(reinterpret_cast<uint8_t*>(shared_memory_->start()), |
4897 | 300 | shared_memory_->size(), |
4898 | 300 | shared_buffer_page_size_kb() * 1024, |
4899 | 300 | SharedMemoryABI::ShmemMode::kDefault); |
4900 | 300 | if (in_process_) { |
4901 | 0 | inproc_shmem_arbiter_.reset(new SharedMemoryArbiterImpl( |
4902 | 0 | shared_memory_->start(), shared_memory_->size(), |
4903 | 0 | SharedMemoryABI::ShmemMode::kDefault, |
4904 | 0 | shared_buffer_page_size_kb_ * 1024, this, weak_runner_.task_runner())); |
4905 | 0 | inproc_shmem_arbiter_->SetDirectSMBPatchingSupportedByService(); |
4906 | 0 | } |
4907 | | |
4908 | 300 | OnTracingSetup(); |
4909 | 300 | service_->UpdateMemoryGuardrail(); |
4910 | 300 | } |
4911 | | |
4912 | 1.79k | SharedMemory* TracingServiceImpl::ProducerEndpointImpl::shared_memory() const { |
4913 | 1.79k | PERFETTO_DCHECK_THREAD(thread_checker_); |
4914 | 1.79k | return shared_memory_.get(); |
4915 | 1.79k | } |
4916 | | |
4917 | | size_t TracingServiceImpl::ProducerEndpointImpl::shared_buffer_page_size_kb() |
4918 | 600 | const { |
4919 | 600 | return shared_buffer_page_size_kb_; |
4920 | 600 | } |
4921 | | |
4922 | | void TracingServiceImpl::ProducerEndpointImpl::ActivateTriggers( |
4923 | 0 | const std::vector<std::string>& triggers) { |
4924 | 0 | service_->ActivateTriggers(id_, triggers); |
4925 | 0 | } |
4926 | | |
4927 | | void TracingServiceImpl::ProducerEndpointImpl::StopDataSource( |
4928 | 300 | DataSourceInstanceID ds_inst_id) { |
4929 | | // TODO(primiano): When we'll support tearing down the SMB, at this point we |
4930 | | // should send the Producer a TearDownTracing if all its data sources have |
4931 | | // been disabled (see b/77532839 and aosp/655179 PS1). |
4932 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4933 | 300 | weak_runner_.PostTask( |
4934 | 300 | [this, ds_inst_id] { producer_->StopDataSource(ds_inst_id); }); |
4935 | 300 | } |
4936 | | |
4937 | | SharedMemoryArbiter* |
4938 | 0 | TracingServiceImpl::ProducerEndpointImpl::MaybeSharedMemoryArbiter() { |
4939 | 0 | if (!inproc_shmem_arbiter_) { |
4940 | 0 | PERFETTO_FATAL( |
4941 | 0 | "The in-process SharedMemoryArbiter can only be used when " |
4942 | 0 | "CreateProducer has been called with in_process=true and after tracing " |
4943 | 0 | "has started."); |
4944 | 0 | } |
4945 | | |
4946 | 0 | PERFETTO_DCHECK(in_process_); |
4947 | 0 | return inproc_shmem_arbiter_.get(); |
4948 | 0 | } |
4949 | | |
4950 | | bool TracingServiceImpl::ProducerEndpointImpl::IsShmemProvidedByProducer() |
4951 | 600 | const { |
4952 | 600 | return is_shmem_provided_by_producer_; |
4953 | 600 | } |
4954 | | |
4955 | | // Can be called on any thread. |
4956 | | std::unique_ptr<TraceWriter> |
4957 | | TracingServiceImpl::ProducerEndpointImpl::CreateTraceWriter( |
4958 | | BufferID buf_id, |
4959 | 0 | BufferExhaustedPolicy buffer_exhausted_policy) { |
4960 | 0 | PERFETTO_DCHECK(MaybeSharedMemoryArbiter()); |
4961 | 0 | return MaybeSharedMemoryArbiter()->CreateTraceWriter(buf_id, |
4962 | 0 | buffer_exhausted_policy); |
4963 | 0 | } |
4964 | | |
4965 | | void TracingServiceImpl::ProducerEndpointImpl::NotifyFlushComplete( |
4966 | 0 | FlushRequestID id) { |
4967 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4968 | 0 | PERFETTO_DCHECK(MaybeSharedMemoryArbiter()); |
4969 | 0 | return MaybeSharedMemoryArbiter()->NotifyFlushComplete(id); |
4970 | 0 | } |
4971 | | |
4972 | 300 | void TracingServiceImpl::ProducerEndpointImpl::OnTracingSetup() { |
4973 | 300 | weak_runner_.PostTask([this] { producer_->OnTracingSetup(); }); |
4974 | 300 | } |
4975 | | |
4976 | | void TracingServiceImpl::ProducerEndpointImpl::Flush( |
4977 | | FlushRequestID flush_request_id, |
4978 | | const std::vector<DataSourceInstanceID>& data_sources, |
4979 | 0 | FlushFlags flush_flags) { |
4980 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4981 | 0 | weak_runner_.PostTask([this, flush_request_id, data_sources, flush_flags] { |
4982 | 0 | producer_->Flush(flush_request_id, data_sources.data(), data_sources.size(), |
4983 | 0 | flush_flags); |
4984 | 0 | }); |
4985 | 0 | } |
4986 | | |
4987 | | void TracingServiceImpl::ProducerEndpointImpl::SetupDataSource( |
4988 | | DataSourceInstanceID ds_id, |
4989 | 300 | const DataSourceConfig& config) { |
4990 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
4991 | 300 | allowed_target_buffers_.insert(static_cast<BufferID>(config.target_buffer())); |
4992 | 300 | weak_runner_.PostTask([this, ds_id, config] { |
4993 | 300 | producer_->SetupDataSource(ds_id, std::move(config)); |
4994 | 300 | }); |
4995 | 300 | } |
4996 | | |
4997 | | void TracingServiceImpl::ProducerEndpointImpl::StartDataSource( |
4998 | | DataSourceInstanceID ds_id, |
4999 | 300 | const DataSourceConfig& config) { |
5000 | 300 | PERFETTO_DCHECK_THREAD(thread_checker_); |
5001 | 300 | weak_runner_.PostTask([this, ds_id, config] { |
5002 | 300 | producer_->StartDataSource(ds_id, std::move(config)); |
5003 | 300 | }); |
5004 | 300 | } |
5005 | | |
5006 | | void TracingServiceImpl::ProducerEndpointImpl::NotifyDataSourceStarted( |
5007 | 0 | DataSourceInstanceID data_source_id) { |
5008 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
5009 | 0 | service_->NotifyDataSourceStarted(id_, data_source_id); |
5010 | 0 | } |
5011 | | |
5012 | | void TracingServiceImpl::ProducerEndpointImpl::NotifyDataSourceStopped( |
5013 | 0 | DataSourceInstanceID data_source_id) { |
5014 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
5015 | 0 | service_->NotifyDataSourceStopped(id_, data_source_id); |
5016 | 0 | } |
5017 | | |
5018 | | void TracingServiceImpl::ProducerEndpointImpl::OnFreeBuffers( |
5019 | 0 | const std::vector<BufferID>& target_buffers) { |
5020 | 0 | if (allowed_target_buffers_.empty()) |
5021 | 0 | return; |
5022 | 0 | for (BufferID buffer : target_buffers) |
5023 | 0 | allowed_target_buffers_.erase(buffer); |
5024 | 0 | } |
5025 | | |
5026 | | void TracingServiceImpl::ProducerEndpointImpl::ClearIncrementalState( |
5027 | 0 | const std::vector<DataSourceInstanceID>& data_sources) { |
5028 | 0 | PERFETTO_DCHECK_THREAD(thread_checker_); |
5029 | 0 | weak_runner_.PostTask([this, data_sources] { |
5030 | 0 | base::StringView producer_name(name_); |
5031 | 0 | producer_->ClearIncrementalState(data_sources.data(), data_sources.size()); |
5032 | 0 | }); |
5033 | 0 | } |
5034 | | |
5035 | | void TracingServiceImpl::ProducerEndpointImpl::Sync( |
5036 | 0 | std::function<void()> callback) { |
5037 | 0 | weak_runner_.task_runner()->PostTask(callback); |
5038 | 0 | } |
5039 | | |
5040 | 600 | bool TracingServiceImpl::ProducerEndpointImpl::IsAndroidProcessFrozen() { |
5041 | | #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) |
5042 | | if (in_process_ || uid() == base::kInvalidUid || pid() == base::kInvalidPid) |
5043 | | return false; |
5044 | | |
5045 | | // As per aosp/3406861, there are three possible mount points for the cgroup. |
5046 | | // Look at all of them. |
5047 | | // - Historically everything was in /uid_xxx/pid_yyy (and still is if |
5048 | | // PRODUCT_CGROUP_V2_SYS_APP_ISOLATION_ENABLED = false) |
5049 | | // - cgroup isolation introduces /apps /system subdirectories. |
5050 | | base::StackString<255> path_v1( |
5051 | | "/sys/fs/cgroup/uid_%" PRIu32 "/pid_%" PRIu32 "/cgroup.freeze", |
5052 | | static_cast<uint32_t>(uid()), static_cast<uint32_t>(pid())); |
5053 | | base::StackString<255> path_v2_app( |
5054 | | "/sys/fs/cgroup/apps/uid_%" PRIu32 "/pid_%" PRIu32 "/cgroup.freeze", |
5055 | | static_cast<uint32_t>(uid()), static_cast<uint32_t>(pid())); |
5056 | | base::StackString<255> path_v2_system( |
5057 | | "/sys/fs/cgroup/system/uid_%" PRIu32 "/pid_%" PRIu32 "/cgroup.freeze", |
5058 | | static_cast<uint32_t>(uid()), static_cast<uint32_t>(pid())); |
5059 | | const char* paths[] = {path_v1.c_str(), path_v2_app.c_str(), |
5060 | | path_v2_system.c_str()}; |
5061 | | |
5062 | | for (const char* path : paths) { |
5063 | | char frozen = '0'; |
5064 | | auto fd = base::OpenFile(path, O_RDONLY); |
5065 | | ssize_t rsize = 0; |
5066 | | if (fd) { |
5067 | | rsize = base::Read(*fd, &frozen, sizeof(frozen)); |
5068 | | if (rsize > 0) { |
5069 | | return frozen == '1'; |
5070 | | } |
5071 | | } |
5072 | | } |
5073 | | PERFETTO_DLOG("Failed to read cgroup.freeze from [%s, %s, %s]", |
5074 | | path_v1.c_str(), path_v2_app.c_str(), path_v2_system.c_str()); |
5075 | | |
5076 | | #endif |
5077 | 600 | return false; |
5078 | 600 | } |
5079 | | |
5080 | | //////////////////////////////////////////////////////////////////////////////// |
5081 | | // TracingServiceImpl::TracingSession implementation |
5082 | | //////////////////////////////////////////////////////////////////////////////// |
5083 | | |
5084 | | TracingServiceImpl::TracingSession::TracingSession( |
5085 | | TracingSessionID session_id, |
5086 | | ConsumerEndpointImpl* consumer, |
5087 | | const TraceConfig& new_config, |
5088 | | base::TaskRunner* task_runner) |
5089 | 300 | : id(session_id), |
5090 | 300 | consumer_maybe_null(consumer), |
5091 | 300 | consumer_uid(consumer->uid_), |
5092 | 300 | config(new_config), |
5093 | 300 | snapshot_periodic_task(task_runner), |
5094 | 300 | timed_stop_task(task_runner) { |
5095 | | // all_data_sources_flushed (and flush_started) is special because we store up |
5096 | | // to 64 events of this type. Other events will go through the default case in |
5097 | | // SnapshotLifecycleEvent() where they will be given a max history of 1. |
5098 | 300 | lifecycle_events.emplace_back( |
5099 | 300 | protos::pbzero::TracingServiceEvent::kAllDataSourcesFlushedFieldNumber, |
5100 | 300 | 64 /* max_size */); |
5101 | 300 | lifecycle_events.emplace_back( |
5102 | 300 | protos::pbzero::TracingServiceEvent::kFlushStartedFieldNumber, |
5103 | 300 | 64 /* max_size */); |
5104 | 300 | } |
5105 | | |
5106 | | //////////////////////////////////////////////////////////////////////////////// |
5107 | | // TracingServiceImpl::RelayEndpointImpl implementation |
5108 | | //////////////////////////////////////////////////////////////////////////////// |
5109 | | TracingServiceImpl::RelayEndpointImpl::RelayEndpointImpl( |
5110 | | RelayClientID relay_client_id, |
5111 | | TracingServiceImpl* service) |
5112 | 0 | : relay_client_id_(relay_client_id), service_(service) {} |
5113 | 0 | TracingServiceImpl::RelayEndpointImpl::~RelayEndpointImpl() = default; |
5114 | | |
5115 | | void TracingServiceImpl::RelayEndpointImpl::SyncClocks( |
5116 | | SyncMode sync_mode, |
5117 | | base::ClockSnapshotVector client_clocks, |
5118 | 0 | base::ClockSnapshotVector host_clocks) { |
5119 | | // We keep only the most recent 5 clock sync snapshots. |
5120 | 0 | static constexpr size_t kNumSyncClocks = 5; |
5121 | 0 | if (synced_clocks_.size() >= kNumSyncClocks) |
5122 | 0 | synced_clocks_.pop_front(); |
5123 | |
|
5124 | 0 | synced_clocks_.emplace_back(sync_mode, std::move(client_clocks), |
5125 | 0 | std::move(host_clocks)); |
5126 | 0 | } |
5127 | | |
5128 | 0 | void TracingServiceImpl::RelayEndpointImpl::Disconnect() { |
5129 | 0 | service_->DisconnectRelayClient(relay_client_id_); |
5130 | 0 | } |
5131 | | |
5132 | | } // namespace perfetto |