xref: /aosp_15_r20/system/extras/simpleperf/IOEventLoop.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker  * Copyright (C) 2016 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker  *
4*288bf522SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker  *
8*288bf522SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker  *
10*288bf522SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker  * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker  */
16*288bf522SAndroid Build Coastguard Worker 
17*288bf522SAndroid Build Coastguard Worker #include "IOEventLoop.h"
18*288bf522SAndroid Build Coastguard Worker 
19*288bf522SAndroid Build Coastguard Worker #include <event2/event.h>
20*288bf522SAndroid Build Coastguard Worker #include <fcntl.h>
21*288bf522SAndroid Build Coastguard Worker 
22*288bf522SAndroid Build Coastguard Worker #include <android-base/logging.h>
23*288bf522SAndroid Build Coastguard Worker 
24*288bf522SAndroid Build Coastguard Worker namespace simpleperf {
25*288bf522SAndroid Build Coastguard Worker 
26*288bf522SAndroid Build Coastguard Worker struct IOEvent {
27*288bf522SAndroid Build Coastguard Worker   IOEventLoop* loop;
28*288bf522SAndroid Build Coastguard Worker   event* e;
29*288bf522SAndroid Build Coastguard Worker   timeval timeout;
30*288bf522SAndroid Build Coastguard Worker   std::function<bool()> callback;
31*288bf522SAndroid Build Coastguard Worker   bool enabled;
32*288bf522SAndroid Build Coastguard Worker 
IOEventsimpleperf::IOEvent33*288bf522SAndroid Build Coastguard Worker   IOEvent(IOEventLoop* loop, const std::function<bool()>& callback)
34*288bf522SAndroid Build Coastguard Worker       : loop(loop), e(nullptr), timeout({}), callback(callback), enabled(false) {}
35*288bf522SAndroid Build Coastguard Worker 
~IOEventsimpleperf::IOEvent36*288bf522SAndroid Build Coastguard Worker   ~IOEvent() {
37*288bf522SAndroid Build Coastguard Worker     if (e != nullptr) {
38*288bf522SAndroid Build Coastguard Worker       event_free(e);
39*288bf522SAndroid Build Coastguard Worker     }
40*288bf522SAndroid Build Coastguard Worker   }
41*288bf522SAndroid Build Coastguard Worker };
42*288bf522SAndroid Build Coastguard Worker 
IOEventLoop()43*288bf522SAndroid Build Coastguard Worker IOEventLoop::IOEventLoop() : ebase_(nullptr), has_error_(false), in_loop_(false) {}
44*288bf522SAndroid Build Coastguard Worker 
~IOEventLoop()45*288bf522SAndroid Build Coastguard Worker IOEventLoop::~IOEventLoop() {
46*288bf522SAndroid Build Coastguard Worker   events_.clear();
47*288bf522SAndroid Build Coastguard Worker   if (ebase_ != nullptr) {
48*288bf522SAndroid Build Coastguard Worker     event_base_free(ebase_);
49*288bf522SAndroid Build Coastguard Worker   }
50*288bf522SAndroid Build Coastguard Worker }
51*288bf522SAndroid Build Coastguard Worker 
EnsureInit()52*288bf522SAndroid Build Coastguard Worker bool IOEventLoop::EnsureInit() {
53*288bf522SAndroid Build Coastguard Worker   if (ebase_ == nullptr) {
54*288bf522SAndroid Build Coastguard Worker     event_config* cfg = event_config_new();
55*288bf522SAndroid Build Coastguard Worker     if (cfg != nullptr) {
56*288bf522SAndroid Build Coastguard Worker       event_config_set_flag(cfg, EVENT_BASE_FLAG_PRECISE_TIMER);
57*288bf522SAndroid Build Coastguard Worker       if (event_config_avoid_method(cfg, "epoll") != 0) {
58*288bf522SAndroid Build Coastguard Worker         LOG(ERROR) << "event_config_avoid_method";
59*288bf522SAndroid Build Coastguard Worker         return false;
60*288bf522SAndroid Build Coastguard Worker       }
61*288bf522SAndroid Build Coastguard Worker       ebase_ = event_base_new_with_config(cfg);
62*288bf522SAndroid Build Coastguard Worker       // perf event files support reporting available data via poll methods. However, it doesn't
63*288bf522SAndroid Build Coastguard Worker       // work well with epoll. Because perf_poll() in kernel/events/core.c uses a report and reset
64*288bf522SAndroid Build Coastguard Worker       // way to report poll events. If perf_poll() is called twice, it may return POLLIN for the
65*288bf522SAndroid Build Coastguard Worker       // first time, and no events for the second time. And epoll may call perf_poll() more than
66*288bf522SAndroid Build Coastguard Worker       // once to confirm events. A failed situation is below:
67*288bf522SAndroid Build Coastguard Worker       // When profiling SimpleperfExampleOfKotlin on Pixel device with `-g --duration 10`, the
68*288bf522SAndroid Build Coastguard Worker       // kernel fills up the buffer before we call epoll_ctl(EPOLL_CTL_ADD). Then the POLLIN event
69*288bf522SAndroid Build Coastguard Worker       // is returned when calling epoll_ctl(), while no events are returned when calling
70*288bf522SAndroid Build Coastguard Worker       // epoll_wait(). As a result, simpleperf doesn't receive any poll wakeup events.
71*288bf522SAndroid Build Coastguard Worker       if (strcmp(event_base_get_method(ebase_), "poll") != 0) {
72*288bf522SAndroid Build Coastguard Worker         LOG(ERROR) << "event_base_get_method isn't poll: " << event_base_get_method(ebase_);
73*288bf522SAndroid Build Coastguard Worker         return false;
74*288bf522SAndroid Build Coastguard Worker       }
75*288bf522SAndroid Build Coastguard Worker       event_config_free(cfg);
76*288bf522SAndroid Build Coastguard Worker       if (event_base_priority_init(ebase_, 2) != 0) {
77*288bf522SAndroid Build Coastguard Worker         LOG(ERROR) << "event_base_priority_init failed";
78*288bf522SAndroid Build Coastguard Worker         return false;
79*288bf522SAndroid Build Coastguard Worker       }
80*288bf522SAndroid Build Coastguard Worker     }
81*288bf522SAndroid Build Coastguard Worker     if (ebase_ == nullptr) {
82*288bf522SAndroid Build Coastguard Worker       LOG(ERROR) << "failed to create event_base";
83*288bf522SAndroid Build Coastguard Worker       return false;
84*288bf522SAndroid Build Coastguard Worker     }
85*288bf522SAndroid Build Coastguard Worker   }
86*288bf522SAndroid Build Coastguard Worker   return true;
87*288bf522SAndroid Build Coastguard Worker }
88*288bf522SAndroid Build Coastguard Worker 
EventCallbackFn(int,int16_t,void * arg)89*288bf522SAndroid Build Coastguard Worker void IOEventLoop::EventCallbackFn(int, int16_t, void* arg) {
90*288bf522SAndroid Build Coastguard Worker   IOEvent* e = static_cast<IOEvent*>(arg);
91*288bf522SAndroid Build Coastguard Worker   if (!e->callback()) {
92*288bf522SAndroid Build Coastguard Worker     e->loop->has_error_ = true;
93*288bf522SAndroid Build Coastguard Worker     e->loop->ExitLoop();
94*288bf522SAndroid Build Coastguard Worker   }
95*288bf522SAndroid Build Coastguard Worker }
96*288bf522SAndroid Build Coastguard Worker 
MakeFdNonBlocking(int fd)97*288bf522SAndroid Build Coastguard Worker static bool MakeFdNonBlocking(int fd) {
98*288bf522SAndroid Build Coastguard Worker   int flags = fcntl(fd, F_GETFL, 0);
99*288bf522SAndroid Build Coastguard Worker   if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
100*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "fcntl() failed";
101*288bf522SAndroid Build Coastguard Worker     return false;
102*288bf522SAndroid Build Coastguard Worker   }
103*288bf522SAndroid Build Coastguard Worker   return true;
104*288bf522SAndroid Build Coastguard Worker }
105*288bf522SAndroid Build Coastguard Worker 
AddReadEvent(int fd,const std::function<bool ()> & callback,IOEventPriority priority)106*288bf522SAndroid Build Coastguard Worker IOEventRef IOEventLoop::AddReadEvent(int fd, const std::function<bool()>& callback,
107*288bf522SAndroid Build Coastguard Worker                                      IOEventPriority priority) {
108*288bf522SAndroid Build Coastguard Worker   if (!MakeFdNonBlocking(fd)) {
109*288bf522SAndroid Build Coastguard Worker     return nullptr;
110*288bf522SAndroid Build Coastguard Worker   }
111*288bf522SAndroid Build Coastguard Worker   return AddEvent(fd, EV_READ | EV_PERSIST, nullptr, callback, priority);
112*288bf522SAndroid Build Coastguard Worker }
113*288bf522SAndroid Build Coastguard Worker 
AddWriteEvent(int fd,const std::function<bool ()> & callback,IOEventPriority priority)114*288bf522SAndroid Build Coastguard Worker IOEventRef IOEventLoop::AddWriteEvent(int fd, const std::function<bool()>& callback,
115*288bf522SAndroid Build Coastguard Worker                                       IOEventPriority priority) {
116*288bf522SAndroid Build Coastguard Worker   if (!MakeFdNonBlocking(fd)) {
117*288bf522SAndroid Build Coastguard Worker     return nullptr;
118*288bf522SAndroid Build Coastguard Worker   }
119*288bf522SAndroid Build Coastguard Worker   return AddEvent(fd, EV_WRITE | EV_PERSIST, nullptr, callback, priority);
120*288bf522SAndroid Build Coastguard Worker }
121*288bf522SAndroid Build Coastguard Worker 
AddSignalEvent(int sig,const std::function<bool ()> & callback,IOEventPriority priority)122*288bf522SAndroid Build Coastguard Worker bool IOEventLoop::AddSignalEvent(int sig, const std::function<bool()>& callback,
123*288bf522SAndroid Build Coastguard Worker                                  IOEventPriority priority) {
124*288bf522SAndroid Build Coastguard Worker   return AddEvent(sig, EV_SIGNAL | EV_PERSIST, nullptr, callback, priority) != nullptr;
125*288bf522SAndroid Build Coastguard Worker }
126*288bf522SAndroid Build Coastguard Worker 
AddSignalEvents(std::vector<int> sigs,const std::function<bool ()> & callback,IOEventPriority priority)127*288bf522SAndroid Build Coastguard Worker bool IOEventLoop::AddSignalEvents(std::vector<int> sigs, const std::function<bool()>& callback,
128*288bf522SAndroid Build Coastguard Worker                                   IOEventPriority priority) {
129*288bf522SAndroid Build Coastguard Worker   for (auto sig : sigs) {
130*288bf522SAndroid Build Coastguard Worker     if (!AddSignalEvent(sig, callback, priority)) {
131*288bf522SAndroid Build Coastguard Worker       return false;
132*288bf522SAndroid Build Coastguard Worker     }
133*288bf522SAndroid Build Coastguard Worker   }
134*288bf522SAndroid Build Coastguard Worker   return true;
135*288bf522SAndroid Build Coastguard Worker }
136*288bf522SAndroid Build Coastguard Worker 
AddPeriodicEvent(timeval duration,const std::function<bool ()> & callback,IOEventPriority priority)137*288bf522SAndroid Build Coastguard Worker IOEventRef IOEventLoop::AddPeriodicEvent(timeval duration, const std::function<bool()>& callback,
138*288bf522SAndroid Build Coastguard Worker                                          IOEventPriority priority) {
139*288bf522SAndroid Build Coastguard Worker   return AddEvent(-1, EV_PERSIST, &duration, callback, priority);
140*288bf522SAndroid Build Coastguard Worker }
141*288bf522SAndroid Build Coastguard Worker 
AddOneTimeEvent(timeval duration,const std::function<bool ()> & callback,IOEventPriority priority)142*288bf522SAndroid Build Coastguard Worker IOEventRef IOEventLoop::AddOneTimeEvent(timeval duration, const std::function<bool()>& callback,
143*288bf522SAndroid Build Coastguard Worker                                         IOEventPriority priority) {
144*288bf522SAndroid Build Coastguard Worker   return AddEvent(-1, 0, &duration, callback, priority);
145*288bf522SAndroid Build Coastguard Worker }
146*288bf522SAndroid Build Coastguard Worker 
AddEvent(int fd_or_sig,int16_t events,timeval * timeout,const std::function<bool ()> & callback,IOEventPriority priority)147*288bf522SAndroid Build Coastguard Worker IOEventRef IOEventLoop::AddEvent(int fd_or_sig, int16_t events, timeval* timeout,
148*288bf522SAndroid Build Coastguard Worker                                  const std::function<bool()>& callback, IOEventPriority priority) {
149*288bf522SAndroid Build Coastguard Worker   if (!EnsureInit()) {
150*288bf522SAndroid Build Coastguard Worker     return nullptr;
151*288bf522SAndroid Build Coastguard Worker   }
152*288bf522SAndroid Build Coastguard Worker   std::unique_ptr<IOEvent> e(new IOEvent(this, callback));
153*288bf522SAndroid Build Coastguard Worker   e->e = event_new(ebase_, fd_or_sig, events, EventCallbackFn, e.get());
154*288bf522SAndroid Build Coastguard Worker   if (e->e == nullptr) {
155*288bf522SAndroid Build Coastguard Worker     LOG(ERROR) << "event_new() failed";
156*288bf522SAndroid Build Coastguard Worker     return nullptr;
157*288bf522SAndroid Build Coastguard Worker   }
158*288bf522SAndroid Build Coastguard Worker   event_priority_set(e->e, priority);
159*288bf522SAndroid Build Coastguard Worker   if (event_add(e->e, timeout) != 0) {
160*288bf522SAndroid Build Coastguard Worker     LOG(ERROR) << "event_add() failed";
161*288bf522SAndroid Build Coastguard Worker     return nullptr;
162*288bf522SAndroid Build Coastguard Worker   }
163*288bf522SAndroid Build Coastguard Worker   if (timeout != nullptr) {
164*288bf522SAndroid Build Coastguard Worker     e->timeout = *timeout;
165*288bf522SAndroid Build Coastguard Worker   }
166*288bf522SAndroid Build Coastguard Worker   e->enabled = true;
167*288bf522SAndroid Build Coastguard Worker   events_.push_back(std::move(e));
168*288bf522SAndroid Build Coastguard Worker   return events_.back().get();
169*288bf522SAndroid Build Coastguard Worker }
170*288bf522SAndroid Build Coastguard Worker 
RunLoop()171*288bf522SAndroid Build Coastguard Worker bool IOEventLoop::RunLoop() {
172*288bf522SAndroid Build Coastguard Worker   in_loop_ = true;
173*288bf522SAndroid Build Coastguard Worker   if (event_base_dispatch(ebase_) == -1) {
174*288bf522SAndroid Build Coastguard Worker     LOG(ERROR) << "event_base_dispatch() failed";
175*288bf522SAndroid Build Coastguard Worker     in_loop_ = false;
176*288bf522SAndroid Build Coastguard Worker     return false;
177*288bf522SAndroid Build Coastguard Worker   }
178*288bf522SAndroid Build Coastguard Worker   if (has_error_) {
179*288bf522SAndroid Build Coastguard Worker     return false;
180*288bf522SAndroid Build Coastguard Worker   }
181*288bf522SAndroid Build Coastguard Worker   return true;
182*288bf522SAndroid Build Coastguard Worker }
183*288bf522SAndroid Build Coastguard Worker 
ExitLoop()184*288bf522SAndroid Build Coastguard Worker bool IOEventLoop::ExitLoop() {
185*288bf522SAndroid Build Coastguard Worker   if (in_loop_) {
186*288bf522SAndroid Build Coastguard Worker     if (event_base_loopbreak(ebase_) == -1) {
187*288bf522SAndroid Build Coastguard Worker       LOG(ERROR) << "event_base_loopbreak() failed";
188*288bf522SAndroid Build Coastguard Worker       return false;
189*288bf522SAndroid Build Coastguard Worker     }
190*288bf522SAndroid Build Coastguard Worker     in_loop_ = false;
191*288bf522SAndroid Build Coastguard Worker   }
192*288bf522SAndroid Build Coastguard Worker   return true;
193*288bf522SAndroid Build Coastguard Worker }
194*288bf522SAndroid Build Coastguard Worker 
DisableEvent(IOEventRef ref)195*288bf522SAndroid Build Coastguard Worker bool IOEventLoop::DisableEvent(IOEventRef ref) {
196*288bf522SAndroid Build Coastguard Worker   if (ref->enabled) {
197*288bf522SAndroid Build Coastguard Worker     if (event_del(ref->e) != 0) {
198*288bf522SAndroid Build Coastguard Worker       LOG(ERROR) << "event_del() failed";
199*288bf522SAndroid Build Coastguard Worker       return false;
200*288bf522SAndroid Build Coastguard Worker     }
201*288bf522SAndroid Build Coastguard Worker     ref->enabled = false;
202*288bf522SAndroid Build Coastguard Worker   }
203*288bf522SAndroid Build Coastguard Worker   return true;
204*288bf522SAndroid Build Coastguard Worker }
205*288bf522SAndroid Build Coastguard Worker 
EnableEvent(IOEventRef ref)206*288bf522SAndroid Build Coastguard Worker bool IOEventLoop::EnableEvent(IOEventRef ref) {
207*288bf522SAndroid Build Coastguard Worker   if (!ref->enabled) {
208*288bf522SAndroid Build Coastguard Worker     timeval* timeout =
209*288bf522SAndroid Build Coastguard Worker         (ref->timeout.tv_sec != 0 || ref->timeout.tv_usec != 0) ? &ref->timeout : nullptr;
210*288bf522SAndroid Build Coastguard Worker     if (event_add(ref->e, timeout) != 0) {
211*288bf522SAndroid Build Coastguard Worker       LOG(ERROR) << "event_add() failed";
212*288bf522SAndroid Build Coastguard Worker       return false;
213*288bf522SAndroid Build Coastguard Worker     }
214*288bf522SAndroid Build Coastguard Worker     ref->enabled = true;
215*288bf522SAndroid Build Coastguard Worker   }
216*288bf522SAndroid Build Coastguard Worker   return true;
217*288bf522SAndroid Build Coastguard Worker }
218*288bf522SAndroid Build Coastguard Worker 
DelEvent(IOEventRef ref)219*288bf522SAndroid Build Coastguard Worker bool IOEventLoop::DelEvent(IOEventRef ref) {
220*288bf522SAndroid Build Coastguard Worker   DisableEvent(ref);
221*288bf522SAndroid Build Coastguard Worker   IOEventLoop* loop = ref->loop;
222*288bf522SAndroid Build Coastguard Worker   for (auto it = loop->events_.begin(); it != loop->events_.end(); ++it) {
223*288bf522SAndroid Build Coastguard Worker     if (it->get() == ref) {
224*288bf522SAndroid Build Coastguard Worker       loop->events_.erase(it);
225*288bf522SAndroid Build Coastguard Worker       break;
226*288bf522SAndroid Build Coastguard Worker     }
227*288bf522SAndroid Build Coastguard Worker   }
228*288bf522SAndroid Build Coastguard Worker   return true;
229*288bf522SAndroid Build Coastguard Worker }
230*288bf522SAndroid Build Coastguard Worker 
231*288bf522SAndroid Build Coastguard Worker }  // namespace simpleperf
232