Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 1 | // Copyright (C) 2020 The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #include "perfetto/perfetto_consumer.h" |
| 16 | |
Igor Murashkin | 70401f4 | 2020-03-04 13:57:12 -0800 | [diff] [blame] | 17 | #include "common/trace.h" |
| 18 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 19 | #include <android-base/logging.h> |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 20 | #include <android-base/properties.h> |
| 21 | #include <utils/Looper.h> |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 22 | #include <utils/Printer.h> |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 23 | |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 24 | #include <limits> |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 25 | #include <map> |
| 26 | #include <memory> |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 27 | #include <mutex> |
| 28 | #include <sstream> |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 29 | #include <vector> |
| 30 | |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 31 | #include <inttypes.h> |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 32 | #include <time.h> |
| 33 | |
| 34 | namespace iorap::perfetto { |
| 35 | |
| 36 | using State = PerfettoConsumer::State; |
| 37 | using Handle = PerfettoConsumer::Handle; |
| 38 | static constexpr Handle kInvalidHandle = PerfettoConsumer::kInvalidHandle; |
| 39 | using OnStateChangedCb = PerfettoConsumer::OnStateChangedCb; |
| 40 | using TraceBuffer = PerfettoConsumer::TraceBuffer; |
| 41 | |
| 42 | enum class StateKind { |
| 43 | kUncreated, |
| 44 | kCreated, |
| 45 | kStartedTracing, |
| 46 | kReadTracing, |
| 47 | kTimedOutDestroyed, // same as kDestroyed but timed out. |
| 48 | kDestroyed, // calling kDestroyed before timing out. |
| 49 | }; |
| 50 | |
| 51 | std::ostream& operator<<(std::ostream& os, StateKind kind) { |
| 52 | switch (kind) { |
| 53 | case StateKind::kUncreated: |
| 54 | os << "kUncreated"; |
| 55 | break; |
| 56 | case StateKind::kCreated: |
| 57 | os << "kCreated"; |
| 58 | break; |
| 59 | case StateKind::kStartedTracing: |
| 60 | os << "kStartedTracing"; |
| 61 | break; |
| 62 | case StateKind::kReadTracing: |
| 63 | os << "kReadTracing"; |
| 64 | break; |
| 65 | case StateKind::kTimedOutDestroyed: |
| 66 | os << "kTimedOutDestroyed"; |
| 67 | break; |
| 68 | case StateKind::kDestroyed: |
| 69 | os << "kDestroyed"; |
| 70 | break; |
| 71 | default: |
| 72 | os << "(invalid)"; |
| 73 | break; |
| 74 | } |
| 75 | return os; |
| 76 | } |
| 77 | |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 78 | std::string ToString(StateKind kind) { |
| 79 | std::stringstream ss; |
| 80 | ss << kind; |
| 81 | return ss.str(); |
| 82 | } |
| 83 | |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 84 | static constexpr uint64_t kSecToNano = 1000000000LL; |
| 85 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 86 | static uint64_t GetTimeNanoseconds() { |
| 87 | struct timespec now; |
| 88 | clock_gettime(CLOCK_REALTIME, &now); |
| 89 | |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 90 | uint64_t now_ns = (now.tv_sec * kSecToNano + now.tv_nsec); |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 91 | return now_ns; |
| 92 | } |
| 93 | |
| 94 | // Describe the state of our handle in detail for debugging/logging. |
| 95 | struct HandleDescription { |
| 96 | Handle handle_; |
George Burgess IV | b054c28 | 2020-02-21 14:25:28 -0800 | [diff] [blame] | 97 | StateKind kind_{StateKind::kUncreated}; // Our state. required for correctness. |
| 98 | OnStateChangedCb callback_{nullptr}; // Required for Destroy callbacks. |
| 99 | void* callback_arg_{nullptr}; |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 100 | |
| 101 | // For dumping to logs: |
George Burgess IV | b054c28 | 2020-02-21 14:25:28 -0800 | [diff] [blame] | 102 | State state_{State::kSessionNotFound}; // perfetto state |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 103 | std::optional<uint64_t> started_tracing_ns_; // when StartedTracing last called. |
Igor Murashkin | 70401f4 | 2020-03-04 13:57:12 -0800 | [diff] [blame] | 104 | std::optional<uint64_t> read_trace_ns_; // When ReadTrace last called. |
George Burgess IV | b054c28 | 2020-02-21 14:25:28 -0800 | [diff] [blame] | 105 | std::uint64_t last_transition_ns_{0}; |
Igor Murashkin | 70401f4 | 2020-03-04 13:57:12 -0800 | [diff] [blame] | 106 | std::optional<uint64_t> trace_cookie_; // atrace beginning at StartTracing. |
| 107 | bool trace_ended_{false}; // atrace ending at ReadTrace or Destroy. |
George Burgess IV | b054c28 | 2020-02-21 14:25:28 -0800 | [diff] [blame] | 108 | |
| 109 | HandleDescription(Handle handle): handle_(handle) {} |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 110 | }; |
| 111 | |
| 112 | // pimpl idiom to hide the implementation details from header |
| 113 | // |
| 114 | // Track and verify that our perfetto usage is sane. |
| 115 | struct PerfettoConsumerImpl::Impl { |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 116 | Impl() : raw_{new PerfettoConsumerRawImpl{}}, |
| 117 | message_handler_{new TraceMessageHandler{this}} { |
| 118 | std::thread watchdog_thread{ [this]() { |
| 119 | ::android::sp<::android::Looper> looper; |
| 120 | { |
| 121 | std::lock_guard<std::mutex> guard{looper_mutex_}; |
| 122 | looper = ::android::Looper::prepare(/*opts*/0); |
| 123 | looper_ = looper; |
| 124 | } |
| 125 | |
| 126 | static constexpr int kTimeoutMillis = std::numeric_limits<int>::max(); |
| 127 | |
| 128 | while (true) { |
| 129 | // Execute any pending callbacks, otherwise just block forever. |
| 130 | int result = looper->pollAll(kTimeoutMillis); |
| 131 | |
| 132 | if (result == ::android::Looper::POLL_ERROR) { |
| 133 | LOG(ERROR) << "PerfettoConsumerImpl::Looper got a POLL_ERROR"; |
| 134 | } else { |
| 135 | LOG(DEBUG) << "PerfettoConsumerImpl::Looper result was " << result; |
| 136 | } |
| 137 | } |
| 138 | }}; |
| 139 | |
| 140 | // Let thread run freely on its own. |
| 141 | watchdog_thread.detach(); |
| 142 | |
| 143 | // Block until looper_ is prepared. |
| 144 | while (true) { |
| 145 | std::lock_guard<std::mutex> guard{looper_mutex_}; |
| 146 | if (looper_ != nullptr) { |
| 147 | break; |
| 148 | } |
| 149 | } |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 150 | } |
| 151 | |
| 152 | private: |
| 153 | std::unique_ptr<PerfettoConsumerRawImpl> raw_; |
| 154 | std::map<Handle, HandleDescription> states_; |
| 155 | |
| 156 | // We need this to be a counter to avoid memory leaks. |
| 157 | Handle last_created_{0}; |
| 158 | Handle last_destroyed_{0}; |
Igor Murashkin | 70401f4 | 2020-03-04 13:57:12 -0800 | [diff] [blame] | 159 | uint64_t trace_cookie_{0}; |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 160 | |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 161 | std::mutex mutex_; // Guard above values. |
| 162 | |
| 163 | ::android::sp<::android::Looper> looper_; |
| 164 | std::mutex looper_mutex_; // Guard looper_. |
| 165 | |
| 166 | struct TraceMessageHandler : public ::android::MessageHandler { |
| 167 | TraceMessageHandler(Impl* impl) : impl_{impl} { |
| 168 | CHECK(impl != nullptr); |
| 169 | } |
| 170 | |
| 171 | Impl* impl_; |
| 172 | |
| 173 | virtual void handleMessage(const ::android::Message& message) override { |
| 174 | impl_->OnTraceMessage(static_cast<Handle>(message.what)); |
| 175 | } |
| 176 | }; |
| 177 | |
| 178 | ::android::sp<TraceMessageHandler> message_handler_; |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 179 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 180 | public: |
| 181 | Handle Create(const void* config_proto, |
| 182 | size_t config_len, |
| 183 | OnStateChangedCb callback, |
| 184 | void* callback_arg) { |
| 185 | LOG(VERBOSE) << "PerfettoConsumer::Create(" |
| 186 | << "config_len=" << config_len << ")"; |
| 187 | Handle handle = raw_->Create(config_proto, config_len, callback, callback_arg); |
| 188 | |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 189 | std::lock_guard<std::mutex> guard{mutex_}; |
| 190 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 191 | // Assume every Handle starts at 0 and then increments by 1 every Create. |
| 192 | ++last_created_; |
| 193 | CHECK_EQ(last_created_, handle) << "perfetto handle had unexpected behavior."; |
| 194 | // Without this^ increment-by-1 behavior our detection of untracked state values is broken. |
| 195 | // If we have to, we can go with Untracked=Uncreated|Destroyed but it's better to distinguish |
| 196 | // the two if possible. |
| 197 | |
George Burgess IV | b054c28 | 2020-02-21 14:25:28 -0800 | [diff] [blame] | 198 | HandleDescription handle_desc{handle}; |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 199 | handle_desc.handle_ = handle; |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 200 | handle_desc.callback_ = callback; |
| 201 | handle_desc.callback_arg_ = callback_arg; |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 202 | UpdateHandleDescription(/*inout*/&handle_desc, StateKind::kCreated); |
| 203 | |
| 204 | // assume we never wrap around due to using int64 |
George Burgess IV | b054c28 | 2020-02-21 14:25:28 -0800 | [diff] [blame] | 205 | bool inserted = states_.insert({handle, handle_desc}).second; |
| 206 | CHECK(inserted) << "perfetto handle was re-used: " << handle; |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 207 | |
| 208 | return handle; |
| 209 | } |
| 210 | |
| 211 | void StartTracing(Handle handle) { |
| 212 | LOG(DEBUG) << "PerfettoConsumer::StartTracing(handle=" << handle << ")"; |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 213 | |
Igor Murashkin | 70401f4 | 2020-03-04 13:57:12 -0800 | [diff] [blame] | 214 | uint64_t trace_cookie; |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 215 | { |
| 216 | std::lock_guard<std::mutex> guard{mutex_}; |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 217 | |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 218 | auto it = states_.find(handle); |
| 219 | if (it == states_.end()) { |
| 220 | LOG(ERROR) << "Cannot StartTracing(" << handle << "), untracked handle"; |
| 221 | return; |
| 222 | } |
| 223 | HandleDescription& handle_desc = it->second; |
| 224 | |
| 225 | raw_->StartTracing(handle); |
| 226 | UpdateHandleDescription(/*inout*/&handle_desc, StateKind::kStartedTracing); |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 227 | } |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 228 | |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 229 | // Use a looper here to add a timeout and immediately destroy the trace buffer. |
| 230 | CHECK_LE(static_cast<int64_t>(handle), static_cast<int64_t>(std::numeric_limits<int>::max())); |
| 231 | int message_code = static_cast<int>(handle); |
| 232 | ::android::Message message{message_code}; |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 233 | |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 234 | std::lock_guard<std::mutex> looper_guard{looper_mutex_}; |
| 235 | looper_->sendMessageDelayed(static_cast<nsecs_t>(GetPropertyTraceTimeoutNs()), |
| 236 | message_handler_, |
| 237 | message); |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 238 | } |
| 239 | |
| 240 | TraceBuffer ReadTrace(Handle handle) { |
| 241 | LOG(DEBUG) << "PerfettoConsumer::ReadTrace(handle=" << handle << ")"; |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 242 | |
| 243 | std::lock_guard<std::mutex> guard{mutex_}; |
| 244 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 245 | auto it = states_.find(handle); |
| 246 | if (it == states_.end()) { |
| 247 | LOG(ERROR) << "Cannot ReadTrace(" << handle << "), untracked handle"; |
| 248 | return TraceBuffer{}; |
| 249 | } |
| 250 | |
| 251 | HandleDescription& handle_desc = it->second; |
| 252 | |
| 253 | TraceBuffer trace_buffer = raw_->ReadTrace(handle); |
| 254 | UpdateHandleDescription(/*inout*/&handle_desc, StateKind::kReadTracing); |
| 255 | |
| 256 | return trace_buffer; |
| 257 | } |
| 258 | |
| 259 | void Destroy(Handle handle) { |
George Burgess IV | b054c28 | 2020-02-21 14:25:28 -0800 | [diff] [blame] | 260 | HandleDescription handle_desc{handle}; |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 261 | TryDestroy(handle, /*do_destroy*/true, /*out*/&handle_desc);; |
| 262 | } |
| 263 | |
| 264 | bool TryDestroy(Handle handle, bool do_destroy, /*out*/HandleDescription* handle_desc_out) { |
| 265 | CHECK(handle_desc_out != nullptr); |
| 266 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 267 | LOG(VERBOSE) << "PerfettoConsumer::Destroy(handle=" << handle << ")"; |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 268 | |
| 269 | std::lock_guard<std::mutex> guard{mutex_}; |
| 270 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 271 | auto it = states_.find(handle); |
| 272 | if (it == states_.end()) { |
| 273 | // Leniency for calling Destroy multiple times. It's not a mistake. |
| 274 | LOG(ERROR) << "Cannot Destroy(" << handle << "), untracked handle"; |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 275 | return false; |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 276 | } |
| 277 | |
| 278 | HandleDescription& handle_desc = it->second; |
| 279 | |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 280 | if (do_destroy) { |
| 281 | raw_->Destroy(handle); |
| 282 | } |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 283 | UpdateHandleDescription(/*inout*/&handle_desc, StateKind::kDestroyed); |
| 284 | |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 285 | *handle_desc_out = handle_desc; |
| 286 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 287 | // No longer track this handle to avoid memory leaks. |
| 288 | last_destroyed_ = handle; |
| 289 | states_.erase(it); |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 290 | |
| 291 | return true; |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 292 | } |
| 293 | |
| 294 | State PollState(Handle handle) { |
| 295 | // Just pass-through the call, we never use it directly anyway. |
| 296 | return raw_->PollState(handle); |
| 297 | } |
| 298 | |
| 299 | // Either fetch or infer the current handle state from a handle. |
| 300 | // Meant for debugging/logging only. |
| 301 | HandleDescription GetOrInferHandleDescription(Handle handle) { |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 302 | std::lock_guard<std::mutex> guard{mutex_}; |
| 303 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 304 | auto it = states_.find(handle); |
| 305 | if (it == states_.end()) { |
George Burgess IV | b054c28 | 2020-02-21 14:25:28 -0800 | [diff] [blame] | 306 | HandleDescription state{handle}; |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 307 | // If it's untracked it hasn't been created yet, or it was already destroyed. |
| 308 | if (IsDestroyed(handle)) { |
| 309 | UpdateHandleDescription(/*inout*/&state, StateKind::kDestroyed); |
| 310 | } else { |
| 311 | if (!IsUncreated(handle)) { |
| 312 | LOG(WARNING) << "bad state detection"; |
| 313 | } |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 314 | UpdateHandleDescription(/*inout*/&state, StateKind::kUncreated); |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 315 | } |
| 316 | return state; |
| 317 | } |
| 318 | return it->second; |
| 319 | } |
| 320 | |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 321 | void OnTraceMessage(Handle handle) { |
| 322 | LOG(VERBOSE) << "OnTraceMessage(" << static_cast<int64_t>(handle) << ")"; |
George Burgess IV | b054c28 | 2020-02-21 14:25:28 -0800 | [diff] [blame] | 323 | HandleDescription handle_desc{handle}; |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 324 | { |
| 325 | std::lock_guard<std::mutex> guard{mutex_}; |
| 326 | |
| 327 | auto it = states_.find(handle); |
| 328 | if (it == states_.end()) { |
| 329 | // Handle values are never re-used, so we can simply ignore the message here |
| 330 | // instead of having to remove it from the message queue. |
| 331 | LOG(VERBOSE) << "OnTraceMessage(" << static_cast<int64_t>(handle) |
| 332 | << ") no longer tracked handle"; |
| 333 | return; |
| 334 | } |
| 335 | handle_desc = it->second; |
| 336 | } |
| 337 | |
| 338 | // First check. Has this trace been active for too long? |
| 339 | uint64_t now_ns = GetTimeNanoseconds(); |
| 340 | if (handle_desc.kind_ == StateKind::kStartedTracing) { |
| 341 | // Ignore other kinds of traces because they don't exhaust perfetto resources. |
| 342 | CHECK(handle_desc.started_tracing_ns_.has_value()) << static_cast<int64_t>(handle); |
| 343 | |
| 344 | uint64_t started_tracing_ns = *handle_desc.started_tracing_ns_; |
| 345 | |
| 346 | if ((now_ns - started_tracing_ns) > GetPropertyTraceTimeoutNs()) { |
| 347 | LOG(WARNING) << "Perfetto Handle timed out after " << (now_ns - started_tracing_ns) << "ns" |
| 348 | << ", forcibly destroying"; |
| 349 | |
| 350 | // Let the callback handler call Destroy. |
| 351 | handle_desc.callback_(handle, State::kTraceFailed, handle_desc.callback_arg_); |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | // Second check. Are there too many traces now? Cull the old traces. |
| 356 | std::vector<HandleDescription> handle_list; |
| 357 | do { |
| 358 | std::lock_guard<std::mutex> guard{mutex_}; |
| 359 | |
| 360 | size_t max_trace_count = GetPropertyMaxTraceCount(); |
| 361 | if (states_.size() > max_trace_count) { |
| 362 | size_t overflow_count = states_.size() - max_trace_count; |
| 363 | LOG(WARNING) << "Too many perfetto handles, overflowed by " << overflow_count |
| 364 | << ", pruning down to " << max_trace_count; |
| 365 | } else { |
| 366 | break; |
| 367 | } |
| 368 | |
| 369 | size_t prune_count = states_.size() - max_trace_count; |
| 370 | auto it = states_.begin(); |
| 371 | for (size_t i = 0; i < prune_count; ++i) { |
| 372 | // Simply prune by handle 1,2,3,4... |
| 373 | // We could do better with a timestamp if we wanted to. |
| 374 | ++it; |
| 375 | handle_list.push_back(it->second); |
| 376 | } |
| 377 | } while (false); |
| 378 | |
| 379 | for (HandleDescription& handle_desc : handle_list) { |
| 380 | LOG(DEBUG) << "Perfetto handle pruned: " << static_cast<int64_t>(handle); |
| 381 | |
| 382 | // Let the callback handler call Destroy. |
| 383 | handle_desc.callback_(handle, State::kTraceFailed, handle_desc.callback_arg_); |
| 384 | } |
| 385 | } |
| 386 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 387 | private: |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 388 | static uint64_t GetPropertyTraceTimeoutNs() { |
| 389 | static uint64_t value = // property is timeout in seconds |
| 390 | ::android::base::GetUintProperty<uint64_t>("iorapd.perfetto.timeout", /*default*/10); |
| 391 | return value * kSecToNano; |
| 392 | } |
| 393 | |
| 394 | static size_t GetPropertyMaxTraceCount() { |
| 395 | static size_t value = |
| 396 | ::android::base::GetUintProperty<size_t>("iorapd.perfetto.max_traces", /*default*/5); |
| 397 | return value; |
| 398 | } |
| 399 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 400 | void UpdateHandleDescription(/*inout*/HandleDescription* handle_desc, StateKind kind) { |
| 401 | CHECK(handle_desc != nullptr); |
| 402 | handle_desc->kind_ = kind; |
| 403 | handle_desc->state_ = raw_->PollState(handle_desc->handle_); |
| 404 | |
| 405 | handle_desc->last_transition_ns_ = GetTimeNanoseconds(); |
| 406 | if (kind == StateKind::kStartedTracing) { |
Igor Murashkin | 70401f4 | 2020-03-04 13:57:12 -0800 | [diff] [blame] | 407 | if (!handle_desc->started_tracing_ns_) { |
| 408 | handle_desc->started_tracing_ns_ = handle_desc->last_transition_ns_; |
| 409 | |
| 410 | handle_desc->trace_cookie_ = ++trace_cookie_; |
| 411 | |
| 412 | atrace_async_begin(ATRACE_TAG_ACTIVITY_MANAGER, |
| 413 | "Perfetto Scoped Trace", |
| 414 | *handle_desc->trace_cookie_); |
| 415 | atrace_int(ATRACE_TAG_ACTIVITY_MANAGER, |
| 416 | "Perfetto::Trace Handle", |
| 417 | static_cast<int32_t>(handle_desc->handle_)); |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | if (kind == StateKind::kReadTracing) { |
| 422 | if (!handle_desc->read_trace_ns_) { |
| 423 | handle_desc->read_trace_ns_ = handle_desc->last_transition_ns_; |
| 424 | |
Igor Murashkin | e399ac4 | 2020-03-10 11:05:21 -0700 | [diff] [blame] | 425 | if (handle_desc->trace_cookie_.has_value() && !handle_desc->trace_ended_) { |
| 426 | atrace_async_end(ATRACE_TAG_ACTIVITY_MANAGER, |
| 427 | "Perfetto Scoped Trace", |
| 428 | handle_desc->trace_cookie_.value()); |
Igor Murashkin | 70401f4 | 2020-03-04 13:57:12 -0800 | [diff] [blame] | 429 | |
Igor Murashkin | e399ac4 | 2020-03-10 11:05:21 -0700 | [diff] [blame] | 430 | handle_desc->trace_ended_ = true; |
| 431 | } |
Igor Murashkin | 70401f4 | 2020-03-04 13:57:12 -0800 | [diff] [blame] | 432 | } |
| 433 | } |
| 434 | |
| 435 | // If Destroy is called prior to ReadTrace, mark the atrace as finished. |
Igor Murashkin | e399ac4 | 2020-03-10 11:05:21 -0700 | [diff] [blame] | 436 | if (kind == StateKind::kDestroyed && handle_desc->trace_cookie_ && !handle_desc->trace_ended_) { |
Igor Murashkin | 70401f4 | 2020-03-04 13:57:12 -0800 | [diff] [blame] | 437 | atrace_async_end(ATRACE_TAG_ACTIVITY_MANAGER, |
| 438 | "Perfetto Scoped Trace", |
| 439 | *handle_desc->trace_cookie_); |
| 440 | handle_desc->trace_ended_ = true; |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 441 | } |
| 442 | } |
| 443 | |
| 444 | // The following state detection is for debugging only. |
| 445 | // We figure out if something is destroyed, uncreated, or live. |
| 446 | |
| 447 | // Does not distinguish between kTimedOutDestroyed and kDestroyed. |
| 448 | bool IsDestroyed(Handle handle) const { |
| 449 | auto it = states_.find(handle); |
| 450 | if (it != states_.end()) { |
| 451 | // Tracked values are not destroyed yet. |
| 452 | return false; |
| 453 | } |
| 454 | |
| 455 | if (handle == kInvalidHandle) { |
| 456 | return false; |
| 457 | } |
| 458 | |
| 459 | // The following assumes handles are incrementally generated: |
| 460 | if (it == states_.end()) { |
| 461 | // value is in range of [0, last_destroyed] => destroyed. |
| 462 | return handle <= last_destroyed_; |
| 463 | } |
| 464 | |
| 465 | auto min_it = states_.begin(); |
| 466 | if (handle < min_it->first) { |
| 467 | // value smaller than anything tracked: it was destroyed and we stopped tracking it. |
| 468 | return true; |
| 469 | } |
| 470 | |
| 471 | auto max_it = states_.rbegin(); |
| 472 | if (handle > max_it->first) { |
| 473 | // value too big: it's uncreated; |
| 474 | return false; |
| 475 | } |
| 476 | |
| 477 | // else it was a value that was previously tracked within [min,max] but no longer |
| 478 | return true; |
| 479 | } |
| 480 | |
| 481 | bool IsUncreated(Handle handle) const { |
| 482 | auto it = states_.find(handle); |
| 483 | if (it != states_.end()) { |
| 484 | // Tracked values are not uncreated. |
| 485 | return false; |
| 486 | } |
| 487 | |
| 488 | if (handle == kInvalidHandle) { |
| 489 | // Strangely enough, an invalid handle can never be created. |
| 490 | return true; |
| 491 | } |
| 492 | |
| 493 | // The following assumes handles are incrementally generated: |
| 494 | if (it == states_.end()) { |
| 495 | // value is in range of (last_destroyed, inf) => uncreated. |
| 496 | return handle > last_destroyed_; |
| 497 | } |
| 498 | |
| 499 | auto min_it = states_.begin(); |
| 500 | if (handle < min_it->first) { |
| 501 | // value smaller than anything tracked: it was destroyed and we stopped tracking it. |
| 502 | return false; |
| 503 | } |
| 504 | |
| 505 | auto max_it = states_.rbegin(); |
| 506 | if (handle > max_it->first) { |
| 507 | // value too big: it's uncreated; |
| 508 | return true; |
| 509 | } |
| 510 | |
| 511 | // else it was a value that was previously tracked within [min,max] but no longer |
| 512 | return false; |
| 513 | } |
| 514 | |
| 515 | public: |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 516 | void Dump(::android::Printer& printer) { |
| 517 | // Locking can fail if we dump during a deadlock, so just do a best-effort lock here. |
| 518 | bool is_it_locked = mutex_.try_lock(); |
| 519 | |
| 520 | printer.printFormatLine("Perfetto consumer state:"); |
Igor Murashkin | ca25a5e | 2020-02-21 11:55:52 -0800 | [diff] [blame] | 521 | if (!is_it_locked) { |
| 522 | printer.printLine(""""" (possible deadlock)"); |
| 523 | } |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 524 | printer.printFormatLine(" Last destroyed handle: %" PRId64, last_destroyed_); |
| 525 | printer.printFormatLine(" Last created handle: %" PRId64, last_created_); |
| 526 | printer.printFormatLine(""); |
| 527 | printer.printFormatLine(" In-flight handles:"); |
| 528 | |
| 529 | for (auto it = states_.begin(); it != states_.end(); ++it) { |
| 530 | HandleDescription& handle_desc = it->second; |
| 531 | uint64_t started_tracing = |
| 532 | handle_desc.started_tracing_ns_ ? *handle_desc.started_tracing_ns_ : 0; |
| 533 | printer.printFormatLine(" Handle %" PRId64, handle_desc.handle_); |
| 534 | printer.printFormatLine(" Kind: %s", ToString(handle_desc.kind_).c_str()); |
| 535 | printer.printFormatLine(" Perfetto State: %d", static_cast<int>(handle_desc.state_)); |
| 536 | printer.printFormatLine(" Started tracing at: %" PRIu64, started_tracing); |
| 537 | printer.printFormatLine(" Last transition at: %" PRIu64, |
| 538 | handle_desc.last_transition_ns_); |
| 539 | } |
| 540 | if (states_.empty()) { |
| 541 | printer.printFormatLine(" (None)"); |
| 542 | } |
| 543 | |
| 544 | printer.printFormatLine(""); |
| 545 | |
| 546 | if (is_it_locked) { // u.b. if calling unlock on an unlocked mutex. |
| 547 | mutex_.unlock(); |
| 548 | } |
| 549 | } |
| 550 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 551 | static PerfettoConsumerImpl::Impl* GetImplSingleton() { |
| 552 | static PerfettoConsumerImpl::Impl impl; |
| 553 | return &impl; |
| 554 | } |
| 555 | }; |
| 556 | |
| 557 | // Use a singleton because fruit instantiates a new PerfettoConsumer object for every |
| 558 | // new rx chain in RxProducerFactory. However, we want to track all perfetto transitions globally |
| 559 | // through 1 impl object. |
| 560 | // |
| 561 | // TODO: Avoiding a singleton would mean a more significant refactoring to remove the fruit/perfetto |
| 562 | // usage. |
| 563 | |
| 564 | |
| 565 | // |
| 566 | // Forward all calls to PerfettoConsumerImpl::Impl |
| 567 | // |
| 568 | |
| 569 | PerfettoConsumerImpl::~PerfettoConsumerImpl() { |
| 570 | // delete impl_; // TODO: no singleton |
| 571 | } |
| 572 | |
| 573 | void PerfettoConsumerImpl::Initialize() { |
| 574 | // impl_ = new PerfettoConsumerImpl::Impl(); // TODO: no singleton |
| 575 | impl_ = PerfettoConsumerImpl::Impl::GetImplSingleton(); |
| 576 | } |
| 577 | |
Igor Murashkin | 8c55619 | 2020-02-20 17:17:19 -0800 | [diff] [blame] | 578 | void PerfettoConsumerImpl::Dump(::android::Printer& printer) { |
| 579 | PerfettoConsumerImpl::Impl::GetImplSingleton()->Dump(/*borrow*/printer); |
| 580 | } |
| 581 | |
Igor Murashkin | 4cf8984 | 2020-02-20 13:27:43 -0800 | [diff] [blame] | 582 | PerfettoConsumer::Handle PerfettoConsumerImpl::Create(const void* config_proto, |
| 583 | size_t config_len, |
| 584 | PerfettoConsumer::OnStateChangedCb callback, |
| 585 | void* callback_arg) { |
| 586 | return impl_->Create(config_proto, |
| 587 | config_len, |
| 588 | callback, |
| 589 | callback_arg); |
| 590 | } |
| 591 | |
| 592 | void PerfettoConsumerImpl::StartTracing(PerfettoConsumer::Handle handle) { |
| 593 | impl_->StartTracing(handle); |
| 594 | } |
| 595 | |
| 596 | PerfettoConsumer::TraceBuffer PerfettoConsumerImpl::ReadTrace(PerfettoConsumer::Handle handle) { |
| 597 | return impl_->ReadTrace(handle); |
| 598 | } |
| 599 | |
| 600 | void PerfettoConsumerImpl::Destroy(PerfettoConsumer::Handle handle) { |
| 601 | impl_->Destroy(handle); |
| 602 | } |
| 603 | |
| 604 | PerfettoConsumer::State PerfettoConsumerImpl::PollState(PerfettoConsumer::Handle handle) { |
| 605 | return impl_->PollState(handle); |
| 606 | } |
| 607 | |
| 608 | } // namespace iorap::perfetto |