• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/rate_limiter.h"
18  
19  #include <sys/stat.h>
20  #include <sys/types.h>
21  #include <unistd.h>
22  
23  #include "perfetto/base/file_utils.h"
24  #include "perfetto/base/logging.h"
25  #include "perfetto/base/scoped_file.h"
26  #include "perfetto/base/utils.h"
27  #include "src/perfetto_cmd/perfetto_cmd.h"
28  
29  #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
30  #include <sys/system_properties.h>
31  #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
32  
33  namespace perfetto {
34  namespace {
35  
36  // 5 mins between traces.
37  const uint64_t kCooldownInSeconds = 60 * 5;
38  
39  // Every 24 hours we reset how much we've uploaded.
40  const uint64_t kMaxUploadResetPeriodInSeconds = 60 * 60 * 24;
41  
42  // Maximum of 10mb every 24h.
43  const uint64_t kMaxUploadInBytes = 1024 * 1024 * 10;
44  
IsUserBuild()45  bool IsUserBuild() {
46  #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
47    char value[PROP_VALUE_MAX];
48    if (!__system_property_get("ro.build.type", value)) {
49      PERFETTO_ELOG("Unable to read ro.build.type: assuming user build");
50      return true;
51    }
52    return strcmp(value, "user") == 0;
53  #else
54    return false;
55  #endif  // PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
56  }
57  
58  }  // namespace
59  
60  RateLimiter::RateLimiter() = default;
61  RateLimiter::~RateLimiter() = default;
62  
ShouldTrace(const Args & args)63  bool RateLimiter::ShouldTrace(const Args& args) {
64    uint64_t now_in_s = static_cast<uint64_t>(args.current_time.count());
65  
66    // Not storing in Dropbox?
67    // -> We can just trace.
68    if (!args.is_dropbox)
69      return true;
70  
71    // If we're tracing a user build we should only trace if the override in
72    // the config is set:
73    if (IsUserBuild() && !args.allow_user_build_tracing) {
74      PERFETTO_ELOG(
75          "Guardrail: allow_user_build_tracing must be set to trace on user "
76          "builds");
77      return false;
78    }
79  
80    // The state file is gone.
81    // Maybe we're tracing for the first time or maybe something went wrong the
82    // last time we tried to save the state. Either way reinitialize the state
83    // file.
84    if (!StateFileExists()) {
85      // We can't write the empty state file?
86      // -> Give up.
87      if (!ClearState()) {
88        PERFETTO_ELOG("Guardrail: failed to initialize guardrail state.");
89        return false;
90      }
91    }
92  
93    bool loaded_state = LoadState(&state_);
94  
95    // Failed to load the state?
96    // Current time is before either saved times?
97    // Last saved trace time is before first saved trace time?
98    // -> Try to save a clean state but don't trace.
99    if (!loaded_state || now_in_s < state_.first_trace_timestamp() ||
100        now_in_s < state_.last_trace_timestamp() ||
101        state_.last_trace_timestamp() < state_.first_trace_timestamp()) {
102      ClearState();
103      PERFETTO_ELOG("Guardrail: state invalid, clearing it.");
104      if (!args.ignore_guardrails)
105        return false;
106    }
107  
108    // If we've uploaded in the last 5mins we shouldn't trace now.
109    if ((now_in_s - state_.last_trace_timestamp()) < kCooldownInSeconds) {
110      PERFETTO_ELOG("Guardrail: Uploaded to DropBox in the last 5mins.");
111      if (!args.ignore_guardrails)
112        return false;
113    }
114  
115    // First trace was more than 24h ago? Reset state.
116    if ((now_in_s - state_.first_trace_timestamp()) >
117        kMaxUploadResetPeriodInSeconds) {
118      state_.set_first_trace_timestamp(0);
119      state_.set_last_trace_timestamp(0);
120      state_.set_total_bytes_uploaded(0);
121      return true;
122    }
123  
124    // If we've uploaded more than 10mb in the last 24 hours we shouldn't trace
125    // now.
126    uint64_t max_upload_guardrail = args.max_upload_bytes_override > 0
127                                        ? args.max_upload_bytes_override
128                                        : kMaxUploadInBytes;
129    if (state_.total_bytes_uploaded() > max_upload_guardrail) {
130      PERFETTO_ELOG("Guardrail: Uploaded >10mb DropBox in the last 24h.");
131      if (!args.ignore_guardrails)
132        return false;
133    }
134  
135    return true;
136  }
137  
OnTraceDone(const Args & args,bool success,uint64_t bytes)138  bool RateLimiter::OnTraceDone(const Args& args, bool success, uint64_t bytes) {
139    uint64_t now_in_s = static_cast<uint64_t>(args.current_time.count());
140  
141    // Failed to upload? Don't update the state.
142    if (!success)
143      return false;
144  
145    if (!args.is_dropbox)
146      return true;
147  
148    // If the first trace timestamp is 0 (either because this is the
149    // first time or because it was reset for being more than 24h ago).
150    // -> We update it to the time of this trace.
151    if (state_.first_trace_timestamp() == 0)
152      state_.set_first_trace_timestamp(now_in_s);
153    // Always updated the last trace timestamp.
154    state_.set_last_trace_timestamp(now_in_s);
155    // Add the amount we uploaded to the running total.
156    state_.set_total_bytes_uploaded(state_.total_bytes_uploaded() + bytes);
157  
158    if (!SaveState(state_)) {
159      PERFETTO_ELOG("Failed to save state.");
160      return false;
161    }
162  
163    return true;
164  }
165  
GetStateFilePath() const166  std::string RateLimiter::GetStateFilePath() const {
167    return std::string(kTempDropBoxTraceDir) + "/.guardraildata";
168  }
169  
StateFileExists()170  bool RateLimiter::StateFileExists() {
171    struct stat out;
172    return stat(GetStateFilePath().c_str(), &out) != -1;
173  }
174  
ClearState()175  bool RateLimiter::ClearState() {
176    PerfettoCmdState zero{};
177    zero.set_total_bytes_uploaded(0);
178    zero.set_last_trace_timestamp(0);
179    zero.set_first_trace_timestamp(0);
180    bool success = SaveState(zero);
181    if (!success && StateFileExists())
182      remove(GetStateFilePath().c_str());
183    return success;
184  }
185  
LoadState(PerfettoCmdState * state)186  bool RateLimiter::LoadState(PerfettoCmdState* state) {
187    base::ScopedFile in_fd(base::OpenFile(GetStateFilePath(), O_RDONLY));
188  
189    if (!in_fd)
190      return false;
191    char buf[1024];
192    ssize_t bytes = PERFETTO_EINTR(read(in_fd.get(), &buf, sizeof(buf)));
193    if (bytes <= 0)
194      return false;
195    return state->ParseFromArray(&buf, static_cast<int>(bytes));
196  }
197  
SaveState(const PerfettoCmdState & state)198  bool RateLimiter::SaveState(const PerfettoCmdState& state) {
199    // Rationale for 0666: the cmdline client can be executed under two
200    // different Unix UIDs: shell and statsd. If we run one after the
201    // other and the file has 0600 permissions, then the 2nd run won't
202    // be able to read the file and will clear it, aborting the trace.
203    // SELinux still prevents that anything other than the perfetto
204    // executable can change the guardrail file.
205    base::ScopedFile out_fd(
206        base::OpenFile(GetStateFilePath(), O_WRONLY | O_CREAT | O_TRUNC, 0666));
207    if (!out_fd)
208      return false;
209    char buf[1024];
210    size_t size = static_cast<size_t>(state.ByteSize());
211    PERFETTO_CHECK(size < sizeof(buf));
212    if (!state.SerializeToArray(&buf, static_cast<int>(size)))
213      return false;
214    ssize_t written = base::WriteAll(out_fd.get(), &buf, size);
215    return written >= 0 && static_cast<size_t>(written) == size;
216  }
217  
218  }  // namespace perfetto
219