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