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