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 <inttypes.h>
20 #include <sys/sendfile.h>
21
22 #include "perfetto/base/build_config.h"
23 #include "perfetto/base/logging.h"
24 #include "perfetto/ext/base/file_utils.h"
25 #include "perfetto/ext/base/uuid.h"
26 #include "perfetto/tracing/core/trace_config.h"
27 #include "src/android_internal/incident_service.h"
28 #include "src/android_internal/lazy_library_loader.h"
29
30 namespace perfetto {
31 namespace {
32
33 constexpr int64_t kSendfileTimeoutNs = 10UL * 1000 * 1000 * 1000; // 10s
34
35 } // namespace
36
SaveTraceIntoDropboxAndIncidentOrCrash()37 void PerfettoCmd::SaveTraceIntoDropboxAndIncidentOrCrash() {
38 PERFETTO_CHECK(save_to_incidentd_);
39 PERFETTO_CHECK(
40 !trace_config_->incident_report_config().destination_package().empty());
41
42 if (bytes_written_ == 0) {
43 LogUploadEvent(PerfettoStatsdAtom::kNotUploadingEmptyTrace);
44 PERFETTO_LOG("Skipping write to incident. Empty trace.");
45 return;
46 }
47
48 // Save the trace as an incident.
49 SaveOutputToIncidentTraceOrCrash();
50
51 if (!uuid_.empty()) {
52 base::Uuid uuid(uuid_);
53 PERFETTO_LOG("go/trace-uuid/%s (%" PRIu64 " bytes)",
54 uuid.ToPrettyString().c_str(), bytes_written_);
55 }
56
57 // Ask incidentd to create a report, which will read the file we just
58 // wrote.
59 const auto& cfg = trace_config_->incident_report_config();
60 PERFETTO_LAZY_LOAD(android_internal::StartIncidentReport, incident_fn);
61 PERFETTO_CHECK(incident_fn(cfg.destination_package().c_str(),
62 cfg.destination_class().c_str(),
63 cfg.privacy_level()));
64 }
65
66 // Open a staging file (unlinking the previous instance), copy the trace
67 // contents over, then rename to a final hardcoded path (known to incidentd).
68 // Such tracing sessions should not normally overlap. We do not use unique
69 // unique filenames to avoid creating an unbounded amount of files in case of
70 // errors.
SaveOutputToIncidentTraceOrCrash()71 void PerfettoCmd::SaveOutputToIncidentTraceOrCrash() {
72 LogUploadEvent(PerfettoStatsdAtom::kUploadIncidentBegin);
73 char kIncidentTracePath[256];
74 sprintf(kIncidentTracePath, "%s/incident-trace", kStateDir);
75
76 char kTempIncidentTracePath[256];
77 sprintf(kTempIncidentTracePath, "%s.temp", kIncidentTracePath);
78
79 PERFETTO_CHECK(unlink(kTempIncidentTracePath) == 0 || errno == ENOENT);
80
81 // TODO(b/155024256) These should not be necessary (we flush when destroying
82 // packet writer and sendfile should ignore file offset) however they should
83 // not harm anything and it will help debug the linked issue.
84 PERFETTO_CHECK(fflush(*trace_out_stream_) == 0);
85 PERFETTO_CHECK(fseek(*trace_out_stream_, 0, SEEK_SET) == 0);
86
87 // SELinux constrains the set of readers.
88 base::ScopedFile staging_fd =
89 base::OpenFile(kTempIncidentTracePath, O_CREAT | O_EXCL | O_RDWR, 0666);
90 PERFETTO_CHECK(staging_fd);
91
92 int fd = fileno(*trace_out_stream_);
93 off_t offset = 0;
94 size_t remaining = static_cast<size_t>(bytes_written_);
95
96 // Count time in terms of CPU to avoid timeouts due to suspend:
97 base::TimeNanos start = base::GetThreadCPUTimeNs();
98 for (;;) {
99 errno = 0;
100 PERFETTO_DCHECK(static_cast<size_t>(offset) + remaining == bytes_written_);
101 auto wsize = PERFETTO_EINTR(sendfile(*staging_fd, fd, &offset, remaining));
102 if (wsize < 0) {
103 PERFETTO_FATAL("sendfile() failed wsize=%zd, off=%" PRId64
104 ", initial=%" PRIu64 ", remaining=%zu",
105 wsize, static_cast<int64_t>(offset), bytes_written_,
106 remaining);
107 }
108 remaining -= static_cast<size_t>(wsize);
109 if (remaining == 0) {
110 break;
111 }
112 base::TimeNanos now = base::GetThreadCPUTimeNs();
113 if (now < start || (now - start).count() > kSendfileTimeoutNs) {
114 PERFETTO_FATAL("sendfile() timed out wsize=%zd, off=%" PRId64
115 ", initial=%" PRIu64
116 ", remaining=%zu, start=%lld, now=%lld",
117 wsize, static_cast<int64_t>(offset), bytes_written_,
118 remaining, static_cast<long long int>(start.count()),
119 static_cast<long long int>(now.count()));
120 }
121 }
122
123 staging_fd.reset();
124 PERFETTO_CHECK(rename(kTempIncidentTracePath, kIncidentTracePath) == 0);
125 // Note: not calling fsync(2), as we're not interested in the file being
126 // consistent in case of a crash.
127 LogUploadEvent(PerfettoStatsdAtom::kUploadIncidentSuccess);
128 }
129
130 // static
CreateUnlinkedTmpFile()131 base::ScopedFile PerfettoCmd::CreateUnlinkedTmpFile() {
132 // If we are tracing to DropBox, there's no need to make a
133 // filesystem-visible temporary file.
134 auto fd = base::OpenFile(kStateDir, O_TMPFILE | O_RDWR, 0600);
135 if (!fd)
136 PERFETTO_PLOG("Could not create a temporary trace file in %s", kStateDir);
137 return fd;
138 }
139
140 } // namespace perfetto
141