xref: /aosp_15_r20/external/perfetto/src/perfetto_cmd/perfetto_cmd_android.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2018 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/perfetto_cmd/perfetto_cmd.h"
18 
19 #include <sys/sendfile.h>
20 
21 #include <cinttypes>
22 
23 #include "perfetto/base/build_config.h"
24 #include "perfetto/base/logging.h"
25 #include "perfetto/ext/base/file_utils.h"
26 #include "perfetto/ext/base/string_utils.h"
27 #include "perfetto/ext/base/uuid.h"
28 #include "perfetto/tracing/core/trace_config.h"
29 #include "src/android_internal/incident_service.h"
30 #include "src/android_internal/lazy_library_loader.h"
31 #include "src/android_internal/tracing_service_proxy.h"
32 
33 namespace perfetto {
34 namespace {
35 
36 constexpr int64_t kSendfileTimeoutNs = 10UL * 1000 * 1000 * 1000;  // 10s
37 
38 }  // namespace
39 
SaveTraceIntoIncidentOrCrash()40 void PerfettoCmd::SaveTraceIntoIncidentOrCrash() {
41   PERFETTO_CHECK(save_to_incidentd_);
42 
43   const auto& cfg = trace_config_->incident_report_config();
44   PERFETTO_CHECK(!cfg.destination_package().empty());
45   PERFETTO_CHECK(!cfg.skip_incidentd());
46 
47   if (bytes_written_ == 0) {
48     LogUploadEvent(PerfettoStatsdAtom::kNotUploadingEmptyTrace);
49     PERFETTO_LOG("Skipping write to incident. Empty trace.");
50     return;
51   }
52 
53   // Save the trace as an incident.
54   SaveOutputToIncidentTraceOrCrash();
55 
56   // Skip the trace-uuid link for traces that are too small. Realistically those
57   // traces contain only a marker (e.g. seized_for_bugreport, or the trace
58   // expired without triggers). Those are useless and introduce only noise.
59   if (bytes_written_ > 4096) {
60     base::Uuid uuid(uuid_);
61     PERFETTO_LOG("go/trace-uuid/%s name=\"%s\" size=%" PRIu64,
62                  uuid.ToPrettyString().c_str(),
63                  trace_config_->unique_session_name().c_str(), bytes_written_);
64   }
65 
66   // Ask incidentd to create a report, which will read the file we just
67   // wrote.
68   PERFETTO_LAZY_LOAD(android_internal::StartIncidentReport, incident_fn);
69   PERFETTO_CHECK(incident_fn(cfg.destination_package().c_str(),
70                              cfg.destination_class().c_str(),
71                              cfg.privacy_level()));
72 }
73 
ReportTraceToAndroidFrameworkOrCrash()74 void PerfettoCmd::ReportTraceToAndroidFrameworkOrCrash() {
75   PERFETTO_CHECK(report_to_android_framework_);
76   PERFETTO_CHECK(trace_out_stream_);
77 
78   const auto& cfg = trace_config_->android_report_config();
79   PERFETTO_CHECK(!cfg.reporter_service_package().empty());
80   PERFETTO_CHECK(!cfg.skip_report());
81 
82   if (bytes_written_ == 0) {
83     LogUploadEvent(PerfettoStatsdAtom::kCmdFwReportEmptyTrace);
84     PERFETTO_LOG("Skipping reporting trace to Android. Empty trace.");
85     return;
86   }
87 
88   LogUploadEvent(PerfettoStatsdAtom::kCmdFwReportBegin);
89   base::StackString<128> self_fd("/proc/self/fd/%d",
90                                  fileno(*trace_out_stream_));
91   base::ScopedFile fd(base::OpenFile(self_fd.c_str(), O_RDONLY | O_CLOEXEC));
92   if (!fd) {
93     PERFETTO_FATAL("Failed to dup fd when reporting to Android");
94   }
95 
96   base::Uuid uuid(uuid_);
97   PERFETTO_LAZY_LOAD(android_internal::ReportTrace, report_fn);
98   PERFETTO_CHECK(report_fn(cfg.reporter_service_package().c_str(),
99                            cfg.reporter_service_class().c_str(), fd.release(),
100                            uuid.lsb(), uuid.msb(),
101                            cfg.use_pipe_in_framework_for_testing()));
102 
103   // Skip the trace-uuid link for traces that are too small. Realistically those
104   // traces contain only a marker (e.g. seized_for_bugreport, or the trace
105   // expired without triggers). Those are useless and introduce only noise.
106   if (bytes_written_ > 4096) {
107     PERFETTO_LOG("go/trace-uuid/%s name=\"%s\" size=%" PRIu64,
108                  uuid.ToPrettyString().c_str(),
109                  trace_config_->unique_session_name().c_str(), bytes_written_);
110   }
111   LogUploadEvent(PerfettoStatsdAtom::kCmdFwReportHandoff);
112 }
113 
114 // Open a staging file (unlinking the previous instance), copy the trace
115 // contents over, then rename to a final hardcoded path (known to incidentd).
116 // Such tracing sessions should not normally overlap. We do not use unique
117 // unique filenames to avoid creating an unbounded amount of files in case of
118 // errors.
SaveOutputToIncidentTraceOrCrash()119 void PerfettoCmd::SaveOutputToIncidentTraceOrCrash() {
120   LogUploadEvent(PerfettoStatsdAtom::kUploadIncidentBegin);
121   base::StackString<256> kIncidentTracePath("%s/incident-trace", kStateDir);
122 
123   base::StackString<256> kTempIncidentTracePath("%s.temp",
124                                                 kIncidentTracePath.c_str());
125 
126   PERFETTO_CHECK(unlink(kTempIncidentTracePath.c_str()) == 0 ||
127                  errno == ENOENT);
128 
129   // TODO(b/155024256) These should not be necessary (we flush when destroying
130   // packet writer and sendfile should ignore file offset) however they should
131   // not harm anything and it will help debug the linked issue.
132   PERFETTO_CHECK(fflush(*trace_out_stream_) == 0);
133   PERFETTO_CHECK(fseek(*trace_out_stream_, 0, SEEK_SET) == 0);
134 
135   // SELinux constrains the set of readers.
136   base::ScopedFile staging_fd = base::OpenFile(kTempIncidentTracePath.c_str(),
137                                                O_CREAT | O_EXCL | O_RDWR, 0666);
138   PERFETTO_CHECK(staging_fd);
139 
140   int fd = fileno(*trace_out_stream_);
141   off_t offset = 0;
142   size_t remaining = static_cast<size_t>(bytes_written_);
143 
144   // Count time in terms of CPU to avoid timeouts due to suspend:
145   base::TimeNanos start = base::GetThreadCPUTimeNs();
146   for (;;) {
147     errno = 0;
148     PERFETTO_DCHECK(static_cast<size_t>(offset) + remaining == bytes_written_);
149     auto wsize = PERFETTO_EINTR(sendfile(*staging_fd, fd, &offset, remaining));
150     if (wsize < 0) {
151       PERFETTO_FATAL("sendfile() failed wsize=%zd, off=%" PRId64
152                      ", initial=%" PRIu64 ", remaining=%zu",
153                      wsize, static_cast<int64_t>(offset), bytes_written_,
154                      remaining);
155     }
156     remaining -= static_cast<size_t>(wsize);
157     if (remaining == 0) {
158       break;
159     }
160     base::TimeNanos now = base::GetThreadCPUTimeNs();
161     if (now < start || (now - start).count() > kSendfileTimeoutNs) {
162       PERFETTO_FATAL("sendfile() timed out wsize=%zd, off=%" PRId64
163                      ", initial=%" PRIu64
164                      ", remaining=%zu, start=%lld, now=%lld",
165                      wsize, static_cast<int64_t>(offset), bytes_written_,
166                      remaining, static_cast<long long int>(start.count()),
167                      static_cast<long long int>(now.count()));
168     }
169   }
170 
171   staging_fd.reset();
172   PERFETTO_CHECK(
173       rename(kTempIncidentTracePath.c_str(), kIncidentTracePath.c_str()) == 0);
174   // Note: not calling fsync(2), as we're not interested in the file being
175   // consistent in case of a crash.
176   LogUploadEvent(PerfettoStatsdAtom::kUploadIncidentSuccess);
177 }
178 
179 // static
CreateUnlinkedTmpFile()180 base::ScopedFile PerfettoCmd::CreateUnlinkedTmpFile() {
181   // If we are tracing to DropBox, there's no need to make a
182   // filesystem-visible temporary file.
183   auto fd = base::OpenFile(kStateDir, O_TMPFILE | O_RDWR, 0600);
184   if (!fd)
185     PERFETTO_PLOG("Could not create a temporary trace file in %s", kStateDir);
186   return fd;
187 }
188 
189 }  // namespace perfetto
190