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