• Home
  • 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 "perfetto/public/consumer_api.h"
18 
19 #include <fcntl.h>
20 #include <inttypes.h>
21 #include <stdlib.h>
22 #include <sys/mman.h>
23 #include <sys/select.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26 #include <sys/uio.h>
27 #include <unistd.h>
28 
29 #include <atomic>
30 #include <memory>
31 #include <mutex>
32 #include <thread>
33 
34 #include "perfetto/base/build_config.h"
35 #include "perfetto/base/event.h"
36 #include "perfetto/base/scoped_file.h"
37 #include "perfetto/base/temp_file.h"
38 #include "perfetto/base/thread_checker.h"
39 #include "perfetto/base/unix_task_runner.h"
40 #include "perfetto/base/utils.h"
41 #include "perfetto/tracing/core/consumer.h"
42 #include "perfetto/tracing/core/trace_config.h"
43 #include "perfetto/tracing/core/trace_packet.h"
44 #include "perfetto/tracing/ipc/consumer_ipc_client.h"
45 #include "src/tracing/ipc/default_socket.h"
46 
47 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
48 #include <linux/memfd.h>
49 #include <sys/syscall.h>
50 #endif
51 
52 #include "perfetto/config/trace_config.pb.h"
53 
54 #define PERFETTO_EXPORTED_API __attribute__((visibility("default")))
55 
56 namespace perfetto {
57 namespace consumer {
58 
59 namespace {
60 
61 class TracingSession : public Consumer {
62  public:
63   TracingSession(base::TaskRunner*,
64                  Handle,
65                  OnStateChangedCb,
66                  void* callback_arg,
67                  const perfetto::protos::TraceConfig&);
68   ~TracingSession() override;
69 
70   // Note: if making this class moveable, the move-ctor/dtor must be updated
71   // to clear up mapped_buf_ on dtor.
72 
73   // These methods are called on a thread != |task_runner_|.
state() const74   State state() const { return state_; }
mapped_buf() const75   std::pair<char*, size_t> mapped_buf() const {
76     // The comparison operator will do an acquire-load on the atomic |state_|.
77     if (state_ == State::kTraceEnded)
78       return std::make_pair(mapped_buf_, mapped_buf_size_);
79     return std::make_pair(nullptr, 0);
80   }
81 
82   // All the methods below are called only on the |task_runner_| thread.
83 
84   bool Initialize();
85   void StartTracing();
86 
87   // perfetto::Consumer implementation.
88   void OnConnect() override;
89   void OnDisconnect() override;
90   void OnTracingDisabled() override;
91   void OnTraceData(std::vector<TracePacket>, bool has_more) override;
92   void OnDetach(bool) override;
93   void OnAttach(bool, const TraceConfig&) override;
94   void OnTraceStats(bool, const TraceStats&) override;
95   void OnObservableEvents(const ObservableEvents&) override;
96 
97  private:
98   TracingSession(const TracingSession&) = delete;
99   TracingSession& operator=(const TracingSession&) = delete;
100 
101   void DestroyConnection();
102   void NotifyCallback();
103 
104   base::TaskRunner* const task_runner_;
105   Handle const handle_;
106   OnStateChangedCb const callback_ = nullptr;
107   void* const callback_arg_ = nullptr;
108   TraceConfig trace_config_;
109   base::ScopedFile buf_fd_;
110   std::unique_ptr<TracingService::ConsumerEndpoint> consumer_endpoint_;
111 
112   // |mapped_buf_| and |mapped_buf_size_| are seq-consistent with |state_|.
113   std::atomic<State> state_{State::kIdle};
114   char* mapped_buf_ = nullptr;
115   size_t mapped_buf_size_ = 0;
116 
117   PERFETTO_THREAD_CHECKER(thread_checker_)
118 };
119 
TracingSession(base::TaskRunner * task_runner,Handle handle,OnStateChangedCb callback,void * callback_arg,const perfetto::protos::TraceConfig & trace_config_proto)120 TracingSession::TracingSession(
121     base::TaskRunner* task_runner,
122     Handle handle,
123     OnStateChangedCb callback,
124     void* callback_arg,
125     const perfetto::protos::TraceConfig& trace_config_proto)
126     : task_runner_(task_runner),
127       handle_(handle),
128       callback_(callback),
129       callback_arg_(callback_arg) {
130   PERFETTO_DETACH_FROM_THREAD(thread_checker_);
131   trace_config_.FromProto(trace_config_proto);
132   trace_config_.set_write_into_file(true);
133 
134   // TODO(primiano): this really doesn't matter because the trace will be
135   // flushed into the file when stopping. We need a way to be able to say
136   // "disable periodic flushing and flush only when stopping".
137   trace_config_.set_file_write_period_ms(60000);
138 }
139 
~TracingSession()140 TracingSession::~TracingSession() {
141   PERFETTO_DCHECK_THREAD(thread_checker_);
142   if (mapped_buf_)
143     PERFETTO_CHECK(munmap(mapped_buf_, mapped_buf_size_) == 0);
144 }
145 
Initialize()146 bool TracingSession::Initialize() {
147   PERFETTO_DCHECK_THREAD(thread_checker_);
148 
149   if (state_ != State::kIdle)
150     return false;
151 
152 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
153   char memfd_name[64];
154   snprintf(memfd_name, sizeof(memfd_name), "perfetto_trace_%" PRId64, handle_);
155   buf_fd_.reset(
156       static_cast<int>(syscall(__NR_memfd_create, memfd_name, MFD_CLOEXEC)));
157 #else
158   // Fallback for testing on Linux/mac.
159   buf_fd_ = base::TempFile::CreateUnlinked().ReleaseFD();
160 #endif
161 
162   if (!buf_fd_) {
163     PERFETTO_PLOG("Failed to allocate temporary tracing buffer");
164     return false;
165   }
166 
167   state_ = State::kConnecting;
168   consumer_endpoint_ =
169       ConsumerIPCClient::Connect(GetConsumerSocket(), this, task_runner_);
170 
171   return true;
172 }
173 
174 // Called after EnabledTracing, soon after the IPC connection is established.
OnConnect()175 void TracingSession::OnConnect() {
176   PERFETTO_DCHECK_THREAD(thread_checker_);
177 
178   PERFETTO_DLOG("OnConnect");
179   PERFETTO_DCHECK(state_ == State::kConnecting);
180   consumer_endpoint_->EnableTracing(trace_config_,
181                                     base::ScopedFile(dup(*buf_fd_)));
182   if (trace_config_.deferred_start())
183     state_ = State::kConfigured;
184   else
185     state_ = State::kTracing;
186   NotifyCallback();
187 }
188 
StartTracing()189 void TracingSession::StartTracing() {
190   PERFETTO_DCHECK_THREAD(thread_checker_);
191 
192   auto state = state_.load();
193   if (state != State::kConfigured) {
194     PERFETTO_ELOG("StartTracing(): invalid state (%d)",
195                   static_cast<int>(state));
196     return;
197   }
198   state_ = State::kTracing;
199   consumer_endpoint_->StartTracing();
200 }
201 
OnTracingDisabled()202 void TracingSession::OnTracingDisabled() {
203   PERFETTO_DCHECK_THREAD(thread_checker_);
204   PERFETTO_DLOG("OnTracingDisabled");
205 
206   struct stat stat_buf {};
207   int res = fstat(buf_fd_.get(), &stat_buf);
208   mapped_buf_size_ = res == 0 ? static_cast<size_t>(stat_buf.st_size) : 0;
209   mapped_buf_ =
210       static_cast<char*>(mmap(nullptr, mapped_buf_size_, PROT_READ | PROT_WRITE,
211                               MAP_SHARED, buf_fd_.get(), 0));
212   DestroyConnection();
213   if (mapped_buf_size_ == 0 || mapped_buf_ == MAP_FAILED) {
214     mapped_buf_ = nullptr;
215     mapped_buf_size_ = 0;
216     state_ = State::kTraceFailed;
217     PERFETTO_ELOG("Tracing session failed");
218   } else {
219     state_ = State::kTraceEnded;
220   }
221   NotifyCallback();
222 }
223 
OnDisconnect()224 void TracingSession::OnDisconnect() {
225   PERFETTO_DCHECK_THREAD(thread_checker_);
226   PERFETTO_DLOG("OnDisconnect");
227   DestroyConnection();
228   state_ = State::kConnectionError;
229   NotifyCallback();
230 }
231 
OnDetach(bool)232 void TracingSession::OnDetach(bool) {
233   PERFETTO_DCHECK(false);  // Should never be called, Detach() is not used here.
234 }
235 
OnAttach(bool,const TraceConfig &)236 void TracingSession::OnAttach(bool, const TraceConfig&) {
237   PERFETTO_DCHECK(false);  // Should never be called, Attach() is not used here.
238 }
239 
OnTraceStats(bool,const TraceStats &)240 void TracingSession::OnTraceStats(bool, const TraceStats&) {
241   // Should never be called, GetTraceStats() is not used here.
242   PERFETTO_DCHECK(false);
243 }
244 
OnObservableEvents(const ObservableEvents &)245 void TracingSession::OnObservableEvents(const ObservableEvents&) {
246   // Should never be called, ObserveEvents() is not used here.
247   PERFETTO_DCHECK(false);
248 }
249 
DestroyConnection()250 void TracingSession::DestroyConnection() {
251   // Destroys the connection in a separate task. This is to avoid destroying
252   // the IPC connection directly from within the IPC callback.
253   TracingService::ConsumerEndpoint* endpoint = consumer_endpoint_.release();
254   task_runner_->PostTask([endpoint] { delete endpoint; });
255 }
256 
OnTraceData(std::vector<TracePacket>,bool)257 void TracingSession::OnTraceData(std::vector<TracePacket>, bool) {
258   // This should be never called because we are using |write_into_file| and
259   // asking the traced service to directly write into the |buf_fd_|.
260   PERFETTO_DFATAL("Should be unreachable.");
261 }
262 
NotifyCallback()263 void TracingSession::NotifyCallback() {
264   if (!callback_)
265     return;
266   auto state = state_.load();
267   auto callback = callback_;
268   auto handle = handle_;
269   auto callback_arg = callback_arg_;
270   task_runner_->PostTask([callback, callback_arg, handle, state] {
271     callback(handle, state, callback_arg);
272   });
273 }
274 
275 class TracingController {
276  public:
277   static TracingController* GetInstance();
278   TracingController();
279 
280   // These methods are called from a thread != |task_runner_|.
281   Handle Create(const void*, size_t, OnStateChangedCb, void* callback_arg);
282   void StartTracing(Handle);
283   State PollState(Handle);
284   TraceBuffer ReadTrace(Handle);
285   void Destroy(Handle);
286 
287  private:
288   void ThreadMain();  // Called on |task_runner_| thread.
289 
290   std::mutex mutex_;
291   std::thread thread_;
292   std::unique_ptr<base::UnixTaskRunner> task_runner_;
293   std::condition_variable task_runner_initialized_;
294   Handle last_handle_ = 0;
295   std::map<Handle, std::unique_ptr<TracingSession>> sessions_;
296 };
297 
GetInstance()298 TracingController* TracingController::GetInstance() {
299   static TracingController* instance = new TracingController();
300   return instance;
301 }
302 
TracingController()303 TracingController::TracingController()
304     : thread_(&TracingController::ThreadMain, this) {
305   std::unique_lock<std::mutex> lock(mutex_);
306   task_runner_initialized_.wait(lock, [this] { return !!task_runner_; });
307 }
308 
ThreadMain()309 void TracingController::ThreadMain() {
310   {
311     std::unique_lock<std::mutex> lock(mutex_);
312     task_runner_.reset(new base::UnixTaskRunner());
313   }
314   task_runner_initialized_.notify_one();
315   task_runner_->Run();
316 }
317 
Create(const void * config_proto_buf,size_t config_len,OnStateChangedCb callback,void * callback_arg)318 Handle TracingController::Create(const void* config_proto_buf,
319                                  size_t config_len,
320                                  OnStateChangedCb callback,
321                                  void* callback_arg) {
322   perfetto::protos::TraceConfig config_proto;
323   bool parsed = config_proto.ParseFromArray(config_proto_buf,
324                                             static_cast<int>(config_len));
325   if (!parsed) {
326     PERFETTO_ELOG("Failed to decode TraceConfig proto");
327     return kInvalidHandle;
328   }
329 
330   if (!config_proto.duration_ms()) {
331     PERFETTO_ELOG("The trace config must specify a duration");
332     return kInvalidHandle;
333   }
334 
335   std::unique_lock<std::mutex> lock(mutex_);
336   Handle handle = ++last_handle_;
337   auto* session = new TracingSession(task_runner_.get(), handle, callback,
338                                      callback_arg, config_proto);
339   sessions_.emplace(handle, std::unique_ptr<TracingSession>(session));
340 
341   // Enable the TracingSession on its own thread.
342   task_runner_->PostTask([session] { session->Initialize(); });
343 
344   return handle;
345 }
346 
StartTracing(Handle handle)347 void TracingController::StartTracing(Handle handle) {
348   std::unique_lock<std::mutex> lock(mutex_);
349   auto it = sessions_.find(handle);
350   if (it == sessions_.end()) {
351     PERFETTO_ELOG("StartTracing(): Invalid tracing session handle");
352     return;
353   };
354   TracingSession* session = it->second.get();
355   task_runner_->PostTask([session] { session->StartTracing(); });
356 }
357 
PollState(Handle handle)358 State TracingController::PollState(Handle handle) {
359   std::unique_lock<std::mutex> lock(mutex_);
360   auto it = sessions_.find(handle);
361   if (it == sessions_.end())
362     return State::kSessionNotFound;
363   return it->second->state();
364 }
365 
ReadTrace(Handle handle)366 TraceBuffer TracingController::ReadTrace(Handle handle) {
367   TraceBuffer buf{};
368 
369   std::unique_lock<std::mutex> lock(mutex_);
370   auto it = sessions_.find(handle);
371   if (it == sessions_.end()) {
372     PERFETTO_DLOG("Handle invalid");
373     return buf;
374   }
375 
376   TracingSession* session = it->second.get();
377   auto state = session->state();
378   if (state == State::kTraceEnded) {
379     std::tie(buf.begin, buf.size) = session->mapped_buf();
380     return buf;
381   }
382 
383   PERFETTO_DLOG("ReadTrace(): called in an unexpected state (%d)",
384                 static_cast<int>(state));
385   return buf;
386 }
387 
Destroy(Handle handle)388 void TracingController::Destroy(Handle handle) {
389   // Post an empty task on the task runner to delete the session on its own
390   // thread.
391   std::unique_lock<std::mutex> lock(mutex_);
392   auto it = sessions_.find(handle);
393   if (it == sessions_.end())
394     return;
395   TracingSession* session = it->second.release();
396   sessions_.erase(it);
397   task_runner_->PostTask([session] { delete session; });
398 }
399 
400 }  // namespace
401 
Create(const void * config_proto,size_t config_len,OnStateChangedCb callback,void * callback_arg)402 PERFETTO_EXPORTED_API Handle Create(const void* config_proto,
403                                     size_t config_len,
404                                     OnStateChangedCb callback,
405                                     void* callback_arg) {
406   return TracingController::GetInstance()->Create(config_proto, config_len,
407                                                   callback, callback_arg);
408 }
409 
410 PERFETTO_EXPORTED_API
StartTracing(Handle handle)411 void StartTracing(Handle handle) {
412   return TracingController::GetInstance()->StartTracing(handle);
413 }
414 
PollState(Handle handle)415 PERFETTO_EXPORTED_API State PollState(Handle handle) {
416   return TracingController::GetInstance()->PollState(handle);
417 }
418 
ReadTrace(Handle handle)419 PERFETTO_EXPORTED_API TraceBuffer ReadTrace(Handle handle) {
420   return TracingController::GetInstance()->ReadTrace(handle);
421 }
422 
Destroy(Handle handle)423 PERFETTO_EXPORTED_API void Destroy(Handle handle) {
424   TracingController::GetInstance()->Destroy(handle);
425 }
426 }  // namespace consumer
427 }  // namespace perfetto
428